blob: 810a8f6194272adde32a4ac83c24f07d2e347877 [file] [log] [blame]
// 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/chrome_web_authentication_delegate.h"
#include <algorithm>
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include "base/base64.h"
#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/containers/span.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/location.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/values.h"
#include "build/buildflag.h"
#include "chrome/browser/extensions/api/web_authentication_proxy/web_authentication_proxy_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/sync/sync_service_factory.h"
#include "chrome/browser/ui/passwords/passwords_client_ui_delegate.h"
#include "chrome/browser/webauthn/passkey_model_factory.h"
#include "chrome/browser/webauthn/unexportable_key_utils.h"
#include "chrome/browser/webauthn/webauthn_pref_names.h"
#include "chrome/browser/webauthn/webauthn_switches.h"
#include "chrome/common/chrome_version.h"
#include "chrome/common/pref_names.h"
#include "components/device_event_log/device_event_log.h"
#include "components/prefs/pref_service.h"
#include "components/signin/public/base/consent_level.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/sync/base/user_selectable_type.h"
#include "components/sync/protocol/webauthn_credential_specifics.pb.h"
#include "components/sync/service/sync_service.h"
#include "components/sync/service/sync_user_settings.h"
#include "components/webauthn/core/browser/passkey_change_quota_tracker.h"
#include "components/webauthn/core/browser/passkey_model.h"
#include "content/public/browser/authenticator_request_client_delegate.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/visibility.h"
#include "content/public/browser/web_authentication_request_proxy.h"
#include "content/public/browser/web_contents.h"
#include "crypto/unexportable_key.h"
#include "device/fido/enclave/constants.h"
#include "device/fido/features.h"
#include "device/fido/mac/credential_metadata.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension.h"
#include "extensions/common/permissions/permissions_data.h"
#include "extensions/common/url_pattern.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "net/base/url_util.h"
#include "url/origin.h"
#include "url/url_constants.h"
#include "url/url_util.h"
#if BUILDFLAG(IS_CHROMEOS)
#include "chromeos/components/webauthn/webauthn_request_registrar.h"
#include "content/public/browser/browser_thread.h"
#include "ui/aura/window.h"
#endif
namespace {
void LogSignalUnknownCredential(
ChromeWebAuthenticationDelegate::SignalUnknownCredentialResult result) {
base::UmaHistogramEnumeration(
"WebAuthentication.SignalUnknownCredentialRemovedGPMPasskey", result);
}
void LogSignalAllAcceptedCredentials(
ChromeWebAuthenticationDelegate::SignalAllAcceptedCredentialsResult
result) {
base::UmaHistogramEnumeration(
"WebAuthentication.SignalAllAcceptedCredentialsRemovedGPMPasskey",
result);
}
void LogSignalCurrentUserDetailsUpdated(
ChromeWebAuthenticationDelegate::SignalCurrentUserDetailsResult result) {
base::UmaHistogramEnumeration(
"WebAuthentication.SignalCurrentUserDetailsUpdatedGPMPasskey", result);
}
// Returns true iff |relying_party_id| is listed in the
// SecurityKeyPermitAttestation policy.
bool IsWebAuthnRPIDListedInSecurityKeyPermitAttestationPolicy(
content::BrowserContext* browser_context,
const std::string& relying_party_id) {
const Profile* profile = Profile::FromBrowserContext(browser_context);
const PrefService* prefs = profile->GetPrefs();
const base::Value::List& permit_attestation =
prefs->GetList(prefs::kSecurityKeyPermitAttestation);
return base::Contains(permit_attestation, relying_party_id);
}
bool IsOriginListedInEnterpriseAttestationSwitch(
const url::Origin& caller_origin) {
std::string cmdline_origins =
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
webauthn::switches::kPermitEnterpriseAttestationOriginList);
std::vector<std::string_view> origin_strings = base::SplitStringPiece(
cmdline_origins, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
return std::ranges::any_of(
origin_strings, [&caller_origin](std::string_view origin_string) {
return url::Origin::Create(GURL(origin_string)) == caller_origin;
});
}
// Returns true if |extension| is allowed to create and assert |rp_id|.
bool ExtensionCanAssertRpId(const extensions::Extension& extension,
const std::string& rp_id) {
// Extensions are always allowed to assert their own extension identifier.
// This has special handling in
// ChromeWebAuthenticationDelegate::MaybeGetRelyingPartyIdOverride, the
// RP ID will be prefixed with the extension scheme to isolate it from web
// origins.
if (extension.id() == rp_id) {
return true;
}
// Extensions may not claim eTLDs as RP IDs, even if WebAuthn does not
// forbid origins from doing so if they are eTLDs themselves.
if (!net::registry_controlled_domains::HostHasRegistryControlledDomain(
base::TrimString(rp_id, ".", base::TrimPositions::TRIM_TRAILING),
net::registry_controlled_domains::INCLUDE_UNKNOWN_REGISTRIES,
net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES) &&
!net::HostStringIsLocalhost(rp_id)) {
return false;
}
// An extension should be able to assert a WebAuthn RP ID if it has host
// permissions over any host that can assert that RP ID. This code duplicates
// some of the logic on content/public/browser/webauthn_security_utils.h.
// https://w3c.github.io/webauthn/#relying-party-identifier
for (const URLPattern& pattern :
extension.permissions_data()->active_permissions().explicit_hosts()) {
// Only https hosts and localhost are allowed to assert RP IDs. By design,
// this means extensions cannot claim RP IDs for other extensions.
if (!pattern.MatchesScheme(url::kHttpsScheme) &&
!(pattern.MatchesScheme(url::kHttpScheme) &&
net::HostStringIsLocalhost(pattern.host()))) {
continue;
}
// IP hosts are not allowed to assert RP IDs.
if (url::HostIsIPAddress(pattern.host())) {
continue;
}
// If the pattern matches the RP ID, then it is allowed to assert it.
// Pattern RP ID Allowed?
// *.com example.com Yes
// example.com example.com Yes
// *.example.com subdomain.example.com Yes
if (pattern.MatchesHost(rp_id)) {
return true;
}
// If the pattern matchens any valid subdomain of the RP ID, then it is
// allowed to assert it, since subdomains can assert parent components up to
// eTLD+1 on WebAuthn.
// Pattern RP ID Allowed?
// subdomain.example.com example.com Yes
// *.subdomain.example.com example.com Yes
// example.com subdomain.example.com No
if (url::DomainIs(pattern.host(), rp_id)) {
return true;
}
}
return false;
}
void DeleteUnacceptedPasskeys(
content::WebContents* web_contents,
const std::string& relying_party_id,
const std::vector<uint8_t>& user_id,
const std::vector<std::vector<uint8_t>>& all_accepted_credentials_ids) {
webauthn::PasskeyModel* passkey_store =
PasskeyModelFactory::GetInstance()->GetForProfile(
Profile::FromBrowserContext(web_contents->GetBrowserContext()));
bool is_passkey_deleted = false;
for (const auto& passkey :
passkey_store->GetPasskeysForRelyingPartyId(relying_party_id)) {
if (std::vector<uint8_t>(passkey.user_id().begin(),
passkey.user_id().end()) == user_id &&
!base::Contains(all_accepted_credentials_ids,
std::vector<uint8_t>(passkey.credential_id().begin(),
passkey.credential_id().end()))) {
passkey_store->DeletePasskey(passkey.credential_id(), FROM_HERE);
is_passkey_deleted = true;
}
}
if (is_passkey_deleted) {
PasswordsClientUIDelegate* manage_passwords_ui_controller =
PasswordsClientUIDelegateFromWebContents(web_contents);
if (manage_passwords_ui_controller) {
manage_passwords_ui_controller->OnPasskeyNotAccepted(relying_party_id);
}
}
LogSignalAllAcceptedCredentials(
is_passkey_deleted
? ChromeWebAuthenticationDelegate::
SignalAllAcceptedCredentialsResult::kPasskeyRemoved
: ChromeWebAuthenticationDelegate::
SignalAllAcceptedCredentialsResult::kNoPasskeyChanged);
}
void HideAndRestorePasskeys(
content::WebContents* web_contents,
const url::Origin& origin,
const std::string& relying_party_id,
const std::vector<uint8_t>& user_id,
const std::vector<std::vector<uint8_t>>& all_accepted_credentials_ids) {
webauthn::PasskeyChangeQuotaTracker* quota_tracker =
webauthn::PasskeyChangeQuotaTracker::GetInstance();
if (!quota_tracker->CanMakeChange(origin)) {
LogSignalAllAcceptedCredentials(
ChromeWebAuthenticationDelegate::SignalAllAcceptedCredentialsResult::
kQuotaExceeded);
FIDO_LOG(ERROR) << "Dropping all accepted credentials request from "
<< origin << ": quota exceeded.";
return;
}
webauthn::PasskeyModel* passkey_store =
PasskeyModelFactory::GetInstance()->GetForProfile(
Profile::FromBrowserContext(web_contents->GetBrowserContext()));
std::vector<sync_pb::WebauthnCredentialSpecifics> passkeys =
passkey_store->GetPasskeysForRelyingPartyId(relying_party_id);
const auto passkey_it =
std::ranges::find_if(passkeys, [&user_id](const auto& passkey) {
return std::vector<uint8_t>(passkey.user_id().begin(),
passkey.user_id().end()) == user_id;
});
if (passkey_it == passkeys.end()) {
LogSignalAllAcceptedCredentials(
ChromeWebAuthenticationDelegate::SignalAllAcceptedCredentialsResult::
kNoPasskeyChanged);
return;
}
bool passkey_in_list =
base::Contains(all_accepted_credentials_ids,
std::vector<uint8_t>(passkey_it->credential_id().begin(),
passkey_it->credential_id().end()));
if ((passkey_in_list && !passkey_it->hidden()) ||
(!passkey_in_list && passkey_it->hidden())) {
LogSignalAllAcceptedCredentials(
ChromeWebAuthenticationDelegate::SignalAllAcceptedCredentialsResult::
kNoPasskeyChanged);
return;
}
passkey_store->SetPasskeyHidden(passkey_it->credential_id(),
!passkey_in_list);
quota_tracker->TrackChange(origin);
LogSignalAllAcceptedCredentials(
passkey_in_list ? ChromeWebAuthenticationDelegate::
SignalAllAcceptedCredentialsResult::kPasskeyRestored
: ChromeWebAuthenticationDelegate::
SignalAllAcceptedCredentialsResult::kPasskeyHidden);
PasswordsClientUIDelegate* manage_passwords_ui_controller =
PasswordsClientUIDelegateFromWebContents(web_contents);
if (passkey_in_list && manage_passwords_ui_controller) {
manage_passwords_ui_controller->OnPasskeyUpdated(relying_party_id);
}
if (!passkey_in_list && manage_passwords_ui_controller) {
manage_passwords_ui_controller->OnPasskeyNotAccepted(relying_party_id);
}
}
} // namespace
ChromeWebAuthenticationDelegate::ChromeWebAuthenticationDelegate() = default;
ChromeWebAuthenticationDelegate::~ChromeWebAuthenticationDelegate() = default;
bool ChromeWebAuthenticationDelegate::
OverrideCallerOriginAndRelyingPartyIdValidation(
content::BrowserContext* browser_context,
const url::Origin& caller_origin,
const std::string& relying_party_id) {
// Allow chrome-extensions:// origins to make WebAuthn requests.
// `MaybeGetRelyingPartyId` will override the RP ID if it matches the
// extension origin.
if (caller_origin.scheme() != extensions::kExtensionScheme) {
return false;
}
const extensions::Extension* extension =
extensions::ExtensionRegistry::Get(browser_context)
->enabled_extensions()
.GetByID(caller_origin.host());
if (!extension) {
return false;
}
return ExtensionCanAssertRpId(*extension, relying_party_id);
}
std::optional<std::string>
ChromeWebAuthenticationDelegate::MaybeGetRelyingPartyIdOverride(
const std::string& claimed_relying_party_id,
const url::Origin& caller_origin) {
// Extensions may claim their origin as an RP ID. In that case, we use the
// whole origin to avoid collisions with the RP ID space for HTTPS origins.
// Extensions may not claim other extensions RP IDs, even if they have host
// permissions on them.
if (caller_origin.scheme() == extensions::kExtensionScheme &&
claimed_relying_party_id == caller_origin.host()) {
return caller_origin.Serialize();
}
return std::nullopt;
}
bool ChromeWebAuthenticationDelegate::ShouldPermitIndividualAttestation(
content::BrowserContext* browser_context,
const url::Origin& caller_origin,
const std::string& relying_party_id) {
return IsOriginListedInEnterpriseAttestationSwitch(caller_origin) ||
IsWebAuthnRPIDListedInSecurityKeyPermitAttestationPolicy(
browser_context, relying_party_id);
}
bool ChromeWebAuthenticationDelegate::SupportsResidentKeys(
content::RenderFrameHost* render_frame_host) {
return true;
}
bool ChromeWebAuthenticationDelegate::IsFocused(
content::WebContents* web_contents) {
return web_contents->GetVisibility() == content::Visibility::VISIBLE;
}
void ChromeWebAuthenticationDelegate::
IsUserVerifyingPlatformAuthenticatorAvailableOverride(
content::RenderFrameHost* render_frame_host,
base::OnceCallback<void(std::optional<bool>)> callback) {
const bool is_google =
render_frame_host->GetLastCommittedOrigin().DomainIs("google.com");
content::WebAuthenticationDelegate::
IsUserVerifyingPlatformAuthenticatorAvailableOverride(
render_frame_host,
base::BindOnce(
[](base::WeakPtr<ChromeWebAuthenticationDelegate>
web_authentication_delegate,
base::WeakPtr<content::BrowserContext> browser_context,
const bool is_google,
base::OnceCallback<void(std::optional<bool>)> callback,
std::optional<bool> testing_api_override) {
if (!browser_context || !web_authentication_delegate) {
return;
}
// If the testing API is active, its override takes precedence.
if (testing_api_override) {
std::move(callback).Run(*testing_api_override);
return;
}
// Chrome disables platform authenticators in Guest sessions.
// They may be available (behind an additional interstitial) in
// Incognito mode.
Profile* profile =
Profile::FromBrowserContext(browser_context.get());
if (profile->IsGuestSession()) {
std::move(callback).Run(false);
return;
}
// The enclave won't allow credentials for an account to be
// stored in GPM _in_ that account. So we don't want to tell
// accounts.google.com that there is a platform authenticator
// if we're later going to reject a create() request for that
// reason. Thus we have to be conservative with IsUVPAA
// responses on google.com and ignore the possibility of using
// the enclave authenticator.
if (is_google) {
std::move(callback).Run(std::nullopt);
return;
}
web_authentication_delegate->BrowserProvidedPasskeysAvailable(
browser_context.get(),
base::BindOnce(
[](base::OnceCallback<void(std::optional<bool>)>
callback,
bool available) {
if (available) {
std::move(callback).Run(true);
return;
}
std::move(callback).Run(std::nullopt);
},
std::move(callback)));
},
weak_ptr_factory_.GetWeakPtr(),
render_frame_host->GetBrowserContext()->GetWeakPtr(), is_google,
std::move(callback)));
}
content::WebAuthenticationRequestProxy*
ChromeWebAuthenticationDelegate::MaybeGetRequestProxy(
content::BrowserContext* browser_context,
const url::Origin& caller_origin) {
auto* service = extensions::WebAuthenticationProxyService::GetIfProxyAttached(
Profile::FromBrowserContext(browser_context));
return service && service->IsActive(caller_origin) ? service : nullptr;
}
void ChromeWebAuthenticationDelegate::PasskeyUnrecognized(
content::WebContents* web_contents,
const url::Origin& origin,
const std::vector<uint8_t>& passkey_credential_id,
const std::string& relying_party_id) {
webauthn::PasskeyChangeQuotaTracker* quota_tracker =
webauthn::PasskeyChangeQuotaTracker::GetInstance();
if (base::FeatureList::IsEnabled(device::kWebAuthnSignalApiHidePasskeys)) {
if (!quota_tracker->CanMakeChange(origin)) {
LogSignalUnknownCredential(SignalUnknownCredentialResult::kQuotaExceeded);
FIDO_LOG(ERROR) << "Dropping removal request from " << origin
<< ": quota exceeded.";
return;
}
}
webauthn::PasskeyModel* passkey_store =
PasskeyModelFactory::GetInstance()->GetForProfile(
Profile::FromBrowserContext(web_contents->GetBrowserContext()));
std::string credential_id(passkey_credential_id.begin(),
passkey_credential_id.end());
std::optional<sync_pb::WebauthnCredentialSpecifics> credential_specifics =
passkey_store->GetPasskeyByCredentialId(relying_party_id, credential_id);
if (!credential_specifics) {
LogSignalUnknownCredential(SignalUnknownCredentialResult::kPasskeyNotFound);
return;
}
if (base::FeatureList::IsEnabled(device::kWebAuthnSignalApiHidePasskeys)) {
if (credential_specifics->hidden()) {
LogSignalUnknownCredential(
SignalUnknownCredentialResult::kPasskeyAlreadyHidden);
return;
}
quota_tracker->TrackChange(origin);
passkey_store->SetPasskeyHidden(std::move(credential_id),
/*hidden=*/true);
LogSignalUnknownCredential(SignalUnknownCredentialResult::kPasskeyHidden);
} else {
passkey_store->DeletePasskey(std::move(credential_id), FROM_HERE);
LogSignalUnknownCredential(SignalUnknownCredentialResult::kPasskeyRemoved);
}
PasswordsClientUIDelegate* manage_passwords_ui_controller =
PasswordsClientUIDelegateFromWebContents(web_contents);
if (manage_passwords_ui_controller) {
manage_passwords_ui_controller->OnPasskeyDeleted();
}
}
void ChromeWebAuthenticationDelegate::SignalAllAcceptedCredentials(
content::WebContents* web_contents,
const url::Origin& origin,
const std::string& relying_party_id,
const std::vector<uint8_t>& user_id,
const std::vector<std::vector<uint8_t>>& all_accepted_credentials_ids) {
if (base::FeatureList::IsEnabled(device::kWebAuthnSignalApiHidePasskeys)) {
HideAndRestorePasskeys(web_contents, origin, relying_party_id, user_id,
all_accepted_credentials_ids);
} else {
DeleteUnacceptedPasskeys(web_contents, relying_party_id, user_id,
all_accepted_credentials_ids);
}
}
void ChromeWebAuthenticationDelegate::UpdateUserPasskeys(
content::WebContents* web_contents,
const url::Origin& origin,
const std::string& relying_party_id,
std::vector<uint8_t>& user_id,
const std::string& name,
const std::string& display_name) {
webauthn::PasskeyChangeQuotaTracker* quota_tracker =
webauthn::PasskeyChangeQuotaTracker::GetInstance();
if (!quota_tracker->CanMakeChange(origin)) {
LogSignalCurrentUserDetailsUpdated(
SignalCurrentUserDetailsResult::kQuotaExceeded);
FIDO_LOG(ERROR) << "Dropping update request from " << origin
<< ": quota exceeded.";
return;
}
webauthn::PasskeyModel* passkey_store =
PasskeyModelFactory::GetInstance()->GetForProfile(
Profile::FromBrowserContext(web_contents->GetBrowserContext()));
bool is_passkey_updated = false;
for (const auto& passkey :
passkey_store->GetPasskeysForRelyingPartyId(relying_party_id)) {
if (std::vector<uint8_t>(passkey.user_id().begin(),
passkey.user_id().end()) == user_id &&
(passkey.user_name() != name ||
passkey.user_display_name() != display_name)) {
passkey_store->UpdatePasskey(
passkey.credential_id(),
{.user_name = name, .user_display_name = display_name},
/*updated_by_user=*/false);
is_passkey_updated = true;
}
}
if (is_passkey_updated) {
FIDO_LOG(EVENT) << "Updating passkey user details for " << origin;
quota_tracker->TrackChange(origin);
PasswordsClientUIDelegate* manage_passwords_ui_controller =
PasswordsClientUIDelegateFromWebContents(web_contents);
if (manage_passwords_ui_controller) {
manage_passwords_ui_controller->OnPasskeyUpdated(relying_party_id);
}
}
LogSignalCurrentUserDetailsUpdated(
is_passkey_updated ? SignalCurrentUserDetailsResult::kPasskeyUpdated
: SignalCurrentUserDetailsResult::kPasskeyNotUpdated);
}
#if BUILDFLAG(IS_MAC)
// static
ChromeWebAuthenticationDelegate::TouchIdAuthenticatorConfig
ChromeWebAuthenticationDelegate::TouchIdAuthenticatorConfigForProfile(
Profile* profile) {
constexpr char kKeychainAccessGroup[] =
MAC_TEAM_IDENTIFIER_STRING "." MAC_BUNDLE_IDENTIFIER_STRING ".webauthn";
std::string metadata_secret = profile->GetPrefs()->GetString(
webauthn::pref_names::kWebAuthnTouchIdMetadataSecretPrefName);
if (metadata_secret.empty() ||
!base::Base64Decode(metadata_secret, &metadata_secret)) {
metadata_secret = device::fido::mac::GenerateCredentialMetadataSecret();
profile->GetPrefs()->SetString(
webauthn::pref_names::kWebAuthnTouchIdMetadataSecretPrefName,
base::Base64Encode(base::as_byte_span(metadata_secret)));
}
return TouchIdAuthenticatorConfig{
.keychain_access_group = kKeychainAccessGroup,
.metadata_secret = std::move(metadata_secret)};
}
std::optional<ChromeWebAuthenticationDelegate::TouchIdAuthenticatorConfig>
ChromeWebAuthenticationDelegate::GetTouchIdAuthenticatorConfig(
content::BrowserContext* browser_context) {
return TouchIdAuthenticatorConfigForProfile(
Profile::FromBrowserContext(browser_context));
}
#endif // BUILDFLAG(IS_MAC)
#if BUILDFLAG(IS_CHROMEOS)
content::WebAuthenticationDelegate::ChromeOSGenerateRequestIdCallback
ChromeWebAuthenticationDelegate::GetGenerateRequestIdCallback(
content::RenderFrameHost* render_frame_host) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
aura::Window* window =
render_frame_host->GetNativeView()->GetToplevelWindow();
return chromeos::webauthn::WebAuthnRequestRegistrar::Get()
->GetRegisterCallback(window);
}
#endif // BUILDFLAG(IS_CHROMEOS)
void ChromeWebAuthenticationDelegate::BrowserProvidedPasskeysAvailable(
content::BrowserContext* browser_context,
base::OnceCallback<void(bool)> callback) {
auto* profile =
Profile::FromBrowserContext(browser_context)->GetOriginalProfile();
auto* const identity_manager = IdentityManagerFactory::GetForProfile(profile);
if (!identity_manager ||
!identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSignin)) {
FIDO_LOG(EVENT)
<< "Enclave authenticator disabled because no suitable account";
std::move(callback).Run(false);
return;
}
auto* const sync_service = SyncServiceFactory::GetForProfile(profile);
if (!sync_service || !sync_service->GetUserSettings()->GetSelectedTypes().Has(
syncer::UserSelectableType::kPasswords)) {
FIDO_LOG(EVENT)
<< "Enclave authenticator disabled because password sync not active";
std::move(callback).Run(false);
return;
}
// Check for TPM availability.
if (tpm_available_.has_value()) {
std::move(callback).Run(*tpm_available_);
return;
}
base::Time tpm_check_start_time = base::Time::Now();
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::TaskPriority::USER_BLOCKING, base::MayBlock()},
base::BindOnce([]() -> bool {
std::unique_ptr<crypto::UnexportableKeyProvider> provider =
GetWebAuthnUnexportableKeyProvider();
if (!provider) {
FIDO_LOG(EVENT)
<< "Enclave authenticator disabled because no key provider";
return false;
}
return provider->SelectAlgorithm(device::enclave::kSigningAlgorithms) !=
std::nullopt;
}),
base::BindOnce(
[](base::OnceCallback<void(bool)> callback,
base::Time tpm_check_start_time,
base::WeakPtr<ChromeWebAuthenticationDelegate> webauthn_delegate,
bool available) {
FIDO_LOG(DEBUG) << "Checking for TPM availability took "
<< (base::Time::Now() - tpm_check_start_time);
if (webauthn_delegate) {
webauthn_delegate->tpm_available_ = available;
}
if (!available) {
FIDO_LOG(EVENT)
<< "Enclave authenticator disabled because of lack of TPM";
}
std::move(callback).Run(available);
},
std::move(callback), tpm_check_start_time,
weak_ptr_factory_.GetWeakPtr()));
}