]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (c) 2015 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 | // Our Header | |
26 | ||
27 | #include <Security/SecureObjectSync/SOSBackupSliceKeyBag.h> | |
28 | ||
29 | ||
30 | // Needed headers for implementation | |
31 | #include "AssertMacros.h" | |
32 | #include <utilities/SecCFWrappers.h> | |
33 | #include <utilities/SecAKSWrappers.h> | |
34 | #include <utilities/SecBuffer.h> | |
35 | #include <utilities/SecCFError.h> | |
36 | #include <utilities/der_set.h> | |
37 | #include <utilities/der_plist_internal.h> | |
38 | #include <Security/SecRandom.h> | |
39 | #include <Security/SecureObjectSync/SOSPeerInfo.h> | |
40 | #include <Security/SecureObjectSync/SOSPeerInfoCollections.h> | |
41 | #include <Security/SecureObjectSync/SOSInternal.h> | |
42 | ||
43 | #include <corecrypto/ccec.h> | |
44 | #include <corecrypto/ccsha2.h> | |
45 | #include <corecrypto/ccrng.h> | |
46 | ||
47 | #include <limits.h> | |
48 | ||
49 | #include "SecRecoveryKey.h" | |
50 | #include "SOSKeyedPubKeyIdentifier.h" | |
51 | #include "SOSInternal.h" | |
52 | #include "SecADWrapper.h" | |
53 | ||
54 | CFStringRef bskbRkbgPrefix = CFSTR("RK"); | |
55 | ||
56 | // | |
57 | // MARK: Type creation | |
58 | // | |
59 | ||
60 | CFGiblisFor(SOSBackupSliceKeyBag); | |
61 | ||
62 | const int kAKSBagSecretLength = 32; | |
63 | ||
64 | struct __OpaqueSOSBackupSliceKeyBag { | |
65 | CFRuntimeBase _base; | |
66 | ||
67 | CFDataRef aks_bag; | |
68 | ||
69 | CFSetRef peers; | |
70 | CFDictionaryRef wrapped_keys; | |
71 | }; | |
72 | ||
73 | static void SOSBackupSliceKeyBagDestroy(CFTypeRef aObj) { | |
74 | SOSBackupSliceKeyBagRef vb = (SOSBackupSliceKeyBagRef) aObj; | |
75 | ||
76 | CFReleaseNull(vb->aks_bag); | |
77 | CFReleaseNull(vb->peers); | |
78 | CFReleaseNull(vb->wrapped_keys); | |
79 | } | |
80 | ||
81 | static CFSetRef SOSBackupSliceKeyBagCopyPeerNames(SOSBackupSliceKeyBagRef bksb) { | |
82 | CFMutableSetRef retval = CFSetCreateMutableForCFTypes(kCFAllocatorDefault); | |
83 | if(!retval) return NULL; | |
84 | CFSetForEach(bksb->peers, ^(const void *value) { | |
85 | SOSPeerInfoRef pi = (SOSPeerInfoRef) value; | |
86 | CFSetAddValue(retval, SOSPeerInfoGetPeerName(pi)); | |
87 | }); | |
88 | return retval; | |
89 | } | |
90 | ||
91 | static CFStringRef SOSBackupSliceKeyBagCopyFormatDescription(CFTypeRef aObj, CFDictionaryRef formatOptions) { | |
92 | SOSBackupSliceKeyBagRef vb = (SOSBackupSliceKeyBagRef) aObj; | |
93 | ||
94 | CFMutableStringRef retval = CFStringCreateMutable(kCFAllocatorDefault, 0); | |
95 | ||
96 | CFSetRef peerIDs = SOSBackupSliceKeyBagCopyPeerNames(vb); | |
97 | CFStringSetPerformWithDescription(peerIDs, ^(CFStringRef description) { | |
98 | CFStringAppendFormat(retval, NULL, CFSTR("<SOSBackupSliceKeyBag@%p %ld %@"), vb, vb->peers ? CFSetGetCount(vb->peers) : 0, description); | |
99 | }); | |
100 | CFReleaseNull(peerIDs); | |
101 | CFStringAppend(retval, CFSTR(">")); | |
102 | ||
103 | return retval; | |
104 | } | |
105 | ||
106 | ||
107 | // | |
108 | // MARK: Encode/Decode | |
109 | // | |
110 | ||
111 | const uint8_t* der_decode_BackupSliceKeyBag(CFAllocatorRef allocator, | |
112 | SOSBackupSliceKeyBagRef* BackupSliceKeyBag, CFErrorRef *error, | |
113 | const uint8_t* der, const uint8_t *der_end) { | |
114 | if (der == NULL) return der; | |
115 | ||
116 | const uint8_t *result = NULL; | |
117 | SOSBackupSliceKeyBagRef vb = CFTypeAllocate(SOSBackupSliceKeyBag, struct __OpaqueSOSBackupSliceKeyBag, allocator); | |
118 | require_quiet(SecAllocationError(vb, error, CFSTR("View bag allocation failed")), fail); | |
119 | ||
120 | const uint8_t *sequence_end = NULL; | |
121 | der = ccder_decode_sequence_tl(&sequence_end, der, der_end); | |
122 | require_quiet(sequence_end == der_end, fail); | |
123 | ||
124 | der = der_decode_data(kCFAllocatorDefault, kCFPropertyListImmutable, &vb->aks_bag, error, der, sequence_end); | |
125 | vb->peers = SOSPeerInfoSetCreateFromArrayDER(kCFAllocatorDefault, &kSOSPeerSetCallbacks, error, | |
126 | &der, der_end); | |
127 | der = der_decode_dictionary(kCFAllocatorDefault, kCFPropertyListImmutable, &vb->wrapped_keys, error, der, sequence_end); | |
128 | ||
129 | require_quiet(SecRequirementError(der == der_end, error, CFSTR("Extra space in sequence")), fail); | |
130 | ||
131 | if (BackupSliceKeyBag) | |
132 | CFTransferRetained(*BackupSliceKeyBag, vb); | |
133 | ||
134 | result = der; | |
135 | ||
136 | fail: | |
137 | CFReleaseNull(vb); | |
138 | return result; | |
139 | } | |
140 | ||
141 | size_t der_sizeof_BackupSliceKeyBag(SOSBackupSliceKeyBagRef BackupSliceKeyBag, CFErrorRef *error) { | |
142 | size_t result = 0; | |
143 | ||
144 | require_quiet(SecRequirementError(BackupSliceKeyBag != NULL, error, CFSTR("Null BackupSliceKeyBag")), fail); | |
145 | require_quiet(BackupSliceKeyBag != NULL, fail); // this is redundant with what happens in SecRequirementError, but the analyzer can't understand that. | |
146 | require_quiet(SecRequirementError(BackupSliceKeyBag->aks_bag != NULL, error, CFSTR("null aks_bag in BackupSliceKeyBag")), fail); | |
147 | require_quiet(BackupSliceKeyBag->aks_bag != NULL, fail); // this is redundant with what happens in SecRequirementError, but the analyzer can't understand that. | |
148 | ||
149 | size_t bag_size = der_sizeof_data(BackupSliceKeyBag->aks_bag, error); | |
150 | require_quiet(bag_size, fail); | |
151 | ||
152 | size_t peers_size = SOSPeerInfoSetGetDEREncodedArraySize(BackupSliceKeyBag->peers, error); | |
153 | require_quiet(peers_size, fail); | |
154 | ||
155 | size_t wrapped_keys_size = der_sizeof_dictionary(BackupSliceKeyBag->wrapped_keys, error); | |
156 | require_quiet(wrapped_keys_size, fail); | |
157 | ||
158 | result = ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, bag_size + peers_size + wrapped_keys_size); | |
159 | ||
160 | fail: | |
161 | return result; | |
162 | } | |
163 | ||
164 | uint8_t* der_encode_BackupSliceKeyBag(SOSBackupSliceKeyBagRef set, CFErrorRef *error, | |
165 | const uint8_t *der, uint8_t *der_end) { | |
166 | uint8_t *result = NULL; | |
167 | if (der_end == NULL) return der_end; | |
168 | ||
169 | require_quiet(SecRequirementError(set != NULL, error, CFSTR("Null set passed to encode")), fail); | |
170 | require_quiet(set, fail); // Silence the NULL warning. | |
171 | ||
172 | require_quiet(SecRequirementError(set->aks_bag != NULL, error, CFSTR("Null set passed to encode")), fail); | |
173 | require_quiet(set->aks_bag, fail); // Silence the warning. | |
174 | ||
175 | der_end = ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der, | |
176 | der_encode_data(set->aks_bag, error, der, | |
177 | SOSPeerInfoSetEncodeToArrayDER(set->peers, error, der, | |
178 | der_encode_dictionary(set->wrapped_keys, error, der, der_end)))); | |
179 | ||
180 | require_quiet(der_end == der, fail); | |
181 | ||
182 | result = der_end; | |
183 | fail: | |
184 | return result; | |
185 | } | |
186 | ||
187 | ||
188 | ||
189 | SOSBackupSliceKeyBagRef SOSBackupSliceKeyBagCreateFromData(CFAllocatorRef allocator, CFDataRef data, CFErrorRef *error) { | |
190 | SOSBackupSliceKeyBagRef result = NULL; | |
191 | SOSBackupSliceKeyBagRef decodedBag = NULL; | |
192 | ||
193 | const uint8_t *der = CFDataGetBytePtr(data); | |
194 | const uint8_t *der_end = der + CFDataGetLength(data); | |
195 | ||
196 | der = der_decode_BackupSliceKeyBag(allocator, &decodedBag, error, der, der_end); | |
197 | ||
198 | require_quiet(SecRequirementError(der == der_end, error, CFSTR("Didn't consume all data supplied")), fail); | |
199 | ||
200 | CFTransferRetained(result, decodedBag); | |
201 | ||
202 | fail: | |
203 | CFReleaseNull(decodedBag); | |
204 | return result; | |
205 | } | |
206 | ||
207 | // | |
208 | // MARK: Construction | |
209 | // | |
210 | ||
211 | bool SOSBSKBIsGoodBackupPublic(CFDataRef publicKey, CFErrorRef *error) { | |
212 | bool result = false; | |
213 | ||
214 | ccec_pub_ctx_decl_cp(SOSGetBackupKeyCurveParameters(), pub_key); | |
215 | ||
216 | int cc_result = ccec_compact_import_pub(SOSGetBackupKeyCurveParameters(), CFDataGetLength(publicKey), CFDataGetBytePtr(publicKey), pub_key); | |
217 | ||
218 | require_action_quiet(cc_result == 0, exit, SOSErrorCreate(kSOSErrorDecodeFailure, error, NULL, CFSTR("Unable to decode public key: %@"), publicKey)); | |
219 | ||
220 | result = true; | |
221 | exit: | |
222 | return result; | |
223 | ||
224 | } | |
225 | ||
226 | ||
227 | static CFDataRef SOSCopyECWrapped(CFDataRef publicKey, CFDataRef secret, CFErrorRef *error) { | |
228 | CFDataRef result = NULL; | |
229 | ||
230 | ccec_pub_ctx_decl_cp(SOSGetBackupKeyCurveParameters(), pub_key); | |
231 | ||
232 | int cc_result = ccec_compact_import_pub(SOSGetBackupKeyCurveParameters(), CFDataGetLength(publicKey), CFDataGetBytePtr(publicKey), pub_key); | |
233 | ||
234 | require_action_quiet(cc_result == 0, exit, SOSErrorCreate(kSOSErrorDecodeFailure, error, NULL, CFSTR("Unable to decode public key: %@"), publicKey)); | |
235 | ||
236 | result = SOSCopyECWrappedData(pub_key, secret, error); | |
237 | ||
238 | exit: | |
239 | return result; | |
240 | } | |
241 | ||
242 | ||
243 | static CFDictionaryRef SOSBackupSliceKeyBagCopyWrappedKeys(SOSBackupSliceKeyBagRef vb, CFDataRef secret, CFDictionaryRef additionalKeys, CFErrorRef *error) { | |
244 | CFDictionaryRef result = NULL; | |
245 | CFMutableDictionaryRef wrappedKeys = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); | |
246 | ||
247 | __block bool success = true; | |
248 | CFSetForEach(vb->peers, ^(const void *value) { | |
249 | SOSPeerInfoRef pi = (SOSPeerInfoRef) value; | |
250 | if (isSOSPeerInfo(pi)) { | |
251 | CFStringRef id = SOSPeerInfoGetPeerID(pi); | |
252 | CFDataRef backupKey = SOSPeerInfoCopyBackupKey(pi); | |
253 | ||
254 | if (backupKey) { | |
255 | CFErrorRef wrapError = NULL; | |
256 | CFDataRef wrappedKey = SOSCopyECWrapped(backupKey, secret, &wrapError); | |
257 | if (wrappedKey) { | |
258 | CFDictionaryAddValue(wrappedKeys, id, wrappedKey); | |
259 | CFDataPerformWithHexString(backupKey, ^(CFStringRef backupKeyString) { | |
260 | CFDataPerformWithHexString(wrappedKey, ^(CFStringRef wrappedKeyString) { | |
261 | secnotice("bskb", "Add for id: %@, bk: %@, wrapped: %@", id, backupKeyString, wrappedKeyString); | |
262 | }); | |
263 | }); | |
264 | } else { | |
265 | CFDataPerformWithHexString(backupKey, ^(CFStringRef backupKeyString) { | |
266 | secnotice("bskb", "Failed at id: %@, bk: %@ error: %@", id, backupKeyString, wrapError); | |
267 | }); | |
268 | CFErrorPropagate(wrapError, error); | |
269 | success = false; | |
270 | } | |
271 | CFReleaseNull(wrappedKey); | |
272 | CFReleaseNull(backupKey); | |
273 | } else { | |
274 | secnotice("bskb", "Skipping id %@, no backup key.", id); | |
275 | } | |
276 | ||
277 | } | |
278 | }); | |
279 | ||
280 | CFDictionaryForEach(additionalKeys, ^(const void *key, const void *value) { | |
281 | CFStringRef prefix = asString(key, NULL); | |
282 | CFDataRef backupKey = asData(value, NULL); | |
283 | if (backupKey) { | |
284 | CFDataRef wrappedKey = NULL; | |
285 | CFErrorRef localError = NULL; | |
286 | CFStringRef id = SOSKeyedPubKeyIdentifierCreateWithData(prefix, backupKey); | |
287 | require_quiet(id, done); | |
288 | ||
289 | wrappedKey = SOSCopyECWrapped(backupKey, secret, &localError); | |
290 | require_quiet(wrappedKey, done); | |
291 | ||
292 | CFDictionaryAddValue(wrappedKeys, id, wrappedKey); | |
293 | ||
294 | done: | |
295 | if (!localError) { | |
296 | CFDataPerformWithHexString(backupKey, ^(CFStringRef backupKeyString) { | |
297 | CFDataPerformWithHexString(wrappedKey, ^(CFStringRef wrappedKeyString) { | |
298 | secnotice("bskb", "Add for bk: %@, wrapped: %@", backupKeyString, wrappedKeyString); | |
299 | }); | |
300 | }); | |
301 | } else { | |
302 | CFDataPerformWithHexString(backupKey, ^(CFStringRef backupKeyString) { | |
303 | secnotice("bskb", "Failed at bk: %@ error: %@", backupKeyString, localError); | |
304 | }); | |
305 | CFErrorPropagate(localError, error); | |
306 | success = false; | |
307 | } | |
308 | CFReleaseNull(wrappedKey); | |
309 | CFReleaseNull(id); | |
310 | } else { | |
311 | secnotice("bskb", "Skipping %@, not data.", value); | |
312 | } | |
313 | }); | |
314 | ||
315 | if (success) | |
316 | CFTransferRetained(result, wrappedKeys); | |
317 | ||
318 | CFReleaseNull(wrappedKeys); | |
319 | return result; | |
320 | } | |
321 | ||
322 | static bool SOSBackupSliceKeyBagCreateBackupBag(SOSBackupSliceKeyBagRef vb, CFDictionaryRef/*CFDataRef*/ additionalKeys, CFErrorRef* error) { | |
323 | CFReleaseNull(vb->aks_bag); | |
324 | ||
325 | // Choose a random key. | |
326 | PerformWithBufferAndClear(kAKSBagSecretLength, ^(size_t size, uint8_t *buffer) { | |
327 | CFDataRef secret = NULL; | |
328 | ||
329 | require_quiet(SecError(SecRandomCopyBytes(kSecRandomDefault, size, buffer), error, CFSTR("SecRandom falied!")), fail); | |
330 | ||
331 | secret = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, buffer, size, kCFAllocatorNull); | |
332 | ||
333 | CFAssignRetained(vb->wrapped_keys, SOSBackupSliceKeyBagCopyWrappedKeys(vb, secret, additionalKeys, error)); | |
334 | CFAssignRetained(vb->aks_bag, SecAKSCopyBackupBagWithSecret(size, buffer, error)); | |
335 | ||
336 | fail: | |
337 | CFReleaseSafe(secret); | |
338 | }); | |
339 | ||
340 | return vb->aks_bag != NULL; | |
341 | } | |
342 | ||
343 | ||
344 | CFDataRef SOSBSKBCopyEncoded(SOSBackupSliceKeyBagRef BackupSliceKeyBag, CFErrorRef* error) { | |
345 | CFDataRef result = NULL; | |
346 | CFMutableDataRef encoded = NULL; | |
347 | ||
348 | size_t encodedSize = der_sizeof_BackupSliceKeyBag(BackupSliceKeyBag, error); | |
349 | require_quiet(encodedSize, fail); | |
350 | ||
351 | encoded = CFDataCreateMutableWithScratch(kCFAllocatorDefault, encodedSize); | |
352 | require_quiet(SecAllocationError(encoded, error, CFSTR("Faild to create scratch")), fail); | |
353 | ||
354 | uint8_t *encode_to = CFDataGetMutableBytePtr(encoded); | |
355 | uint8_t *encode_to_end = encode_to + CFDataGetLength(encoded); | |
356 | require_quiet(encode_to == der_encode_BackupSliceKeyBag(BackupSliceKeyBag, error, encode_to, encode_to_end), fail); | |
357 | ||
358 | CFTransferRetained(result, encoded); | |
359 | ||
360 | fail: | |
361 | CFReleaseSafe(encoded); | |
362 | return result; | |
363 | } | |
364 | ||
365 | static CFSetRef SOSBackupSliceKeyBagCreatePeerSet(CFAllocatorRef allocator, CFSetRef peers) { | |
366 | CFMutableSetRef result = CFSetCreateMutableForSOSPeerInfosByID(allocator); | |
367 | ||
368 | CFSetForEach(peers, ^(const void *value) { | |
369 | CFSetAddValue(result, value); | |
370 | }); | |
371 | ||
372 | return result; | |
373 | } | |
374 | ||
375 | SOSBackupSliceKeyBagRef SOSBackupSliceKeyBagCreate(CFAllocatorRef allocator, CFSetRef peers, CFErrorRef* error) { | |
376 | CFMutableDictionaryRef additionalKeys = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); | |
377 | ||
378 | SOSBackupSliceKeyBagRef result = SOSBackupSliceKeyBagCreateWithAdditionalKeys(allocator, peers, additionalKeys, NULL); | |
379 | ||
380 | CFReleaseNull(additionalKeys); | |
381 | ||
382 | return result; | |
383 | } | |
384 | ||
385 | SOSBackupSliceKeyBagRef SOSBackupSliceKeyBagCreateWithAdditionalKeys(CFAllocatorRef allocator, | |
386 | CFSetRef /*SOSPeerInfoRef*/ peers, | |
387 | CFDictionaryRef /*CFStringRef (prefix) CFDataRef (keydata) */ additionalKeys, | |
388 | CFErrorRef* error) { | |
389 | SOSBackupSliceKeyBagRef result = NULL; | |
390 | SOSBackupSliceKeyBagRef vb = CFTypeAllocate(SOSBackupSliceKeyBag, struct __OpaqueSOSBackupSliceKeyBag, allocator); | |
391 | require_quiet(SecAllocationError(vb, error, CFSTR("View bag allocation failed")), fail); | |
392 | ||
393 | require_quiet(SecRequirementError(CFSetGetCount(peers) > 0, error, CFSTR("Need peers")), fail); | |
394 | ||
395 | vb->peers = SOSBackupSliceKeyBagCreatePeerSet(allocator, peers); | |
396 | vb->wrapped_keys = CFDictionaryCreateMutableForCFTypes(allocator); | |
397 | ||
398 | require_quiet(SOSBackupSliceKeyBagCreateBackupBag(vb, additionalKeys, error), fail); | |
399 | ||
400 | CFTransferRetained(result, vb); | |
401 | ||
402 | fail: | |
403 | CFReleaseNull(vb); | |
404 | return result; | |
405 | } | |
406 | ||
407 | ||
408 | SOSBackupSliceKeyBagRef SOSBackupSliceKeyBagCreateDirect(CFAllocatorRef allocator, CFDataRef aks_bag, CFErrorRef *error) | |
409 | { | |
410 | SOSBackupSliceKeyBagRef result = NULL; | |
411 | SOSBackupSliceKeyBagRef vb = CFTypeAllocate(SOSBackupSliceKeyBag, struct __OpaqueSOSBackupSliceKeyBag, allocator); | |
412 | require_quiet(SecAllocationError(vb, error, CFSTR("View bag allocation failed")), fail); | |
413 | ||
414 | require_quiet(SecRequirementError(aks_bag, error, CFSTR("Need aks bag")), fail); | |
415 | ||
416 | vb->aks_bag = CFRetainSafe(aks_bag); | |
417 | vb->peers = CFSetCreateMutableForSOSPeerInfosByID(allocator); | |
418 | vb->wrapped_keys = CFDictionaryCreateMutableForCFTypes(allocator); | |
419 | ||
420 | CFTransferRetained(result, vb); | |
421 | ||
422 | fail: | |
423 | CFReleaseNull(vb); | |
424 | return result; | |
425 | } | |
426 | ||
427 | // | |
428 | // MARK: Use | |
429 | // | |
430 | ||
431 | bool SOSBSKBIsDirect(SOSBackupSliceKeyBagRef backupSliceKeyBag) | |
432 | { | |
433 | return 0 == CFSetGetCount(backupSliceKeyBag->peers); | |
434 | } | |
435 | ||
436 | CFDataRef SOSBSKBCopyAKSBag(SOSBackupSliceKeyBagRef backupSliceKeyBag, CFErrorRef* error) | |
437 | { | |
438 | return CFRetainSafe(backupSliceKeyBag->aks_bag); | |
439 | } | |
440 | ||
441 | CFSetRef SOSBSKBGetPeers(SOSBackupSliceKeyBagRef backupSliceKeyBag){ | |
442 | return backupSliceKeyBag->peers; | |
443 | } | |
444 | ||
445 | int SOSBSKBCountPeers(SOSBackupSliceKeyBagRef backupSliceKeyBag) { | |
446 | return (int) CFSetGetCount(backupSliceKeyBag->peers); | |
447 | } | |
448 | ||
449 | bool SOSBSKBPeerIsInKeyBag(SOSBackupSliceKeyBagRef backupSliceKeyBag, SOSPeerInfoRef pi) { | |
450 | return CFSetGetValue(backupSliceKeyBag->peers, pi) != NULL; | |
451 | } | |
452 | ||
453 | ||
454 | ||
455 | bool SOSBKSBKeyIsInKeyBag(SOSBackupSliceKeyBagRef backupSliceKeyBag, CFDataRef publicKey) { | |
456 | bool result = false; | |
457 | CFStringRef keyID = SOSCopyIDOfDataBuffer(publicKey, NULL); | |
458 | require_quiet(keyID, done); | |
459 | ||
460 | result = CFDictionaryContainsKey(backupSliceKeyBag->wrapped_keys, keyID); | |
461 | ||
462 | done: | |
463 | CFReleaseSafe(keyID); | |
464 | return result; | |
465 | } | |
466 | ||
467 | bool SOSBKSBPrefixedKeyIsInKeyBag(SOSBackupSliceKeyBagRef backupSliceKeyBag, CFStringRef prefix, CFDataRef publicKey) { | |
468 | bool result = false; | |
469 | CFStringRef kpkid = SOSKeyedPubKeyIdentifierCreateWithData(prefix, publicKey); | |
470 | require_quiet(kpkid, done); | |
471 | ||
472 | result = CFDictionaryContainsKey(backupSliceKeyBag->wrapped_keys, kpkid); | |
473 | ||
474 | done: | |
475 | CFReleaseSafe(kpkid); | |
476 | return result; | |
477 | ||
478 | } | |
479 | ||
480 | ||
481 | ||
482 | bskb_keybag_handle_t SOSBSKBLoadLocked(SOSBackupSliceKeyBagRef backupSliceKeyBag, | |
483 | CFErrorRef *error) | |
484 | { | |
485 | #if !TARGET_HAS_KEYSTORE | |
486 | return bad_keybag_handle; | |
487 | #else | |
488 | keybag_handle_t result = bad_keybag_handle; | |
489 | keybag_handle_t bag_handle = bad_keybag_handle; | |
490 | ||
491 | require_quiet(SecRequirementError(backupSliceKeyBag->aks_bag, error, | |
492 | CFSTR("No aks bag to load")), exit); | |
493 | require_quiet(SecRequirementError(CFDataGetLength(backupSliceKeyBag->aks_bag) < INT_MAX, error, | |
494 | CFSTR("No aks bag to load")), exit); | |
495 | ||
496 | kern_return_t aks_result; | |
497 | aks_result = aks_load_bag(CFDataGetBytePtr(backupSliceKeyBag->aks_bag), | |
498 | (int) CFDataGetLength(backupSliceKeyBag->aks_bag), | |
499 | &bag_handle); | |
500 | require_quiet(SecKernError(aks_result, error, | |
501 | CFSTR("aks_load_bag failed: %d"), aks_result), exit); | |
502 | ||
503 | result = bag_handle; | |
504 | bag_handle = bad_keybag_handle; | |
505 | ||
506 | exit: | |
507 | if (bag_handle != bad_keybag_handle) { | |
508 | (void) aks_unload_bag(bag_handle); | |
509 | } | |
510 | ||
511 | return result; | |
512 | #endif | |
513 | ||
514 | } | |
515 | ||
516 | static keybag_handle_t SOSBSKBLoadAndUnlockBagWithSecret(SOSBackupSliceKeyBagRef backupSliceKeyBag, | |
517 | size_t secretSize, const uint8_t *secret, | |
518 | CFErrorRef *error) | |
519 | { | |
520 | #if !TARGET_HAS_KEYSTORE | |
521 | return bad_keybag_handle; | |
522 | #else | |
523 | keybag_handle_t result = bad_keybag_handle; | |
524 | keybag_handle_t bag_handle = bad_keybag_handle; | |
525 | ||
526 | require_quiet(SecRequirementError(backupSliceKeyBag->aks_bag, error, | |
527 | CFSTR("No aks bag to load")), exit); | |
528 | require_quiet(SecRequirementError(CFDataGetLength(backupSliceKeyBag->aks_bag) < INT_MAX, error, | |
529 | CFSTR("No aks bag to load")), exit); | |
530 | require_quiet(SecRequirementError(secretSize <= INT_MAX, error, | |
531 | CFSTR("secret too big")), exit); | |
532 | ||
533 | kern_return_t aks_result; | |
534 | aks_result = aks_load_bag(CFDataGetBytePtr(backupSliceKeyBag->aks_bag), | |
535 | (int) CFDataGetLength(backupSliceKeyBag->aks_bag), | |
536 | &bag_handle); | |
537 | require_quiet(SecKernError(aks_result, error, | |
538 | CFSTR("aks_load_bag failed: %d"), aks_result), exit); | |
539 | ||
540 | aks_result = aks_unlock_bag(bag_handle, secret, (int) secretSize); | |
541 | require_quiet(SecKernError(aks_result, error, | |
542 | CFSTR("failed to unlock bag: %d"), aks_result), exit); | |
543 | ||
544 | result = bag_handle; | |
545 | bag_handle = bad_keybag_handle; | |
546 | ||
547 | exit: | |
548 | if (bag_handle != bad_keybag_handle) { | |
549 | (void) aks_unload_bag(bag_handle); | |
550 | } | |
551 | ||
552 | return result; | |
553 | #endif | |
554 | } | |
555 | ||
556 | keybag_handle_t SOSBSKBLoadAndUnlockWithPeerIDAndSecret(SOSBackupSliceKeyBagRef backupSliceKeyBag, | |
557 | CFStringRef peerID, CFDataRef peerSecret, | |
558 | CFErrorRef *error) | |
559 | { | |
560 | __block keybag_handle_t result = bad_keybag_handle; | |
561 | ||
562 | CFDataRef lookedUpData = CFDictionaryGetValue(backupSliceKeyBag->wrapped_keys, peerID); | |
563 | require_quiet(SecRequirementError(lookedUpData != NULL, error, CFSTR("%@ has no wrapped key in %@"), peerID, backupSliceKeyBag), exit); | |
564 | ||
565 | require_quiet(SOSPerformWithDeviceBackupFullKey(SOSGetBackupKeyCurveParameters(), peerSecret, error, ^(ccec_full_ctx_t fullKey) { | |
566 | SOSPerformWithUnwrappedData(fullKey, lookedUpData, error, ^(size_t size, uint8_t *buffer) { | |
567 | result = SOSBSKBLoadAndUnlockBagWithSecret(backupSliceKeyBag, size, buffer, error); | |
568 | }); | |
569 | }), exit); | |
570 | ||
571 | exit: | |
572 | return result; | |
573 | } | |
574 | ||
575 | ||
576 | keybag_handle_t SOSBSKBLoadAndUnlockWithPeerSecret(SOSBackupSliceKeyBagRef backupSliceKeyBag, | |
577 | SOSPeerInfoRef peer, CFDataRef peerSecret, | |
578 | CFErrorRef *error) | |
579 | { | |
580 | return SOSBSKBLoadAndUnlockWithPeerIDAndSecret(backupSliceKeyBag, SOSPeerInfoGetPeerID(peer), peerSecret, error); | |
581 | } | |
582 | ||
583 | keybag_handle_t SOSBSKBLoadAndUnlockWithDirectSecret(SOSBackupSliceKeyBagRef backupSliceKeyBag, | |
584 | CFDataRef secret, | |
585 | CFErrorRef *error) | |
586 | { | |
587 | keybag_handle_t result = bad_keybag_handle; | |
588 | require_quiet(SecRequirementError(SOSBSKBIsDirect(backupSliceKeyBag), error, CFSTR("Not direct bag")), exit); | |
589 | ||
590 | result = SOSBSKBLoadAndUnlockBagWithSecret(backupSliceKeyBag, | |
591 | CFDataGetLength(secret), | |
592 | CFDataGetBytePtr(secret), | |
593 | error); | |
594 | ||
595 | exit: | |
596 | return result; | |
597 | } | |
598 | ||
599 | #include "SecRecoveryKey.h" | |
600 | ||
601 | static bool SOSPerformWithRecoveryKeyFullKey(CFDataRef wrappingSecret, CFErrorRef *error, void (^operation)(ccec_full_ctx_t fullKey, CFStringRef keyID)) { | |
602 | bool result = false; | |
603 | ||
604 | CFStringRef keyID = NULL; | |
605 | SecRecoveryKey *sRecKey = NULL; | |
606 | CFDataRef fullKeyBytes = NULL; | |
607 | CFDataRef pubKeyBytes = NULL; | |
608 | CFStringRef restoreKeySecret = CFStringCreateWithBytes(kCFAllocatorDefault, CFDataGetBytePtr(wrappingSecret), CFDataGetLength(wrappingSecret), kCFStringEncodingUTF8, false); | |
609 | require_action_quiet(restoreKeySecret, errOut, SOSErrorCreate(kSOSErrorDecodeFailure, error, NULL, CFSTR("Unable to create key string from data."))); | |
610 | sRecKey = SecRKCreateRecoveryKey(restoreKeySecret); | |
611 | require_action_quiet(sRecKey, errOut, SOSErrorCreate(kSOSErrorDecodeFailure, error, NULL, CFSTR("Unable to create recovery key from string."))); | |
612 | fullKeyBytes = SecRKCopyBackupFullKey(sRecKey); | |
613 | pubKeyBytes = SecRKCopyBackupPublicKey(sRecKey); | |
614 | require_action_quiet(fullKeyBytes && pubKeyBytes, errOut, SOSErrorCreate(kSOSErrorDecodeFailure, error, NULL, CFSTR("Unable to create recovery key public and private keys."))); | |
615 | keyID = SOSCopyIDOfDataBuffer(pubKeyBytes, error); | |
616 | require_quiet(keyID, errOut); | |
617 | { | |
618 | size_t keysize = ccec_compact_import_priv_size(CFDataGetLength(fullKeyBytes)); | |
619 | ccec_const_cp_t cp = ccec_curve_for_length_lookup(keysize, ccec_cp_256(), ccec_cp_384(), ccec_cp_521()); | |
620 | ccec_full_ctx_decl_cp(cp, fullKey); | |
621 | int res = ccec_compact_import_priv(cp, CFDataGetLength(fullKeyBytes), CFDataGetBytePtr(fullKeyBytes), fullKey); | |
622 | if(res == 0) { | |
623 | operation(fullKey, keyID); | |
624 | result = true; | |
625 | ccec_full_ctx_clear_cp(cp, fullKey); | |
626 | } | |
627 | } | |
628 | if(!result) SOSErrorCreate(kSOSErrorProcessingFailure, error, NULL, CFSTR("Unable to perform crypto operation from fullKeyBytes.")); | |
629 | ||
630 | errOut: | |
631 | CFReleaseNull(keyID); | |
632 | CFReleaseNull(sRecKey); | |
633 | CFReleaseNull(fullKeyBytes); | |
634 | CFReleaseNull(pubKeyBytes); | |
635 | CFReleaseNull(restoreKeySecret); | |
636 | return result; | |
637 | } | |
638 | ||
639 | bskb_keybag_handle_t SOSBSKBLoadAndUnlockWithWrappingSecret(SOSBackupSliceKeyBagRef backupSliceKeyBag, | |
640 | CFDataRef wrappingSecret, | |
641 | CFErrorRef *error) { | |
642 | __block keybag_handle_t result = bad_keybag_handle; | |
643 | ||
644 | CFDataRef lookedUpData = SOSBSKBCopyRecoveryKey(backupSliceKeyBag); | |
645 | require_quiet(SecRequirementError(lookedUpData != NULL, error, CFSTR("no recovery key found in %@"), backupSliceKeyBag), errOut); | |
646 | ||
647 | SOSPerformWithRecoveryKeyFullKey(wrappingSecret, error, ^(ccec_full_ctx_t fullKey, CFStringRef keyID) { | |
648 | SOSPerformWithUnwrappedData(fullKey, lookedUpData, error, ^(size_t size, uint8_t *buffer) { | |
649 | result = SOSBSKBLoadAndUnlockBagWithSecret(backupSliceKeyBag, size, buffer, error); | |
650 | }); | |
651 | }); | |
652 | ||
653 | errOut: | |
654 | CFReleaseSafe(lookedUpData); | |
655 | return result; | |
656 | } | |
657 | ||
658 | static CFDictionaryRef SOSBSKBCopyAdditionalKeysWithPrefix(CFAllocatorRef allocator, SOSBackupSliceKeyBagRef bskb, CFStringRef prefix) { | |
659 | CFMutableDictionaryRef retval = CFDictionaryCreateMutableForCFTypes(allocator); | |
660 | if(!retval) return NULL; | |
661 | CFDictionaryForEach(bskb->wrapped_keys, ^(const void *key, const void *value) { | |
662 | CFStringRef kpkid = asString(key, NULL); | |
663 | CFDataRef keyData = asData(value, NULL); | |
664 | if(kpkid && keyData && SOSKeyedPubKeyIdentifierIsPrefixed(kpkid)) { | |
665 | CFStringRef idPrefix = SOSKeyedPubKeyIdentifierCopyPrefix(kpkid); | |
666 | if(CFEqualSafe(idPrefix, prefix)) { | |
667 | CFDictionaryAddValue(retval, kpkid, keyData); | |
668 | } | |
669 | CFReleaseNull(idPrefix); | |
670 | } | |
671 | }); | |
672 | return retval; | |
673 | } | |
674 | ||
675 | static bool SOSBSKBHasPrefixedKey(SOSBackupSliceKeyBagRef bskb, CFStringRef prefix) { | |
676 | CFDictionaryRef keyDict = SOSBSKBCopyAdditionalKeysWithPrefix(kCFAllocatorDefault, bskb, prefix); | |
677 | bool haveKeys = CFDictionaryGetCount(keyDict) > 0; | |
678 | CFReleaseNull(keyDict); | |
679 | return haveKeys; | |
680 | } | |
681 | ||
682 | CFDataRef SOSBSKBCopyRecoveryKey(SOSBackupSliceKeyBagRef bskb) { | |
683 | CFDictionaryRef keyDict = SOSBSKBCopyAdditionalKeysWithPrefix(kCFAllocatorDefault, bskb, bskbRkbgPrefix); | |
684 | if(CFDictionaryGetCount(keyDict) == 1) { | |
685 | __block CFDataRef keyData = NULL; | |
686 | CFDictionaryForEach(keyDict, ^(const void *key, const void *value) { | |
687 | keyData = asData(value, NULL); | |
688 | }); | |
689 | return CFDataCreateCopy(kCFAllocatorDefault, keyData); | |
690 | } | |
691 | CFReleaseNull(keyDict); | |
692 | return NULL; | |
693 | } | |
694 | ||
695 | bool SOSBSKBHasRecoveryKey(SOSBackupSliceKeyBagRef bskb) { | |
696 | if(SOSBSKBHasPrefixedKey(bskb, bskbRkbgPrefix)) return true; | |
697 | // old way for RecoveryKeys | |
698 | int keyCount = (int) CFDictionaryGetCount(bskb->wrapped_keys); | |
699 | int peerCount = SOSBSKBCountPeers(bskb); | |
700 | return !SOSBSKBIsDirect(bskb) && ((keyCount - peerCount) > 0); | |
701 | } | |
702 |