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