#ifdef UNSAFE_BUFFERS_BUILD
#pragma allow_unsafe_buffers
#endif
#include "chrome/browser/webauthn/enclave_manager.h"
#include <array>
#include <cstdint>
#include <deque>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <tuple>
#include <utility>
#include <vector>
#include "base/check.h"
#include "base/check_op.h"
#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/containers/span.h"
#include "base/feature_list.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/important_file_writer.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/functional/overloaded.h"
#include "base/location.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/notreached.h"
#include "base/numerics/checked_math.h"
#include "base/numerics/safe_conversions.h"
#include "base/ranges/algorithm.h"
#include "base/sequence_checker.h"
#include "base/stl_util.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/time/time.h"
#include "base/types/expected.h"
#include "base/types/strong_alias.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/webauthn/proto/enclave_local_state.pb.h"
#include "chrome/browser/webauthn/unexportable_key_utils.h"
#include "components/cbor/diagnostic_writer.h"
#include "components/cbor/values.h"
#include "components/cbor/writer.h"
#include "components/device_event_log/device_event_log.h"
#include "components/os_crypt/sync/os_crypt.h"
#include "components/signin/public/base/consent_level.h"
#include "components/signin/public/identity_manager/access_token_info.h"
#include "components/signin/public/identity_manager/account_info.h"
#include "components/signin/public/identity_manager/accounts_in_cookie_jar_info.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/signin/public/identity_manager/primary_account_access_token_fetcher.h"
#include "components/signin/public/identity_manager/primary_account_change_event.h"
#include "components/signin/public/identity_manager/scope_set.h"
#include "components/trusted_vault/frontend_trusted_vault_connection.h"
#include "components/trusted_vault/proto/recovery_key_store.pb.h"
#include "components/trusted_vault/recovery_key_store_connection.h"
#include "components/trusted_vault/recovery_key_store_connection_impl.h"
#include "components/trusted_vault/securebox.h"
#include "components/trusted_vault/trusted_vault_access_token_fetcher_frontend.h"
#include "components/trusted_vault/trusted_vault_access_token_fetcher_impl.h"
#include "components/trusted_vault/trusted_vault_connection.h"
#include "components/trusted_vault/trusted_vault_server_constants.h"
#include "components/unexportable_keys/ref_counted_unexportable_signing_key.h"
#include "components/unexportable_keys/unexportable_key_id.h"
#include "content/public/browser/render_frame_host.h"
#include "crypto/aead.h"
#include "crypto/hkdf.h"
#include "crypto/random.h"
#include "crypto/sha2.h"
#include "crypto/unexportable_key.h"
#include "crypto/user_verifying_key.h"
#include "device/fido/enclave/constants.h"
#include "device/fido/enclave/transact.h"
#include "device/fido/enclave/types.h"
#include "device/fido/features.h"
#include "device/fido/network_context_factory.h"
#include "google_apis/gaia/core_account_id.h"
#include "google_apis/gaia/gaia_auth_util.h"
#include "google_apis/gaia/gaia_constants.h"
#include "google_apis/gaia/google_service_auth_error.h"
#include "net/base/url_util.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "services/network/public/mojom/url_loader_factory.mojom.h"
#include "third_party/abseil-cpp/absl/types/variant.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/evp.h"
#include "third_party/boringssl/src/include/openssl/rand.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "ash/shell.h"
#endif
#if BUILDFLAG(IS_MAC)
#include "crypto/scoped_lacontext.h"
#include "device/fido/enclave/icloud_recovery_key_mac.h"
#include "device/fido/mac/util.h"
#endif
enclave;
EnclaveLocalState;
struct EnclaveManager::StoreKeysArgs { … };
struct EnclaveManager::PendingAction { … };
namespace {
static bool g_invariant_override_ = …;
constexpr size_t kMaxFetchBodyBytes = …;
constexpr int kRefreshDays = …;
const net::NetworkTrafficAnnotationTag kTrafficAnnotation = …;
static const uint8_t kHashPrefix[] = …;
base::span<const uint8_t> ToSpan(const std::string& s) { … }
template <size_t N>
base::span<const uint8_t, N> ToSizedSpan(const std::string& s) { … }
template <size_t N>
std::array<uint8_t, N> ToArray(base::span<const uint8_t, N> in) { … }
std::vector<uint8_t> ToVector(const std::string& s) { … }
std::string VecToString(base::span<const uint8_t> v) { … }
bool IsValidSubjectPublicKeyInfo(base::span<const uint8_t> spki) { … }
bool IsValidUncompressedP256X962(base::span<const uint8_t> x962) { … }
std::optional<int> CheckPINInvariants(
const EnclaveLocalState::WrappedPIN& wrapped_pin) { … }
std::optional<int> CheckInvariants(const EnclaveLocalState::User& user) { … }
cbor::Value BuildRegistrationMessage(
const std::string& device_id,
const crypto::UnexportableSigningKey& identity_key,
scoped_refptr<crypto::RefCountedUserVerifyingSigningKey> uv_key,
bool defer_uv_key) { … }
cbor::Value BuildUnregisterMessage(const std::string& device_id) { … }
EnclaveLocalState::User* StateForUser(EnclaveLocalState* local_state,
const CoreAccountInfo& account) { … }
EnclaveLocalState::User* CreateStateForUser(EnclaveLocalState* local_state,
const CoreAccountInfo& account) { … }
bool IsAllOk(const cbor::Value& response, const size_t num_responses) { … }
bool SetSecurityDomainMemberKey(EnclaveLocalState::User* user,
const cbor::Value& wrap_response) { … }
cbor::Value::ArrayValue BuildSecretWrappingEnclaveRequest(
const base::flat_map<int32_t, std::vector<uint8_t>>
new_security_domain_secrets) { … }
cbor::Value::ArrayValue BuildRecoveryKeyStorePINWrappingEnclaveRequest(
base::span<const uint8_t> hashed_pin,
std::string cert_xml,
std::string sig_xml) { … }
cbor::Value::ArrayValue BuildPINWrappingEnclaveRequest(
base::span<const uint8_t> hashed_pin,
int64_t generation,
base::span<const uint8_t, 32> claim_key,
base::span<const uint8_t, enclave::kCounterIDLen> counter_id,
base::span<const uint8_t, enclave::kVaultHandleLen - 1>
vault_handle_without_type,
base::span<const uint8_t> wrapped_secret) { … }
cbor::Value::ArrayValue BuildRecoveryKeyStorePINChangeEnclaveRequest(
base::span<const uint8_t> hashed_pin,
std::string cert_xml,
std::string sig_xml,
base::span<const uint8_t, enclave::kCounterIDLen> counter_id,
base::span<const uint8_t, enclave::kVaultHandleLen - 1>
vault_handle_without_type,
base::span<const uint8_t> wrapped_secret) { … }
cbor::Value BuildPINRenewalRequest(std::string cert_xml,
std::string sig_xml,
base::span<const uint8_t> wrapped_secret,
base::span<const uint8_t> wrapped_pin) { … }
cbor::Value ConcatEnclaveRequests(cbor::Value::ArrayValue head,
cbor::Value::ArrayValue tail) { … }
bool StoreWrappedSecrets(EnclaveLocalState::User* user,
const base::flat_map<int32_t, std::vector<uint8_t>>
new_security_domain_secrets,
base::span<const cbor::Value> responses) { … }
const char* TrustedVaultRegistrationStatusToString(
trusted_vault::TrustedVaultRegistrationStatus status) { … }
std::unique_ptr<EnclaveLocalState> ParseStateFile(
const std::string& contents_str) { … }
base::flat_set<std::string> GetGaiaIDs(
const std::vector<gaia::ListedAccount>& listed_accounts) { … }
base::flat_set<std::string> GetGaiaIDs(
const google::protobuf::Map<std::string, EnclaveLocalState::User>& users) { … }
std::string UserVerifyingLabelToString(crypto::UserVerifyingKeyLabel label) { … }
std::optional<crypto::UserVerifyingKeyLabel> UserVerifyingKeyLabelFromString(
std::string saved_label) { … }
std::unique_ptr<network::SimpleURLLoader> FetchURL(
network::mojom::URLLoaderFactory* url_loader_factory,
std::string_view url,
base::OnceCallback<void(std::optional<std::string>)> callback) { … }
std::optional<std::string> CBORListOfBytestringToASN1Sequence(
const cbor::Value& array) { … }
struct PinMetadata { … };
std::optional<std::unique_ptr<trusted_vault_pb::Vault>>
RecoveryKeyStoreWrapResponseToProto(
const PinMetadata& pin_metadata,
const cbor::Value& recovery_key_store_wrap_response) { … }
base::flat_map<int32_t, std::vector<uint8_t>> GetNewSecretsToStore(
const EnclaveLocalState::User& user,
const EnclaveManager::StoreKeysArgs& args) { … }
#if BUILDFLAG(IS_CHROMEOS_ASH)
UserVerifyingKeyProviderConfigChromeos MakeUserVerifyingKeyConfig(
EnclaveManager::UVKeyOptions options) {
UserVerifyingKeyProviderConfigChromeos config{options.dialog_controller,
nullptr,
options.rp_id};
if (options.render_frame_host_id) {
auto* rfh = content::RenderFrameHost::FromID(options.render_frame_host_id);
CHECK(rfh);
config.window = rfh->GetNativeView()->GetToplevelWindow();
}
return config;
}
#else
crypto::UserVerifyingKeyProvider::Config MakeUserVerifyingKeyConfig(
EnclaveManager::UVKeyOptions options) { … }
#endif
std::unique_ptr<crypto::UserVerifyingKeyProvider>
GetUserVerifyingKeyProviderForSigning(EnclaveManager::UVKeyOptions options) { … }
std::unique_ptr<crypto::UserVerifyingKeyProvider>
GetUserVerifyingKeyProviderForCreateAndDeleteOnly() { … }
struct HashedPIN { … };
std::unique_ptr<HashedPIN> HashPINSlowly(std::string_view pin) { … }
std::pair<int32_t, std::vector<uint8_t>> GetCurrentWrappedSecretForUser(
const EnclaveLocalState::User* user) { … }
std::vector<uint8_t> EncryptWrappedPIN(
base::span<const uint8_t> security_domain_secret,
base::span<const uint8_t> cbor_bytes) { … }
static std::optional<std::pair<std::unique_ptr<trusted_vault_pb::Vault>,
trusted_vault::MemberKeysSource>>
ParseVaultAndMemberResponse(const int32_t key_version,
const PinMetadata& pin_metadata,
const cbor::Value::MapValue& response) { … }
}
class EnclaveManager::StateMachine { … };
EnclaveManager::UVKeyOptions::UVKeyOptions() = default;
EnclaveManager::UVKeyOptions::~UVKeyOptions() = default;
EnclaveManager::UVKeyOptions::UVKeyOptions(UVKeyOptions&&) = default;
EnclaveManager::UVKeyOptions& EnclaveManager::UVKeyOptions::operator=(
EnclaveManager::UVKeyOptions&& other) = default;
EnclaveManager::EnclaveManager(
const base::FilePath& base_dir,
signin::IdentityManager* identity_manager,
device::NetworkContextFactory network_context_factory,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
: … { … }
EnclaveManager::~EnclaveManager() = default;
EnclaveManager* EnclaveManager::GetEnclaveManager() { … }
bool EnclaveManager::is_idle() const { … }
bool EnclaveManager::is_loaded() const { … }
bool EnclaveManager::is_registered() const { … }
bool EnclaveManager::has_pending_keys() const { … }
bool EnclaveManager::is_ready() const { … }
unsigned EnclaveManager::store_keys_count() const { … }
void EnclaveManager::Load(base::OnceClosure closure) { … }
void EnclaveManager::RegisterIfNeeded(EnclaveManager::Callback callback) { … }
void EnclaveManager::SetupWithPIN(std::string pin,
EnclaveManager::Callback callback) { … }
bool EnclaveManager::AddDeviceToAccount(
std::optional<trusted_vault::GpmPinMetadata> pin_metadata,
EnclaveManager::Callback callback) { … }
void EnclaveManager::AddDeviceAndPINToAccount(
std::string pin,
EnclaveManager::Callback callback) { … }
void EnclaveManager::ChangePIN(std::string updated_pin,
std::string rapt,
EnclaveManager::Callback callback) { … }
void EnclaveManager::RenewPIN(EnclaveManager::Callback callback) { … }
#if BUILDFLAG(IS_MAC)
void EnclaveManager::AddICloudRecoveryKey(
std::unique_ptr<device::enclave::ICloudRecoveryKey> icloud_recovery_key,
EnclaveManager::Callback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(user_->registered());
CHECK(!secret_.empty())
<< "AddICloudRecoveryKey must be called immediately after registration "
"and before discarding the security domain secret";
auto action = std::make_unique<PendingAction>();
action->callback = std::move(callback);
action->icloud_recovery_key = std::move(icloud_recovery_key);
pending_actions_.emplace_back(std::move(action));
Act();
}
#endif
void EnclaveManager::Unenroll(EnclaveManager::Callback callback) { … }
bool EnclaveManager::ConsiderSecurityDomainState(
const trusted_vault::DownloadAuthenticationFactorsRegistrationStateResult&
state,
EnclaveManager::Callback callback) { … }
void EnclaveManager::GetIdentityKeyForSignature(
base::OnceCallback<void(
scoped_refptr<unexportable_keys::RefCountedUnexportableSigningKey>)>
callback) { … }
enclave::SigningCallback EnclaveManager::IdentityKeySigningCallback() { … }
void EnclaveManager::GetUserVerifyingKeyForSignature(
UVKeyOptions options,
base::OnceCallback<void(
scoped_refptr<crypto::RefCountedUserVerifyingSigningKey>)> callback) { … }
enclave::SigningCallback EnclaveManager::UserVerifyingKeySigningCallback(
UVKeyOptions options) { … }
device::enclave::UVKeyCreationCallback
EnclaveManager::UserVerifyingKeyCreationCallback() { … }
std::optional<std::vector<uint8_t>> EnclaveManager::GetWrappedSecret(
int32_t version) { … }
std::pair<int32_t, std::vector<uint8_t>>
EnclaveManager::GetCurrentWrappedSecret() { … }
std::optional<std::pair<int32_t, std::vector<uint8_t>>>
EnclaveManager::TakeSecret() { … }
bool EnclaveManager::has_wrapped_pin() const { … }
bool EnclaveManager::wrapped_pin_is_arbitrary() const { … }
std::unique_ptr<webauthn_pb::EnclaveLocalState_WrappedPIN>
EnclaveManager::GetWrappedPIN() { … }
EnclaveManager::UvKeyState EnclaveManager::uv_key_state(
bool platform_has_biometrics) const { … }
void EnclaveManager::AreUserVerifyingKeysSupported(Callback callback) { … }
std::unique_ptr<signin::PrimaryAccountAccessTokenFetcher>
EnclaveManager::GetAccessToken(
base::OnceCallback<void(std::optional<std::string>)> callback) { … }
void EnclaveManager::AddObserver(Observer* observer) { … }
void EnclaveManager::RemoveObserver(Observer* observer) { … }
void EnclaveManager::StoreKeys(const std::string& gaia_id,
std::vector<std::vector<uint8_t>> keys,
int last_key_version) { … }
std::unique_ptr<enclave::ClaimedPIN> EnclaveManager::MakeClaimedPINSlowly(
std::string pin,
std::unique_ptr<webauthn_pb::EnclaveLocalState_WrappedPIN> wrapped_pin) { … }
bool EnclaveManager::RunWhenStoppedForTesting(base::OnceClosure on_stop) { … }
EnclaveLocalState& EnclaveManager::local_state_for_testing() const { … }
void EnclaveManager::ClearCachedKeysForTesting() { … }
void EnclaveManager::ResetForTesting() { … }
void EnclaveManager::ClearRegistrationForTesting() { … }
void EnclaveManager::EnableInvariantChecksForTesting(bool enabled) { … }
unsigned EnclaveManager::renewal_checks_for_testing() const { … }
unsigned EnclaveManager::renewal_attempts_for_testing() const { … }
std::string EnclaveManager::MakeWrappedPINForTesting(
base::span<const uint8_t> security_domain_secret,
std::string_view pin) { … }
class EnclaveManager::IdentityObserver
: public signin::IdentityManager::Observer { … };
void EnclaveManager::Act() { … }
void EnclaveManager::LoadComplete(std::optional<std::string> contents) { … }
void EnclaveManager::HandleIdentityChange(bool is_post_load) { … }
void EnclaveManager::Stopped() { … }
void EnclaveManager::CancelAllActions() { … }
void EnclaveManager::WriteState(EnclaveLocalState* new_state) { … }
void EnclaveManager::DoWriteState(std::string serialized) { … }
void EnclaveManager::WriteStateComplete(bool success) { … }
void EnclaveManager::ClearRegistration() { … }
void EnclaveManager::UnregisterComplete(EnclaveManager::Callback callback,
bool success) { … }
void EnclaveManager::SetSecret(int32_t key_version,
base::span<const uint8_t> secret) { … }
void EnclaveManager::ConsiderPinRenewal() { … }
void EnclaveManager::OnRenewalComplete(bool success) { … }
base::WeakPtr<EnclaveManager> EnclaveManager::GetWeakPtr() { … }