123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309 |
- // Copyright 2019 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "device/fido/credential_management_handler.h"
- #include <memory>
- #include "base/bind.h"
- #include "base/strings/strcat.h"
- #include "base/test/task_environment.h"
- #include "device/fido/credential_management.h"
- #include "device/fido/fido_constants.h"
- #include "device/fido/fido_request_handler_base.h"
- #include "device/fido/public_key_credential_descriptor.h"
- #include "device/fido/public_key_credential_rp_entity.h"
- #include "device/fido/public_key_credential_user_entity.h"
- #include "device/fido/test_callback_receiver.h"
- #include "device/fido/virtual_fido_device_factory.h"
- #include "testing/gmock/include/gmock/gmock.h"
- #include "testing/gtest/include/gtest/gtest.h"
- namespace device {
- namespace {
- using testing::UnorderedElementsAreArray;
- constexpr char kPIN[] = "1234";
- constexpr uint8_t kCredentialID[] = {0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa,
- 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa};
- constexpr char kRPID[] = "example.com";
- constexpr char kRPName[] = "Example Corp";
- constexpr uint8_t kUserID[] = {0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1,
- 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1};
- constexpr char kUserName[] = "alice@example.com";
- constexpr char kUserDisplayName[] = "Alice Example <alice@example.com>";
- class CredentialManagementHandlerTest : public ::testing::Test {
- protected:
- std::unique_ptr<CredentialManagementHandler> MakeHandler() {
- auto handler = std::make_unique<CredentialManagementHandler>(
- &virtual_device_factory_,
- base::flat_set<FidoTransportProtocol>{
- FidoTransportProtocol::kUsbHumanInterfaceDevice},
- ready_callback_.callback(),
- base::BindRepeating(&CredentialManagementHandlerTest::GetPIN,
- base::Unretained(this)),
- finished_callback_.callback());
- return handler;
- }
- void GetPIN(CredentialManagementHandler::AuthenticatorProperties
- authenticator_properties,
- base::OnceCallback<void(std::string)> provide_pin) {
- std::move(provide_pin).Run(kPIN);
- }
- base::test::TaskEnvironment task_environment_;
- test::TestCallbackReceiver<> ready_callback_;
- test::StatusAndValuesCallbackReceiver<
- CtapDeviceResponseCode,
- absl::optional<std::vector<AggregatedEnumerateCredentialsResponse>>,
- absl::optional<size_t>>
- get_credentials_callback_;
- test::ValueCallbackReceiver<CtapDeviceResponseCode> delete_callback_;
- test::ValueCallbackReceiver<CtapDeviceResponseCode>
- update_user_info_callback_;
- test::ValueCallbackReceiver<CredentialManagementStatus> finished_callback_;
- test::VirtualFidoDeviceFactory virtual_device_factory_;
- };
- TEST_F(CredentialManagementHandlerTest, TestDeleteCredential) {
- VirtualCtap2Device::Config ctap_config;
- ctap_config.pin_support = true;
- ctap_config.resident_key_support = true;
- ctap_config.credential_management_support = true;
- ctap_config.resident_credential_storage = 100;
- virtual_device_factory_.SetCtap2Config(ctap_config);
- virtual_device_factory_.SetSupportedProtocol(device::ProtocolVersion::kCtap2);
- virtual_device_factory_.mutable_state()->pin = kPIN;
- virtual_device_factory_.mutable_state()->pin_retries = device::kMaxPinRetries;
- PublicKeyCredentialRpEntity rp(kRPID, kRPName,
- /*icon_url=*/absl::nullopt);
- PublicKeyCredentialUserEntity user(fido_parsing_utils::Materialize(kUserID),
- kUserName, kUserDisplayName,
- /*icon_url=*/absl::nullopt);
- ASSERT_TRUE(virtual_device_factory_.mutable_state()->InjectResidentKey(
- kCredentialID, rp, user));
- auto handler = MakeHandler();
- ready_callback_.WaitForCallback();
- handler->GetCredentials(get_credentials_callback_.callback());
- get_credentials_callback_.WaitForCallback();
- auto result = get_credentials_callback_.TakeResult();
- ASSERT_EQ(std::get<0>(result), CtapDeviceResponseCode::kSuccess);
- auto opt_response = std::move(std::get<1>(result));
- ASSERT_TRUE(opt_response);
- EXPECT_EQ(opt_response->size(), 1u);
- EXPECT_EQ(opt_response->front().rp, rp);
- ASSERT_EQ(opt_response->front().credentials.size(), 1u);
- EXPECT_EQ(opt_response->front().credentials.front().user, user);
- auto num_remaining = std::get<2>(result);
- ASSERT_TRUE(num_remaining);
- EXPECT_EQ(*num_remaining, 99u);
- handler->DeleteCredential(
- opt_response->front().credentials.front().credential_id,
- delete_callback_.callback());
- delete_callback_.WaitForCallback();
- ASSERT_EQ(CtapDeviceResponseCode::kSuccess, delete_callback_.value());
- EXPECT_EQ(virtual_device_factory_.mutable_state()->registrations.size(), 0u);
- EXPECT_FALSE(finished_callback_.was_called());
- }
- TEST_F(CredentialManagementHandlerTest, TestUpdateUserInformation) {
- VirtualCtap2Device::Config ctap_config;
- ctap_config.pin_support = true;
- ctap_config.resident_key_support = true;
- ctap_config.credential_management_support = true;
- ctap_config.resident_credential_storage = 100;
- ctap_config.ctap2_versions = {device::Ctap2Version::kCtap2_1};
- virtual_device_factory_.SetCtap2Config(ctap_config);
- virtual_device_factory_.SetSupportedProtocol(device::ProtocolVersion::kCtap2);
- virtual_device_factory_.mutable_state()->pin = kPIN;
- virtual_device_factory_.mutable_state()->pin_retries = device::kMaxPinRetries;
- std::vector<uint8_t> credential_id =
- fido_parsing_utils::Materialize(kCredentialID);
- PublicKeyCredentialRpEntity rp(kRPID, kRPName,
- /*icon_url=*/absl::nullopt);
- PublicKeyCredentialUserEntity user(fido_parsing_utils::Materialize(kUserID),
- kUserName, kUserDisplayName,
- /*icon_url=*/absl::nullopt);
- ASSERT_TRUE(virtual_device_factory_.mutable_state()->InjectResidentKey(
- kCredentialID, rp, user));
- auto handler = MakeHandler();
- ready_callback_.WaitForCallback();
- PublicKeyCredentialUserEntity updated_user(
- fido_parsing_utils::Materialize(kUserID), "bobbyr@example.com",
- "Bobby R. Smith",
- /*icon_url=*/absl::nullopt);
- handler->UpdateUserInformation(
- device::PublicKeyCredentialDescriptor(device::CredentialType::kPublicKey,
- credential_id),
- updated_user, update_user_info_callback_.callback());
- update_user_info_callback_.WaitForCallback();
- ASSERT_EQ(CtapDeviceResponseCode::kSuccess,
- update_user_info_callback_.value());
- EXPECT_EQ(virtual_device_factory_.mutable_state()
- ->registrations[credential_id]
- .user,
- updated_user);
- EXPECT_FALSE(finished_callback_.was_called());
- }
- TEST_F(CredentialManagementHandlerTest, TestForcePINChange) {
- virtual_device_factory_.mutable_state()->pin = kPIN;
- virtual_device_factory_.mutable_state()->force_pin_change = true;
- VirtualCtap2Device::Config ctap_config;
- ctap_config.pin_support = true;
- ctap_config.resident_key_support = true;
- ctap_config.credential_management_support = true;
- ctap_config.min_pin_length_support = true;
- ctap_config.pin_uv_auth_token_support = true;
- ctap_config.ctap2_versions = {Ctap2Version::kCtap2_1};
- virtual_device_factory_.SetCtap2Config(ctap_config);
- virtual_device_factory_.SetSupportedProtocol(device::ProtocolVersion::kCtap2);
- auto handler = MakeHandler();
- finished_callback_.WaitForCallback();
- ASSERT_EQ(finished_callback_.value(),
- CredentialManagementStatus::kForcePINChange);
- }
- TEST_F(CredentialManagementHandlerTest,
- EnumerateCredentialResponse_TruncatedUTF8) {
- // Webauthn says[1] that authenticators may truncate strings in user entities.
- // Since authenticators aren't going to do UTF-8 processing, that means that
- // they may truncate a multi-byte code point and thus produce an invalid
- // string in the CBOR. This test exercises that case.
- //
- // [1] https://www.w3.org/TR/webauthn/#sctn-user-credential-params
- VirtualCtap2Device::Config ctap_config;
- ctap_config.pin_support = true;
- ctap_config.resident_key_support = true;
- ctap_config.credential_management_support = true;
- ctap_config.resident_credential_storage = 100;
- ctap_config.allow_invalid_utf8_in_credential_entities = true;
- virtual_device_factory_.SetCtap2Config(ctap_config);
- virtual_device_factory_.SetSupportedProtocol(device::ProtocolVersion::kCtap2);
- virtual_device_factory_.mutable_state()->pin = kPIN;
- virtual_device_factory_.mutable_state()->pin_retries = device::kMaxPinRetries;
- const std::string rp_name = base::StrCat({std::string(57, 'a'), "💣"});
- const std::string user_name = base::StrCat({std::string(57, 'b'), "💣"});
- const std::string display_name = base::StrCat({std::string(57, 'c'), "💣"});
- constexpr char kTruncatedUTF8[] = "\xf0\x9f\x92";
- // Simulate a truncated rp and user entity strings by appending a partial
- // UTF-8 sequence during InjectResidentKey(). The total string length
- // including the trailing sequence will be 64 bytes.
- DCHECK_EQ(rp_name.size(), 61u);
- ASSERT_TRUE(virtual_device_factory_.mutable_state()->InjectResidentKey(
- kCredentialID,
- PublicKeyCredentialRpEntity(kRPID,
- base::StrCat({rp_name, kTruncatedUTF8}),
- /*icon_url=*/absl::nullopt),
- PublicKeyCredentialUserEntity(
- fido_parsing_utils::Materialize(kUserID),
- base::StrCat({user_name, kTruncatedUTF8}),
- base::StrCat({display_name, kTruncatedUTF8}),
- /*icon_url=*/absl::nullopt)));
- auto handler = MakeHandler();
- ready_callback_.WaitForCallback();
- handler->GetCredentials(get_credentials_callback_.callback());
- get_credentials_callback_.WaitForCallback();
- auto result = get_credentials_callback_.TakeResult();
- ASSERT_EQ(std::get<0>(result), CtapDeviceResponseCode::kSuccess);
- auto opt_response = std::move(std::get<1>(result));
- ASSERT_TRUE(opt_response);
- ASSERT_EQ(opt_response->size(), 1u);
- ASSERT_EQ(opt_response->front().credentials.size(), 1u);
- EXPECT_EQ(opt_response->front().rp,
- PublicKeyCredentialRpEntity(kRPID, rp_name,
- /*icon_url=*/absl::nullopt));
- EXPECT_EQ(
- opt_response->front().credentials.front().user,
- PublicKeyCredentialUserEntity(fido_parsing_utils::Materialize(kUserID),
- user_name, display_name,
- /*icon_url=*/absl::nullopt));
- }
- TEST_F(CredentialManagementHandlerTest, EnumerateCredentialsMultipleRPs) {
- VirtualCtap2Device::Config ctap_config;
- ctap_config.pin_support = true;
- ctap_config.resident_key_support = true;
- ctap_config.credential_management_support = true;
- ctap_config.resident_credential_storage = 100;
- virtual_device_factory_.SetCtap2Config(ctap_config);
- virtual_device_factory_.SetSupportedProtocol(device::ProtocolVersion::kCtap2);
- virtual_device_factory_.mutable_state()->pin = kPIN;
- virtual_device_factory_.mutable_state()->pin_retries = device::kMaxPinRetries;
- const PublicKeyCredentialRpEntity rps[] = {
- {"foo.com", "foo", absl::nullopt},
- {"bar.com", "bar", absl::nullopt},
- {"foobar.com", "foobar", absl::nullopt},
- };
- const PublicKeyCredentialUserEntity users[] = {
- {{0}, "alice", "Alice", absl::nullopt},
- {{1}, "bob", "Bob", absl::nullopt},
- };
- uint8_t credential_id[] = {0};
- for (const auto& rp : rps) {
- for (const auto& user : users) {
- ASSERT_TRUE(virtual_device_factory_.mutable_state()->InjectResidentKey(
- credential_id, rp, user));
- credential_id[0]++;
- }
- }
- auto handler = MakeHandler();
- ready_callback_.WaitForCallback();
- handler->GetCredentials(get_credentials_callback_.callback());
- get_credentials_callback_.WaitForCallback();
- auto result = get_credentials_callback_.TakeResult();
- ASSERT_EQ(std::get<0>(result), CtapDeviceResponseCode::kSuccess);
- std::vector<AggregatedEnumerateCredentialsResponse> responses =
- std::move(*std::get<1>(result));
- ASSERT_EQ(responses.size(), 3u);
- PublicKeyCredentialRpEntity got_rps[3];
- std::transform(responses.begin(), responses.end(), std::begin(got_rps),
- [](const auto& response) { return response.rp; });
- EXPECT_THAT(got_rps, UnorderedElementsAreArray(rps));
- for (const AggregatedEnumerateCredentialsResponse& response : responses) {
- ASSERT_EQ(response.credentials.size(), 2u);
- PublicKeyCredentialUserEntity got_users[2];
- std::transform(response.credentials.begin(), response.credentials.end(),
- std::begin(got_users),
- [](const auto& credential) { return credential.user; });
- EXPECT_THAT(got_users, UnorderedElementsAreArray(users));
- }
- }
- } // namespace
- } // namespace device
|