]> git.saurik.com Git - apple/security.git/blob - OSX/sec/securityd/SecDbBackupManager.m
Security-59306.11.20.tar.gz
[apple/security.git] / OSX / sec / securityd / SecDbBackupManager.m
1 /*
2 * Copyright (c) 2018 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 #import "SecDbBackupManager.h"
25
26 NSString* const KeychainBackupsErrorDomain = @"com.apple.security.keychain.backups";
27
28 // oink oink
29 @implementation SecDbBackupWrappedItemKey
30 + (BOOL)supportsSecureCoding {
31 return YES;
32 }
33 - (void)encodeWithCoder:(nonnull NSCoder *)coder {
34 [coder encodeObject:self.wrappedKey forKey:@"wrappedKey"];
35 [coder encodeObject:self.baguuid forKey:@"baguuid"];
36 }
37
38 - (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder {
39 if (self = [super init]) {
40 _wrappedKey = [coder decodeObjectOfClass:[NSData class] forKey:@"wrappedKey"];
41 _baguuid = [coder decodeObjectOfClass:[NSData class] forKey:@"baguuid"];
42 }
43 return self;
44 }
45 @end
46
47 #if !SECDB_BACKUPS_ENABLED
48
49 @implementation SecDbBackupManager
50
51 + (instancetype)manager
52 {
53 return nil;
54 }
55
56 - (void)verifyBackupIntegrity:(bool)lightweight
57 completion:(void (^)(NSDictionary<NSString*, NSString*>* results, NSError* _Nullable error))completion
58 {
59 completion(nil, [NSError errorWithDomain:KeychainBackupsErrorDomain
60 code:SecDbBackupNotSupported
61 userInfo:@{NSLocalizedDescriptionKey : @"platform doesn't do backups"}]);
62 }
63
64 - (SecDbBackupWrappedItemKey* _Nullable)wrapItemKey:(id)key forKeyclass:(keyclass_t)keyclass error:(NSError**)error
65 {
66 return nil;
67 }
68
69 @end
70
71 bool SecDbBackupCreateOrLoadBackupInfrastructure(CFErrorRef* error)
72 {
73 return true;
74 }
75
76 #else // SECDB_BACKUPS_ENABLED is true, roll out the code
77
78 #import "SecDbBackupManager_Internal.h"
79 #include <CommonCrypto/CommonRandom.h>
80 #import <Foundation/NSData_Private.h>
81 #import "SecItemServer.h"
82 #import "SecItemDb.h"
83 #import "keychain/categories/NSError+UsefulConstructors.h"
84 #import "sec_action.h"
85 #import "SecItemServer.h"
86 #include "utilities/der_plist.h"
87
88 // TODO: fire off metric on outcome
89 bool SecDbBackupCreateOrLoadBackupInfrastructure(CFErrorRef* error)
90 {
91 NSError* localError;
92 bool ok = [[SecDbBackupManager manager] createOrLoadBackupInfrastructure:&localError];
93 if (!ok) {
94 // Translate this to intelligible constant in C, but other errors can be passed along
95 if (localError.code == SecDbBackupKeychainLocked) {
96 *error = CFErrorCreate(kCFAllocatorDefault, kSecErrorDomain, errSecInteractionNotAllowed, NULL);
97 } else {
98 *error = (CFErrorRef)CFBridgingRetain(localError);
99 }
100 }
101 return ok;
102 }
103
104 // Reading from disk is relatively expensive. Keep wrapped key in memory and just delete the unwrapped copy on lock
105 @interface InMemoryKCSK : NSObject
106 @property aks_ref_key_t refKey;
107 @property (nonatomic) NSData* wrappedKey;
108 @property (nonatomic) SFECKeyPair* key;
109 @end
110
111 @implementation InMemoryKCSK
112 - (void)dealloc
113 {
114 if (_refKey) {
115 free(_refKey);
116 }
117 }
118
119 + (instancetype)kcskWithRefKey:(aks_ref_key_t)refKey wrappedKey:(NSData*)wrappedKey key:(SFECKeyPair*)key
120 {
121 InMemoryKCSK* kcsk = [InMemoryKCSK new];
122 kcsk.refKey = refKey;
123 kcsk.wrappedKey = wrappedKey;
124 kcsk.key = key;
125 return kcsk;
126 }
127
128 @end
129
130 @interface SecDbBackupManager () {
131 dispatch_queue_t _queue;
132 keybag_handle_t _handle;
133 SecDbBackupBagIdentity* _bagIdentity;
134 NSMutableDictionary<NSNumber*, InMemoryKCSK*>* _cachedKCSKs;
135 }
136 @end
137
138 @implementation SecDbBackupManager
139
140 #pragma mark - Misc Helpers
141
142 - (NSData*)getSHA256OfData:(NSData*)data
143 {
144 NSMutableData* digest = [[NSMutableData alloc] initWithLength:CC_SHA512_DIGEST_LENGTH];
145 if (!CC_SHA512(data.bytes, (CC_LONG)data.length, digest.mutableBytes)) {
146 return nil;
147 }
148 return digest;
149 }
150
151 - (void)setBagIdentity:(SecDbBackupBagIdentity *)bagIdentity
152 {
153 _bagIdentity = bagIdentity;
154 }
155
156 - (SecDbBackupBagIdentity*)bagIdentity
157 {
158 return _bagIdentity;
159 }
160
161 - (bool)fillError:(NSError**)error code:(enum SecDbBackupErrorCode)code underlying:(NSError*)underlying description:(NSString*)format, ... NS_FORMAT_FUNCTION(4, 5)
162 {
163 if (error) {
164 va_list ap;
165 va_start(ap, format);
166 NSString* desc = [[NSString alloc] initWithFormat:format arguments:ap];
167 va_end(ap);
168 if (underlying) {
169 *error = [NSError errorWithDomain:KeychainBackupsErrorDomain code:code description:desc underlying:underlying];
170 } else {
171 *error = [NSError errorWithDomain:KeychainBackupsErrorDomain code:code description:desc];
172 }
173 }
174
175 // analyzer gets upset when a method taking an error** doesn't return a value
176 return true;
177 }
178
179 static SecDbBackupManager* staticManager;
180 + (instancetype)manager
181 {
182 static dispatch_once_t onceToken;
183 dispatch_once(&onceToken, ^{
184 staticManager = [SecDbBackupManager new];
185 });
186 return staticManager;
187 }
188
189 // Testing only please
190 + (void)resetManager
191 {
192 if (staticManager) {
193 staticManager = [SecDbBackupManager new];
194 }
195 }
196
197 - (instancetype)init
198 {
199 if (!checkV12DevEnabled()) {
200 return nil;
201 }
202 if (self = [super init]) {
203 _queue = dispatch_queue_create("com.apple.security.secdbbackupmanager", DISPATCH_QUEUE_SERIAL_WITH_AUTORELEASE_POOL);
204 _handle = bad_keybag_handle;
205 _cachedKCSKs = [NSMutableDictionary new];
206 }
207 return self;
208 }
209
210 - (SFECKeyPair*)getECKeyPairFromDERBytes:(void*)bytes length:(size_t)len error:(NSError**)error
211 {
212 if (!bytes || len == 0) {
213 [self fillError:error code:SecDbBackupInvalidArgument underlying:nil description:@"Need valid byte buffer to make EC keypair from"];
214 return nil;
215 }
216 CFTypeRef cftype = NULL;
217 CFErrorRef cferr = NULL;
218 const uint8_t* derp = der_decode_plist(kCFAllocatorDefault, NSPropertyListImmutable, &cftype, &cferr, bytes, bytes + len);
219 free(bytes);
220 if (derp == NULL || derp != (bytes + len) || cftype == NULL) {
221 [self fillError:error code:SecDbBackupMalformedKCSKDataOnDisk underlying:CFBridgingRelease(cferr) description:@"Unable to parse der data"];
222 return nil;
223 }
224
225 return [[SFECKeyPair alloc] initWithData:(__bridge_transfer NSData*)cftype
226 specifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]
227 error:error];
228 }
229
230 #pragma mark - Fixup And Verification
231
232 - (void)verifyBackupIntegrity:(bool)lightweight
233 completion:(void (^)(NSDictionary<NSString*, NSString*>* _Nonnull, NSError * _Nullable))completion
234 {
235 NSError* error = nil;
236 completion(@{@"summary" : @"Unimplemented"}, error);
237 }
238
239 #pragma mark - Backup Bag Management
240
241 // Get the bag's UUID from AKS and the hash from provided data. This must always be the original bag's data
242 - (SecDbBackupBagIdentity*)bagIdentityWithHandle:(keybag_handle_t)handle data:(NSData*)data error:(NSError**)error {
243 assert(data);
244 secnotice("SecDbBackup", "Getting bag identity");
245 SecDbBackupBagIdentity* identity = [SecDbBackupBagIdentity new];
246
247 uuid_t uuid = {0};
248 kern_return_t aksResult = aks_get_bag_uuid(handle, uuid);
249 if (aksResult != kAKSReturnSuccess) {
250 [self fillError:error code:SecDbBackupAKSFailure underlying:nil description:@"Unable to get keybag UUID (%d)", aksResult];
251 return nil;
252 }
253 identity.baguuid = [NSData dataWithBytes:uuid length:16];
254
255 NSData* digest = [self getSHA256OfData:data];
256 if (!digest) {
257 [self fillError:error code:SecDbBackupCryptoFailure underlying:nil description:@"CC_SHA512 returned failure, can't get bag hash"];
258 return nil;
259 }
260 identity.baghash = digest;
261
262 secnotice("SecDbBackup", "Obtained bag identity: %@", identity);
263
264 return identity;
265 }
266
267 - (NSData*)createBackupBagSecret:(NSError**)error
268 {
269 uint8_t* data = calloc(1, BACKUPBAG_PASSPHRASE_LENGTH);
270 if (!data) {
271 return nil; // Good luck allocating an error message
272 }
273
274 CCRNGStatus rngResult = CCRandomGenerateBytes(data, BACKUPBAG_PASSPHRASE_LENGTH);
275 if (rngResult != kCCSuccess) {
276 [self fillError:error code:SecDbBackupCryptoFailure underlying:nil description:@"Unable to generate random bytes (%d)", rngResult];
277 return nil;
278 }
279
280 NSData* secret = [NSData _newZeroingDataWithBytesNoCopy:data length:BACKUPBAG_PASSPHRASE_LENGTH deallocator:NSDataDeallocatorNone];
281 return secret;
282 }
283
284 - (keybag_handle_t)onQueueCreateBackupBagWithSecret:(NSData*)secret error:(NSError**)error
285 {
286 dispatch_assert_queue(_queue);
287
288 keybag_handle_t handle = bad_keybag_handle;
289 kern_return_t aksresult = aks_create_bag(secret.bytes, BACKUPBAG_PASSPHRASE_LENGTH, kAppleKeyStoreAsymmetricBackupBag, &handle);
290 if (aksresult != kAKSReturnSuccess) {
291 [self fillError:error code:SecDbBackupAKSFailure underlying:nil description:@"Unable to create keybag (%d)", aksresult];
292 return bad_keybag_handle;
293 }
294
295 // Make secret keys unavailable. Causes pubkeys to be destroyed so reload bag before use
296 aksresult = aks_lock_bag(handle);
297 if (aksresult != kAKSReturnSuccess) { // This would be rather surprising
298 [self fillError:error code:SecDbBackupAKSFailure underlying:nil description:@"Unable to lock keybag (%d)", aksresult];
299 aks_unload_bag(handle);
300 return bad_keybag_handle;
301 }
302
303 return handle;
304 }
305
306 - (BOOL)onQueueInTransaction:(SecDbConnectionRef)dbt saveBackupBag:(keybag_handle_t)handle asDefault:(BOOL)asDefault error:(NSError**)error
307 {
308 dispatch_assert_queue(_queue);
309
310 void* buf = NULL;
311 int buflen = 0;
312 kern_return_t aksResult = aks_save_bag(handle, &buf, &buflen);
313 if (aksResult != kAKSReturnSuccess) {
314 [self fillError:error code:SecDbBackupAKSFailure underlying:nil description:@"Unable to serialize keybag (%d)", aksResult];
315 return NO;
316 }
317 NSData* bagData = [NSData dataWithBytesNoCopy:buf length:buflen];
318
319 SecDbBackupBagIdentity* bagIdentity = [self bagIdentityWithHandle:handle data:bagData error:error];
320 if (!bagIdentity) {
321 return NO;
322 }
323 SecDbBackupBag* bag = [SecDbBackupBag new];
324 bag.bagIdentity = bagIdentity;
325 bag.keybag = bagData;
326
327 __block CFErrorRef cfError = NULL;
328 __block bool ok = true;
329 if (asDefault) {
330 ok &= SecDbPrepare(dbt, CFSTR("UPDATE backupbags SET defaultvalue = 0 WHERE defaultvalue = 1"), &cfError, ^(sqlite3_stmt *stmt) {
331 ok &= SecDbStep(dbt, stmt, &cfError, ^(bool *stop) {
332 // FIXME: Should this be an error? Should something else happen?
333 secwarning("SecDbBackup: Marking existing bag as non-default");
334 });
335 });
336 }
337 if (!ok) {
338 return ok;
339 }
340 ok &= SecDbPrepare(dbt, CFSTR("INSERT INTO backupbags (backupUUID, backupbag, defaultvalue) VALUES (?,?,?)"), &cfError, ^(sqlite3_stmt *stmt) {
341 ok &= SecDbBindObject(stmt, 1, (__bridge CFDataRef)bag.bagIdentity.baguuid, &cfError);
342 ok &= SecDbBindObject(stmt, 2, (__bridge CFDataRef)bag.data, &cfError);
343 ok &= SecDbBindInt(stmt, 3, asDefault ? 1 : 0, &cfError);
344 ok &= SecDbStep(dbt, stmt, &cfError, NULL);
345 });
346
347 if (!ok) {
348 secerror("SecDbBackup: unable to save keybag to disk: %@", cfError);
349 [self fillError:error code:SecDbBackupWriteFailure underlying:CFBridgingRelease(cfError) description:@"Unable to save keybag to disk"];
350 }
351
352 return ok;
353 }
354
355 - (keybag_handle_t)onQueueLoadBackupBag:(NSUUID*)uuid error:(NSError**)error {
356 dispatch_assert_queue(_queue);
357
358 secnotice("SecDbBackup", "Attempting to load backup bag from disk");
359
360 __block CFErrorRef localErr = NULL;
361 __block bool ok = true;
362 __block NSData* readUUID;
363 __block NSData* readBagData;
364 __block unsigned found = 0;
365 ok &= kc_with_dbt_non_item_tables(false, &localErr, ^bool(SecDbConnectionRef dbt) {
366 NSString* sql = [NSString stringWithFormat:@"SELECT backupUUID, backupbag FROM backupbags WHERE %@", uuid ? @"backupUUID = ?" : @"defaultvalue = 1"];
367 ok &= SecDbPrepare(dbt, (__bridge CFStringRef)sql, &localErr, ^(sqlite3_stmt *stmt) {
368 if (uuid) {
369 unsigned char uuidbytes[UUIDBYTESLENGTH] = {0};
370 [uuid getUUIDBytes:uuidbytes];
371 ok &= SecDbBindBlob(stmt, 1, uuidbytes, UUIDBYTESLENGTH, SQLITE_TRANSIENT, &localErr);
372 }
373 ok &= SecDbStep(dbt, stmt, &localErr, ^(bool *stop) {
374 if (found > 0) { // For uuids this should have violated constraints
375 secerror("Encountered more than one backup bag by %@", uuid ? @"backupUUID" : @"defaultvalue");
376 *stop = true;
377 }
378 readUUID = [[NSData alloc] initWithBytes:sqlite3_column_blob(stmt, 0) length:sqlite3_column_bytes(stmt, 0)];
379 readBagData = [[NSData alloc] initWithBytes:sqlite3_column_blob(stmt, 1) length:sqlite3_column_bytes(stmt, 1)];
380 ++found;
381 });
382 });
383 return ok;
384 });
385
386 if (!ok) {
387 secerror("SecDbBackup: Unable to load backup bag from disk: %@", localErr);
388 [self fillError:error code:SecDbBackupWriteFailure underlying:CFBridgingRelease(localErr) description:@"Unable to load backup bag from disk"];
389 return bad_keybag_handle;
390 }
391
392 if (!found) {
393 [self fillError:error code:SecDbBackupNoBackupBagFound underlying:nil description:@"No backup bag found to load from disk"];
394 return bad_keybag_handle;
395 } else if (found > 1) {
396 [self fillError:error
397 code:uuid ? SecDbBackupDuplicateBagFound : SecDbBackupMultipleDefaultBagsFound
398 underlying:nil
399 description:@"More than one backup bag found"];
400 return bad_keybag_handle;
401 }
402
403 if (!readUUID || readUUID.length != UUIDBYTESLENGTH || !readBagData || !readBagData.length) {
404 [self fillError:error code:SecDbBackupMalformedBagDataOnDisk underlying:nil description:@"bags read from disk malformed"];
405 return bad_keybag_handle;
406 }
407
408 SecDbBackupBag* readBag = [[SecDbBackupBag alloc] initWithData:readBagData];
409 if (!readBag) {
410 [self fillError:error code:SecDbBackupDeserializationFailure underlying:nil description:@"bag from disk does not deserialize"];
411 return bad_keybag_handle;
412 }
413
414 secnotice("SecDbBackup", "Successfully read backup bag from disk; loading and verifying. Read bag ID: %@", readBag.bagIdentity);
415
416 keybag_handle_t handle = bad_keybag_handle;
417 kern_return_t aksResult = aks_load_bag(readBag.keybag.bytes, (int)readBag.keybag.length, &handle);
418 if (aksResult != kAKSReturnSuccess) {
419 [self fillError:error code:SecDbBackupAKSFailure underlying:nil description:@"Unable to load bag from disk (%d)", aksResult];
420 return bad_keybag_handle;
421 }
422
423 SecDbBackupBagIdentity* loadedID = [self bagIdentityWithHandle:handle data:readBag.keybag error:error];
424 if (!loadedID) {
425 aks_unload_bag(handle);
426 return bad_keybag_handle;
427 }
428
429 if (memcmp(loadedID.baguuid.bytes, readBag.bagIdentity.baguuid.bytes, UUIDBYTESLENGTH) ||
430 memcmp(loadedID.baguuid.bytes, readUUID.bytes, UUIDBYTESLENGTH)) {
431 [self fillError:error code:SecDbBackupUUIDMismatch underlying:nil description:@"Loaded UUID does not match UUIDs on disk"];
432 aks_unload_bag(handle);
433 return bad_keybag_handle;
434 }
435
436 if (memcmp(loadedID.baghash.bytes, readBag.bagIdentity.baghash.bytes, CC_SHA512_DIGEST_LENGTH)) {
437 [self fillError:error code:SecDbBackupDeserializationFailure underlying:nil description:@"Keybag hash does not match its identity's hash"];
438 return bad_keybag_handle;
439 }
440
441 // TODO: verify that bag is still signed, rdar://problem/46702467
442
443 secnotice("SecDbBackup", "Backup bag loaded and verified.");
444
445 // Must load readBag's identity because the hash from AKS is unstable.
446 // This is the hash of the original saved bag and is anchored in the KCSKes.
447 _bagIdentity = readBag.bagIdentity;
448
449 return handle;
450 }
451
452 - (BOOL)onQueueReloadDefaultBackupBagWithError:(NSError**)error
453 {
454 if (_handle != bad_keybag_handle) {
455 aks_unload_bag(_handle);
456 }
457
458 _handle = [self onQueueLoadBackupBag:nil error:error];
459 return _handle != bad_keybag_handle;
460 }
461
462 #pragma mark - KCSK Management
463
464 - (SecDbBackupKeyClassSigningKey*)createKCSKForKeyClass:(keyclass_t)class withWrapper:(SFAESKey*)wrapper error:(NSError**)error
465 {
466 if (!wrapper) {
467 [self fillError:error code:SecDbBackupInvalidArgument underlying:nil description:@"Need wrapper for KCSK"];
468 return nil;
469 }
470
471 SecDbBackupKeyClassSigningKey* kcsk = [SecDbBackupKeyClassSigningKey new];
472 kcsk.keyClass = class;
473
474 SFECKeyPair* keypair = [[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]];
475 kcsk.publicKey = [keypair.publicKey.keyData copy];
476
477 // Create a DER-encoded dictionary of bag identity
478 void* der_blob;
479 size_t der_len;
480 CFErrorRef cfErr = NULL;
481 NSDictionary* identDict = @{@"baguuid" : _bagIdentity.baguuid, @"baghash" : _bagIdentity.baghash};
482 NSData* identData = (__bridge_transfer NSData*)CFPropertyListCreateDERData(NULL, (__bridge CFDictionaryRef)identDict, &cfErr);
483 aks_operation_optional_params(NULL, 0, identData.bytes, identData.length, NULL, 0, &der_blob, &der_len);
484
485 // Create ref key with embedded bag identity DER data
486 aks_ref_key_t refkey = NULL;
487 kern_return_t aksResult = aks_ref_key_create(KEYBAG_DEVICE, class, key_type_sym, der_blob, der_len, &refkey);
488 free(der_blob);
489 if (aksResult != kAKSReturnSuccess) {
490 [self fillError:error code:SecDbBackupAKSFailure underlying:nil
491 description:@"Unable to create AKS ref key for KCSK class %d: %d", class, aksResult];
492 return nil;
493 }
494
495 size_t refkeyblobsize = 0;
496 const uint8_t* refkeyblob = aks_ref_key_get_blob(refkey, &refkeyblobsize);
497 kcsk.aksRefKey = [NSData dataWithBytes:refkeyblob length:refkeyblobsize];
498
499 size_t wrappedKeyLen = 0;
500 void* wrappedKey = NULL;
501 NSData* keypairAsData = keypair.keyData;
502 aksResult = aks_ref_key_encrypt(refkey, NULL, 0, keypairAsData.bytes, keypairAsData.length, &wrappedKey, &wrappedKeyLen);
503 if (aksResult != kAKSReturnSuccess) {
504 [self fillError:error code:SecDbBackupAKSFailure underlying:nil
505 description:@"Unable to encrypt KCSK class %d with AKS ref key: %d", class, aksResult];
506 return nil;
507 }
508 aks_ref_key_free(&refkey);
509 kcsk.aksWrappedKey = [NSData dataWithBytesNoCopy:wrappedKey length:wrappedKeyLen];
510
511 // Also add DER-encoded bag identity here
512 SFAuthenticatedEncryptionOperation* op = [[SFAuthenticatedEncryptionOperation alloc] initWithKeySpecifier:[[SFAESKeySpecifier alloc] initWithBitSize:SFAESKeyBitSize256]];
513 SFAuthenticatedCiphertext* backupwrapped = [op encrypt:keypair.keyData withKey:wrapper additionalAuthenticatedData:identData error:error];
514 kcsk.backupWrappedKey = [NSKeyedArchiver archivedDataWithRootObject:backupwrapped requiringSecureCoding:YES error:error];
515 if (!kcsk.backupWrappedKey) {
516 return nil;
517 }
518
519 return kcsk;
520 }
521
522 - (BOOL)inTransaction:(SecDbConnectionRef)dbt writeKCSKToKeychain:(SecDbBackupKeyClassSigningKey*)kcsk error:(NSError**)error
523 {
524 __block bool ok = true;
525 __block CFErrorRef cfError = NULL;
526 NSString* sql = @"INSERT INTO backupkeyclasssigningkeys (keyclass, backupUUID, signingkey) VALUES (?, ?, ?)";
527 ok &= SecDbPrepare(dbt, (__bridge CFStringRef)sql, &cfError, ^(sqlite3_stmt *stmt) {
528 ok &= SecDbBindInt(stmt, 1, kcsk.keyClass, &cfError);
529 ok &= SecDbBindObject(stmt, 2, (__bridge CFTypeRef)(self->_bagIdentity.baguuid), &cfError);
530 ok &= SecDbBindObject(stmt, 3, (__bridge CFTypeRef)(kcsk.data), &cfError);
531 ok &= SecDbStep(dbt, stmt, &cfError, NULL);
532 });
533
534 if (!ok) {
535 secerror("SecDbBackup: Unable to write KCSK for class %d to keychain: %@", kcsk.keyClass, cfError);
536 [self fillError:error code:SecDbBackupWriteFailure underlying:CFBridgingRelease(cfError) description:@"Unable to write KCSK for class %d to keychain", kcsk.keyClass];
537 }
538
539 return ok;
540 }
541
542 - (InMemoryKCSK*)onQueueReadKCSKFromDiskForClass:(keyclass_t)keyclass error:(NSError**)error
543 {
544 __block bool ok = true;
545 __block CFErrorRef cfError = NULL;
546 __block NSData* readUUID;
547 __block NSData* readKCSK;
548 NSString* sql = @"SELECT backupUUID, signingkey FROM backupkeyclasssigningkeys WHERE keyclass = ?";
549 ok &= kc_with_dbt_non_item_tables(NO, &cfError, ^bool(SecDbConnectionRef dbt) {
550 ok &= SecDbPrepare(dbt, (__bridge CFStringRef)sql, &cfError, ^(sqlite3_stmt *stmt) {
551 ok &= SecDbBindInt(stmt, 1, keyclass, &cfError);
552 ok &= SecDbStep(dbt, stmt, &cfError, ^(bool *stop) {
553 readUUID = [[NSData alloc] initWithBytes:sqlite3_column_blob(stmt, 0) length:sqlite3_column_bytes(stmt, 0)];
554 readKCSK = [[NSData alloc] initWithBytes:sqlite3_column_blob(stmt, 1) length:sqlite3_column_bytes(stmt, 1)];
555 });
556 });
557 return ok;
558 });
559 if (!readKCSK || !readUUID) {
560 [self fillError:error code:SecDbBackupNoKCSKFound underlying:nil description:@"KCSK for class %d not on disk", keyclass];
561 return nil;
562 }
563
564 SecDbBackupKeyClassSigningKey* kcsk = [[SecDbBackupKeyClassSigningKey alloc] initWithData:readKCSK];
565 if (!kcsk) {
566 [self fillError:error code:SecDbBackupMalformedKCSKDataOnDisk underlying:nil description:@"Retrieved KCSK blob but it didn't become a KCSK"];
567 return nil;
568 }
569
570 aks_ref_key_t refkey = NULL;
571 kern_return_t aksResult = aks_ref_key_create_with_blob(KEYBAG_DEVICE, kcsk.aksRefKey.bytes, kcsk.aksRefKey.length, &refkey);
572 if (aksResult != kAKSReturnSuccess) {
573 [self fillError:error
574 code:SecDbBackupAKSFailure
575 underlying:nil
576 description:@"Failed to create refkey from KCSK blob for class %d: %d", keyclass, aksResult];
577 return nil;
578 }
579
580 size_t externalDataLen = 0;
581 const uint8_t* externalData = aks_ref_key_get_external_data(refkey, &externalDataLen);
582 NSData* derBagIdent = [NSData dataWithBytes:externalData length:externalDataLen];
583 CFErrorRef cfErr = NULL;
584 NSDictionary* identData = (__bridge_transfer NSDictionary*)CFPropertyListCreateWithDERData(NULL, (__bridge CFDataRef)derBagIdent, 0, NULL, &cfErr);
585 if (!identData || ![_bagIdentity.baghash isEqualToData:identData[@"baghash"]] || ![_bagIdentity.baguuid isEqualToData:identData[@"baguuid"]]) {
586 secerror("SecDbBackup: KCSK ref key embedded bag identity does not match loaded bag. %@ vs %@", identData, _bagIdentity);
587 [self fillError:error code:SecDbBackupMalformedKCSKDataOnDisk underlying:CFBridgingRelease(cfErr) description:@"KCSK ref key embedded bag identity does not match loaded bag."];
588 return nil;
589 }
590
591 // AKS refkey claims in its external data to belong to our backup bag. Let's see if the claim holds up: use the key.
592 void* keypairBytes = NULL;
593 size_t keypairLength = 0;
594 aksResult = aks_ref_key_decrypt(refkey, NULL, 0, kcsk.aksWrappedKey.bytes, kcsk.aksWrappedKey.length, &keypairBytes, &keypairLength);
595 if (aksResult == kSKSReturnNoPermission) {
596 [self fillError:error code:SecDbBackupKeychainLocked underlying:nil description:@"Unable to unwrap KCSK private key for class %d. Locked", keyclass];
597 return nil;
598 } else if (aksResult != kAKSReturnSuccess) {
599 // Failure could indicate key was corrupted or tampered with
600 [self fillError:error
601 code:SecDbBackupAKSFailure
602 underlying:nil
603 description:@"AKS did not unwrap KCSK private key for class %d: %d", keyclass, aksResult];
604 return nil;
605 }
606
607 SFECKeyPair* keypair = [self getECKeyPairFromDERBytes:keypairBytes length:keypairLength error:error];
608 return keypair ? [InMemoryKCSK kcskWithRefKey:refkey wrappedKey:kcsk.aksWrappedKey key:keypair] : nil;
609 }
610
611 - (SFECKeyPair*)onQueueFetchKCSKForKeyclass:(keyclass_t)keyclass error:(NSError**)error
612 {
613 assert(error);
614 assert(_bagIdentity);
615 assert(_handle != bad_keybag_handle);
616
617 InMemoryKCSK* cached = _cachedKCSKs[@(keyclass)];
618 if (cached.key) {
619 return cached.key;
620 }
621
622 if (cached) {
623 secnotice("SecDbBackup", "Cached but wrapped KCSK found for class %d, unwrapping", keyclass);
624 void* keybytes = NULL;
625 size_t keylen = 0;
626 kern_return_t aksResult = aks_ref_key_decrypt(cached.refKey, NULL, 0, cached.wrappedKey.bytes, cached.wrappedKey.length, &keybytes, &keylen);
627 if (aksResult == kAKSReturnSuccess) {
628 cached.key = [self getECKeyPairFromDERBytes:keybytes length:keylen error:error];
629 return cached.key;
630 } else {
631 secerror("SecDbBackup: Cached KCSK isn't unwrapping key material. This is a bug.");
632 }
633 }
634
635 secnotice("SecDbBackup", "No cached KCSK for class %d, reading from disk", keyclass);
636 cached = [self onQueueReadKCSKFromDiskForClass:keyclass error:error];
637 if (!cached.key) {
638 secerror("SecDbBackup: Failed to obtain KCSK for class %d: %@", keyclass, *error);
639 if ((*error).code != SecDbBackupKeychainLocked) {
640 seccritical("SecDbBackup: KCSK unavailable, cannot backup-wrap class %d items. Need to perform recovery.", keyclass);
641 // TODO: We're borked. Need to recover from this.
642 }
643 }
644 _cachedKCSKs[@(keyclass)] = cached;
645 return cached.key;
646 }
647
648 #pragma mark - Recovery Set Management
649
650 - (BOOL)onQueueInTransaction:(SecDbConnectionRef)dbt writeRecoverySetToKeychain:(SecDbBackupRecoverySet*)set error:(NSError**)error
651 {
652 dispatch_assert_queue(_queue);
653 __block bool ok = true;
654 __block CFErrorRef cfError = NULL;
655
656 NSString* sql = @"INSERT INTO backuprecoverysets (backupUUID, recoverytype, recoveryset) VALUES (?, ?, ?)";
657 ok &= SecDbPrepare(dbt, (__bridge CFStringRef)sql, &cfError, ^(sqlite3_stmt *stmt) {
658 ok &= SecDbBindObject(stmt, 1, (__bridge CFDataRef)set.bagIdentity.baguuid, &cfError);
659 ok &= SecDbBindObject(stmt, 2, (__bridge CFNumberRef)@(set.recoveryType), &cfError);
660 ok &= SecDbBindObject(stmt, 3, (__bridge CFDataRef)set.data, &cfError);
661 ok &= SecDbStep(dbt, stmt, &cfError, NULL);
662 });
663
664 if (!ok) {
665 secerror("SecDbBackup: Unable to write recovery set to keychain: %@", cfError);
666 [self fillError:error code:SecDbBackupWriteFailure underlying:CFBridgingRelease(cfError) description:@"Unable to write recovery set to keychain"];
667 }
668
669 return ok;
670 }
671
672 - (SecDbBackupRecoverySet*)onQueueInTransaction:(SecDbConnectionRef)dbt createRecoverySetWithBagSecret:(NSData*)secret
673 forType:(SecDbBackupRecoveryType)type error:(NSError**)error
674 {
675 dispatch_assert_queue(_queue);
676 if (!secret) {
677 [self fillError:error code:SecDbBackupInvalidArgument underlying:nil description:@"Can't create recovery set without secret"];
678 return nil;
679 }
680
681 SecDbBackupRecoverySet* set;
682 switch (type) {
683 case SecDbBackupRecoveryTypeAKS:
684 set = [self inTransaction:dbt createAKSTypeRecoverySetWithBagSecret:secret handle:_handle error:error];
685 break;
686 case SecDbBackupRecoveryTypeCylon:
687 secerror("SecDbBackup: Cylon recovery type not yet implemented");
688 [self fillError:error code:SecDbBackupUnknownOption underlying:nil description:@"Recovery type Cylon not yet implemented"];
689 break;
690 case SecDbBackupRecoveryTypeRecoveryKey:
691 secerror("SecDbBackup: RecoveryKey recovery type not yet implemented");
692 [self fillError:error code:SecDbBackupUnknownOption underlying:nil description:@"Recovery type RecoveryKey not yet implemented"];
693 break;
694 default:
695 secerror("SecDbBackup: Unknown type %ld", (long)type);
696 [self fillError:error code:SecDbBackupUnknownOption underlying:nil description:@"Recovery type %li unknown", (long)type];
697 break;
698 }
699
700 return set;
701 }
702
703 - (SecDbBackupRecoverySet*)inTransaction:(SecDbConnectionRef)dbt createAKSTypeRecoverySetWithBagSecret:(NSData*)secret handle:(keybag_handle_t)handle error:(NSError**)error
704 {
705 SecDbBackupRecoverySet* set = [SecDbBackupRecoverySet new];
706 set.recoveryType = SecDbBackupRecoveryTypeAKS;
707 set.bagIdentity = _bagIdentity;
708
709 NSError* cryptoError;
710 SFAESKeySpecifier* specifier = [[SFAESKeySpecifier alloc] initWithBitSize:SFAESKeyBitSize256];
711 SFAESKey* KCSKSecret = [[SFAESKey alloc] initRandomKeyWithSpecifier:specifier error:&cryptoError];
712 if (!KCSKSecret) {
713 [self fillError:error code:SecDbBackupCryptoFailure underlying:cryptoError description:@"Unable to create AKS recovery set"];
714 return nil;
715 }
716
717 // We explicitly do NOT want akpu. For the rest, create and write all KCSKs
718 for (NSNumber* class in @[@(key_class_ak), @(key_class_ck), @(key_class_dk), @(key_class_aku), @(key_class_cku), @(key_class_dku)]) {
719 SecDbBackupKeyClassSigningKey* kcsk = [self createKCSKForKeyClass:[class intValue] withWrapper:KCSKSecret error:error];
720 if (!kcsk) {
721 secerror("SecDbBackup: Unable to create KCSK for class %@: %@", class, *error);
722 return nil;
723 }
724 if (![self inTransaction:dbt writeKCSKToKeychain:kcsk error:error]) {
725 secerror("SecDbBackup: Unable to write KCSK for class %@ to keychain: %@", class, *error);
726 return nil;
727 }
728 }
729
730 SFAESKey* recoverykey = [[SFAESKey alloc] initRandomKeyWithSpecifier:specifier error:&cryptoError];
731 if (!recoverykey) {
732 [self fillError:error code:SecDbBackupCryptoFailure underlying:cryptoError description:@"Unable to create recovery key"];
733 return nil;
734 }
735
736 SFAuthenticatedEncryptionOperation* op = [[SFAuthenticatedEncryptionOperation alloc]
737 initWithKeySpecifier:[[SFAESKeySpecifier alloc]
738 initWithBitSize:SFAESKeyBitSize256]];
739 SFAuthenticatedCiphertext* wrappedsecret = [op encrypt:secret withKey:recoverykey error:&cryptoError];
740 if (!wrappedsecret) {
741 secerror("SecDbBackup: Unable to wrap keybag secret: %@", cryptoError);
742 [self fillError:error code:SecDbBackupCryptoFailure underlying:cryptoError description:@"Unable to wrap keybag secret"];
743 return nil;
744 }
745 set.wrappedBagSecret = [NSKeyedArchiver archivedDataWithRootObject:wrappedsecret requiringSecureCoding:YES error:error];
746
747 SFAuthenticatedCiphertext* wrappedkcsksecret = [op encrypt:KCSKSecret.keyData withKey:recoverykey error:&cryptoError];
748 if (!wrappedkcsksecret) {
749 secerror("SecDbBackup: Unable to wrap KCSK secret: %@", cryptoError);
750 [self fillError:error code:SecDbBackupCryptoFailure underlying:cryptoError description:@"Unable to wrap KCSK secret"];
751 return nil;
752 }
753 set.wrappedKCSKSecret = [NSKeyedArchiver archivedDataWithRootObject:wrappedkcsksecret requiringSecureCoding:YES error:error];
754
755 NSMutableData* wrappedrecoverykey = [[NSMutableData alloc] initWithLength:APPLE_KEYSTORE_MAX_SYM_WRAPPED_KEY_LEN];
756 if (![SecAKSObjCWrappers aksEncryptWithKeybag:KEYBAG_DEVICE keyclass:key_class_aku plaintext:recoverykey.keyData outKeyclass:nil ciphertext:wrappedrecoverykey error:&cryptoError]) {
757 secerror("SecDbBackup: Unable to wrap recovery key to AKS: %@", cryptoError);
758 [self fillError:error code:SecDbBackupAKSFailure underlying:cryptoError description:@"Unable to wrap recovery key to AKS"];
759 return nil;
760 }
761 set.wrappedRecoveryKey = [wrappedrecoverykey copy];
762
763 return set;
764 }
765
766 #pragma mark - Backup System Initialization / Maintenance
767
768 - (BOOL)createOrLoadBackupInfrastructure:(NSError**)error {
769 assert(error);
770 __block BOOL ok = true;
771 __block NSError* localError;
772 dispatch_sync(_queue, ^{
773 ok = [self onQueueCreateOrLoadBackupInfrastructure:&localError];
774 });
775
776 if (localError) {
777 *error = localError;
778 }
779 return ok;
780 }
781
782 // TODO: if this creates the infrastructure, kick off a fixup routine
783 // TODO: if not, make sure we actually delete stuff. Nested transactions are not a thing (use checkpointing or delete explicitly)
784
785 - (BOOL)onQueueCreateOrLoadBackupInfrastructure:(NSError**)error {
786 dispatch_assert_queue(_queue);
787 assert(error);
788 if (self->_handle != bad_keybag_handle) {
789 return true;
790 }
791
792 self->_handle = [self onQueueLoadBackupBag:nil error:error];
793 if (self->_handle != bad_keybag_handle) {
794 secnotice("SecDbBackup", "Keybag found and loaded");
795 return true;
796 } else if (self->_handle == bad_keybag_handle && (*error).code != SecDbBackupNoBackupBagFound) {
797 return false;
798 }
799 *error = nil;
800
801 __block BOOL ok = YES;
802 __block CFErrorRef cfError = NULL;
803 __block NSError* localError;
804 secnotice("SecDbBackup", "CreateOrLoad: No backup bag found, attempting to create new infrastructure");
805 if (ok && !SecAKSDoWithUserBagLockAssertion(&cfError, ^{
806 ok &= kc_with_dbt_non_item_tables(YES, &cfError, ^bool(SecDbConnectionRef dbt) {
807 ok &= kc_transaction(dbt, &cfError, ^bool{
808 NSData* secret = [self createBackupBagSecret:&localError];
809 if (!secret) {
810 return false;
811 }
812
813 self->_handle = [self onQueueCreateBackupBagWithSecret:secret error:&localError];
814 if (self->_handle == bad_keybag_handle) {
815 return false;
816 }
817 secnotice("SecDbBackup", "CreateOrLoad: Successfully created backup bag");
818
819 if (![self onQueueInTransaction:dbt saveBackupBag:self->_handle asDefault:YES error:&localError]) {
820 return false;
821 }
822 secnotice("SecDbBackup", "CreateOrLoad: Successfully saved backup bag");
823
824 if (![self onQueueReloadDefaultBackupBagWithError:&localError]) {
825 return false;
826 }
827 secnotice("SecDbBackup", "CreateOrLoad: Successfully reloaded backup bag");
828
829 SecDbBackupRecoverySet* set = [self onQueueInTransaction:dbt
830 createRecoverySetWithBagSecret:secret
831 forType:SecDbBackupRecoveryTypeAKS
832 error:&localError];
833 if (!set) {
834 secnotice("SecDbBackup", "CreateOrLoad: Successfully created recovery set");
835 return false;
836 }
837
838 if (![self onQueueInTransaction:dbt writeRecoverySetToKeychain:set error:&localError]) {
839 return false;
840 }
841 secnotice("SecDbBackup", "CreateOrLoad: Successfully saved recovery set");
842
843 return true;
844 });
845 return ok;
846 });
847 })) { // could not perform action with lock assertion
848 static dispatch_once_t once;
849 static sec_action_t action;
850 dispatch_once(&once, ^{
851 action = sec_action_create("keybag_locked_during_backup_setup_complaint", 5);
852 sec_action_set_handler(action, ^{
853 secerror("SecDbBackup: Cannot obtain AKS lock assertion so cannot setup backup infrastructure");
854 });
855 });
856 sec_action_perform(action);
857 [self fillError:&localError code:SecDbBackupKeychainLocked underlying:nil
858 description:@"Unable to initialize backup infrastructure, keychain locked"];
859 ok = NO;
860 }
861
862 if (!ok) {
863 self->_bagIdentity = nil;
864 aks_unload_bag(self->_handle);
865 self->_handle = bad_keybag_handle;
866 }
867
868 if (ok) {
869 secnotice("SecDbBackup", "Hurray! Successfully created backup infrastructure");
870 } else {
871 assert(localError || cfError);
872 if (localError) {
873 secerror("SecDbBackup: Could not initialize backup infrastructure: %@", localError);
874 *error = localError;
875 } else if (cfError) {
876 secerror("SecDbBackup: Could not initialize backup infrastructure: %@", cfError);
877 [self fillError:error code:SecDbBackupSetupFailure underlying:CFBridgingRelease(cfError)
878 description:@"Unable to initialize backup infrastructure"];
879 } else {
880 secerror("SecDbBackup: Could not initialize backup infrastructure but have no error");
881 [self fillError:error code:SecDbBackupSetupFailure underlying:nil
882 description:@"Unable to initialize backup infrastructure (not sure why)"];
883 }
884 CFReleaseNull(cfError);
885 }
886
887 return ok;
888 }
889
890 #pragma mark - Item Encryption
891
892 - (SecDbBackupWrappedItemKey*)wrapItemKey:(SFAESKey*)key forKeyclass:(keyclass_t)keyclass error:(NSError**)error
893 {
894 assert(error);
895 if (keyclass == key_class_akpu) {
896 secwarning("SecDbBackup: Don't tempt me Frodo!");
897 [self fillError:error code:SecDbBackupInvalidArgument underlying:nil description:@"Do not call wrapItemKey with class akpu"];
898 return nil;
899 }
900
901 if (![self createOrLoadBackupInfrastructure:error]) {
902 if ((*error).domain != (__bridge NSString*)kSecErrorDomain || (*error).code != errSecInteractionNotAllowed) {
903 secerror("SecDbBackup: Could not create/load backup infrastructure: %@", *error);
904 }
905 return nil;
906 }
907
908 __block SecDbBackupWrappedItemKey* backupWrappedKey;
909
910 __block NSMutableData* wrappedKey = [NSMutableData dataWithLength:APPLE_KEYSTORE_MAX_ASYM_WRAPPED_KEY_LEN];
911 __block NSError* localError;
912 dispatch_sync(_queue, ^{
913 if (![self onQueueCreateOrLoadBackupInfrastructure:&localError]) {
914 return;
915 }
916 if (![SecAKSObjCWrappers aksEncryptWithKeybag:self->_handle
917 keyclass:keyclass
918 plaintext:key.keyData
919 outKeyclass:nil
920 ciphertext:wrappedKey
921 error:&localError]) {
922 return;
923 }
924 SFSignedData* wrappedAndSigned = [self onQueueSignData:wrappedKey withKCSKForKeyclass:keyclass error:&localError];
925 if (!wrappedAndSigned) {
926 if (localError.code != SecDbBackupKeychainLocked) {
927 secerror("SecDbBackup: Unable to sign item for class %d: %@", keyclass, localError);
928 return;
929 }
930 }
931 backupWrappedKey = [SecDbBackupWrappedItemKey new];
932 backupWrappedKey.wrappedKey = [NSKeyedArchiver archivedDataWithRootObject:wrappedAndSigned requiringSecureCoding:YES error:&localError];
933 backupWrappedKey.baguuid = self->_bagIdentity.baguuid;
934 });
935
936 if (localError) {
937 secerror("SecDbBackup: Unable to wrap-and-sign item of class %d: %@", keyclass, localError);
938 *error = localError;
939 return nil;
940 }
941
942 return backupWrappedKey;
943 }
944
945 - (SFSignedData*)onQueueSignData:(NSMutableData*)data withKCSKForKeyclass:(keyclass_t)keyclass error:(NSError**)error
946 {
947 SFECKeyPair* kcsk = [self onQueueFetchKCSKForKeyclass:keyclass error:error];
948 if (!kcsk) {
949 return nil;
950 }
951
952 SFEC_X962SigningOperation* op = [[SFEC_X962SigningOperation alloc] initWithKeySpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]];
953 return [op sign:data withKey:kcsk error:error];
954 }
955
956 #pragma mark - Testing Helpers
957
958 - (keybag_handle_t)createBackupBagWithSecret:(NSData*)secret error:(NSError**)error
959 {
960 assert(error);
961 __block keybag_handle_t handle = bad_keybag_handle;
962 __block NSError* localError;
963 dispatch_sync(_queue, ^{
964 handle = [self onQueueCreateBackupBagWithSecret:secret error:&localError];
965 });
966 if (localError) {
967 *error = localError;
968 } else if (handle == bad_keybag_handle) {
969 [self fillError:error code:SecDbBackupTestCodeFailure underlying:nil description:@"Unable to create backup bag, but no reason"];
970 }
971 return handle;
972 }
973
974 - (BOOL)saveBackupBag:(keybag_handle_t)handle asDefault:(BOOL)asDefault error:(NSError**)error
975 {
976 assert(error);
977 __block bool ok = true;
978 __block NSError* localErr;
979 __block CFErrorRef cfError = NULL;
980 dispatch_sync(_queue, ^{
981 ok &= kc_with_dbt_non_item_tables(YES, &cfError, ^bool(SecDbConnectionRef dbt) {
982 ok &= kc_transaction(dbt, &cfError, ^bool{
983 ok &= [self onQueueInTransaction:dbt saveBackupBag:handle asDefault:asDefault error:&localErr];
984 return ok;
985 });
986 return ok;
987 });
988 });
989
990 if (!ok) {
991 if (cfError) {
992 [self fillError:error code:SecDbBackupTestCodeFailure underlying:CFBridgingRelease(cfError) description:@"Unable to save keybag to disk"];
993 } else if (localErr) {
994 *error = localErr;
995 } else if (!localErr) {
996 [self fillError:error code:SecDbBackupTestCodeFailure underlying:nil description:@"Unable to save keybag to disk but who knows why"];
997 }
998 }
999 return ok;
1000 }
1001
1002 - (keybag_handle_t)loadBackupBag:(NSUUID*)uuid error:(NSError**)error {
1003 __block keybag_handle_t handle = bad_keybag_handle;
1004 __block NSError* localError;
1005 dispatch_sync(_queue, ^{
1006 handle = [self onQueueLoadBackupBag:uuid error:&localError];
1007 });
1008 if (error && localError) {
1009 *error = localError;
1010 }
1011 return handle;
1012 }
1013
1014 - (SecDbBackupRecoverySet*)createRecoverySetWithBagSecret:(NSData*)secret forType:(SecDbBackupRecoveryType)type error:(NSError**)error
1015 {
1016 __block SecDbBackupRecoverySet* set;
1017 __block BOOL ok = YES;
1018 __block NSError* localError;
1019 __block CFErrorRef cfError = NULL;
1020 dispatch_sync(_queue, ^{
1021 ok &= kc_with_dbt_non_item_tables(true, &cfError, ^bool(SecDbConnectionRef dbt) {
1022 ok &= kc_transaction(dbt, &cfError, ^bool{
1023 set = [self onQueueInTransaction:dbt createRecoverySetWithBagSecret:secret forType:type error:&localError];
1024 return set != nil;
1025 });
1026 return ok;
1027 });
1028 });
1029 if (error && cfError) {
1030 *error = CFBridgingRelease(cfError);
1031 } else if (error && localError) {
1032 *error = localError;
1033 }
1034 CFReleaseNull(cfError);
1035
1036 return set;
1037 }
1038
1039 - (SFECKeyPair*)fetchKCSKForKeyclass:(keyclass_t)keyclass error:(NSError**)error
1040 {
1041 __block SFECKeyPair* keypair;
1042 __block NSError* localError;
1043 dispatch_sync(_queue, ^{
1044 keypair = [self onQueueFetchKCSKForKeyclass:keyclass error:&localError];
1045 });
1046 if (localError && error) {
1047 *error = localError;
1048 }
1049
1050 return keypair;
1051 }
1052
1053 @end
1054
1055 #endif // SECDB_BACKUPS_ENABLED