chromium/content/browser/webauth/authenticator_impl_unittest.cc

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/342213636): Remove this and spanify to fix the errors.
#pragma allow_unsafe_buffers
#endif

#include "content/browser/webauth/authenticator_impl.h"

#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <list>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <tuple>
#include <utility>
#include <vector>

#include "base/base64url.h"
#include "base/check.h"
#include "base/check_op.h"
#include "base/compiler_specific.h"
#include "base/containers/flat_set.h"
#include "base/containers/span.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_forward.h"
#include "base/functional/callback_helpers.h"
#include "base/json/json_reader.h"
#include "base/location.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/stack_allocated.h"
#include "base/notreached.h"
#include "base/rand_util.h"
#include "base/ranges/algorithm.h"
#include "base/run_loop.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_command_line.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "base/time/time.h"
#include "base/values.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "components/cbor/reader.h"
#include "components/cbor/values.h"
#include "components/ukm/test_ukm_recorder.h"
#include "components/webauthn/content/browser/internal_authenticator_impl.h"
#include "content/browser/webauth/authenticator_common_impl.h"
#include "content/browser/webauth/authenticator_environment.h"
#include "content/browser/webauth/client_data_json.h"
#include "content/public/browser/authenticator_request_client_delegate.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_authentication_request_proxy.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_renderer_host.h"
#include "crypto/sha2.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/test/mock_bluetooth_adapter.h"
#include "device/fido/attested_credential_data.h"
#include "device/fido/authenticator_data.h"
#include "device/fido/authenticator_get_assertion_response.h"
#include "device/fido/authenticator_selection_criteria.h"
#include "device/fido/cable/cable_discovery_data.h"
#include "device/fido/cable/fido_tunnel_device.h"
#include "device/fido/cable/v2_authenticator.h"
#include "device/fido/cable/v2_constants.h"
#include "device/fido/cable/v2_discovery.h"
#include "device/fido/cable/v2_handshake.h"
#include "device/fido/cable/v2_test_util.h"
#include "device/fido/discoverable_credential_metadata.h"
#include "device/fido/fake_fido_discovery.h"
#include "device/fido/features.h"
#include "device/fido/fido_authenticator.h"
#include "device/fido/fido_constants.h"
#include "device/fido/fido_discovery_base.h"
#include "device/fido/fido_parsing_utils.h"
#include "device/fido/fido_request_handler_base.h"
#include "device/fido/fido_transport_protocol.h"
#include "device/fido/fido_types.h"
#include "device/fido/filter.h"
#include "device/fido/large_blob.h"
#include "device/fido/mock_fido_device.h"
#include "device/fido/multiple_virtual_fido_device_factory.h"
#include "device/fido/pin.h"
#include "device/fido/public_key_credential_descriptor.h"
#include "device/fido/public_key_credential_params.h"
#include "device/fido/public_key_credential_rp_entity.h"
#include "device/fido/public_key_credential_user_entity.h"
#include "device/fido/virtual_ctap2_device.h"
#include "device/fido/virtual_fido_device.h"
#include "device/fido/virtual_fido_device_factory.h"
#include "mojo/public/cpp/base/big_buffer.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/system/functions.h"
#include "services/data_decoder/gzipper.h"
#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "services/metrics/public/cpp/ukm_source.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/mojom/webauthn/authenticator.mojom.h"
#include "third_party/boringssl/src/include/openssl/base.h"
#include "third_party/boringssl/src/include/openssl/bytestring.h"
#include "third_party/boringssl/src/include/openssl/ec.h"
#include "third_party/boringssl/src/include/openssl/ec_key.h"
#include "third_party/boringssl/src/include/openssl/evp.h"
#include "third_party/boringssl/src/include/openssl/hmac.h"
#include "third_party/boringssl/src/include/openssl/obj.h"
#include "url/origin.h"
#include "url/url_util.h"

#if BUILDFLAG(IS_MAC)
#include "base/files/file_path.h"
#include "base/path_service.h"
#include "crypto/scoped_fake_apple_keychain_v2.h"
#include "device/fido/mac/authenticator_config.h"
#include "device/fido/mac/credential_store.h"
#include "device/fido/mac/icloud_keychain.h"
#include "device/fido/mac/scoped_icloud_keychain_test_environment.h"
#include "device/fido/mac/scoped_touch_id_test_environment.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/resource/resource_scale_factor.h"
#endif

#if BUILDFLAG(IS_WIN)
#include "content/public/test/test_browser_context.h"
#include "device/fido/fido_test_data.h"
#include "device/fido/win/fake_webauthn_api.h"
#include "device/fido/win/util.h"
#endif

#if BUILDFLAG(IS_CHROMEOS)
#include "chromeos/dbus/tpm_manager/tpm_manager_client.h"
#include "chromeos/dbus/u2f/u2f_client.h"
#endif

