xref: /aosp_15_r20/external/cronet/crypto/apple_keychain_ios.mm (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1*6777b538SAndroid Build Coastguard Worker// Copyright 2012 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/apple_keychain.h"
6*6777b538SAndroid Build Coastguard Worker
7*6777b538SAndroid Build Coastguard Worker#import <Foundation/Foundation.h>
8*6777b538SAndroid Build Coastguard Worker
9*6777b538SAndroid Build Coastguard Worker#include "base/apple/bridging.h"
10*6777b538SAndroid Build Coastguard Worker#include "base/apple/foundation_util.h"
11*6777b538SAndroid Build Coastguard Worker#include "base/apple/scoped_cftyperef.h"
12*6777b538SAndroid Build Coastguard Worker
13*6777b538SAndroid Build Coastguard Workerusing base::apple::CFToNSPtrCast;
14*6777b538SAndroid Build Coastguard Workerusing base::apple::NSToCFOwnershipCast;
15*6777b538SAndroid Build Coastguard Worker
16*6777b538SAndroid Build Coastguard Workernamespace {
17*6777b538SAndroid Build Coastguard Worker
18*6777b538SAndroid Build Coastguard Workerenum KeychainAction {
19*6777b538SAndroid Build Coastguard Worker  kKeychainActionCreate,
20*6777b538SAndroid Build Coastguard Worker  kKeychainActionUpdate
21*6777b538SAndroid Build Coastguard Worker};
22*6777b538SAndroid Build Coastguard Worker
23*6777b538SAndroid Build Coastguard WorkerNSString* StringWithBytesAndLength(const char* bytes, UInt32 length) {
24*6777b538SAndroid Build Coastguard Worker  return [[NSString alloc] initWithBytes:bytes
25*6777b538SAndroid Build Coastguard Worker                                  length:length
26*6777b538SAndroid Build Coastguard Worker                                encoding:NSUTF8StringEncoding];
27*6777b538SAndroid Build Coastguard Worker}
28*6777b538SAndroid Build Coastguard Worker
29*6777b538SAndroid Build Coastguard Worker// Creates a dictionary that can be used to query the keystore.
30*6777b538SAndroid Build Coastguard Workerbase::apple::ScopedCFTypeRef<CFDictionaryRef> MakeGenericPasswordQuery(
31*6777b538SAndroid Build Coastguard Worker    UInt32 serviceNameLength,
32*6777b538SAndroid Build Coastguard Worker    const char* serviceName,
33*6777b538SAndroid Build Coastguard Worker    UInt32 accountNameLength,
34*6777b538SAndroid Build Coastguard Worker    const char* accountName) {
35*6777b538SAndroid Build Coastguard Worker  NSDictionary* query = @{
36*6777b538SAndroid Build Coastguard Worker    // Type of element is generic password.
37*6777b538SAndroid Build Coastguard Worker    CFToNSPtrCast(kSecClass) : CFToNSPtrCast(kSecClassGenericPassword),
38*6777b538SAndroid Build Coastguard Worker
39*6777b538SAndroid Build Coastguard Worker    // Set the service name.
40*6777b538SAndroid Build Coastguard Worker    CFToNSPtrCast(kSecAttrService) :
41*6777b538SAndroid Build Coastguard Worker        StringWithBytesAndLength(serviceName, serviceNameLength),
42*6777b538SAndroid Build Coastguard Worker
43*6777b538SAndroid Build Coastguard Worker    // Set the account name.
44*6777b538SAndroid Build Coastguard Worker    CFToNSPtrCast(kSecAttrAccount) :
45*6777b538SAndroid Build Coastguard Worker        StringWithBytesAndLength(accountName, accountNameLength),
46*6777b538SAndroid Build Coastguard Worker
47*6777b538SAndroid Build Coastguard Worker    // Use the proper search constants, return only the data of the first match.
48*6777b538SAndroid Build Coastguard Worker    CFToNSPtrCast(kSecMatchLimit) : CFToNSPtrCast(kSecMatchLimitOne),
49*6777b538SAndroid Build Coastguard Worker    CFToNSPtrCast(kSecReturnData) : @YES,
50*6777b538SAndroid Build Coastguard Worker  };
51*6777b538SAndroid Build Coastguard Worker  return base::apple::ScopedCFTypeRef<CFDictionaryRef>(
52*6777b538SAndroid Build Coastguard Worker      NSToCFOwnershipCast(query));
53*6777b538SAndroid Build Coastguard Worker}
54*6777b538SAndroid Build Coastguard Worker
55*6777b538SAndroid Build Coastguard Worker// Creates a dictionary containing the data to save into the keychain.
56*6777b538SAndroid Build Coastguard Workerbase::apple::ScopedCFTypeRef<CFDictionaryRef> MakeKeychainData(
57*6777b538SAndroid Build Coastguard Worker    UInt32 serviceNameLength,
58*6777b538SAndroid Build Coastguard Worker    const char* serviceName,
59*6777b538SAndroid Build Coastguard Worker    UInt32 accountNameLength,
60*6777b538SAndroid Build Coastguard Worker    const char* accountName,
61*6777b538SAndroid Build Coastguard Worker    UInt32 passwordLength,
62*6777b538SAndroid Build Coastguard Worker    const void* passwordData,
63*6777b538SAndroid Build Coastguard Worker    KeychainAction action) {
64*6777b538SAndroid Build Coastguard Worker  NSData* password = [NSData dataWithBytes:passwordData length:passwordLength];
65*6777b538SAndroid Build Coastguard Worker
66*6777b538SAndroid Build Coastguard Worker  NSDictionary* keychain_data;
67*6777b538SAndroid Build Coastguard Worker
68*6777b538SAndroid Build Coastguard Worker  if (action != kKeychainActionCreate) {
69*6777b538SAndroid Build Coastguard Worker    // If this is not a creation, no structural information is needed, only the
70*6777b538SAndroid Build Coastguard Worker    // password.
71*6777b538SAndroid Build Coastguard Worker    keychain_data = @{
72*6777b538SAndroid Build Coastguard Worker      // Set the password.
73*6777b538SAndroid Build Coastguard Worker      CFToNSPtrCast(kSecValueData) : password,
74*6777b538SAndroid Build Coastguard Worker    };
75*6777b538SAndroid Build Coastguard Worker  } else {
76*6777b538SAndroid Build Coastguard Worker    keychain_data = @{
77*6777b538SAndroid Build Coastguard Worker      // Set the password.
78*6777b538SAndroid Build Coastguard Worker      CFToNSPtrCast(kSecValueData) : password,
79*6777b538SAndroid Build Coastguard Worker
80*6777b538SAndroid Build Coastguard Worker      // Set the type of the data.
81*6777b538SAndroid Build Coastguard Worker      CFToNSPtrCast(kSecClass) : CFToNSPtrCast(kSecClassGenericPassword),
82*6777b538SAndroid Build Coastguard Worker
83*6777b538SAndroid Build Coastguard Worker      // Only allow access when the device has been unlocked.
84*6777b538SAndroid Build Coastguard Worker      CFToNSPtrCast(kSecAttrAccessible) :
85*6777b538SAndroid Build Coastguard Worker          CFToNSPtrCast(kSecAttrAccessibleWhenUnlocked),
86*6777b538SAndroid Build Coastguard Worker
87*6777b538SAndroid Build Coastguard Worker      // Set the service name.
88*6777b538SAndroid Build Coastguard Worker      CFToNSPtrCast(kSecAttrService) :
89*6777b538SAndroid Build Coastguard Worker          StringWithBytesAndLength(serviceName, serviceNameLength),
90*6777b538SAndroid Build Coastguard Worker
91*6777b538SAndroid Build Coastguard Worker      // Set the account name.
92*6777b538SAndroid Build Coastguard Worker      CFToNSPtrCast(kSecAttrAccount) :
93*6777b538SAndroid Build Coastguard Worker          StringWithBytesAndLength(accountName, accountNameLength),
94*6777b538SAndroid Build Coastguard Worker    };
95*6777b538SAndroid Build Coastguard Worker  }
96*6777b538SAndroid Build Coastguard Worker
97*6777b538SAndroid Build Coastguard Worker  return base::apple::ScopedCFTypeRef<CFDictionaryRef>(
98*6777b538SAndroid Build Coastguard Worker      NSToCFOwnershipCast(keychain_data));
99*6777b538SAndroid Build Coastguard Worker}
100*6777b538SAndroid Build Coastguard Worker
101*6777b538SAndroid Build Coastguard Worker}  // namespace
102*6777b538SAndroid Build Coastguard Worker
103*6777b538SAndroid Build Coastguard Workernamespace crypto {
104*6777b538SAndroid Build Coastguard Worker
105*6777b538SAndroid Build Coastguard WorkerAppleKeychain::AppleKeychain() = default;
106*6777b538SAndroid Build Coastguard Worker
107*6777b538SAndroid Build Coastguard WorkerAppleKeychain::~AppleKeychain() = default;
108*6777b538SAndroid Build Coastguard Worker
109*6777b538SAndroid Build Coastguard WorkerOSStatus AppleKeychain::ItemFreeContent(void* data) const {
110*6777b538SAndroid Build Coastguard Worker  free(data);
111*6777b538SAndroid Build Coastguard Worker  return noErr;
112*6777b538SAndroid Build Coastguard Worker}
113*6777b538SAndroid Build Coastguard Worker
114*6777b538SAndroid Build Coastguard WorkerOSStatus AppleKeychain::AddGenericPassword(
115*6777b538SAndroid Build Coastguard Worker    UInt32 serviceNameLength,
116*6777b538SAndroid Build Coastguard Worker    const char* serviceName,
117*6777b538SAndroid Build Coastguard Worker    UInt32 accountNameLength,
118*6777b538SAndroid Build Coastguard Worker    const char* accountName,
119*6777b538SAndroid Build Coastguard Worker    UInt32 passwordLength,
120*6777b538SAndroid Build Coastguard Worker    const void* passwordData,
121*6777b538SAndroid Build Coastguard Worker    AppleSecKeychainItemRef* itemRef) const {
122*6777b538SAndroid Build Coastguard Worker  base::apple::ScopedCFTypeRef<CFDictionaryRef> query =
123*6777b538SAndroid Build Coastguard Worker      MakeGenericPasswordQuery(serviceNameLength, serviceName,
124*6777b538SAndroid Build Coastguard Worker                               accountNameLength, accountName);
125*6777b538SAndroid Build Coastguard Worker  // Check that there is not already a password.
126*6777b538SAndroid Build Coastguard Worker  OSStatus status = SecItemCopyMatching(query.get(), /*result=*/nullptr);
127*6777b538SAndroid Build Coastguard Worker  if (status == errSecItemNotFound) {
128*6777b538SAndroid Build Coastguard Worker    // A new entry must be created.
129*6777b538SAndroid Build Coastguard Worker    base::apple::ScopedCFTypeRef<CFDictionaryRef> keychain_data =
130*6777b538SAndroid Build Coastguard Worker        MakeKeychainData(serviceNameLength, serviceName, accountNameLength,
131*6777b538SAndroid Build Coastguard Worker                         accountName, passwordLength, passwordData,
132*6777b538SAndroid Build Coastguard Worker                         kKeychainActionCreate);
133*6777b538SAndroid Build Coastguard Worker    status = SecItemAdd(keychain_data.get(), /*result=*/nullptr);
134*6777b538SAndroid Build Coastguard Worker  } else if (status == noErr) {
135*6777b538SAndroid Build Coastguard Worker    // The entry must be updated.
136*6777b538SAndroid Build Coastguard Worker    base::apple::ScopedCFTypeRef<CFDictionaryRef> keychain_data =
137*6777b538SAndroid Build Coastguard Worker        MakeKeychainData(serviceNameLength, serviceName, accountNameLength,
138*6777b538SAndroid Build Coastguard Worker                         accountName, passwordLength, passwordData,
139*6777b538SAndroid Build Coastguard Worker                         kKeychainActionUpdate);
140*6777b538SAndroid Build Coastguard Worker    status = SecItemUpdate(query.get(), keychain_data.get());
141*6777b538SAndroid Build Coastguard Worker  }
142*6777b538SAndroid Build Coastguard Worker
143*6777b538SAndroid Build Coastguard Worker  return status;
144*6777b538SAndroid Build Coastguard Worker}
145*6777b538SAndroid Build Coastguard Worker
146*6777b538SAndroid Build Coastguard WorkerOSStatus AppleKeychain::FindGenericPassword(
147*6777b538SAndroid Build Coastguard Worker    UInt32 serviceNameLength,
148*6777b538SAndroid Build Coastguard Worker    const char* serviceName,
149*6777b538SAndroid Build Coastguard Worker    UInt32 accountNameLength,
150*6777b538SAndroid Build Coastguard Worker    const char* accountName,
151*6777b538SAndroid Build Coastguard Worker    UInt32* passwordLength,
152*6777b538SAndroid Build Coastguard Worker    void** passwordData,
153*6777b538SAndroid Build Coastguard Worker    AppleSecKeychainItemRef* itemRef) const {
154*6777b538SAndroid Build Coastguard Worker  DCHECK((passwordData && passwordLength) ||
155*6777b538SAndroid Build Coastguard Worker         (!passwordData && !passwordLength));
156*6777b538SAndroid Build Coastguard Worker  base::apple::ScopedCFTypeRef<CFDictionaryRef> query =
157*6777b538SAndroid Build Coastguard Worker      MakeGenericPasswordQuery(serviceNameLength, serviceName,
158*6777b538SAndroid Build Coastguard Worker                               accountNameLength, accountName);
159*6777b538SAndroid Build Coastguard Worker
160*6777b538SAndroid Build Coastguard Worker  // Get the keychain item containing the password.
161*6777b538SAndroid Build Coastguard Worker  base::apple::ScopedCFTypeRef<CFTypeRef> result;
162*6777b538SAndroid Build Coastguard Worker  OSStatus status = SecItemCopyMatching(query.get(), result.InitializeInto());
163*6777b538SAndroid Build Coastguard Worker
164*6777b538SAndroid Build Coastguard Worker  if (status != noErr) {
165*6777b538SAndroid Build Coastguard Worker    if (passwordData) {
166*6777b538SAndroid Build Coastguard Worker      *passwordData = nullptr;
167*6777b538SAndroid Build Coastguard Worker      *passwordLength = 0;
168*6777b538SAndroid Build Coastguard Worker    }
169*6777b538SAndroid Build Coastguard Worker    return status;
170*6777b538SAndroid Build Coastguard Worker  }
171*6777b538SAndroid Build Coastguard Worker
172*6777b538SAndroid Build Coastguard Worker  if (passwordData) {
173*6777b538SAndroid Build Coastguard Worker    CFDataRef data = base::apple::CFCast<CFDataRef>(result.get());
174*6777b538SAndroid Build Coastguard Worker    NSUInteger length = CFDataGetLength(data);
175*6777b538SAndroid Build Coastguard Worker    *passwordData = malloc(length * sizeof(UInt8));
176*6777b538SAndroid Build Coastguard Worker    CFDataGetBytes(data, CFRangeMake(0, length), (UInt8*)*passwordData);
177*6777b538SAndroid Build Coastguard Worker    *passwordLength = length;
178*6777b538SAndroid Build Coastguard Worker  }
179*6777b538SAndroid Build Coastguard Worker  return status;
180*6777b538SAndroid Build Coastguard Worker}
181*6777b538SAndroid Build Coastguard Worker
182*6777b538SAndroid Build Coastguard Worker}  // namespace crypto
183