xref: /aosp_15_r20/external/cronet/crypto/user_verifying_key_mac.mm (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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