namespace content {

_;

GetAssertionOutcome;
MakeCredentialOutcome;
RequestMode;

AttestationConveyancePreference;
AuthenticationExtensionsClientInputs;
AuthenticationExtensionsClientOutputs;
AuthenticatorSelectionCriteria;
AuthenticatorSelectionCriteriaPtr;
AuthenticatorStatus;
AuthenticatorTransport;
CableAuthentication;
CableAuthenticationPtr;
CommonCredentialInfo;
GetAssertionAuthenticatorResponse;
GetAssertionAuthenticatorResponsePtr;
MakeCredentialAuthenticatorResponse;
MakeCredentialAuthenticatorResponsePtr;
PublicKeyCredentialCreationOptions;
PublicKeyCredentialCreationOptionsPtr;
PublicKeyCredentialDescriptor;
PublicKeyCredentialDescriptorPtr;
PublicKeyCredentialParameters;
PublicKeyCredentialParametersPtr;
PublicKeyCredentialReportOptions;
PublicKeyCredentialReportOptionsPtr;
PublicKeyCredentialRequestOptions;
PublicKeyCredentialRequestOptionsPtr;
PublicKeyCredentialRpEntity;
PublicKeyCredentialRpEntityPtr;
PublicKeyCredentialType;
PublicKeyCredentialUserEntity;
PublicKeyCredentialUserEntityPtr;
RemoteDesktopClientOverride;
RemoteDesktopClientOverridePtr;
WebAuthnDOMExceptionDetails;
WebAuthnDOMExceptionDetailsPtr;
Reader;
Value;
VirtualCtap2Device;
VirtualFidoDevice;
Event;

namespace {

InterestingFailureReason;
FailureReasonFuture;

constexpr base::TimeDelta kTestTimeout =;

// The size of credential IDs returned by GetTestCredentials().
constexpr size_t kTestCredentialIdLength =;

constexpr char kTestOrigin1[] =;
constexpr char kTestOrigin2[] =;
constexpr char kTestRelyingPartyId[] =;
constexpr char kExtensionScheme[] =;
static constexpr char kCorpCrdOrigin[] =;

constexpr uint8_t kTestChallengeBytes[] =;

constexpr char kTestRegisterClientDataJsonString[] =;

constexpr char kTestSignClientDataJsonString[] =;

OriginClaimedAuthorityPair;

constexpr OriginClaimedAuthorityPair kValidRelyingPartyTestCases[] =;

constexpr OriginClaimedAuthorityPair kInvalidRelyingPartyTestCases[] =;

TestIsUvpaaFuture;
TestMakeCredentialFuture;
TestGetAssertionFuture;
TestRequestStartedFuture;
TestReportFuture;

std::vector<uint8_t> GetTestChallengeBytes() {}

device::PublicKeyCredentialRpEntity GetTestPublicKeyCredentialRPEntity() {}

device::PublicKeyCredentialUserEntity GetTestPublicKeyCredentialUserEntity() {}

std::vector<device::PublicKeyCredentialParams::CredentialInfo>
GetTestPublicKeyCredentialParameters(int32_t algorithm_identifier) {}

device::AuthenticatorSelectionCriteria GetTestAuthenticatorSelectionCriteria() {}

std::vector<device::PublicKeyCredentialDescriptor> GetTestCredentials(
    size_t num_credentials = 1) {}

PublicKeyCredentialCreationOptionsPtr
GetTestPublicKeyCredentialCreationOptions() {}

PublicKeyCredentialRequestOptionsPtr
GetTestPublicKeyCredentialRequestOptions() {}

PublicKeyCredentialReportOptionsPtr GetTestPublicKeyCredentialReportOptions() {}

std::vector<device::CableDiscoveryData> GetTestCableExtension() {}

device::AuthenticatorData AuthDataFromMakeCredentialResponse(
    const MakeCredentialAuthenticatorResponsePtr& response) {}

bool HasUV(const MakeCredentialAuthenticatorResponsePtr& response) {}

bool HasUV(const GetAssertionAuthenticatorResponsePtr& response) {}

url::Origin GetTestOrigin() {}

std::string GetTestClientDataJSON(ClientDataRequestType type) {}

device::LargeBlob CompressLargeBlob(base::span<const uint8_t> blob) {}

std::vector<uint8_t> UncompressLargeBlob(device::LargeBlob blob) {}

// Convert a blink::mojom::AttestationConveyancePreference to a
// device::AtttestationConveyancePreference.
device::AttestationConveyancePreference ConvertAttestationConveyancePreference(
    AttestationConveyancePreference in) {}

std::array<uint8_t, crypto::kSHA256Length> EvaluateHMAC(
    base::span<const uint8_t> key,
    base::span<const uint8_t> salt) {}

}  // namespace

class AuthenticatorTestBase : public RenderViewHostTestHarness {};

class AuthenticatorImplTest : public AuthenticatorTestBase {};

TEST_F(AuthenticatorImplTest, ClientDataJSONSerialization) {}

// Verify behavior for various combinations of origins and RP IDs.
TEST_F(AuthenticatorImplTest, MakeCredentialOriginAndRpIds) {}

// Test that MakeCredential request times out with NOT_ALLOWED_ERROR if user
// verification is required for U2F devices.
TEST_F(AuthenticatorImplTest, MakeCredentialUserVerification) {}

TEST_F(AuthenticatorImplTest, MakeCredentialResidentKeyUnsupported) {}

// Test that MakeCredential request times out with NOT_ALLOWED_ERROR if a
// platform authenticator is requested for U2F devices.
TEST_F(AuthenticatorImplTest, MakeCredentialPlatformAuthenticator) {}

// Parses its arguments as JSON and expects that all the keys in the first are
// also in the second, and with the same value.
static void CheckJSONIsSubsetOfJSON(std::string_view subset_str,
                                    std::string_view test_str) {}

// Test that client data serializes to JSON properly.
TEST(ClientDataSerializationTest, Register) {}

TEST(ClientDataSerializationTest, Sign) {}

TEST_F(AuthenticatorImplTest, TestMakeCredentialTimeout) {}

// Verify behavior for various combinations of origins and RP IDs.
TEST_F(AuthenticatorImplTest, GetAssertionOriginAndRpIds) {}

// Verify behavior for various combinations of origins and RP IDs.
TEST_F(AuthenticatorImplTest, ReportOriginAndRpIds) {}

constexpr OriginClaimedAuthorityPair kValidAppIdCases[] =;

// Verify behavior for various combinations of origins and RP IDs.
TEST_F(AuthenticatorImplTest, AppIdExtensionValues) {}

// Verify that a credential registered with U2F can be used via webauthn.
TEST_F(AuthenticatorImplTest, AppIdExtension) {}

TEST_F(AuthenticatorImplTest, AppIdExcludeExtension) {}

TEST_F(AuthenticatorImplTest, TestGetAssertionTimeout) {}

TEST_F(AuthenticatorImplTest, OversizedCredentialId) {}

TEST_F(AuthenticatorImplTest, NoSilentAuthenticationForCable) {}

TEST_F(AuthenticatorImplTest, GuessAtTransportsForCable) {}

TEST_F(AuthenticatorImplTest, TestGetAssertionU2fDeviceBackwardsCompatibility) {}

TEST_F(AuthenticatorImplTest, GetAssertionWithEmptyAllowCredentials) {}

TEST_F(AuthenticatorImplTest, MakeCredentialAlreadyRegistered) {}

TEST_F(AuthenticatorImplTest, MakeCredentialPendingRequest) {}

TEST_F(AuthenticatorImplTest, GetAssertionPendingRequest) {}

TEST_F(AuthenticatorImplTest, ReportPendingRequest) {}

TEST_F(AuthenticatorImplTest, NavigationDuringOperation) {}

TEST_F(AuthenticatorImplTest, InvalidResponse) {}

TEST_F(AuthenticatorImplTest, Ctap2AssertionWithUnknownCredential) {}

TEST_F(AuthenticatorImplTest, GetAssertionResponseWithAttestedCredentialData) {}

#if BUILDFLAG(IS_WIN)
TEST_F(AuthenticatorImplTest, Win_IsUVPAA) {
  virtual_device_factory_->set_discover_win_webauthn_api_authenticator(true);
  NavigateAndCommit(GURL(kTestOrigin1));
  mojo::Remote<blink::mojom::Authenticator> authenticator =
      ConnectToAuthenticator();

  for (const bool enable_win_webauthn_api : {false, true}) {
    SCOPED_TRACE(enable_win_webauthn_api ? "enable_win_webauthn_api"
                                         : "!enable_win_webauthn_api");
    for (const bool is_uvpaa : {false, true}) {
      SCOPED_TRACE(is_uvpaa ? "is_uvpaa" : "!is_uvpaa");
      for (bool is_off_the_record : {true, false}) {
        SCOPED_TRACE(is_off_the_record ? "off the record" : "on the record");
        static_cast<TestBrowserContext*>(GetBrowserContext())
            ->set_is_off_the_record(is_off_the_record);
        fake_win_webauthn_api_.set_available(enable_win_webauthn_api);
        fake_win_webauthn_api_.set_is_uvpaa(is_uvpaa);
        EXPECT_EQ(AuthenticatorIsUvpaa(), enable_win_webauthn_api && is_uvpaa);
      }
    }
  }
}
#endif  // BUILDFLAG(IS_WIN)

#if BUILDFLAG(IS_CHROMEOS)
TEST_F(AuthenticatorImplTest, IsUVPAA) {
  NavigateAndCommit(GURL(kTestOrigin1));
  EXPECT_FALSE(AuthenticatorIsUvpaa());
}
#endif  // BUILDFLAG(IS_CHROMEOS)

// TestWebAuthenticationRequestProxy is a test fake implementation of the
// WebAuthenticationRequestProxy embedder interface.
class TestWebAuthenticationRequestProxy : public WebAuthenticationRequestProxy {};

// TestWebAuthenticationDelegate is a test fake implementation of the
// WebAuthenticationDelegate embedder interface.
class TestWebAuthenticationDelegate : public WebAuthenticationDelegate {};

enum class EnterprisePolicy {};

enum class AttestationConsent {};

enum class AttestationType {};

const char* AttestationConveyancePreferenceToString(
    AttestationConveyancePreference v) {}

const char* AttestationConveyancePreferenceToString(
    device::AttestationConveyancePreference v) {}

const char* AttestationConsentToString(AttestationConsent ac) {}

// TestAuthenticatorRequestDelegate is a test fake implementation of the
// AuthenticatorRequestClientDelegate embedder interface.
class TestAuthenticatorRequestDelegate
    : public AuthenticatorRequestClientDelegate {};

// TestAuthenticatorContentBrowserClient is a test fake implementation of the
// ContentBrowserClient interface that injects |TestWebAuthenticationDelegate|
// and |TestAuthenticatorRequestDelegate| instances into |AuthenticatorImpl|.
class TestAuthenticatorContentBrowserClient : public ContentBrowserClient {};

// A test class that installs and removes an
// |TestAuthenticatorContentBrowserClient| automatically and can run tests
// against simulated attestation results.
class AuthenticatorContentBrowserClientTest : public AuthenticatorImplTest {};

TEST_F(AuthenticatorContentBrowserClientTest, MakeCredentialTLSError) {}

TEST_F(AuthenticatorContentBrowserClientTest, GetAssertionTLSError) {}

TEST_F(AuthenticatorContentBrowserClientTest,
       MakeCredentialSkipTLSCheckWithVirtualEnvironment) {}

TEST_F(AuthenticatorContentBrowserClientTest,
       GetAssertionSkipTLSCheckWithVirtualEnvironment) {}

TEST_F(AuthenticatorContentBrowserClientTest, TestGetAssertionCancel) {}

TEST_F(AuthenticatorContentBrowserClientTest, TestMakeCredentialCancel) {}

// Test that credentials can be created and used from an extension origin when
// permitted by the delegate.
TEST_F(AuthenticatorContentBrowserClientTest, ChromeExtensions) {}

TEST_F(AuthenticatorContentBrowserClientTest, ChromeExtensionBadRpIds) {}

TEST_F(AuthenticatorContentBrowserClientTest, AttestationBehaviour) {}

TEST_F(AuthenticatorContentBrowserClientTest, Ctap2EnterpriseAttestation) {}

TEST_F(AuthenticatorContentBrowserClientTest,
       Ctap2EnterpriseAttestationUnsolicited) {}

TEST_F(AuthenticatorContentBrowserClientTest,
       InappropriatelyIdentifyingAttestation) {}

// Test attestation erasure for an authenticator that uses self-attestation
// (which requires a zero AAGUID), but has a non-zero AAGUID. This mirrors the
// behavior of the Touch ID platform authenticator.
TEST_F(AuthenticatorContentBrowserClientTest,
       PlatformAuthenticatorAttestation) {}

TEST_F(AuthenticatorContentBrowserClientTest, Ctap2SelfAttestation) {}

TEST_F(AuthenticatorContentBrowserClientTest,
       Ctap2SelfAttestationNonZeroAaguid) {}

TEST_F(AuthenticatorContentBrowserClientTest, BlockedAttestation) {}

TEST_F(AuthenticatorContentBrowserClientTest, FilteringMakeCredential) {}

TEST_F(AuthenticatorContentBrowserClientTest, FilteringGetAssertion) {}

TEST_F(AuthenticatorContentBrowserClientTest, FilteringFailsOpen) {}

TEST_F(AuthenticatorContentBrowserClientTest,
       MakeCredentialRequestStartedCallback) {}

TEST_F(AuthenticatorContentBrowserClientTest,
       GetAssertionRequestStartedCallback) {}

TEST_F(AuthenticatorContentBrowserClientTest, MakeCredentialStartOver) {}

TEST_F(AuthenticatorContentBrowserClientTest, GetAssertionStartOver) {}

TEST_F(AuthenticatorContentBrowserClientTest, Unfocused) {}

TEST_F(AuthenticatorContentBrowserClientTest,
       NullDelegate_RejectsWithPendingRequest) {}

TEST_F(AuthenticatorContentBrowserClientTest, IsUVPAAOverride) {}

TEST_F(AuthenticatorContentBrowserClientTest,
       GPMPasskeys_IsConditionalMediationAvailable) {}

// AuthenticatorImplRemoteDesktopClientOverrideTest exercises the
// RemoteDesktopClientOverride extension, which is used by remote desktop
// applications exercising requests on behalf of other origins.
class AuthenticatorImplRemoteDesktopClientOverrideTest
    : public AuthenticatorContentBrowserClientTest {};

TEST_F(AuthenticatorImplRemoteDesktopClientOverrideTest, MakeCredential) {}

TEST_F(AuthenticatorImplRemoteDesktopClientOverrideTest, GetAssertion) {}

TEST_F(AuthenticatorImplRemoteDesktopClientOverrideTest, MakeCredentialAppid) {}

TEST_F(AuthenticatorImplRemoteDesktopClientOverrideTest, GetAssertionAppid) {}

class MockAuthenticatorRequestDelegateObserver
    : public TestAuthenticatorRequestDelegate {};

// Fake test construct that shares all other behavior with
// AuthenticatorCommonImpl except that:
//  - FakeAuthenticatorCommonImpl does not trigger UI activity.
//  - MockAuthenticatorRequestDelegateObserver is injected to
//  |request_delegate_|
//    instead of ChromeAuthenticatorRequestDelegate.
class FakeAuthenticatorCommonImpl : public AuthenticatorCommonImpl {};

class AuthenticatorImplRequestDelegateTest : public AuthenticatorImplTest {};

TEST_F(AuthenticatorImplRequestDelegateTest,
       TestRequestDelegateObservesFidoRequestHandler) {}

TEST_F(AuthenticatorImplRequestDelegateTest, FailureReasonForTimeout) {}

TEST_F(AuthenticatorImplRequestDelegateTest,
       FailureReasonForDuplicateRegistration) {}

TEST_F(AuthenticatorImplRequestDelegateTest,
       FailureReasonForMissingRegistration) {}

TEST_F(AuthenticatorImplTest, NoNonAuthoritativeTransports) {}

TEST_F(AuthenticatorImplTest, TransportsFromGetInfo) {}

TEST_F(AuthenticatorImplTest, TransportsInAttestationCertificate) {}

TEST_F(AuthenticatorImplTest, ExtensionHMACSecret) {}

// Tests that for an authenticator that does not support batching, credential
// lists get probed silently to work around authenticators rejecting exclude
// lists exceeding a certain size.
TEST_F(AuthenticatorImplTest, MakeCredentialWithLargeExcludeList) {}

TEST_F(AuthenticatorImplTest, GetAssertionResultMetricError) {}

TEST_F(AuthenticatorImplTest, GetAssertionResultMetricSuccess) {}

TEST_F(AuthenticatorImplTest, MakeCredentialResultMetricError) {}

TEST_F(AuthenticatorImplTest, MakeCredentialResultMetricSuccess) {}

// Tests that for an authenticator that does not support batching, credential
// lists get probed silently to work around authenticators rejecting allow lists
// exceeding a certain size.
TEST_F(AuthenticatorImplTest, GetAssertionWithLargeAllowList) {}

// Tests that, regardless of batching support, GetAssertion requests with a
// single allowed credential ID don't result in a silent probing request.
TEST_F(AuthenticatorImplTest, GetAssertionSingleElementAllowListDoesNotProbe) {}

// Tests that an allow list that fits into a single batch does not result in a
// silent probing request.
TEST_F(AuthenticatorImplTest, GetAssertionSingleBatchListDoesNotProbe) {}

TEST_F(AuthenticatorImplTest, OptionalCredentialInAssertionResponse) {}

// Tests that an allowList with only credential IDs of a length exceeding the
// maxCredentialIdLength parameter is not mistakenly interpreted as an empty
// allow list.
TEST_F(AuthenticatorImplTest, AllowListWithOnlyOversizedCredentialIds) {}

// Tests that duplicate credential IDs are filtered from an assertion allow_list
// parameter.
TEST_F(AuthenticatorImplTest, AllowListWithDuplicateCredentialIds) {}

// Tests that duplicate credential IDs are filtered from a registration
// exclude_list parameter.
TEST_F(AuthenticatorImplTest, ExcludeListWithDuplicateCredentialIds) {}

// Test that allow lists over 64 entries are verboten.
TEST_F(AuthenticatorImplTest, OversizedAllowList) {}

// Test that exclude lists over 64 entries are verboten.
TEST_F(AuthenticatorImplTest, OversizedExcludeList) {}

TEST_F(AuthenticatorImplTest, NoUnexpectedAuthenticatorExtensions) {}

TEST_F(AuthenticatorImplTest, NoUnexpectedClientExtensions) {}

// Tests that on an authenticator that supports batching, exclude lists that fit
// into a single batch are sent without probing.
TEST_F(AuthenticatorImplTest, ExcludeListBatching) {}

TEST_F(AuthenticatorImplTest, GetPublicKey) {}

TEST_F(AuthenticatorImplTest, AlgorithmsOmitted) {}

TEST_F(AuthenticatorImplTest, VirtualAuthenticatorPublicKeyAlgos) {}

TEST_F(AuthenticatorImplTest, TestAuthenticationTransport) {}

TEST_F(AuthenticatorImplTest, ResetDiscoveryFactoryOverride) {}

TEST_F(AuthenticatorImplTest, InvalidU2FPublicKey) {}

TEST_F(AuthenticatorImplTest, InvalidU2FSignature) {}

TEST_F(AuthenticatorImplTest, CredBlob) {}

TEST_F(AuthenticatorImplTest, MinPINLength) {}

// Regression test for crbug.com/1257281.
// Tests that a request is not cancelled when an authenticator returns
// CTAP2_ERR_KEEPALIVE_CANCEL after selecting another authenticator for a
// request.
TEST_F(AuthenticatorImplTest, CancellingAuthenticatorDoesNotTerminateRequest) {}

TEST_F(AuthenticatorImplTest, PRFWithoutSupport) {}

static constexpr char kTestPIN[] =;
static constexpr char16_t kTestPIN16[] =;

class UVTestAuthenticatorClientDelegate
    : public AuthenticatorRequestClientDelegate {};

class UVTestAuthenticatorContentBrowserClient : public ContentBrowserClient {};

class UVAuthenticatorImplTest : public AuthenticatorImplTest {};

PINReason;
PINError;

// PINExpectation represent expected |mode|, |attempts|, |min_pin_length| and
// the PIN to answer with.
struct PINExpectation {};

class PINTestAuthenticatorRequestDelegate
    : public AuthenticatorRequestClientDelegate {};

class PINTestAuthenticatorContentBrowserClient : public ContentBrowserClient {};

class PINAuthenticatorImplTest : public UVAuthenticatorImplTest {};

static constexpr device::UserVerificationRequirement kUVLevel[3] =;

static const char* kUVDescription[3] =;

static const char* kPINSupportDescription[3] =;

TEST_F(PINAuthenticatorImplTest, MakeCredential) {}

TEST_F(PINAuthenticatorImplTest, MakeCredentialSoftLock) {}

TEST_F(PINAuthenticatorImplTest, MakeCredentialHardLock) {}

TEST_F(PINAuthenticatorImplTest, MakeCredentialWrongPINFirst) {}

TEST_F(PINAuthenticatorImplTest, MakeCredentialSkipPINTouch) {}

TEST_F(PINAuthenticatorImplTest, MakeCredentialDontSkipPINTouch) {}

TEST_F(PINAuthenticatorImplTest, MakeCredentialAlwaysUv) {}

TEST_F(PINAuthenticatorImplTest, MakeCredentialMinPINLengthNewPIN) {}

TEST_F(PINAuthenticatorImplTest, MakeCredentialMinPINLengthExistingPIN) {}

TEST_F(PINAuthenticatorImplTest, MakeCredentialForcePINChange) {}

TEST_F(PINAuthenticatorImplTest, MakeCredUvNotRqd) {}

TEST_F(PINAuthenticatorImplTest, MakeCredUvNotRqdAndAlwaysUv) {}

TEST_F(PINAuthenticatorImplTest, MakeCredentialHMACSecret) {}

TEST_F(PINAuthenticatorImplTest, GetAssertion) {}

TEST_F(PINAuthenticatorImplTest, GetAssertionSoftLock) {}

TEST_F(PINAuthenticatorImplTest, GetAssertionHardLock) {}

TEST_F(PINAuthenticatorImplTest, GetAssertionSkipPINTouch) {}

TEST_F(PINAuthenticatorImplTest, GetAssertionDontSkipPINTouch) {}

TEST_F(PINAuthenticatorImplTest, GetAssertionAlwaysUv) {}

TEST_F(PINAuthenticatorImplTest, MakeCredentialNoSupportedAlgorithm) {}

TEST_F(PINAuthenticatorImplTest, PRFCreatedOnCTAP2) {}

// Test that pinUvAuthToken gets sent with every single batch of an exclude
// list. If it wasn't, any batch after the first would be unable to match
// credProtect=uvRequired credentials.
TEST_F(PINAuthenticatorImplTest, ExcludeListBatchesIncludePinToken) {}

TEST_F(PINAuthenticatorImplTest, RemoveSecondAuthenticator) {}

TEST_F(PINAuthenticatorImplTest, AppIdExcludeExtensionWithPinRequiredError) {}

class InternalUVAuthenticatorImplTest : public UVAuthenticatorImplTest {};

TEST_F(InternalUVAuthenticatorImplTest, MakeCredential) {}

// Test falling back to PIN for devices that support internal user verification
// but not uv token.
TEST_F(InternalUVAuthenticatorImplTest, MakeCredentialFallBackToPin) {}

// Test making a credential on an authenticator that supports biometric
// enrollment but has no fingerprints enrolled.
TEST_F(InternalUVAuthenticatorImplTest, MakeCredentialInlineBioEnrollment) {}

// Test making a credential skipping biometric enrollment during credential
// creation.
TEST_F(InternalUVAuthenticatorImplTest, MakeCredentialSkipInlineBioEnrollment) {}

TEST_F(InternalUVAuthenticatorImplTest, MakeCredUvNotRqd) {}

TEST_F(InternalUVAuthenticatorImplTest, GetAssertion) {}

// Test falling back to PIN for devices that support internal user verification
// but not uv token.
TEST_F(InternalUVAuthenticatorImplTest, GetAssertionFallbackToPIN) {}

class UVTokenAuthenticatorImplTest : public UVAuthenticatorImplTest {};

TEST_F(UVTokenAuthenticatorImplTest, GetAssertionUVToken) {}

// Test exhausting all internal user verification attempts on an authenticator
// that does not support PINs.
TEST_F(UVTokenAuthenticatorImplTest, GetAssertionUvFails) {}

// Test exhausting all internal user verification attempts on an authenticator
// that supports PINs.
TEST_F(UVTokenAuthenticatorImplTest, GetAssertionFallBackToPin) {}

// Tests that a device supporting UV token with UV blocked at the start of a get
// assertion request gets a touch and then falls back to PIN.
TEST_F(UVTokenAuthenticatorImplTest, GetAssertionUvBlockedFallBackToPin) {}

TEST_F(UVTokenAuthenticatorImplTest, MakeCredentialUVToken) {}

// Test exhausting all internal user verification attempts on an authenticator
// that does not support PINs.
TEST_F(UVTokenAuthenticatorImplTest, MakeCredentialUvFails) {}

// Test exhausting all internal user verification attempts on an authenticator
// that supports PINs.
TEST_F(UVTokenAuthenticatorImplTest, MakeCredentialFallBackToPin) {}

// Tests that a device supporting UV token with UV blocked at the start of a get
// assertion request gets a touch and then falls back to PIN.
TEST_F(UVTokenAuthenticatorImplTest, MakeCredentialUvBlockedFallBackToPin) {}

class BlockingAuthenticatorRequestDelegate
    : public AuthenticatorRequestClientDelegate {};

class BlockingDelegateContentBrowserClient : public ContentBrowserClient {};

class BlockingDelegateAuthenticatorImplTest : public AuthenticatorImplTest {};

TEST_F(BlockingDelegateAuthenticatorImplTest, PostCancelMessage) {}

// ResidentKeyTestAuthenticatorRequestDelegate is a delegate that:
//   a) always returns |kTestPIN| when asked for a PIN.
//   b) sorts potential resident-key accounts by user ID, maps them to a string
//      form ("<hex user ID>:<user name>:<display name>"), joins the strings
//      with "/", and compares the result against |expected_accounts|.
//   c) auto-selects the account with the user ID matching |selected_user_id|.
class ResidentKeyTestAuthenticatorRequestDelegate
    : public AuthenticatorRequestClientDelegate {};

class ResidentKeyTestAuthenticatorContentBrowserClient
    : public ContentBrowserClient {};

class ResidentKeyAuthenticatorImplTest : public UVAuthenticatorImplTest {};

TEST_F(ResidentKeyAuthenticatorImplTest, MakeCredentialRkRequired) {}

TEST_F(ResidentKeyAuthenticatorImplTest, MakeCredentialRkPreferred) {}

TEST_F(ResidentKeyAuthenticatorImplTest, MakeCredentialRkPreferredStorageFull) {}

TEST_F(ResidentKeyAuthenticatorImplTest, MakeCredentialRkPreferredSetsPIN) {}

TEST_F(ResidentKeyAuthenticatorImplTest, StorageFull) {}

TEST_F(ResidentKeyAuthenticatorImplTest,
       MakeCredentialEmptyFields_SecurityKey) {}

// Regression test for crbug.com/346835891.
TEST_F(ResidentKeyAuthenticatorImplTest, MakeCredentialEmptyFields_Phone) {}

TEST_F(ResidentKeyAuthenticatorImplTest, GetAssertionSingleNoPII) {}

TEST_F(ResidentKeyAuthenticatorImplTest, GetAssertionUserSelected) {}

TEST_F(ResidentKeyAuthenticatorImplTest, GetAssertionSingleWithPII) {}

TEST_F(ResidentKeyAuthenticatorImplTest, GetAssertionMulti) {}

TEST_F(ResidentKeyAuthenticatorImplTest, GetAssertionUVDiscouraged) {}

static const char* BlobSupportDescription(device::LargeBlobSupport support) {}

TEST_F(ResidentKeyAuthenticatorImplTest, MakeCredentialLargeBlob) {}

TEST_F(ResidentKeyAuthenticatorImplTest, GetAssertionLargeBlobRead) {}

TEST_F(ResidentKeyAuthenticatorImplTest, GetAssertionLargeBlobWrite) {}

TEST_F(ResidentKeyAuthenticatorImplTest,
       GetAssertionLargeBlobExtensionNoSupport) {}

TEST_F(ResidentKeyAuthenticatorImplTest, GetAssertionLargeBlobExtension) {}

static const char* ProtectionPolicyDescription(
    blink::mojom::ProtectionPolicy p) {}

static const char* CredProtectDescription(device::CredProtect cred_protect) {}

TEST_F(ResidentKeyAuthenticatorImplTest, CredProtectRegistration) {}

TEST_F(ResidentKeyAuthenticatorImplTest, AuthenticatorSetsCredProtect) {}

TEST_F(ResidentKeyAuthenticatorImplTest, AuthenticatorDefaultCredProtect) {}

TEST_F(ResidentKeyAuthenticatorImplTest, ProtectedNonResidentCreds) {}

TEST_F(ResidentKeyAuthenticatorImplTest, WithAppIDExtension) {}

#if BUILDFLAG(IS_WIN)
// Requests with a credProtect extension that have |enforce_protection_policy|
// set should be rejected if the Windows WebAuthn API doesn't support
// credProtect.
TEST_F(ResidentKeyAuthenticatorImplTest, WinCredProtectApiVersion) {
  // The canned response returned by the Windows API fake is for acme.com.
  virtual_device_factory_->set_discover_win_webauthn_api_authenticator(true);
  fake_win_webauthn_api_.set_available(true);
  NavigateAndCommit(GURL("https://acme.com"));
  for (const bool supports_cred_protect : {false, true}) {
    SCOPED_TRACE(testing::Message()
                 << "supports_cred_protect: " << supports_cred_protect);

    fake_win_webauthn_api_.set_version(supports_cred_protect
                                           ? WEBAUTHN_API_VERSION_2
                                           : WEBAUTHN_API_VERSION_1);

    PublicKeyCredentialCreationOptionsPtr options = make_credential_options();
    options->relying_party = device::PublicKeyCredentialRpEntity();
    options->relying_party.id = device::test_data::kRelyingPartyId;
    options->relying_party.name = "";
    options->authenticator_selection->user_verification_requirement =
        device::UserVerificationRequirement::kRequired;
    options->authenticator_selection->resident_key =
        device::ResidentKeyRequirement::kRequired;
    options->protection_policy =
        blink::mojom::ProtectionPolicy::UV_OR_CRED_ID_REQUIRED;
    options->enforce_protection_policy = true;

    EXPECT_EQ(AuthenticatorMakeCredential(std::move(options)).status,
              supports_cred_protect ? AuthenticatorStatus::SUCCESS
                                    : AuthenticatorStatus::NOT_ALLOWED_ERROR);
  }
}

// Tests that the incognito flag is plumbed through conditional UI requests.
TEST_F(ResidentKeyAuthenticatorImplTest, ConditionalUI_Incognito) {
  virtual_device_factory_->set_discover_win_webauthn_api_authenticator(true);
  fake_win_webauthn_api_.set_available(true);
  fake_win_webauthn_api_.set_version(WEBAUTHN_API_VERSION_4);
  fake_win_webauthn_api_.set_supports_silent_discovery(true);
  device::PublicKeyCredentialRpEntity rp(kTestRelyingPartyId);
  device::PublicKeyCredentialUserEntity user({1, 2, 3, 4});
  fake_win_webauthn_api_.InjectDiscoverableCredential(
      /*credential_id=*/{{4, 3, 2, 1}}, std::move(rp), std::move(user));

  // |SelectAccount| should not be called for conditional UI requests.
  test_client_.delegate_config.expected_accounts = "<invalid>";
  test_client_.delegate_config.expect_conditional = true;

  for (bool is_off_the_record : {true, false}) {
    SCOPED_TRACE(is_off_the_record ? "off the record" : "on the record");
    static_cast<TestBrowserContext*>(GetBrowserContext())
        ->set_is_off_the_record(is_off_the_record);
    PublicKeyCredentialRequestOptionsPtr options(get_credential_options());
    options->is_conditional = true;
    GetAssertionResult result = AuthenticatorGetAssertion(std::move(options));
    EXPECT_EQ(AuthenticatorStatus::SUCCESS, result.status);
    ASSERT_TRUE(fake_win_webauthn_api_.last_get_credentials_options());
    EXPECT_EQ(fake_win_webauthn_api_.last_get_credentials_options()
                  ->bBrowserInPrivateMode,
              is_off_the_record);
  }
}

// Tests that attempting to make a credential with large blob = required and
// attachment = platform on Windows fails and the request is not sent to the
// WebAuthn API.
// This is because largeBlob = required is ignored by the Windows platform
// authenticator at the time of writing (Feb 2023).
TEST_F(ResidentKeyAuthenticatorImplTest, MakeCredentialLargeBlobWinPlatform) {
  fake_win_webauthn_api_.set_available(true);
  fake_win_webauthn_api_.set_version(WEBAUTHN_API_VERSION_3);
  PublicKeyCredentialCreationOptionsPtr options =
      GetTestPublicKeyCredentialCreationOptions();
  options->large_blob_enable = device::LargeBlobSupport::kRequired;
  options->authenticator_selection->resident_key =
      device::ResidentKeyRequirement::kRequired;
  options->authenticator_selection->authenticator_attachment =
      device::AuthenticatorAttachment::kPlatform;
  MakeCredentialResult result = AuthenticatorMakeCredential(std::move(options));
  EXPECT_EQ(result.status, AuthenticatorStatus::NOT_ALLOWED_ERROR);
  EXPECT_FALSE(fake_win_webauthn_api_.last_make_credential_options());
}
#endif  // BUILDFLAG(IS_WIN)

// Tests that chrome does not attempt setting the PRF extension during a
// PinUvAuthToken GetAssertion request if it is not supported by the
// authenticator.
// Regression test for crbug.com/1408786.
TEST_F(ResidentKeyAuthenticatorImplTest, PRFNotSupportedWithPinUvAuthToken) {}

TEST_F(ResidentKeyAuthenticatorImplTest, PRFExtension) {}

// Tests that the PRF function is evaluated for all credentials in an empty
// allow-list request. Regression test for crbug.com/1520646.
TEST_F(ResidentKeyAuthenticatorImplTest, PRFEvaluationForMultipleCreds) {}

TEST_F(ResidentKeyAuthenticatorImplTest, PRFEvaluationDuringMakeCredential) {}

TEST_F(ResidentKeyAuthenticatorImplTest, MakeCredentialPRFExtension) {}

TEST_F(ResidentKeyAuthenticatorImplTest,
       PRFExtensionOnUnconfiguredAuthenticator) {}

TEST_F(ResidentKeyAuthenticatorImplTest, ConditionalUI) {}

// Tests that the AuthenticatorRequestDelegate can choose a known platform
// authentictor credential as "preselected", which causes the request to be
// specialized to the chosen credential ID and post-request account selection UI
// to be skipped.
TEST_F(ResidentKeyAuthenticatorImplTest, PreselectDiscoverableCredential) {}

// Tests that preselecting a credential sets the response user entity to that of
// the credential metadata if it is not present in the response.
// Regression test for crbug.com/329412574.
TEST_F(ResidentKeyAuthenticatorImplTest, PreselectCredentialUserEntity) {}

class InternalAuthenticatorImplTest : public AuthenticatorTestBase {};

// Regression test for crbug.com/1433416.
TEST_F(InternalAuthenticatorImplTest, MakeCredentialSkipTLSCheck) {}

// Regression test for crbug.com/1433416.
TEST_F(InternalAuthenticatorImplTest, GetAssertionSkipTLSCheck) {}

// Verify behavior for various combinations of origins and RP IDs.
TEST_F(InternalAuthenticatorImplTest, MakeCredentialOriginAndRpIds) {}

// Verify behavior for various combinations of origins and RP IDs.
TEST_F(InternalAuthenticatorImplTest, GetAssertionOriginAndRpIds) {}

#if BUILDFLAG(IS_MAC)
class TouchIdAuthenticatorImplTest : public AuthenticatorImplTest {
 protected:
  using Credential = device::fido::mac::Credential;
  using CredentialMetadata = device::fido::mac::CredentialMetadata;

