blob: 900b3c07fc48b704f3eff520ffc0890d112d58a0 [file] [log] [blame]
Nina Satragno850d0342024-03-15 18:16:171// 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
Ken Buchanan0111d4e22024-07-11 19:53:535#include "crypto/user_verifying_key.h"
6
7#import <LocalAuthentication/LocalAuthentication.h>
8
Nina Satragno850d0342024-03-15 18:16:179#include <atomic>
10#include <functional>
11#include <memory>
12#include <utility>
13
14#include "base/functional/bind.h"
15#include "base/functional/callback_helpers.h"
16#include "base/location.h"
17#include "base/logging.h"
18#include "base/memory/scoped_refptr.h"
Nina Satragno850d0342024-03-15 18:16:1719#include "base/task/bind_post_task.h"
20#include "base/task/single_thread_task_runner.h"
21#include "base/task/thread_pool.h"
22#include "base/threading/scoped_thread_priority.h"
Ken Buchanan0111d4e22024-07-11 19:53:5323#include "base/types/expected.h"
Elly Fong-Jones38c29b62025-07-23 22:54:5324#include "crypto/apple/keychain_v2.h"
Nina Satragnodae44922024-03-22 21:20:0725#include "crypto/scoped_lacontext.h"
Nina Satragno850d0342024-03-15 18:16:1726#include "crypto/unexportable_key.h"
Nina Satragnodae44922024-03-22 21:20:0727#include "crypto/unexportable_key_mac.h"
Nina Satragno850d0342024-03-15 18:16:1728
29namespace crypto {
30
31namespace {
32
33// Refcounted wrapper for UnexportableSigningKey.
34class RefCountedUnexportableSigningKey
35 : public base::RefCountedThreadSafe<RefCountedUnexportableSigningKey> {
36 public:
37 explicit RefCountedUnexportableSigningKey(
38 std::unique_ptr<UnexportableSigningKey> key)
39 : key_(std::move(key)) {}
40
41 UnexportableSigningKey* key() { return key_.get(); }
42
43 private:
44 friend class base::RefCountedThreadSafe<RefCountedUnexportableSigningKey>;
45 ~RefCountedUnexportableSigningKey() = default;
46
47 std::unique_ptr<UnexportableSigningKey> key_;
48};
49
50// Wraps signing |data| with |key|.
Ken Buchanan0111d4e22024-07-11 19:53:5351base::expected<std::vector<uint8_t>, UserVerifyingKeySigningError> DoSign(
Nina Satragno850d0342024-03-15 18:16:1752 std::vector<uint8_t> data,
53 scoped_refptr<RefCountedUnexportableSigningKey> key) {
Ken Buchanan0111d4e22024-07-11 19:53:5354 auto opt_signature = key->key()->SignSlowly(data);
55 if (!opt_signature.has_value()) {
56 return base::unexpected(UserVerifyingKeySigningError::kUnknownError);
57 }
58 return base::ok(*opt_signature);
Nina Satragno850d0342024-03-15 18:16:1759}
60
61std::string ToString(const std::vector<uint8_t>& vec) {
62 return std::string(vec.begin(), vec.end());
63}
64
65// User verifying key implementation that delegates the heavy lifting to
66// UnexportableKeyMac.
67class UserVerifyingSigningKeyMac : public UserVerifyingSigningKey {
68 public:
69 explicit UserVerifyingSigningKeyMac(
70 std::unique_ptr<UnexportableSigningKey> key)
71 : key_name_(ToString(key->GetWrappedKey())),
72 key_(base::MakeRefCounted<RefCountedUnexportableSigningKey>(
73 std::move(key))) {}
74 ~UserVerifyingSigningKeyMac() override = default;
75
76 void Sign(base::span<const uint8_t> data,
Ken Buchanan0111d4e22024-07-11 19:53:5377 UserVerifyingKeySignatureCallback callback) override {
Nina Satragno850d0342024-03-15 18:16:1778 // Signing will result in the system TouchID prompt being shown if the
79 // caller does not pass an authenticated LAContext, so we run the signing
80 // code in a separate thread.
81 scoped_refptr<base::SequencedTaskRunner> worker_task_runner =
82 base::ThreadPool::CreateSequencedTaskRunner(
83 {base::MayBlock(), base::TaskPriority::USER_BLOCKING});
84
85 // Copy |data| to avoid needing to guarantee its backing storage to outlive
86 // the thread.
87 std::vector<uint8_t> data_copy(data.begin(), data.end());
88
89 worker_task_runner->PostTaskAndReplyWithResult(
90 FROM_HERE, base::BindOnce(&DoSign, std::move(data_copy), key_),
91 std::move(callback));
92 }
93
94 std::vector<uint8_t> GetPublicKey() const override {
95 return key_->key()->GetSubjectPublicKeyInfo();
96 }
97
98 const UserVerifyingKeyLabel& GetKeyLabel() const override {
99 return key_name_;
100 }
101
Adam Langley884e6c42024-08-02 19:44:23102 bool IsHardwareBacked() const override { return true; }
103
Nina Satragno850d0342024-03-15 18:16:17104 private:
105 // The key's wrapped key as a binary string.
106 const std::string key_name_;
107 const scoped_refptr<RefCountedUnexportableSigningKey> key_;
108};
109
Ken Buchanan0111d4e22024-07-11 19:53:53110base::expected<std::unique_ptr<UserVerifyingSigningKey>,
111 UserVerifyingKeyCreationError>
112DoGenerateKey(base::span<const SignatureVerifier::SignatureAlgorithm>
113 acceptable_algorithms,
114 UnexportableKeyProvider::Config config,
115 LAContext* lacontext) {
Nina Satragnoc804f562024-03-27 16:36:46116 std::unique_ptr<UnexportableKeyProviderMac> key_provider =
117 GetUnexportableKeyProviderMac(std::move(config));
118 if (!key_provider) {
Ken Buchanan0111d4e22024-07-11 19:53:53119 return base::unexpected(UserVerifyingKeyCreationError::kPlatformApiError);
Nina Satragnoc804f562024-03-27 16:36:46120 }
121
122 std::unique_ptr<UnexportableSigningKey> key =
123 key_provider->GenerateSigningKeySlowly(acceptable_algorithms, lacontext);
124 if (!key) {
Ken Buchanan0111d4e22024-07-11 19:53:53125 return base::unexpected(UserVerifyingKeyCreationError::kPlatformApiError);
Nina Satragnoc804f562024-03-27 16:36:46126 }
Ken Buchanan0111d4e22024-07-11 19:53:53127 return base::ok(std::make_unique<UserVerifyingSigningKeyMac>(std::move(key)));
Nina Satragnoc804f562024-03-27 16:36:46128}
129
Ken Buchanan0111d4e22024-07-11 19:53:53130base::expected<std::unique_ptr<UserVerifyingSigningKey>,
131 UserVerifyingKeyCreationError>
132DoGetKey(std::vector<uint8_t> wrapped_key,
133 UnexportableKeyProvider::Config config,
134 LAContext* lacontext) {
Nina Satragnoc804f562024-03-27 16:36:46135 std::unique_ptr<UnexportableKeyProviderMac> key_provider =
136 GetUnexportableKeyProviderMac(std::move(config));
137 if (!key_provider) {
Ken Buchanan0111d4e22024-07-11 19:53:53138 return base::unexpected(UserVerifyingKeyCreationError::kPlatformApiError);
Nina Satragnoc804f562024-03-27 16:36:46139 }
140 std::unique_ptr<UnexportableSigningKey> key =
141 key_provider->FromWrappedSigningKeySlowly(wrapped_key, lacontext);
142 if (!key) {
Ken Buchanan0111d4e22024-07-11 19:53:53143 return base::unexpected(UserVerifyingKeyCreationError::kPlatformApiError);
Nina Satragnoc804f562024-03-27 16:36:46144 }
Ken Buchanan0111d4e22024-07-11 19:53:53145 return base::ok(std::make_unique<UserVerifyingSigningKeyMac>(std::move(key)));
Nina Satragnoc804f562024-03-27 16:36:46146}
147
148bool DoDeleteKey(std::vector<uint8_t> wrapped_key,
149 UnexportableKeyProvider::Config config) {
150 std::unique_ptr<UnexportableKeyProvider> key_provider =
151 GetUnexportableKeyProvider(std::move(config));
152 if (!key_provider) {
153 return false;
154 }
Ken Buchanane9f04b32024-04-26 19:50:06155 return key_provider->DeleteSigningKeySlowly(wrapped_key);
Nina Satragnoc804f562024-03-27 16:36:46156}
157
Nina Satragno850d0342024-03-15 18:16:17158class UserVerifyingKeyProviderMac : public UserVerifyingKeyProvider {
159 public:
160 explicit UserVerifyingKeyProviderMac(UserVerifyingKeyProvider::Config config)
Nina Satragnodae44922024-03-22 21:20:07161 : lacontext_(config.lacontext ? config.lacontext->release() : nil),
162 config_(std::move(config)) {}
Nina Satragno850d0342024-03-15 18:16:17163 ~UserVerifyingKeyProviderMac() override = default;
164
165 void GenerateUserVerifyingSigningKey(
166 base::span<const SignatureVerifier::SignatureAlgorithm>
167 acceptable_algorithms,
Ken Buchanan0111d4e22024-07-11 19:53:53168 UserVerifyingKeyCreationCallback callback) override {
Nina Satragnoc804f562024-03-27 16:36:46169 // Creating a key may result in disk access, so do it in a separate thread
170 // to avoid blocking the UI.
171 scoped_refptr<base::SequencedTaskRunner> worker_task_runner =
172 base::ThreadPool::CreateSequencedTaskRunner(
173 {base::MayBlock(), base::TaskPriority::USER_BLOCKING});
174 std::vector<SignatureVerifier::SignatureAlgorithm> algorithms(
175 acceptable_algorithms.begin(), acceptable_algorithms.end());
176 worker_task_runner->PostTaskAndReplyWithResult(
177 FROM_HERE,
178 base::BindOnce(&DoGenerateKey, std::move(algorithms),
179 MakeUnexportableKeyConfig(), lacontext_),
180 std::move(callback));
Nina Satragno850d0342024-03-15 18:16:17181 }
182
183 void GetUserVerifyingSigningKey(
184 UserVerifyingKeyLabel key_label,
Ken Buchanan0111d4e22024-07-11 19:53:53185 UserVerifyingKeyCreationCallback callback) override {
Nina Satragnoc804f562024-03-27 16:36:46186 // Retrieving a key may result in disk access, so do it in a separate thread
187 // to avoid blocking the UI.
188 scoped_refptr<base::SequencedTaskRunner> worker_task_runner =
189 base::ThreadPool::CreateSequencedTaskRunner(
190 {base::MayBlock(), base::TaskPriority::USER_BLOCKING});
Nina Satragno850d0342024-03-15 18:16:17191 std::vector<uint8_t> wrapped_key(key_label.begin(), key_label.end());
Nina Satragnoc804f562024-03-27 16:36:46192 worker_task_runner->PostTaskAndReplyWithResult(
193 FROM_HERE,
194 base::BindOnce(&DoGetKey, std::move(std::move(wrapped_key)),
195 MakeUnexportableKeyConfig(), lacontext_),
196 std::move(callback));
Nina Satragno850d0342024-03-15 18:16:17197 }
198
Nina Satragno9666abf2024-03-18 22:58:59199 void DeleteUserVerifyingKey(
200 UserVerifyingKeyLabel key_label,
201 base::OnceCallback<void(bool)> callback) override {
Nina Satragnoc804f562024-03-27 16:36:46202 // Deleting a key may result in disk access, so do it in a separate thread
203 // to avoid blocking the UI.
204 scoped_refptr<base::SequencedTaskRunner> worker_task_runner =
205 base::ThreadPool::CreateSequencedTaskRunner(
206 {base::MayBlock(), base::TaskPriority::USER_BLOCKING});
Nina Satragno9666abf2024-03-18 22:58:59207 std::vector<uint8_t> wrapped_key(key_label.begin(), key_label.end());
Nina Satragnoc804f562024-03-27 16:36:46208 worker_task_runner->PostTaskAndReplyWithResult(
209 FROM_HERE,
210 base::BindOnce(&DoDeleteKey, std::move(wrapped_key),
211 MakeUnexportableKeyConfig()),
212 std::move(callback));
Nina Satragno9666abf2024-03-18 22:58:59213 }
214
Nina Satragno850d0342024-03-15 18:16:17215 private:
216 UnexportableKeyProvider::Config MakeUnexportableKeyConfig() {
217 return {
218 .keychain_access_group = config_.keychain_access_group,
Adam Langleyc98c4812024-06-24 21:57:36219 .access_control =
220 UnexportableKeyProvider::Config::AccessControl::kUserPresence,
Nina Satragno850d0342024-03-15 18:16:17221 };
222 }
Nina Satragnodae44922024-03-22 21:20:07223 LAContext* __strong lacontext_;
Nina Satragno850d0342024-03-15 18:16:17224 const UserVerifyingKeyProvider::Config config_;
225};
226
227} // namespace
228
229std::unique_ptr<UserVerifyingKeyProvider> GetUserVerifyingKeyProviderMac(
230 UserVerifyingKeyProvider::Config config) {
231 return std::make_unique<UserVerifyingKeyProviderMac>(std::move(config));
232}
233
234void AreMacUnexportableKeysAvailable(UserVerifyingKeyProvider::Config config,
235 base::OnceCallback<void(bool)> callback) {
236 if (!GetUnexportableKeyProvider(
237 {.keychain_access_group = std::move(config.keychain_access_group)})) {
238 std::move(callback).Run(false);
239 return;
240 }
Nina Satragnoc7dc23a2024-03-20 17:19:22241 std::move(callback).Run(
Elly Fong-Jones38c29b62025-07-23 22:54:53242 crypto::apple::KeychainV2::GetInstance().LAContextCanEvaluatePolicy(
Nina Satragnoc7dc23a2024-03-20 17:19:22243 LAPolicyDeviceOwnerAuthentication, /*error=*/nil));
Nina Satragno850d0342024-03-15 18:16:17244}
245
246} // namespace crypto