]>
Commit | Line | Data |
---|---|---|
d8f41ccd A |
1 | /* |
2 | * Copyright (c) 2006-2014 Apple Inc. All Rights Reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
12 | * | |
13 | * The Original Code and all software distributed under the License are | |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | ||
24 | /* | |
25 | * SecDbKeychainItem.c - CoreFoundation-based constants and functions for | |
26 | access to Security items (certificates, keys, identities, and | |
27 | passwords.) | |
28 | */ | |
29 | ||
30 | #include <securityd/SecDbKeychainItem.h> | |
31 | ||
32 | #include <securityd/SecItemSchema.h> | |
33 | #include <CommonCrypto/CommonCryptor.h> | |
34 | #include <CommonCrypto/CommonCryptorSPI.h> | |
35 | #include <Security/SecBasePriv.h> | |
36 | #include <Security/SecItem.h> | |
fa7225c8 | 37 | #include <Security/SecItemPriv.h> |
d8f41ccd A |
38 | #include <Security/SecItemInternal.h> |
39 | #include <Security/SecRandom.h> | |
40 | #include <Security/SecAccessControl.h> | |
41 | #include <Security/SecAccessControlPriv.h> | |
42 | #include <utilities/der_plist.h> | |
5c19dc3a A |
43 | #include <utilities/der_plist_internal.h> |
44 | #include <utilities/SecCFCCWrappers.h> | |
d8f41ccd A |
45 | |
46 | #if USE_KEYSTORE | |
47 | #include <LocalAuthentication/LAPublicDefines.h> | |
48 | #include <LocalAuthentication/LAPrivateDefines.h> | |
49 | #include <coreauthd_spi.h> | |
50 | #include <libaks_acl_cf_keys.h> | |
51 | #include <securityd/spi.h> | |
6b200bc3 | 52 | |
d8f41ccd A |
53 | #endif /* USE_KEYSTORE */ |
54 | ||
55 | pthread_key_t CURRENT_CONNECTION_KEY; | |
56 | ||
57 | // From SecItemServer, should be a acl-check block | |
58 | bool itemInAccessGroup(CFDictionaryRef item, CFArrayRef accessGroups); | |
59 | ||
60 | static keyclass_t kc_parse_keyclass(CFTypeRef value, CFErrorRef *error); | |
61 | static CFTypeRef kc_encode_keyclass(keyclass_t keyclass); | |
62 | static CFDataRef kc_copy_protection_data(SecAccessControlRef access_control); | |
5c19dc3a | 63 | static CFTypeRef kc_copy_protection_from(const uint8_t *der, const uint8_t *der_end); |
6b200bc3 A |
64 | static CF_RETURNS_RETAINED CFMutableDictionaryRef s3dl_item_v2_decode(CFDataRef plain, CFErrorRef *error); |
65 | static CF_RETURNS_RETAINED CFMutableDictionaryRef s3dl_item_v3_decode(CFDataRef plain, CFErrorRef *error); | |
d8f41ccd | 66 | #if USE_KEYSTORE |
5c19dc3a A |
67 | static CFDataRef kc_create_auth_data(SecAccessControlRef access_control, CFDictionaryRef auth_attributes); |
68 | static bool kc_attribs_key_encrypted_data_from_blob(keybag_handle_t keybag, const SecDbClass *class, const void *blob_data, size_t blob_data_len, SecAccessControlRef access_control, uint32_t version, | |
69 | CFMutableDictionaryRef *authenticated_attributes, aks_ref_key_t *ref_key, CFDataRef *encrypted_data, CFErrorRef *error); | |
d8f41ccd A |
70 | static CFDataRef kc_copy_access_groups_data(CFArrayRef access_groups, CFErrorRef *error); |
71 | #endif | |
5c19dc3a A |
72 | |
73 | static const uint8_t* der_decode_plist_with_repair(CFAllocatorRef pl, CFOptionFlags mutability, CFPropertyListRef* cf, CFErrorRef *error, | |
74 | const uint8_t* der, const uint8_t *der_end, | |
75 | const uint8_t* (^repairBlock)(CFAllocatorRef allocator, CFOptionFlags mutability, CFPropertyListRef* pl, CFErrorRef *error, | |
76 | const uint8_t* der, const uint8_t *der_end)); | |
77 | static const uint8_t* der_decode_dictionary_with_repair(CFAllocatorRef allocator, CFOptionFlags mutability, CFDictionaryRef* dictionary, CFErrorRef *error, | |
78 | const uint8_t* der, const uint8_t *der_end, | |
79 | const uint8_t* (^repairBlock)(CFAllocatorRef allocator, CFOptionFlags mutability, CFPropertyListRef* pl, CFErrorRef *error, | |
80 | const uint8_t* der, const uint8_t *der_end)); | |
81 | static const uint8_t* der_decode_key_value_with_repair(CFAllocatorRef allocator, CFOptionFlags mutability, CFPropertyListRef* key, CFPropertyListRef* value, CFErrorRef *error, | |
82 | const uint8_t* der, const uint8_t *der_end, | |
83 | const uint8_t* (^repairBlock)(CFAllocatorRef allocator, CFOptionFlags mutability, CFPropertyListRef* pl, CFErrorRef *error, | |
84 | const uint8_t* der, const uint8_t *der_end)); | |
85 | static const uint8_t* der_decode_array_with_repair(CFAllocatorRef allocator, CFOptionFlags mutability, CFArrayRef* array, CFErrorRef *error, | |
86 | const uint8_t* der, const uint8_t *der_end, | |
87 | const uint8_t* (^repairBlock)(CFAllocatorRef allocator, CFOptionFlags mutability, CFPropertyListRef* pl, CFErrorRef *error, | |
88 | const uint8_t* der, const uint8_t *der_end)); | |
89 | static const uint8_t* der_decode_set_with_repair(CFAllocatorRef allocator, CFOptionFlags mutability, CFSetRef* set, CFErrorRef *error, | |
90 | const uint8_t* der, const uint8_t *der_end, | |
91 | const uint8_t* (^repairBlock)(CFAllocatorRef allocator, CFOptionFlags mutability, CFPropertyListRef* pl, CFErrorRef *error, | |
92 | const uint8_t* der, const uint8_t *der_end)); | |
93 | ||
fa7225c8 A |
94 | const uint32_t kUseDefaultIVMask = 1<<31; |
95 | const int16_t kIVSizeAESGCM = 12; | |
96 | ||
97 | // echo "keychainblobstaticiv" | openssl dgst -sha256 | cut -c1-24 | xargs -I {} echo "0x{}" | xxd -r | xxd -p -i | |
fa7225c8 A |
98 | static const uint8_t gcmIV[kIVSizeAESGCM] = { |
99 | 0x1e, 0xa0, 0x5c, 0xa9, 0x98, 0x2e, 0x87, 0xdc, 0xf1, 0x45, 0xe8, 0x24 | |
100 | }; | |
101 | ||
d8f41ccd A |
102 | /* Given plainText create and return a CFDataRef containing: |
103 | BULK_KEY = RandomKey() | |
104 | version || keyclass|ACL || KeyStore_WRAP(keyclass, BULK_KEY) || | |
105 | AES(BULK_KEY, NULL_IV, plainText || padding) | |
106 | */ | |
5c19dc3a | 107 | bool ks_encrypt_data(keybag_handle_t keybag, SecAccessControlRef access_control, CFDataRef acm_context, |
fa7225c8 | 108 | CFDictionaryRef attributes, CFDictionaryRef authenticated_attributes, CFDataRef *pBlob, bool useDefaultIV, CFErrorRef *error) { |
d8f41ccd A |
109 | CFMutableDataRef blob = NULL; |
110 | CFDataRef ac_data = NULL; | |
111 | bool ok = true; | |
112 | //check(keybag >= 0); | |
113 | ||
114 | /* Precalculate output blob length. */ | |
115 | const uint32_t bulkKeySize = 32; /* Use 256 bit AES key for bulkKey. */ | |
116 | const uint32_t maxKeyWrapOverHead = 8 + 32; | |
117 | uint8_t bulkKey[bulkKeySize]; | |
118 | CFMutableDataRef bulkKeyWrapped = CFDataCreateMutable(NULL, 0); | |
119 | CFDataSetLength(bulkKeyWrapped, bulkKeySize + maxKeyWrapOverHead); | |
120 | uint32_t key_wrapped_size; | |
fa7225c8 A |
121 | size_t ivLen = 0; |
122 | const uint8_t *iv = NULL; | |
123 | const uint8_t *aad = NULL; // Additional Authenticated Data | |
124 | ptrdiff_t aadLen = 0; | |
125 | ||
d8f41ccd | 126 | #if USE_KEYSTORE |
5c19dc3a | 127 | CFDataRef auth_data = NULL; |
d8f41ccd | 128 | #endif |
5c19dc3a | 129 | |
d8f41ccd | 130 | /* If access_control specifies only protection and no ACL, use legacy blob format version 3, |
fa7225c8 A |
131 | which has better support for sync/backup. Otherwise, force new format v6 unless useDefaultIV is set. */ |
132 | bool hasACLConstraints = SecAccessControlGetConstraints(access_control); | |
133 | const uint32_t version = (hasACLConstraints ? 6 : 3); | |
d8f41ccd A |
134 | CFDataRef plainText = NULL; |
135 | if (version < 4) { | |
136 | CFMutableDictionaryRef attributes_dict = CFDictionaryCreateMutableCopy(NULL, 0, attributes); | |
137 | if (authenticated_attributes) { | |
138 | CFDictionaryForEach(authenticated_attributes, ^(const void *key, const void *value) { | |
139 | CFDictionaryAddValue(attributes_dict, key, value); | |
140 | }); | |
141 | } | |
fa7225c8 | 142 | |
d8f41ccd | 143 | if (attributes_dict) { |
5c19dc3a | 144 | // Drop the accc attribute for non v6 items during encode. |
d8f41ccd | 145 | CFDictionaryRemoveValue(attributes_dict, kSecAttrAccessControl); |
5c19dc3a | 146 | plainText = CFPropertyListCreateDERData(kCFAllocatorDefault, attributes_dict, error); |
d8f41ccd A |
147 | CFRelease(attributes_dict); |
148 | } | |
149 | } else { | |
150 | #if USE_KEYSTORE | |
151 | if (attributes) { | |
5c19dc3a | 152 | plainText = CFPropertyListCreateDERData(kCFAllocatorDefault, attributes, error); |
d8f41ccd A |
153 | } |
154 | #else | |
155 | CFMutableDictionaryRef attributes_dict = CFDictionaryCreateMutableCopy(NULL, 0, attributes); | |
156 | if (authenticated_attributes) { | |
157 | CFDictionaryForEach(authenticated_attributes, ^(const void *key, const void *value) { | |
158 | CFDictionaryAddValue(attributes_dict, key, value); | |
159 | }); | |
160 | } | |
fa7225c8 | 161 | |
d8f41ccd | 162 | if (attributes_dict) { |
5c19dc3a | 163 | plainText = CFPropertyListCreateDERData(kCFAllocatorDefault, attributes_dict, error); |
d8f41ccd A |
164 | CFRelease(attributes_dict); |
165 | } | |
166 | #endif | |
167 | } | |
168 | ||
169 | if (!plainText || CFGetTypeID(plainText) != CFDataGetTypeID() | |
170 | || access_control == 0) { | |
171 | ok = SecError(errSecParam, error, CFSTR("ks_encrypt_data: invalid plain text")); | |
172 | goto out; | |
173 | } | |
174 | ||
175 | size_t ptLen = CFDataGetLength(plainText); | |
176 | size_t ctLen = ptLen; | |
177 | size_t tagLen = 16; | |
6b200bc3 | 178 | keyclass_t actual_class = 0; |
d8f41ccd A |
179 | |
180 | if (SecRandomCopyBytes(kSecRandomDefault, bulkKeySize, bulkKey)) { | |
181 | ok = SecError(errSecAllocate, error, CFSTR("ks_encrypt_data: SecRandomCopyBytes failed")); | |
182 | goto out; | |
183 | } | |
184 | ||
185 | /* Extract keyclass from access control. */ | |
186 | keyclass_t keyclass = kc_parse_keyclass(SecAccessControlGetProtection(access_control), error); | |
187 | if (!keyclass) | |
188 | goto out; | |
189 | ||
190 | #if USE_KEYSTORE | |
191 | if (version >= 4) { | |
5c19dc3a A |
192 | auth_data = kc_create_auth_data(access_control, authenticated_attributes); |
193 | require_quiet(ok = ks_encrypt_acl(keybag, keyclass, bulkKeySize, bulkKey, bulkKeyWrapped, auth_data, acm_context, access_control, error), out); | |
194 | } else | |
d8f41ccd | 195 | #endif |
5c19dc3a A |
196 | { |
197 | /* Encrypt bulkKey. */ | |
198 | require_quiet(ok = ks_crypt(kAKSKeyOpEncrypt, keybag, | |
199 | keyclass, bulkKeySize, bulkKey, | |
200 | &actual_class, bulkKeyWrapped, | |
201 | error), out); | |
d8f41ccd | 202 | } |
5c19dc3a | 203 | |
d8f41ccd A |
204 | key_wrapped_size = (uint32_t)CFDataGetLength(bulkKeyWrapped); |
205 | UInt8 *cursor; | |
206 | size_t blobLen = sizeof(version); | |
6b200bc3 | 207 | uint32_t prot_length = 0; |
d8f41ccd | 208 | |
fa7225c8 | 209 | if (!hasACLConstraints) { |
d8f41ccd A |
210 | blobLen += sizeof(actual_class); |
211 | } else { | |
212 | require_quiet(ac_data = kc_copy_protection_data(access_control), out); | |
213 | prot_length = (uint32_t)CFDataGetLength(ac_data); | |
214 | blobLen += sizeof(prot_length) + prot_length; | |
215 | } | |
216 | ||
217 | blobLen += sizeof(key_wrapped_size) + key_wrapped_size + ctLen + tagLen; | |
218 | require_quiet(blob = CFDataCreateMutable(NULL, blobLen), out); | |
219 | CFDataSetLength(blob, blobLen); | |
220 | cursor = CFDataGetMutableBytePtr(blob); | |
221 | ||
fa7225c8 | 222 | *((uint32_t *)cursor) = useDefaultIV ? (version | kUseDefaultIVMask) : version; |
d8f41ccd A |
223 | cursor += sizeof(version); |
224 | ||
225 | //secerror("class: %d actual class: %d", keyclass, actual_class); | |
fa7225c8 | 226 | if (!hasACLConstraints) { |
d8f41ccd A |
227 | *((keyclass_t *)cursor) = actual_class; |
228 | cursor += sizeof(keyclass); | |
229 | } else { | |
230 | *((uint32_t *)cursor) = prot_length; | |
231 | cursor += sizeof(prot_length); | |
232 | ||
233 | CFDataGetBytes(ac_data, CFRangeMake(0, prot_length), cursor); | |
234 | cursor += prot_length; | |
235 | } | |
236 | ||
237 | *((uint32_t *)cursor) = key_wrapped_size; | |
238 | cursor += sizeof(key_wrapped_size); | |
239 | ||
fa7225c8 A |
240 | if (useDefaultIV) { |
241 | iv = gcmIV; | |
242 | ivLen = kIVSizeAESGCM; | |
243 | // AAD is (version || ac_data || key_wrapped_size) | |
244 | aad = CFDataGetMutableBytePtr(blob); | |
245 | aadLen = cursor - aad; | |
246 | } | |
247 | ||
d8f41ccd A |
248 | memcpy(cursor, CFDataGetBytePtr(bulkKeyWrapped), key_wrapped_size); |
249 | cursor += key_wrapped_size; | |
250 | ||
251 | /* Encrypt the plainText with the bulkKey. */ | |
252 | CCCryptorStatus ccerr = CCCryptorGCM(kCCEncrypt, kCCAlgorithmAES128, | |
253 | bulkKey, bulkKeySize, | |
fa7225c8 A |
254 | iv, ivLen, /* iv */ |
255 | aad, aadLen, /* auth data */ | |
d8f41ccd A |
256 | CFDataGetBytePtr(plainText), ptLen, |
257 | cursor, | |
258 | cursor + ctLen, &tagLen); | |
259 | if (ccerr) { | |
260 | ok = SecError(errSecInternal, error, CFSTR("ks_encrypt_data: CCCryptorGCM failed: %d"), ccerr); | |
261 | goto out; | |
262 | } | |
263 | if (tagLen != 16) { | |
264 | ok = SecError(errSecInternal, error, CFSTR("ks_encrypt_data: CCCryptorGCM expected: 16 got: %ld byte tag"), tagLen); | |
265 | goto out; | |
266 | } | |
267 | ||
268 | out: | |
269 | memset(bulkKey, 0, sizeof(bulkKey)); | |
270 | CFReleaseSafe(ac_data); | |
271 | CFReleaseSafe(bulkKeyWrapped); | |
272 | CFReleaseSafe(plainText); | |
273 | if (!ok) { | |
274 | CFReleaseSafe(blob); | |
275 | } else { | |
276 | *pBlob = blob; | |
277 | } | |
278 | ||
279 | #if USE_KEYSTORE | |
5c19dc3a | 280 | CFReleaseSafe(auth_data); |
d8f41ccd A |
281 | #endif |
282 | return ok; | |
283 | } | |
284 | ||
285 | /* Given cipherText containing: | |
286 | version || keyclass || KeyStore_WRAP(keyclass, BULK_KEY) || | |
287 | AES(BULK_KEY, NULL_IV, plainText || padding) | |
288 | return the plainText. */ | |
5c19dc3a | 289 | bool ks_decrypt_data(keybag_handle_t keybag, CFTypeRef cryptoOp, SecAccessControlRef *paccess_control, CFDataRef acm_context, |
d8f41ccd A |
290 | CFDataRef blob, const SecDbClass *db_class, CFArrayRef caller_access_groups, |
291 | CFMutableDictionaryRef *attributes_p, uint32_t *version_p, CFErrorRef *error) { | |
292 | const uint32_t v0KeyWrapOverHead = 8; | |
293 | CFMutableDataRef bulkKey = CFDataCreateMutable(0, 32); /* Use 256 bit AES key for bulkKey. */ | |
294 | CFDataSetLength(bulkKey, 32); /* Use 256 bit AES key for bulkKey. */ | |
295 | bool ok = true; | |
296 | SecAccessControlRef access_control = NULL; | |
fa7225c8 A |
297 | |
298 | if (attributes_p) | |
299 | *attributes_p = NULL; | |
300 | if (version_p) | |
301 | *version_p = 0; | |
d8f41ccd A |
302 | |
303 | CFMutableDataRef plainText = NULL; | |
304 | CFMutableDictionaryRef attributes = NULL; | |
305 | uint32_t version = 0; | |
fa7225c8 A |
306 | size_t ivLen = 0; |
307 | const uint8_t *iv = NULL; | |
308 | const uint8_t *aad = NULL; // Additional Authenticated Data | |
309 | ptrdiff_t aadLen = 0; | |
5c19dc3a | 310 | |
d8f41ccd | 311 | #if USE_KEYSTORE |
d8f41ccd A |
312 | CFMutableDictionaryRef authenticated_attributes = NULL; |
313 | CFDataRef caller_access_groups_data = NULL; | |
5c19dc3a A |
314 | CFDataRef ed_data = NULL; |
315 | aks_ref_key_t ref_key = NULL; | |
d8f41ccd A |
316 | #if TARGET_OS_IPHONE |
317 | check(keybag >= 0); | |
318 | #else | |
319 | check((keybag >= 0) || (keybag == session_keybag_handle)); | |
320 | #endif | |
321 | #endif | |
322 | ||
323 | if (!blob) { | |
324 | ok = SecError(errSecParam, error, CFSTR("ks_decrypt_data: invalid blob")); | |
325 | goto out; | |
326 | } | |
327 | ||
328 | size_t blobLen = CFDataGetLength(blob); | |
329 | const uint8_t *cursor = CFDataGetBytePtr(blob); | |
330 | keyclass_t keyclass; | |
d8f41ccd | 331 | |
b04fe171 A |
332 | if (blobLen < sizeof(version)) { |
333 | ok = SecError(errSecDecode, error, CFSTR("ks_decrypt_data: Check for underflow (length)")); | |
d8f41ccd A |
334 | goto out; |
335 | } | |
336 | ||
337 | version = *((uint32_t *)cursor); | |
fa7225c8 A |
338 | if (version & kUseDefaultIVMask) { |
339 | version &= ~kUseDefaultIVMask; | |
340 | iv = gcmIV; | |
341 | ivLen = kIVSizeAESGCM; | |
342 | } | |
343 | ||
d8f41ccd | 344 | cursor += sizeof(version); |
b04fe171 | 345 | blobLen -= sizeof(version); |
d8f41ccd | 346 | |
fa7225c8 A |
347 | bool hasProtectionData = (version >= 4); |
348 | ||
349 | if (hasProtectionData) { | |
d8f41ccd | 350 | /* Deserialize SecAccessControl object from the blob. */ |
b04fe171 A |
351 | uint32_t prot_length; |
352 | ||
353 | /* | |
354 | * Parse proto length | |
355 | */ | |
356 | ||
357 | if (blobLen < sizeof(prot_length)) { | |
358 | ok = SecError(errSecDecode, error, CFSTR("ks_decrypt_data: Check for underflow (prot_length)")); | |
359 | goto out; | |
360 | } | |
361 | ||
362 | prot_length = *((uint32_t *)cursor); | |
d8f41ccd | 363 | cursor += sizeof(prot_length); |
b04fe171 A |
364 | blobLen -= sizeof(prot_length); |
365 | ||
366 | /* | |
367 | * Parse proto itself | |
368 | */ | |
369 | ||
370 | if (blobLen < prot_length) { | |
371 | ok = SecError(errSecDecode, error, CFSTR("ks_decrypt_data: Check for underflow (prot)")); | |
372 | goto out; | |
373 | } | |
d8f41ccd | 374 | |
5c19dc3a | 375 | CFTypeRef protection = kc_copy_protection_from(cursor, cursor + prot_length); |
d8f41ccd A |
376 | if (!protection) { |
377 | ok = SecError(errSecDecode, error, CFSTR("ks_decrypt_data: invalid ACL")); | |
378 | goto out; | |
b04fe171 | 379 | } else { |
d8f41ccd A |
380 | access_control = SecAccessControlCreate(NULL, NULL); |
381 | require_quiet(access_control, out); | |
382 | ok = SecAccessControlSetProtection(access_control, protection, NULL); | |
383 | CFRelease(protection); | |
384 | if (!ok) { | |
385 | SecError(errSecDecode, error, CFSTR("ks_decrypt_data: invalid ACL")); | |
386 | goto out; | |
387 | } | |
388 | } | |
389 | ||
390 | cursor += prot_length; | |
b04fe171 | 391 | blobLen -= prot_length; |
d8f41ccd | 392 | |
b04fe171 A |
393 | /* |
394 | * Get numeric value of keyclass from the access_control. | |
395 | */ | |
d8f41ccd A |
396 | keyclass = kc_parse_keyclass(SecAccessControlGetProtection(access_control), error); |
397 | if (!keyclass) { | |
398 | ok = SecError(errSecDecode, error, CFSTR("ks_decrypt_data: invalid ACL")); | |
399 | goto out; | |
400 | } | |
401 | } else { | |
b04fe171 A |
402 | if (blobLen < sizeof(keyclass)) { |
403 | ok = SecError(errSecDecode, error, CFSTR("ks_decrypt_data: Check for underflow (keyclass)")); | |
404 | goto out; | |
405 | } | |
406 | ||
d8f41ccd | 407 | keyclass = *((keyclass_t *)cursor); |
b04fe171 | 408 | |
d8f41ccd A |
409 | #if USE_KEYSTORE |
410 | CFTypeRef protection = kc_encode_keyclass(keyclass & key_class_last); // mask out generation | |
411 | #else | |
412 | CFTypeRef protection = kc_encode_keyclass(keyclass); | |
413 | #endif | |
5c19dc3a A |
414 | require_action_quiet(protection, out, ok = SecError(errSecDecode, error, CFSTR("ks_decrypt_data: invalid keyclass detected"))); |
415 | require_action_quiet(access_control = SecAccessControlCreate(kCFAllocatorDefault, error), out, | |
416 | ok = SecError(errSecDecode, error, CFSTR("ks_decrypt_data: SecAccessControlCreate failed"))); | |
417 | require_action_quiet(SecAccessControlSetProtection(access_control, protection, error), out, | |
418 | ok = SecError(errSecDecode, error, CFSTR("ks_decrypt_data: SecAccessControlSetProtection failed"))); | |
419 | ||
d8f41ccd | 420 | cursor += sizeof(keyclass); |
b04fe171 | 421 | blobLen -= sizeof(keyclass); |
d8f41ccd A |
422 | } |
423 | ||
424 | size_t tagLen = 0; | |
b04fe171 A |
425 | uint32_t wrapped_key_size = 0; |
426 | ||
d8f41ccd A |
427 | switch (version) { |
428 | case 0: | |
429 | wrapped_key_size = (uint32_t)CFDataGetLength(bulkKey) + v0KeyWrapOverHead; | |
430 | break; | |
431 | case 2: | |
432 | case 3: | |
433 | /* DROPTHROUGH */ | |
434 | /* v2 and v3 have the same crypto, just different dictionary encodings. */ | |
5c19dc3a | 435 | /* Difference between v3 and v6 is already handled above, so treat v3 as v6. */ |
d8f41ccd | 436 | case 4: |
5c19dc3a A |
437 | case 5: |
438 | case 6: | |
d8f41ccd | 439 | tagLen = 16; |
d8f41ccd A |
440 | /* DROPTHROUGH */ |
441 | case 1: | |
b04fe171 A |
442 | if (blobLen < sizeof(wrapped_key_size)) { |
443 | ok = SecError(errSecDecode, error, CFSTR("ks_decrypt_data: Check for underflow (wrapped_key_size)")); | |
444 | goto out; | |
445 | } | |
d8f41ccd | 446 | wrapped_key_size = *((uint32_t *)cursor); |
b04fe171 | 447 | |
d8f41ccd | 448 | cursor += sizeof(wrapped_key_size); |
b04fe171 A |
449 | blobLen -= sizeof(wrapped_key_size); |
450 | ||
d8f41ccd A |
451 | break; |
452 | default: | |
453 | ok = SecError(errSecDecode, error, CFSTR("ks_decrypt_data: invalid version %d"), version); | |
454 | goto out; | |
455 | } | |
456 | ||
b04fe171 A |
457 | if (blobLen < tagLen + wrapped_key_size) { |
458 | ok = SecError(errSecDecode, error, CFSTR("ks_decrypt_data: Check for underflow (wrapped_key/taglen)")); | |
d8f41ccd A |
459 | goto out; |
460 | } | |
461 | ||
b04fe171 A |
462 | size_t ctLen = blobLen - tagLen - wrapped_key_size; |
463 | ||
464 | /* | |
465 | * Pre-version 2 have some additial constraints since it use AES in CBC mode | |
466 | */ | |
467 | if (version < 2) { | |
468 | if (ctLen < kCCBlockSizeAES128) { | |
469 | ok = SecError(errSecDecode, error, CFSTR("ks_decrypt_data: Check for underflow (CBC check)")); | |
470 | goto out; | |
471 | } | |
472 | if ((ctLen & 0xF) != 0) { | |
473 | ok = SecError(errSecDecode, error, CFSTR("ks_decrypt_data: invalid length on CBC data")); | |
474 | goto out; | |
475 | } | |
476 | } | |
477 | ||
d8f41ccd | 478 | #if USE_KEYSTORE |
fa7225c8 | 479 | if (hasProtectionData) { |
d8f41ccd A |
480 | if (caller_access_groups) { |
481 | caller_access_groups_data = kc_copy_access_groups_data(caller_access_groups, error); | |
482 | require_quiet(ok = (caller_access_groups_data != NULL), out); | |
483 | } | |
5c19dc3a A |
484 | |
485 | require_quiet(ok = kc_attribs_key_encrypted_data_from_blob(keybag, db_class, cursor, wrapped_key_size, access_control, version, | |
486 | &authenticated_attributes, &ref_key, &ed_data, error), out); | |
487 | if (CFEqual(cryptoOp, kAKSKeyOpDecrypt)) { | |
488 | require_quiet(ok = ks_decrypt_acl(ref_key, ed_data, bulkKey, acm_context, caller_access_groups_data, access_control, error), out); | |
489 | } else if (CFEqual(cryptoOp, kAKSKeyOpDelete)) { | |
490 | require_quiet(ok = ks_delete_acl(ref_key, ed_data, acm_context, caller_access_groups_data, access_control, error), out); | |
d8f41ccd A |
491 | attributes = CFRetainSafe(authenticated_attributes); |
492 | goto out; | |
b04fe171 A |
493 | } else { |
494 | ok = SecError(errSecInternal, error, CFSTR("ks_decrypt_data: invalid operation")); | |
495 | goto out; | |
d8f41ccd | 496 | } |
5c19dc3a | 497 | } else |
d8f41ccd | 498 | #endif |
5c19dc3a | 499 | { |
d8f41ccd | 500 | /* Now unwrap the bulk key using a key in the keybag. */ |
5c19dc3a | 501 | require_quiet(ok = ks_crypt(cryptoOp, keybag, |
d8f41ccd | 502 | keyclass, wrapped_key_size, cursor, NULL, bulkKey, error), out); |
d8f41ccd | 503 | } |
5c19dc3a | 504 | |
fa7225c8 | 505 | if (iv) { |
b04fe171 | 506 | // AAD is (version || ... [|| key_wrapped_size ]) |
fa7225c8 A |
507 | aad = CFDataGetBytePtr(blob); |
508 | aadLen = cursor - aad; | |
509 | } | |
510 | ||
d8f41ccd A |
511 | cursor += wrapped_key_size; |
512 | ||
513 | plainText = CFDataCreateMutable(NULL, ctLen); | |
514 | if (!plainText) { | |
515 | ok = SecError(errSecDecode, error, CFSTR("ks_decrypt_data: failed to allocate data for plain text")); | |
516 | goto out; | |
517 | } | |
518 | CFDataSetLength(plainText, ctLen); | |
519 | ||
520 | /* Decrypt the cipherText with the bulkKey. */ | |
521 | CCCryptorStatus ccerr; | |
522 | if (tagLen) { | |
523 | uint8_t tag[tagLen]; | |
524 | ccerr = CCCryptorGCM(kCCDecrypt, kCCAlgorithmAES128, | |
525 | CFDataGetBytePtr(bulkKey), CFDataGetLength(bulkKey), | |
fa7225c8 A |
526 | iv, ivLen, /* iv */ |
527 | aad, aadLen, /* auth data */ | |
d8f41ccd A |
528 | cursor, ctLen, |
529 | CFDataGetMutableBytePtr(plainText), | |
530 | tag, &tagLen); | |
531 | if (ccerr) { | |
532 | /* TODO: Should this be errSecDecode once AppleKeyStore correctly | |
533 | identifies uuid unwrap failures? */ | |
534 | /* errSecInteractionNotAllowed; */ | |
535 | ok = SecError(errSecDecode, error, CFSTR("ks_decrypt_data: CCCryptorGCM failed: %d"), ccerr); | |
536 | goto out; | |
537 | } | |
538 | if (tagLen != 16) { | |
539 | ok = SecError(errSecInternal, error, CFSTR("ks_decrypt_data: CCCryptorGCM expected: 16 got: %ld byte tag"), tagLen); | |
540 | goto out; | |
541 | } | |
542 | cursor += ctLen; | |
3a7be6fd | 543 | if (timingsafe_bcmp(tag, cursor, tagLen)) { |
d8f41ccd A |
544 | ok = SecError(errSecDecode, error, CFSTR("ks_decrypt_data: CCCryptorGCM computed tag not same as tag in blob")); |
545 | goto out; | |
546 | } | |
547 | } else { | |
548 | size_t ptLen; | |
549 | ccerr = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, | |
550 | CFDataGetBytePtr(bulkKey), CFDataGetLength(bulkKey), NULL, cursor, ctLen, | |
551 | CFDataGetMutableBytePtr(plainText), ctLen, &ptLen); | |
552 | if (ccerr) { | |
553 | /* TODO: Should this be errSecDecode once AppleKeyStore correctly | |
554 | identifies uuid unwrap failures? */ | |
555 | /* errSecInteractionNotAllowed; */ | |
556 | ok = SecError(errSecDecode, error, CFSTR("ks_decrypt_data: CCCrypt failed: %d"), ccerr); | |
557 | goto out; | |
558 | } | |
559 | CFDataSetLength(plainText, ptLen); | |
560 | } | |
561 | ||
562 | if (version < 2) { | |
563 | attributes = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | |
564 | CFDictionaryAddValue(attributes, CFSTR("v_Data"), plainText); | |
565 | } else if (version < 3) { | |
566 | attributes = s3dl_item_v2_decode(plainText, error); | |
567 | } else { | |
568 | attributes = s3dl_item_v3_decode(plainText, error); | |
d8f41ccd A |
569 | } |
570 | ||
866f8763 | 571 | require_action_quiet(attributes, out, { ok = false; secerror("decode v%d failed: %@", version, error ? *error : NULL); }); |
5c19dc3a A |
572 | |
573 | #if USE_KEYSTORE | |
574 | if (version >= 4 && authenticated_attributes != NULL) { | |
575 | CFDictionaryForEach(authenticated_attributes, ^(const void *key, const void *value) { | |
576 | CFDictionaryAddValue(attributes, key, value); | |
577 | }); | |
d8f41ccd | 578 | } |
5c19dc3a A |
579 | #endif |
580 | ||
d8f41ccd A |
581 | out: |
582 | memset(CFDataGetMutableBytePtr(bulkKey), 0, CFDataGetLength(bulkKey)); | |
6b200bc3 A |
583 | CFReleaseNull(bulkKey); |
584 | CFReleaseNull(plainText); | |
d8f41ccd A |
585 | |
586 | // Always copy access control data (if present), because if we fail it may indicate why. | |
587 | if (paccess_control) | |
588 | *paccess_control = access_control; | |
589 | else | |
6b200bc3 | 590 | CFReleaseNull(access_control); |
d8f41ccd A |
591 | |
592 | if (ok) { | |
593 | if (attributes_p) | |
6b200bc3 | 594 | CFRetainAssign(*attributes_p, attributes); |
d8f41ccd A |
595 | if (version_p) |
596 | *version_p = version; | |
597 | } | |
6b200bc3 | 598 | CFReleaseNull(attributes); |
d8f41ccd | 599 | #if USE_KEYSTORE |
6b200bc3 A |
600 | CFReleaseNull(authenticated_attributes); |
601 | CFReleaseNull(caller_access_groups_data); | |
602 | CFReleaseNull(ed_data); | |
5c19dc3a | 603 | if (ref_key) aks_ref_key_free(&ref_key); |
d8f41ccd A |
604 | #endif |
605 | return ok; | |
606 | } | |
607 | ||
d8f41ccd A |
608 | static keyclass_t kc_parse_keyclass(CFTypeRef value, CFErrorRef *error) { |
609 | if (!isString(value)) { | |
610 | SecError(errSecParam, error, CFSTR("accessible attribute %@ not a string"), value); | |
611 | } else if (CFEqual(value, kSecAttrAccessibleWhenUnlocked)) { | |
612 | return key_class_ak; | |
613 | } else if (CFEqual(value, kSecAttrAccessibleAfterFirstUnlock)) { | |
614 | return key_class_ck; | |
fa7225c8 | 615 | } else if (CFEqual(value, kSecAttrAccessibleAlwaysPrivate)) { |
d8f41ccd A |
616 | return key_class_dk; |
617 | } else if (CFEqual(value, kSecAttrAccessibleWhenUnlockedThisDeviceOnly)) { | |
618 | return key_class_aku; | |
619 | } else if (CFEqual(value, kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly)) { | |
620 | return key_class_cku; | |
fa7225c8 | 621 | } else if (CFEqual(value, kSecAttrAccessibleAlwaysThisDeviceOnlyPrivate)) { |
d8f41ccd A |
622 | return key_class_dku; |
623 | } else if (CFEqual(value, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly)) { | |
624 | return key_class_akpu; | |
625 | } else { | |
626 | SecError(errSecParam, error, CFSTR("accessible attribute %@ unknown"), value); | |
627 | } | |
628 | return 0; | |
629 | } | |
630 | ||
631 | static CFTypeRef kc_encode_keyclass(keyclass_t keyclass) { | |
632 | switch (keyclass) { | |
633 | case key_class_ak: | |
634 | return kSecAttrAccessibleWhenUnlocked; | |
635 | case key_class_ck: | |
636 | return kSecAttrAccessibleAfterFirstUnlock; | |
637 | case key_class_dk: | |
fa7225c8 | 638 | return kSecAttrAccessibleAlwaysPrivate; |
d8f41ccd A |
639 | case key_class_aku: |
640 | return kSecAttrAccessibleWhenUnlockedThisDeviceOnly; | |
641 | case key_class_cku: | |
642 | return kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly; | |
643 | case key_class_dku: | |
fa7225c8 | 644 | return kSecAttrAccessibleAlwaysThisDeviceOnlyPrivate; |
d8f41ccd A |
645 | case key_class_akpu: |
646 | return kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly; | |
647 | default: | |
648 | return 0; | |
649 | } | |
650 | } | |
651 | ||
652 | #if USE_KEYSTORE | |
5c19dc3a A |
653 | static bool kc_attribs_key_encrypted_data_from_blob(keybag_handle_t keybag, const SecDbClass *class, const void *blob_data, size_t blob_data_len, SecAccessControlRef access_control, uint32_t version, |
654 | CFMutableDictionaryRef *authenticated_attributes, aks_ref_key_t *ref_key, CFDataRef *encrypted_data, CFErrorRef *error) | |
d8f41ccd | 655 | { |
b04fe171 | 656 | CFMutableDictionaryRef acl = NULL; |
5c19dc3a | 657 | CFDictionaryRef blob_dict = NULL; |
b04fe171 | 658 | aks_ref_key_t tmp_ref_key = NULL; |
5c19dc3a A |
659 | CFDataRef key_data = NULL; |
660 | CFDataRef ed = NULL; | |
b04fe171 | 661 | bool ok = false; |
5c19dc3a A |
662 | |
663 | der_decode_plist(NULL, kCFPropertyListImmutable, (CFPropertyListRef*)&blob_dict, NULL, blob_data, blob_data + blob_data_len); | |
664 | require_action_quiet(blob_dict, out, SecError(errSecDecode, error, CFSTR("kc_attribs_key_encrypted_data_from_blob: failed to decode 'blob data'"))); | |
665 | ||
666 | if (!ks_separate_data_and_key(blob_dict, &ed, &key_data)) { | |
667 | ed = CFDataCreate(kCFAllocatorDefault, blob_data, blob_data_len); | |
668 | key_data = CFRetain(ed); | |
669 | } | |
670 | require_action_quiet(ed, out, SecError(errSecDecode, error, CFSTR("kc_attribs_key_encrypted_data_from_blob: failed to decode 'encrypted data'"))); | |
671 | require_action_quiet(key_data, out, SecError(errSecDecode, error, CFSTR("kc_attribs_key_encrypted_data_from_blob: failed to decode 'key data'"))); | |
672 | ||
5c19dc3a A |
673 | const void *external_data = NULL; |
674 | size_t external_data_len = 0; | |
675 | require_quiet(external_data = ks_ref_key_get_external_data(keybag, key_data, &tmp_ref_key, &external_data_len, error), out); | |
676 | ||
677 | CFPropertyListRef external_data_dict = NULL; | |
678 | der_decode_plist(NULL, kCFPropertyListImmutable, &external_data_dict, NULL, external_data, external_data + external_data_len); | |
679 | require_action_quiet(external_data_dict, out, SecError(errSecDecode, error, CFSTR("kc_attribs_key_encrypted_data_from_blob: failed to decode 'encrypted data dictionary'"))); | |
680 | acl = CFDictionaryCreateMutableCopy(NULL, 0, external_data_dict); | |
681 | SecDbForEachAttrWithMask(class, attr_desc, kSecDbInAuthenticatedDataFlag) { | |
682 | CFDictionaryRemoveValue(acl, attr_desc->name); | |
683 | CFTypeRef value = CFDictionaryGetValue(external_data_dict, attr_desc->name); | |
684 | if (value) { | |
685 | if (!*authenticated_attributes) | |
686 | *authenticated_attributes = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | |
687 | ||
688 | CFDictionaryAddValue(*authenticated_attributes, attr_desc->name, value); | |
d8f41ccd A |
689 | } |
690 | } | |
5c19dc3a A |
691 | CFReleaseSafe(external_data_dict); |
692 | ||
693 | if (acl) { | |
694 | /* v4 data format used wrong ACL placement, for backward compatibility we have to support both formats */ | |
b04fe171 | 695 | if (version == 4) { |
5c19dc3a | 696 | SecAccessControlSetConstraints(access_control, acl); |
b04fe171 A |
697 | } else { |
698 | CFDictionaryRef constraints = CFDictionaryGetValue(acl, kAKSKeyAcl); | |
699 | require_action_quiet(isDictionary(constraints), out, | |
700 | SecError(errSecDecode, error, CFSTR("kc_attribs_key_encrypted_data_from_blob: acl missing"))); | |
701 | SecAccessControlSetConstraints(access_control, constraints); | |
702 | } | |
5c19dc3a A |
703 | |
704 | /* v4/v5 data format usualy does not contain kAKSKeyOpEncrypt, so add kAKSKeyOpEncrypt if is missing */ | |
705 | if (version < 6) { | |
706 | SecAccessConstraintRef encryptConstraint = SecAccessControlGetConstraint(access_control, kAKSKeyOpEncrypt); | |
707 | if (!encryptConstraint) | |
708 | SecAccessControlAddConstraintForOperation(access_control, kAKSKeyOpEncrypt, kCFBooleanTrue, NULL); | |
709 | } | |
710 | ||
5c19dc3a A |
711 | } |
712 | ||
713 | if (encrypted_data) | |
714 | *encrypted_data = CFRetain(ed); | |
715 | ||
716 | if (ref_key) { | |
717 | *ref_key = tmp_ref_key; | |
718 | tmp_ref_key = NULL; | |
719 | } | |
720 | ||
721 | ok = true; | |
722 | ||
723 | out: | |
724 | if (tmp_ref_key) | |
725 | aks_ref_key_free(&tmp_ref_key); | |
726 | CFReleaseSafe(blob_dict); | |
727 | CFReleaseSafe(key_data); | |
728 | CFReleaseSafe(ed); | |
b04fe171 A |
729 | CFReleaseSafe(acl); |
730 | ||
731 | ||
5c19dc3a | 732 | return ok; |
d8f41ccd A |
733 | } |
734 | ||
5c19dc3a | 735 | static CFDataRef kc_create_auth_data(SecAccessControlRef access_control, CFDictionaryRef auth_attributes) { |
d8f41ccd | 736 | CFDictionaryRef constraints = SecAccessControlGetConstraints(access_control); |
5c19dc3a A |
737 | CFMutableDictionaryRef auth_data = CFDictionaryCreateMutableCopy(NULL, 0, auth_attributes); |
738 | CFDictionarySetValue(auth_data, kAKSKeyAcl, constraints); | |
739 | CFDataRef encoded = CFPropertyListCreateDERData(kCFAllocatorDefault, auth_data, NULL); | |
d8f41ccd A |
740 | CFReleaseSafe(auth_data); |
741 | return encoded; | |
742 | } | |
743 | ||
744 | static CFDataRef kc_copy_access_groups_data(CFArrayRef access_groups, CFErrorRef *error) | |
745 | { | |
746 | size_t ag_size = der_sizeof_plist(access_groups, error); | |
747 | CFMutableDataRef result = CFDataCreateMutable(kCFAllocatorDefault, 0); | |
748 | CFDataSetLength(result, ag_size); | |
749 | if (!der_encode_plist(access_groups, error, CFDataGetMutableBytePtr(result), CFDataGetMutableBytePtr(result) + ag_size)) { | |
750 | CFRelease(result); | |
751 | return NULL; | |
752 | } | |
753 | else | |
754 | return result; | |
755 | } | |
756 | ||
757 | #endif /* USE_KEYSTORE */ | |
758 | ||
759 | static CFDataRef kc_copy_protection_data(SecAccessControlRef access_control) | |
760 | { | |
761 | CFTypeRef protection = SecAccessControlGetProtection(access_control); | |
762 | size_t protection_size = der_sizeof_plist(protection, NULL); | |
763 | CFMutableDataRef result = CFDataCreateMutable(NULL, 0); | |
764 | CFDataSetLength(result, protection_size); | |
765 | if (!der_encode_plist(protection, NULL, CFDataGetMutableBytePtr(result), CFDataGetMutableBytePtr(result) + protection_size)) { | |
766 | CFRelease(result); | |
767 | return NULL; | |
768 | } | |
769 | else | |
770 | return result; | |
771 | } | |
772 | ||
5c19dc3a | 773 | static CFTypeRef kc_copy_protection_from(const uint8_t *der, const uint8_t *der_end) |
d8f41ccd A |
774 | { |
775 | CFTypeRef result = NULL; | |
5c19dc3a | 776 | der_decode_plist(NULL, kCFPropertyListImmutable, &result, NULL, der, der_end); |
d8f41ccd A |
777 | return result; |
778 | } | |
779 | ||
780 | /* Return a (mutable) dictionary if plist is a dictionary, return NULL and set error otherwise. Does nothing if plist is already NULL. */ | |
781 | static CF_RETURNS_RETAINED | |
782 | CFMutableDictionaryRef dictionaryFromPlist(CFPropertyListRef plist CF_CONSUMED, CFErrorRef *error) { | |
783 | if (plist && !isDictionary(plist)) { | |
784 | CFStringRef typeName = CFCopyTypeIDDescription(CFGetTypeID((CFTypeRef)plist)); | |
785 | SecError(errSecDecode, error, CFSTR("plist is a %@, expecting a dictionary"), typeName); | |
786 | CFReleaseSafe(typeName); | |
787 | CFReleaseNull(plist); | |
788 | } | |
789 | return (CFMutableDictionaryRef)plist; | |
790 | } | |
791 | ||
792 | static CF_RETURNS_RETAINED | |
793 | CFMutableDictionaryRef s3dl_item_v2_decode(CFDataRef plain, CFErrorRef *error) { | |
794 | CFPropertyListRef item; | |
795 | item = CFPropertyListCreateWithData(0, plain, kCFPropertyListMutableContainers, NULL, error); | |
796 | return dictionaryFromPlist(item, error); | |
797 | } | |
798 | ||
5c19dc3a A |
799 | static const uint8_t* (^s3dl_item_v3_decode_repair_date)(CFAllocatorRef, CFOptionFlags, CFPropertyListRef*, CFErrorRef*, const uint8_t*, const uint8_t*) = |
800 | ^const uint8_t*(CFAllocatorRef allocator, CFOptionFlags mutability, CFPropertyListRef* pl, CFErrorRef *error, const uint8_t* der, const uint8_t *der_end) { | |
801 | if (error && CFEqualSafe(CFErrorGetDomain(*error), sSecDERErrorDomain) && CFErrorGetCode(*error) == kSecDERErrorUnknownEncoding) { | |
802 | CFAbsoluteTime date = 0; | |
803 | CFCalendarRef calendar = CFCalendarCreateWithIdentifier(allocator, kCFGregorianCalendar); | |
804 | CFTimeZoneRef tz = CFTimeZoneCreateWithTimeIntervalFromGMT(allocator, 0); | |
805 | CFCalendarSetTimeZone(calendar, tz); | |
806 | CFCalendarComposeAbsoluteTime(calendar, &date, "yMd", 2001, 3, 24); // random date for <rdar://problem/20458954> 15A143: can't recover keychain | |
807 | CFReleaseSafe(tz); | |
808 | CFReleaseSafe(calendar); | |
809 | ||
810 | *pl = CFDateCreate(allocator, date); | |
811 | if (NULL != *pl) { | |
812 | CFReleaseNull(*error); | |
813 | return der_end; | |
814 | } | |
815 | } | |
816 | return NULL; | |
817 | }; | |
818 | ||
d8f41ccd A |
819 | static CF_RETURNS_RETAINED |
820 | CFMutableDictionaryRef s3dl_item_v3_decode(CFDataRef plain, CFErrorRef *error) { | |
821 | CFPropertyListRef item = NULL; | |
5c19dc3a A |
822 | const uint8_t *der_beg = CFDataGetBytePtr(plain); |
823 | const uint8_t *der_end = der_beg + CFDataGetLength(plain); | |
824 | const uint8_t *der = der_decode_plist(0, kCFPropertyListMutableContainers, &item, error, der_beg, der_end); | |
825 | if (!der && error && CFEqualSafe(CFErrorGetDomain(*error), sSecDERErrorDomain) && CFErrorGetCode(*error) == kSecDERErrorUnknownEncoding) { | |
826 | CFReleaseNull(*error); | |
827 | der = der_decode_plist_with_repair(0, kCFPropertyListMutableContainers, &item, error, der_beg, der_end, s3dl_item_v3_decode_repair_date); | |
828 | } | |
d8f41ccd A |
829 | if (der && der != der_end) { |
830 | SecCFCreateError(errSecDecode, kSecErrorDomain, CFSTR("trailing garbage at end of decrypted item"), NULL, error); | |
831 | CFReleaseNull(item); | |
832 | } | |
833 | return dictionaryFromPlist(item, error); | |
834 | } | |
835 | ||
836 | bool s3dl_item_from_data(CFDataRef edata, Query *q, CFArrayRef accessGroups, | |
837 | CFMutableDictionaryRef *item, SecAccessControlRef *access_control, CFErrorRef *error) { | |
838 | SecAccessControlRef ac = NULL; | |
839 | CFDataRef ac_data = NULL; | |
840 | bool ok = false; | |
841 | ||
842 | /* Decrypt and decode the item and check the decoded attributes against the query. */ | |
843 | uint32_t version = 0; | |
5c19dc3a A |
844 | require_quiet((ok = ks_decrypt_data(q->q_keybag, kAKSKeyOpDecrypt, &ac, q->q_use_cred_handle, edata, q->q_class, |
845 | q->q_caller_access_groups, item, &version, error)), out); | |
d8f41ccd A |
846 | if (version < 2) { |
847 | goto out; | |
848 | } | |
849 | ||
850 | ac_data = SecAccessControlCopyData(ac); | |
5c19dc3a | 851 | if (!itemInAccessGroup(*item, accessGroups)) { |
d8f41ccd A |
852 | secerror("items accessGroup %@ not in %@", |
853 | CFDictionaryGetValue(*item, kSecAttrAccessGroup), | |
854 | accessGroups); | |
855 | ok = SecError(errSecDecode, error, CFSTR("items accessGroup %@ not in %@"), | |
856 | CFDictionaryGetValue(*item, kSecAttrAccessGroup), | |
857 | accessGroups); | |
858 | CFReleaseNull(*item); | |
859 | } | |
860 | ||
861 | /* AccessControl attribute does not exist in the db, so synthesize it. */ | |
5c19dc3a | 862 | if (version > 3) |
d8f41ccd | 863 | CFDictionarySetValue(*item, kSecAttrAccessControl, ac_data); |
d8f41ccd A |
864 | |
865 | /* TODO: Validate access_control attribute. */ | |
866 | ||
867 | out: | |
868 | if (access_control) | |
869 | *access_control = CFRetainSafe(ac); | |
870 | CFReleaseSafe(ac); | |
871 | CFReleaseSafe(ac_data); | |
872 | return ok; | |
873 | } | |
874 | ||
875 | /* Infer accessibility and access group for pre-v2 (iOS4.x and earlier) items | |
876 | being imported from a backup. */ | |
877 | static bool SecDbItemImportMigrate(SecDbItemRef item, CFErrorRef *error) { | |
878 | bool ok = true; | |
879 | CFStringRef agrp = SecDbItemGetCachedValueWithName(item, kSecAttrAccessGroup); | |
880 | CFStringRef accessible = SecDbItemGetCachedValueWithName(item, kSecAttrAccessible); | |
881 | ||
882 | if (!isString(agrp) || !isString(accessible)) | |
883 | return ok; | |
866f8763 | 884 | if (SecDbItemGetClass(item) == genp_class() && CFEqual(accessible, kSecAttrAccessibleAlwaysPrivate)) { |
d8f41ccd A |
885 | CFStringRef svce = SecDbItemGetCachedValueWithName(item, kSecAttrService); |
886 | if (!isString(svce)) return ok; | |
887 | if (CFEqual(agrp, CFSTR("apple"))) { | |
888 | if (CFEqual(svce, CFSTR("AirPort"))) { | |
889 | ok = SecDbItemSetValueWithName(item, kSecAttrAccessible, kSecAttrAccessibleAfterFirstUnlock, error); | |
890 | } else if (CFEqual(svce, CFSTR("com.apple.airplay.password"))) { | |
891 | ok = SecDbItemSetValueWithName(item, kSecAttrAccessible, kSecAttrAccessibleWhenUnlocked, error); | |
892 | } else if (CFEqual(svce, CFSTR("YouTube"))) { | |
893 | ok = (SecDbItemSetValueWithName(item, kSecAttrAccessible, kSecAttrAccessibleWhenUnlocked, error) && | |
894 | SecDbItemSetValueWithName(item, kSecAttrAccessGroup, CFSTR("com.apple.youtube.credentials"), error)); | |
895 | } else { | |
896 | CFStringRef desc = SecDbItemGetCachedValueWithName(item, kSecAttrDescription); | |
897 | if (!isString(desc)) return ok; | |
898 | if (CFEqual(desc, CFSTR("IPSec Shared Secret")) || CFEqual(desc, CFSTR("PPP Password"))) { | |
899 | ok = SecDbItemSetValueWithName(item, kSecAttrAccessible, kSecAttrAccessibleAfterFirstUnlock, error); | |
900 | } | |
901 | } | |
902 | } | |
866f8763 | 903 | } else if (SecDbItemGetClass(item) == inet_class() && CFEqual(accessible, kSecAttrAccessibleAlwaysPrivate)) { |
d8f41ccd A |
904 | if (CFEqual(agrp, CFSTR("PrintKitAccessGroup"))) { |
905 | ok = SecDbItemSetValueWithName(item, kSecAttrAccessible, kSecAttrAccessibleWhenUnlocked, error); | |
906 | } else if (CFEqual(agrp, CFSTR("apple"))) { | |
907 | CFTypeRef ptcl = SecDbItemGetCachedValueWithName(item, kSecAttrProtocol); | |
908 | bool is_proxy = false; | |
909 | if (isNumber(ptcl)) { | |
910 | SInt32 iptcl; | |
911 | CFNumberGetValue(ptcl, kCFNumberSInt32Type, &iptcl); | |
912 | is_proxy = (iptcl == FOUR_CHAR_CODE('htpx') || | |
913 | iptcl == FOUR_CHAR_CODE('htsx') || | |
914 | iptcl == FOUR_CHAR_CODE('ftpx') || | |
915 | iptcl == FOUR_CHAR_CODE('rtsx') || | |
916 | iptcl == FOUR_CHAR_CODE('xpth') || | |
917 | iptcl == FOUR_CHAR_CODE('xsth') || | |
918 | iptcl == FOUR_CHAR_CODE('xptf') || | |
919 | iptcl == FOUR_CHAR_CODE('xstr')); | |
920 | } else if (isString(ptcl)) { | |
921 | is_proxy = (CFEqual(ptcl, kSecAttrProtocolHTTPProxy) || | |
922 | CFEqual(ptcl, kSecAttrProtocolHTTPSProxy) || | |
923 | CFEqual(ptcl, kSecAttrProtocolRTSPProxy) || | |
924 | CFEqual(ptcl, kSecAttrProtocolFTPProxy)); | |
925 | } | |
926 | if (is_proxy) | |
927 | ok = SecDbItemSetValueWithName(item, kSecAttrAccessible, kSecAttrAccessibleWhenUnlocked, error); | |
928 | } | |
929 | } | |
930 | return ok; | |
931 | } | |
932 | ||
5c19dc3a | 933 | bool SecDbItemDecrypt(SecDbItemRef item, CFDataRef edata, CFErrorRef *error) { |
d8f41ccd A |
934 | bool ok = true; |
935 | CFMutableDictionaryRef dict = NULL; | |
936 | SecAccessControlRef access_control = NULL; | |
937 | uint32_t version = 0; | |
938 | ||
5c19dc3a A |
939 | require_quiet(ok = ks_decrypt_data(SecDbItemGetKeybag(item), item->cryptoOp, &access_control, item->credHandle, edata, |
940 | item->class, item->callerAccessGroups, &dict, &version, error), out); | |
d8f41ccd | 941 | |
5c19dc3a A |
942 | if (version < 2) { |
943 | /* Old V4 style keychain backup being imported. */ | |
944 | ok = SecDbItemSetValueWithName(item, CFSTR("v_Data"), CFDictionaryGetValue(dict, CFSTR("v_Data")), error) && | |
945 | SecDbItemImportMigrate(item, error); | |
d8f41ccd | 946 | } else { |
5c19dc3a | 947 | ok = dict && SecDbItemSetValues(item, dict, error); |
d8f41ccd A |
948 | } |
949 | ||
950 | SecAccessControlRef my_access_control = SecDbItemCopyAccessControl(item, error); | |
951 | if (!my_access_control) { | |
952 | ok = false; | |
953 | goto out; | |
954 | } | |
955 | ||
956 | /* Make sure that the protection of ACL in the dictionary (read from DB) matched what we got | |
957 | back from decoding the data blob. */ | |
958 | if (!CFEqual(SecAccessControlGetProtection(my_access_control), SecAccessControlGetProtection(access_control))) { | |
959 | ok = SecError(errSecDecode, error, CFSTR("ACL protection doesn't match the one in blob (%@ : %@)"), | |
960 | SecAccessControlGetProtection(my_access_control), | |
961 | SecAccessControlGetProtection(access_control)); | |
962 | } | |
963 | CFRelease(my_access_control); | |
964 | ||
d8f41ccd | 965 | out: |
5c19dc3a A |
966 | // If we got protection back from ks_decrypt_data, update the appropriate attribute even if anything else |
967 | // (incl. actual decryption) failed. We need to access the protection type even if we are not able to actually | |
968 | // decrypt the data. | |
969 | ok = SecDbItemSetAccessControl(item, access_control, NULL) && ok; | |
970 | ||
d8f41ccd A |
971 | CFReleaseSafe(dict); |
972 | CFReleaseSafe(access_control); | |
973 | return ok; | |
974 | } | |
975 | ||
976 | /* Automagically make a item syncable, based on various attributes. */ | |
977 | bool SecDbItemInferSyncable(SecDbItemRef item, CFErrorRef *error) | |
978 | { | |
979 | CFStringRef agrp = SecDbItemGetCachedValueWithName(item, kSecAttrAccessGroup); | |
980 | ||
981 | if (!isString(agrp)) | |
982 | return true; | |
983 | ||
866f8763 | 984 | if (CFEqual(agrp, CFSTR("com.apple.cfnetwork")) && SecDbItemGetClass(item) == inet_class()) { |
d8f41ccd A |
985 | CFTypeRef srvr = SecDbItemGetCachedValueWithName(item, kSecAttrServer); |
986 | CFTypeRef ptcl = SecDbItemGetCachedValueWithName(item, kSecAttrProtocol); | |
987 | CFTypeRef atyp = SecDbItemGetCachedValueWithName(item, kSecAttrAuthenticationType); | |
988 | ||
989 | if (isString(srvr) && isString(ptcl) && isString(atyp)) { | |
990 | /* This looks like a Mobile Safari Password, make syncable */ | |
991 | secnotice("item", "Make this item syncable: %@", item); | |
992 | return SecDbItemSetSyncable(item, true, error); | |
993 | } | |
994 | } | |
995 | ||
996 | return true; | |
997 | } | |
998 | ||
999 | /* This create a SecDbItem from the item dictionnary that are exported for backups. | |
1000 | Item are stored in the backup as a dictionary containing two keys: | |
1001 | - v_Data: the encrypted data blob | |
1002 | - v_PersistentRef: a persistent Ref. | |
1003 | src_keybag is normally the backup keybag. | |
1004 | dst_keybag is normally the device keybag. | |
1005 | */ | |
1006 | SecDbItemRef SecDbItemCreateWithBackupDictionary(CFAllocatorRef allocator, const SecDbClass *dbclass, CFDictionaryRef dict, keybag_handle_t src_keybag, keybag_handle_t dst_keybag, CFErrorRef *error) | |
1007 | { | |
1008 | CFDataRef edata = CFDictionaryGetValue(dict, CFSTR("v_Data")); | |
1009 | SecDbItemRef item = NULL; | |
1010 | ||
1011 | if (edata) { | |
1012 | item = SecDbItemCreateWithEncryptedData(kCFAllocatorDefault, dbclass, edata, src_keybag, error); | |
1013 | if (item) | |
1014 | if (!SecDbItemSetKeybag(item, dst_keybag, error)) | |
1015 | CFReleaseNull(item); | |
1016 | } else { | |
1017 | SecError(errSecDecode, error, CFSTR("No v_Data in backup dictionary %@"), dict); | |
1018 | } | |
1019 | ||
1020 | return item; | |
1021 | } | |
1022 | ||
1023 | bool SecDbItemExtractRowIdFromBackupDictionary(SecDbItemRef item, CFDictionaryRef dict, CFErrorRef *error) { | |
1024 | CFDataRef ref = CFDictionaryGetValue(dict, CFSTR("v_PersistentRef")); | |
1025 | if (!ref) | |
1026 | return SecError(errSecDecode, error, CFSTR("No v_PersistentRef in backup dictionary %@"), dict); | |
1027 | ||
1028 | CFStringRef className; | |
1029 | sqlite3_int64 rowid; | |
866f8763 | 1030 | if (!_SecItemParsePersistentRef(ref, &className, &rowid, NULL)) |
d8f41ccd A |
1031 | return SecError(errSecDecode, error, CFSTR("v_PersistentRef %@ failed to decode"), ref); |
1032 | ||
1033 | if (!CFEqual(SecDbItemGetClass(item)->name, className)) | |
1034 | return SecError(errSecDecode, error, CFSTR("v_PersistentRef has unexpected class %@"), className); | |
1035 | ||
1036 | return SecDbItemSetRowId(item, rowid, error); | |
1037 | } | |
5c19dc3a A |
1038 | |
1039 | static CFDataRef SecDbItemCopyDERWithMask(SecDbItemRef item, CFOptionFlags mask, CFErrorRef *error) { | |
1040 | CFDataRef der = NULL; | |
1041 | CFMutableDictionaryRef dict = SecDbItemCopyPListWithMask(item, mask, error); | |
1042 | if (dict) { | |
1043 | der = CFPropertyListCreateDERData(kCFAllocatorDefault, dict, error); | |
1044 | CFRelease(dict); | |
1045 | } | |
1046 | return der; | |
1047 | } | |
1048 | ||
1049 | static CFTypeRef SecDbItemCopyDigestWithMask(SecDbItemRef item, CFOptionFlags mask, CFErrorRef *error) { | |
1050 | CFDataRef digest = NULL; | |
1051 | CFDataRef der = SecDbItemCopyDERWithMask(item, mask, error); | |
1052 | if (der) { | |
1053 | digest = CFDataCopySHA1Digest(der, error); | |
1054 | CFRelease(der); | |
1055 | } | |
1056 | return digest; | |
1057 | } | |
1058 | ||
866f8763 A |
1059 | static CFTypeRef SecDbItemCopySHA256DigestWithMask(SecDbItemRef item, CFOptionFlags mask, CFErrorRef *error) { |
1060 | CFDataRef digest = NULL; | |
1061 | CFDataRef der = SecDbItemCopyDERWithMask(item, mask, error); | |
1062 | if (der) { | |
1063 | digest = CFDataCopySHA256Digest(der, error); | |
1064 | CFRelease(der); | |
1065 | } | |
1066 | return digest; | |
1067 | } | |
1068 | ||
5c19dc3a A |
1069 | CFTypeRef SecDbKeychainItemCopyPrimaryKey(SecDbItemRef item, const SecDbAttr *attr, CFErrorRef *error) { |
1070 | return SecDbItemCopyDigestWithMask(item, kSecDbPrimaryKeyFlag, error); | |
1071 | } | |
1072 | ||
866f8763 A |
1073 | CFTypeRef SecDbKeychainItemCopySHA256PrimaryKey(SecDbItemRef item, CFErrorRef *error) { |
1074 | return SecDbItemCopySHA256DigestWithMask(item, kSecDbPrimaryKeyFlag, error); | |
1075 | } | |
1076 | ||
5c19dc3a A |
1077 | CFTypeRef SecDbKeychainItemCopySHA1(SecDbItemRef item, const SecDbAttr *attr, CFErrorRef *error) { |
1078 | return SecDbItemCopyDigestWithMask(item, kSecDbInHashFlag, error); | |
1079 | } | |
1080 | ||
1081 | CFTypeRef SecDbKeychainItemCopyEncryptedData(SecDbItemRef item, const SecDbAttr *attr, CFErrorRef *error) { | |
1082 | CFDataRef edata = NULL; | |
1083 | CFMutableDictionaryRef attributes = SecDbItemCopyPListWithMask(item, kSecDbInCryptoDataFlag, error); | |
1084 | CFMutableDictionaryRef auth_attributes = SecDbItemCopyPListWithMask(item, kSecDbInAuthenticatedDataFlag, error); | |
1085 | if (attributes || auth_attributes) { | |
1086 | SecAccessControlRef access_control = SecDbItemCopyAccessControl(item, error); | |
1087 | if (access_control) { | |
fa7225c8 | 1088 | if (ks_encrypt_data(item->keybag, access_control, item->credHandle, attributes, auth_attributes, &edata, true, error)) { |
5c19dc3a A |
1089 | item->_edataState = kSecDbItemEncrypting; |
1090 | } else if (!error || !*error || CFErrorGetCode(*error) != errSecAuthNeeded || !CFEqualSafe(CFErrorGetDomain(*error), kSecErrorDomain) ) { | |
1091 | seccritical("ks_encrypt_data (db): failed: %@", error ? *error : (CFErrorRef)CFSTR("")); | |
1092 | } | |
1093 | CFRelease(access_control); | |
1094 | } | |
1095 | CFReleaseSafe(attributes); | |
1096 | CFReleaseSafe(auth_attributes); | |
1097 | } | |
1098 | ||
1099 | return edata; | |
1100 | } | |
1101 | ||
1102 | CFTypeRef SecDbKeychainItemCopyCurrentDate(SecDbItemRef item, const SecDbAttr *attr, CFErrorRef *error) { | |
1103 | CFTypeRef value = NULL; | |
1104 | switch (attr->kind) { | |
1105 | case kSecDbDateAttr: | |
1106 | value = CFDateCreate(kCFAllocatorDefault, 0.0); | |
1107 | break; | |
1108 | case kSecDbCreationDateAttr: | |
1109 | case kSecDbModificationDateAttr: | |
1110 | value = CFDateCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent()); | |
1111 | break; | |
1112 | default: | |
1113 | SecError(errSecInternal, error, CFSTR("attr %@ has no default value"), attr->name); | |
1114 | value = NULL; | |
1115 | } | |
1116 | ||
1117 | return value; | |
1118 | } | |
1119 | ||
1120 | SecAccessControlRef SecDbItemCopyAccessControl(SecDbItemRef item, CFErrorRef *error) { | |
1121 | SecAccessControlRef accc = NULL, pdmn = NULL, result = NULL; | |
1122 | CFTypeRef acccData = SecDbItemGetValue(item, SecDbClassAttrWithKind(item->class, kSecDbAccessControlAttr, error), error); | |
1123 | CFTypeRef pdmnValue = SecDbItemGetValue(item, SecDbClassAttrWithKind(item->class, kSecDbAccessAttr, error), error); | |
1124 | ||
1125 | if (!acccData || !pdmnValue) | |
1126 | return NULL; | |
1127 | if (!CFEqual(acccData, kCFNull)) | |
1128 | require_quiet(accc = SecAccessControlCreateFromData(CFGetAllocator(item), acccData, error), out); | |
1129 | ||
1130 | if (!CFEqual(pdmnValue, kCFNull)) { | |
1131 | require_quiet(pdmn = SecAccessControlCreate(CFGetAllocator(item), error), out); | |
1132 | require_quiet(SecAccessControlSetProtection(pdmn, pdmnValue, error), out); | |
1133 | } | |
1134 | ||
1135 | if (accc && pdmn) { | |
1136 | CFTypeRef acccProt = SecAccessControlGetProtection(accc); | |
1137 | CFTypeRef pdmnProt = SecAccessControlGetProtection(pdmn); | |
1138 | if (!acccProt || !pdmnProt || !CFEqual(acccProt, pdmnProt)) { | |
1139 | secerror("SecDbItemCopyAccessControl accc %@ != pdmn %@, setting pdmn to accc value", acccProt, pdmnProt); | |
1140 | __security_simulatecrash(CFSTR("Corrupted item on decrypt accc != pdmn"), __sec_exception_code_CorruptItem); | |
1141 | // Setting pdmn to accc prot value. | |
1142 | require_quiet(SecDbItemSetValue(item, SecDbClassAttrWithKind(item->class, kSecDbAccessAttr, error), acccProt, error), out); | |
1143 | } | |
1144 | } | |
1145 | ||
1146 | if (accc) | |
1147 | CFRetainAssign(result, accc); | |
1148 | else if(pdmn) | |
1149 | CFRetainAssign(result, pdmn); | |
1150 | ||
1151 | out: | |
1152 | CFReleaseSafe(accc); | |
1153 | CFReleaseSafe(pdmn); | |
1154 | ||
1155 | return result; | |
1156 | } | |
1157 | ||
1158 | static const uint8_t* der_decode_plist_with_repair(CFAllocatorRef allocator, CFOptionFlags mutability, | |
1159 | CFPropertyListRef* pl, CFErrorRef *error, | |
1160 | const uint8_t* der, const uint8_t *der_end, | |
1161 | const uint8_t* (^repairBlock)(CFAllocatorRef, CFOptionFlags, CFPropertyListRef*, CFErrorRef*, | |
1162 | const uint8_t*, const uint8_t*)) | |
1163 | { | |
1164 | if (NULL == der) { | |
1165 | SecCFDERCreateError(kSecDERErrorUnknownEncoding, CFSTR("Null DER"), NULL, error); | |
1166 | return NULL; | |
1167 | } | |
1168 | ||
1169 | ccder_tag tag; | |
1170 | if (NULL == ccder_decode_tag(&tag, der, der_end)) { | |
1171 | SecCFDERCreateError(kSecDERErrorUnknownEncoding, CFSTR("Unknown data encoding"), NULL, error); | |
1172 | return NULL; | |
1173 | } | |
1174 | ||
1175 | switch (tag) { | |
1176 | case CCDER_NULL: | |
1177 | return der_decode_null(allocator, mutability, (CFNullRef*)pl, error, der, der_end); | |
1178 | case CCDER_BOOLEAN: | |
1179 | return der_decode_boolean(allocator, mutability, (CFBooleanRef*)pl, error, der, der_end); | |
1180 | case CCDER_OCTET_STRING: | |
1181 | return der_decode_data(allocator, mutability, (CFDataRef*)pl, error, der, der_end); | |
1182 | case CCDER_GENERALIZED_TIME: { | |
1183 | const uint8_t* der_result = der_decode_date(allocator, mutability, (CFDateRef*)pl, error, der, der_end); | |
1184 | if (!der_result) { | |
1185 | der_result = repairBlock(allocator, mutability, pl, error, der, der_end); | |
1186 | } | |
1187 | return der_result; | |
1188 | } | |
1189 | case CCDER_CONSTRUCTED_SEQUENCE: | |
1190 | return der_decode_array_with_repair(allocator, mutability, (CFArrayRef*)pl, error, der, der_end, repairBlock); | |
1191 | case CCDER_UTF8_STRING: | |
1192 | return der_decode_string(allocator, mutability, (CFStringRef*)pl, error, der, der_end); | |
1193 | case CCDER_INTEGER: | |
1194 | return der_decode_number(allocator, mutability, (CFNumberRef*)pl, error, der, der_end); | |
1195 | case CCDER_CONSTRUCTED_SET: | |
1196 | return der_decode_dictionary_with_repair(allocator, mutability, (CFDictionaryRef*)pl, error, der, der_end, repairBlock); | |
1197 | case CCDER_CONSTRUCTED_CFSET: | |
1198 | return der_decode_set_with_repair(allocator, mutability, (CFSetRef*)pl, error, der, der_end, repairBlock); | |
1199 | default: | |
1200 | SecCFDERCreateError(kSecDERErrorUnsupportedDERType, CFSTR("Unsupported DER Type"), NULL, error); | |
1201 | return NULL; | |
1202 | } | |
1203 | } | |
1204 | ||
1205 | static const uint8_t* der_decode_dictionary_with_repair(CFAllocatorRef allocator, CFOptionFlags mutability, | |
1206 | CFDictionaryRef* dictionary, CFErrorRef *error, | |
1207 | const uint8_t* der, const uint8_t *der_end, | |
1208 | const uint8_t* (^repairBlock)(CFAllocatorRef, CFOptionFlags, CFPropertyListRef*, CFErrorRef*, | |
1209 | const uint8_t*, const uint8_t*)) | |
1210 | { | |
1211 | if (NULL == der) { | |
1212 | SecCFDERCreateError(kSecDERErrorUnknownEncoding, CFSTR("Null DER"), NULL, error); | |
1213 | return NULL; | |
1214 | } | |
1215 | ||
1216 | const uint8_t *payload_end = 0; | |
1217 | const uint8_t *payload = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SET, &payload_end, der, der_end); | |
1218 | ||
1219 | if (NULL == payload) { | |
1220 | SecCFDERCreateError(kSecDERErrorUnknownEncoding, CFSTR("Unknown data encoding, expected CCDER_CONSTRUCTED_SET"), NULL, error); | |
1221 | return NULL; | |
1222 | } | |
1223 | ||
1224 | ||
1225 | CFMutableDictionaryRef dict = CFDictionaryCreateMutable(allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | |
1226 | ||
1227 | if (NULL == dict) { | |
1228 | SecCFDERCreateError(kSecDERErrorAllocationFailure, CFSTR("Failed to create dictionary"), NULL, error); | |
1229 | payload = NULL; | |
1230 | goto exit; | |
1231 | } | |
1232 | ||
1233 | while (payload != NULL && payload < payload_end) { | |
1234 | CFTypeRef key = NULL; | |
1235 | CFTypeRef value = NULL; | |
1236 | ||
1237 | payload = der_decode_key_value_with_repair(allocator, mutability, &key, &value, error, payload, payload_end, repairBlock); | |
1238 | ||
1239 | if (payload) { | |
1240 | CFDictionaryAddValue(dict, key, value); | |
1241 | } | |
1242 | ||
1243 | CFReleaseNull(key); | |
1244 | CFReleaseNull(value); | |
1245 | } | |
1246 | ||
1247 | ||
1248 | exit: | |
1249 | if (payload == payload_end) { | |
1250 | *dictionary = dict; | |
1251 | dict = NULL; | |
1252 | } | |
1253 | ||
1254 | CFReleaseNull(dict); | |
1255 | ||
1256 | return payload; | |
1257 | } | |
1258 | ||
1259 | static const uint8_t* der_decode_key_value_with_repair(CFAllocatorRef allocator, CFOptionFlags mutability, | |
1260 | CFPropertyListRef* key, CFPropertyListRef* value, CFErrorRef *error, | |
1261 | const uint8_t* der, const uint8_t *der_end, | |
1262 | const uint8_t* (^repairBlock)(CFAllocatorRef, CFOptionFlags, CFPropertyListRef*, CFErrorRef*, | |
1263 | const uint8_t*, const uint8_t*)) | |
1264 | { | |
1265 | const uint8_t *payload_end = 0; | |
1266 | const uint8_t *payload = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &payload_end, der, der_end); | |
1267 | ||
1268 | if (NULL == payload) { | |
1269 | SecCFDERCreateError(kSecDERErrorUnknownEncoding, CFSTR("Unknown data encoding, expected CCDER_CONSTRUCTED_SEQUENCE"), NULL, error); | |
1270 | return NULL; | |
1271 | } | |
1272 | ||
1273 | CFTypeRef keyObject = NULL; | |
1274 | CFTypeRef valueObject = NULL; | |
1275 | ||
1276 | ||
1277 | payload = der_decode_plist_with_repair(allocator, mutability, &keyObject, error, payload, payload_end, repairBlock); | |
1278 | payload = der_decode_plist_with_repair(allocator, mutability, &valueObject, error, payload, payload_end, repairBlock); | |
1279 | ||
1280 | if (payload != NULL) { | |
1281 | *key = keyObject; | |
1282 | *value = valueObject; | |
1283 | } else { | |
1284 | CFReleaseNull(keyObject); | |
1285 | CFReleaseNull(valueObject); | |
1286 | } | |
1287 | return payload; | |
1288 | } | |
1289 | ||
1290 | static const uint8_t* der_decode_array_with_repair(CFAllocatorRef allocator, CFOptionFlags mutability, | |
1291 | CFArrayRef* array, CFErrorRef *error, | |
1292 | const uint8_t* der, const uint8_t *der_end, | |
1293 | const uint8_t* (^repairBlock)(CFAllocatorRef, CFOptionFlags, CFPropertyListRef*, CFErrorRef*, | |
1294 | const uint8_t*, const uint8_t*)) | |
1295 | { | |
1296 | if (NULL == der) { | |
1297 | SecCFDERCreateError(kSecDERErrorUnknownEncoding, CFSTR("Null DER"), NULL, error); | |
1298 | return NULL; | |
1299 | } | |
1300 | ||
1301 | CFMutableArrayRef result = CFArrayCreateMutable(allocator, 0, &kCFTypeArrayCallBacks); | |
1302 | ||
1303 | const uint8_t *elements_end; | |
1304 | const uint8_t *current_element = ccder_decode_sequence_tl(&elements_end, der, der_end); | |
1305 | ||
1306 | while (current_element != NULL && current_element < elements_end) { | |
1307 | CFPropertyListRef element = NULL; | |
1308 | current_element = der_decode_plist_with_repair(allocator, mutability, &element, error, current_element, elements_end, repairBlock); | |
1309 | if (current_element) { | |
1310 | CFArrayAppendValue(result, element); | |
1311 | CFReleaseNull(element); | |
1312 | } | |
1313 | } | |
1314 | ||
1315 | if (current_element) { | |
1316 | *array = result; | |
1317 | result = NULL; | |
1318 | } | |
1319 | ||
1320 | CFReleaseNull(result); | |
1321 | return current_element; | |
1322 | } | |
1323 | ||
1324 | static const uint8_t* der_decode_set_with_repair(CFAllocatorRef allocator, CFOptionFlags mutability, | |
1325 | CFSetRef* set, CFErrorRef *error, | |
1326 | const uint8_t* der, const uint8_t *der_end, | |
1327 | const uint8_t* (^repairBlock)(CFAllocatorRef, CFOptionFlags, CFPropertyListRef*, CFErrorRef*, | |
1328 | const uint8_t*, const uint8_t*)) | |
1329 | { | |
1330 | if (NULL == der) { | |
1331 | SecCFDERCreateError(kSecDERErrorUnknownEncoding, CFSTR("Null DER"), NULL, error); | |
1332 | return NULL; | |
1333 | } | |
1334 | ||
1335 | const uint8_t *payload_end = 0; | |
1336 | const uint8_t *payload = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_CFSET, &payload_end, der, der_end); | |
1337 | ||
1338 | if (NULL == payload) { | |
1339 | SecCFDERCreateError(kSecDERErrorUnknownEncoding, CFSTR("Unknown data encoding, expected CCDER_CONSTRUCTED_CFSET"), NULL, error); | |
1340 | return NULL; | |
1341 | } | |
1342 | ||
1343 | CFMutableSetRef theSet = (set && *set) ? CFSetCreateMutableCopy(allocator, 0, *set) | |
1344 | : CFSetCreateMutable(allocator, 0, &kCFTypeSetCallBacks); | |
1345 | ||
1346 | if (NULL == theSet) { | |
1347 | SecCFDERCreateError(kSecDERErrorAllocationFailure, CFSTR("Failed to create set"), NULL, error); | |
1348 | payload = NULL; | |
1349 | goto exit; | |
1350 | } | |
1351 | ||
1352 | while (payload != NULL && payload < payload_end) { | |
1353 | CFTypeRef value = NULL; | |
1354 | ||
1355 | payload = der_decode_plist_with_repair(allocator, mutability, &value, error, payload, payload_end, repairBlock); | |
1356 | ||
1357 | if (payload) { | |
1358 | CFSetAddValue(theSet, value); | |
1359 | } | |
1360 | CFReleaseNull(value); | |
1361 | } | |
1362 | ||
1363 | ||
1364 | exit: | |
1365 | if (set && payload == payload_end) { | |
1366 | CFTransferRetained(*set, theSet); | |
1367 | } | |
1368 | ||
1369 | CFReleaseNull(theSet); | |
1370 | ||
1371 | return payload; | |
1372 | } |