2 * Copyright (c) 2017 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 #import <CloudKit/CloudKit.h>
27 #import <XCTest/XCTest.h>
28 #import <OCMock/OCMock.h>
31 #include <Security/SecItemPriv.h>
33 #import "keychain/ckks/tests/CloudKitMockXCTest.h"
34 #import "keychain/ckks/tests/CloudKitKeychainSyncingMockXCTest.h"
35 #import "keychain/ckks/CKKS.h"
36 #import "keychain/ckks/CKKSKeychainView.h"
37 #import "keychain/ckks/CKKSCurrentKeyPointer.h"
38 #import "keychain/ckks/CKKSItemEncrypter.h"
39 #import "keychain/ckks/CKKSKey.h"
40 #import "keychain/ckks/CKKSOutgoingQueueEntry.h"
41 #import "keychain/ckks/CKKSIncomingQueueEntry.h"
42 #import "keychain/ckks/CKKSSynchronizeOperation.h"
43 #import "keychain/ckks/CKKSViewManager.h"
44 #import "keychain/ckks/CKKSZoneStateEntry.h"
45 #import "keychain/ckks/CKKSManifest.h"
47 #import "keychain/ckks/tests/MockCloudKit.h"
49 #pragma clang diagnostic push
50 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
51 #include <Security/SecureObjectSync/SOSCloudCircle.h>
52 #include <Security/SecureObjectSync/SOSAccount.h>
53 #include <Security/SecureObjectSync/SOSInternal.h>
54 #include <Security/SecureObjectSync/SOSFullPeerInfo.h>
55 #include <Security/SecKey.h>
56 #include <Security/SecKeyPriv.h>
57 #pragma clang diagnostic pop
59 @interface CloudKitKeychainSyncingSOSIntegrationTests : CloudKitKeychainSyncingMockXCTest
61 @property CKRecordZoneID* engramZoneID;
62 @property CKKSKeychainView* engramView;
63 @property FakeCKZone* engramZone;
64 @property (readonly) ZoneKeys* engramZoneKeys;
66 @property CKRecordZoneID* manateeZoneID;
67 @property CKKSKeychainView* manateeView;
68 @property FakeCKZone* manateeZone;
69 @property (readonly) ZoneKeys* manateeZoneKeys;
71 @property CKRecordZoneID* autoUnlockZoneID;
72 @property CKKSKeychainView* autoUnlockView;
73 @property FakeCKZone* autoUnlockZone;
74 @property (readonly) ZoneKeys* autoUnlockZoneKeys;
76 @property CKRecordZoneID* healthZoneID;
77 @property CKKSKeychainView* healthView;
78 @property FakeCKZone* healthZone;
79 @property (readonly) ZoneKeys* healthZoneKeys;
81 @property CKRecordZoneID* applepayZoneID;
82 @property CKKSKeychainView* applepayView;
83 @property FakeCKZone* applepayZone;
84 @property (readonly) ZoneKeys* applepayZoneKeys;
88 @implementation CloudKitKeychainSyncingSOSIntegrationTests
91 SecCKKSResetSyncing();
97 (void)[CKKSManifest shouldSyncManifests]; // initialize.
98 SecCKKSSetSyncManifests(false);
99 SecCKKSSetEnforceManifests(false);
102 SecCKKSTestSetDisableSOS(false);
104 // Wait for the ViewManager to be brought up
105 XCTAssertEqual(0, [self.injectedManager.completedSecCKKSInitialize wait:4*NSEC_PER_SEC], "No timeout waiting for SecCKKSInitialize");
107 self.engramZoneID = [[CKRecordZoneID alloc] initWithZoneName:@"Engram" ownerName:CKCurrentUserDefaultName];
108 self.engramZone = [[FakeCKZone alloc] initZone: self.engramZoneID];
109 self.zones[self.engramZoneID] = self.engramZone;
110 self.engramView = [[CKKSViewManager manager] findView:@"Engram"];
111 XCTAssertNotNil(self.engramView, "CKKSViewManager created the Engram view");
113 self.manateeZoneID = [[CKRecordZoneID alloc] initWithZoneName:@"Manatee" ownerName:CKCurrentUserDefaultName];
114 self.manateeZone = [[FakeCKZone alloc] initZone: self.manateeZoneID];
115 self.zones[self.manateeZoneID] = self.manateeZone;
116 self.manateeView = [[CKKSViewManager manager] findView:@"Manatee"];
117 XCTAssertNotNil(self.manateeView, "CKKSViewManager created the Manatee view");
119 self.autoUnlockZoneID = [[CKRecordZoneID alloc] initWithZoneName:@"AutoUnlock" ownerName:CKCurrentUserDefaultName];
120 self.autoUnlockZone = [[FakeCKZone alloc] initZone: self.autoUnlockZoneID];
121 self.zones[self.autoUnlockZoneID] = self.autoUnlockZone;
122 self.autoUnlockView = [[CKKSViewManager manager] findView:@"AutoUnlock"];
123 XCTAssertNotNil(self.autoUnlockView, "CKKSViewManager created the AutoUnlock view");
125 self.healthZoneID = [[CKRecordZoneID alloc] initWithZoneName:@"Health" ownerName:CKCurrentUserDefaultName];
126 self.healthZone = [[FakeCKZone alloc] initZone: self.healthZoneID];
127 self.zones[self.healthZoneID] = self.healthZone;
128 self.healthView = [[CKKSViewManager manager] findView:@"Health"];
129 XCTAssertNotNil(self.healthView, "CKKSViewManager created the Health view");
131 self.applepayZoneID = [[CKRecordZoneID alloc] initWithZoneName:@"ApplePay" ownerName:CKCurrentUserDefaultName];
132 self.applepayZone = [[FakeCKZone alloc] initZone: self.healthZoneID];
133 self.zones[self.applepayZoneID] = self.applepayZone;
134 self.applepayView = [[CKKSViewManager manager] findView:@"ApplePay"];
135 XCTAssertNotNil(self.applepayView, "CKKSViewManager created the ApplePay view");
139 SecCKKSTestSetDisableSOS(true);
141 SecCKKSResetSyncing();
145 // If the test didn't already do this, allow each zone to spin up
146 self.accountStatus = CKAccountStatusNoAccount;
147 [self startCKKSSubsystem];
149 [self.engramView cancelAllOperations];
150 [self.engramView waitUntilAllOperationsAreFinished];
151 self.engramView = nil;
153 [self.manateeView cancelAllOperations];
154 [self.manateeView waitUntilAllOperationsAreFinished];
155 self.manateeView = nil;
157 [self.autoUnlockView cancelAllOperations];
158 [self.autoUnlockView waitUntilAllOperationsAreFinished];
159 self.autoUnlockView = nil;
161 [self.healthView cancelAllOperations];
162 [self.healthView waitUntilAllOperationsAreFinished];
163 self.healthView = nil;
165 [self.applepayView cancelAllOperations];
166 [self.applepayView waitUntilAllOperationsAreFinished];
167 self.applepayView = nil;
172 - (ZoneKeys*)engramZoneKeys {
173 return self.keys[self.engramZoneID];
176 - (ZoneKeys*)manateeZoneKeys {
177 return self.keys[self.manateeZoneID];
180 -(void)saveFakeKeyHierarchiesToLocalDatabase {
181 [self createAndSaveFakeKeyHierarchy: self.engramZoneID];
182 [self createAndSaveFakeKeyHierarchy: self.manateeZoneID];
183 [self createAndSaveFakeKeyHierarchy: self.autoUnlockZoneID];
184 [self createAndSaveFakeKeyHierarchy: self.healthZoneID];
185 [self createAndSaveFakeKeyHierarchy: self.applepayZoneID];
188 -(void)testAddEngramManateeItems {
189 [self saveFakeKeyHierarchiesToLocalDatabase]; // Make life easy for this test.
191 [self startCKKSSubsystem];
193 XCTestExpectation* engramChanged = [self expectChangeForView:self.engramZoneID.zoneName];
194 XCTestExpectation* pcsChanged = [self expectChangeForView:@"PCS"];
195 XCTestExpectation* manateeChanged = [self expectChangeForView:self.manateeZoneID.zoneName];
197 // We expect a single record to be uploaded to the engram view.
198 [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.engramZoneID];
199 [self addGenericPassword: @"data" account: @"account-delete-me-engram" viewHint:(NSString*) kSecAttrViewHintEngram];
201 OCMVerifyAllWithDelay(self.mockDatabase, 20);
202 [self waitForExpectations:@[engramChanged] timeout:1];
203 [self waitForExpectations:@[pcsChanged] timeout:1];
205 pcsChanged = [self expectChangeForView:@"PCS"];
207 // We expect a single record to be uploaded to the manatee view.
208 [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.manateeZoneID];
209 [self addGenericPassword: @"data" account: @"account-delete-me-manatee" viewHint:(NSString*) kSecAttrViewHintManatee];
211 OCMVerifyAllWithDelay(self.mockDatabase, 8);
212 [self waitForExpectations:@[manateeChanged] timeout:1];
213 [self waitForExpectations:@[pcsChanged] timeout:1];
216 -(void)testAddAutoUnlockItems {
217 [self saveFakeKeyHierarchiesToLocalDatabase]; // Make life easy for this test.
219 [self startCKKSSubsystem];
221 XCTestExpectation* autoUnlockChanged = [self expectChangeForView:self.autoUnlockZoneID.zoneName];
222 // AutoUnlock is NOT is PCS view, so it should not send the fake 'PCS' view notification
223 XCTestExpectation* pcsChanged = [self expectChangeForView:@"PCS"];
224 pcsChanged.inverted = YES;
226 // We expect a single record to be uploaded to the AutoUnlock view.
227 [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.autoUnlockZoneID];
228 [self addGenericPassword: @"data" account: @"account-delete-me-autounlock" viewHint:(NSString*) kSecAttrViewHintAutoUnlock];
230 OCMVerifyAllWithDelay(self.mockDatabase, 8);
231 [self waitForExpectations:@[autoUnlockChanged] timeout:1];
232 [self waitForExpectations:@[pcsChanged] timeout:0.2];
235 -(void)testAddHealthItems {
236 [self saveFakeKeyHierarchiesToLocalDatabase]; // Make life easy for this test.
238 [self startCKKSSubsystem];
240 XCTestExpectation* healthChanged = [self expectChangeForView:self.healthZoneID.zoneName];
241 // Health is NOT is PCS view, so it should not send the fake 'PCS' view notification
242 XCTestExpectation* pcsChanged = [self expectChangeForView:@"PCS"];
243 pcsChanged.inverted = YES;
245 // We expect a single record to be uploaded to the Health view.
246 [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.healthZoneID];
247 [self addGenericPassword: @"data" account: @"account-delete-me-autounlock" viewHint:(NSString*) kSecAttrViewHintHealth];
249 OCMVerifyAllWithDelay(self.mockDatabase, 8);
250 [self waitForExpectations:@[healthChanged] timeout:1];
251 [self waitForExpectations:@[pcsChanged] timeout:0.2];
254 -(void)testAddApplePayItems {
255 [self saveFakeKeyHierarchiesToLocalDatabase]; // Make life easy for this test.
257 [self startCKKSSubsystem];
259 XCTestExpectation* applepayChanged = [self expectChangeForView:self.applepayZoneID.zoneName];
260 // ApplePay is NOT is PCS view, so it should not send the fake 'PCS' view notification
261 XCTestExpectation* pcsChanged = [self expectChangeForView:@"PCS"];
262 pcsChanged.inverted = YES;
264 // We expect a single record to be uploaded to the ApplePay view.
265 [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.applepayZoneID];
266 [self addGenericPassword: @"data" account: @"account-delete-me-autounlock" viewHint:(NSString*) kSecAttrViewHintApplePay];
268 OCMVerifyAllWithDelay(self.mockDatabase, 8);
269 [self waitForExpectations:@[applepayChanged] timeout:1];
270 [self waitForExpectations:@[pcsChanged] timeout:0.2];
274 -(void)testAddOtherViewHintItem {
275 [self saveFakeKeyHierarchiesToLocalDatabase]; // Make life easy for this test.
277 [self startCKKSSubsystem];
279 // We expect no uploads to CKKS.
280 [self addGenericPassword: @"data" account: @"account-delete-me-no-viewhint"];
281 [self addGenericPassword: @"data" account: @"account-delete-me-password" viewHint:(NSString*) kSOSViewAutofillPasswords];
284 OCMVerifyAllWithDelay(self.mockDatabase, 8);
287 - (void)testReceiveItemInView {
288 [self saveFakeKeyHierarchiesToLocalDatabase]; // Make life easy for this test.
289 [self startCKKSSubsystem];
291 [self waitForKeyHierarchyReadinesses];
293 [self findGenericPassword:@"account-delete-me" expecting:errSecItemNotFound];
295 CKRecord* ckr = [self createFakeRecord:self.engramZoneID recordName:@"7B598D31-F9C5-481E-98AC-5A507ACB2D85"];
296 [self.engramZone addToZone: ckr];
298 XCTestExpectation* engramChanged = [self expectChangeForView:self.engramZoneID.zoneName];
299 XCTestExpectation* pcsChanged = [self expectChangeForView:@"PCS"];
301 // Trigger a notification (with hilariously fake data)
302 [self.engramView notifyZoneChange:nil];
304 [self.engramView waitForFetchAndIncomingQueueProcessing];
305 [self findGenericPassword:@"account-delete-me" expecting:errSecSuccess];
307 [self waitForExpectations:@[engramChanged] timeout:1];
308 [self waitForExpectations:@[pcsChanged] timeout:1];
311 - (void)testFindManateePiggyTLKs {
312 [self saveFakeKeyHierarchyToLocalDatabase:self.manateeZoneID];
313 [self saveTLKMaterialToKeychain:self.manateeZoneID];
315 NSDictionary* piggyTLKs = [self SOSPiggyBackCopyFromKeychain];
317 [self deleteTLKMaterialFromKeychain:self.manateeZoneID];
319 [self SOSPiggyBackAddToKeychain:piggyTLKs];
321 NSError* error = nil;
322 [self.manateeZoneKeys.tlk loadKeyMaterialFromKeychain:&error];
323 XCTAssertNil(error, "No error loading tlk from piggy contents");
326 - (void)testFindPiggyTLKs {
327 [self putFakeKeyHierachiesInCloudKit];
328 [self saveTLKsToKeychain];
330 NSDictionary* piggyTLKs = [self SOSPiggyBackCopyFromKeychain];
332 [self deleteTLKMaterialsFromKeychain];
334 [self SOSPiggyBackAddToKeychain:piggyTLKs];
336 NSError* error = nil;
337 [self.manateeZoneKeys.tlk loadKeyMaterialFromKeychain:&error];
338 XCTAssertNil(error, "No error loading manatee tlk from piggy contents");
340 [self.engramZoneKeys.tlk loadKeyMaterialFromKeychain:&error];
341 XCTAssertNil(error, "No error loading engram tlk from piggy contents");
343 [self.autoUnlockZoneKeys.tlk loadKeyMaterialFromKeychain:&error];
344 XCTAssertNil(error, "No error loading AutoUnlock tlk from piggy contents");
346 [self.healthZoneKeys.tlk loadKeyMaterialFromKeychain:&error];
347 XCTAssertNil(error, "No error loading Health tlk from piggy contents");
349 [self.applepayZoneKeys.tlk loadKeyMaterialFromKeychain:&error];
350 XCTAssertNil(error, "No error loading ApplePay tlk from piggy contents");
353 -(NSString*)fileForStorage
355 static dispatch_once_t onceToken;
356 static NSString *tempPath = NULL;
357 dispatch_once(&onceToken, ^{
358 tempPath = [[[[NSFileManager defaultManager] temporaryDirectory] URLByAppendingPathComponent:@"PiggyPacket"] path];
361 NSLog(@"using temp path: %@", tempPath);
365 -(void)testPiggybackingData{
366 [self putFakeKeyHierachiesInCloudKit];
367 [self saveTLKsToKeychain];
369 [self startCKKSSubsystem];
371 [self waitForKeyHierarchyReadinesses];
373 OCMVerifyAllWithDelay(self.mockDatabase, 8);
376 * Pull data from keychain and view manager
379 NSDictionary* piggydata = [self SOSPiggyBackCopyFromKeychain];
380 NSArray<NSData *>* icloudidentities = piggydata[@"idents"];
381 NSArray<NSDictionary *>* tlks = piggydata[@"tlk"];
383 XCTAssertEqual([tlks count], [[self.injectedManager viewList] count], "TLKs not same as views");
385 XCTAssertNotNil(tlks, "tlks not set");
386 XCTAssertNotEqual([tlks count], (NSUInteger)0, "0 tlks");
387 XCTAssertNotNil(icloudidentities, "idents not set");
388 XCTAssertNotEqual([icloudidentities count], (NSUInteger)0, "0 icloudidentities");
390 NSData *initial = SOSPiggyCreateInitialSyncData(icloudidentities, tlks);
392 XCTAssertNotNil(initial, "Initial not set");
393 BOOL writeStatus = [initial writeToFile:[self fileForStorage] options:NSDataWritingAtomic error: nil];
394 XCTAssertTrue(writeStatus, "had trouble writing to disk");
395 XCTAssertNotEqual((int)[initial length], 0, "initial sync data is greater than 0");
398 * Check that they make it accross
401 const uint8_t* der = [initial bytes];
402 const uint8_t *der_end = der + [initial length];
404 NSDictionary *result = SOSPiggyCopyInitialSyncData(&der, der_end);
405 XCTAssertNotNil(result, "Initial not set");
406 NSArray *copiedTLKs = result[@"tlks"];
407 XCTAssertNotNil(copiedTLKs, "tlks not set");
408 XCTAssertEqual([copiedTLKs count], [tlks count], "tlks count not same");
410 NSArray *copiediCloudidentities = result[@"idents"];
411 XCTAssertNotNil(copiediCloudidentities, "idents not set");
412 XCTAssertEqual([copiediCloudidentities count], [icloudidentities count], "ident count not same");
415 -(void)testVerifyTLKSorting {
416 char key[32*2] = {0};
417 NSArray<NSDictionary *> *tlks = @[
419 @"acct" : @"11111111",
420 @"srvr" : @"Manatee",
421 @"v_Data" : [NSData dataWithBytes:key length:sizeof(key)],
425 @"acct" : @"55555555",
427 @"v_Data" : [NSData dataWithBytes:key length:sizeof(key)],
430 @"acct" : @"22222222",
432 @"v_Data" : [NSData dataWithBytes:key length:sizeof(key)],
436 @"acct" : @"44444444",
437 @"srvr" : @"Manatee",
438 @"v_Data" : [NSData dataWithBytes:key length:sizeof(key)],
441 @"acct" : @"33333333",
443 @"v_Data" : [NSData dataWithBytes:key length:sizeof(key)],
448 NSArray<NSDictionary *>* sortedTLKs = SOSAccountSortTLKS(tlks);
449 XCTAssertNotNil(sortedTLKs, "sortedTLKs not set");
451 NSLog(@"TLKs: %@", tlks);
452 NSLog(@"sortedTLKs: %@", sortedTLKs);
454 NSArray<NSString *> *expectedOrder = @[ @"11111111", @"22222222", @"33333333", @"44444444", @"55555555"];
455 [sortedTLKs enumerateObjectsUsingBlock:^(NSDictionary *tlk, NSUInteger idx, BOOL * _Nonnull stop) {
456 NSString *uuid = tlk[@"acct"];
457 XCTAssertEqualObjects(uuid, expectedOrder[idx], "wrong order");
462 - (void)testAcceptExistingPiggyKeyHierarchy {
463 // Test starts with no keys in CKKS database, but one in our fake CloudKit.
464 // Test also begins with the TLK having arrived in the local keychain (via SOS)
465 [self putFakeKeyHierachiesInCloudKit];
466 [self saveTLKsToKeychain];
467 NSDictionary* piggyTLKS = [self SOSPiggyBackCopyFromKeychain];
468 [self SOSPiggyBackAddToKeychain:piggyTLKS];
469 [self deleteTLKMaterialsFromKeychain];
471 // Spin up CKKS subsystem.
472 [self startCKKSSubsystem];
474 // The CKKS subsystem should not try to write anything to the CloudKit database while it's accepting the keys
475 [self.manateeView waitForKeyHierarchyReadiness];
477 OCMVerifyAllWithDelay(self.mockDatabase, 8);
479 // Verify that there are three local keys, and three local current key records
480 __weak __typeof(self) weakSelf = self;
481 [self.manateeView dispatchSync: ^bool{
482 __strong __typeof(weakSelf) strongSelf = weakSelf;
483 XCTAssertNotNil(strongSelf, "self exists");
485 NSError* error = nil;
487 NSArray<CKKSKey*>* keys = [CKKSKey localKeys:strongSelf.manateeZoneID error:&error];
488 XCTAssertNil(error, "no error fetching keys");
489 XCTAssertEqual(keys.count, 3u, "Three keys in local database");
491 NSArray<CKKSCurrentKeyPointer*>* currentkeys = [CKKSCurrentKeyPointer all:strongSelf.manateeZoneID error:&error];
492 XCTAssertNil(error, "no error fetching current keys");
493 XCTAssertEqual(currentkeys.count, 3u, "Three current key pointers in local database");
495 // Ensure that the manatee syncable TLK is created from a piggy
496 NSDictionary* query = @{
497 (id)kSecClass : (id)kSecClassInternetPassword,
498 (id)kSecAttrNoLegacy : @YES,
499 (id)kSecAttrAccessGroup : @"com.apple.security.ckks",
500 (id)kSecAttrDescription: SecCKKSKeyClassTLK,
501 (id)kSecAttrAccount: strongSelf.manateeZoneKeys.tlk.uuid,
502 (id)kSecAttrServer: strongSelf.manateeZoneID.zoneName,
503 (id)kSecAttrSynchronizable: @YES,
504 (id)kSecReturnAttributes: @YES,
505 (id)kSecReturnData: @YES,
507 CFTypeRef result = nil;
508 XCTAssertEqual(errSecSuccess, SecItemCopyMatching((__bridge CFDictionaryRef)query, &result), "Found a syncable TLK");
509 XCTAssertNotNil((__bridge id) result, "Received a result from SecItemCopyMatching");
510 CFReleaseNull(result);
516 -(void)putFakeKeyHierachiesInCloudKit{
517 [self putFakeKeyHierarchyInCloudKit: self.engramZoneID];
518 [self putFakeKeyHierarchyInCloudKit: self.manateeZoneID];
519 [self putFakeKeyHierarchyInCloudKit: self.autoUnlockZoneID];
520 [self putFakeKeyHierarchyInCloudKit: self.healthZoneID];
521 [self putFakeKeyHierarchyInCloudKit: self.applepayZoneID];
523 -(void)saveTLKsToKeychain{
524 [self saveTLKMaterialToKeychain:self.engramZoneID];
525 [self saveTLKMaterialToKeychain:self.manateeZoneID];
526 [self saveTLKMaterialToKeychain:self.autoUnlockZoneID];
527 [self saveTLKMaterialToKeychain:self.healthZoneID];
528 [self saveTLKMaterialToKeychain:self.applepayZoneID];
530 -(void)deleteTLKMaterialsFromKeychain{
531 [self deleteTLKMaterialFromKeychain: self.engramZoneID];
532 [self deleteTLKMaterialFromKeychain: self.manateeZoneID];
533 [self deleteTLKMaterialFromKeychain: self.autoUnlockZoneID];
534 [self deleteTLKMaterialFromKeychain: self.healthZoneID];
535 [self deleteTLKMaterialFromKeychain: self.applepayZoneID];
538 -(void)waitForKeyHierarchyReadinesses {
539 [self.manateeView waitForKeyHierarchyReadiness];
540 [self.engramView waitForKeyHierarchyReadiness];
541 [self.autoUnlockView waitForKeyHierarchyReadiness];
542 [self.healthView waitForKeyHierarchyReadiness];
543 [self.applepayView waitForKeyHierarchyReadiness];
546 -(void)testAcceptExistingAndUsePiggyKeyHierarchy {
547 // Test starts with nothing in database, but one in our fake CloudKit.
548 [self putFakeKeyHierachiesInCloudKit];
549 [self saveTLKsToKeychain];
550 NSDictionary* piggyData = [self SOSPiggyBackCopyFromKeychain];
551 [self deleteTLKMaterialsFromKeychain];
553 // Spin up CKKS subsystem.
554 [self startCKKSSubsystem];
556 // The CKKS subsystem should not try to write anything to the CloudKit database.
559 OCMVerifyAllWithDelay(self.mockDatabase, 8);
561 // Now, save the TLK to the keychain (to simulate it coming in later via piggybacking).
562 [self SOSPiggyBackAddToKeychain:piggyData];
563 [self waitForKeyHierarchyReadinesses];
565 // We expect a single record to be uploaded for each key class
566 [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.manateeZoneID checkItem: [self checkClassCBlock:self.manateeZoneID message:@"Object was encrypted under class C key in hierarchy"]];
567 [self addGenericPassword: @"data" account: @"account-delete-me-manatee" viewHint:(id)kSecAttrViewHintManatee];
569 OCMVerifyAllWithDelay(self.mockDatabase, 8);
571 [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.manateeZoneID checkItem: [self checkClassABlock:self.manateeZoneID message:@"Object was encrypted under class A key in hierarchy"]];
573 [self addGenericPassword:@"asdf"
574 account:@"account-class-A"
575 viewHint:(id)kSecAttrViewHintManatee
576 access:(id)kSecAttrAccessibleWhenUnlocked
577 expecting:errSecSuccess
578 message:@"Adding class A item"];
579 OCMVerifyAllWithDelay(self.mockDatabase, 8);