#include "chrome/browser/webauthn/chrome_authenticator_request_delegate.h"
#include <algorithm>
#include <array>
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "base/containers/contains.h"
#include "base/containers/span.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/test/scoped_command_line.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_future.h"
#include "base/values.h"
#include "build/build_config.h"
#include "chrome/browser/password_manager/chrome_webauthn_credentials_delegate_factory.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/sync/sync_service_factory.h"
#include "chrome/browser/webauthn/authenticator_request_dialog_controller.h"
#include "chrome/browser/webauthn/authenticator_request_dialog_model.h"
#include "chrome/browser/webauthn/passkey_model_factory.h"
#include "chrome/browser/webauthn/webauthn_pref_names.h"
#include "chrome/browser/webauthn/webauthn_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/network_session_configurator/common/network_switches.h"
#include "components/prefs/pref_service.h"
#include "components/signin/public/base/consent_level.h"
#include "components/signin/public/identity_manager/identity_test_utils.h"
#include "components/sync/base/features.h"
#include "components/sync/base/user_selectable_type.h"
#include "components/sync/test/test_sync_service.h"
#include "components/webauthn/core/browser/passkey_model.h"
#include "components/webauthn/core/browser/test_passkey_model.h"
#include "content/public/browser/authenticator_request_client_delegate.h"
#include "content/public/browser/browser_context.h"
#include "content/public/test/web_contents_tester.h"
#include "crypto/scoped_mock_unexportable_key_provider.h"
#include "device/fido/cable/cable_discovery_data.h"
#include "device/fido/cable/v2_constants.h"
#include "device/fido/discoverable_credential_metadata.h"
#include "device/fido/features.h"
#include "device/fido/fido_constants.h"
#include "device/fido/fido_discovery_factory.h"
#include "device/fido/fido_request_handler_base.h"
#include "device/fido/fido_types.h"
#include "device/fido/virtual_ctap2_device.h"
#include "device/fido/virtual_fido_device_authenticator.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/common/extension_builder.h"
#include "extensions/common/permissions/permissions_data.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
#include "url/origin.h"
#if BUILDFLAG(IS_WIN)
#include "device/fido/win/authenticator.h"
#include "device/fido/win/fake_webauthn_api.h"
#include "device/fido/win/webauthn_api.h"
#include "third_party/microsoft_webauthn/webauthn.h"
#endif
#if BUILDFLAG(IS_MAC)
#include "chrome/test/base/testing_profile.h"
#include "device/fido/mac/authenticator_config.h"
#endif
namespace {
static constexpr char kRelyingPartyID[] = …;
TransportAvailabilityInfo;
class Observer : public testing::NiceMock<
ChromeAuthenticatorRequestDelegate::TestObserver> { … };
class MockCableDiscoveryFactory : public device::FidoDiscoveryFactory { … };
class ChromeAuthenticatorRequestDelegateTest
: public ChromeRenderViewHostTestHarness { … };
class TestAuthenticatorModelObserver final
: public AuthenticatorRequestDialogModel::Observer { … };
TEST_F(ChromeAuthenticatorRequestDelegateTest, IndividualAttestation) { … }
TEST_F(ChromeAuthenticatorRequestDelegateTest, CableConfiguration) { … }
TEST_F(ChromeAuthenticatorRequestDelegateTest, NoExtraDiscoveriesWithoutUI) { … }
TEST_F(ChromeAuthenticatorRequestDelegateTest, ConditionalUI) { … }
constexpr char kExtensionId[] = …;
constexpr char kExtensionOrigin[] = …;
PatternRpIdPair;
constexpr PatternRpIdPair kValidRelyingPartyTestCases[] = …;
constexpr PatternRpIdPair kInvalidRelyingPartyTestCases[] = …;
TEST_F(ChromeAuthenticatorRequestDelegateTest,
OverrideValidateDomainAndRelyingPartyIDTest_ExtensionValidCases) { … }
TEST_F(ChromeAuthenticatorRequestDelegateTest,
OverrideValidateDomainAndRelyingPartyIDTest_ExtensionInvalidCases) { … }
TEST_F(ChromeAuthenticatorRequestDelegateTest,
OverrideValidateDomainAndRelyingPartyIDTest_ExtensionNotFound) { … }
TEST_F(ChromeAuthenticatorRequestDelegateTest,
OverrideValidateDomainAndRelyingPartyIDTest_WebOrigin) { … }
TEST_F(ChromeAuthenticatorRequestDelegateTest, MaybeGetRelyingPartyIdOverride) { … }
TEST_F(ChromeAuthenticatorRequestDelegateTest, VirtualEnvironmentAttestation) { … }
TEST_F(ChromeAuthenticatorRequestDelegateTest, GpmPasskeys) { … }
TEST_F(ChromeAuthenticatorRequestDelegateTest, GpmPasskeys_NoSyncPairedPhones) { … }
TEST_F(ChromeAuthenticatorRequestDelegateTest, GpmPasskeys_ShadowedPasskeys) { … }
TEST_F(ChromeAuthenticatorRequestDelegateTest, FilterGoogleComPasskeys) { … }
class EnclaveAuthenticatorRequestDelegateTest
: public ChromeAuthenticatorRequestDelegateTest { … };
#if !BUILDFLAG(IS_CHROMEOS)
TEST_F(EnclaveAuthenticatorRequestDelegateTest,
BrowserProvidedPasskeysAvailable) { … }
#endif
#if BUILDFLAG(IS_MAC)
std::string TouchIdMetadataSecret(ChromeWebAuthenticationDelegate& delegate,
content::BrowserContext* browser_context) {
return delegate.GetTouchIdAuthenticatorConfig(browser_context)
->metadata_secret;
}
TEST_F(ChromeAuthenticatorRequestDelegateTest, TouchIdMetadataSecret) {
ChromeWebAuthenticationDelegate delegate;
std::string secret = TouchIdMetadataSecret(delegate, GetBrowserContext());
EXPECT_EQ(secret.size(), 32u);
EXPECT_EQ(secret, TouchIdMetadataSecret(delegate, GetBrowserContext()));
}
TEST_F(ChromeAuthenticatorRequestDelegateTest,
TouchIdMetadataSecret_EqualForSameProfile) {
ChromeWebAuthenticationDelegate delegate1;
ChromeWebAuthenticationDelegate delegate2;
EXPECT_EQ(TouchIdMetadataSecret(delegate1, GetBrowserContext()),
TouchIdMetadataSecret(delegate2, GetBrowserContext()));
}
TEST_F(ChromeAuthenticatorRequestDelegateTest,
TouchIdMetadataSecret_NotEqualForDifferentProfiles) {
auto other_browser_context = CreateBrowserContext();
ChromeWebAuthenticationDelegate delegate;
EXPECT_NE(TouchIdMetadataSecret(delegate, GetBrowserContext()),
TouchIdMetadataSecret(delegate, other_browser_context.get()));
EXPECT_EQ(
32u, TouchIdMetadataSecret(delegate, other_browser_context.get()).size());
}
#endif
#if BUILDFLAG(IS_WIN)
TEST_F(ChromeAuthenticatorRequestDelegateTest, ShouldPromptForAttestationWin) {
::device::FakeWinWebAuthnApi win_webauthn_api;
win_webauthn_api.set_version(WEBAUTHN_API_VERSION_2);
::device::WinWebAuthnApiAuthenticator authenticator(
nullptr, &win_webauthn_api);
base::test::TestFuture<bool> future;
ChromeAuthenticatorRequestDelegate delegate(main_rfh());
delegate.ShouldReturnAttestation(kRelyingPartyID, &authenticator,
false,
future.GetCallback());
EXPECT_TRUE(future.Wait());
EXPECT_EQ(future.Get(), true);
}
#endif
class OriginMayUseRemoteDesktopClientOverrideTest
: public ChromeAuthenticatorRequestDelegateTest { … };
TEST_F(OriginMayUseRemoteDesktopClientOverrideTest,
RemoteProxiedRequestsAllowedPolicy) { … }
TEST_F(OriginMayUseRemoteDesktopClientOverrideTest,
OriginMayUseRemoteDesktopClientOverrideAdditionalOriginSwitch) { … }
}
#if BUILDFLAG(IS_MAC)
class ChromeAuthenticatorRequestDelegatePrivateTest : public testing::Test {
content::BrowserTaskEnvironment task_environment_;
};
TEST_F(ChromeAuthenticatorRequestDelegatePrivateTest, DaysSinceDate) {
const base::Time now = base::Time::FromTimeT(1691188997);
const struct {
char input[16];
std::optional<int> expected_result;
} kTestCases[] = {
{"", std::nullopt},
{"2023-08-", std::nullopt},
{"2023-08-04", 0},
{"2023-08-03", 1},
{"2023-8-3", 1},
{"2023-07-04", 31},
{"2001-11-23", 7924},
};
for (const auto& test : kTestCases) {
SCOPED_TRACE(test.input);
const std::optional<int> result =
ChromeAuthenticatorRequestDelegate::DaysSinceDate(test.input, now);
EXPECT_EQ(result, test.expected_result);
}
}
TEST_F(ChromeAuthenticatorRequestDelegatePrivateTest, GetICloudKeychainPref) {
TestingProfile profile;
EXPECT_FALSE(ChromeAuthenticatorRequestDelegate::GetICloudKeychainPref(
profile.GetPrefs())
.has_value());
profile.GetPrefs()->SetBoolean(prefs::kCreatePasskeysInICloudKeychain, true);
EXPECT_EQ(*ChromeAuthenticatorRequestDelegate::GetICloudKeychainPref(
profile.GetPrefs()),
true);
}
TEST_F(ChromeAuthenticatorRequestDelegatePrivateTest,
ShouldCreateInICloudKeychain) {
EXPECT_FALSE(ChromeAuthenticatorRequestDelegate::ShouldCreateInICloudKeychain(
ChromeAuthenticatorRequestDelegate::RequestSource::
kSecurePaymentConfirmation,
false,
true, true,
true));
for (const bool preference : {false, true}) {
EXPECT_EQ(preference,
ChromeAuthenticatorRequestDelegate::ShouldCreateInICloudKeychain(
ChromeAuthenticatorRequestDelegate::RequestSource::
kWebAuthentication,
false,
true,
true,
preference));
}
}
#endif