]> git.saurik.com Git - apple/security.git/blob - keychain/ckks/tests/CKKSSOSTests.m
Security-58286.1.32.tar.gz
[apple/security.git] / keychain / ckks / tests / CKKSSOSTests.m
1 /*
2 * Copyright (c) 2017 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 #if OCTAGON
25
26 #import <CloudKit/CloudKit.h>
27 #import <XCTest/XCTest.h>
28 #import <OCMock/OCMock.h>
29 #import <notify.h>
30
31 #include <Security/SecItemPriv.h>
32
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"
46
47 #import "keychain/ckks/tests/MockCloudKit.h"
48
49 #include <Security/SecureObjectSync/SOSCloudCircle.h>
50 #include <Security/SecureObjectSync/SOSAccountPriv.h>
51 #include <Security/SecureObjectSync/SOSAccount.h>
52 #include <Security/SecureObjectSync/SOSInternal.h>
53 #include <Security/SecureObjectSync/SOSFullPeerInfo.h>
54 #include <Security/SecKey.h>
55 #include <Security/SecKeyPriv.h>
56 @interface CloudKitKeychainSyncingSOSIntegrationTests : CloudKitKeychainSyncingMockXCTest
57
58 @property CKRecordZoneID* engramZoneID;
59 @property CKKSKeychainView* engramView;
60 @property FakeCKZone* engramZone;
61 @property (readonly) ZoneKeys* engramZoneKeys;
62
63 @property CKRecordZoneID* manateeZoneID;
64 @property CKKSKeychainView* manateeView;
65 @property FakeCKZone* manateeZone;
66 @property (readonly) ZoneKeys* manateeZoneKeys;
67
68 @property CKRecordZoneID* autoUnlockZoneID;
69 @property CKKSKeychainView* autoUnlockView;
70 @property FakeCKZone* autoUnlockZone;
71 @property (readonly) ZoneKeys* autoUnlockZoneKeys;
72
73 @property CKRecordZoneID* healthZoneID;
74 @property CKKSKeychainView* healthView;
75 @property FakeCKZone* healthZone;
76 @property (readonly) ZoneKeys* healthZoneKeys;
77
78
79 @end
80
81 @implementation CloudKitKeychainSyncingSOSIntegrationTests
82 + (void)setUp {
83 SecCKKSEnable();
84 SecCKKSResetSyncing();
85 [super setUp];
86 }
87
88 - (void)setUp {
89 // No manifests.
90 (void)[CKKSManifest shouldSyncManifests]; // initialize.
91 SecCKKSSetSyncManifests(false);
92 SecCKKSSetEnforceManifests(false);
93
94 [super setUp];
95 SecCKKSTestSetDisableSOS(false);
96
97 // Wait for the ViewManager to be brought up
98 XCTAssertEqual(0, [self.injectedManager.completedSecCKKSInitialize wait:4*NSEC_PER_SEC], "No timeout waiting for SecCKKSInitialize");
99
100 self.engramZoneID = [[CKRecordZoneID alloc] initWithZoneName:@"Engram" ownerName:CKCurrentUserDefaultName];
101 self.engramZone = [[FakeCKZone alloc] initZone: self.engramZoneID];
102 self.zones[self.engramZoneID] = self.engramZone;
103 self.engramView = [[CKKSViewManager manager] findView:@"Engram"];
104 XCTAssertNotNil(self.engramView, "CKKSViewManager created the Engram view");
105
106 self.manateeZoneID = [[CKRecordZoneID alloc] initWithZoneName:@"Manatee" ownerName:CKCurrentUserDefaultName];
107 self.manateeZone = [[FakeCKZone alloc] initZone: self.manateeZoneID];
108 self.zones[self.manateeZoneID] = self.manateeZone;
109 self.manateeView = [[CKKSViewManager manager] findView:@"Manatee"];
110 XCTAssertNotNil(self.manateeView, "CKKSViewManager created the Manatee view");
111
112 self.autoUnlockZoneID = [[CKRecordZoneID alloc] initWithZoneName:@"AutoUnlock" ownerName:CKCurrentUserDefaultName];
113 self.autoUnlockZone = [[FakeCKZone alloc] initZone: self.autoUnlockZoneID];
114 self.zones[self.autoUnlockZoneID] = self.autoUnlockZone;
115 self.autoUnlockView = [[CKKSViewManager manager] findView:@"AutoUnlock"];
116 XCTAssertNotNil(self.autoUnlockView, "CKKSViewManager created the AutoUnlock view");
117
118 self.healthZoneID = [[CKRecordZoneID alloc] initWithZoneName:@"Health" ownerName:CKCurrentUserDefaultName];
119 self.healthZone = [[FakeCKZone alloc] initZone: self.healthZoneID];
120 self.zones[self.healthZoneID] = self.healthZone;
121 self.healthView = [[CKKSViewManager manager] findView:@"Health"];
122 XCTAssertNotNil(self.autoUnlockView, "CKKSViewManager created the Health view");
123 }
124
125 + (void)tearDown {
126 SecCKKSTestSetDisableSOS(true);
127 [super tearDown];
128 SecCKKSResetSyncing();
129 }
130
131 - (void)tearDown {
132 [self.engramView cancelAllOperations];
133 [self.engramView waitUntilAllOperationsAreFinished];
134
135 [self.manateeView cancelAllOperations];
136 [self.manateeView waitUntilAllOperationsAreFinished];
137
138 [self.autoUnlockView cancelAllOperations];
139 [self.autoUnlockView waitUntilAllOperationsAreFinished];
140
141 [self.healthView cancelAllOperations];
142 [self.healthView waitUntilAllOperationsAreFinished];
143
144 [super tearDown];
145 }
146
147 - (ZoneKeys*)engramZoneKeys {
148 return self.keys[self.engramZoneID];
149 }
150
151 - (ZoneKeys*)manateeZoneKeys {
152 return self.keys[self.manateeZoneID];
153 }
154
155 -(void)saveFakeKeyHierarchiesToLocalDatabase {
156 [self createAndSaveFakeKeyHierarchy: self.engramZoneID];
157 [self createAndSaveFakeKeyHierarchy: self.manateeZoneID];
158 [self createAndSaveFakeKeyHierarchy: self.autoUnlockZoneID];
159 [self createAndSaveFakeKeyHierarchy: self.healthZoneID];
160 }
161
162 -(void)testAddEngramManateeItems {
163 [self saveFakeKeyHierarchiesToLocalDatabase]; // Make life easy for this test.
164
165 [self startCKKSSubsystem];
166
167 XCTestExpectation* engramChanged = [self expectChangeForView:self.engramZoneID.zoneName];
168 XCTestExpectation* pcsChanged = [self expectChangeForView:@"PCS"];
169 XCTestExpectation* manateeChanged = [self expectChangeForView:self.manateeZoneID.zoneName];
170
171 // We expect a single record to be uploaded to the engram view.
172 [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.engramZoneID];
173 [self addGenericPassword: @"data" account: @"account-delete-me-engram" viewHint:(NSString*) kSecAttrViewHintEngram];
174
175 OCMVerifyAllWithDelay(self.mockDatabase, 20);
176 [self waitForExpectations:@[engramChanged] timeout:1];
177 [self waitForExpectations:@[pcsChanged] timeout:1];
178
179 pcsChanged = [self expectChangeForView:@"PCS"];
180
181 // We expect a single record to be uploaded to the manatee view.
182 [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.manateeZoneID];
183 [self addGenericPassword: @"data" account: @"account-delete-me-manatee" viewHint:(NSString*) kSecAttrViewHintManatee];
184
185 OCMVerifyAllWithDelay(self.mockDatabase, 8);
186 [self waitForExpectations:@[manateeChanged] timeout:1];
187 [self waitForExpectations:@[pcsChanged] timeout:1];
188 }
189
190 -(void)testAddAutoUnlockItems {
191 [self saveFakeKeyHierarchiesToLocalDatabase]; // Make life easy for this test.
192
193 [self startCKKSSubsystem];
194
195 XCTestExpectation* autoUnlockChanged = [self expectChangeForView:self.autoUnlockZoneID.zoneName];
196 // AutoUnlock is NOT is PCS view, so it should not send the fake 'PCS' view notification
197 XCTestExpectation* pcsChanged = [self expectChangeForView:@"PCS"];
198 pcsChanged.inverted = YES;
199
200 // We expect a single record to be uploaded to the AutoUnlock view.
201 [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.autoUnlockZoneID];
202 [self addGenericPassword: @"data" account: @"account-delete-me-autounlock" viewHint:(NSString*) kSecAttrViewHintAutoUnlock];
203
204 OCMVerifyAllWithDelay(self.mockDatabase, 8);
205 [self waitForExpectations:@[autoUnlockChanged] timeout:1];
206 [self waitForExpectations:@[pcsChanged] timeout:0.2];
207 }
208
209 -(void)testAddHealthItems {
210 [self saveFakeKeyHierarchiesToLocalDatabase]; // Make life easy for this test.
211
212 [self startCKKSSubsystem];
213
214 XCTestExpectation* healthChanged = [self expectChangeForView:self.healthZoneID.zoneName];
215 // AutoUnlock is NOT is PCS view, so it should not send the fake 'PCS' view notification
216 XCTestExpectation* pcsChanged = [self expectChangeForView:@"PCS"];
217 pcsChanged.inverted = YES;
218
219 // We expect a single record to be uploaded to the Health view.
220 [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.healthZoneID];
221 [self addGenericPassword: @"data" account: @"account-delete-me-autounlock" viewHint:(NSString*) kSecAttrViewHintHealth];
222
223 OCMVerifyAllWithDelay(self.mockDatabase, 8);
224 [self waitForExpectations:@[healthChanged] timeout:1];
225 [self waitForExpectations:@[pcsChanged] timeout:0.2];
226 }
227
228 -(void)testAddOtherViewHintItem {
229 [self saveFakeKeyHierarchiesToLocalDatabase]; // Make life easy for this test.
230
231 [self startCKKSSubsystem];
232
233 // We expect no uploads to CKKS.
234 [self addGenericPassword: @"data" account: @"account-delete-me-no-viewhint"];
235 [self addGenericPassword: @"data" account: @"account-delete-me-password" viewHint:(NSString*) kSOSViewAutofillPasswords];
236
237 sleep(1);
238 OCMVerifyAllWithDelay(self.mockDatabase, 8);
239 }
240
241 - (void)testReceiveItemInView {
242 [self saveFakeKeyHierarchiesToLocalDatabase]; // Make life easy for this test.
243 [self startCKKSSubsystem];
244
245 [self waitForKeyHierarchyReadinesses];
246
247 [self findGenericPassword:@"account-delete-me" expecting:errSecItemNotFound];
248
249 CKRecord* ckr = [self createFakeRecord:self.engramZoneID recordName:@"7B598D31-F9C5-481E-98AC-5A507ACB2D85"];
250 [self.engramZone addToZone: ckr];
251
252 XCTestExpectation* engramChanged = [self expectChangeForView:self.engramZoneID.zoneName];
253 XCTestExpectation* pcsChanged = [self expectChangeForView:@"PCS"];
254
255 // Trigger a notification (with hilariously fake data)
256 [self.engramView notifyZoneChange:nil];
257
258 [self.engramView waitForFetchAndIncomingQueueProcessing];
259 [self findGenericPassword:@"account-delete-me" expecting:errSecSuccess];
260
261 [self waitForExpectations:@[engramChanged] timeout:1];
262 [self waitForExpectations:@[pcsChanged] timeout:1];
263 }
264
265 - (void)testFindManateePiggyTLKs {
266 [self saveFakeKeyHierarchyToLocalDatabase:self.manateeZoneID];
267 [self saveTLKMaterialToKeychain:self.manateeZoneID];
268
269 NSDictionary* piggyTLKs = [self SOSPiggyBackCopyFromKeychain];
270
271 [self deleteTLKMaterialFromKeychain:self.manateeZoneID];
272
273 [self SOSPiggyBackAddToKeychain:piggyTLKs];
274
275 NSError* error = nil;
276 [self.manateeZoneKeys.tlk loadKeyMaterialFromKeychain:&error];
277 XCTAssertNil(error, "No error loading tlk from piggy contents");
278 }
279
280 - (void)testFindPiggyTLKs {
281 [self putFakeKeyHierachiesInCloudKit];
282 [self saveTLKsToKeychain];
283
284 NSDictionary* piggyTLKs = [self SOSPiggyBackCopyFromKeychain];
285
286 [self deleteTLKMaterialsFromKeychain];
287
288 [self SOSPiggyBackAddToKeychain:piggyTLKs];
289
290 NSError* error = nil;
291 [self.manateeZoneKeys.tlk loadKeyMaterialFromKeychain:&error];
292 XCTAssertNil(error, "No error loading manatee tlk from piggy contents");
293
294 [self.engramZoneKeys.tlk loadKeyMaterialFromKeychain:&error];
295 XCTAssertNil(error, "No error loading engram tlk from piggy contents");
296
297 [self.autoUnlockZoneKeys.tlk loadKeyMaterialFromKeychain:&error];
298 XCTAssertNil(error, "No error loading AutoUnlock tlk from piggy contents");
299
300 [self.healthZoneKeys.tlk loadKeyMaterialFromKeychain:&error];
301 XCTAssertNil(error, "No error loading Health tlk from piggy contents");
302 }
303
304 -(NSString*)fileForStorage
305 {
306 static dispatch_once_t onceToken;
307 static NSString *tempPath = NULL;
308 dispatch_once(&onceToken, ^{
309 tempPath = [[[[NSFileManager defaultManager] temporaryDirectory] URLByAppendingPathComponent:@"PiggyPacket"] path];
310
311 });
312 NSLog(@"using temp path: %@", tempPath);
313 return tempPath;
314 }
315
316 -(void)testPiggybackingData{
317 [self putFakeKeyHierachiesInCloudKit];
318 [self saveTLKsToKeychain];
319
320 [self startCKKSSubsystem];
321
322 [self waitForKeyHierarchyReadinesses];
323
324 OCMVerifyAllWithDelay(self.mockDatabase, 8);
325
326 /*
327 * Pull data from keychain and view manager
328 */
329
330 NSDictionary* piggydata = [self SOSPiggyBackCopyFromKeychain];
331 NSArray<NSData *>* icloudidentities = piggydata[@"idents"];
332 NSArray<NSDictionary *>* tlks = piggydata[@"tlk"];
333
334 XCTAssertEqual([tlks count], [[CKKSViewManager viewList] count], "TLKs not same as views");
335
336 XCTAssertNotNil(tlks, "tlks not set");
337 XCTAssertNotEqual([tlks count], (NSUInteger)0, "0 tlks");
338 XCTAssertNotNil(icloudidentities, "idents not set");
339 XCTAssertNotEqual([icloudidentities count], (NSUInteger)0, "0 icloudidentities");
340
341 NSData *initial = SOSPiggyCreateInitialSyncData(icloudidentities, tlks);
342
343 XCTAssertNotNil(initial, "Initial not set");
344 BOOL writeStatus = [initial writeToFile:[self fileForStorage] options:NSDataWritingAtomic error: nil];
345 XCTAssertTrue(writeStatus, "had trouble writing to disk");
346 XCTAssertNotEqual((int)[initial length], 0, "initial sync data is greater than 0");
347
348 /*
349 * Check that they make it accross
350 */
351
352 const uint8_t* der = [initial bytes];
353 const uint8_t *der_end = der + [initial length];
354
355 NSDictionary *result = SOSPiggyCopyInitialSyncData(&der, der_end);
356 XCTAssertNotNil(result, "Initial not set");
357 NSArray *copiedTLKs = result[@"tlks"];
358 XCTAssertNotNil(copiedTLKs, "tlks not set");
359 XCTAssertEqual([copiedTLKs count], [tlks count], "tlks count not same");
360
361 NSArray *copiediCloudidentities = result[@"idents"];
362 XCTAssertNotNil(copiediCloudidentities, "idents not set");
363 XCTAssertEqual([copiediCloudidentities count], [icloudidentities count], "ident count not same");
364 }
365
366 -(void)testVerifyTLKSorting {
367 char key[32*2] = {0};
368 NSArray<NSDictionary *> *tlks = @[
369 @{
370 @"acct" : @"11111111",
371 @"srvr" : @"Manatee",
372 @"v_Data" : [NSData dataWithBytes:key length:sizeof(key)],
373 @"auth" : @YES,
374 },
375 @{
376 @"acct" : @"55555555",
377 @"srvr" : @"Health",
378 @"v_Data" : [NSData dataWithBytes:key length:sizeof(key)],
379 },
380 @{
381 @"acct" : @"22222222",
382 @"srvr" : @"Engram",
383 @"v_Data" : [NSData dataWithBytes:key length:sizeof(key)],
384 @"auth" : @YES,
385 },
386 @{
387 @"acct" : @"44444444",
388 @"srvr" : @"Manatee",
389 @"v_Data" : [NSData dataWithBytes:key length:sizeof(key)],
390 },
391 @{
392 @"acct" : @"33333333",
393 @"srvr" : @"Health",
394 @"v_Data" : [NSData dataWithBytes:key length:sizeof(key)],
395 @"auth" : @YES,
396 },
397 ];
398
399 NSArray<NSDictionary *>* sortedTLKs = SOSAccountSortTLKS(tlks);
400 XCTAssertNotNil(sortedTLKs, "sortedTLKs not set");
401
402 NSLog(@"TLKs: %@", tlks);
403 NSLog(@"sortedTLKs: %@", sortedTLKs);
404
405 NSArray<NSString *> *expectedOrder = @[ @"11111111", @"22222222", @"33333333", @"44444444", @"55555555"];
406 [sortedTLKs enumerateObjectsUsingBlock:^(NSDictionary *tlk, NSUInteger idx, BOOL * _Nonnull stop) {
407 NSString *uuid = tlk[@"acct"];
408 XCTAssertEqualObjects(uuid, expectedOrder[idx], "wrong order");
409 }];
410 }
411
412
413 - (void)testAcceptExistingPiggyKeyHierarchy {
414 // Test starts with no keys in CKKS database, but one in our fake CloudKit.
415 // Test also begins with the TLK having arrived in the local keychain (via SOS)
416 [self putFakeKeyHierachiesInCloudKit];
417 [self saveTLKsToKeychain];
418 NSDictionary* piggyTLKS = [self SOSPiggyBackCopyFromKeychain];
419 [self SOSPiggyBackAddToKeychain:piggyTLKS];
420 [self deleteTLKMaterialsFromKeychain];
421
422 // Spin up CKKS subsystem.
423 [self startCKKSSubsystem];
424
425 // The CKKS subsystem should not try to write anything to the CloudKit database while it's accepting the keys
426 [self.manateeView waitForKeyHierarchyReadiness];
427
428 OCMVerifyAllWithDelay(self.mockDatabase, 8);
429
430 // Verify that there are three local keys, and three local current key records
431 __weak __typeof(self) weakSelf = self;
432 [self.manateeView dispatchSync: ^bool{
433 __strong __typeof(weakSelf) strongSelf = weakSelf;
434 XCTAssertNotNil(strongSelf, "self exists");
435
436 NSError* error = nil;
437
438 NSArray<CKKSKey*>* keys = [CKKSKey localKeys:strongSelf.manateeZoneID error:&error];
439 XCTAssertNil(error, "no error fetching keys");
440 XCTAssertEqual(keys.count, 3u, "Three keys in local database");
441
442 NSArray<CKKSCurrentKeyPointer*>* currentkeys = [CKKSCurrentKeyPointer all:strongSelf.manateeZoneID error:&error];
443 XCTAssertNil(error, "no error fetching current keys");
444 XCTAssertEqual(currentkeys.count, 3u, "Three current key pointers in local database");
445
446 // Ensure that the manatee syncable TLK is created from a piggy
447 NSDictionary* query = @{
448 (id)kSecClass : (id)kSecClassInternetPassword,
449 (id)kSecAttrNoLegacy : @YES,
450 (id)kSecAttrAccessGroup : @"com.apple.security.ckks",
451 (id)kSecAttrDescription: SecCKKSKeyClassTLK,
452 (id)kSecAttrAccount: strongSelf.manateeZoneKeys.tlk.uuid,
453 (id)kSecAttrServer: strongSelf.manateeZoneID.zoneName,
454 (id)kSecAttrSynchronizable: @YES,
455 (id)kSecReturnAttributes: @YES,
456 (id)kSecReturnData: @YES,
457 };
458 CFTypeRef result = nil;
459 XCTAssertEqual(errSecSuccess, SecItemCopyMatching((__bridge CFDictionaryRef)query, &result), "Found a syncable TLK");
460 XCTAssertNotNil((__bridge id) result, "Received a result from SecItemCopyMatching");
461 CFReleaseNull(result);
462
463 return false;
464 }];
465 }
466
467 -(void)putFakeKeyHierachiesInCloudKit{
468 [self putFakeKeyHierarchyInCloudKit: self.engramZoneID];
469 [self putFakeKeyHierarchyInCloudKit: self.manateeZoneID];
470 [self putFakeKeyHierarchyInCloudKit: self.autoUnlockZoneID];
471 [self putFakeKeyHierarchyInCloudKit: self.healthZoneID];
472 }
473 -(void)saveTLKsToKeychain{
474 [self saveTLKMaterialToKeychain:self.engramZoneID];
475 [self saveTLKMaterialToKeychain:self.manateeZoneID];
476 [self saveTLKMaterialToKeychain:self.autoUnlockZoneID];
477 [self saveTLKMaterialToKeychain:self.healthZoneID];
478 }
479 -(void)deleteTLKMaterialsFromKeychain{
480 [self deleteTLKMaterialFromKeychain: self.engramZoneID];
481 [self deleteTLKMaterialFromKeychain: self.manateeZoneID];
482 [self deleteTLKMaterialFromKeychain: self.autoUnlockZoneID];
483 [self deleteTLKMaterialFromKeychain: self.healthZoneID];
484 }
485
486 -(void)waitForKeyHierarchyReadinesses {
487 [self.manateeView waitForKeyHierarchyReadiness];
488 [self.engramView waitForKeyHierarchyReadiness];
489 [self.autoUnlockView waitForKeyHierarchyReadiness];
490 [self.healthView waitForKeyHierarchyReadiness];
491 }
492
493 -(void)testAcceptExistingAndUsePiggyKeyHierarchy {
494 // Test starts with nothing in database, but one in our fake CloudKit.
495 [self putFakeKeyHierachiesInCloudKit];
496 [self saveTLKsToKeychain];
497 NSDictionary* piggyData = [self SOSPiggyBackCopyFromKeychain];
498 [self deleteTLKMaterialsFromKeychain];
499
500 // Spin up CKKS subsystem.
501 [self startCKKSSubsystem];
502
503 // The CKKS subsystem should not try to write anything to the CloudKit database.
504 sleep(1);
505
506 OCMVerifyAllWithDelay(self.mockDatabase, 8);
507
508 // Now, save the TLK to the keychain (to simulate it coming in later via piggybacking).
509 [self SOSPiggyBackAddToKeychain:piggyData];
510 [self waitForKeyHierarchyReadinesses];
511
512 // We expect a single record to be uploaded for each key class
513 [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.manateeZoneID checkItem: [self checkClassCBlock:self.manateeZoneID message:@"Object was encrypted under class C key in hierarchy"]];
514 [self addGenericPassword: @"data" account: @"account-delete-me-manatee" viewHint:(id)kSecAttrViewHintManatee];
515
516 OCMVerifyAllWithDelay(self.mockDatabase, 8);
517
518 [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.manateeZoneID checkItem: [self checkClassABlock:self.manateeZoneID message:@"Object was encrypted under class A key in hierarchy"]];
519
520 [self addGenericPassword:@"asdf"
521 account:@"account-class-A"
522 viewHint:(id)kSecAttrViewHintManatee
523 access:(id)kSecAttrAccessibleWhenUnlocked
524 expecting:errSecSuccess
525 message:@"Adding class A item"];
526 OCMVerifyAllWithDelay(self.mockDatabase, 8);
527 }
528 @end
529
530 #endif // OCTAGON
531