diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-10-12 14:27:29 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-10-13 09:35:20 +0000 |
commit | c30a6232df03e1efbd9f3b226777b07e087a1122 (patch) | |
tree | e992f45784689f373bcc38d1b79a239ebe17ee23 /chromium/content/browser/webauth | |
parent | 7b5b123ac58f58ffde0f4f6e488bcd09aa4decd3 (diff) | |
download | qtwebengine-chromium-85-based.tar.gz |
BASELINE: Update Chromium to 85.0.4183.14085-based
Change-Id: Iaa42f4680837c57725b1344f108c0196741f6057
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/content/browser/webauth')
5 files changed, 535 insertions, 504 deletions
diff --git a/chromium/content/browser/webauth/authenticator_common.cc b/chromium/content/browser/webauth/authenticator_common.cc index 6a5570ccd99..a90df64c68b 100644 --- a/chromium/content/browser/webauth/authenticator_common.cc +++ b/chromium/content/browser/webauth/authenticator_common.cc @@ -36,7 +36,6 @@ #include "content/public/common/origin_util.h" #include "crypto/sha2.h" #include "device/base/features.h" -#include "device/bluetooth/bluetooth_adapter_factory.h" #include "device/fido/attestation_statement.h" #include "device/fido/ctap_make_credential_request.h" #include "device/fido/features.h" @@ -45,6 +44,7 @@ #include "device/fido/fido_transport_protocol.h" #include "device/fido/get_assertion_request_handler.h" #include "device/fido/make_credential_request_handler.h" +#include "device/fido/public_key.h" #include "device/fido/public_key_credential_descriptor.h" #include "device/fido/public_key_credential_params.h" #include "net/base/registry_controlled_domains/registry_controlled_domain.h" @@ -318,6 +318,9 @@ CreateMakeCredentialResponse( auto common_info = blink::mojom::CommonCredentialInfo::New(); common_info->client_data_json.assign(client_data_json.begin(), client_data_json.end()); + common_info->authenticator_data = response_data.attestation_object() + .authenticator_data() + .SerializeToByteArray(); if (response_data.android_client_data_ext()) { DCHECK(base::FeatureList::IsEnabled(device::kWebAuthPhoneSupport)); common_info->client_data_json = *response_data.android_client_data_ext(); @@ -373,6 +376,17 @@ CreateMakeCredentialResponse( response->attestation_object = response_data.GetCBOREncodedAttestationObject(); + const device::PublicKey* public_key = response_data.attestation_object() + .authenticator_data() + .attested_data() + ->public_key(); + response->public_key_algo = public_key->algorithm; + const base::Optional<std::vector<uint8_t>>& public_key_der = + public_key->der_bytes; + if (public_key_der) { + response->public_key_der.emplace(public_key_der.value()); + } + return response; } @@ -391,7 +405,7 @@ blink::mojom::GetAssertionAuthenticatorResponsePtr CreateGetAssertionResponse( common_info->raw_id = response_data.raw_credential_id(); common_info->id = response_data.GetId(); response->info = std::move(common_info); - response->authenticator_data = + response->info->authenticator_data = response_data.auth_data().SerializeToByteArray(); response->signature = response_data.signature(); if (echo_appid_extension) { @@ -456,14 +470,6 @@ base::flat_set<device::FidoTransportProtocol> GetAvailableTransports( {device::FidoTransportProtocol::kUsbHumanInterfaceDevice}); } - // Try all transports if the FidoDiscoveryFactory has been injected in tests - // or via the testing API. - if (AuthenticatorEnvironmentImpl::GetInstance()->GetDiscoveryFactoryOverride( - static_cast<RenderFrameHostImpl*>(render_frame_host) - ->frame_tree_node())) { - return device::GetAllTransportProtocols(); - } - base::flat_set<device::FidoTransportProtocol> transports; transports.insert(device::FidoTransportProtocol::kUsbHumanInterfaceDevice); @@ -471,25 +477,28 @@ base::flat_set<device::FidoTransportProtocol> GetAvailableTransports( AuthenticatorEnvironmentImpl::GetInstance()->GetDiscoveryFactoryOverride( static_cast<RenderFrameHostImpl*>(render_frame_host) ->frame_tree_node()); - if (!discovery_factory) { - discovery_factory = delegate->GetDiscoveryFactory(); - } - - // Don't instantiate a platform discovery in contexts where IsUVPAA() would - // return false. This avoids platform authenticators mistakenly being - // available when e.g. an embedder provided implementation of - // IsUserVerifyingPlatformAuthenticatorAvailableOverride() returned false. - if (IsUserVerifyingPlatformAuthenticatorAvailableImpl( - delegate, discovery_factory, - content::WebContents::FromRenderFrameHost(render_frame_host) - ->GetBrowserContext())) { + if (discovery_factory) { + // The desktop implementation does not support BLE or NFC, but we emulate + // them if the testing API is enabled. + transports.insert(device::FidoTransportProtocol::kBluetoothLowEnergy); + transports.insert(device::FidoTransportProtocol::kNearFieldCommunication); + + // Instantiate a virtual platform discovery regardless of IsUVPAA() to + // support non-uv, platform authenticators. transports.insert(device::FidoTransportProtocol::kInternal); - } + } else { + discovery_factory = delegate->GetDiscoveryFactory(); - // FIXME(martinkr): Check whether this can be moved in front of the BLE - // adapter enumeration logic in FidoRequestHandlerBase. - if (!device::BluetoothAdapterFactory::Get()->IsLowEnergySupported()) { - return transports; + // Don't instantiate a platform discovery in contexts where IsUVPAA() would + // return false. This avoids platform authenticators mistakenly being + // available when e.g. an embedder provided implementation of + // IsUserVerifyingPlatformAuthenticatorAvailableOverride() returned false. + if (IsUserVerifyingPlatformAuthenticatorAvailableImpl( + delegate, discovery_factory, + content::WebContents::FromRenderFrameHost(render_frame_host) + ->GetBrowserContext())) { + transports.insert(device::FidoTransportProtocol::kInternal); + } } if (base::FeatureList::IsEnabled(features::kWebAuthCable) || @@ -986,8 +995,9 @@ void AuthenticatorCommon::GetAssertion( if (options->appid) { app_id_ = ProcessAppIdExtension(*options->appid, caller_origin_); if (!app_id_) { - std::move(callback).Run(blink::mojom::AuthenticatorStatus::INVALID_DOMAIN, - nullptr); + InvokeCallbackAndCleanup( + std::move(callback), + blink::mojom::AuthenticatorStatus::INVALID_DOMAIN); return; } } @@ -1123,6 +1133,13 @@ void AuthenticatorCommon::OnRegisterResponse( kAuthenticatorMissingUserVerification, blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR); return; + case device::MakeCredentialStatus::kNoCommonAlgorithms: + SignalFailureToRequestDelegate( + authenticator, + AuthenticatorRequestClientDelegate::InterestingFailureReason:: + kNoCommonAlgorithms, + blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR); + return; case device::MakeCredentialStatus::kStorageFull: SignalFailureToRequestDelegate( authenticator, diff --git a/chromium/content/browser/webauth/authenticator_impl_unittest.cc b/chromium/content/browser/webauth/authenticator_impl_unittest.cc index ed897583fce..54b6cf0b20a 100644 --- a/chromium/content/browser/webauth/authenticator_impl_unittest.cc +++ b/chromium/content/browser/webauth/authenticator_impl_unittest.cc @@ -15,9 +15,10 @@ #include "base/bind.h" #include "base/bind_helpers.h" #include "base/compiler_specific.h" -#include "base/json/json_parser.h" +#include "base/json/json_reader.h" #include "base/json/json_writer.h" #include "base/run_loop.h" +#include "base/stl_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/system/sys_info.h" @@ -59,6 +60,8 @@ #include "mojo/public/cpp/bindings/remote.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/boringssl/src/include/openssl/bytestring.h" +#include "third_party/boringssl/src/include/openssl/evp.h" #include "url/url_util.h" #if defined(OS_MACOSX) @@ -124,10 +127,6 @@ constexpr char kCryptotokenOrigin[] = constexpr char kTestExtensionOrigin[] = "chrome-extension://abcdefghijklmnopqrstuvwxyzabcdef"; -// Test data. CBOR test data can be built using the given -// diagnostic strings and the utility at "http://CBOR.me/". -constexpr int32_t kCoseEs256 = -7; - constexpr uint8_t kTestChallengeBytes[] = { 0x68, 0x71, 0x34, 0x96, 0x82, 0x22, 0xEC, 0x17, 0x20, 0x2E, 0x42, 0x50, 0x5F, 0x8E, 0xD2, 0xB1, 0x6A, 0xE2, 0x2F, 0x16, 0xBB, 0x05, @@ -344,8 +343,8 @@ GetTestPublicKeyCredentialCreationOptions() { auto options = PublicKeyCredentialCreationOptions::New(); options->relying_party = GetTestPublicKeyCredentialRPEntity(); options->user = GetTestPublicKeyCredentialUserEntity(); - options->public_key_parameters = - GetTestPublicKeyCredentialParameters(kCoseEs256); + options->public_key_parameters = GetTestPublicKeyCredentialParameters( + static_cast<int32_t>(device::CoseAlgorithmIdentifier::kEs256)); options->challenge.assign(32, 0x0A); options->timeout = base::TimeDelta::FromMinutes(1); options->authenticator_selection = GetTestAuthenticatorSelectionCriteria(); @@ -376,12 +375,27 @@ std::vector<device::CableDiscoveryData> GetTestCableExtension() { return ret; } +device::AuthenticatorData AuthDataFromMakeCredentialResponse( + const MakeCredentialAuthenticatorResponsePtr& response) { + base::Optional<Value> attestation_value = + Reader::Read(response->attestation_object); + CHECK(attestation_value); + const auto& attestation = attestation_value->GetMap(); + + const auto auth_data_it = attestation.find(Value(device::kAuthDataKey)); + CHECK(auth_data_it != attestation.end()); + const std::vector<uint8_t>& auth_data = auth_data_it->second.GetBytestring(); + base::Optional<device::AuthenticatorData> parsed_auth_data = + device::AuthenticatorData::DecodeAuthenticatorData(auth_data); + return std::move(parsed_auth_data.value()); +} + } // namespace class AuthenticatorTestBase : public content::RenderViewHostTestHarness { protected: AuthenticatorTestBase() = default; - ~AuthenticatorTestBase() override {} + ~AuthenticatorTestBase() override = default; void SetUp() override { content::RenderViewHostTestHarness::SetUp(); @@ -441,9 +455,9 @@ class AuthenticatorImplTest : public AuthenticatorTestBase { mojo::Remote<blink::mojom::Authenticator> ConnectToAuthenticator( std::unique_ptr<base::OneShotTimer> timer) { - authenticator_impl_.reset(new AuthenticatorImpl( + authenticator_impl_ = std::make_unique<AuthenticatorImpl>( main_rfh(), - std::make_unique<AuthenticatorCommon>(main_rfh(), std::move(timer)))); + std::make_unique<AuthenticatorCommon>(main_rfh(), std::move(timer))); mojo::Remote<blink::mojom::Authenticator> authenticator; authenticator_impl_->Bind(authenticator.BindNewPipeAndPassReceiver()); return authenticator; @@ -451,8 +465,6 @@ class AuthenticatorImplTest : public AuthenticatorTestBase { mojo::Remote<blink::mojom::Authenticator> ConstructAuthenticatorWithTimer( scoped_refptr<base::TestMockTimeTaskRunner> task_runner) { - fake_hid_manager_ = std::make_unique<device::FakeFidoHidManager>(); - // Set up a timer for testing. auto timer = std::make_unique<base::OneShotTimer>(task_runner->GetMockTickClock()); @@ -531,7 +543,6 @@ class AuthenticatorImplTest : public AuthenticatorTestBase { protected: std::unique_ptr<AuthenticatorImpl> authenticator_impl_; - std::unique_ptr<device::FakeFidoHidManager> fake_hid_manager_; base::Optional<base::test::ScopedFeatureList> scoped_feature_list_; std::unique_ptr<device::BluetoothAdapterFactory::GlobalValuesForTesting> bluetooth_global_values_ = @@ -605,9 +616,14 @@ TEST_F(AuthenticatorImplTest, ClientDataJSONSerialization) { // Verify behavior for various combinations of origins and RP IDs. TEST_F(AuthenticatorImplTest, MakeCredentialOriginAndRpIds) { - // These instances should return security errors (for circumstances - // that would normally crash the renderer). - for (auto test_case : kInvalidRelyingPartyTestCases) { + std::vector<OriginClaimedAuthorityPair> tests( + &kValidRelyingPartyTestCases[0], + &kValidRelyingPartyTestCases[base::size(kValidRelyingPartyTestCases)]); + tests.insert(tests.end(), &kInvalidRelyingPartyTestCases[0], + &kInvalidRelyingPartyTestCases[base::size( + kInvalidRelyingPartyTestCases)]); + + for (const auto& test_case : tests) { SCOPED_TRACE(std::string(test_case.claimed_authority) + " " + std::string(test_case.origin)); @@ -623,164 +639,56 @@ TEST_F(AuthenticatorImplTest, MakeCredentialOriginAndRpIds) { callback_receiver.WaitForCallback(); EXPECT_EQ(test_case.expected_status, callback_receiver.status()); } - - // These instances time out with NOT_ALLOWED_ERROR due to unsupported - // algorithm. - for (auto test_case : kValidRelyingPartyTestCases) { - SCOPED_TRACE(std::string(test_case.claimed_authority) + " " + - std::string(test_case.origin)); - - NavigateAndCommit(GURL(test_case.origin)); - auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>( - base::Time::Now(), base::TimeTicks::Now()); - auto authenticator = ConstructAuthenticatorWithTimer(task_runner); - - PublicKeyCredentialCreationOptionsPtr options = - GetTestPublicKeyCredentialCreationOptions(); - options->relying_party.id = test_case.claimed_authority; - options->public_key_parameters = GetTestPublicKeyCredentialParameters(123); - - TestMakeCredentialCallback callback_receiver; - authenticator->MakeCredential(std::move(options), - callback_receiver.callback()); - // Trigger timer. - base::RunLoop().RunUntilIdle(); - task_runner->FastForwardBy(base::TimeDelta::FromMinutes(1)); - callback_receiver.WaitForCallback(); - EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR, - callback_receiver.status()); - } } -// Test that MakeCredential request returns INVALID_ICON_URL if the RP or user -// icon URLs are not a priori-authenticated URLs. -TEST_F(AuthenticatorImplTest, MakeCredentialInvalidIconUrl) { - SimulateNavigation(GURL(kTestOrigin1)); - const GURL kInvalidIconUrlTestCases[] = { - GURL("http://insecure-origin.com/kitten.png"), - GURL("invalid:/url"), - }; - - // Test relying party icons. - for (auto test_case : kInvalidIconUrlTestCases) { - SCOPED_TRACE(test_case.possibly_invalid_spec()); - mojo::Remote<blink::mojom::Authenticator> authenticator = - ConnectToAuthenticator(); - PublicKeyCredentialCreationOptionsPtr options = - GetTestPublicKeyCredentialCreationOptions(); - options->relying_party.icon_url = test_case; - TestMakeCredentialCallback callback_receiver; - authenticator->MakeCredential(std::move(options), - callback_receiver.callback()); - callback_receiver.WaitForCallback(); - EXPECT_EQ(AuthenticatorStatus::INVALID_ICON_URL, - callback_receiver.status()); - } - - // Test user icons. - for (auto test_case : kInvalidIconUrlTestCases) { - SCOPED_TRACE(test_case.possibly_invalid_spec()); - mojo::Remote<blink::mojom::Authenticator> authenticator = - ConnectToAuthenticator(); - PublicKeyCredentialCreationOptionsPtr options = - GetTestPublicKeyCredentialCreationOptions(); - options->user.icon_url = test_case; - TestMakeCredentialCallback callback_receiver; - authenticator->MakeCredential(std::move(options), - callback_receiver.callback()); - callback_receiver.WaitForCallback(); - EXPECT_EQ(AuthenticatorStatus::INVALID_ICON_URL, - callback_receiver.status()); - } -} - -// Test that MakeCredential request does not return INVALID_ICON_URL for a -// priori-authenticated URLs. -TEST_F(AuthenticatorImplTest, MakeCredentialValidIconUrl) { - const GURL kValidUrlTestCases[] = { - GURL(), - GURL("https://secure-origin.com/kitten.png"), - GURL("about:blank"), - GURL("about:srcdoc"), - GURL("data:image/" +// Test that MakeCredential returns INVALID_ICON_URL in the correct cases. +TEST_F(AuthenticatorImplTest, MakeCredentialURLs) { + constexpr auto ok = AuthenticatorStatus::SUCCESS; + constexpr auto bad = AuthenticatorStatus::INVALID_ICON_URL; + const std::pair<GURL, AuthenticatorStatus> kTestCases[] = { + {GURL(), ok}, + {GURL("https://secure-origin.com/kitten.png"), ok}, + {GURL("about:blank"), ok}, + {GURL("about:srcdoc"), ok}, + {GURL( + "data:image/" "png;base64," "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAACXBIWXMAAC4jAAAuIwF" "4pT92AAAAB3RJTUUH4wYUETEs5V5U8gAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdG" "ggR0lNUFeBDhcAAABGSURBVCjPY/z//" "z8DKYAJmcPYyICHi0UDyTYMDg2MFIUSnsAZAp5mbGT4X49DBcxLEAUsBMxrRCiFABb8" "gYNpLTXiAT8AAEeHFZvhj9g8AAAAAElFTkSuQmCC"), - }; - SimulateNavigation(GURL(kTestOrigin1)); - - // Test relying party icons. - for (auto test_case : kValidUrlTestCases) { - SCOPED_TRACE(test_case.possibly_invalid_spec()); - auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>( - base::Time::Now(), base::TimeTicks::Now()); - auto authenticator = ConstructAuthenticatorWithTimer(task_runner); + ok}, - PublicKeyCredentialCreationOptionsPtr options = - GetTestPublicKeyCredentialCreationOptions(); - options->public_key_parameters = GetTestPublicKeyCredentialParameters(123); - options->relying_party.icon_url = test_case; + {GURL("http://insecure-origin.com/kitten.png"), bad}, + {GURL("invalid:/url"), bad}, + }; - TestMakeCredentialCallback callback_receiver; - authenticator->MakeCredential(std::move(options), - callback_receiver.callback()); - // Trigger timer. - base::RunLoop().RunUntilIdle(); - task_runner->FastForwardBy(base::TimeDelta::FromMinutes(1)); - callback_receiver.WaitForCallback(); - EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR, - callback_receiver.status()); - } + SimulateNavigation(GURL(kTestOrigin1)); + auto authenticator = ConnectToAuthenticator(); - // Test user icons. - for (auto test_case : kValidUrlTestCases) { - SCOPED_TRACE(test_case.possibly_invalid_spec()); - auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>( - base::Time::Now(), base::TimeTicks::Now()); - auto authenticator = ConstructAuthenticatorWithTimer(task_runner); + for (const bool test_user_icon : {false, true}) { + for (auto test_case : kTestCases) { + SCOPED_TRACE(test_case.first.possibly_invalid_spec()); + SCOPED_TRACE(test_user_icon); - PublicKeyCredentialCreationOptionsPtr options = - GetTestPublicKeyCredentialCreationOptions(); - options->public_key_parameters = GetTestPublicKeyCredentialParameters(123); - options->user.icon_url = test_case; + PublicKeyCredentialCreationOptionsPtr options = + GetTestPublicKeyCredentialCreationOptions(); + if (test_user_icon) { + options->user.icon_url = test_case.first; + } else { + options->relying_party.icon_url = test_case.first; + } - TestMakeCredentialCallback callback_receiver; - authenticator->MakeCredential(std::move(options), - callback_receiver.callback()); - // Trigger timer. - base::RunLoop().RunUntilIdle(); - task_runner->FastForwardBy(base::TimeDelta::FromMinutes(1)); - callback_receiver.WaitForCallback(); - EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR, - callback_receiver.status()); + TestMakeCredentialCallback callback_receiver; + authenticator->MakeCredential(std::move(options), + callback_receiver.callback()); + callback_receiver.WaitForCallback(); + EXPECT_EQ(test_case.second, callback_receiver.status()); + } } } -// Test that MakeCredential request times out with NOT_ALLOWED_ERROR if no -// parameters contain a supported algorithm. -TEST_F(AuthenticatorImplTest, MakeCredentialNoSupportedAlgorithm) { - SimulateNavigation(GURL(kTestOrigin1)); - auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>( - base::Time::Now(), base::TimeTicks::Now()); - auto authenticator = ConstructAuthenticatorWithTimer(task_runner); - - PublicKeyCredentialCreationOptionsPtr options = - GetTestPublicKeyCredentialCreationOptions(); - options->public_key_parameters = GetTestPublicKeyCredentialParameters(123); - - TestMakeCredentialCallback callback_receiver; - authenticator->MakeCredential(std::move(options), - callback_receiver.callback()); - // Trigger timer. - base::RunLoop().RunUntilIdle(); - task_runner->FastForwardBy(base::TimeDelta::FromMinutes(1)); - callback_receiver.WaitForCallback(); - EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR, callback_receiver.status()); -} - // Test that MakeCredential request times out with NOT_ALLOWED_ERROR if user // verification is required for U2F devices. TEST_F(AuthenticatorImplTest, MakeCredentialUserVerification) { @@ -916,39 +824,6 @@ TEST_F(AuthenticatorImplTest, TestMakeCredentialTimeout) { EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR, callback_receiver.status()); } -TEST_F(AuthenticatorImplTest, TestMakeCredentialRSA) { - virtual_device_factory_->SetSupportedProtocol( - device::ProtocolVersion::kCtap2); - SimulateNavigation(GURL(kTestOrigin1)); - - mojo::Remote<blink::mojom::Authenticator> authenticator = - ConnectToAuthenticator(); - PublicKeyCredentialCreationOptionsPtr options = - GetTestPublicKeyCredentialCreationOptions(); - options->public_key_parameters = GetTestPublicKeyCredentialParameters( - static_cast<int32_t>(device::CoseAlgorithmIdentifier::kCoseRs256)); - TestMakeCredentialCallback callback; - authenticator->MakeCredential(std::move(options), callback.callback()); - base::RunLoop().RunUntilIdle(); - callback.WaitForCallback(); - - ASSERT_EQ(callback.status(), AuthenticatorStatus::SUCCESS); - - base::Optional<Value> attestation_value = - Reader::Read(callback.value()->attestation_object); - ASSERT_TRUE(attestation_value); - ASSERT_TRUE(attestation_value->is_map()); - const auto& attestation = attestation_value->GetMap(); - - const auto auth_data_it = attestation.find(Value("authData")); - ASSERT_TRUE(auth_data_it != attestation.end()); - ASSERT_TRUE(auth_data_it->second.is_bytestring()); - auto auth_data = device::AuthenticatorData::DecodeAuthenticatorData( - auth_data_it->second.GetBytestring()); - EXPECT_EQ(static_cast<int32_t>(device::CoseAlgorithmIdentifier::kCoseRs256), - auth_data->attested_data()->public_key()->algorithm()); -} - // Verify behavior for various combinations of origins and RP IDs. TEST_F(AuthenticatorImplTest, GetAssertionOriginAndRpIds) { // These instances should return security errors (for circumstances @@ -1871,9 +1746,9 @@ TEST_F(OverrideRPIDAuthenticatorTest, ChromeExtensions) { } } -enum class IndividualAttestation { - REQUESTED, - NOT_REQUESTED, +enum class EnterprisePolicy { + LISTED, + NOT_LISTED, }; enum class AttestationConsent { @@ -1912,17 +1787,17 @@ class TestAuthenticatorRequestDelegate TestAuthenticatorRequestDelegate( RenderFrameHost* render_frame_host, base::OnceClosure action_callbacks_registered_callback, - IndividualAttestation individual_attestation, + EnterprisePolicy enterprise_policy, AttestationConsent attestation_consent, bool is_focused, bool is_uvpaa) : action_callbacks_registered_callback_( std::move(action_callbacks_registered_callback)), - individual_attestation_(individual_attestation), + enterprise_policy_(enterprise_policy), attestation_consent_(attestation_consent), is_focused_(is_focused), is_uvpaa_(is_uvpaa) {} - ~TestAuthenticatorRequestDelegate() override {} + ~TestAuthenticatorRequestDelegate() override = default; void RegisterActionCallbacks( base::OnceClosure cancel_callback, @@ -1937,7 +1812,7 @@ class TestAuthenticatorRequestDelegate bool ShouldPermitIndividualAttestation( const std::string& relying_party_id) override { - return individual_attestation_ == IndividualAttestation::REQUESTED; + return enterprise_policy_ == EnterprisePolicy::LISTED; } void ShouldReturnAttestation( @@ -1968,7 +1843,7 @@ class TestAuthenticatorRequestDelegate base::OnceClosure action_callbacks_registered_callback_; base::Optional<base::OnceClosure> cancel_callback_; - const IndividualAttestation individual_attestation_; + const EnterprisePolicy enterprise_policy_; const AttestationConsent attestation_consent_; const bool is_focused_; const bool is_uvpaa_; @@ -1989,15 +1864,14 @@ class TestAuthenticatorContentBrowserClient : public ContentBrowserClient { action_callbacks_registered_callback ? std::move(action_callbacks_registered_callback) : base::DoNothing(), - individual_attestation, attestation_consent, is_focused, is_uvpaa); + enterprise_policy, attestation_consent, is_focused, is_uvpaa); } // If set, this closure will be called when the subsequently constructed // delegate is informed that the request has started. base::OnceClosure action_callbacks_registered_callback; - IndividualAttestation individual_attestation = - IndividualAttestation::NOT_REQUESTED; + EnterprisePolicy enterprise_policy = EnterprisePolicy::NOT_LISTED; AttestationConsent attestation_consent = AttestationConsent::DENIED; bool is_focused = true; @@ -2017,7 +1891,7 @@ class AuthenticatorContentBrowserClientTest : public AuthenticatorImplTest { struct TestCase { AttestationConveyancePreference attestation_requested; - IndividualAttestation individual_attestation; + EnterprisePolicy enterprise_policy; AttestationConsent attestation_consent; AuthenticatorStatus expected_status; AttestationType expected_attestation; @@ -2043,15 +1917,14 @@ class AuthenticatorContentBrowserClientTest : public AuthenticatorImplTest { SCOPED_TRACE(test.attestation_consent == AttestationConsent::GRANTED ? "consent granted" : "consent denied"); - SCOPED_TRACE(test.individual_attestation == - IndividualAttestation::REQUESTED + SCOPED_TRACE(test.enterprise_policy == EnterprisePolicy::LISTED ? "individual attestation" : "no individual attestation"); SCOPED_TRACE( AttestationConveyancePreferenceToString(test.attestation_requested)); SCOPED_TRACE(i); - test_client_.individual_attestation = test.individual_attestation; + test_client_.enterprise_policy = test.enterprise_policy; test_client_.attestation_consent = test.attestation_consent; PublicKeyCredentialCreationOptionsPtr options = @@ -2071,20 +1944,15 @@ class AuthenticatorContentBrowserClientTest : public AuthenticatorImplTest { continue; } + const device::AuthenticatorData auth_data = + AuthDataFromMakeCredentialResponse(callback_receiver.value()); + base::Optional<Value> attestation_value = Reader::Read(callback_receiver.value()->attestation_object); ASSERT_TRUE(attestation_value); ASSERT_TRUE(attestation_value->is_map()); const auto& attestation = attestation_value->GetMap(); - base::Optional<device::AuthenticatorData> auth_data = base::nullopt; - const auto auth_data_it = attestation.find(Value("authData")); - if (auth_data_it != attestation.end() && - auth_data_it->second.is_bytestring()) { - auth_data = device::AuthenticatorData::DecodeAuthenticatorData( - auth_data_it->second.GetBytestring()); - } - switch (test.expected_attestation) { case AttestationType::ANY: ASSERT_STREQ("", test.expected_certificate_substring); @@ -2093,13 +1961,13 @@ class AuthenticatorContentBrowserClientTest : public AuthenticatorImplTest { case AttestationType::NONE: ASSERT_STREQ("", test.expected_certificate_substring); ExpectMapHasKeyWithStringValue(attestation, "fmt", "none"); - EXPECT_TRUE(auth_data->attested_data()->IsAaguidZero()); + EXPECT_TRUE(auth_data.attested_data()->IsAaguidZero()); break; case AttestationType::NONE_WITH_NONZERO_AAGUID: ASSERT_STREQ("", test.expected_certificate_substring); ExpectMapHasKeyWithStringValue(attestation, "fmt", "none"); - EXPECT_FALSE(auth_data->attested_data()->IsAaguidZero()); + EXPECT_FALSE(auth_data.attested_data()->IsAaguidZero()); break; case AttestationType::U2F: @@ -2126,7 +1994,7 @@ class AuthenticatorContentBrowserClientTest : public AuthenticatorImplTest { attestation_statement.end()); ASSERT_TRUE(attestation_statement.find(Value("ecdaaKeyId")) == attestation_statement.end()); - EXPECT_TRUE(auth_data->attested_data()->IsAaguidZero()); + EXPECT_TRUE(auth_data.attested_data()->IsAaguidZero()); break; } case AttestationType::SELF_WITH_NONZERO_AAGUID: { @@ -2145,7 +2013,7 @@ class AuthenticatorContentBrowserClientTest : public AuthenticatorImplTest { attestation_statement.end()); ASSERT_TRUE(attestation_statement.find(Value("ecdaaKeyId")) == attestation_statement.end()); - EXPECT_FALSE(auth_data->attested_data()->IsAaguidZero()); + EXPECT_FALSE(auth_data.attested_data()->IsAaguidZero()); break; } } @@ -2221,7 +2089,7 @@ TEST_F(AuthenticatorContentBrowserClientTest, AttestationBehaviour) { const std::vector<TestCase> kTests = { { AttestationConveyancePreference::NONE, - IndividualAttestation::NOT_REQUESTED, + EnterprisePolicy::NOT_LISTED, AttestationConsent::DENIED, AuthenticatorStatus::SUCCESS, AttestationType::NONE, @@ -2229,7 +2097,7 @@ TEST_F(AuthenticatorContentBrowserClientTest, AttestationBehaviour) { }, { AttestationConveyancePreference::NONE, - IndividualAttestation::REQUESTED, + EnterprisePolicy::LISTED, AttestationConsent::DENIED, AuthenticatorStatus::SUCCESS, AttestationType::NONE, @@ -2237,7 +2105,7 @@ TEST_F(AuthenticatorContentBrowserClientTest, AttestationBehaviour) { }, { AttestationConveyancePreference::INDIRECT, - IndividualAttestation::NOT_REQUESTED, + EnterprisePolicy::NOT_LISTED, AttestationConsent::DENIED, AuthenticatorStatus::SUCCESS, AttestationType::NONE, @@ -2245,7 +2113,7 @@ TEST_F(AuthenticatorContentBrowserClientTest, AttestationBehaviour) { }, { AttestationConveyancePreference::INDIRECT, - IndividualAttestation::REQUESTED, + EnterprisePolicy::LISTED, AttestationConsent::DENIED, AuthenticatorStatus::SUCCESS, AttestationType::NONE, @@ -2253,7 +2121,7 @@ TEST_F(AuthenticatorContentBrowserClientTest, AttestationBehaviour) { }, { AttestationConveyancePreference::INDIRECT, - IndividualAttestation::NOT_REQUESTED, + EnterprisePolicy::NOT_LISTED, AttestationConsent::GRANTED, AuthenticatorStatus::SUCCESS, AttestationType::U2F, @@ -2261,7 +2129,7 @@ TEST_F(AuthenticatorContentBrowserClientTest, AttestationBehaviour) { }, { AttestationConveyancePreference::INDIRECT, - IndividualAttestation::REQUESTED, + EnterprisePolicy::LISTED, AttestationConsent::GRANTED, AuthenticatorStatus::SUCCESS, AttestationType::U2F, @@ -2269,7 +2137,7 @@ TEST_F(AuthenticatorContentBrowserClientTest, AttestationBehaviour) { }, { AttestationConveyancePreference::DIRECT, - IndividualAttestation::NOT_REQUESTED, + EnterprisePolicy::NOT_LISTED, AttestationConsent::DENIED, AuthenticatorStatus::SUCCESS, AttestationType::NONE, @@ -2277,7 +2145,7 @@ TEST_F(AuthenticatorContentBrowserClientTest, AttestationBehaviour) { }, { AttestationConveyancePreference::DIRECT, - IndividualAttestation::REQUESTED, + EnterprisePolicy::LISTED, AttestationConsent::DENIED, AuthenticatorStatus::SUCCESS, AttestationType::NONE, @@ -2285,7 +2153,7 @@ TEST_F(AuthenticatorContentBrowserClientTest, AttestationBehaviour) { }, { AttestationConveyancePreference::DIRECT, - IndividualAttestation::NOT_REQUESTED, + EnterprisePolicy::NOT_LISTED, AttestationConsent::GRANTED, AuthenticatorStatus::SUCCESS, AttestationType::U2F, @@ -2293,7 +2161,7 @@ TEST_F(AuthenticatorContentBrowserClientTest, AttestationBehaviour) { }, { AttestationConveyancePreference::DIRECT, - IndividualAttestation::REQUESTED, + EnterprisePolicy::LISTED, AttestationConsent::GRANTED, AuthenticatorStatus::SUCCESS, AttestationType::U2F, @@ -2301,7 +2169,7 @@ TEST_F(AuthenticatorContentBrowserClientTest, AttestationBehaviour) { }, { AttestationConveyancePreference::ENTERPRISE, - IndividualAttestation::NOT_REQUESTED, + EnterprisePolicy::NOT_LISTED, AttestationConsent::DENIED, AuthenticatorStatus::SUCCESS, AttestationType::NONE, @@ -2309,7 +2177,7 @@ TEST_F(AuthenticatorContentBrowserClientTest, AttestationBehaviour) { }, { AttestationConveyancePreference::ENTERPRISE, - IndividualAttestation::REQUESTED, + EnterprisePolicy::LISTED, AttestationConsent::DENIED, AuthenticatorStatus::SUCCESS, AttestationType::NONE, @@ -2317,7 +2185,7 @@ TEST_F(AuthenticatorContentBrowserClientTest, AttestationBehaviour) { }, { AttestationConveyancePreference::ENTERPRISE, - IndividualAttestation::NOT_REQUESTED, + EnterprisePolicy::NOT_LISTED, AttestationConsent::GRANTED, AuthenticatorStatus::SUCCESS, AttestationType::U2F, @@ -2325,7 +2193,7 @@ TEST_F(AuthenticatorContentBrowserClientTest, AttestationBehaviour) { }, { AttestationConveyancePreference::ENTERPRISE, - IndividualAttestation::REQUESTED, + EnterprisePolicy::LISTED, AttestationConsent::GRANTED, AuthenticatorStatus::SUCCESS, AttestationType::U2F, @@ -2351,7 +2219,7 @@ TEST_F(AuthenticatorContentBrowserClientTest, const std::vector<TestCase> kTests = { { AttestationConveyancePreference::ENTERPRISE, - IndividualAttestation::NOT_REQUESTED, + EnterprisePolicy::NOT_LISTED, AttestationConsent::DENIED, AuthenticatorStatus::SUCCESS, AttestationType::NONE, @@ -2359,7 +2227,7 @@ TEST_F(AuthenticatorContentBrowserClientTest, }, { AttestationConveyancePreference::DIRECT, - IndividualAttestation::NOT_REQUESTED, + EnterprisePolicy::NOT_LISTED, AttestationConsent::GRANTED, AuthenticatorStatus::SUCCESS, // If individual attestation was not requested then the attestation @@ -2370,7 +2238,7 @@ TEST_F(AuthenticatorContentBrowserClientTest, }, { AttestationConveyancePreference::ENTERPRISE, - IndividualAttestation::NOT_REQUESTED, + EnterprisePolicy::NOT_LISTED, AttestationConsent::GRANTED, AuthenticatorStatus::SUCCESS, // If individual attestation was not requested then the attestation @@ -2382,7 +2250,7 @@ TEST_F(AuthenticatorContentBrowserClientTest, { AttestationConveyancePreference::ENTERPRISE, - IndividualAttestation::REQUESTED, + EnterprisePolicy::LISTED, AttestationConsent::GRANTED, AuthenticatorStatus::SUCCESS, AttestationType::U2F, @@ -2420,7 +2288,7 @@ TEST_F(AuthenticatorContentBrowserClientTest, // attestation is requested, the self-attestation will be removed but, // because the transport is kInternal, the AAGUID will be preserved. AttestationConveyancePreference::NONE, - IndividualAttestation::NOT_REQUESTED, + EnterprisePolicy::NOT_LISTED, AttestationConsent::DENIED, AuthenticatorStatus::SUCCESS, AttestationType::NONE_WITH_NONZERO_AAGUID, @@ -2431,7 +2299,7 @@ TEST_F(AuthenticatorContentBrowserClientTest, // attestation. But because the transport is kInternal, the AAGUID // will be preserved. AttestationConveyancePreference::DIRECT, - IndividualAttestation::NOT_REQUESTED, + EnterprisePolicy::NOT_LISTED, AttestationConsent::DENIED, AuthenticatorStatus::SUCCESS, AttestationType::NONE_WITH_NONZERO_AAGUID, @@ -2441,7 +2309,7 @@ TEST_F(AuthenticatorContentBrowserClientTest, // If attestation is requested and granted, the self attestation // will be returned. AttestationConveyancePreference::DIRECT, - IndividualAttestation::NOT_REQUESTED, + EnterprisePolicy::NOT_LISTED, AttestationConsent::GRANTED, AuthenticatorStatus::SUCCESS, AttestationType::SELF_WITH_NONZERO_AAGUID, @@ -2463,7 +2331,7 @@ TEST_F(AuthenticatorContentBrowserClientTest, Ctap2SelfAttestation) { // If no attestation is requested, we'll return the self attestation // rather than erasing it. AttestationConveyancePreference::NONE, - IndividualAttestation::NOT_REQUESTED, + EnterprisePolicy::NOT_LISTED, AttestationConsent::DENIED, AuthenticatorStatus::SUCCESS, AttestationType::SELF, @@ -2473,7 +2341,7 @@ TEST_F(AuthenticatorContentBrowserClientTest, Ctap2SelfAttestation) { // If attestation is requested, but denied, we'll return none // attestation. AttestationConveyancePreference::DIRECT, - IndividualAttestation::NOT_REQUESTED, + EnterprisePolicy::NOT_LISTED, AttestationConsent::DENIED, AuthenticatorStatus::SUCCESS, AttestationType::NONE, @@ -2483,7 +2351,7 @@ TEST_F(AuthenticatorContentBrowserClientTest, Ctap2SelfAttestation) { // If attestation is requested and granted, the self attestation will // be returned. AttestationConveyancePreference::DIRECT, - IndividualAttestation::NOT_REQUESTED, + EnterprisePolicy::NOT_LISTED, AttestationConsent::GRANTED, AuthenticatorStatus::SUCCESS, AttestationType::SELF, @@ -2509,7 +2377,7 @@ TEST_F(AuthenticatorContentBrowserClientTest, // self-attestation should still be replaced with a "none" // attestation. AttestationConveyancePreference::NONE, - IndividualAttestation::NOT_REQUESTED, + EnterprisePolicy::NOT_LISTED, AttestationConsent::DENIED, AuthenticatorStatus::SUCCESS, AttestationType::NONE, @@ -2526,62 +2394,62 @@ TEST_F(AuthenticatorContentBrowserClientTest, BlockedAttestation) { static constexpr struct { const char* domains; AttestationConveyancePreference attestation; - IndividualAttestation individual_attestation; + EnterprisePolicy enterprise_policy; AttestationType result; } kTests[] = { // Empty or nonsense parameter doesn't block anything. { "", AttestationConveyancePreference::DIRECT, - IndividualAttestation::NOT_REQUESTED, + EnterprisePolicy::NOT_LISTED, AttestationType::U2F, }, { " ,, ,, ", AttestationConveyancePreference::DIRECT, - IndividualAttestation::NOT_REQUESTED, + EnterprisePolicy::NOT_LISTED, AttestationType::U2F, }, // Direct listing of domain blocks... { "foo.example.com", AttestationConveyancePreference::DIRECT, - IndividualAttestation::NOT_REQUESTED, + EnterprisePolicy::NOT_LISTED, AttestationType::NONE, }, // ... unless attestation is permitted by policy. { "foo.example.com", AttestationConveyancePreference::DIRECT, - IndividualAttestation::REQUESTED, + EnterprisePolicy::LISTED, AttestationType::U2F, }, // Additional stuff in the string doesn't break the blocking. { "other,foo.example.com,,nonsenseXYZ123", AttestationConveyancePreference::DIRECT, - IndividualAttestation::NOT_REQUESTED, + EnterprisePolicy::NOT_LISTED, AttestationType::NONE, }, // The whole domain can be blocked. { "(*.)example.com", AttestationConveyancePreference::DIRECT, - IndividualAttestation::NOT_REQUESTED, + EnterprisePolicy::NOT_LISTED, AttestationType::NONE, }, // Policy again overrides { "(*.)example.com", AttestationConveyancePreference::DIRECT, - IndividualAttestation::REQUESTED, + EnterprisePolicy::LISTED, AttestationType::U2F, }, // Trying to block everything doesn't work. { "(*.)", AttestationConveyancePreference::DIRECT, - IndividualAttestation::NOT_REQUESTED, + EnterprisePolicy::NOT_LISTED, AttestationType::U2F, }, }; @@ -2601,7 +2469,7 @@ TEST_F(AuthenticatorContentBrowserClientTest, BlockedAttestation) { const std::vector<TestCase> kTestCase = { { test.attestation, - test.individual_attestation, + test.enterprise_policy, AttestationConsent::GRANTED, AuthenticatorStatus::SUCCESS, test.result, @@ -2656,16 +2524,13 @@ TEST_F(AuthenticatorContentBrowserClientTest, Unfocused) { ConnectToAuthenticator(); { - PublicKeyCredentialCreationOptionsPtr options = - GetTestPublicKeyCredentialCreationOptions(); - options->public_key_parameters = GetTestPublicKeyCredentialParameters(123); - TestMakeCredentialCallback cb; TestRequestStartedCallback request_started; test_client_.action_callbacks_registered_callback = request_started.callback(); - authenticator->MakeCredential(std::move(options), cb.callback()); + authenticator->MakeCredential(GetTestPublicKeyCredentialCreationOptions(), + cb.callback()); cb.WaitForCallback(); EXPECT_EQ(AuthenticatorStatus::NOT_FOCUSED, cb.status()); @@ -2673,9 +2538,6 @@ TEST_F(AuthenticatorContentBrowserClientTest, Unfocused) { } { - PublicKeyCredentialRequestOptionsPtr options = - GetTestPublicKeyCredentialRequestOptions(); - device::PublicKeyCredentialDescriptor credential; credential.SetCredentialTypeForTesting(device::CredentialType::kPublicKey); credential.GetIdForTesting().resize(16); @@ -2684,6 +2546,8 @@ TEST_F(AuthenticatorContentBrowserClientTest, Unfocused) { ASSERT_TRUE(virtual_device_factory_->mutable_state()->InjectRegistration( credential.id(), kTestRelyingPartyId)); + PublicKeyCredentialRequestOptionsPtr options = + GetTestPublicKeyCredentialRequestOptions(); options->allow_credentials.emplace_back(credential); TestGetAssertionCallback cb; @@ -2805,13 +2669,13 @@ class MockAuthenticatorRequestDelegateObserver using InterestingFailureReasonCallback = base::OnceCallback<void(InterestingFailureReason)>; - MockAuthenticatorRequestDelegateObserver( + explicit MockAuthenticatorRequestDelegateObserver( InterestingFailureReasonCallback failure_reasons_callback = base::DoNothing()) : TestAuthenticatorRequestDelegate( nullptr /* render_frame_host */, base::DoNothing() /* did_start_request_callback */, - IndividualAttestation::NOT_REQUESTED, + EnterprisePolicy::NOT_LISTED, AttestationConsent::DENIED, true /* is_focused */, /*is_uvpaa=*/false), @@ -2868,8 +2732,8 @@ class FakeAuthenticatorCommon : public AuthenticatorCommon { class AuthenticatorImplRequestDelegateTest : public AuthenticatorImplTest { public: - AuthenticatorImplRequestDelegateTest() {} - ~AuthenticatorImplRequestDelegateTest() override {} + AuthenticatorImplRequestDelegateTest() = default; + ~AuthenticatorImplRequestDelegateTest() override = default; void TearDown() override { // The |RenderFrameHost| must outlive |AuthenticatorImpl|. @@ -2880,9 +2744,9 @@ class AuthenticatorImplRequestDelegateTest : public AuthenticatorImplTest { mojo::Remote<blink::mojom::Authenticator> ConnectToFakeAuthenticator( std::unique_ptr<MockAuthenticatorRequestDelegateObserver> delegate, std::unique_ptr<base::OneShotTimer> timer) { - authenticator_impl_.reset(new AuthenticatorImpl( + authenticator_impl_ = std::make_unique<AuthenticatorImpl>( main_rfh(), std::make_unique<FakeAuthenticatorCommon>( - main_rfh(), std::move(timer), std::move(delegate)))); + main_rfh(), std::move(timer), std::move(delegate))); mojo::Remote<blink::mojom::Authenticator> authenticator; authenticator_impl_->Bind(authenticator.BindNewPipeAndPassReceiver()); return authenticator; @@ -2891,8 +2755,6 @@ class AuthenticatorImplRequestDelegateTest : public AuthenticatorImplTest { mojo::Remote<blink::mojom::Authenticator> ConstructFakeAuthenticatorWithTimer( std::unique_ptr<MockAuthenticatorRequestDelegateObserver> delegate, scoped_refptr<base::TestMockTimeTaskRunner> task_runner) { - fake_hid_manager_ = std::make_unique<device::FakeFidoHidManager>(); - // Set up a timer for testing. auto timer = std::make_unique<base::OneShotTimer>(task_runner->GetMockTickClock()); @@ -3087,55 +2949,48 @@ TEST_F(AuthenticatorImplTest, ExtensionHMACSecret) { NavigateAndCommit(GURL(kTestOrigin1)); for (const bool include_extension : {false, true}) { - SCOPED_TRACE(include_extension); + for (const bool authenticator_support : {false, true}) { + SCOPED_TRACE(include_extension); + SCOPED_TRACE(authenticator_support); - virtual_device_factory_->SetSupportedProtocol( - device::ProtocolVersion::kCtap2); + device::VirtualCtap2Device::Config config; + config.hmac_secret_support = authenticator_support; + virtual_device_factory_->SetCtap2Config(config); - mojo::Remote<blink::mojom::Authenticator> authenticator = - ConnectToAuthenticator(); - PublicKeyCredentialCreationOptionsPtr options = - GetTestPublicKeyCredentialCreationOptions(); - options->hmac_create_secret = include_extension; - TestMakeCredentialCallback callback_receiver; - authenticator->MakeCredential(std::move(options), - callback_receiver.callback()); - callback_receiver.WaitForCallback(); - EXPECT_EQ(AuthenticatorStatus::SUCCESS, callback_receiver.status()); + mojo::Remote<blink::mojom::Authenticator> authenticator = + ConnectToAuthenticator(); + PublicKeyCredentialCreationOptionsPtr options = + GetTestPublicKeyCredentialCreationOptions(); + options->hmac_create_secret = include_extension; + TestMakeCredentialCallback callback_receiver; + authenticator->MakeCredential(std::move(options), + callback_receiver.callback()); + callback_receiver.WaitForCallback(); + EXPECT_EQ(AuthenticatorStatus::SUCCESS, callback_receiver.status()); - base::Optional<Value> attestation_value = - Reader::Read(callback_receiver.value()->attestation_object); - ASSERT_TRUE(attestation_value); - ASSERT_TRUE(attestation_value->is_map()); - const auto& attestation = attestation_value->GetMap(); - - const auto auth_data_it = attestation.find(Value(device::kAuthDataKey)); - ASSERT_TRUE(auth_data_it != attestation.end()); - ASSERT_TRUE(auth_data_it->second.is_bytestring()); - const std::vector<uint8_t>& auth_data = - auth_data_it->second.GetBytestring(); - base::Optional<device::AuthenticatorData> parsed_auth_data = - device::AuthenticatorData::DecodeAuthenticatorData(auth_data); - - // The virtual CTAP2 device always echos the hmac-secret extension on - // registrations. Therefore, if |hmac_secret| was set above it should be - // serialised in the CBOR and correctly passed all the way back around to - // the reply's authenticator data. - bool has_hmac_secret = false; - const auto& extensions = parsed_auth_data->extensions(); - if (extensions) { - CHECK(extensions->is_map()); - const cbor::Value::MapValue& extensions_map = extensions->GetMap(); - const auto hmac_secret_it = - extensions_map.find(cbor::Value(device::kExtensionHmacSecret)); - if (hmac_secret_it != extensions_map.end()) { - ASSERT_TRUE(hmac_secret_it->second.is_bool()); - EXPECT_TRUE(hmac_secret_it->second.GetBool()); - has_hmac_secret = true; + device::AuthenticatorData parsed_auth_data = + AuthDataFromMakeCredentialResponse(callback_receiver.value()); + + // The virtual CTAP2 device always echos the hmac-secret extension on + // registrations. Therefore, if |hmac_secret| was set above it should be + // serialised in the CBOR and correctly passed all the way back around to + // the reply's authenticator data. + bool has_hmac_secret = false; + const auto& extensions = parsed_auth_data.extensions(); + if (extensions) { + CHECK(extensions->is_map()); + const cbor::Value::MapValue& extensions_map = extensions->GetMap(); + const auto hmac_secret_it = + extensions_map.find(cbor::Value(device::kExtensionHmacSecret)); + if (hmac_secret_it != extensions_map.end()) { + ASSERT_TRUE(hmac_secret_it->second.is_bool()); + EXPECT_TRUE(hmac_secret_it->second.GetBool()); + has_hmac_secret = true; + } } - } - EXPECT_EQ(include_extension, has_hmac_secret); + EXPECT_EQ(include_extension && authenticator_support, has_hmac_secret); + } } } @@ -3468,6 +3323,90 @@ TEST_F(AuthenticatorImplTest, ExcludeListBatching) { } } +TEST_F(AuthenticatorImplTest, GetPublicKey) { + device::VirtualCtap2Device::Config config; + config.support_invalid_for_testing_algorithm = true; + virtual_device_factory_->SetCtap2Config(config); + NavigateAndCommit(GURL(kTestOrigin1)); + + mojo::Remote<blink::mojom::Authenticator> authenticator = + ConnectToAuthenticator(); + + static constexpr struct { + device::CoseAlgorithmIdentifier algo; + base::Optional<int> evp_id; + } kTests[] = { + {device::CoseAlgorithmIdentifier::kEs256, EVP_PKEY_EC}, + {device::CoseAlgorithmIdentifier::kRs256, EVP_PKEY_RSA}, + {device::CoseAlgorithmIdentifier::kEdDSA, EVP_PKEY_ED25519}, + {device::CoseAlgorithmIdentifier::kInvalidForTesting, base::nullopt}, + }; + + for (const auto& test : kTests) { + PublicKeyCredentialCreationOptionsPtr options = + GetTestPublicKeyCredentialCreationOptions(); + options->public_key_parameters = + GetTestPublicKeyCredentialParameters(static_cast<int32_t>(test.algo)); + TestMakeCredentialCallback callback; + authenticator->MakeCredential(std::move(options), callback.callback()); + base::RunLoop().RunUntilIdle(); + callback.WaitForCallback(); + + ASSERT_EQ(callback.status(), AuthenticatorStatus::SUCCESS); + const auto& response = callback.value(); + EXPECT_EQ(response->public_key_algo, static_cast<int32_t>(test.algo)); + EXPECT_FALSE(response->info->authenticator_data.empty()); + + ASSERT_EQ(test.evp_id.has_value(), response->public_key_der.has_value()); + if (!test.evp_id) { + continue; + } + + const std::vector<uint8_t>& public_key_der = + response->public_key_der.value(); + + CBS cbs; + CBS_init(&cbs, public_key_der.data(), public_key_der.size()); + bssl::UniquePtr<EVP_PKEY> pkey(EVP_parse_public_key(&cbs)); + EXPECT_EQ(0u, CBS_len(&cbs)); + ASSERT_TRUE(pkey.get()); + + EXPECT_EQ(test.evp_id.value(), EVP_PKEY_id(pkey.get())); + } +} + +TEST_F(AuthenticatorImplTest, ResetDiscoveryFactoryOverride) { + // This is a regression test for crbug.com/1087158. + NavigateAndCommit(GURL(kTestOrigin1)); + + base::RunLoop run_loop; + virtual_device_factory_->SetSupportedProtocol( + device::ProtocolVersion::kCtap2); + virtual_device_factory_->mutable_state()->simulate_press_callback = + base::BindLambdaForTesting([&](device::VirtualFidoDevice* device) { + run_loop.QuitClosure().Run(); + return false; + }); + + auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>( + base::Time::Now(), base::TimeTicks::Now()); + mojo::Remote<blink::mojom::Authenticator> authenticator = + ConstructAuthenticatorWithTimer(task_runner); + PublicKeyCredentialCreationOptionsPtr options = + GetTestPublicKeyCredentialCreationOptions(); + TestMakeCredentialCallback callback; + authenticator->MakeCredential(std::move(options), callback.callback()); + + // Reset the FidoDiscoveryFactory while the request is processing, then let it + // time out. + run_loop.Run(); + ResetVirtualDevice(); + task_runner->FastForwardBy(base::TimeDelta::FromMinutes(1)); + + callback.WaitForCallback(); + EXPECT_EQ(callback.status(), AuthenticatorStatus::NOT_ALLOWED_ERROR); +} + static constexpr char kTestPIN[] = "1234"; class UVTestAuthenticatorClientDelegate @@ -3589,26 +3528,15 @@ class UVAuthenticatorImplTest : public AuthenticatorImplTest { static bool HasUV(const TestMakeCredentialCallback& callback) { DCHECK_EQ(AuthenticatorStatus::SUCCESS, callback.status()); - base::Optional<Value> attestation_value = - Reader::Read(callback.value()->attestation_object); - DCHECK(attestation_value); - DCHECK(attestation_value->is_map()); - const auto& attestation = attestation_value->GetMap(); - - const auto auth_data_it = attestation.find(Value("authData")); - DCHECK(auth_data_it != attestation.end() && - auth_data_it->second.is_bytestring()); - base::Optional<device::AuthenticatorData> auth_data = - device::AuthenticatorData::DecodeAuthenticatorData( - auth_data_it->second.GetBytestring()); - return auth_data->obtained_user_verification(); + return AuthDataFromMakeCredentialResponse(callback.value()) + .obtained_user_verification(); } static bool HasUV(const TestGetAssertionCallback& callback) { DCHECK_EQ(AuthenticatorStatus::SUCCESS, callback.status()); base::Optional<device::AuthenticatorData> auth_data = device::AuthenticatorData::DecodeAuthenticatorData( - callback.value()->authenticator_data); + callback.value()->info->authenticator_data); return auth_data->obtained_user_verification(); } @@ -3696,7 +3624,7 @@ class PINAuthenticatorImplTest : public UVAuthenticatorImplTest { void TearDown() override { SetBrowserClientForTesting(old_client_); - AuthenticatorImplTest::TearDown(); + UVAuthenticatorImplTest::TearDown(); } protected: @@ -3710,8 +3638,11 @@ class PINAuthenticatorImplTest : public UVAuthenticatorImplTest { kUsePIN, }; - void ConfigureVirtualDevice(int support_level) { + void ConfigureVirtualDevice(bool pin_uv_auth_token, int support_level) { device::VirtualCtap2Device::Config config; + config.pin_uv_auth_token_support = pin_uv_auth_token; + config.ctap2_versions = {device::Ctap2Version::kCtap2_0, + device::Ctap2Version::kCtap2_1}; switch (support_level) { case 0: // No support. @@ -3791,69 +3722,76 @@ TEST_F(PINAuthenticatorImplTest, MakeCredential) { }; // clang-format on - for (bool ui_support : {false, true}) { - SCOPED_TRACE(::testing::Message() << "ui_support=" << ui_support); - const Expectations& expected = - ui_support ? kExpectedWithUISupport : kExpectedWithoutUISupport; - test_client_.supports_pin = ui_support; - - for (int support_level = 0; support_level <= 2; support_level++) { - SCOPED_TRACE(kPINSupportDescription[support_level]); - ConfigureVirtualDevice(support_level); - - for (int uv_level = 0; uv_level <= 2; uv_level++) { - SCOPED_TRACE(kUVDescription[uv_level]); - - switch (expected[support_level][uv_level]) { - case kNoPIN: - case kFailure: - // There shouldn't be any PIN prompts. - test_client_.expected.clear(); - break; - - case kSetPIN: - // A single PIN prompt to set a PIN is expected. - test_client_.expected = {{base::nullopt, kTestPIN}}; - break; - - case kUsePIN: - // A single PIN prompt to get the PIN is expected. - test_client_.expected = {{8, kTestPIN}}; - break; - - default: - NOTREACHED(); - } - - mojo::Remote<blink::mojom::Authenticator> authenticator = - ConnectToAuthenticator(); - TestMakeCredentialCallback callback_receiver; - authenticator->MakeCredential( - make_credential_options(kUVLevel[uv_level]), - callback_receiver.callback()); - callback_receiver.WaitForCallback(); - - switch (expected[support_level][uv_level]) { - case kFailure: - EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR, - callback_receiver.status()); - break; - - case kNoPIN: - EXPECT_EQ(AuthenticatorStatus::SUCCESS, callback_receiver.status()); - EXPECT_EQ("", virtual_device_factory_->mutable_state()->pin); - EXPECT_FALSE(HasUV(callback_receiver)); - break; - - case kSetPIN: - case kUsePIN: - EXPECT_EQ(AuthenticatorStatus::SUCCESS, callback_receiver.status()); - EXPECT_EQ(kTestPIN, virtual_device_factory_->mutable_state()->pin); - EXPECT_TRUE(HasUV(callback_receiver)); - break; + for (bool pin_uv_auth_token : {false, true}) { + SCOPED_TRACE(::testing::Message() + << "pin_uv_auth_token=" << pin_uv_auth_token); + for (bool ui_support : {false, true}) { + SCOPED_TRACE(::testing::Message() << "ui_support=" << ui_support); + const Expectations& expected = + ui_support ? kExpectedWithUISupport : kExpectedWithoutUISupport; + test_client_.supports_pin = ui_support; + + for (int support_level = 0; support_level <= 2; support_level++) { + SCOPED_TRACE(kPINSupportDescription[support_level]); + ConfigureVirtualDevice(pin_uv_auth_token, support_level); + + for (int uv_level = 0; uv_level <= 2; uv_level++) { + SCOPED_TRACE(kUVDescription[uv_level]); + + switch (expected[support_level][uv_level]) { + case kNoPIN: + case kFailure: + // There shouldn't be any PIN prompts. + test_client_.expected.clear(); + break; + + case kSetPIN: + // A single PIN prompt to set a PIN is expected. + test_client_.expected = {{base::nullopt, kTestPIN}}; + break; + + case kUsePIN: + // A single PIN prompt to get the PIN is expected. + test_client_.expected = {{8, kTestPIN}}; + break; + + default: + NOTREACHED(); + } - default: - NOTREACHED(); + mojo::Remote<blink::mojom::Authenticator> authenticator = + ConnectToAuthenticator(); + TestMakeCredentialCallback callback_receiver; + authenticator->MakeCredential( + make_credential_options(kUVLevel[uv_level]), + callback_receiver.callback()); + callback_receiver.WaitForCallback(); + + switch (expected[support_level][uv_level]) { + case kFailure: + EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR, + callback_receiver.status()); + break; + + case kNoPIN: + EXPECT_EQ(AuthenticatorStatus::SUCCESS, + callback_receiver.status()); + EXPECT_EQ("", virtual_device_factory_->mutable_state()->pin); + EXPECT_FALSE(HasUV(callback_receiver)); + break; + + case kSetPIN: + case kUsePIN: + EXPECT_EQ(AuthenticatorStatus::SUCCESS, + callback_receiver.status()); + EXPECT_EQ(kTestPIN, + virtual_device_factory_->mutable_state()->pin); + EXPECT_TRUE(HasUV(callback_receiver)); + break; + + default: + NOTREACHED(); + } } } } @@ -3926,61 +3864,67 @@ TEST_F(PINAuthenticatorImplTest, GetAssertion) { ASSERT_TRUE(virtual_device_factory_->mutable_state()->InjectRegistration( dummy_options->allow_credentials[0].id(), kTestRelyingPartyId)); - for (bool ui_support : {false, true}) { - SCOPED_TRACE(::testing::Message() << "ui_support=" << ui_support); - const Expectations& expected = - ui_support ? kExpectedWithUISupport : kExpectedWithoutUISupport; - test_client_.supports_pin = ui_support; - - for (int support_level = 0; support_level <= 2; support_level++) { - SCOPED_TRACE(kPINSupportDescription[support_level]); - ConfigureVirtualDevice(support_level); - - for (int uv_level = 0; uv_level <= 2; uv_level++) { - SCOPED_TRACE(kUVDescription[uv_level]); - - switch (expected[support_level][uv_level]) { - case kNoPIN: - case kFailure: - // No PIN prompts are expected. - test_client_.expected.clear(); - break; - - case kUsePIN: - // A single prompt to get the PIN is expected. - test_client_.expected = {{8, kTestPIN}}; - break; - - default: - NOTREACHED(); - } - - mojo::Remote<blink::mojom::Authenticator> authenticator = - ConnectToAuthenticator(); - TestGetAssertionCallback callback_receiver; - authenticator->GetAssertion(get_credential_options(kUVLevel[uv_level]), - callback_receiver.callback()); - callback_receiver.WaitForCallback(); - - switch (expected[support_level][uv_level]) { - case kFailure: - EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR, - callback_receiver.status()); - break; - - case kNoPIN: - EXPECT_EQ(AuthenticatorStatus::SUCCESS, callback_receiver.status()); - EXPECT_FALSE(HasUV(callback_receiver)); - break; - - case kUsePIN: - EXPECT_EQ(AuthenticatorStatus::SUCCESS, callback_receiver.status()); - EXPECT_EQ(kTestPIN, virtual_device_factory_->mutable_state()->pin); - EXPECT_TRUE(HasUV(callback_receiver)); - break; + for (bool pin_uv_auth_token : {false, true}) { + for (bool ui_support : {false, true}) { + SCOPED_TRACE(::testing::Message() << "ui_support=" << ui_support); + const Expectations& expected = + ui_support ? kExpectedWithUISupport : kExpectedWithoutUISupport; + test_client_.supports_pin = ui_support; + + for (int support_level = 0; support_level <= 2; support_level++) { + SCOPED_TRACE(kPINSupportDescription[support_level]); + ConfigureVirtualDevice(pin_uv_auth_token, support_level); + + for (int uv_level = 0; uv_level <= 2; uv_level++) { + SCOPED_TRACE(kUVDescription[uv_level]); + + switch (expected[support_level][uv_level]) { + case kNoPIN: + case kFailure: + // No PIN prompts are expected. + test_client_.expected.clear(); + break; + + case kUsePIN: + // A single prompt to get the PIN is expected. + test_client_.expected = {{8, kTestPIN}}; + break; + + default: + NOTREACHED(); + } - default: - NOTREACHED(); + mojo::Remote<blink::mojom::Authenticator> authenticator = + ConnectToAuthenticator(); + TestGetAssertionCallback callback_receiver; + authenticator->GetAssertion( + get_credential_options(kUVLevel[uv_level]), + callback_receiver.callback()); + callback_receiver.WaitForCallback(); + + switch (expected[support_level][uv_level]) { + case kFailure: + EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR, + callback_receiver.status()); + break; + + case kNoPIN: + EXPECT_EQ(AuthenticatorStatus::SUCCESS, + callback_receiver.status()); + EXPECT_FALSE(HasUV(callback_receiver)); + break; + + case kUsePIN: + EXPECT_EQ(AuthenticatorStatus::SUCCESS, + callback_receiver.status()); + EXPECT_EQ(kTestPIN, + virtual_device_factory_->mutable_state()->pin); + EXPECT_TRUE(HasUV(callback_receiver)); + break; + + default: + NOTREACHED(); + } } } } @@ -4031,6 +3975,65 @@ TEST_F(PINAuthenticatorImplTest, GetAssertionHardLock) { *test_client_.failure_reason); } +TEST_F(PINAuthenticatorImplTest, MakeCredentialNoSupportedAlgorithm) { + SimulateNavigation(GURL(kTestOrigin1)); + + for (int i = 0; i < 3; i++) { + SCOPED_TRACE(i); + + test_client_.expected.clear(); + bool expected_to_succeed = false; + if (i == 0) { + device::VirtualCtap2Device::Config config; + // The first config is a CTAP2 device that doesn't support the + // kInvalidForTesting algorithm. A dummy touch should be requested in this + // case. + config.support_invalid_for_testing_algorithm = false; + virtual_device_factory_->SetCtap2Config(config); + } else if (i == 1) { + device::VirtualCtap2Device::Config config; + // The second config is a device with a PIN set that _does_ support the + // algorithm. Since the PIN is set, we might convert the makeCredential + // request to U2F, but shouldn't because the algorithm cannot be + // represented in U2F. + config.support_invalid_for_testing_algorithm = true; + config.u2f_support = true; + config.pin_support = true; + virtual_device_factory_->mutable_state()->pin = kTestPIN; + virtual_device_factory_->mutable_state()->pin_retries = + device::kMaxPinRetries; + virtual_device_factory_->SetCtap2Config(config); + test_client_.expected = {{device::kMaxPinRetries, kTestPIN}}; + // Since converting to U2F isn't possible, this will trigger a PIN prompt + // and succeed because the device does actually support the algorithm. + expected_to_succeed = true; + } else if (i == 2) { + // The third case is a plain U2F authenticator, which implicitly only + // supports ES256. + virtual_device_factory_->SetSupportedProtocol( + device::ProtocolVersion::kU2f); + } + + auto authenticator = ConnectToAuthenticator(); + PublicKeyCredentialCreationOptionsPtr options = + GetTestPublicKeyCredentialCreationOptions(); + // Set uv=discouraged so that U2F fallback is possible. + options->authenticator_selection->SetUserVerificationRequirementForTesting( + device::UserVerificationRequirement::kDiscouraged); + options->public_key_parameters = + GetTestPublicKeyCredentialParameters(static_cast<int32_t>( + device::CoseAlgorithmIdentifier::kInvalidForTesting)); + + TestMakeCredentialCallback callback_receiver; + authenticator->MakeCredential(std::move(options), + callback_receiver.callback()); + callback_receiver.WaitForCallback(); + EXPECT_EQ(expected_to_succeed ? AuthenticatorStatus::SUCCESS + : AuthenticatorStatus::NOT_ALLOWED_ERROR, + callback_receiver.status()); + } +} + class InternalUVAuthenticatorImplTest : public UVAuthenticatorImplTest { public: struct TestCase { @@ -4339,8 +4342,9 @@ class UVTokenAuthenticatorImplTest : public UVAuthenticatorImplTest { void SetUp() override { UVAuthenticatorImplTest::SetUp(); device::VirtualCtap2Device::Config config; + config.ctap2_versions = {device::Ctap2Version::kCtap2_1}; config.internal_uv_support = true; - config.uv_token_support = true; + config.pin_uv_auth_token_support = true; virtual_device_factory_->SetCtap2Config(config); NavigateAndCommit(GURL(kTestOrigin1)); } @@ -4398,8 +4402,9 @@ TEST_F(UVTokenAuthenticatorImplTest, GetAssertionUvFails) { mojo::Remote<blink::mojom::Authenticator> authenticator = ConnectToAuthenticator(); device::VirtualCtap2Device::Config config; + config.ctap2_versions = {device::Ctap2Version::kCtap2_1}; config.internal_uv_support = true; - config.uv_token_support = true; + config.pin_uv_auth_token_support = true; config.user_verification_succeeds = false; config.pin_support = false; virtual_device_factory_->SetCtap2Config(config); @@ -4432,8 +4437,9 @@ TEST_F(UVTokenAuthenticatorImplTest, GetAssertionFallBackToPin) { mojo::Remote<blink::mojom::Authenticator> authenticator = ConnectToAuthenticator(); device::VirtualCtap2Device::Config config; + config.ctap2_versions = {device::Ctap2Version::kCtap2_1}; config.internal_uv_support = true; - config.uv_token_support = true; + config.pin_uv_auth_token_support = true; config.user_verification_succeeds = false; config.pin_support = true; virtual_device_factory_->SetCtap2Config(config); @@ -4469,8 +4475,9 @@ TEST_F(UVTokenAuthenticatorImplTest, GetAssertionUvBlockedFallBackToPin) { mojo::Remote<blink::mojom::Authenticator> authenticator = ConnectToAuthenticator(); device::VirtualCtap2Device::Config config; + config.ctap2_versions = {device::Ctap2Version::kCtap2_1}; config.internal_uv_support = true; - config.uv_token_support = true; + config.pin_uv_auth_token_support = true; config.user_verification_succeeds = false; config.pin_support = true; @@ -4538,8 +4545,9 @@ TEST_F(UVTokenAuthenticatorImplTest, MakeCredentialUvFails) { mojo::Remote<blink::mojom::Authenticator> authenticator = ConnectToAuthenticator(); device::VirtualCtap2Device::Config config; + config.ctap2_versions = {device::Ctap2Version::kCtap2_1}; config.internal_uv_support = true; - config.uv_token_support = true; + config.pin_uv_auth_token_support = true; config.user_verification_succeeds = false; config.pin_support = false; virtual_device_factory_->SetCtap2Config(config); @@ -4573,8 +4581,9 @@ TEST_F(UVTokenAuthenticatorImplTest, MakeCredentialFallBackToPin) { mojo::Remote<blink::mojom::Authenticator> authenticator = ConnectToAuthenticator(); device::VirtualCtap2Device::Config config; + config.ctap2_versions = {device::Ctap2Version::kCtap2_1}; config.internal_uv_support = true; - config.uv_token_support = true; + config.pin_uv_auth_token_support = true; config.user_verification_succeeds = false; config.pin_support = true; virtual_device_factory_->SetCtap2Config(config); @@ -4611,8 +4620,9 @@ TEST_F(UVTokenAuthenticatorImplTest, MakeCredentialUvBlockedFallBackToPin) { mojo::Remote<blink::mojom::Authenticator> authenticator = ConnectToAuthenticator(); device::VirtualCtap2Device::Config config; + config.ctap2_versions = {device::Ctap2Version::kCtap2_1}; config.internal_uv_support = true; - config.uv_token_support = true; + config.pin_uv_auth_token_support = true; config.user_verification_succeeds = false; config.pin_support = true; @@ -4755,7 +4765,7 @@ class ResidentKeyAuthenticatorImplTest : public UVAuthenticatorImplTest { void TearDown() override { SetBrowserClientForTesting(old_client_); - AuthenticatorImplTest::TearDown(); + UVAuthenticatorImplTest::TearDown(); } protected: @@ -5422,8 +5432,6 @@ class InternalAuthenticatorImplTest : public AuthenticatorTestBase { InternalAuthenticatorImpl* ConstructAuthenticatorWithTimer( const url::Origin& effective_origin_url, scoped_refptr<base::TestMockTimeTaskRunner> task_runner) { - fake_hid_manager_ = std::make_unique<device::FakeFidoHidManager>(); - // Set up a timer for testing. auto timer = std::make_unique<base::OneShotTimer>(task_runner->GetMockTickClock()); @@ -5433,7 +5441,6 @@ class InternalAuthenticatorImplTest : public AuthenticatorTestBase { protected: std::unique_ptr<InternalAuthenticatorImpl> internal_authenticator_impl_; - std::unique_ptr<device::FakeFidoHidManager> fake_hid_manager_; }; // Verify behavior for various combinations of origins and RP IDs. diff --git a/chromium/content/browser/webauth/authenticator_mojom_traits_unittest.cc b/chromium/content/browser/webauth/authenticator_mojom_traits_unittest.cc index 53ffaf0d635..58ee437336b 100644 --- a/chromium/content/browser/webauth/authenticator_mojom_traits_unittest.cc +++ b/chromium/content/browser/webauth/authenticator_mojom_traits_unittest.cc @@ -70,7 +70,7 @@ void AssertSerializeAndDeserializeSucceeds(std::vector<UserType> test_cases) { TEST(AuthenticatorMojomTraitsTest, SerializeCredentialParams) { std::vector<PublicKeyCredentialParams::CredentialInfo> success_cases = { {CredentialType::kPublicKey, - base::strict_cast<int>(CoseAlgorithmIdentifier::kCoseEs256)}}; + base::strict_cast<int>(CoseAlgorithmIdentifier::kEs256)}}; AssertSerializeAndDeserializeSucceeds< blink::mojom::PublicKeyCredentialParameters, diff --git a/chromium/content/browser/webauth/virtual_fido_discovery_factory.cc b/chromium/content/browser/webauth/virtual_fido_discovery_factory.cc index 18d68a24d4e..a8b0436d4ea 100644 --- a/chromium/content/browser/webauth/virtual_fido_discovery_factory.cc +++ b/chromium/content/browser/webauth/virtual_fido_discovery_factory.cc @@ -50,8 +50,15 @@ VirtualAuthenticator* VirtualFidoDiscoveryFactory::CreateAuthenticator( auto authenticator = std::make_unique<VirtualAuthenticator>( protocol, transport, attachment, has_resident_key, has_user_verification); auto* authenticator_ptr = authenticator.get(); - authenticators_.emplace(authenticator_ptr->unique_id(), - std::move(authenticator)); + bool was_inserted; + std::tie(std::ignore, was_inserted) = authenticators_.insert( + {authenticator_ptr->unique_id(), std::move(authenticator)}); + if (!was_inserted) { + // unique_id() is unique, so map insertion should succeed. But let's be + // paranoid so we don't accidentally return a dangling pointer. + NOTREACHED(); + return nullptr; + } for (auto* discovery : discoveries_) { if (discovery->transport() != authenticator_ptr->transport()) @@ -119,6 +126,10 @@ void VirtualFidoDiscoveryFactory::CreateAuthenticator( auto* authenticator = CreateAuthenticator( options->protocol, options->transport, options->attachment, options->has_resident_key, options->has_user_verification); + if (!authenticator) { + std::move(callback).Run(mojo::NullRemote()); + return; + } authenticator->SetUserPresence(options->is_user_present); std::move(callback).Run(GetMojoToVirtualAuthenticator(authenticator)); diff --git a/chromium/content/browser/webauth/webauth_browsertest.cc b/chromium/content/browser/webauth/webauth_browsertest.cc index e75e4ec190c..58ac97f641b 100644 --- a/chromium/content/browser/webauth/webauth_browsertest.cc +++ b/chromium/content/browser/webauth/webauth_browsertest.cc @@ -1136,8 +1136,8 @@ base::Optional<std::string> ExecuteScriptAndExtractPrefixedString( return base::nullopt; } - base::JSONReader reader(base::JSON_ALLOW_TRAILING_COMMAS); - std::unique_ptr<base::Value> result = reader.ReadToValueDeprecated(json); + base::Optional<base::Value> result = + base::JSONReader::Read(json, base::JSON_ALLOW_TRAILING_COMMAS); if (!result) { return base::nullopt; } @@ -1419,31 +1419,27 @@ IN_PROC_BROWSER_TEST_F(WebAuthJavascriptClientBrowserTest, } } -// TODO(crbug/1081450): FakeWinWebAuthnApi needs to support injecting -// credentials in order for assertion responses to pass response validation. -IN_PROC_BROWSER_TEST_F(WebAuthJavascriptClientBrowserTest, - DISABLED_WinGetAssertion) { +IN_PROC_BROWSER_TEST_F(WebAuthJavascriptClientBrowserTest, WinGetAssertion) { EXPECT_TRUE( NavigateToURL(shell(), GetHttpsURL("www.acme.com", "/title1.html"))); + constexpr uint8_t credential_id[] = {'A', 'A', 'A'}; + device::FakeWinWebAuthnApi fake_api; - fake_api.set_hresult(S_OK); + fake_api.InjectNonDiscoverableCredential(credential_id, "www.acme.com"); auto* virtual_device_factory = InjectVirtualFidoDeviceFactory(); virtual_device_factory->set_win_webauthn_api(&fake_api); + GetParameters get_parameters; + get_parameters.allow_credentials = + "allowCredentials: [{ type: 'public-key', id: new " + "TextEncoder().encode('AAA')}]"; + base::Optional<std::string> result = ExecuteScriptAndExtractPrefixedString( - shell()->web_contents(), BuildGetCallWithParameters(GetParameters()), + shell()->web_contents(), BuildGetCallWithParameters(get_parameters), "webauth: "); ASSERT_TRUE(result); ASSERT_EQ(kOkMessage, *result); - - // The authenticator response was good but the return code indicated failure. - fake_api.set_hresult(E_FAIL); - result = ExecuteScriptAndExtractPrefixedString( - shell()->web_contents(), BuildGetCallWithParameters(GetParameters()), - "webauth: "); - ASSERT_TRUE(result); - ASSERT_EQ(kNotAllowedErrorMessage, *result); } IN_PROC_BROWSER_TEST_F(WebAuthJavascriptClientBrowserTest, |