  void SetUp() override {
    AuthenticatorImplTest::SetUp();
    test_client_.web_authentication_delegate.touch_id_authenticator_config =
        config_;
    test_client_.web_authentication_delegate.supports_resident_keys = true;
    old_client_ = SetBrowserClientForTesting(&test_client_);
  }

  void TearDown() override {
    SetBrowserClientForTesting(old_client_);
    AuthenticatorImplTest::TearDown();
  }

  void ResetVirtualDevice() override {}

  std::vector<Credential> GetCredentials(const std::string& rp_id) {
    return device::fido::mac::TouchIdCredentialStore::FindCredentialsForTesting(
        config_, rp_id);
  }

  TestAuthenticatorContentBrowserClient test_client_;
  raw_ptr<ContentBrowserClient> old_client_ = nullptr;
  device::fido::mac::AuthenticatorConfig config_{
      .keychain_access_group = "test-keychain-access-group",
      .metadata_secret = "TestMetadataSecret"};
  device::fido::mac::ScopedTouchIdTestEnvironment touch_id_test_environment_{
      config_};
};

TEST_F(TouchIdAuthenticatorImplTest, IsUVPAA) {
  NavigateAndCommit(GURL(kTestOrigin1));
  for (const bool touch_id_available : {false, true}) {
    SCOPED_TRACE(::testing::Message()
                 << "touch_id_available=" << touch_id_available);
    touch_id_test_environment_.SetTouchIdAvailable(touch_id_available);
    EXPECT_EQ(AuthenticatorIsUvpaa(), touch_id_available);
  }
}

TEST_F(TouchIdAuthenticatorImplTest, MakeCredential) {
  NavigateAndCommit(GURL(kTestOrigin1));
  mojo::Remote<blink::mojom::Authenticator> authenticator =
      ConnectToAuthenticator();
  auto options = GetTestPublicKeyCredentialCreationOptions();
  options->authenticator_selection->authenticator_attachment =
      device::AuthenticatorAttachment::kPlatform;
  touch_id_test_environment_.SimulateTouchIdPromptSuccess();
  EXPECT_EQ(AuthenticatorMakeCredential(std::move(options)).status,
            AuthenticatorStatus::SUCCESS);
  auto credentials = GetCredentials(kTestRelyingPartyId);
  EXPECT_EQ(credentials.size(), 1u);
  const CredentialMetadata& metadata = credentials.at(0).metadata;
  // New credentials are always created discoverable.
  EXPECT_TRUE(metadata.is_resident);
  auto expected_user = GetTestPublicKeyCredentialUserEntity();
  EXPECT_EQ(metadata.ToPublicKeyCredentialUserEntity(), expected_user);
}

TEST_F(TouchIdAuthenticatorImplTest, OptionalUv) {
  NavigateAndCommit(GURL(kTestOrigin1));
  mojo::Remote<blink::mojom::Authenticator> authenticator =
      ConnectToAuthenticator();
  // Disable biometrics to verify that requests without uv required do not
  // prompt the user for their macOS password.
  touch_id_test_environment_.keychain()->SetUVMethod(
      crypto::ScopedFakeAppleKeychainV2::UVMethod::kPasswordOnly);
  for (const auto uv : {device::UserVerificationRequirement::kDiscouraged,
                        device::UserVerificationRequirement::kPreferred,
                        device::UserVerificationRequirement::kRequired}) {
    SCOPED_TRACE(static_cast<int>(uv));
    auto options = GetTestPublicKeyCredentialCreationOptions();
    options->authenticator_selection->authenticator_attachment =
        device::AuthenticatorAttachment::kPlatform;
    // Set rk to required. On platform authenticators Chrome should not
    // universally require UV to make make a resident/discoverable credential,
    // like it would on a security key.
    options->authenticator_selection->resident_key =
        device::ResidentKeyRequirement::kRequired;
    options->authenticator_selection->user_verification_requirement = uv;
    bool requires_uv = uv == device::UserVerificationRequirement::kRequired;
    if (requires_uv) {
      touch_id_test_environment_.SimulateTouchIdPromptSuccess();
    } else {
      touch_id_test_environment_.DoNotResolveNextPrompt();
    }
    auto result = AuthenticatorMakeCredential(std::move(options));
    EXPECT_EQ(result.status, AuthenticatorStatus::SUCCESS);
    EXPECT_EQ(HasUV(result.response), requires_uv);
    auto credentials = GetCredentials(kTestRelyingPartyId);
    EXPECT_EQ(credentials.size(), 1u);

    auto assertion_options = GetTestPublicKeyCredentialRequestOptions();
    assertion_options->user_verification = uv;
    assertion_options->allow_credentials =
        std::vector<device::PublicKeyCredentialDescriptor>(
            {{device::CredentialType::kPublicKey,
              credentials[0].credential_id}});
    if (requires_uv) {
      touch_id_test_environment_.SimulateTouchIdPromptSuccess();
    } else {
      touch_id_test_environment_.DoNotResolveNextPrompt();
    }
    auto assertion = AuthenticatorGetAssertion(std::move(assertion_options));
    EXPECT_EQ(assertion.status, AuthenticatorStatus::SUCCESS);
    EXPECT_EQ(HasUV(assertion.response), requires_uv);
  }
}

TEST_F(TouchIdAuthenticatorImplTest, MakeCredential_Resident) {
  NavigateAndCommit(GURL(kTestOrigin1));
  mojo::Remote<blink::mojom::Authenticator> authenticator =
      ConnectToAuthenticator();
  auto options = GetTestPublicKeyCredentialCreationOptions();
  options->authenticator_selection->authenticator_attachment =
      device::AuthenticatorAttachment::kPlatform;
  options->authenticator_selection->resident_key =
      device::ResidentKeyRequirement::kRequired;
  touch_id_test_environment_.SimulateTouchIdPromptSuccess();
  EXPECT_EQ(AuthenticatorMakeCredential(std::move(options)).status,
            AuthenticatorStatus::SUCCESS);
  auto credentials = GetCredentials(kTestRelyingPartyId);
  EXPECT_EQ(credentials.size(), 1u);
  EXPECT_TRUE(credentials.at(0).metadata.is_resident);
}

TEST_F(TouchIdAuthenticatorImplTest, MakeCredential_Eviction) {
  NavigateAndCommit(GURL(kTestOrigin1));
  mojo::Remote<blink::mojom::Authenticator> authenticator =
      ConnectToAuthenticator();

  // A resident credential will overwrite the non-resident one.
  auto options = GetTestPublicKeyCredentialCreationOptions();
  options->authenticator_selection->authenticator_attachment =
      device::AuthenticatorAttachment::kPlatform;
  options->authenticator_selection->resident_key =
      device::ResidentKeyRequirement::kRequired;
  touch_id_test_environment_.SimulateTouchIdPromptSuccess();
  EXPECT_EQ(AuthenticatorMakeCredential(options->Clone()).status,
            AuthenticatorStatus::SUCCESS);
  EXPECT_EQ(GetCredentials(kTestRelyingPartyId).size(), 1u);

  // Another resident credential for the same user will evict the previous one.
  touch_id_test_environment_.SimulateTouchIdPromptSuccess();
  EXPECT_EQ(AuthenticatorMakeCredential(options->Clone()).status,
            AuthenticatorStatus::SUCCESS);
  EXPECT_EQ(GetCredentials(kTestRelyingPartyId).size(), 1u);

  // But a resident credential for a different user shouldn't.
  touch_id_test_environment_.SimulateTouchIdPromptSuccess();
  options->user.id = std::vector<uint8_t>({99});
  EXPECT_EQ(AuthenticatorMakeCredential(std::move(options)).status,
            AuthenticatorStatus::SUCCESS);
  EXPECT_EQ(GetCredentials(kTestRelyingPartyId).size(), 2u);

  // Neither should a credential for a different RP.
  touch_id_test_environment_.SimulateTouchIdPromptSuccess();
  options = GetTestPublicKeyCredentialCreationOptions();
  options->authenticator_selection->authenticator_attachment =
      device::AuthenticatorAttachment::kPlatform;
  options->relying_party.id = "a.google.com";
  EXPECT_EQ(AuthenticatorMakeCredential(std::move(options)).status,
            AuthenticatorStatus::SUCCESS);
  EXPECT_EQ(GetCredentials(kTestRelyingPartyId).size(), 2u);
}

class ICloudKeychainAuthenticatorImplTest : public AuthenticatorImplTest {
 protected:
  class InspectTAIAuthenticatorRequestDelegate
      : public AuthenticatorRequestClientDelegate {
   public:
    using Callback = base::RepeatingCallback<void(
        const device::FidoRequestHandlerBase::TransportAvailabilityInfo&)>;
    explicit InspectTAIAuthenticatorRequestDelegate(Callback callback)
        : callback_(std::move(callback)) {}

    void ConfigureDiscoveries(
        const url::Origin& origin,
        const std::string& rp_id,
        RequestSource request_source,
        device::FidoRequestType request_type,
        std::optional<device::ResidentKeyRequirement> resident_key_requirement,
        device::UserVerificationRequirement user_verification_requirement,
        std::optional<std::string_view> user_name,
        base::span<const device::CableDiscoveryData> pairings_from_extension,
        bool is_enclave_authenticator_available,
        device::FidoDiscoveryFactory* fido_discovery_factory) override {
      // nswindow must be set for the iCloud Keychain authenticator to be
      // discovered.
      fido_discovery_factory->set_nswindow(
          device::fido::icloud_keychain::kFakeNSWindowForTesting);
    }

    void OnTransportAvailabilityEnumerated(
        device::FidoRequestHandlerBase::TransportAvailabilityInfo tai)
        override {
      callback_.Run(tai);
    }

   private:
    Callback callback_;
  };

