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