• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "crypto/user_verifying_key.h"
6
7#import <LocalAuthentication/LocalAuthentication.h>
8
9#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"
19#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"
23#include "base/types/expected.h"
24#include "crypto/apple_keychain_v2.h"
25#include "crypto/scoped_lacontext.h"
26#include "crypto/unexportable_key.h"
27#include "crypto/unexportable_key_mac.h"
28
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|.
51base::expected<std::vector<uint8_t>, UserVerifyingKeySigningError> DoSign(
52    std::vector<uint8_t> data,
53    scoped_refptr<RefCountedUnexportableSigningKey> key) {
54  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);
59}
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,
77            UserVerifyingKeySignatureCallback callback) override {
78    // 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
102  bool IsHardwareBacked() const override  { return true; }
103
104 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
110base::expected<std::unique_ptr<UserVerifyingSigningKey>,
111               UserVerifyingKeyCreationError>
112DoGenerateKey(base::span<const SignatureVerifier::SignatureAlgorithm>
113                  acceptable_algorithms,
114              UnexportableKeyProvider::Config config,
115              LAContext* lacontext) {
116  std::unique_ptr<UnexportableKeyProviderMac> key_provider =
117      GetUnexportableKeyProviderMac(std::move(config));
118  if (!key_provider) {
119    return base::unexpected(UserVerifyingKeyCreationError::kPlatformApiError);
120  }
121
122  std::unique_ptr<UnexportableSigningKey> key =
123      key_provider->GenerateSigningKeySlowly(acceptable_algorithms, lacontext);
124  if (!key) {
125    return base::unexpected(UserVerifyingKeyCreationError::kPlatformApiError);
126  }
127  return base::ok(std::make_unique<UserVerifyingSigningKeyMac>(std::move(key)));
128}
129
130base::expected<std::unique_ptr<UserVerifyingSigningKey>,
131               UserVerifyingKeyCreationError>
132DoGetKey(std::vector<uint8_t> wrapped_key,
133         UnexportableKeyProvider::Config config,
134         LAContext* lacontext) {
135  std::unique_ptr<UnexportableKeyProviderMac> key_provider =
136      GetUnexportableKeyProviderMac(std::move(config));
137  if (!key_provider) {
138    return base::unexpected(UserVerifyingKeyCreationError::kPlatformApiError);
139  }
140  std::unique_ptr<UnexportableSigningKey> key =
141      key_provider->FromWrappedSigningKeySlowly(wrapped_key, lacontext);
142  if (!key) {
143    return base::unexpected(UserVerifyingKeyCreationError::kPlatformApiError);
144  }
145  return base::ok(std::make_unique<UserVerifyingSigningKeyMac>(std::move(key)));
146}
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  }
155  return key_provider->DeleteSigningKeySlowly(wrapped_key);
156}
157
158class UserVerifyingKeyProviderMac : public UserVerifyingKeyProvider {
159 public:
160  explicit UserVerifyingKeyProviderMac(UserVerifyingKeyProvider::Config config)
161      : lacontext_(config.lacontext ? config.lacontext->release() : nil),
162        config_(std::move(config)) {}
163  ~UserVerifyingKeyProviderMac() override = default;
164
165  void GenerateUserVerifyingSigningKey(
166      base::span<const SignatureVerifier::SignatureAlgorithm>
167          acceptable_algorithms,
168      UserVerifyingKeyCreationCallback callback) override {
169    // 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));
181  }
182
183  void GetUserVerifyingSigningKey(
184      UserVerifyingKeyLabel key_label,
185      UserVerifyingKeyCreationCallback callback) override {
186    // 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});
191    std::vector<uint8_t> wrapped_key(key_label.begin(), key_label.end());
192    worker_task_runner->PostTaskAndReplyWithResult(
193        FROM_HERE,
194        base::BindOnce(&DoGetKey, std::move(std::move(wrapped_key)),
195                       MakeUnexportableKeyConfig(), lacontext_),
196        std::move(callback));
197  }
198
199  void DeleteUserVerifyingKey(
200      UserVerifyingKeyLabel key_label,
201      base::OnceCallback<void(bool)> callback) override {
202    // 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});
207    std::vector<uint8_t> wrapped_key(key_label.begin(), key_label.end());
208    worker_task_runner->PostTaskAndReplyWithResult(
209        FROM_HERE,
210        base::BindOnce(&DoDeleteKey, std::move(wrapped_key),
211                       MakeUnexportableKeyConfig()),
212        std::move(callback));
213  }
214
215 private:
216  UnexportableKeyProvider::Config MakeUnexportableKeyConfig() {
217    return {
218        .keychain_access_group = config_.keychain_access_group,
219        .access_control =
220            UnexportableKeyProvider::Config::AccessControl::kUserPresence,
221    };
222  }
223  LAContext* __strong lacontext_;
224  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  }
241  std::move(callback).Run(
242      AppleKeychainV2::GetInstance().LAContextCanEvaluatePolicy(
243          LAPolicyDeviceOwnerAuthentication, /*error=*/nil));
244}
245
246}  // namespace crypto
247