credential_management_handler_unittest.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. // Copyright 2019 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "device/fido/credential_management_handler.h"
  5. #include <memory>
  6. #include "base/bind.h"
  7. #include "base/strings/strcat.h"
  8. #include "base/test/task_environment.h"
  9. #include "device/fido/credential_management.h"
  10. #include "device/fido/fido_constants.h"
  11. #include "device/fido/fido_request_handler_base.h"
  12. #include "device/fido/public_key_credential_descriptor.h"
  13. #include "device/fido/public_key_credential_rp_entity.h"
  14. #include "device/fido/public_key_credential_user_entity.h"
  15. #include "device/fido/test_callback_receiver.h"
  16. #include "device/fido/virtual_fido_device_factory.h"
  17. #include "testing/gmock/include/gmock/gmock.h"
  18. #include "testing/gtest/include/gtest/gtest.h"
  19. namespace device {
  20. namespace {
  21. using testing::UnorderedElementsAreArray;
  22. constexpr char kPIN[] = "1234";
  23. constexpr uint8_t kCredentialID[] = {0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa,
  24. 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa};
  25. constexpr char kRPID[] = "example.com";
  26. constexpr char kRPName[] = "Example Corp";
  27. constexpr uint8_t kUserID[] = {0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1,
  28. 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1};
  29. constexpr char kUserName[] = "alice@example.com";
  30. constexpr char kUserDisplayName[] = "Alice Example <alice@example.com>";
  31. class CredentialManagementHandlerTest : public ::testing::Test {
  32. protected:
  33. std::unique_ptr<CredentialManagementHandler> MakeHandler() {
  34. auto handler = std::make_unique<CredentialManagementHandler>(
  35. &virtual_device_factory_,
  36. base::flat_set<FidoTransportProtocol>{
  37. FidoTransportProtocol::kUsbHumanInterfaceDevice},
  38. ready_callback_.callback(),
  39. base::BindRepeating(&CredentialManagementHandlerTest::GetPIN,
  40. base::Unretained(this)),
  41. finished_callback_.callback());
  42. return handler;
  43. }
  44. void GetPIN(CredentialManagementHandler::AuthenticatorProperties
  45. authenticator_properties,
  46. base::OnceCallback<void(std::string)> provide_pin) {
  47. std::move(provide_pin).Run(kPIN);
  48. }
  49. base::test::TaskEnvironment task_environment_;
  50. test::TestCallbackReceiver<> ready_callback_;
  51. test::StatusAndValuesCallbackReceiver<
  52. CtapDeviceResponseCode,
  53. absl::optional<std::vector<AggregatedEnumerateCredentialsResponse>>,
  54. absl::optional<size_t>>
  55. get_credentials_callback_;
  56. test::ValueCallbackReceiver<CtapDeviceResponseCode> delete_callback_;
  57. test::ValueCallbackReceiver<CtapDeviceResponseCode>
  58. update_user_info_callback_;
  59. test::ValueCallbackReceiver<CredentialManagementStatus> finished_callback_;
  60. test::VirtualFidoDeviceFactory virtual_device_factory_;
  61. };
  62. TEST_F(CredentialManagementHandlerTest, TestDeleteCredential) {
  63. VirtualCtap2Device::Config ctap_config;
  64. ctap_config.pin_support = true;
  65. ctap_config.resident_key_support = true;
  66. ctap_config.credential_management_support = true;
  67. ctap_config.resident_credential_storage = 100;
  68. virtual_device_factory_.SetCtap2Config(ctap_config);
  69. virtual_device_factory_.SetSupportedProtocol(device::ProtocolVersion::kCtap2);
  70. virtual_device_factory_.mutable_state()->pin = kPIN;
  71. virtual_device_factory_.mutable_state()->pin_retries = device::kMaxPinRetries;
  72. PublicKeyCredentialRpEntity rp(kRPID, kRPName,
  73. /*icon_url=*/absl::nullopt);
  74. PublicKeyCredentialUserEntity user(fido_parsing_utils::Materialize(kUserID),
  75. kUserName, kUserDisplayName,
  76. /*icon_url=*/absl::nullopt);
  77. ASSERT_TRUE(virtual_device_factory_.mutable_state()->InjectResidentKey(
  78. kCredentialID, rp, user));
  79. auto handler = MakeHandler();
  80. ready_callback_.WaitForCallback();
  81. handler->GetCredentials(get_credentials_callback_.callback());
  82. get_credentials_callback_.WaitForCallback();
  83. auto result = get_credentials_callback_.TakeResult();
  84. ASSERT_EQ(std::get<0>(result), CtapDeviceResponseCode::kSuccess);
  85. auto opt_response = std::move(std::get<1>(result));
  86. ASSERT_TRUE(opt_response);
  87. EXPECT_EQ(opt_response->size(), 1u);
  88. EXPECT_EQ(opt_response->front().rp, rp);
  89. ASSERT_EQ(opt_response->front().credentials.size(), 1u);
  90. EXPECT_EQ(opt_response->front().credentials.front().user, user);
  91. auto num_remaining = std::get<2>(result);
  92. ASSERT_TRUE(num_remaining);
  93. EXPECT_EQ(*num_remaining, 99u);
  94. handler->DeleteCredential(
  95. opt_response->front().credentials.front().credential_id,
  96. delete_callback_.callback());
  97. delete_callback_.WaitForCallback();
  98. ASSERT_EQ(CtapDeviceResponseCode::kSuccess, delete_callback_.value());
  99. EXPECT_EQ(virtual_device_factory_.mutable_state()->registrations.size(), 0u);
  100. EXPECT_FALSE(finished_callback_.was_called());
  101. }
  102. TEST_F(CredentialManagementHandlerTest, TestUpdateUserInformation) {
  103. VirtualCtap2Device::Config ctap_config;
  104. ctap_config.pin_support = true;
  105. ctap_config.resident_key_support = true;
  106. ctap_config.credential_management_support = true;
  107. ctap_config.resident_credential_storage = 100;
  108. ctap_config.ctap2_versions = {device::Ctap2Version::kCtap2_1};
  109. virtual_device_factory_.SetCtap2Config(ctap_config);
  110. virtual_device_factory_.SetSupportedProtocol(device::ProtocolVersion::kCtap2);
  111. virtual_device_factory_.mutable_state()->pin = kPIN;
  112. virtual_device_factory_.mutable_state()->pin_retries = device::kMaxPinRetries;
  113. std::vector<uint8_t> credential_id =
  114. fido_parsing_utils::Materialize(kCredentialID);
  115. PublicKeyCredentialRpEntity rp(kRPID, kRPName,
  116. /*icon_url=*/absl::nullopt);
  117. PublicKeyCredentialUserEntity user(fido_parsing_utils::Materialize(kUserID),
  118. kUserName, kUserDisplayName,
  119. /*icon_url=*/absl::nullopt);
  120. ASSERT_TRUE(virtual_device_factory_.mutable_state()->InjectResidentKey(
  121. kCredentialID, rp, user));
  122. auto handler = MakeHandler();
  123. ready_callback_.WaitForCallback();
  124. PublicKeyCredentialUserEntity updated_user(
  125. fido_parsing_utils::Materialize(kUserID), "bobbyr@example.com",
  126. "Bobby R. Smith",
  127. /*icon_url=*/absl::nullopt);
  128. handler->UpdateUserInformation(
  129. device::PublicKeyCredentialDescriptor(device::CredentialType::kPublicKey,
  130. credential_id),
  131. updated_user, update_user_info_callback_.callback());
  132. update_user_info_callback_.WaitForCallback();
  133. ASSERT_EQ(CtapDeviceResponseCode::kSuccess,
  134. update_user_info_callback_.value());
  135. EXPECT_EQ(virtual_device_factory_.mutable_state()
  136. ->registrations[credential_id]
  137. .user,
  138. updated_user);
  139. EXPECT_FALSE(finished_callback_.was_called());
  140. }
  141. TEST_F(CredentialManagementHandlerTest, TestForcePINChange) {
  142. virtual_device_factory_.mutable_state()->pin = kPIN;
  143. virtual_device_factory_.mutable_state()->force_pin_change = true;
  144. VirtualCtap2Device::Config ctap_config;
  145. ctap_config.pin_support = true;
  146. ctap_config.resident_key_support = true;
  147. ctap_config.credential_management_support = true;
  148. ctap_config.min_pin_length_support = true;
  149. ctap_config.pin_uv_auth_token_support = true;
  150. ctap_config.ctap2_versions = {Ctap2Version::kCtap2_1};
  151. virtual_device_factory_.SetCtap2Config(ctap_config);
  152. virtual_device_factory_.SetSupportedProtocol(device::ProtocolVersion::kCtap2);
  153. auto handler = MakeHandler();
  154. finished_callback_.WaitForCallback();
  155. ASSERT_EQ(finished_callback_.value(),
  156. CredentialManagementStatus::kForcePINChange);
  157. }
  158. TEST_F(CredentialManagementHandlerTest,
  159. EnumerateCredentialResponse_TruncatedUTF8) {
  160. // Webauthn says[1] that authenticators may truncate strings in user entities.
  161. // Since authenticators aren't going to do UTF-8 processing, that means that
  162. // they may truncate a multi-byte code point and thus produce an invalid
  163. // string in the CBOR. This test exercises that case.
  164. //
  165. // [1] https://www.w3.org/TR/webauthn/#sctn-user-credential-params
  166. VirtualCtap2Device::Config ctap_config;
  167. ctap_config.pin_support = true;
  168. ctap_config.resident_key_support = true;
  169. ctap_config.credential_management_support = true;
  170. ctap_config.resident_credential_storage = 100;
  171. ctap_config.allow_invalid_utf8_in_credential_entities = true;
  172. virtual_device_factory_.SetCtap2Config(ctap_config);
  173. virtual_device_factory_.SetSupportedProtocol(device::ProtocolVersion::kCtap2);
  174. virtual_device_factory_.mutable_state()->pin = kPIN;
  175. virtual_device_factory_.mutable_state()->pin_retries = device::kMaxPinRetries;
  176. const std::string rp_name = base::StrCat({std::string(57, 'a'), "💣"});
  177. const std::string user_name = base::StrCat({std::string(57, 'b'), "💣"});
  178. const std::string display_name = base::StrCat({std::string(57, 'c'), "💣"});
  179. constexpr char kTruncatedUTF8[] = "\xf0\x9f\x92";
  180. // Simulate a truncated rp and user entity strings by appending a partial
  181. // UTF-8 sequence during InjectResidentKey(). The total string length
  182. // including the trailing sequence will be 64 bytes.
  183. DCHECK_EQ(rp_name.size(), 61u);
  184. ASSERT_TRUE(virtual_device_factory_.mutable_state()->InjectResidentKey(
  185. kCredentialID,
  186. PublicKeyCredentialRpEntity(kRPID,
  187. base::StrCat({rp_name, kTruncatedUTF8}),
  188. /*icon_url=*/absl::nullopt),
  189. PublicKeyCredentialUserEntity(
  190. fido_parsing_utils::Materialize(kUserID),
  191. base::StrCat({user_name, kTruncatedUTF8}),
  192. base::StrCat({display_name, kTruncatedUTF8}),
  193. /*icon_url=*/absl::nullopt)));
  194. auto handler = MakeHandler();
  195. ready_callback_.WaitForCallback();
  196. handler->GetCredentials(get_credentials_callback_.callback());
  197. get_credentials_callback_.WaitForCallback();
  198. auto result = get_credentials_callback_.TakeResult();
  199. ASSERT_EQ(std::get<0>(result), CtapDeviceResponseCode::kSuccess);
  200. auto opt_response = std::move(std::get<1>(result));
  201. ASSERT_TRUE(opt_response);
  202. ASSERT_EQ(opt_response->size(), 1u);
  203. ASSERT_EQ(opt_response->front().credentials.size(), 1u);
  204. EXPECT_EQ(opt_response->front().rp,
  205. PublicKeyCredentialRpEntity(kRPID, rp_name,
  206. /*icon_url=*/absl::nullopt));
  207. EXPECT_EQ(
  208. opt_response->front().credentials.front().user,
  209. PublicKeyCredentialUserEntity(fido_parsing_utils::Materialize(kUserID),
  210. user_name, display_name,
  211. /*icon_url=*/absl::nullopt));
  212. }
  213. TEST_F(CredentialManagementHandlerTest, EnumerateCredentialsMultipleRPs) {
  214. VirtualCtap2Device::Config ctap_config;
  215. ctap_config.pin_support = true;
  216. ctap_config.resident_key_support = true;
  217. ctap_config.credential_management_support = true;
  218. ctap_config.resident_credential_storage = 100;
  219. virtual_device_factory_.SetCtap2Config(ctap_config);
  220. virtual_device_factory_.SetSupportedProtocol(device::ProtocolVersion::kCtap2);
  221. virtual_device_factory_.mutable_state()->pin = kPIN;
  222. virtual_device_factory_.mutable_state()->pin_retries = device::kMaxPinRetries;
  223. const PublicKeyCredentialRpEntity rps[] = {
  224. {"foo.com", "foo", absl::nullopt},
  225. {"bar.com", "bar", absl::nullopt},
  226. {"foobar.com", "foobar", absl::nullopt},
  227. };
  228. const PublicKeyCredentialUserEntity users[] = {
  229. {{0}, "alice", "Alice", absl::nullopt},
  230. {{1}, "bob", "Bob", absl::nullopt},
  231. };
  232. uint8_t credential_id[] = {0};
  233. for (const auto& rp : rps) {
  234. for (const auto& user : users) {
  235. ASSERT_TRUE(virtual_device_factory_.mutable_state()->InjectResidentKey(
  236. credential_id, rp, user));
  237. credential_id[0]++;
  238. }
  239. }
  240. auto handler = MakeHandler();
  241. ready_callback_.WaitForCallback();
  242. handler->GetCredentials(get_credentials_callback_.callback());
  243. get_credentials_callback_.WaitForCallback();
  244. auto result = get_credentials_callback_.TakeResult();
  245. ASSERT_EQ(std::get<0>(result), CtapDeviceResponseCode::kSuccess);
  246. std::vector<AggregatedEnumerateCredentialsResponse> responses =
  247. std::move(*std::get<1>(result));
  248. ASSERT_EQ(responses.size(), 3u);
  249. PublicKeyCredentialRpEntity got_rps[3];
  250. std::transform(responses.begin(), responses.end(), std::begin(got_rps),
  251. [](const auto& response) { return response.rp; });
  252. EXPECT_THAT(got_rps, UnorderedElementsAreArray(rps));
  253. for (const AggregatedEnumerateCredentialsResponse& response : responses) {
  254. ASSERT_EQ(response.credentials.size(), 2u);
  255. PublicKeyCredentialUserEntity got_users[2];
  256. std::transform(response.credentials.begin(), response.credentials.end(),
  257. std::begin(got_users),
  258. [](const auto& credential) { return credential.user; });
  259. EXPECT_THAT(got_users, UnorderedElementsAreArray(users));
  260. }
  261. }
  262. } // namespace
  263. } // namespace device