// 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 #include #include #include #import #include "base/functional/bind.h" #include "base/functional/callback_helpers.h" #include "base/location.h" #include "base/logging.h" #include "base/memory/scoped_refptr.h" #include "base/task/bind_post_task.h" #include "base/task/single_thread_task_runner.h" #include "base/task/thread_pool.h" #include "base/threading/scoped_thread_priority.h" #include "crypto/apple_keychain_v2.h" #include "crypto/scoped_lacontext.h" #include "crypto/unexportable_key.h" #include "crypto/unexportable_key_mac.h" #include "crypto/user_verifying_key.h" namespace crypto { namespace { // Refcounted wrapper for UnexportableSigningKey. class RefCountedUnexportableSigningKey : public base::RefCountedThreadSafe { public: explicit RefCountedUnexportableSigningKey( std::unique_ptr key) : key_(std::move(key)) {} UnexportableSigningKey* key() { return key_.get(); } private: friend class base::RefCountedThreadSafe; ~RefCountedUnexportableSigningKey() = default; std::unique_ptr key_; }; // Wraps signing |data| with |key|. std::optional> DoSign( std::vector data, scoped_refptr key) { return key->key()->SignSlowly(data); } std::string ToString(const std::vector& vec) { return std::string(vec.begin(), vec.end()); } // User verifying key implementation that delegates the heavy lifting to // UnexportableKeyMac. class UserVerifyingSigningKeyMac : public UserVerifyingSigningKey { public: explicit UserVerifyingSigningKeyMac( std::unique_ptr key) : key_name_(ToString(key->GetWrappedKey())), key_(base::MakeRefCounted( std::move(key))) {} ~UserVerifyingSigningKeyMac() override = default; void Sign(base::span data, base::OnceCallback>)> callback) override { // Signing will result in the system TouchID prompt being shown if the // caller does not pass an authenticated LAContext, so we run the signing // code in a separate thread. scoped_refptr worker_task_runner = base::ThreadPool::CreateSequencedTaskRunner( {base::MayBlock(), base::TaskPriority::USER_BLOCKING}); // Copy |data| to avoid needing to guarantee its backing storage to outlive // the thread. std::vector data_copy(data.begin(), data.end()); worker_task_runner->PostTaskAndReplyWithResult( FROM_HERE, base::BindOnce(&DoSign, std::move(data_copy), key_), std::move(callback)); } std::vector GetPublicKey() const override { return key_->key()->GetSubjectPublicKeyInfo(); } const UserVerifyingKeyLabel& GetKeyLabel() const override { return key_name_; } private: // The key's wrapped key as a binary string. const std::string key_name_; const scoped_refptr key_; }; std::unique_ptr DoGenerateKey( base::span acceptable_algorithms, UnexportableKeyProvider::Config config, LAContext* lacontext) { std::unique_ptr key_provider = GetUnexportableKeyProviderMac(std::move(config)); if (!key_provider) { return nullptr; } std::unique_ptr key = key_provider->GenerateSigningKeySlowly(acceptable_algorithms, lacontext); if (!key) { return nullptr; } return std::make_unique(std::move(key)); } std::unique_ptr DoGetKey( std::vector wrapped_key, UnexportableKeyProvider::Config config, LAContext* lacontext) { std::unique_ptr key_provider = GetUnexportableKeyProviderMac(std::move(config)); if (!key_provider) { return nullptr; } std::unique_ptr key = key_provider->FromWrappedSigningKeySlowly(wrapped_key, lacontext); if (!key) { return nullptr; } return std::make_unique(std::move(key)); } bool DoDeleteKey(std::vector wrapped_key, UnexportableKeyProvider::Config config) { std::unique_ptr key_provider = GetUnexportableKeyProvider(std::move(config)); if (!key_provider) { return false; } return key_provider->DeleteSigningKey(wrapped_key); } class UserVerifyingKeyProviderMac : public UserVerifyingKeyProvider { public: explicit UserVerifyingKeyProviderMac(UserVerifyingKeyProvider::Config config) : lacontext_(config.lacontext ? config.lacontext->release() : nil), config_(std::move(config)) {} ~UserVerifyingKeyProviderMac() override = default; void GenerateUserVerifyingSigningKey( base::span acceptable_algorithms, base::OnceCallback)> callback) override { // Creating a key may result in disk access, so do it in a separate thread // to avoid blocking the UI. scoped_refptr worker_task_runner = base::ThreadPool::CreateSequencedTaskRunner( {base::MayBlock(), base::TaskPriority::USER_BLOCKING}); std::vector algorithms( acceptable_algorithms.begin(), acceptable_algorithms.end()); worker_task_runner->PostTaskAndReplyWithResult( FROM_HERE, base::BindOnce(&DoGenerateKey, std::move(algorithms), MakeUnexportableKeyConfig(), lacontext_), std::move(callback)); } void GetUserVerifyingSigningKey( UserVerifyingKeyLabel key_label, base::OnceCallback)> callback) override { // Retrieving a key may result in disk access, so do it in a separate thread // to avoid blocking the UI. scoped_refptr worker_task_runner = base::ThreadPool::CreateSequencedTaskRunner( {base::MayBlock(), base::TaskPriority::USER_BLOCKING}); std::vector wrapped_key(key_label.begin(), key_label.end()); worker_task_runner->PostTaskAndReplyWithResult( FROM_HERE, base::BindOnce(&DoGetKey, std::move(std::move(wrapped_key)), MakeUnexportableKeyConfig(), lacontext_), std::move(callback)); } void DeleteUserVerifyingKey( UserVerifyingKeyLabel key_label, base::OnceCallback callback) override { // Deleting a key may result in disk access, so do it in a separate thread // to avoid blocking the UI. scoped_refptr worker_task_runner = base::ThreadPool::CreateSequencedTaskRunner( {base::MayBlock(), base::TaskPriority::USER_BLOCKING}); std::vector wrapped_key(key_label.begin(), key_label.end()); worker_task_runner->PostTaskAndReplyWithResult( FROM_HERE, base::BindOnce(&DoDeleteKey, std::move(wrapped_key), MakeUnexportableKeyConfig()), std::move(callback)); } private: UnexportableKeyProvider::Config MakeUnexportableKeyConfig() { return { .keychain_access_group = config_.keychain_access_group, .access_control = UnexportableKeyProvider::Config::AccessControl::kUserPresence, }; } LAContext* __strong lacontext_; const UserVerifyingKeyProvider::Config config_; }; } // namespace std::unique_ptr GetUserVerifyingKeyProviderMac( UserVerifyingKeyProvider::Config config) { return std::make_unique(std::move(config)); } void AreMacUnexportableKeysAvailable(UserVerifyingKeyProvider::Config config, base::OnceCallback callback) { if (!GetUnexportableKeyProvider( {.keychain_access_group = std::move(config.keychain_access_group)})) { std::move(callback).Run(false); return; } std::move(callback).Run( AppleKeychainV2::GetInstance().LAContextCanEvaluatePolicy( LAPolicyDeviceOwnerAuthentication, /*error=*/nil)); } } // namespace crypto