/ tests / SecDbBackupTests / SecDbBackupTests.m
SecDbBackupTests.m
  1  #import "SecDbBackupTestsBase.h"
  2  #import "keychain/securityd/SecDbBackupManager.h"
  3  
  4  #if !SECDB_BACKUPS_ENABLED
  5  
  6  @interface SecDbBackupTests : XCTestCase
  7  @end
  8  
  9  @implementation SecDbBackupTests
 10  @end
 11  
 12  #else // SECDB_BACKUPS_ENABLED
 13  
 14  #import "keychain/securityd/SecDbBackupManager_Internal.h"
 15  
 16  
 17  #import <objc/runtime.h>
 18  #include "utilities/der_plist.h"
 19  #include <Security/SecItemPriv.h>
 20  
 21  @interface SecDbBackupTests : SecDbBackupTestsBase
 22  
 23  @end
 24  
 25  SecDbBackupManager* _manager;
 26  
 27  @implementation SecDbBackupTests
 28  
 29  + (void)setUp {
 30      [super setUp];
 31  }
 32  
 33  + (void)tearDown {
 34  	[super tearDown];
 35  }
 36  
 37  - (void)setUp {
 38      [super setUp];
 39      [SecDbBackupManager resetManager];
 40      _manager = [SecDbBackupManager manager];
 41  }
 42  
 43  - (void)setFakeBagIdentity {
 44      SecDbBackupBagIdentity* identity = [SecDbBackupBagIdentity new];
 45      NSUUID* nsuuid = [NSUUID UUID];
 46      uuid_t uuid;
 47      [nsuuid getUUIDBytes:uuid];
 48      identity.baguuid = [NSData dataWithBytes:uuid length:UUIDBYTESLENGTH];
 49      NSMutableData* digest = [NSMutableData dataWithLength:CC_SHA512_DIGEST_LENGTH];
 50      CC_SHA512(identity.baguuid.bytes, (CC_LONG)identity.baguuid.length, digest.mutableBytes);
 51      identity.baghash = digest;
 52      _manager.bagIdentity = identity;
 53  }
 54  
 55  - (SFAESKey*)randomAESKey {
 56      return [[SFAESKey alloc] initRandomKeyWithSpecifier:[[SFAESKeySpecifier alloc] initWithBitSize:SFAESKeyBitSize256] error:nil];
 57  }
 58  
 59  - (NSData*)bagIdentData {
 60      NSDictionary* bagIdentDict = @{@"baguuid" : _manager.bagIdentity.baguuid, @"baghash" : _manager.bagIdentity.baghash};
 61      return (__bridge_transfer NSData*)CFPropertyListCreateDERData(NULL, (__bridge CFDictionaryRef)bagIdentDict, NULL);
 62  }
 63  
 64  #pragma mark - Tests
 65  
 66  - (void)testAAA_MakeSureThisMakesSense {
 67      XCTAssertEqual(checkV12DevEnabled(), 1, "V12 dev flag is off, no good will come of this");
 68      if (!checkV12DevEnabled) {
 69          abort();
 70      }
 71  }
 72  
 73  - (void)testCreateBackupBagSecret {
 74      NSError* error;
 75      NSData* secret = [_manager createBackupBagSecret:&error];
 76      XCTAssertNil(error, "Error creating backup bag secret");
 77      XCTAssertNotNil(secret, "No NSData from creating backup bag secret: %@", error);
 78      XCTAssertEqual(secret.length, BACKUPBAG_PASSPHRASE_LENGTH, "NSData is not %i bytes long", BACKUPBAG_PASSPHRASE_LENGTH);
 79  
 80      // Good luck testing randomness, but let's stipulate we don't accept all-zeroes as a key
 81      uint8_t buf[BACKUPBAG_PASSPHRASE_LENGTH] = {0};
 82      XCTAssertNotEqual(memcmp(secret.bytes, buf, MIN(BACKUPBAG_PASSPHRASE_LENGTH, secret.length)), 0, "Secret is all zeroes");
 83  
 84      XCTAssert([secret isMemberOfClass:objc_lookUpClass("_NSClrDat")], "Secret is not a zeroing NSData");
 85  }
 86  
 87  - (void)testCreateAndSaveBackupBag {
 88      NSError* error;
 89      NSData* secret = [_manager createBackupBagSecret:&error];
 90      XCTAssertNil(error, "Unable to generate secret");
 91      XCTAssertNotNil(secret, "Didn't get secret");
 92      keybag_handle_t handle = [_manager createBackupBagWithSecret:secret error:&error];
 93      XCTAssertNil(error, "Got error creating backup bag: %@", error);
 94      XCTAssertNotEqual(handle, bad_keybag_handle, "Unexpected bag handle");
 95      keybag_state_t keybagstate;
 96      XCTAssertEqual(aks_get_lock_state(handle, &keybagstate), kAKSReturnSuccess, "Unable to check lock state of backup bag");
 97      XCTAssert(keybagstate | keybag_state_locked, "Keybag unexpectedly not locked");
 98      XCTAssert(keybagstate | keybag_state_been_unlocked, "Keybag unexpectedly never been unlocked (huh?)");
 99  
100      XCTAssert([_manager saveBackupBag:handle asDefault:YES error:&error]);
101      XCTAssertNil(error, "Error saving backup bag to keychain");
102      XCTAssertEqual(aks_unload_bag(handle), kAKSReturnSuccess, "Couldn't unload backup bag");
103  }
104  
105  - (void)testCreateAndSaveBagTwice {
106      NSError* error;
107      NSData* secret = [_manager createBackupBagSecret:&error];
108      XCTAssertNil(error, "Unable to generate secret");
109      XCTAssertNotNil(secret, "Didn't get secret");
110      keybag_handle_t handle = [_manager createBackupBagWithSecret:secret error:&error];
111      XCTAssertNil(error, "Got error creating backup bag: %@", error);
112      XCTAssertNotEqual(handle, bad_keybag_handle, "Unexpected bag handle");
113  
114      XCTAssert([_manager saveBackupBag:handle asDefault:YES error:&error]);
115      XCTAssertNil(error, "Error saving backup bag to keychain");
116  
117      XCTAssertFalse([_manager saveBackupBag:handle asDefault:YES error:&error]);
118      XCTAssert(error, "Unexpectedly did not get error saving same bag twice");
119      XCTAssertEqual(error.code, SecDbBackupWriteFailure, "Unexpected error code for double insertion: %@", error);
120      XCTAssertEqual(aks_unload_bag(handle), kAKSReturnSuccess, "Couldn't unload backup bag");
121  }
122  
123  - (void)testLoadNonExistentDefaultBackupBag {
124      NSError* error;
125      XCTAssertEqual([_manager loadBackupBag:nil error:&error], bad_keybag_handle, "Found default bag after not inserting any");
126      XCTAssertEqual(error.code, SecDbBackupNoBackupBagFound, "Didn't get an appropriate error for missing keybag: %@", error);
127  }
128  
129  - (void)testLoadDefaultBackupBag {
130      NSError* error;
131      keybag_handle_t handle = bad_keybag_handle;
132      handle = [_manager createBackupBagWithSecret:[_manager createBackupBagSecret:&error] error:&error];
133      XCTAssertNotEqual(handle, bad_keybag_handle, "Didn't get a good keybag handle");
134      XCTAssertNotEqual(handle, device_keybag_handle, "Got device keybag handle (or manager is nil)");
135      XCTAssertNil(error, "Error creating backup bag");
136      [_manager saveBackupBag:handle asDefault:YES error:&error];
137      XCTAssertNil(error, "Error saving backup bag");
138  
139      uuid_t uuid1 = {0};
140      XCTAssertEqual(aks_get_bag_uuid(handle, uuid1), kAKSReturnSuccess, "Couldn't get bag uuid");
141      XCTAssertEqual(aks_unload_bag(handle), kAKSReturnSuccess, "Couldn't unload backup bag");
142      handle = bad_keybag_handle;
143  
144      handle = [_manager loadBackupBag:nil error:&error];
145      XCTAssertNotEqual(handle, bad_keybag_handle, "Got bad handle loading default keybag");
146      XCTAssertNil(error, "Got error loading default keybag");
147  
148      uuid_t uuid2 = {0};
149      XCTAssertEqual(aks_get_bag_uuid(handle, uuid2), kAKSReturnSuccess, "Couldn't get bag uuid");
150      XCTAssertEqual(aks_unload_bag(handle), kAKSReturnSuccess, "Couldn't unload backup bag");
151      XCTAssertEqual(memcmp(uuid1, uuid2, UUIDBYTESLENGTH), 0, "UUIDs do not match after backup bag save/load");
152  
153      // sanity check
154      uuid_t uuidnull = {0};
155      XCTAssertNotEqual(memcmp(uuid1, uuidnull, UUIDBYTESLENGTH), 0, "uuid1 is all zeroes");
156      XCTAssertNotEqual(memcmp(uuid2, uuidnull, UUIDBYTESLENGTH), 0, "uuid2 is all zeroes");
157  
158      // TODO: signature match?
159  }
160  
161  - (void)testLoadBackupBagByUUID {
162      NSError* error;
163      keybag_handle_t handle1 = bad_keybag_handle;
164      handle1 = [_manager createBackupBagWithSecret:[_manager createBackupBagSecret:&error] error:&error];
165      XCTAssertNotEqual(handle1, bad_keybag_handle, "Didn't get a good keybag handle");
166      XCTAssertNil(error, "Error creating backup bag");
167      XCTAssert([_manager saveBackupBag:handle1 asDefault:NO error:&error], "Unable to save bag 1");
168      XCTAssertNil(error, "Error saving backup bag");
169  
170      keybag_handle_t handle2 = bad_keybag_handle;
171      handle2 = [_manager createBackupBagWithSecret:[_manager createBackupBagSecret:&error] error:&error];
172      XCTAssertNotEqual(handle2, bad_keybag_handle, "Didn't get a good keybag handle");
173      XCTAssertNil(error, "Error creating backup bag");
174      XCTAssert([_manager saveBackupBag:handle2 asDefault:NO error:&error], "Unable to save bag 2");
175      XCTAssertNil(error, "Error saving backup bag");
176  
177      uuid_t uuid1 = {0};
178      uuid_t uuid2 = {0};
179      XCTAssertEqual(aks_get_bag_uuid(handle1, uuid1), kAKSReturnSuccess, "Couldn't get bag 1 uuid");
180      XCTAssertEqual(aks_get_bag_uuid(handle2, uuid2), kAKSReturnSuccess, "Couldn't get bag 2 uuid");
181      XCTAssertEqual(aks_unload_bag(handle1), kAKSReturnSuccess, "Couldn't unload backup bag 1");
182      XCTAssertEqual(aks_unload_bag(handle2), kAKSReturnSuccess, "Couldn't unload backup bag 2");
183      handle1 = bad_keybag_handle;
184      handle2 = bad_keybag_handle;
185  
186      XCTAssertNotEqual(handle1 = [_manager loadBackupBag:[[NSUUID alloc] initWithUUIDBytes:uuid1] error:&error], bad_keybag_handle, "Didn't get handle loading bag 1 by UUID");
187      XCTAssertNotEqual(handle2 = [_manager loadBackupBag:[[NSUUID alloc] initWithUUIDBytes:uuid2] error:&error], bad_keybag_handle, "Didn't get handle loading bag 2 by UUID");
188  
189      uuid_t uuid1_2 = {0};
190      uuid_t uuid2_2 = {0};
191      XCTAssertEqual(aks_get_bag_uuid(handle1, uuid1_2), kAKSReturnSuccess, "Couldn't get bag 1 uuid");
192      XCTAssertEqual(aks_get_bag_uuid(handle2, uuid2_2), kAKSReturnSuccess, "Couldn't get bag 2 uuid");
193  
194      XCTAssertEqual(memcmp(uuid1, uuid1_2, UUIDBYTESLENGTH), 0, "UUIDs do not match after bag 1 save/load");
195      XCTAssertEqual(memcmp(uuid2, uuid2_2, UUIDBYTESLENGTH), 0, "UUIDs do not match after bag 2 save/load");
196  
197      XCTAssertEqual(aks_unload_bag(handle1), kAKSReturnSuccess, "Couldn't unload backup bag 1");
198      XCTAssertEqual(aks_unload_bag(handle2), kAKSReturnSuccess, "Couldn't unload backup bag 2");
199  }
200  
201  - (void)testCreateBackupInfrastructure
202  {
203      NSError* error;
204      XCTAssert([_manager createOrLoadBackupInfrastructure:&error], @"Couldn't create/load backup infrastructure");
205      XCTAssertNil(error, @"Error creating/loading backup infrastructure");
206  
207      SFECKeyPair* ak = [_manager fetchKCSKForKeyclass:key_class_ak error:&error];
208      XCTAssertNotNil(ak);
209      XCTAssertNil(error);
210  
211      SFECKeyPair* ck = [_manager fetchKCSKForKeyclass:key_class_ck error:&error];
212      XCTAssertNotNil(ck);
213      XCTAssertNil(error);
214  
215      SFECKeyPair* dk = [_manager fetchKCSKForKeyclass:key_class_dk error:&error];
216      XCTAssertNotNil(dk);
217      XCTAssertNil(error);
218  
219      SFECKeyPair* aku = [_manager fetchKCSKForKeyclass:key_class_aku error:&error];
220      XCTAssertNotNil(aku);
221      XCTAssertNil(error);
222  
223      SFECKeyPair* cku = [_manager fetchKCSKForKeyclass:key_class_cku error:&error];
224      XCTAssertNotNil(cku);
225      XCTAssertNil(error);
226  
227      SFECKeyPair* dku = [_manager fetchKCSKForKeyclass:key_class_dku error:&error];
228      XCTAssertNotNil(dku);
229      XCTAssertNil(error);
230  
231      SFECKeyPair* akpu = [_manager fetchKCSKForKeyclass:key_class_akpu error:&error];
232      XCTAssertNil(akpu);
233      XCTAssertEqual(error.code, SecDbBackupNoKCSKFound);
234  }
235  
236  - (void)testCreateOrLoadBackupInfrastructureFromC
237  {
238      CFErrorRef cferror = NULL;
239      XCTAssertTrue(SecDbBackupCreateOrLoadBackupInfrastructure(&cferror), @"Could create backup infrastructure from C");
240      XCTAssertFalse(cferror, @"Do not expect error creating backup infrastructure from C: %@", cferror);
241      CFReleaseNull(cferror);
242  }
243  
244  // Should not run this on real AKS because don't want to lock keybag
245  - (void)disabledtestCreateOrLoadBackupInfrastructureWhileLocked
246  {
247      NSError* error;
248      XCTAssertFalse([_manager createOrLoadBackupInfrastructure:&error], @"Keychain locked, don't expect to create infrastructure");
249      XCTAssertEqual(error.code, SecDbBackupKeychainLocked, @"Expected failure creating backup infrastructure while locked");
250  }
251  
252  // Should not run this on real AKS because don't want to lock keybag
253  - (void)disabledtestCreateOrLoadBackupInfrastructureWhileLockedFromC
254  {
255      CFErrorRef cferror = NULL;
256      XCTAssertFalse(SecDbBackupCreateOrLoadBackupInfrastructure(&cferror), @"Could create backup infrastructure from C");
257      XCTAssertTrue(cferror, @"Expect error creating backup infrastructure while locked from C: %@", cferror);
258      if (cferror) {
259          XCTAssertEqual(CFErrorGetCode(cferror), errSecInteractionNotAllowed, @"Expect errSecInteractionNotAllowed creating backup infrastructure while locked from C");
260      }
261      CFReleaseNull(cferror);
262  }
263  
264  - (void)testOnlyOneDefaultBackupBag {
265      // Generate two backup bags, each successively claiming makeDefault
266      // Expect: the second call should fail
267  }
268  
269  - (void)testLoadBackupBagFromDifferentDevice {
270      // Generate keybag on some device, manually insert it on some other device and try to load it.
271      // Expect: failure unless in recovery mode.
272  }
273  
274  - (void)testLoadBackupBagWithGarbageData {
275      // manually write garbage to keychain, then call loadBackupBag
276      // Expect: ?
277  }
278  
279  - (void)testCreateKCSK {
280      [self setFakeBagIdentity];
281  
282      NSError* error;
283      XCTAssertNil([_manager createKCSKForKeyClass:key_class_ck withWrapper:nil error:&error], @"Shouldn't get KCSK without wrapper");
284      XCTAssertEqual(error.code, SecDbBackupInvalidArgument, @"createKSCKForKeyClass ought to be angry about not having a wrapper");
285      error = nil;
286  
287      SFAESKey* key = [self randomAESKey];
288      XCTAssertNotNil(key, @"Expect key from SFAESKey");
289      SecDbBackupKeyClassSigningKey* kcsk = [_manager createKCSKForKeyClass:key_class_ak withWrapper:key error:&error];
290      XCTAssertNotNil(kcsk, @"Got a KCSK");
291      XCTAssertNil(error, @"Did not expect KCSK error: %@", error);
292  
293      // Let's examine the KCSK
294  
295      XCTAssertEqual(kcsk.keyClass, key_class_ak, @"key class matches");
296  
297      // Verify refkey
298      aks_ref_key_t refkey = NULL;
299      XCTAssertNotNil(kcsk.aksRefKey, @"Got an AKS ref key");
300      XCTAssertEqual(aks_ref_key_create_with_blob(KEYBAG_DEVICE, kcsk.aksRefKey.bytes,
301                                                  kcsk.aksRefKey.length, &refkey), kAKSReturnSuccess, @"Got a refkey out of kcsk blob");
302  
303      // Verify aksWrappedKey
304      void* aksunwrappedbytes = NULL;
305      size_t aksunwrappedlen = 0;
306      XCTAssertEqual(aks_ref_key_decrypt(refkey, NULL, 0, kcsk.aksWrappedKey.bytes, kcsk.aksWrappedKey.length, &aksunwrappedbytes, &aksunwrappedlen), kAKSReturnSuccess, @"Successfully unwrapped KCSK private key");
307      SFECKeyPair* aksunwrapped = [_manager getECKeyPairFromDERBytes:aksunwrappedbytes length:aksunwrappedlen error:&error];
308      XCTAssertNil(error, @"No error reconstructing AKS backup key");
309      XCTAssert(aksunwrapped, @"Got key from getECKeyPairFromDERBytes");
310      aks_ref_key_free(&refkey);
311  
312      // Verify backupWrappedKey
313      SFAuthenticatedEncryptionOperation* op = [[SFAuthenticatedEncryptionOperation alloc] initWithKeySpecifier:[[SFAESKeySpecifier alloc] initWithBitSize:SFAESKeyBitSize256]];
314      SFAuthenticatedCiphertext* ciphertext = [NSKeyedUnarchiver unarchivedObjectOfClass:[SFAuthenticatedCiphertext class] fromData:kcsk.backupWrappedKey error:&error];
315      XCTAssertNotNil(ciphertext, @"Reconstituted ciphertext from kcsk (%@)", error);
316      XCTAssertNil(error, @"Didn't expect error reconstituting ciphertext from kcsk: %@", error);
317  
318      NSData* bagIdentData = [self bagIdentData];
319      NSData* bkunwrappedData = [op decrypt:ciphertext withKey:key additionalAuthenticatedData:bagIdentData error:&error];
320      XCTAssertNotNil(bkunwrappedData, @"backup-wrapped key decrypts");
321      SFECKeyPair* bkunwrapped = [[SFECKeyPair alloc] initWithData:bkunwrappedData specifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384] error:&error];
322      XCTAssertNotNil(bkunwrapped, @"unwrapped blob turns into an SFECKey (%@)", error);
323  
324      XCTAssertEqualObjects(aksunwrapped, bkunwrapped, @"Private key same between aks and bk");
325  }
326  
327  - (void)testCreateRecoverySetForRecoveryKey {
328      // Not Implemented
329  }
330  
331  - (void)testCreateRecoverySetForAKS {
332      NSError* error;
333  
334      [self setFakeBagIdentity];
335  
336      SecDbBackupRecoverySet* set = [_manager createRecoverySetWithBagSecret:nil forType:SecDbBackupRecoveryTypeAKS error:&error];
337      XCTAssertNil(set, @"No set without secret!");
338      XCTAssertEqual(error.code, SecDbBackupInvalidArgument, @"Expected different error without secret: %@", error);
339      error = nil;
340  
341      NSData* secret = [_manager createBackupBagSecret:&error];
342      set = [_manager createRecoverySetWithBagSecret:secret forType:SecDbBackupRecoveryTypeAKS error:&error];
343      XCTAssertNotNil(set, @"Got aks recoveryset from backup manager");
344      XCTAssertNil(error, @"Didn't expect error obtaining recoveryset: %@", error);
345  
346      XCTAssertEqual(set.recoveryType, SecDbBackupRecoveryTypeAKS, @"Unexpected recovery type");
347      XCTAssertEqualObjects(set.bagIdentity, _manager.bagIdentity, @"Bag identity copied properly");
348      XCTAssertNotNil(set.wrappedBagSecret, @"Have bag secret in recovery set");
349      XCTAssertNotNil(set.wrappedKCSKSecret, @"Have kcsk secret in recovery set");
350      XCTAssertNotNil(set.wrappedRecoveryKey, @"Have recovery key in recovery set");
351  
352      NSMutableData* recoverykeydata = [NSMutableData dataWithLength:APPLE_KEYSTORE_MAX_KEY_LEN];
353      [SecAKSObjCWrappers aksDecryptWithKeybag:KEYBAG_DEVICE keyclass:key_class_aku
354                                    ciphertext:set.wrappedRecoveryKey outKeyclass:nil plaintext:recoverykeydata error:&error];
355      XCTAssertNil(error, @"Able to decrypt recovery key: %@", error);
356      SFAESKey* recoverykey = [[SFAESKey alloc] initWithData:recoverykeydata specifier:[[SFAESKeySpecifier alloc]
357                                                                                        initWithBitSize:SFAESKeyBitSize256] error:&error];
358      XCTAssert(recoverykey, @"Got a recovery key from blob");
359      XCTAssertNil(error, @"Didn't get error from recovery key blob: %@", error);
360  
361      SFAuthenticatedEncryptionOperation* op = [[SFAuthenticatedEncryptionOperation alloc] initWithKeySpecifier:[[SFAESKeySpecifier alloc]
362                                                                                                                 initWithBitSize:SFAESKeyBitSize256]];
363      NSData* bagsecret = [op decrypt:[NSKeyedUnarchiver unarchivedObjectOfClass:[SFAuthenticatedCiphertext class] fromData:set.wrappedBagSecret error:&error] withKey:recoverykey error:&error];
364      XCTAssert(bagsecret, @"Reconstituted bag secret");
365      XCTAssertNil(error, @"Didn't expect error reconstituting bag secret: %@", error);
366      XCTAssertEqualObjects(bagsecret, secret, @"Returned bag secret same as provided secret");
367  
368      NSData* kcsksecret = [op decrypt:[NSKeyedUnarchiver unarchivedObjectOfClass:[SFAuthenticatedCiphertext class] fromData:set.wrappedKCSKSecret error:&error] withKey:recoverykey error:&error];
369      XCTAssert(kcsksecret, @"Reconstituted kcsk secret");
370      XCTAssertNil(error, @"Didn't expect error reconstituting kcsk secret: %@", error);
371  }
372  
373  - (void)testWrapItemKey {
374      SFAESKey* randomKey = [self randomAESKey];
375      NSError* error;
376      SecDbBackupWrappedKey* itemKey = [_manager wrapItemKey:randomKey forKeyclass:key_class_akpu error:&error];
377      XCTAssertNil(itemKey, @"Do not expect result wrapping to akpu");
378      XCTAssertEqual(error.code, SecDbBackupInvalidArgument, @"Expect invalid argument error wrapping to akpu");
379  
380      error = nil;
381      itemKey = [_manager wrapItemKey:randomKey forKeyclass:key_class_ak error:&error];
382      XCTAssertNil(error, @"No error wrapping item to ak");
383      XCTAssertEqualObjects(itemKey.baguuid, _manager.bagIdentity.baguuid, @"item wrapped under expected bag uuid");
384  
385      // TODO: implement decryption and test it
386  }
387  
388  - (void)testWrapMetadataKey {
389      SFAESKey* randomKey = [self randomAESKey];
390      NSError* error;
391      SecDbBackupWrappedKey* itemKey = [_manager wrapMetadataKey:randomKey forKeyclass:key_class_akpu error:&error];
392      XCTAssertNil(itemKey, @"Do not expect result wrapping to akpu");
393      XCTAssertEqual(error.code, SecDbBackupInvalidArgument, @"Expect invalid argument error wrapping to akpu");
394  
395      error = nil;
396      itemKey = [_manager wrapMetadataKey:randomKey forKeyclass:key_class_ak error:&error];
397      XCTAssertNil(error, @"No error wrapping item to ak");
398      XCTAssertEqualObjects(itemKey.baguuid, _manager.bagIdentity.baguuid, @"item wrapped under expected bag uuid");
399  
400      // TODO: implement decryption and test it
401  }
402  
403  // Does not inspect the item because it's encrypted and no code yet built to do recovery.
404  - (void)testSecItemAddAddsBackupEncryption {
405      NSDictionary* q = @{(id)kSecClass : (id)kSecClassGenericPassword,
406                          (id)kSecValueData : [@"password" dataUsingEncoding:NSUTF8StringEncoding],
407                          (id)kSecAttrAccessGroup : @"com.apple.security.securityd",
408                          (id)kSecUseDataProtectionKeychain : @(YES),
409                          (id)kSecReturnAttributes : @(YES)
410                          };
411      OSStatus status = SecItemAdd((__bridge CFDictionaryRef)q, NULL);
412      XCTAssertEqual(status, errSecSuccess, @"Regular old SecItemAdd succeeds");
413  
414      __block CFErrorRef cfError = NULL;
415      __block bool ok = true;
416      __block NSData* readUUID;
417      ok &= kc_with_dbt(false, &cfError, ^bool(SecDbConnectionRef dbt) {
418          NSString* sql = @"SELECT backupUUID FROM genp WHERE agrp = 'com.apple.security.securityd'";
419          ok &= SecDbPrepare(dbt, (__bridge CFStringRef)sql, &cfError, ^(sqlite3_stmt *stmt) {
420              ok &= SecDbStep(dbt, stmt, &cfError, ^(bool *stop) {
421                  readUUID = [[NSData alloc] initWithBytes:sqlite3_column_blob(stmt, 0) length:sqlite3_column_bytes(stmt, 0)];
422              });
423          });
424          return ok;
425      });
426  
427      XCTAssert(ok, @"Talking to keychain went okay");
428      XCTAssertEqual(cfError, NULL, @"Talking to keychain didn't yield an error (%@)", cfError);
429      CFReleaseNull(cfError);
430      XCTAssert(readUUID, @"Got stuff out of the keychain");
431  
432      XCTAssertEqualObjects(readUUID, _manager.bagIdentity.baguuid, @"backup UUID is good");
433  }
434  
435  @end
436  
437  #endif  // SECDB_BACKUPS_ENABLED