xref: /aosp_15_r20/external/cronet/crypto/unexportable_key_mac.mm (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1*6777b538SAndroid Build Coastguard Worker// Copyright 2024 The Chromium Authors
2*6777b538SAndroid Build Coastguard Worker// Use of this source code is governed by a BSD-style license that can be
3*6777b538SAndroid Build Coastguard Worker// found in the LICENSE file.
4*6777b538SAndroid Build Coastguard Worker
5*6777b538SAndroid Build Coastguard Worker#include <algorithm>
6*6777b538SAndroid Build Coastguard Worker#include <iterator>
7*6777b538SAndroid Build Coastguard Worker#include <memory>
8*6777b538SAndroid Build Coastguard Worker#include <string>
9*6777b538SAndroid Build Coastguard Worker#include <utility>
10*6777b538SAndroid Build Coastguard Worker#include <vector>
11*6777b538SAndroid Build Coastguard Worker
12*6777b538SAndroid Build Coastguard Worker#import <CoreFoundation/CoreFoundation.h>
13*6777b538SAndroid Build Coastguard Worker#import <CryptoTokenKit/CryptoTokenKit.h>
14*6777b538SAndroid Build Coastguard Worker#import <Foundation/Foundation.h>
15*6777b538SAndroid Build Coastguard Worker#include <LocalAuthentication/LocalAuthentication.h>
16*6777b538SAndroid Build Coastguard Worker#import <Security/Security.h>
17*6777b538SAndroid Build Coastguard Worker
18*6777b538SAndroid Build Coastguard Worker#include "base/apple/bridging.h"
19*6777b538SAndroid Build Coastguard Worker#include "base/apple/foundation_util.h"
20*6777b538SAndroid Build Coastguard Worker#include "base/apple/scoped_cftyperef.h"
21*6777b538SAndroid Build Coastguard Worker#include "base/containers/contains.h"
22*6777b538SAndroid Build Coastguard Worker#include "base/containers/span.h"
23*6777b538SAndroid Build Coastguard Worker#include "base/feature_list.h"
24*6777b538SAndroid Build Coastguard Worker#include "base/logging.h"
25*6777b538SAndroid Build Coastguard Worker#include "base/memory/scoped_policy.h"
26*6777b538SAndroid Build Coastguard Worker#include "base/numerics/safe_conversions.h"
27*6777b538SAndroid Build Coastguard Worker#include "base/strings/sys_string_conversions.h"
28*6777b538SAndroid Build Coastguard Worker#include "crypto/apple_keychain_util.h"
29*6777b538SAndroid Build Coastguard Worker#include "crypto/apple_keychain_v2.h"
30*6777b538SAndroid Build Coastguard Worker#include "crypto/features.h"
31*6777b538SAndroid Build Coastguard Worker#include "crypto/signature_verifier.h"
32*6777b538SAndroid Build Coastguard Worker#include "crypto/unexportable_key.h"
33*6777b538SAndroid Build Coastguard Worker#include "crypto/unexportable_key_mac.h"
34*6777b538SAndroid Build Coastguard Worker#include "third_party/boringssl/src/include/openssl/bn.h"
35*6777b538SAndroid Build Coastguard Worker#include "third_party/boringssl/src/include/openssl/bytestring.h"
36*6777b538SAndroid Build Coastguard Worker#include "third_party/boringssl/src/include/openssl/ec.h"
37*6777b538SAndroid Build Coastguard Worker#include "third_party/boringssl/src/include/openssl/evp.h"
38*6777b538SAndroid Build Coastguard Worker#include "third_party/boringssl/src/include/openssl/mem.h"
39*6777b538SAndroid Build Coastguard Worker#include "third_party/boringssl/src/include/openssl/obj.h"
40*6777b538SAndroid Build Coastguard Worker
41*6777b538SAndroid Build Coastguard Workerusing base::apple::CFToNSPtrCast;
42*6777b538SAndroid Build Coastguard Workerusing base::apple::NSToCFPtrCast;
43*6777b538SAndroid Build Coastguard Worker
44*6777b538SAndroid Build Coastguard Workernamespace crypto {
45*6777b538SAndroid Build Coastguard Worker
46*6777b538SAndroid Build Coastguard Workernamespace {
47*6777b538SAndroid Build Coastguard Worker
48*6777b538SAndroid Build Coastguard Worker// The size of an uncompressed x9.63 encoded EC public key, 04 || X || Y.
49*6777b538SAndroid Build Coastguard Workerconstexpr size_t kUncompressedPointLength = 65;
50*6777b538SAndroid Build Coastguard Worker
51*6777b538SAndroid Build Coastguard Worker// The value of the kSecAttrLabel when generating the key. The documentation
52*6777b538SAndroid Build Coastguard Worker// claims this should be a user-visible label, but there does not exist any UI
53*6777b538SAndroid Build Coastguard Worker// that shows this value. Therefore, it is left untranslated.
54*6777b538SAndroid Build Coastguard Workerconstexpr char kAttrLabel[] = "Chromium unexportable key";
55*6777b538SAndroid Build Coastguard Worker
56*6777b538SAndroid Build Coastguard Worker// Returns a span of a CFDataRef.
57*6777b538SAndroid Build Coastguard Workerbase::span<const uint8_t> ToSpan(CFDataRef data) {
58*6777b538SAndroid Build Coastguard Worker  return base::make_span(CFDataGetBytePtr(data),
59*6777b538SAndroid Build Coastguard Worker                         base::checked_cast<size_t>(CFDataGetLength(data)));
60*6777b538SAndroid Build Coastguard Worker}
61*6777b538SAndroid Build Coastguard Worker
62*6777b538SAndroid Build Coastguard Worker// Copies a CFDataRef into a vector of bytes.
63*6777b538SAndroid Build Coastguard Workerstd::vector<uint8_t> CFDataToVec(CFDataRef data) {
64*6777b538SAndroid Build Coastguard Worker  base::span<const uint8_t> span = ToSpan(data);
65*6777b538SAndroid Build Coastguard Worker  return std::vector<uint8_t>(span.begin(), span.end());
66*6777b538SAndroid Build Coastguard Worker}
67*6777b538SAndroid Build Coastguard Worker
68*6777b538SAndroid Build Coastguard Workerstd::optional<std::vector<uint8_t>> Convertx963ToDerSpki(
69*6777b538SAndroid Build Coastguard Worker    base::span<const uint8_t> x962) {
70*6777b538SAndroid Build Coastguard Worker  // Parse x9.63 point into an |EC_POINT|.
71*6777b538SAndroid Build Coastguard Worker  bssl::UniquePtr<EC_GROUP> p256(
72*6777b538SAndroid Build Coastguard Worker      EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
73*6777b538SAndroid Build Coastguard Worker  bssl::UniquePtr<EC_POINT> point(EC_POINT_new(p256.get()));
74*6777b538SAndroid Build Coastguard Worker  if (x962.size() != kUncompressedPointLength ||
75*6777b538SAndroid Build Coastguard Worker      x962[0] != POINT_CONVERSION_UNCOMPRESSED ||
76*6777b538SAndroid Build Coastguard Worker      !EC_POINT_oct2point(p256.get(), point.get(), x962.data(), x962.size(),
77*6777b538SAndroid Build Coastguard Worker                          /*ctx=*/nullptr)) {
78*6777b538SAndroid Build Coastguard Worker    LOG(ERROR) << "P-256 public key is not on curve";
79*6777b538SAndroid Build Coastguard Worker    return std::nullopt;
80*6777b538SAndroid Build Coastguard Worker  }
81*6777b538SAndroid Build Coastguard Worker  // Marshal point into a DER SPKI.
82*6777b538SAndroid Build Coastguard Worker  bssl::UniquePtr<EC_KEY> ec_key(
83*6777b538SAndroid Build Coastguard Worker      EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
84*6777b538SAndroid Build Coastguard Worker  CHECK(EC_KEY_set_public_key(ec_key.get(), point.get()));
85*6777b538SAndroid Build Coastguard Worker  bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
86*6777b538SAndroid Build Coastguard Worker  CHECK(EVP_PKEY_assign_EC_KEY(pkey.get(), ec_key.release()));
87*6777b538SAndroid Build Coastguard Worker  bssl::ScopedCBB cbb;
88*6777b538SAndroid Build Coastguard Worker  uint8_t* der_bytes = nullptr;
89*6777b538SAndroid Build Coastguard Worker  size_t der_bytes_len = 0;
90*6777b538SAndroid Build Coastguard Worker  CHECK(CBB_init(cbb.get(), /* initial size */ 128) &&
91*6777b538SAndroid Build Coastguard Worker        EVP_marshal_public_key(cbb.get(), pkey.get()) &&
92*6777b538SAndroid Build Coastguard Worker        CBB_finish(cbb.get(), &der_bytes, &der_bytes_len));
93*6777b538SAndroid Build Coastguard Worker  std::vector<uint8_t> ret(der_bytes, der_bytes + der_bytes_len);
94*6777b538SAndroid Build Coastguard Worker  OPENSSL_free(der_bytes);
95*6777b538SAndroid Build Coastguard Worker  return ret;
96*6777b538SAndroid Build Coastguard Worker}
97*6777b538SAndroid Build Coastguard Worker
98*6777b538SAndroid Build Coastguard Worker// UnexportableSigningKeyMac is an implementation of the UnexportableSigningKey
99*6777b538SAndroid Build Coastguard Worker// interface on top of Apple's Secure Enclave.
100*6777b538SAndroid Build Coastguard Workerclass UnexportableSigningKeyMac : public UnexportableSigningKey {
101*6777b538SAndroid Build Coastguard Worker public:
102*6777b538SAndroid Build Coastguard Worker  UnexportableSigningKeyMac(base::apple::ScopedCFTypeRef<SecKeyRef> key,
103*6777b538SAndroid Build Coastguard Worker                            CFDictionaryRef key_attributes)
104*6777b538SAndroid Build Coastguard Worker      : key_(std::move(key)),
105*6777b538SAndroid Build Coastguard Worker        application_label_(
106*6777b538SAndroid Build Coastguard Worker            CFDataToVec(base::apple::GetValueFromDictionary<CFDataRef>(
107*6777b538SAndroid Build Coastguard Worker                key_attributes,
108*6777b538SAndroid Build Coastguard Worker                kSecAttrApplicationLabel))) {
109*6777b538SAndroid Build Coastguard Worker    base::apple::ScopedCFTypeRef<SecKeyRef> public_key(
110*6777b538SAndroid Build Coastguard Worker        AppleKeychainV2::GetInstance().KeyCopyPublicKey(key_.get()));
111*6777b538SAndroid Build Coastguard Worker    base::apple::ScopedCFTypeRef<CFDataRef> x962_bytes(
112*6777b538SAndroid Build Coastguard Worker        AppleKeychainV2::GetInstance().KeyCopyExternalRepresentation(
113*6777b538SAndroid Build Coastguard Worker            public_key.get(), /*error=*/nil));
114*6777b538SAndroid Build Coastguard Worker    CHECK(x962_bytes);
115*6777b538SAndroid Build Coastguard Worker    base::span<const uint8_t> x962_span = ToSpan(x962_bytes.get());
116*6777b538SAndroid Build Coastguard Worker    public_key_spki_ = *Convertx963ToDerSpki(x962_span);
117*6777b538SAndroid Build Coastguard Worker  }
118*6777b538SAndroid Build Coastguard Worker
119*6777b538SAndroid Build Coastguard Worker  ~UnexportableSigningKeyMac() override = default;
120*6777b538SAndroid Build Coastguard Worker
121*6777b538SAndroid Build Coastguard Worker  SignatureVerifier::SignatureAlgorithm Algorithm() const override {
122*6777b538SAndroid Build Coastguard Worker    return SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256;
123*6777b538SAndroid Build Coastguard Worker  }
124*6777b538SAndroid Build Coastguard Worker
125*6777b538SAndroid Build Coastguard Worker  std::vector<uint8_t> GetSubjectPublicKeyInfo() const override {
126*6777b538SAndroid Build Coastguard Worker    return public_key_spki_;
127*6777b538SAndroid Build Coastguard Worker  }
128*6777b538SAndroid Build Coastguard Worker
129*6777b538SAndroid Build Coastguard Worker  std::vector<uint8_t> GetWrappedKey() const override {
130*6777b538SAndroid Build Coastguard Worker    return application_label_;
131*6777b538SAndroid Build Coastguard Worker  }
132*6777b538SAndroid Build Coastguard Worker
133*6777b538SAndroid Build Coastguard Worker  std::optional<std::vector<uint8_t>> SignSlowly(
134*6777b538SAndroid Build Coastguard Worker      base::span<const uint8_t> data) override {
135*6777b538SAndroid Build Coastguard Worker    SecKeyAlgorithm algorithm = kSecKeyAlgorithmECDSASignatureMessageX962SHA256;
136*6777b538SAndroid Build Coastguard Worker    if (!SecKeyIsAlgorithmSupported(key_.get(), kSecKeyOperationTypeSign,
137*6777b538SAndroid Build Coastguard Worker                                    algorithm)) {
138*6777b538SAndroid Build Coastguard Worker      // This is not expected to happen, but it could happen if e.g. the key had
139*6777b538SAndroid Build Coastguard Worker      // been replaced by a key of a different type with the same label.
140*6777b538SAndroid Build Coastguard Worker      LOG(ERROR) << "Key does not support ECDSA algorithm";
141*6777b538SAndroid Build Coastguard Worker      return std::nullopt;
142*6777b538SAndroid Build Coastguard Worker    }
143*6777b538SAndroid Build Coastguard Worker
144*6777b538SAndroid Build Coastguard Worker    NSData* nsdata = [NSData dataWithBytes:data.data() length:data.size()];
145*6777b538SAndroid Build Coastguard Worker    base::apple::ScopedCFTypeRef<CFErrorRef> error;
146*6777b538SAndroid Build Coastguard Worker    base::apple::ScopedCFTypeRef<CFDataRef> signature(
147*6777b538SAndroid Build Coastguard Worker        AppleKeychainV2::GetInstance().KeyCreateSignature(
148*6777b538SAndroid Build Coastguard Worker            key_.get(), algorithm, NSToCFPtrCast(nsdata),
149*6777b538SAndroid Build Coastguard Worker            error.InitializeInto()));
150*6777b538SAndroid Build Coastguard Worker    if (!signature) {
151*6777b538SAndroid Build Coastguard Worker      LOG(ERROR) << "Error signing with key: " << error.get();
152*6777b538SAndroid Build Coastguard Worker      return std::nullopt;
153*6777b538SAndroid Build Coastguard Worker    }
154*6777b538SAndroid Build Coastguard Worker    return CFDataToVec(signature.get());
155*6777b538SAndroid Build Coastguard Worker  }
156*6777b538SAndroid Build Coastguard Worker
157*6777b538SAndroid Build Coastguard Worker  SecKeyRef GetSecKeyRef() const override { return key_.get(); }
158*6777b538SAndroid Build Coastguard Worker
159*6777b538SAndroid Build Coastguard Worker private:
160*6777b538SAndroid Build Coastguard Worker  // The wrapped key as returned by the Keychain API.
161*6777b538SAndroid Build Coastguard Worker  const base::apple::ScopedCFTypeRef<SecKeyRef> key_;
162*6777b538SAndroid Build Coastguard Worker
163*6777b538SAndroid Build Coastguard Worker  // The MacOS Keychain API sets the application label to the hash of the public
164*6777b538SAndroid Build Coastguard Worker  // key. We use this to uniquely identify the key in lieu of a wrapped private
165*6777b538SAndroid Build Coastguard Worker  // key.
166*6777b538SAndroid Build Coastguard Worker  const std::vector<uint8_t> application_label_;
167*6777b538SAndroid Build Coastguard Worker
168*6777b538SAndroid Build Coastguard Worker  // The public key in DER SPKI format.
169*6777b538SAndroid Build Coastguard Worker  std::vector<uint8_t> public_key_spki_;
170*6777b538SAndroid Build Coastguard Worker};
171*6777b538SAndroid Build Coastguard Worker
172*6777b538SAndroid Build Coastguard Worker}  // namespace
173*6777b538SAndroid Build Coastguard Worker
174*6777b538SAndroid Build Coastguard Workerstruct UnexportableKeyProviderMac::ObjCStorage {
175*6777b538SAndroid Build Coastguard Worker  NSString* __strong keychain_access_group_;
176*6777b538SAndroid Build Coastguard Worker  NSString* __strong application_tag_;
177*6777b538SAndroid Build Coastguard Worker};
178*6777b538SAndroid Build Coastguard Worker
179*6777b538SAndroid Build Coastguard WorkerUnexportableKeyProviderMac::UnexportableKeyProviderMac(Config config)
180*6777b538SAndroid Build Coastguard Worker    : access_control_(config.access_control),
181*6777b538SAndroid Build Coastguard Worker      objc_storage_(std::make_unique<ObjCStorage>()) {
182*6777b538SAndroid Build Coastguard Worker  objc_storage_->keychain_access_group_ =
183*6777b538SAndroid Build Coastguard Worker      base::SysUTF8ToNSString(std::move(config.keychain_access_group));
184*6777b538SAndroid Build Coastguard Worker  objc_storage_->application_tag_ =
185*6777b538SAndroid Build Coastguard Worker      base::SysUTF8ToNSString(std::move(config.application_tag));
186*6777b538SAndroid Build Coastguard Worker}
187*6777b538SAndroid Build Coastguard WorkerUnexportableKeyProviderMac::~UnexportableKeyProviderMac() = default;
188*6777b538SAndroid Build Coastguard Worker
189*6777b538SAndroid Build Coastguard Workerstd::optional<SignatureVerifier::SignatureAlgorithm>
190*6777b538SAndroid Build Coastguard WorkerUnexportableKeyProviderMac::SelectAlgorithm(
191*6777b538SAndroid Build Coastguard Worker    base::span<const SignatureVerifier::SignatureAlgorithm>
192*6777b538SAndroid Build Coastguard Worker        acceptable_algorithms) {
193*6777b538SAndroid Build Coastguard Worker  return base::Contains(acceptable_algorithms, SignatureVerifier::ECDSA_SHA256)
194*6777b538SAndroid Build Coastguard Worker             ? std::make_optional(SignatureVerifier::ECDSA_SHA256)
195*6777b538SAndroid Build Coastguard Worker             : std::nullopt;
196*6777b538SAndroid Build Coastguard Worker}
197*6777b538SAndroid Build Coastguard Worker
198*6777b538SAndroid Build Coastguard Workerstd::unique_ptr<UnexportableSigningKey>
199*6777b538SAndroid Build Coastguard WorkerUnexportableKeyProviderMac::GenerateSigningKeySlowly(
200*6777b538SAndroid Build Coastguard Worker    base::span<const SignatureVerifier::SignatureAlgorithm>
201*6777b538SAndroid Build Coastguard Worker        acceptable_algorithms) {
202*6777b538SAndroid Build Coastguard Worker  return GenerateSigningKeySlowly(acceptable_algorithms, /*lacontext=*/nil);
203*6777b538SAndroid Build Coastguard Worker}
204*6777b538SAndroid Build Coastguard Worker
205*6777b538SAndroid Build Coastguard Workerstd::unique_ptr<UnexportableSigningKey>
206*6777b538SAndroid Build Coastguard WorkerUnexportableKeyProviderMac::GenerateSigningKeySlowly(
207*6777b538SAndroid Build Coastguard Worker    base::span<const SignatureVerifier::SignatureAlgorithm>
208*6777b538SAndroid Build Coastguard Worker        acceptable_algorithms,
209*6777b538SAndroid Build Coastguard Worker    LAContext* lacontext) {
210*6777b538SAndroid Build Coastguard Worker  // The Secure Enclave only supports elliptic curve keys.
211*6777b538SAndroid Build Coastguard Worker  if (!SelectAlgorithm(acceptable_algorithms)) {
212*6777b538SAndroid Build Coastguard Worker    return nullptr;
213*6777b538SAndroid Build Coastguard Worker  }
214*6777b538SAndroid Build Coastguard Worker
215*6777b538SAndroid Build Coastguard Worker  // Generate the key pair.
216*6777b538SAndroid Build Coastguard Worker  SecAccessControlCreateFlags control_flags = kSecAccessControlPrivateKeyUsage;
217*6777b538SAndroid Build Coastguard Worker  switch (access_control_) {
218*6777b538SAndroid Build Coastguard Worker    case UnexportableKeyProvider::Config::AccessControl::kUserPresence:
219*6777b538SAndroid Build Coastguard Worker      control_flags |= kSecAccessControlUserPresence;
220*6777b538SAndroid Build Coastguard Worker      break;
221*6777b538SAndroid Build Coastguard Worker    case UnexportableKeyProvider::Config::AccessControl::kNone:
222*6777b538SAndroid Build Coastguard Worker      // No additional flag.
223*6777b538SAndroid Build Coastguard Worker      break;
224*6777b538SAndroid Build Coastguard Worker  }
225*6777b538SAndroid Build Coastguard Worker  base::apple::ScopedCFTypeRef<SecAccessControlRef> access(
226*6777b538SAndroid Build Coastguard Worker      SecAccessControlCreateWithFlags(
227*6777b538SAndroid Build Coastguard Worker          kCFAllocatorDefault, kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly,
228*6777b538SAndroid Build Coastguard Worker          control_flags,
229*6777b538SAndroid Build Coastguard Worker          /*error=*/nil));
230*6777b538SAndroid Build Coastguard Worker
231*6777b538SAndroid Build Coastguard Worker  NSMutableDictionary* key_attributes =
232*6777b538SAndroid Build Coastguard Worker      [NSMutableDictionary dictionaryWithDictionary:@{
233*6777b538SAndroid Build Coastguard Worker        CFToNSPtrCast(kSecAttrIsPermanent) : @YES,
234*6777b538SAndroid Build Coastguard Worker        CFToNSPtrCast(kSecAttrAccessControl) : (__bridge id)access.get(),
235*6777b538SAndroid Build Coastguard Worker      }];
236*6777b538SAndroid Build Coastguard Worker  if (lacontext) {
237*6777b538SAndroid Build Coastguard Worker    key_attributes[CFToNSPtrCast(kSecUseAuthenticationContext)] = lacontext;
238*6777b538SAndroid Build Coastguard Worker  }
239*6777b538SAndroid Build Coastguard Worker
240*6777b538SAndroid Build Coastguard Worker  NSDictionary* attributes = @{
241*6777b538SAndroid Build Coastguard Worker    CFToNSPtrCast(kSecUseDataProtectionKeychain) : @YES,
242*6777b538SAndroid Build Coastguard Worker    CFToNSPtrCast(kSecAttrKeyType) :
243*6777b538SAndroid Build Coastguard Worker        CFToNSPtrCast(kSecAttrKeyTypeECSECPrimeRandom),
244*6777b538SAndroid Build Coastguard Worker    CFToNSPtrCast(kSecAttrKeySizeInBits) : @256,
245*6777b538SAndroid Build Coastguard Worker    CFToNSPtrCast(kSecAttrTokenID) :
246*6777b538SAndroid Build Coastguard Worker        CFToNSPtrCast(kSecAttrTokenIDSecureEnclave),
247*6777b538SAndroid Build Coastguard Worker    CFToNSPtrCast(kSecPrivateKeyAttrs) : key_attributes,
248*6777b538SAndroid Build Coastguard Worker    CFToNSPtrCast(kSecAttrAccessGroup) : objc_storage_->keychain_access_group_,
249*6777b538SAndroid Build Coastguard Worker    CFToNSPtrCast(kSecAttrLabel) : base::SysUTF8ToNSString(kAttrLabel),
250*6777b538SAndroid Build Coastguard Worker    CFToNSPtrCast(kSecAttrApplicationTag) : objc_storage_->application_tag_,
251*6777b538SAndroid Build Coastguard Worker  };
252*6777b538SAndroid Build Coastguard Worker
253*6777b538SAndroid Build Coastguard Worker  base::apple::ScopedCFTypeRef<CFErrorRef> error;
254*6777b538SAndroid Build Coastguard Worker  base::apple::ScopedCFTypeRef<SecKeyRef> private_key(
255*6777b538SAndroid Build Coastguard Worker      AppleKeychainV2::GetInstance().KeyCreateRandomKey(
256*6777b538SAndroid Build Coastguard Worker          NSToCFPtrCast(attributes), error.InitializeInto()));
257*6777b538SAndroid Build Coastguard Worker  if (!private_key) {
258*6777b538SAndroid Build Coastguard Worker    LOG(ERROR) << "Could not create private key: " << error.get();
259*6777b538SAndroid Build Coastguard Worker    return nullptr;
260*6777b538SAndroid Build Coastguard Worker  }
261*6777b538SAndroid Build Coastguard Worker  base::apple::ScopedCFTypeRef<CFDictionaryRef> key_metadata =
262*6777b538SAndroid Build Coastguard Worker      AppleKeychainV2::GetInstance().KeyCopyAttributes(private_key.get());
263*6777b538SAndroid Build Coastguard Worker  return std::make_unique<UnexportableSigningKeyMac>(std::move(private_key),
264*6777b538SAndroid Build Coastguard Worker                                                     key_metadata.get());
265*6777b538SAndroid Build Coastguard Worker}
266*6777b538SAndroid Build Coastguard Worker
267*6777b538SAndroid Build Coastguard Workerstd::unique_ptr<UnexportableSigningKey>
268*6777b538SAndroid Build Coastguard WorkerUnexportableKeyProviderMac::FromWrappedSigningKeySlowly(
269*6777b538SAndroid Build Coastguard Worker    base::span<const uint8_t> wrapped_key) {
270*6777b538SAndroid Build Coastguard Worker  return FromWrappedSigningKeySlowly(wrapped_key, /*lacontext=*/nil);
271*6777b538SAndroid Build Coastguard Worker}
272*6777b538SAndroid Build Coastguard Worker
273*6777b538SAndroid Build Coastguard Workerstd::unique_ptr<UnexportableSigningKey>
274*6777b538SAndroid Build Coastguard WorkerUnexportableKeyProviderMac::FromWrappedSigningKeySlowly(
275*6777b538SAndroid Build Coastguard Worker    base::span<const uint8_t> wrapped_key,
276*6777b538SAndroid Build Coastguard Worker    LAContext* lacontext) {
277*6777b538SAndroid Build Coastguard Worker  base::apple::ScopedCFTypeRef<CFTypeRef> key_data;
278*6777b538SAndroid Build Coastguard Worker
279*6777b538SAndroid Build Coastguard Worker  NSMutableDictionary* query = [NSMutableDictionary dictionaryWithDictionary:@{
280*6777b538SAndroid Build Coastguard Worker    CFToNSPtrCast(kSecClass) : CFToNSPtrCast(kSecClassKey),
281*6777b538SAndroid Build Coastguard Worker    CFToNSPtrCast(kSecAttrKeyType) :
282*6777b538SAndroid Build Coastguard Worker        CFToNSPtrCast(kSecAttrKeyTypeECSECPrimeRandom),
283*6777b538SAndroid Build Coastguard Worker    CFToNSPtrCast(kSecReturnRef) : @YES,
284*6777b538SAndroid Build Coastguard Worker    CFToNSPtrCast(kSecReturnAttributes) : @YES,
285*6777b538SAndroid Build Coastguard Worker    CFToNSPtrCast(kSecAttrAccessGroup) : objc_storage_->keychain_access_group_,
286*6777b538SAndroid Build Coastguard Worker    CFToNSPtrCast(kSecAttrApplicationLabel) :
287*6777b538SAndroid Build Coastguard Worker        [NSData dataWithBytes:wrapped_key.data() length:wrapped_key.size()],
288*6777b538SAndroid Build Coastguard Worker  }];
289*6777b538SAndroid Build Coastguard Worker  if (lacontext) {
290*6777b538SAndroid Build Coastguard Worker    query[CFToNSPtrCast(kSecUseAuthenticationContext)] = lacontext;
291*6777b538SAndroid Build Coastguard Worker  }
292*6777b538SAndroid Build Coastguard Worker  AppleKeychainV2::GetInstance().ItemCopyMatching(NSToCFPtrCast(query),
293*6777b538SAndroid Build Coastguard Worker                                                  key_data.InitializeInto());
294*6777b538SAndroid Build Coastguard Worker  CFDictionaryRef key_attributes =
295*6777b538SAndroid Build Coastguard Worker      base::apple::CFCast<CFDictionaryRef>(key_data.get());
296*6777b538SAndroid Build Coastguard Worker  if (!key_attributes) {
297*6777b538SAndroid Build Coastguard Worker    return nullptr;
298*6777b538SAndroid Build Coastguard Worker  }
299*6777b538SAndroid Build Coastguard Worker  base::apple::ScopedCFTypeRef<SecKeyRef> key(
300*6777b538SAndroid Build Coastguard Worker      base::apple::GetValueFromDictionary<SecKeyRef>(key_attributes,
301*6777b538SAndroid Build Coastguard Worker                                                     kSecValueRef),
302*6777b538SAndroid Build Coastguard Worker      base::scoped_policy::RETAIN);
303*6777b538SAndroid Build Coastguard Worker  return std::make_unique<UnexportableSigningKeyMac>(std::move(key),
304*6777b538SAndroid Build Coastguard Worker                                                     key_attributes);
305*6777b538SAndroid Build Coastguard Worker}
306*6777b538SAndroid Build Coastguard Worker
307*6777b538SAndroid Build Coastguard Workerbool UnexportableKeyProviderMac::DeleteSigningKey(
308*6777b538SAndroid Build Coastguard Worker    base::span<const uint8_t> wrapped_key) {
309*6777b538SAndroid Build Coastguard Worker  NSDictionary* query = @{
310*6777b538SAndroid Build Coastguard Worker    CFToNSPtrCast(kSecClass) : CFToNSPtrCast(kSecClassKey),
311*6777b538SAndroid Build Coastguard Worker    CFToNSPtrCast(kSecAttrKeyType) :
312*6777b538SAndroid Build Coastguard Worker        CFToNSPtrCast(kSecAttrKeyTypeECSECPrimeRandom),
313*6777b538SAndroid Build Coastguard Worker    CFToNSPtrCast(kSecAttrAccessGroup) : objc_storage_->keychain_access_group_,
314*6777b538SAndroid Build Coastguard Worker    CFToNSPtrCast(kSecAttrApplicationLabel) :
315*6777b538SAndroid Build Coastguard Worker        [NSData dataWithBytes:wrapped_key.data() length:wrapped_key.size()],
316*6777b538SAndroid Build Coastguard Worker  };
317*6777b538SAndroid Build Coastguard Worker  OSStatus result =
318*6777b538SAndroid Build Coastguard Worker      AppleKeychainV2::GetInstance().ItemDelete(NSToCFPtrCast(query));
319*6777b538SAndroid Build Coastguard Worker  return result == errSecSuccess;
320*6777b538SAndroid Build Coastguard Worker}
321*6777b538SAndroid Build Coastguard Worker
322*6777b538SAndroid Build Coastguard Workerstd::unique_ptr<UnexportableKeyProviderMac> GetUnexportableKeyProviderMac(
323*6777b538SAndroid Build Coastguard Worker    UnexportableKeyProvider::Config config) {
324*6777b538SAndroid Build Coastguard Worker  if (!base::FeatureList::IsEnabled(crypto::kEnableMacUnexportableKeys)) {
325*6777b538SAndroid Build Coastguard Worker    return nullptr;
326*6777b538SAndroid Build Coastguard Worker  }
327*6777b538SAndroid Build Coastguard Worker  CHECK(!config.keychain_access_group.empty())
328*6777b538SAndroid Build Coastguard Worker      << "A keychain access group must be set when using unexportable keys on "
329*6777b538SAndroid Build Coastguard Worker         "macOS";
330*6777b538SAndroid Build Coastguard Worker  if (![AppleKeychainV2::GetInstance().GetTokenIDs()
331*6777b538SAndroid Build Coastguard Worker          containsObject:CFToNSPtrCast(kSecAttrTokenIDSecureEnclave)]) {
332*6777b538SAndroid Build Coastguard Worker    return nullptr;
333*6777b538SAndroid Build Coastguard Worker  }
334*6777b538SAndroid Build Coastguard Worker  // Inspecting the binary for the entitlement is not available on iOS, assume
335*6777b538SAndroid Build Coastguard Worker  // it is available.
336*6777b538SAndroid Build Coastguard Worker#if !BUILDFLAG(IS_IOS)
337*6777b538SAndroid Build Coastguard Worker  if (!ExecutableHasKeychainAccessGroupEntitlement(
338*6777b538SAndroid Build Coastguard Worker          config.keychain_access_group)) {
339*6777b538SAndroid Build Coastguard Worker    LOG(ERROR) << "Unexportable keys unavailable because keychain-access-group "
340*6777b538SAndroid Build Coastguard Worker                  "entitlement missing or incorrect. Expected value: "
341*6777b538SAndroid Build Coastguard Worker               << config.keychain_access_group;
342*6777b538SAndroid Build Coastguard Worker    return nullptr;
343*6777b538SAndroid Build Coastguard Worker  }
344*6777b538SAndroid Build Coastguard Worker#endif  // !BUILDFLAG(IS_IOS)
345*6777b538SAndroid Build Coastguard Worker  return std::make_unique<UnexportableKeyProviderMac>(std::move(config));
346*6777b538SAndroid Build Coastguard Worker}
347*6777b538SAndroid Build Coastguard Worker
348*6777b538SAndroid Build Coastguard Worker}  // namespace crypto
349