  class InspectTAIContentBrowserClient : public ContentBrowserClient {
   public:
    explicit InspectTAIContentBrowserClient(
        InspectTAIAuthenticatorRequestDelegate::Callback callback)
        : callback_(std::move(callback)) {}

    std::unique_ptr<AuthenticatorRequestClientDelegate>
    GetWebAuthenticationRequestDelegate(
        RenderFrameHost* render_frame_host) override {
      return std::make_unique<InspectTAIAuthenticatorRequestDelegate>(
          callback_);
    }

   private:
    InspectTAIAuthenticatorRequestDelegate::Callback callback_;
  };

  void SetUp() override {
    AuthenticatorImplTest::SetUp();
    old_client_ = SetBrowserClientForTesting(&test_client_);
    // This test uses the real discoveries and sets the transports on an
    // allowlist entry to limit it to kInternal.
    virtual_device_factory_ = nullptr;
    AuthenticatorEnvironment::GetInstance()->Reset();
  }

  void TearDown() override {
    SetBrowserClientForTesting(old_client_);
    AuthenticatorImplTest::TearDown();
  }

  void OnTransportAvailabilityEnumerated(
      const device::FidoRequestHandlerBase::TransportAvailabilityInfo& tai) {
    if (tai_callback_) {
      std::move(tai_callback_).Run(tai);
    }
  }

