xref: /aosp_15_r20/external/cronet/crypto/fake_apple_keychain_v2.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 "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