1// Copyright 2024 The Chromium Authors 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <atomic> 6#include <functional> 7#include <memory> 8#include <utility> 9 10#import <LocalAuthentication/LocalAuthentication.h> 11 12#include "base/functional/bind.h" 13#include "base/functional/callback_helpers.h" 14#include "base/location.h" 15#include "base/logging.h" 16#include "base/memory/scoped_refptr.h" 17#include "base/task/bind_post_task.h" 18#include "base/task/single_thread_task_runner.h" 19#include "base/task/thread_pool.h" 20#include "base/threading/scoped_thread_priority.h" 21#include "crypto/apple_keychain_v2.h" 22#include "crypto/scoped_lacontext.h" 23#include "crypto/unexportable_key.h" 24#include "crypto/unexportable_key_mac.h" 25#include "crypto/user_verifying_key.h" 26 27namespace crypto { 28 29namespace { 30 31// Refcounted wrapper for UnexportableSigningKey. 32class RefCountedUnexportableSigningKey 33 : public base::RefCountedThreadSafe<RefCountedUnexportableSigningKey> { 34 public: 35 explicit RefCountedUnexportableSigningKey( 36 std::unique_ptr<UnexportableSigningKey> key) 37 : key_(std::move(key)) {} 38 39 UnexportableSigningKey* key() { return key_.get(); } 40 41 private: 42 friend class base::RefCountedThreadSafe<RefCountedUnexportableSigningKey>; 43 ~RefCountedUnexportableSigningKey() = default; 44 45 std::unique_ptr<UnexportableSigningKey> key_; 46}; 47 48// Wraps signing |data| with |key|. 49std::optional<std::vector<uint8_t>> DoSign( 50 std::vector<uint8_t> data, 51 scoped_refptr<RefCountedUnexportableSigningKey> key) { 52 return key->key()->SignSlowly(data); 53} 54 55std::string ToString(const std::vector<uint8_t>& vec) { 56 return std::string(vec.begin(), vec.end()); 57} 58 59// User verifying key implementation that delegates the heavy lifting to 60// UnexportableKeyMac. 61class UserVerifyingSigningKeyMac : public UserVerifyingSigningKey { 62 public: 63 explicit UserVerifyingSigningKeyMac( 64 std::unique_ptr<UnexportableSigningKey> key) 65 : key_name_(ToString(key->GetWrappedKey())), 66 key_(base::MakeRefCounted<RefCountedUnexportableSigningKey>( 67 std::move(key))) {} 68 ~UserVerifyingSigningKeyMac() override = default; 69 70 void Sign(base::span<const uint8_t> data, 71 base::OnceCallback<void(std::optional<std::vector<uint8_t>>)> 72 callback) override { 73 // Signing will result in the system TouchID prompt being shown if the 74 // caller does not pass an authenticated LAContext, so we run the signing 75 // code in a separate thread. 76 scoped_refptr<base::SequencedTaskRunner> worker_task_runner = 77 base::ThreadPool::CreateSequencedTaskRunner( 78 {base::MayBlock(), base::TaskPriority::USER_BLOCKING}); 79 80 // Copy |data| to avoid needing to guarantee its backing storage to outlive 81 // the thread. 82 std::vector<uint8_t> data_copy(data.begin(), data.end()); 83 84 worker_task_runner->PostTaskAndReplyWithResult( 85 FROM_HERE, base::BindOnce(&DoSign, std::move(data_copy), key_), 86 std::move(callback)); 87 } 88 89 std::vector<uint8_t> GetPublicKey() const override { 90 return key_->key()->GetSubjectPublicKeyInfo(); 91 } 92 93 const UserVerifyingKeyLabel& GetKeyLabel() const override { 94 return key_name_; 95 } 96 97 private: 98 // The key's wrapped key as a binary string. 99 const std::string key_name_; 100 const scoped_refptr<RefCountedUnexportableSigningKey> key_; 101}; 102 103std::unique_ptr<UserVerifyingSigningKey> DoGenerateKey( 104 base::span<const SignatureVerifier::SignatureAlgorithm> 105 acceptable_algorithms, 106 UnexportableKeyProvider::Config config, 107 LAContext* lacontext) { 108 std::unique_ptr<UnexportableKeyProviderMac> key_provider = 109 GetUnexportableKeyProviderMac(std::move(config)); 110 if (!key_provider) { 111 return nullptr; 112 } 113 114 std::unique_ptr<UnexportableSigningKey> key = 115 key_provider->GenerateSigningKeySlowly(acceptable_algorithms, lacontext); 116 if (!key) { 117 return nullptr; 118 } 119 return std::make_unique<UserVerifyingSigningKeyMac>(std::move(key)); 120} 121 122std::unique_ptr<UserVerifyingSigningKey> DoGetKey( 123 std::vector<uint8_t> wrapped_key, 124 UnexportableKeyProvider::Config config, 125 LAContext* lacontext) { 126 std::unique_ptr<UnexportableKeyProviderMac> key_provider = 127 GetUnexportableKeyProviderMac(std::move(config)); 128 if (!key_provider) { 129 return nullptr; 130 } 131 std::unique_ptr<UnexportableSigningKey> key = 132 key_provider->FromWrappedSigningKeySlowly(wrapped_key, lacontext); 133 if (!key) { 134 return nullptr; 135 } 136 return std::make_unique<UserVerifyingSigningKeyMac>(std::move(key)); 137} 138 139bool DoDeleteKey(std::vector<uint8_t> wrapped_key, 140 UnexportableKeyProvider::Config config) { 141 std::unique_ptr<UnexportableKeyProvider> key_provider = 142 GetUnexportableKeyProvider(std::move(config)); 143 if (!key_provider) { 144 return false; 145 } 146 return key_provider->DeleteSigningKey(wrapped_key); 147} 148 149class UserVerifyingKeyProviderMac : public UserVerifyingKeyProvider { 150 public: 151 explicit UserVerifyingKeyProviderMac(UserVerifyingKeyProvider::Config config) 152 : lacontext_(config.lacontext ? config.lacontext->release() : nil), 153 config_(std::move(config)) {} 154 ~UserVerifyingKeyProviderMac() override = default; 155 156 void GenerateUserVerifyingSigningKey( 157 base::span<const SignatureVerifier::SignatureAlgorithm> 158 acceptable_algorithms, 159 base::OnceCallback<void(std::unique_ptr<UserVerifyingSigningKey>)> 160 callback) override { 161 // Creating a key may result in disk access, so do it in a separate thread 162 // to avoid blocking the UI. 163 scoped_refptr<base::SequencedTaskRunner> worker_task_runner = 164 base::ThreadPool::CreateSequencedTaskRunner( 165 {base::MayBlock(), base::TaskPriority::USER_BLOCKING}); 166 std::vector<SignatureVerifier::SignatureAlgorithm> algorithms( 167 acceptable_algorithms.begin(), acceptable_algorithms.end()); 168 worker_task_runner->PostTaskAndReplyWithResult( 169 FROM_HERE, 170 base::BindOnce(&DoGenerateKey, std::move(algorithms), 171 MakeUnexportableKeyConfig(), lacontext_), 172 std::move(callback)); 173 } 174 175 void GetUserVerifyingSigningKey( 176 UserVerifyingKeyLabel key_label, 177 base::OnceCallback<void(std::unique_ptr<UserVerifyingSigningKey>)> 178 callback) override { 179 // Retrieving a key may result in disk access, so do it in a separate thread 180 // to avoid blocking the UI. 181 scoped_refptr<base::SequencedTaskRunner> worker_task_runner = 182 base::ThreadPool::CreateSequencedTaskRunner( 183 {base::MayBlock(), base::TaskPriority::USER_BLOCKING}); 184 std::vector<uint8_t> wrapped_key(key_label.begin(), key_label.end()); 185 worker_task_runner->PostTaskAndReplyWithResult( 186 FROM_HERE, 187 base::BindOnce(&DoGetKey, std::move(std::move(wrapped_key)), 188 MakeUnexportableKeyConfig(), lacontext_), 189 std::move(callback)); 190 } 191 192 void DeleteUserVerifyingKey( 193 UserVerifyingKeyLabel key_label, 194 base::OnceCallback<void(bool)> callback) override { 195 // Deleting a key may result in disk access, so do it in a separate thread 196 // to avoid blocking the UI. 197 scoped_refptr<base::SequencedTaskRunner> worker_task_runner = 198 base::ThreadPool::CreateSequencedTaskRunner( 199 {base::MayBlock(), base::TaskPriority::USER_BLOCKING}); 200 std::vector<uint8_t> wrapped_key(key_label.begin(), key_label.end()); 201 worker_task_runner->PostTaskAndReplyWithResult( 202 FROM_HERE, 203 base::BindOnce(&DoDeleteKey, std::move(wrapped_key), 204 MakeUnexportableKeyConfig()), 205 std::move(callback)); 206 } 207 208 private: 209 UnexportableKeyProvider::Config MakeUnexportableKeyConfig() { 210 return { 211 .keychain_access_group = config_.keychain_access_group, 212 .access_control = 213 UnexportableKeyProvider::Config::AccessControl::kUserPresence, 214 }; 215 } 216 LAContext* __strong lacontext_; 217 const UserVerifyingKeyProvider::Config config_; 218}; 219 220} // namespace 221 222std::unique_ptr<UserVerifyingKeyProvider> GetUserVerifyingKeyProviderMac( 223 UserVerifyingKeyProvider::Config config) { 224 return std::make_unique<UserVerifyingKeyProviderMac>(std::move(config)); 225} 226 227void AreMacUnexportableKeysAvailable(UserVerifyingKeyProvider::Config config, 228 base::OnceCallback<void(bool)> callback) { 229 if (!GetUnexportableKeyProvider( 230 {.keychain_access_group = std::move(config.keychain_access_group)})) { 231 std::move(callback).Run(false); 232 return; 233 } 234 std::move(callback).Run( 235 AppleKeychainV2::GetInstance().LAContextCanEvaluatePolicy( 236 LAPolicyDeviceOwnerAuthentication, /*error=*/nil)); 237} 238 239} // namespace crypto 240