  static std::vector<device::DiscoverableCredentialMetadata> GetCredentials() {
    device::DiscoverableCredentialMetadata metadata(
        device::AuthenticatorType::kICloudKeychain, kTestRelyingPartyId,
        {1, 2, 3, 4}, {{5, 6, 7, 8}, "name", "displayName"});
    return {std::move(metadata)};
  }

  InspectTAIContentBrowserClient test_client_{base::BindRepeating(
      &ICloudKeychainAuthenticatorImplTest::OnTransportAvailabilityEnumerated,
      base::Unretained(this))};
  raw_ptr<ContentBrowserClient> old_client_ = nullptr;
  InspectTAIAuthenticatorRequestDelegate::Callback tai_callback_;
};

// Gardener 2024-06-18: Disabled due to asan failures (crbug.com/347287026).
TEST_F(ICloudKeychainAuthenticatorImplTest, DISABLED_Discovery) {
  if (__builtin_available(macOS 13.5, *)) {
    NavigateAndCommit(GURL(kTestOrigin1));
    device::fido::icloud_keychain::ScopedTestEnvironment test_environment(
        GetCredentials());
    bool tai_seen = false;
    tai_callback_ = base::BindLambdaForTesting(
        [&tai_seen](
            const device::FidoRequestHandlerBase::TransportAvailabilityInfo&
                tai) {
          tai_seen = true;
          CHECK_EQ(tai.has_icloud_keychain, true);
          CHECK_EQ(tai.recognized_credentials.size(), 1u);
          CHECK_EQ(tai.has_icloud_keychain_credential,
                   device::FidoRequestHandlerBase::RecognizedCredential::
                       kHasRecognizedCredential);

          CHECK_EQ(tai.recognized_credentials[0].user.name.value(), "name");
        });

    auto options = GetTestPublicKeyCredentialRequestOptions();
    options->allow_credentials.clear();
    options->allow_credentials.push_back(device::PublicKeyCredentialDescriptor(
        device::CredentialType::kPublicKey, {1, 2, 3, 4},
        {device::FidoTransportProtocol::kInternal}));
    const auto result = AuthenticatorGetAssertion(std::move(options));
    EXPECT_EQ(result.status, AuthenticatorStatus::NOT_ALLOWED_ERROR);
    EXPECT_TRUE(tai_seen);

  } else {
    GTEST_SKIP() << "Need macOS 13.3 for this test";
  }
}

#endif  // BUILDFLAG(IS_MAC)

// AuthenticatorCableV2Test tests features of the caBLEv2 transport and
// protocol.
class AuthenticatorCableV2Test : public AuthenticatorImplRequestDelegateTest {};

TEST_F(AuthenticatorCableV2Test, QRBasedWithNoPairing) {}

TEST_F(AuthenticatorCableV2Test, HandshakeError) {}

// Test having the network service crash between creating a discovery and
// performing a cable transaction. Regression test for crbug.com/332724843.
TEST_F(AuthenticatorCableV2Test, NetworkServiceCrash) {}

TEST_F(AuthenticatorCableV2Test, PairingBased) {}

TEST_F(AuthenticatorCableV2Test, PairingBasedWithConnectionSignal) {}

static std::unique_ptr<device::cablev2::Pairing> DummyPairing() {}

TEST_F(AuthenticatorCableV2Test, ContactIDDisabled) {}

// ServerLinkValues contains keys that mimic those created by a site doing
// caBLEv2 server-link.
struct ServerLinkValues {};

// CreateServerLink simulates a site doing caBLEv2 server-link and calculates
// server-link values that could be sent to the desktop and phone sides of a
// transaction.
static ServerLinkValues CreateServerLink() {}

TEST_F(AuthenticatorCableV2Test, ServerLink) {}

TEST_F(AuthenticatorCableV2Test, LateLinking) {}

// AuthenticatorCableV2AuthenticatorTest tests aspects of the authenticator
// implementation, rather than of the underlying caBLEv2 transport.
class AuthenticatorCableV2AuthenticatorTest
    : public AuthenticatorCableV2Test,
      public device::cablev2::authenticator::Observer {};

TEST_F(AuthenticatorCableV2AuthenticatorTest, GetAssertion) {}

TEST_F(AuthenticatorCableV2AuthenticatorTest, MakeDiscoverableCredential) {}

TEST_F(AuthenticatorCableV2AuthenticatorTest, EmptyAllowList) {}

TEST_F(AuthenticatorCableV2AuthenticatorTest, PRFMakeCredential) {}

static std::vector<uint8_t> HashPRFInput(base::span<const uint8_t> input) {}

static std::tuple<PublicKeyCredentialRequestOptionsPtr,
                  std::vector<uint8_t>,
                  std::vector<uint8_t>>
BuildPRFGetAssertion(device::VirtualCtap2Device& virtual_device,
                     bool use_eval_by_credential) {}

TEST_F(AuthenticatorCableV2AuthenticatorTest, PRFGetAssertion) {}

TEST_F(AuthenticatorCableV2AuthenticatorTest, PRFGetAssertionByCredential) {}

// AuthenticatorImplWithRequestProxyTest tests behavior with an installed
// TestWebAuthenticationRequestProxy that takes over WebAuthn request handling.
class AuthenticatorImplWithRequestProxyTest : public AuthenticatorImplTest {};

TEST_F(AuthenticatorImplWithRequestProxyTest, Inactive) {}

TEST_F(AuthenticatorImplWithRequestProxyTest, IsUVPAA) {}

TEST_F(AuthenticatorImplWithRequestProxyTest, IsConditionalMediationAvailable) {}

// Temporary regression test for crbug.com/1489468.
// TODO(crbug.com/40284051): Remove after passkey metadata syncing is enabled by
// default.
TEST_F(AuthenticatorImplWithRequestProxyTest,
       IsConditionalMediationAvailable_MetadataSyncing) {}

TEST_F(AuthenticatorImplWithRequestProxyTest, MakeCredential) {}

// Verify requests with an attached proxy run RP ID checks.
TEST_F(AuthenticatorImplWithRequestProxyTest, MakeCredentialOriginAndRpIds) {}

// Tests that attempting to make a credential when a request is already proxied
// fails with NotAllowedError.
TEST_F(AuthenticatorImplWithRequestProxyTest, MakeCredentialAlreadyProxied) {}

TEST_F(AuthenticatorImplWithRequestProxyTest, AppId) {}

TEST_F(AuthenticatorImplWithRequestProxyTest, MakeCredential_Timeout) {}

TEST_F(AuthenticatorImplWithRequestProxyTest, GetAssertion) {}

// Tests that attempting to get an assertion when a request is already proxied
// fails with NotAllowedError.
TEST_F(AuthenticatorImplWithRequestProxyTest, GetAssertionAlreadyProxied) {}

// Verify that Conditional UI requests are not proxied.
TEST_F(AuthenticatorImplWithRequestProxyTest, GetAssertionConditionalUI) {}

// Verify requests with an attached proxy run RP ID checks.
TEST_F(AuthenticatorImplWithRequestProxyTest, GetAssertionOriginAndRpIds) {}

TEST_F(AuthenticatorImplWithRequestProxyTest, GetAssertion_Timeout) {}

TEST_F(AuthenticatorImplWithRequestProxyTest,
       VirtualAuthenticatorTakesPrecedence) {}

}  // namespace content