webauthn: set GPM user verification policy.
When uv=discouraged, GPM will avoid prompting for user verification. For uv=preferred, GPM will do UV only if biometrics are available. (This does not include the profile authenticator on macOS, the behaviour of which is left unchanged.) Knowning whether biometrics are available on Windows is a bit complex, and asynchronous. So provisions have been laid to allow this to be plugged in in a future CL but, for now, we act as if biometrics are always available on Windows. This also means that we don't know whether biometrics are available until the TransportAvailabilityInfo is ready (since that contains lots of asynchronous information about the platform already). Thus the enclave controller can't pick its UV method until later in the flow, which isn't quite as clean. Bug: 40274370 Change-Id: I8ecf0b0cad782145385af89a70fd95fbaff2fdee Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5608337 Commit-Queue: Adam Langley <agl@chromium.org> Reviewed-by: Nina Satragno <nsatragno@chromium.org> Reviewed-by: Ken Buchanan <kenrb@chromium.org> Cr-Commit-Position: refs/heads/main@{#1323056}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
b015d2d6a4
commit
f9da6bca6d
chrome/browser
BUILD.gn
webauthn
authenticator_request_dialog_model.ccauthenticator_request_dialog_model.hauthenticator_request_dialog_model_unittest.ccenclave_authenticator_browsertest.ccenclave_manager.ccenclave_manager.henclave_manager_unittest.ccgpm_enclave_controller.ccgpm_enclave_controller.hgpm_user_verification_policy.ccgpm_user_verification_policy.h
device/fido
@ -4577,6 +4577,8 @@ static_library("browser") {
|
||||
"webauthn/enclave_manager_interface.h",
|
||||
"webauthn/gpm_enclave_controller.cc",
|
||||
"webauthn/gpm_enclave_controller.h",
|
||||
"webauthn/gpm_user_verification_policy.cc",
|
||||
"webauthn/gpm_user_verification_policy.h",
|
||||
"webauthn/local_credential_management.cc",
|
||||
"webauthn/local_credential_management.h",
|
||||
"webauthn/observable_authenticator_list.cc",
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "chrome/browser/webauthn/authenticator_reference.h"
|
||||
#include "chrome/browser/webauthn/authenticator_transport.h"
|
||||
#include "chrome/browser/webauthn/change_pin_controller_impl.h"
|
||||
#include "chrome/browser/webauthn/gpm_user_verification_policy.h"
|
||||
#include "chrome/browser/webauthn/passkey_model_factory.h"
|
||||
#include "chrome/browser/webauthn/webauthn_metrics_util.h"
|
||||
#include "chrome/browser/webauthn/webauthn_pref_names.h"
|
||||
@ -62,7 +63,6 @@
|
||||
#include "device/fido/fido_transport_protocol.h"
|
||||
#include "device/fido/fido_types.h"
|
||||
#include "device/fido/pin.h"
|
||||
#include "device/fido/platform_user_verification_policy.h"
|
||||
#include "device/fido/public_key_credential_descriptor.h"
|
||||
#include "third_party/abseil-cpp/absl/types/variant.h"
|
||||
#include "third_party/icu/source/common/unicode/locid.h"
|
||||
@ -433,9 +433,12 @@ std::optional<content::GlobalRenderFrameHostId> FrameHostIdFromMaybeNull(
|
||||
return render_frame_host->GetGlobalId();
|
||||
}
|
||||
|
||||
bool HaveTouchId() {
|
||||
bool ProfileAuthenticatorWillDoUserVerification(
|
||||
device::UserVerificationRequirement requirement,
|
||||
bool platform_has_biometrics) {
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
return device::fido::mac::DeviceHasBiometricsAvailable();
|
||||
return device::fido::mac::ProfileAuthenticatorWillDoUserVerification(
|
||||
requirement, platform_has_biometrics);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
@ -726,8 +729,10 @@ void AuthenticatorRequestDialogController::TransitionToModalWebAuthnRequest() {
|
||||
|
||||
void AuthenticatorRequestDialogController::
|
||||
StartGuidedFlowForMostLikelyTransportOrShowMechanismSelection() {
|
||||
const bool will_do_uv = device::fido::PlatformWillDoUserVerification(
|
||||
transport_availability_.user_verification_requirement);
|
||||
const bool enclave_will_do_uv = GpmWillDoUserVerification(
|
||||
transport_availability_.user_verification_requirement,
|
||||
transport_availability_.platform_has_biometrics);
|
||||
constexpr bool kIsMac = BUILDFLAG(IS_MAC);
|
||||
|
||||
if (pending_step_) {
|
||||
SetCurrentStep(*pending_step_);
|
||||
@ -754,7 +759,8 @@ void AuthenticatorRequestDialogController::
|
||||
(cred->value().source == device::AuthenticatorType::kICloudKeychain ||
|
||||
// The enclave Touch ID prompts shows the credential details.
|
||||
(cred->value().source == device::AuthenticatorType::kEnclave &&
|
||||
will_do_uv && HaveTouchId()));
|
||||
enclave_will_do_uv && kIsMac &&
|
||||
transport_availability_.platform_has_biometrics));
|
||||
|
||||
if (cred != nullptr &&
|
||||
// Credentials on phones should never be triggered automatically.
|
||||
@ -769,7 +775,9 @@ void AuthenticatorRequestDialogController::
|
||||
// biometric or a UV requirement because, otherwise, there'll not be
|
||||
// *any* UI.
|
||||
(cred->value().source == device::AuthenticatorType::kTouchID &&
|
||||
!will_do_uv))) {
|
||||
!ProfileAuthenticatorWillDoUserVerification(
|
||||
transport_availability_.user_verification_requirement,
|
||||
transport_availability_.platform_has_biometrics)))) {
|
||||
SetCurrentStep(Step::kSelectPriorityMechanism);
|
||||
} else if (cred != nullptr || !hints_.transport.has_value() ||
|
||||
transport_availability_.request_type !=
|
||||
@ -810,36 +818,42 @@ void AuthenticatorRequestDialogController::
|
||||
}
|
||||
return;
|
||||
}
|
||||
// If not doing UV, but the allowlist matches an enclave credential,
|
||||
// show UI to serve as user presence.
|
||||
if (!will_do_uv && transport_availability_.request_type ==
|
||||
device::FidoRequestType::kGetAssertion) {
|
||||
for (auto& cred : transport_availability_.recognized_credentials) {
|
||||
if (cred.source == device::AuthenticatorType::kEnclave) {
|
||||
model_->creds = {cred};
|
||||
SetCurrentStep(Step::kPreSelectSingleAccount);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (transport_availability_.has_platform_authenticator_credential ==
|
||||
device::FidoRequestHandlerBase::RecognizedCredential::
|
||||
kNoRecognizedCredential) {
|
||||
// If there are no local matches but there are phone or enclave
|
||||
// passkeys, jump to the first one of them.
|
||||
for (auto& mechanism : model_->mechanisms) {
|
||||
const auto& type = mechanism.type;
|
||||
if (absl::holds_alternative<Mechanism::Credential>(type)) {
|
||||
if (absl::get<Mechanism::Credential>(type)->source ==
|
||||
device::AuthenticatorType::kEnclave) {
|
||||
CHECK(will_do_uv);
|
||||
mechanism.callback.Run();
|
||||
|
||||
// Don't jump to an enclave credential if we need to do reauth because the
|
||||
// OAuth token won't work. Also, don't jump to a phone credential either
|
||||
// because reauthenticating is probably a better option for the user.
|
||||
if (!enclave_needs_reauth_) {
|
||||
// If not doing UV, but the allowlist matches an enclave credential,
|
||||
// show UI to serve as user presence.
|
||||
if (!enclave_will_do_uv && transport_availability_.request_type ==
|
||||
device::FidoRequestType::kGetAssertion) {
|
||||
for (auto& cred : transport_availability_.recognized_credentials) {
|
||||
if (cred.source == device::AuthenticatorType::kEnclave) {
|
||||
model_->creds = {cred};
|
||||
SetCurrentStep(Step::kPreSelectSingleAccount);
|
||||
return;
|
||||
}
|
||||
if (absl::get<Mechanism::Credential>(type)->source ==
|
||||
device::AuthenticatorType::kPhone) {
|
||||
SetCurrentStep(Step::kPhoneConfirmationSheet);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (transport_availability_.has_platform_authenticator_credential ==
|
||||
device::FidoRequestHandlerBase::RecognizedCredential::
|
||||
kNoRecognizedCredential) {
|
||||
// If there are no local matches but there are phone or enclave
|
||||
// passkeys, jump to the first one of them.
|
||||
for (auto& mechanism : model_->mechanisms) {
|
||||
const auto& type = mechanism.type;
|
||||
if (absl::holds_alternative<Mechanism::Credential>(type)) {
|
||||
if (absl::get<Mechanism::Credential>(type)->source ==
|
||||
device::AuthenticatorType::kEnclave) {
|
||||
CHECK(enclave_will_do_uv);
|
||||
mechanism.callback.Run();
|
||||
return;
|
||||
}
|
||||
if (absl::get<Mechanism::Credential>(type)->source ==
|
||||
device::AuthenticatorType::kPhone) {
|
||||
SetCurrentStep(Step::kPhoneConfirmationSheet);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1095,15 +1109,13 @@ void AuthenticatorRequestDialogController::StartPlatformAuthenticatorFlow() {
|
||||
} else {
|
||||
// For requests with an allow list, pre-select a random credential.
|
||||
model_->creds = {platform_credentials.front()};
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
if (device::fido::PlatformWillDoUserVerification(
|
||||
transport_availability_.user_verification_requirement)) {
|
||||
if (ProfileAuthenticatorWillDoUserVerification(
|
||||
transport_availability_.user_verification_requirement,
|
||||
transport_availability_.platform_has_biometrics)) {
|
||||
// If it's not preferable to complete the request by clicking
|
||||
// "Continue" then don't show the account selection sheet.
|
||||
HideDialogAndDispatchToPlatformAuthenticator();
|
||||
} else // NOLINT(readability/braces)
|
||||
#endif
|
||||
{
|
||||
} else {
|
||||
// Otherwise show the chosen credential to the user. For platform
|
||||
// authenticators with optional UV (e.g. Touch ID), this step
|
||||
// essentially acts as the user presence check.
|
||||
@ -2562,6 +2574,8 @@ void AuthenticatorRequestDialogController::
|
||||
base::Contains(transport_availability_.available_transports,
|
||||
device::FidoTransportProtocol::kUsbHumanInterfaceDevice);
|
||||
model_->is_off_the_record = transport_availability_.is_off_the_record_context;
|
||||
model_->platform_has_biometrics =
|
||||
transport_availability_.platform_has_biometrics;
|
||||
if (model_->cable_ui_type) {
|
||||
model_->cable_should_suggest_usb =
|
||||
*model_->cable_ui_type !=
|
||||
|
@ -478,6 +478,8 @@ struct AuthenticatorRequestDialogModel {
|
||||
std::vector<device::DiscoverableCredentialMetadata> creds;
|
||||
// preselected_cred contains a credential preselected by the user.
|
||||
std::optional<device::DiscoverableCredentialMetadata> preselected_cred;
|
||||
// Whether the platform can check biometrics and has biometrics configured.
|
||||
std::optional<bool> platform_has_biometrics;
|
||||
// offer_try_again_in_ui indicates whether a button to retry the request
|
||||
// should be included on the dialog sheet shown when encountering certain
|
||||
// errors.
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include "chrome/browser/password_manager/chrome_webauthn_credentials_delegate_factory.h"
|
||||
#include "chrome/browser/webauthn/authenticator_reference.h"
|
||||
#include "chrome/browser/webauthn/authenticator_transport.h"
|
||||
#include "chrome/browser/webauthn/gpm_user_verification_policy.h"
|
||||
#include "chrome/browser/webauthn/webauthn_pref_names.h"
|
||||
#include "chrome/grit/generated_resources.h"
|
||||
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
|
||||
@ -61,7 +62,6 @@
|
||||
#include "device/fido/fido_request_handler_base.h"
|
||||
#include "device/fido/fido_transport_protocol.h"
|
||||
#include "device/fido/fido_types.h"
|
||||
#include "device/fido/platform_user_verification_policy.h"
|
||||
#include "device/fido/public_key_credential_user_entity.h"
|
||||
#include "testing/gmock/include/gmock/gmock.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
@ -74,10 +74,6 @@
|
||||
#include "device/fido/win/webauthn_api.h"
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
#include "device/fido/mac/util.h"
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
using testing::ElementsAre;
|
||||
@ -203,6 +199,7 @@ enum class TransportAvailabilityParam {
|
||||
kCreateInICloudKeychain,
|
||||
kNoTouchId,
|
||||
kUVRequired,
|
||||
kUVPreferred,
|
||||
kHintSecurityKeys,
|
||||
kHintHybrid,
|
||||
kHintClientDevice,
|
||||
@ -263,6 +260,8 @@ std::string_view TransportAvailabilityParamToString(
|
||||
return "kNoTouchId";
|
||||
case TransportAvailabilityParam::kUVRequired:
|
||||
return "kUVRequired";
|
||||
case TransportAvailabilityParam::kUVPreferred:
|
||||
return "kUVPreferred";
|
||||
case TransportAvailabilityParam::kHintSecurityKeys:
|
||||
return "kHintSecurityKeys";
|
||||
case TransportAvailabilityParam::kHintHybrid:
|
||||
@ -398,13 +397,17 @@ class FakeEnclaveController : public AuthenticatorRequestDialogModel::Observer {
|
||||
}
|
||||
|
||||
void OnGPMPasskeySelected(std::vector<uint8_t> credential_id) override {
|
||||
if (device::fido::PlatformWillDoUserVerification(
|
||||
device::UserVerificationRequirement::kPreferred)) {
|
||||
if (GpmWillDoUserVerification(
|
||||
device::UserVerificationRequirement::kPreferred,
|
||||
*model_->platform_has_biometrics)) {
|
||||
if (kIsMac) {
|
||||
model_->SetStep(AuthenticatorRequestDialogModel::Step::kGPMTouchID);
|
||||
} else {
|
||||
model_->SetStep(AuthenticatorRequestDialogModel::Step::kGPMEnterPin);
|
||||
}
|
||||
} else {
|
||||
model_->SetStep(
|
||||
AuthenticatorRequestDialogModel::Step::kSelectSingleAccount);
|
||||
}
|
||||
}
|
||||
|
||||
@ -499,6 +502,8 @@ TEST_F(AuthenticatorRequestDialogControllerTest, Mechanisms) {
|
||||
TransportAvailabilityParam::kNoTouchId;
|
||||
[[maybe_unused]] const auto ickc_creds =
|
||||
TransportAvailabilityParam::kHasICloudKeychainCreds;
|
||||
[[maybe_unused]] const auto uv_pref =
|
||||
TransportAvailabilityParam::kUVPreferred;
|
||||
[[maybe_unused]] const auto uv_req = TransportAvailabilityParam::kUVRequired;
|
||||
const auto enclave_needs_sign_in =
|
||||
TransportAvailabilityParam::kEnclaveNeedsSignIn;
|
||||
@ -598,12 +603,15 @@ TEST_F(AuthenticatorRequestDialogControllerTest, Mechanisms) {
|
||||
{c(touchid_cred1)}, hero},
|
||||
// When TouchID is present, we can jump directly to the platform UI, which
|
||||
// will be a Touch ID prompt.
|
||||
{L, ga, {internal}, {has_plat, one_touchid_cred}, {}, {c(touchid_cred1)},
|
||||
plat_ui},
|
||||
{L, ga, {internal}, {has_plat, one_touchid_cred, uv_pref}, {},
|
||||
{c(touchid_cred1)}, plat_ui},
|
||||
// Or if uv=required, plat_ui is also ok because it'll be a password
|
||||
// prompt.
|
||||
{L, ga, {internal}, {has_plat, one_touchid_cred, uv_req, no_touchid}, {},
|
||||
{c(touchid_cred1)}, plat_ui},
|
||||
// The profile authenticator does UV even for uv=discouraged.
|
||||
{L, ga, {internal}, {has_plat, one_touchid_cred}, {}, {c(touchid_cred1)},
|
||||
plat_ui},
|
||||
#endif
|
||||
// Even with an empty allow list.
|
||||
{L,
|
||||
@ -887,7 +895,7 @@ TEST_F(AuthenticatorRequestDialogControllerTest, Mechanisms) {
|
||||
{L,
|
||||
ga,
|
||||
{cable, internal},
|
||||
{only_hybrid_or_internal, empty_al, enclave_cred},
|
||||
{only_hybrid_or_internal, empty_al, enclave_cred, uv_pref},
|
||||
{},
|
||||
{c(enclave_cred1), add},
|
||||
enclave_touchid},
|
||||
@ -895,20 +903,38 @@ TEST_F(AuthenticatorRequestDialogControllerTest, Mechanisms) {
|
||||
{L,
|
||||
ga,
|
||||
{cable, internal},
|
||||
{only_hybrid_or_internal, empty_al, enclave_cred, no_touchid},
|
||||
{only_hybrid_or_internal, empty_al, enclave_cred, no_touchid, uv_pref},
|
||||
{},
|
||||
{c(enclave_cred1), add},
|
||||
hero},
|
||||
// And not if uv=discouraged
|
||||
{L,
|
||||
ga,
|
||||
{cable, internal},
|
||||
{only_hybrid_or_internal, empty_al, enclave_cred},
|
||||
{},
|
||||
{c(enclave_cred1), add},
|
||||
hero},
|
||||
#endif
|
||||
#if !BUILDFLAG(IS_CHROMEOS)
|
||||
// If an enclave credential is in an allowlist, we should jump to UV
|
||||
// immediately.
|
||||
{L,
|
||||
ga,
|
||||
{cable},
|
||||
{only_hybrid_or_internal, enclave_cred, uv_pref},
|
||||
{},
|
||||
{c(enclave_cred1), add},
|
||||
kIsMac ? enclave_touchid : use_pk},
|
||||
#endif
|
||||
// But, again, not for uv=discouraged.
|
||||
{L,
|
||||
ga,
|
||||
{cable, internal},
|
||||
{only_hybrid_or_internal, enclave_cred},
|
||||
{},
|
||||
{c(enclave_cred1), add},
|
||||
kIsMac ? enclave_touchid : enclave_pin},
|
||||
use_pk},
|
||||
// When the enclave needs to sign-in again, that should appear as a
|
||||
// mechanism and the MSS should be shown.
|
||||
{L,
|
||||
@ -1246,7 +1272,10 @@ TEST_F(AuthenticatorRequestDialogControllerTest, Mechanisms) {
|
||||
transports_info.user_verification_requirement =
|
||||
base::Contains(test.params, TransportAvailabilityParam::kUVRequired)
|
||||
? device::UserVerificationRequirement::kRequired
|
||||
: device::UserVerificationRequirement::kDiscouraged;
|
||||
: (base::Contains(test.params,
|
||||
TransportAvailabilityParam::kUVPreferred)
|
||||
? device::UserVerificationRequirement::kPreferred
|
||||
: device::UserVerificationRequirement::kDiscouraged);
|
||||
|
||||
if (base::Contains(test.params,
|
||||
TransportAvailabilityParam::kHasPlatformCredential)) {
|
||||
@ -1391,8 +1420,8 @@ TEST_F(AuthenticatorRequestDialogControllerTest, Mechanisms) {
|
||||
controller.set_should_create_in_icloud_keychain(true);
|
||||
}
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
device::fido::mac::ScopedBiometricsOverride scoped_biometrics_override(
|
||||
!base::Contains(test.params, TransportAvailabilityParam::kNoTouchId));
|
||||
transports_info.platform_has_biometrics =
|
||||
!base::Contains(test.params, TransportAvailabilityParam::kNoTouchId);
|
||||
#endif
|
||||
|
||||
std::optional<device::FidoTransportProtocol> hint_transport;
|
||||
@ -2389,13 +2418,6 @@ TEST_F(AuthenticatorRequestDialogControllerTest, PreSelect) {
|
||||
transports_info.request_type = device::FidoRequestType::kGetAssertion;
|
||||
transports_info.available_transports = kAllTransports;
|
||||
transports_info.has_empty_allow_list = has_empty_allow_list;
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
// The TouchID authenticator will be immediately dispatched to if the device
|
||||
// has biometrics configured. Simulate a lack of biometrics to align with
|
||||
// other platforms.
|
||||
device::fido::mac::ScopedBiometricsOverride scoped_biometrics_override(
|
||||
false);
|
||||
#endif // BUILDFLAG(IS_MAC)
|
||||
transports_info.user_verification_requirement =
|
||||
device::UserVerificationRequirement::kPreferred;
|
||||
transports_info.has_platform_authenticator_credential = device::
|
||||
|
@ -395,8 +395,6 @@ static constexpr char kGetAssertionConditionalUI[] = R"((() => {
|
||||
bool IsReady(GPMEnclaveController::AccountState state) {
|
||||
switch (state) {
|
||||
case GPMEnclaveController::AccountState::kReady:
|
||||
case GPMEnclaveController::AccountState::kReadyWithPIN:
|
||||
case GPMEnclaveController::AccountState::kReadyWithBiometrics:
|
||||
return true;
|
||||
default:
|
||||
LOG(ERROR) << "State " << static_cast<int>(state)
|
||||
@ -1190,17 +1188,8 @@ IN_PROC_BROWSER_TEST_F(EnclaveAuthenticatorWithPinBrowserTest,
|
||||
|
||||
EXPECT_EQ(dialog_model()->step(),
|
||||
AuthenticatorRequestDialogModel::Step::kGPMCreatePasskey);
|
||||
#if !BUILDFLAG(IS_MAC)
|
||||
model_observer()->SetStepToObserve(
|
||||
AuthenticatorRequestDialogController::Step::kGPMEnterPin);
|
||||
#endif
|
||||
dialog_model()->OnGPMCreatePasskey();
|
||||
|
||||
#if !BUILDFLAG(IS_MAC)
|
||||
model_observer()->WaitForStep();
|
||||
dialog_model()->OnGPMPinEntered(u"123456");
|
||||
#endif
|
||||
|
||||
ASSERT_TRUE(message_queue.WaitForMessage(&script_result));
|
||||
|
||||
std::tie(enabled, first, second) = ParsePrfResult(script_result);
|
||||
|
@ -2587,7 +2587,9 @@ void EnclaveManager::ChangePIN(std::string updated_pin,
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
CHECK(user_->registered());
|
||||
CHECK(user_->has_wrapped_pin());
|
||||
CHECK(rapt.has_value() || uv_key_state() != UvKeyState::kNone);
|
||||
// TODO(enclave): remove the ability to change the PIN based on UV. We don't
|
||||
// want to allow that.
|
||||
CHECK(rapt.has_value() || uv_key_state(false) != UvKeyState::kNone);
|
||||
|
||||
auto action = std::make_unique<PendingAction>();
|
||||
action->callback = std::move(callback);
|
||||
@ -3061,7 +3063,8 @@ EnclaveManager::GetWrappedPIN() {
|
||||
user_->wrapped_pin());
|
||||
}
|
||||
|
||||
EnclaveManager::UvKeyState EnclaveManager::uv_key_state() const {
|
||||
EnclaveManager::UvKeyState EnclaveManager::uv_key_state(
|
||||
bool platform_has_biometrics) const {
|
||||
CHECK(is_ready());
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
if (user_->deferred_uv_key_creation()) {
|
||||
@ -3072,7 +3075,7 @@ EnclaveManager::UvKeyState EnclaveManager::uv_key_state() const {
|
||||
return UvKeyState::kNone;
|
||||
}
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
if (device::fido::mac::DeviceHasBiometricsAvailable()) {
|
||||
if (platform_has_biometrics) {
|
||||
// LAAuthenticationView is only supported on macOS 12+.
|
||||
if (__builtin_available(macOS 12.0, *)) {
|
||||
// Chrome will display an LAAuthenticationView with a Touch ID prompt.
|
||||
|
@ -238,7 +238,7 @@ class EnclaveManager : public EnclaveManagerInterface {
|
||||
// biometrics.
|
||||
kUsesChromeUI,
|
||||
};
|
||||
UvKeyState uv_key_state() const;
|
||||
UvKeyState uv_key_state(bool platform_has_biometrics) const;
|
||||
// Calls the given callback with `true` if the current platform supports
|
||||
// making user-verifying keys.
|
||||
static void AreUserVerifyingKeysSupported(Callback callback);
|
||||
|
@ -1176,7 +1176,7 @@ TEST_F(EnclaveManagerTest, MAYBE_HardwareKeyLost) {
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
// Windows does deferred UV key creation. This test has to trigger the actual
|
||||
// create before testing that it is later deleted.
|
||||
EXPECT_EQ(manager_.uv_key_state(),
|
||||
EXPECT_EQ(manager_.uv_key_state(/*platform_has_biometrics=*/false),
|
||||
EnclaveManager::UvKeyState::kUsesSystemUIDeferredCreation);
|
||||
auto key_creation_callback = manager_.UserVerifyingKeyCreationCallback();
|
||||
quit_closure = task_env_.QuitClosure();
|
||||
@ -1297,10 +1297,11 @@ TEST_F(EnclaveUVTest, UserVerifyingKeyAvailable) {
|
||||
EXPECT_TRUE(add_future.Wait());
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
EXPECT_EQ(manager_.uv_key_state(),
|
||||
EXPECT_EQ(manager_.uv_key_state(/*platform_has_biometrics=*/false),
|
||||
EnclaveManager::UvKeyState::kUsesSystemUIDeferredCreation);
|
||||
#else
|
||||
EXPECT_EQ(manager_.uv_key_state(), EnclaveManager::UvKeyState::kUsesSystemUI);
|
||||
EXPECT_EQ(manager_.uv_key_state(/*platform_has_biometrics=*/false),
|
||||
EnclaveManager::UvKeyState::kUsesSystemUI);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -1329,7 +1330,8 @@ TEST_F(EnclaveUVTest, UserVerifyingKeyUnavailable) {
|
||||
ASSERT_FALSE(manager_.is_idle());
|
||||
EXPECT_TRUE(add_future.Wait());
|
||||
ASSERT_TRUE(manager_.is_registered());
|
||||
EXPECT_EQ(manager_.uv_key_state(), EnclaveManager::UvKeyState::kNone);
|
||||
EXPECT_EQ(manager_.uv_key_state(/*platform_has_biometrics=*/false),
|
||||
EnclaveManager::UvKeyState::kNone);
|
||||
}
|
||||
|
||||
TEST_F(EnclaveUVTest, UserVerifyingKeyLost) {
|
||||
@ -1360,7 +1362,7 @@ TEST_F(EnclaveUVTest, UserVerifyingKeyLost) {
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
// Windows does deferred UV key creation. This test has to trigger the actual
|
||||
// create before testing that it is later deleted.
|
||||
EXPECT_EQ(manager_.uv_key_state(),
|
||||
EXPECT_EQ(manager_.uv_key_state(/*platform_has_biometrics=*/false),
|
||||
EnclaveManager::UvKeyState::kUsesSystemUIDeferredCreation);
|
||||
auto key_creation_callback = manager_.UserVerifyingKeyCreationCallback();
|
||||
quit_closure = task_env_.QuitClosure();
|
||||
@ -1372,7 +1374,8 @@ TEST_F(EnclaveUVTest, UserVerifyingKeyLost) {
|
||||
}));
|
||||
task_env_.RunUntilQuit();
|
||||
#else
|
||||
ASSERT_EQ(manager_.uv_key_state(), EnclaveManager::UvKeyState::kUsesSystemUI);
|
||||
ASSERT_EQ(manager_.uv_key_state(/*platform_has_biometrics=*/false),
|
||||
EnclaveManager::UvKeyState::kUsesSystemUI);
|
||||
#endif
|
||||
manager_.ClearCachedKeysForTesting();
|
||||
DisableUVKeySupport();
|
||||
@ -1432,7 +1435,8 @@ TEST_F(EnclaveUVTest, UserVerifyingKeyUseExisting) {
|
||||
ASSERT_FALSE(manager_.is_idle());
|
||||
EXPECT_TRUE(add_future.Wait());
|
||||
|
||||
ASSERT_EQ(manager_.uv_key_state(), EnclaveManager::UvKeyState::kUsesSystemUI);
|
||||
ASSERT_EQ(manager_.uv_key_state(/*platform_has_biometrics=*/false),
|
||||
EnclaveManager::UvKeyState::kUsesSystemUI);
|
||||
}
|
||||
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
@ -1466,16 +1470,17 @@ TEST_F(EnclaveUVTest, ChromeHandlesBiometrics) {
|
||||
crypto::ScopedFakeAppleKeychainV2::UVMethod::kBiometrics);
|
||||
// The TouchID view is only available on macOS 12+.
|
||||
if (__builtin_available(macos 12, *)) {
|
||||
EXPECT_EQ(manager_.uv_key_state(),
|
||||
EXPECT_EQ(manager_.uv_key_state(/*platform_has_biometrics=*/true),
|
||||
EnclaveManager::UvKeyState::kUsesChromeUI);
|
||||
} else {
|
||||
EXPECT_EQ(manager_.uv_key_state(),
|
||||
EXPECT_EQ(manager_.uv_key_state(/*platform_has_biometrics=*/false),
|
||||
EnclaveManager::UvKeyState::kUsesSystemUI);
|
||||
}
|
||||
|
||||
scoped_fake_apple_keychain_.SetUVMethod(
|
||||
crypto::ScopedFakeAppleKeychainV2::UVMethod::kPasswordOnly);
|
||||
EXPECT_EQ(manager_.uv_key_state(), EnclaveManager::UvKeyState::kUsesSystemUI);
|
||||
EXPECT_EQ(manager_.uv_key_state(/*platform_has_biometrics=*/false),
|
||||
EnclaveManager::UvKeyState::kUsesSystemUI);
|
||||
}
|
||||
#endif // BUILDFLAG(IS_MAC)
|
||||
|
||||
@ -1504,7 +1509,7 @@ TEST_F(EnclaveUVTest, DeferredUVKeyCreation) {
|
||||
ASSERT_FALSE(manager_.is_idle());
|
||||
EXPECT_TRUE(add_future.Wait());
|
||||
|
||||
EXPECT_EQ(manager_.uv_key_state(),
|
||||
EXPECT_EQ(manager_.uv_key_state(/*platform_has_biometrics=*/false),
|
||||
EnclaveManager::UvKeyState::kUsesSystemUIDeferredCreation);
|
||||
const auto& user_state =
|
||||
manager_.local_state_for_testing().users().find(gaia_id_)->second;
|
||||
@ -1550,7 +1555,7 @@ TEST_F(EnclaveUVTest, UnregisterOnFailedDeferredUVKeyCreation) {
|
||||
ASSERT_FALSE(manager_.is_idle());
|
||||
EXPECT_TRUE(add_future.Wait());
|
||||
|
||||
EXPECT_EQ(manager_.uv_key_state(),
|
||||
EXPECT_EQ(manager_.uv_key_state(/*platform_has_biometrics=*/false),
|
||||
EnclaveManager::UvKeyState::kUsesSystemUIDeferredCreation);
|
||||
const auto& user_state =
|
||||
manager_.local_state_for_testing().users().find(gaia_id_)->second;
|
||||
|
@ -41,7 +41,9 @@
|
||||
#include "chrome/browser/signin/identity_manager_factory.h"
|
||||
#include "chrome/browser/webauthn/authenticator_request_dialog_model.h"
|
||||
#include "chrome/browser/webauthn/change_pin_controller_impl.h"
|
||||
#include "chrome/browser/webauthn/enclave_manager.h"
|
||||
#include "chrome/browser/webauthn/enclave_manager_factory.h"
|
||||
#include "chrome/browser/webauthn/gpm_user_verification_policy.h"
|
||||
#include "chrome/browser/webauthn/passkey_model_factory.h"
|
||||
#include "chrome/browser/webauthn/proto/enclave_local_state.pb.h"
|
||||
#include "chrome/browser/webauthn/webauthn_pref_names.h"
|
||||
@ -64,7 +66,6 @@
|
||||
#include "device/fido/fido_discovery_base.h"
|
||||
#include "device/fido/fido_discovery_factory.h"
|
||||
#include "device/fido/fido_types.h"
|
||||
#include "device/fido/platform_user_verification_policy.h"
|
||||
#include "services/network/public/cpp/shared_url_loader_factory.h"
|
||||
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
@ -273,12 +274,19 @@ EnclaveUserVerificationMethod PickEnclaveUserVerificationMethod(
|
||||
device::UserVerificationRequirement uv,
|
||||
bool have_added_device,
|
||||
bool has_pin,
|
||||
EnclaveManager::UvKeyState uv_key_state) {
|
||||
EnclaveManager::UvKeyState uv_key_state,
|
||||
bool platform_has_biometrics) {
|
||||
if (have_added_device) {
|
||||
return EnclaveUserVerificationMethod::kImplicit;
|
||||
}
|
||||
|
||||
if (!device::fido::PlatformWillDoUserVerification(uv)) {
|
||||
// If the platform has biometrics now, but didn't when we enrolled, we need to
|
||||
// act as if they are missing because we've no UV key to use them with.
|
||||
if (uv_key_state == EnclaveManager::UvKeyState::kNone) {
|
||||
platform_has_biometrics = false;
|
||||
}
|
||||
|
||||
if (!GpmWillDoUserVerification(uv, platform_has_biometrics)) {
|
||||
return EnclaveUserVerificationMethod::kNone;
|
||||
}
|
||||
|
||||
@ -779,49 +787,21 @@ void GPMEnclaveController::OnEnclaveAccountSetUpComplete() {
|
||||
SetAccountStateReady();
|
||||
SetFailedPINAttemptCount(0);
|
||||
|
||||
switch (account_state_) {
|
||||
case AccountState::kReady:
|
||||
model_->SetStep(Step::kGPMConnecting);
|
||||
StartTransaction();
|
||||
break;
|
||||
uv_method_ = PickEnclaveUserVerificationMethod(
|
||||
user_verification_requirement_, have_added_device_,
|
||||
enclave_manager_->has_wrapped_pin(),
|
||||
enclave_manager_->uv_key_state(*model_->platform_has_biometrics),
|
||||
*model_->platform_has_biometrics);
|
||||
// `have_added_device_` is set, which satisfies UV, so we must have picked
|
||||
// "implicit" UV.
|
||||
CHECK_EQ(*uv_method_, EnclaveUserVerificationMethod::kImplicit);
|
||||
|
||||
case AccountState::kReadyWithBiometrics:
|
||||
model_->SetStep(Step::kGPMTouchID);
|
||||
break;
|
||||
|
||||
default:
|
||||
// kReadyWithPIN is not possible because `have_added_device_` is set
|
||||
// and so user verification will be satisfied with the stored security
|
||||
// domain secret in this case.
|
||||
NOTREACHED_NORETURN();
|
||||
}
|
||||
model_->SetStep(Step::kGPMConnecting);
|
||||
StartTransaction();
|
||||
}
|
||||
|
||||
void GPMEnclaveController::SetAccountStateReady() {
|
||||
uv_method_ = PickEnclaveUserVerificationMethod(
|
||||
user_verification_requirement_, have_added_device_,
|
||||
enclave_manager_->has_wrapped_pin(), enclave_manager_->uv_key_state());
|
||||
switch (*uv_method_) {
|
||||
case EnclaveUserVerificationMethod::kUVKeyWithSystemUI:
|
||||
case EnclaveUserVerificationMethod::kDeferredUVKeyWithSystemUI:
|
||||
case EnclaveUserVerificationMethod::kNone:
|
||||
case EnclaveUserVerificationMethod::kImplicit:
|
||||
account_state_ = AccountState::kReady;
|
||||
break;
|
||||
|
||||
case EnclaveUserVerificationMethod::kPIN:
|
||||
account_state_ = AccountState::kReadyWithPIN;
|
||||
break;
|
||||
|
||||
case EnclaveUserVerificationMethod::kUVKeyWithChromeUI:
|
||||
account_state_ = AccountState::kReadyWithBiometrics;
|
||||
break;
|
||||
|
||||
case EnclaveUserVerificationMethod::kUnsatisfiable:
|
||||
account_state_ = AccountState::kNone;
|
||||
break;
|
||||
}
|
||||
|
||||
account_state_ = AccountState::kReady;
|
||||
pin_is_arbitrary_ = enclave_manager_->has_wrapped_pin() &&
|
||||
enclave_manager_->wrapped_pin_is_arbitrary();
|
||||
}
|
||||
@ -834,13 +814,33 @@ void GPMEnclaveController::OnGPMSelected() {
|
||||
|
||||
switch (account_state_) {
|
||||
case AccountState::kEmpty:
|
||||
case AccountState::kReady:
|
||||
case AccountState::kReadyWithPIN:
|
||||
model_->SetStep(Step::kGPMCreatePasskey);
|
||||
break;
|
||||
|
||||
case AccountState::kReadyWithBiometrics:
|
||||
model_->SetStep(Step::kGPMTouchID);
|
||||
case AccountState::kReady:
|
||||
uv_method_ = PickEnclaveUserVerificationMethod(
|
||||
user_verification_requirement_, have_added_device_,
|
||||
enclave_manager_->has_wrapped_pin(),
|
||||
enclave_manager_->uv_key_state(*model_->platform_has_biometrics),
|
||||
*model_->platform_has_biometrics);
|
||||
|
||||
switch (*uv_method_) {
|
||||
case EnclaveUserVerificationMethod::kUVKeyWithSystemUI:
|
||||
case EnclaveUserVerificationMethod::kDeferredUVKeyWithSystemUI:
|
||||
case EnclaveUserVerificationMethod::kNone:
|
||||
case EnclaveUserVerificationMethod::kImplicit:
|
||||
case EnclaveUserVerificationMethod::kPIN:
|
||||
model_->SetStep(Step::kGPMCreatePasskey);
|
||||
break;
|
||||
|
||||
case EnclaveUserVerificationMethod::kUVKeyWithChromeUI:
|
||||
model_->SetStep(Step::kGPMTouchID);
|
||||
break;
|
||||
|
||||
case EnclaveUserVerificationMethod::kUnsatisfiable:
|
||||
model_->SetStep(Step::kGPMError);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case AccountState::kRecoverable:
|
||||
@ -879,19 +879,36 @@ void GPMEnclaveController::OnGPMPasskeySelected(
|
||||
|
||||
switch (account_state_) {
|
||||
case AccountState::kReady:
|
||||
if (model_->step() != Step::kConditionalMediation) {
|
||||
// The autofill UI shows its own loading indicator.
|
||||
model_->SetStep(Step::kGPMConnecting);
|
||||
uv_method_ = PickEnclaveUserVerificationMethod(
|
||||
user_verification_requirement_, have_added_device_,
|
||||
enclave_manager_->has_wrapped_pin(),
|
||||
enclave_manager_->uv_key_state(*model_->platform_has_biometrics),
|
||||
*model_->platform_has_biometrics);
|
||||
|
||||
switch (*uv_method_) {
|
||||
case EnclaveUserVerificationMethod::kUVKeyWithSystemUI:
|
||||
case EnclaveUserVerificationMethod::kDeferredUVKeyWithSystemUI:
|
||||
case EnclaveUserVerificationMethod::kNone:
|
||||
case EnclaveUserVerificationMethod::kImplicit:
|
||||
if (model_->step() != Step::kConditionalMediation) {
|
||||
// The autofill UI shows its own loading indicator.
|
||||
model_->SetStep(Step::kGPMConnecting);
|
||||
}
|
||||
StartTransaction();
|
||||
break;
|
||||
|
||||
case EnclaveUserVerificationMethod::kPIN:
|
||||
PromptForPin();
|
||||
break;
|
||||
|
||||
case EnclaveUserVerificationMethod::kUVKeyWithChromeUI:
|
||||
model_->SetStep(Step::kGPMTouchID);
|
||||
break;
|
||||
|
||||
case EnclaveUserVerificationMethod::kUnsatisfiable:
|
||||
model_->SetStep(Step::kGPMError);
|
||||
break;
|
||||
}
|
||||
StartTransaction();
|
||||
break;
|
||||
|
||||
case AccountState::kReadyWithPIN:
|
||||
PromptForPin();
|
||||
break;
|
||||
|
||||
case AccountState::kReadyWithBiometrics:
|
||||
model_->SetStep(Step::kGPMTouchID);
|
||||
break;
|
||||
|
||||
case AccountState::kRecoverable:
|
||||
@ -994,20 +1011,30 @@ void GPMEnclaveController::OnGPMPinOptionChanged(bool is_arbitrary) {
|
||||
void GPMEnclaveController::OnGPMCreatePasskey() {
|
||||
CHECK_EQ(model_->step(), Step::kGPMCreatePasskey);
|
||||
CHECK(account_state_ == AccountState::kEmpty ||
|
||||
account_state_ == AccountState::kReady ||
|
||||
account_state_ == AccountState::kReadyWithPIN ||
|
||||
account_state_ == AccountState::kReadyWithBiometrics);
|
||||
account_state_ == AccountState::kReady);
|
||||
if (account_state_ == AccountState::kEmpty) {
|
||||
model_->SetStep(Step::kGPMCreatePin);
|
||||
} else if (account_state_ == AccountState::kReady) {
|
||||
model_->SetStep(Step::kGPMConnecting);
|
||||
StartTransaction();
|
||||
} else if (account_state_ == AccountState::kReadyWithPIN) {
|
||||
PromptForPin();
|
||||
} else if (account_state_ == AccountState::kReadyWithBiometrics) {
|
||||
model_->SetStep(Step::kGPMTouchID);
|
||||
} else {
|
||||
NOTREACHED_NORETURN();
|
||||
switch (*uv_method_) {
|
||||
case EnclaveUserVerificationMethod::kUVKeyWithSystemUI:
|
||||
case EnclaveUserVerificationMethod::kDeferredUVKeyWithSystemUI:
|
||||
case EnclaveUserVerificationMethod::kNone:
|
||||
case EnclaveUserVerificationMethod::kImplicit:
|
||||
model_->SetStep(Step::kGPMConnecting);
|
||||
StartTransaction();
|
||||
break;
|
||||
|
||||
case EnclaveUserVerificationMethod::kPIN:
|
||||
PromptForPin();
|
||||
break;
|
||||
|
||||
case EnclaveUserVerificationMethod::kUVKeyWithChromeUI:
|
||||
model_->SetStep(Step::kGPMTouchID);
|
||||
break;
|
||||
|
||||
case EnclaveUserVerificationMethod::kUnsatisfiable:
|
||||
NOTREACHED_NORETURN();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -74,12 +74,6 @@ class GPMEnclaveController : AuthenticatorRequestDialogModel::Observer,
|
||||
kEmpty,
|
||||
// The enclave is ready to use.
|
||||
kReady,
|
||||
// The enclave is ready to use, but the UI needs to collect a PIN before
|
||||
// making a transaction.
|
||||
kReadyWithPIN,
|
||||
// The enclave is ready to use, but the UI needs to collect biometrics
|
||||
// before making a transaction.
|
||||
kReadyWithBiometrics,
|
||||
};
|
||||
|
||||
explicit GPMEnclaveController(
|
||||
|
29
chrome/browser/webauthn/gpm_user_verification_policy.cc
Normal file
29
chrome/browser/webauthn/gpm_user_verification_policy.cc
Normal file
@ -0,0 +1,29 @@
|
||||
// Copyright 2024 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "chrome/browser/webauthn/gpm_user_verification_policy.h"
|
||||
|
||||
#include "build/build_config.h"
|
||||
#include "device/fido/fido_types.h"
|
||||
|
||||
bool GpmWillDoUserVerification(device::UserVerificationRequirement requirement,
|
||||
bool platform_has_biometrics) {
|
||||
switch (requirement) {
|
||||
case device::UserVerificationRequirement::kRequired:
|
||||
return true;
|
||||
case device::UserVerificationRequirement::kPreferred:
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
return platform_has_biometrics;
|
||||
#elif BUILDFLAG(IS_MAC)
|
||||
return platform_has_biometrics;
|
||||
#elif BUILDFLAG(IS_LINUX)
|
||||
return false;
|
||||
#else
|
||||
// This default is for unit tests.
|
||||
return true;
|
||||
#endif
|
||||
case device::UserVerificationRequirement::kDiscouraged:
|
||||
return false;
|
||||
}
|
||||
}
|
17
chrome/browser/webauthn/gpm_user_verification_policy.h
Normal file
17
chrome/browser/webauthn/gpm_user_verification_policy.h
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright 2024 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef CHROME_BROWSER_WEBAUTHN_GPM_USER_VERIFICATION_POLICY_H_
|
||||
#define CHROME_BROWSER_WEBAUTHN_GPM_USER_VERIFICATION_POLICY_H_
|
||||
|
||||
namespace device {
|
||||
enum class UserVerificationRequirement;
|
||||
}
|
||||
|
||||
// Returns whether Google Password Manager policy is to do user verification for
|
||||
// the given requirement.
|
||||
bool GpmWillDoUserVerification(device::UserVerificationRequirement requirement,
|
||||
bool platform_has_biometrics);
|
||||
|
||||
#endif // CHROME_BROWSER_WEBAUTHN_GPM_USER_VERIFICATION_POLICY_H_
|
@ -236,8 +236,6 @@ component("fido") {
|
||||
"pin_internal.cc",
|
||||
"pin_internal.h",
|
||||
"platform_credential_store.h",
|
||||
"platform_user_verification_policy.cc",
|
||||
"platform_user_verification_policy.h",
|
||||
"reset_request_handler.cc",
|
||||
"reset_request_handler.h",
|
||||
"set_pin_request_handler.cc",
|
||||
|
@ -35,6 +35,7 @@
|
||||
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
#include "base/process/process_info.h"
|
||||
#include "device/fido/mac/util.h"
|
||||
#endif
|
||||
|
||||
namespace device {
|
||||
@ -80,6 +81,10 @@ struct TransportAvailabilityCallbackReadiness {
|
||||
// callback is pending |OnIsUvpaa| being called.
|
||||
bool win_is_uvpaa_check_pending = false;
|
||||
|
||||
// platform_biometrics_check_pending is set if an asynchronous check for
|
||||
// local biometric availability is pending.
|
||||
bool platform_biometrics_check_pending = false;
|
||||
|
||||
// num_discoveries_pending is the number of discoveries that are still yet to
|
||||
// signal that they have started.
|
||||
unsigned num_discoveries_pending = 0;
|
||||
@ -93,7 +98,8 @@ struct TransportAvailabilityCallbackReadiness {
|
||||
bool CanMakeCallback() const {
|
||||
return !callback_made && !ble_information_pending &&
|
||||
num_platform_credential_checks_pending == 0 &&
|
||||
!win_is_uvpaa_check_pending && num_discoveries_pending == 0;
|
||||
!win_is_uvpaa_check_pending && !platform_biometrics_check_pending &&
|
||||
num_discoveries_pending == 0;
|
||||
}
|
||||
};
|
||||
|
||||
@ -282,6 +288,14 @@ void FidoRequestHandlerBase::InitDiscoveries(
|
||||
weak_factory_.GetWeakPtr()));
|
||||
}
|
||||
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
transport_availability_info_.platform_has_biometrics =
|
||||
device::fido::mac::DeviceHasBiometricsAvailable();
|
||||
#elif BUILDFLAG(IS_WIN)
|
||||
// TODO(enclave): this should check whether biometrics are actually available.
|
||||
transport_availability_info_.platform_has_biometrics = true;
|
||||
#endif
|
||||
|
||||
MaybeSignalTransportsEnumerated();
|
||||
}
|
||||
|
||||
|
@ -149,6 +149,9 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoRequestHandlerBase
|
||||
// authenticator is available.
|
||||
bool win_is_uvpaa = false;
|
||||
|
||||
// Whether the platform can check biometrics and has biometrics configured.
|
||||
bool platform_has_biometrics = false;
|
||||
|
||||
// Indicates whether the request is occurring in an off-the-record
|
||||
// BrowserContext (e.g. Chrome Incognito mode).
|
||||
bool is_off_the_record_context = false;
|
||||
|
@ -21,7 +21,6 @@
|
||||
#include "device/fido/fido_types.h"
|
||||
#include "device/fido/mac/credential_metadata.h"
|
||||
#include "device/fido/mac/util.h"
|
||||
#include "device/fido/platform_user_verification_policy.h"
|
||||
#include "device/fido/public_key_credential_descriptor.h"
|
||||
#include "device/fido/public_key_credential_user_entity.h"
|
||||
#include "device/fido/strings/grit/fido_strings.h"
|
||||
@ -64,12 +63,13 @@ void GetAssertionOperation::Run() {
|
||||
return;
|
||||
}
|
||||
|
||||
bool require_uv =
|
||||
PlatformWillDoUserVerification(request_.user_verification) ||
|
||||
std::any_of(credentials->begin(), credentials->end(),
|
||||
[](const Credential& credential) {
|
||||
return credential.RequiresUvForSignature();
|
||||
});
|
||||
bool require_uv = ProfileAuthenticatorWillDoUserVerification(
|
||||
request_.user_verification,
|
||||
device::fido::mac::DeviceHasBiometricsAvailable()) ||
|
||||
std::any_of(credentials->begin(), credentials->end(),
|
||||
[](const Credential& credential) {
|
||||
return credential.RequiresUvForSignature();
|
||||
});
|
||||
if (require_uv) {
|
||||
touch_id_context_->PromptTouchId(
|
||||
l10n_util::GetStringFUTF16(IDS_WEBAUTHN_TOUCH_ID_PROMPT_REASON,
|
||||
|
@ -26,7 +26,6 @@
|
||||
#include "device/fido/mac/credential_metadata.h"
|
||||
#include "device/fido/mac/credential_store.h"
|
||||
#include "device/fido/mac/util.h"
|
||||
#include "device/fido/platform_user_verification_policy.h"
|
||||
#include "device/fido/public_key.h"
|
||||
#include "device/fido/strings/grit/fido_strings.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
@ -54,8 +53,9 @@ void MakeCredentialOperation::Run() {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool require_uv =
|
||||
PlatformWillDoUserVerification(request_.user_verification);
|
||||
const bool require_uv = ProfileAuthenticatorWillDoUserVerification(
|
||||
request_.user_verification,
|
||||
device::fido::mac::DeviceHasBiometricsAvailable());
|
||||
if (require_uv) {
|
||||
touch_id_context_->PromptTouchId(
|
||||
l10n_util::GetStringFUTF16(IDS_WEBAUTHN_TOUCH_ID_PROMPT_REASON,
|
||||
|
@ -75,6 +75,12 @@ class COMPONENT_EXPORT(DEVICE_FIDO) ScopedProcessIsSignedOverride {
|
||||
~ScopedProcessIsSignedOverride();
|
||||
};
|
||||
|
||||
// Returns whether the macOS profile authenticator will do user verification.
|
||||
COMPONENT_EXPORT(DEVICE_FIDO)
|
||||
bool ProfileAuthenticatorWillDoUserVerification(
|
||||
device::UserVerificationRequirement requirement,
|
||||
bool platform_has_biometrics);
|
||||
|
||||
// Returns whether biometrics are available for use. On macOS, this translates
|
||||
// to whether the device supports Touch ID, and whether the sensor is ready to
|
||||
// be used (i.e. not soft-locked from consecutive bad attempts; laptop lid not
|
||||
|
@ -196,6 +196,13 @@ CodeSigningState ProcessIsSigned() {
|
||||
: CodeSigningState::kNotSigned;
|
||||
}
|
||||
|
||||
bool ProfileAuthenticatorWillDoUserVerification(
|
||||
device::UserVerificationRequirement requirement,
|
||||
bool platform_has_biometrics) {
|
||||
return requirement == device::UserVerificationRequirement::kRequired ||
|
||||
platform_has_biometrics;
|
||||
}
|
||||
|
||||
std::optional<bool>& GetBiometricOverride() {
|
||||
static std::optional<bool> flag;
|
||||
return flag;
|
||||
|
@ -1,27 +0,0 @@
|
||||
// Copyright 2024 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "device/fido/platform_user_verification_policy.h"
|
||||
|
||||
#include "build/build_config.h"
|
||||
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
#include "device/fido/mac/util.h"
|
||||
#endif
|
||||
|
||||
namespace device::fido {
|
||||
|
||||
bool PlatformWillDoUserVerification(UserVerificationRequirement requirement) {
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
return true;
|
||||
#elif BUILDFLAG(IS_MAC)
|
||||
return requirement == UserVerificationRequirement::kRequired ||
|
||||
mac::DeviceHasBiometricsAvailable();
|
||||
#else
|
||||
// This default is for unit tests.
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace device::fido
|
@ -1,21 +0,0 @@
|
||||
// Copyright 2024 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef DEVICE_FIDO_PLATFORM_USER_VERIFICATION_POLICY_H_
|
||||
#define DEVICE_FIDO_PLATFORM_USER_VERIFICATION_POLICY_H_
|
||||
|
||||
#include "base/component_export.h"
|
||||
#include "device/fido/fido_types.h"
|
||||
|
||||
namespace device::fido {
|
||||
|
||||
// Returns whether the platform norm is to do user verification for the given
|
||||
// requirement. The platform norm is taken from the native platform
|
||||
// authenticator, e.g. Windows Hello on Windows or iCloud Keychain on macOS.
|
||||
COMPONENT_EXPORT(DEVICE_FIDO)
|
||||
bool PlatformWillDoUserVerification(UserVerificationRequirement requirement);
|
||||
|
||||
} // namespace device::fido
|
||||
|
||||
#endif // DEVICE_FIDO_PLATFORM_USER_VERIFICATION_POLICY_H_
|
Reference in New Issue
Block a user