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 "crypto/fake_apple_keychain_v2.h" 6*6777b538SAndroid Build Coastguard Worker 7*6777b538SAndroid Build Coastguard Worker#include <vector> 8*6777b538SAndroid Build Coastguard Worker 9*6777b538SAndroid Build Coastguard Worker#if defined(LEAK_SANITIZER) 10*6777b538SAndroid Build Coastguard Worker#include <sanitizer/lsan_interface.h> 11*6777b538SAndroid Build Coastguard Worker#endif 12*6777b538SAndroid Build Coastguard Worker 13*6777b538SAndroid Build Coastguard Worker#import <CoreFoundation/CoreFoundation.h> 14*6777b538SAndroid Build Coastguard Worker#import <Foundation/Foundation.h> 15*6777b538SAndroid Build Coastguard Worker#import <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/check_op.h" 22*6777b538SAndroid Build Coastguard Worker#include "base/notimplemented.h" 23*6777b538SAndroid Build Coastguard Worker#include "base/notreached.h" 24*6777b538SAndroid Build Coastguard Worker#include "base/strings/sys_string_conversions.h" 25*6777b538SAndroid Build Coastguard Worker#include "crypto/apple_keychain_v2.h" 26*6777b538SAndroid Build Coastguard Worker 27*6777b538SAndroid Build Coastguard Workernamespace crypto { 28*6777b538SAndroid Build Coastguard Worker 29*6777b538SAndroid Build Coastguard WorkerFakeAppleKeychainV2::FakeAppleKeychainV2( 30*6777b538SAndroid Build Coastguard Worker const std::string& keychain_access_group) 31*6777b538SAndroid Build Coastguard Worker : keychain_access_group_( 32*6777b538SAndroid Build Coastguard Worker base::SysUTF8ToCFStringRef(keychain_access_group)) {} 33*6777b538SAndroid Build Coastguard WorkerFakeAppleKeychainV2::~FakeAppleKeychainV2() { 34*6777b538SAndroid Build Coastguard Worker // Avoid shutdown leak of error string in Security.framework. 35*6777b538SAndroid Build Coastguard Worker // See 36*6777b538SAndroid Build Coastguard Worker // https://github.com/apple-oss-distributions/Security/blob/Security-60158.140.3/OSX/libsecurity_keychain/lib/SecBase.cpp#L88 37*6777b538SAndroid Build Coastguard Worker#if defined(LEAK_SANITIZER) 38*6777b538SAndroid Build Coastguard Worker __lsan_do_leak_check(); 39*6777b538SAndroid Build Coastguard Worker#endif 40*6777b538SAndroid Build Coastguard Worker} 41*6777b538SAndroid Build Coastguard Worker 42*6777b538SAndroid Build Coastguard WorkerNSArray* FakeAppleKeychainV2::GetTokenIDs() { 43*6777b538SAndroid Build Coastguard Worker if (is_secure_enclave_available_) { 44*6777b538SAndroid Build Coastguard Worker return @[ base::apple::CFToNSPtrCast(kSecAttrTokenIDSecureEnclave) ]; 45*6777b538SAndroid Build Coastguard Worker } 46*6777b538SAndroid Build Coastguard Worker return @[]; 47*6777b538SAndroid Build Coastguard Worker} 48*6777b538SAndroid Build Coastguard Worker 49*6777b538SAndroid Build Coastguard Workerbase::apple::ScopedCFTypeRef<SecKeyRef> FakeAppleKeychainV2::KeyCreateRandomKey( 50*6777b538SAndroid Build Coastguard Worker CFDictionaryRef params, 51*6777b538SAndroid Build Coastguard Worker CFErrorRef* error) { 52*6777b538SAndroid Build Coastguard Worker // Validate certain fields that we always expect to be set. 53*6777b538SAndroid Build Coastguard Worker DCHECK( 54*6777b538SAndroid Build Coastguard Worker base::apple::GetValueFromDictionary<CFStringRef>(params, kSecAttrLabel)); 55*6777b538SAndroid Build Coastguard Worker // kSecAttrApplicationTag is CFDataRef for new credentials and CFStringRef for 56*6777b538SAndroid Build Coastguard Worker // version < 3. Keychain docs say it should be CFDataRef 57*6777b538SAndroid Build Coastguard Worker // (https://developer.apple.com/documentation/security/ksecattrapplicationtag). 58*6777b538SAndroid Build Coastguard Worker CFTypeRef application_tag = nil; 59*6777b538SAndroid Build Coastguard Worker CFDictionaryGetValueIfPresent(params, kSecAttrApplicationTag, 60*6777b538SAndroid Build Coastguard Worker &application_tag); 61*6777b538SAndroid Build Coastguard Worker if (application_tag) { 62*6777b538SAndroid Build Coastguard Worker CHECK(base::apple::CFCast<CFDataRef>(application_tag) || 63*6777b538SAndroid Build Coastguard Worker base::apple::CFCast<CFStringRef>(application_tag)); 64*6777b538SAndroid Build Coastguard Worker } 65*6777b538SAndroid Build Coastguard Worker DCHECK_EQ( 66*6777b538SAndroid Build Coastguard Worker base::apple::GetValueFromDictionary<CFStringRef>(params, kSecAttrTokenID), 67*6777b538SAndroid Build Coastguard Worker kSecAttrTokenIDSecureEnclave); 68*6777b538SAndroid Build Coastguard Worker DCHECK(CFEqual(base::apple::GetValueFromDictionary<CFStringRef>( 69*6777b538SAndroid Build Coastguard Worker params, kSecAttrAccessGroup), 70*6777b538SAndroid Build Coastguard Worker keychain_access_group_.get())); 71*6777b538SAndroid Build Coastguard Worker 72*6777b538SAndroid Build Coastguard Worker // Call Keychain services to create a key pair, but first drop all parameters 73*6777b538SAndroid Build Coastguard Worker // that aren't appropriate in tests. 74*6777b538SAndroid Build Coastguard Worker base::apple::ScopedCFTypeRef<CFMutableDictionaryRef> params_copy( 75*6777b538SAndroid Build Coastguard Worker CFDictionaryCreateMutableCopy(kCFAllocatorDefault, /*capacity=*/0, 76*6777b538SAndroid Build Coastguard Worker params)); 77*6777b538SAndroid Build Coastguard Worker // Don't create a Secure Enclave key. 78*6777b538SAndroid Build Coastguard Worker CFDictionaryRemoveValue(params_copy.get(), kSecAttrTokenID); 79*6777b538SAndroid Build Coastguard Worker // Don't bind to a keychain-access-group, which would require an entitlement. 80*6777b538SAndroid Build Coastguard Worker CFDictionaryRemoveValue(params_copy.get(), kSecAttrAccessGroup); 81*6777b538SAndroid Build Coastguard Worker 82*6777b538SAndroid Build Coastguard Worker base::apple::ScopedCFTypeRef<CFMutableDictionaryRef> private_key_params( 83*6777b538SAndroid Build Coastguard Worker CFDictionaryCreateMutableCopy( 84*6777b538SAndroid Build Coastguard Worker kCFAllocatorDefault, /*capacity=*/0, 85*6777b538SAndroid Build Coastguard Worker base::apple::GetValueFromDictionary<CFDictionaryRef>( 86*6777b538SAndroid Build Coastguard Worker params_copy.get(), kSecPrivateKeyAttrs))); 87*6777b538SAndroid Build Coastguard Worker DCHECK(CFEqual(base::apple::GetValueFromDictionary<CFBooleanRef>( 88*6777b538SAndroid Build Coastguard Worker private_key_params.get(), kSecAttrIsPermanent), 89*6777b538SAndroid Build Coastguard Worker kCFBooleanTrue)); 90*6777b538SAndroid Build Coastguard Worker CFDictionarySetValue(private_key_params.get(), kSecAttrIsPermanent, 91*6777b538SAndroid Build Coastguard Worker kCFBooleanFalse); 92*6777b538SAndroid Build Coastguard Worker CFDictionaryRemoveValue(private_key_params.get(), kSecAttrAccessControl); 93*6777b538SAndroid Build Coastguard Worker CFDictionaryRemoveValue(private_key_params.get(), 94*6777b538SAndroid Build Coastguard Worker kSecUseAuthenticationContext); 95*6777b538SAndroid Build Coastguard Worker CFDictionarySetValue(params_copy.get(), kSecPrivateKeyAttrs, 96*6777b538SAndroid Build Coastguard Worker private_key_params.get()); 97*6777b538SAndroid Build Coastguard Worker base::apple::ScopedCFTypeRef<SecKeyRef> private_key( 98*6777b538SAndroid Build Coastguard Worker SecKeyCreateRandomKey(params_copy.get(), error)); 99*6777b538SAndroid Build Coastguard Worker if (!private_key) { 100*6777b538SAndroid Build Coastguard Worker return base::apple::ScopedCFTypeRef<SecKeyRef>(); 101*6777b538SAndroid Build Coastguard Worker } 102*6777b538SAndroid Build Coastguard Worker 103*6777b538SAndroid Build Coastguard Worker // Stash everything in `items_` so it can be retrieved in with 104*6777b538SAndroid Build Coastguard Worker // `ItemCopyMatching. This uses the original `params` rather than the modified 105*6777b538SAndroid Build Coastguard Worker // copy so that `ItemCopyMatching()` will correctly filter on 106*6777b538SAndroid Build Coastguard Worker // kSecAttrAccessGroup. 107*6777b538SAndroid Build Coastguard Worker base::apple::ScopedCFTypeRef<CFMutableDictionaryRef> keychain_item( 108*6777b538SAndroid Build Coastguard Worker CFDictionaryCreateMutableCopy(kCFAllocatorDefault, /*capacity=*/0, 109*6777b538SAndroid Build Coastguard Worker params)); 110*6777b538SAndroid Build Coastguard Worker CFDictionarySetValue(keychain_item.get(), kSecValueRef, private_key.get()); 111*6777b538SAndroid Build Coastguard Worker 112*6777b538SAndroid Build Coastguard Worker // When left unset, the real keychain sets the application label to the hash 113*6777b538SAndroid Build Coastguard Worker // of the public key on creation. We need to retrieve it to allow filtering 114*6777b538SAndroid Build Coastguard Worker // for it later. 115*6777b538SAndroid Build Coastguard Worker if (!base::apple::GetValueFromDictionary<CFDataRef>( 116*6777b538SAndroid Build Coastguard Worker keychain_item.get(), kSecAttrApplicationLabel)) { 117*6777b538SAndroid Build Coastguard Worker base::apple::ScopedCFTypeRef<CFDictionaryRef> key_metadata( 118*6777b538SAndroid Build Coastguard Worker SecKeyCopyAttributes(private_key.get())); 119*6777b538SAndroid Build Coastguard Worker CFDataRef application_label = 120*6777b538SAndroid Build Coastguard Worker base::apple::GetValueFromDictionary<CFDataRef>( 121*6777b538SAndroid Build Coastguard Worker key_metadata.get(), kSecAttrApplicationLabel); 122*6777b538SAndroid Build Coastguard Worker CFDictionarySetValue(keychain_item.get(), kSecAttrApplicationLabel, 123*6777b538SAndroid Build Coastguard Worker application_label); 124*6777b538SAndroid Build Coastguard Worker } 125*6777b538SAndroid Build Coastguard Worker items_.push_back(keychain_item); 126*6777b538SAndroid Build Coastguard Worker 127*6777b538SAndroid Build Coastguard Worker return private_key; 128*6777b538SAndroid Build Coastguard Worker} 129*6777b538SAndroid Build Coastguard Worker 130*6777b538SAndroid Build Coastguard Workerbase::apple::ScopedCFTypeRef<CFDictionaryRef> 131*6777b538SAndroid Build Coastguard WorkerFakeAppleKeychainV2::KeyCopyAttributes(SecKeyRef key) { 132*6777b538SAndroid Build Coastguard Worker const auto& it = std::ranges::find_if(items_, [&key](const auto& item) { 133*6777b538SAndroid Build Coastguard Worker return CFEqual(key, CFDictionaryGetValue(item.get(), kSecValueRef)); 134*6777b538SAndroid Build Coastguard Worker }); 135*6777b538SAndroid Build Coastguard Worker if (it == items_.end()) { 136*6777b538SAndroid Build Coastguard Worker return base::apple::ScopedCFTypeRef<CFDictionaryRef>(); 137*6777b538SAndroid Build Coastguard Worker } 138*6777b538SAndroid Build Coastguard Worker base::apple::ScopedCFTypeRef<CFMutableDictionaryRef> result( 139*6777b538SAndroid Build Coastguard Worker CFDictionaryCreateMutableCopy(kCFAllocatorDefault, /*capacity=*/0, 140*6777b538SAndroid Build Coastguard Worker it->get())); 141*6777b538SAndroid Build Coastguard Worker // The real implementation does not return the actual key. 142*6777b538SAndroid Build Coastguard Worker CFDictionaryRemoveValue(result.get(), kSecValueRef); 143*6777b538SAndroid Build Coastguard Worker return result; 144*6777b538SAndroid Build Coastguard Worker} 145*6777b538SAndroid Build Coastguard Worker 146*6777b538SAndroid Build Coastguard WorkerOSStatus FakeAppleKeychainV2::ItemCopyMatching(CFDictionaryRef query, 147*6777b538SAndroid Build Coastguard Worker CFTypeRef* result) { 148*6777b538SAndroid Build Coastguard Worker // In practice we don't need to care about limit queries, or leaving out the 149*6777b538SAndroid Build Coastguard Worker // SecKeyRef or attributes from the result set. 150*6777b538SAndroid Build Coastguard Worker DCHECK_EQ( 151*6777b538SAndroid Build Coastguard Worker base::apple::GetValueFromDictionary<CFBooleanRef>(query, kSecReturnRef), 152*6777b538SAndroid Build Coastguard Worker kCFBooleanTrue); 153*6777b538SAndroid Build Coastguard Worker DCHECK_EQ(base::apple::GetValueFromDictionary<CFBooleanRef>( 154*6777b538SAndroid Build Coastguard Worker query, kSecReturnAttributes), 155*6777b538SAndroid Build Coastguard Worker kCFBooleanTrue); 156*6777b538SAndroid Build Coastguard Worker CFStringRef match_limit = 157*6777b538SAndroid Build Coastguard Worker base::apple::GetValueFromDictionary<CFStringRef>(query, kSecMatchLimit); 158*6777b538SAndroid Build Coastguard Worker bool match_all = match_limit && CFEqual(match_limit, kSecMatchLimitAll); 159*6777b538SAndroid Build Coastguard Worker 160*6777b538SAndroid Build Coastguard Worker // Match fields present in `query`. 161*6777b538SAndroid Build Coastguard Worker CFStringRef query_label = 162*6777b538SAndroid Build Coastguard Worker base::apple::GetValueFromDictionary<CFStringRef>(query, kSecAttrLabel); 163*6777b538SAndroid Build Coastguard Worker CFDataRef query_application_label = 164*6777b538SAndroid Build Coastguard Worker base::apple::GetValueFromDictionary<CFDataRef>(query, 165*6777b538SAndroid Build Coastguard Worker kSecAttrApplicationLabel); 166*6777b538SAndroid Build Coastguard Worker // kSecAttrApplicationTag can be CFStringRef for legacy credentials and 167*6777b538SAndroid Build Coastguard Worker // CFDataRef for new ones, hence using CFTypeRef. 168*6777b538SAndroid Build Coastguard Worker CFTypeRef query_application_tag = 169*6777b538SAndroid Build Coastguard Worker CFDictionaryGetValue(query, kSecAttrApplicationTag); 170*6777b538SAndroid Build Coastguard Worker 171*6777b538SAndroid Build Coastguard Worker // Filter the items based on `query`. 172*6777b538SAndroid Build Coastguard Worker base::apple::ScopedCFTypeRef<CFMutableArrayRef> items( 173*6777b538SAndroid Build Coastguard Worker CFArrayCreateMutable(nullptr, items_.size(), &kCFTypeArrayCallBacks)); 174*6777b538SAndroid Build Coastguard Worker for (auto& item : items_) { 175*6777b538SAndroid Build Coastguard Worker // Each `Keychain` instance is expected to operate only on items of a single 176*6777b538SAndroid Build Coastguard Worker // keychain-access-group, which is tied to the `Profile`. 177*6777b538SAndroid Build Coastguard Worker CFStringRef keychain_access_group = 178*6777b538SAndroid Build Coastguard Worker base::apple::GetValueFromDictionary<CFStringRef>(query, 179*6777b538SAndroid Build Coastguard Worker kSecAttrAccessGroup); 180*6777b538SAndroid Build Coastguard Worker DCHECK(CFEqual(keychain_access_group, 181*6777b538SAndroid Build Coastguard Worker base::apple::GetValueFromDictionary<CFStringRef>( 182*6777b538SAndroid Build Coastguard Worker item.get(), kSecAttrAccessGroup)) && 183*6777b538SAndroid Build Coastguard Worker CFEqual(keychain_access_group, keychain_access_group_.get())); 184*6777b538SAndroid Build Coastguard Worker 185*6777b538SAndroid Build Coastguard Worker CFStringRef item_label = base::apple::GetValueFromDictionary<CFStringRef>( 186*6777b538SAndroid Build Coastguard Worker item.get(), kSecAttrLabel); 187*6777b538SAndroid Build Coastguard Worker CFDataRef item_application_label = 188*6777b538SAndroid Build Coastguard Worker base::apple::GetValueFromDictionary<CFDataRef>( 189*6777b538SAndroid Build Coastguard Worker item.get(), kSecAttrApplicationLabel); 190*6777b538SAndroid Build Coastguard Worker CFTypeRef item_application_tag = 191*6777b538SAndroid Build Coastguard Worker CFDictionaryGetValue(item.get(), kSecAttrApplicationTag); 192*6777b538SAndroid Build Coastguard Worker if ((query_label && (!item_label || !CFEqual(query_label, item_label))) || 193*6777b538SAndroid Build Coastguard Worker (query_application_label && 194*6777b538SAndroid Build Coastguard Worker (!item_application_label || 195*6777b538SAndroid Build Coastguard Worker !CFEqual(query_application_label, item_application_label))) || 196*6777b538SAndroid Build Coastguard Worker (query_application_tag && 197*6777b538SAndroid Build Coastguard Worker (!item_application_tag || 198*6777b538SAndroid Build Coastguard Worker !CFEqual(query_application_tag, item_application_tag)))) { 199*6777b538SAndroid Build Coastguard Worker continue; 200*6777b538SAndroid Build Coastguard Worker } 201*6777b538SAndroid Build Coastguard Worker if (match_all) { 202*6777b538SAndroid Build Coastguard Worker base::apple::ScopedCFTypeRef<CFDictionaryRef> item_copy( 203*6777b538SAndroid Build Coastguard Worker CFDictionaryCreateCopy(kCFAllocatorDefault, item.get())); 204*6777b538SAndroid Build Coastguard Worker CFArrayAppendValue(items.get(), item_copy.get()); 205*6777b538SAndroid Build Coastguard Worker } else { 206*6777b538SAndroid Build Coastguard Worker *result = CFDictionaryCreateCopy(kCFAllocatorDefault, item.get()); 207*6777b538SAndroid Build Coastguard Worker return errSecSuccess; 208*6777b538SAndroid Build Coastguard Worker } 209*6777b538SAndroid Build Coastguard Worker } 210*6777b538SAndroid Build Coastguard Worker if (CFArrayGetCount(items.get()) == 0) { 211*6777b538SAndroid Build Coastguard Worker return errSecItemNotFound; 212*6777b538SAndroid Build Coastguard Worker } 213*6777b538SAndroid Build Coastguard Worker *result = items.release(); 214*6777b538SAndroid Build Coastguard Worker return errSecSuccess; 215*6777b538SAndroid Build Coastguard Worker} 216*6777b538SAndroid Build Coastguard Worker 217*6777b538SAndroid Build Coastguard WorkerOSStatus FakeAppleKeychainV2::ItemDelete(CFDictionaryRef query) { 218*6777b538SAndroid Build Coastguard Worker // Validate certain fields that we always expect to be set. 219*6777b538SAndroid Build Coastguard Worker DCHECK_EQ(base::apple::GetValueFromDictionary<CFStringRef>(query, kSecClass), 220*6777b538SAndroid Build Coastguard Worker kSecClassKey); 221*6777b538SAndroid Build Coastguard Worker DCHECK(CFEqual(base::apple::GetValueFromDictionary<CFStringRef>( 222*6777b538SAndroid Build Coastguard Worker query, kSecAttrAccessGroup), 223*6777b538SAndroid Build Coastguard Worker keychain_access_group_.get())); 224*6777b538SAndroid Build Coastguard Worker // Only supporting deletion via `kSecAttrApplicationLabel` (credential ID) for 225*6777b538SAndroid Build Coastguard Worker // now (see `TouchIdCredentialStore::DeleteCredentialById()`). 226*6777b538SAndroid Build Coastguard Worker CFDataRef query_credential_id = 227*6777b538SAndroid Build Coastguard Worker base::apple::GetValueFromDictionary<CFDataRef>(query, 228*6777b538SAndroid Build Coastguard Worker kSecAttrApplicationLabel); 229*6777b538SAndroid Build Coastguard Worker DCHECK(query_credential_id); 230*6777b538SAndroid Build Coastguard Worker for (auto it = items_.begin(); it != items_.end(); ++it) { 231*6777b538SAndroid Build Coastguard Worker const base::apple::ScopedCFTypeRef<CFDictionaryRef>& item = *it; 232*6777b538SAndroid Build Coastguard Worker CFDataRef item_credential_id = 233*6777b538SAndroid Build Coastguard Worker base::apple::GetValueFromDictionary<CFDataRef>( 234*6777b538SAndroid Build Coastguard Worker item.get(), kSecAttrApplicationLabel); 235*6777b538SAndroid Build Coastguard Worker DCHECK(item_credential_id); 236*6777b538SAndroid Build Coastguard Worker if (CFEqual(query_credential_id, item_credential_id)) { 237*6777b538SAndroid Build Coastguard Worker items_.erase(it); // N.B. `it` becomes invalid 238*6777b538SAndroid Build Coastguard Worker return errSecSuccess; 239*6777b538SAndroid Build Coastguard Worker } 240*6777b538SAndroid Build Coastguard Worker } 241*6777b538SAndroid Build Coastguard Worker return errSecItemNotFound; 242*6777b538SAndroid Build Coastguard Worker} 243*6777b538SAndroid Build Coastguard Worker 244*6777b538SAndroid Build Coastguard WorkerOSStatus FakeAppleKeychainV2::ItemUpdate(CFDictionaryRef query, 245*6777b538SAndroid Build Coastguard Worker CFDictionaryRef attributes_to_update) { 246*6777b538SAndroid Build Coastguard Worker DCHECK_EQ(base::apple::GetValueFromDictionary<CFStringRef>(query, kSecClass), 247*6777b538SAndroid Build Coastguard Worker kSecClassKey); 248*6777b538SAndroid Build Coastguard Worker DCHECK(CFEqual(base::apple::GetValueFromDictionary<CFStringRef>( 249*6777b538SAndroid Build Coastguard Worker query, kSecAttrAccessGroup), 250*6777b538SAndroid Build Coastguard Worker keychain_access_group_.get())); 251*6777b538SAndroid Build Coastguard Worker CFDataRef query_credential_id = 252*6777b538SAndroid Build Coastguard Worker base::apple::GetValueFromDictionary<CFDataRef>(query, 253*6777b538SAndroid Build Coastguard Worker kSecAttrApplicationLabel); 254*6777b538SAndroid Build Coastguard Worker DCHECK(query_credential_id); 255*6777b538SAndroid Build Coastguard Worker for (base::apple::ScopedCFTypeRef<CFDictionaryRef>& item : items_) { 256*6777b538SAndroid Build Coastguard Worker CFDataRef item_credential_id = 257*6777b538SAndroid Build Coastguard Worker base::apple::GetValueFromDictionary<CFDataRef>( 258*6777b538SAndroid Build Coastguard Worker item.get(), kSecAttrApplicationLabel); 259*6777b538SAndroid Build Coastguard Worker DCHECK(item_credential_id); 260*6777b538SAndroid Build Coastguard Worker if (!CFEqual(query_credential_id, item_credential_id)) { 261*6777b538SAndroid Build Coastguard Worker continue; 262*6777b538SAndroid Build Coastguard Worker } 263*6777b538SAndroid Build Coastguard Worker base::apple::ScopedCFTypeRef<CFMutableDictionaryRef> item_copy( 264*6777b538SAndroid Build Coastguard Worker CFDictionaryCreateMutableCopy(kCFAllocatorDefault, /*capacity=*/0, 265*6777b538SAndroid Build Coastguard Worker item.get())); 266*6777b538SAndroid Build Coastguard Worker [base::apple::CFToNSPtrCast(item_copy.get()) 267*6777b538SAndroid Build Coastguard Worker addEntriesFromDictionary:base::apple::CFToNSPtrCast( 268*6777b538SAndroid Build Coastguard Worker attributes_to_update)]; 269*6777b538SAndroid Build Coastguard Worker item = item_copy; 270*6777b538SAndroid Build Coastguard Worker return errSecSuccess; 271*6777b538SAndroid Build Coastguard Worker } 272*6777b538SAndroid Build Coastguard Worker return errSecItemNotFound; 273*6777b538SAndroid Build Coastguard Worker} 274*6777b538SAndroid Build Coastguard Worker 275*6777b538SAndroid Build Coastguard Worker#if !BUILDFLAG(IS_IOS) 276*6777b538SAndroid Build Coastguard Workerbase::apple::ScopedCFTypeRef<CFTypeRef> 277*6777b538SAndroid Build Coastguard WorkerFakeAppleKeychainV2::TaskCopyValueForEntitlement(SecTaskRef task, 278*6777b538SAndroid Build Coastguard Worker CFStringRef entitlement, 279*6777b538SAndroid Build Coastguard Worker CFErrorRef* error) { 280*6777b538SAndroid Build Coastguard Worker CHECK(task); 281*6777b538SAndroid Build Coastguard Worker CHECK(CFEqual(entitlement, 282*6777b538SAndroid Build Coastguard Worker base::SysUTF8ToCFStringRef("keychain-access-groups").get())) 283*6777b538SAndroid Build Coastguard Worker << "Entitlement " << entitlement << " not supported by fake"; 284*6777b538SAndroid Build Coastguard Worker base::apple::ScopedCFTypeRef<CFMutableArrayRef> keychain_access_groups( 285*6777b538SAndroid Build Coastguard Worker CFArrayCreateMutable(kCFAllocatorDefault, /*capacity=*/1, 286*6777b538SAndroid Build Coastguard Worker &kCFTypeArrayCallBacks)); 287*6777b538SAndroid Build Coastguard Worker CFArrayAppendValue( 288*6777b538SAndroid Build Coastguard Worker keychain_access_groups.get(), 289*6777b538SAndroid Build Coastguard Worker CFStringCreateCopy(kCFAllocatorDefault, keychain_access_group_.get())); 290*6777b538SAndroid Build Coastguard Worker return keychain_access_groups; 291*6777b538SAndroid Build Coastguard Worker} 292*6777b538SAndroid Build Coastguard Worker#endif // !BUILDFLAG(IS_IOS) 293*6777b538SAndroid Build Coastguard Worker 294*6777b538SAndroid Build Coastguard WorkerBOOL FakeAppleKeychainV2::LAContextCanEvaluatePolicy( 295*6777b538SAndroid Build Coastguard Worker LAPolicy policy, 296*6777b538SAndroid Build Coastguard Worker NSError* __autoreleasing* error) { 297*6777b538SAndroid Build Coastguard Worker switch (policy) { 298*6777b538SAndroid Build Coastguard Worker case LAPolicyDeviceOwnerAuthentication: 299*6777b538SAndroid Build Coastguard Worker return uv_method_ == UVMethod::kBiometrics || 300*6777b538SAndroid Build Coastguard Worker uv_method_ == UVMethod::kPasswordOnly; 301*6777b538SAndroid Build Coastguard Worker case LAPolicyDeviceOwnerAuthenticationWithBiometrics: 302*6777b538SAndroid Build Coastguard Worker return uv_method_ == UVMethod::kBiometrics; 303*6777b538SAndroid Build Coastguard Worker case LAPolicyDeviceOwnerAuthenticationWithBiometricsOrWatch: 304*6777b538SAndroid Build Coastguard Worker return uv_method_ == UVMethod::kBiometrics; 305*6777b538SAndroid Build Coastguard Worker default: // Avoid needing to refer to values not available in the minimum 306*6777b538SAndroid Build Coastguard Worker // supported macOS version. 307*6777b538SAndroid Build Coastguard Worker NOTIMPLEMENTED(); 308*6777b538SAndroid Build Coastguard Worker return false; 309*6777b538SAndroid Build Coastguard Worker } 310*6777b538SAndroid Build Coastguard Worker} 311*6777b538SAndroid Build Coastguard Worker 312*6777b538SAndroid Build Coastguard Worker} // namespace crypto 313