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 <Foundation/Foundation.h>
28 #import <XCTest/XCTest.h>
29 #import <OCMock/OCMock.h>
31 #import "OTTestsBase.h"
33 static NSString* const testContextID = @"Foo";
34 static NSString* const testDSID = @"123456789";
36 static NSString* OTCKRecordBottledPeerType = @"OTBottledPeer";
37 /* Octagon Trust BottledPeerSchema */
38 static NSString* OTCKRecordEscrowRecordID = @"escrowRecordID";
39 static NSString* OTCKRecordRecordID = @"bottledPeerRecordID";
40 static NSString* OTCKRecordSPID = @"spID";
41 static NSString* OTCKRecordEscrowSigningSPKI = @"escrowSigningSPKI";
42 static NSString* OTCKRecordPeerSigningSPKI = @"peerSigningSPKI";
43 static NSString* OTCKRecordEscrowSigningPubKey = @"escrowSigningPubKey";
44 static NSString* OTCKRecordPeerSigningPubKey = @"peerSigningPubKey";
45 static NSString* OTCKRecordSignatureFromEscrow = @"signatureUsingEscrow";
46 static NSString* OTCKRecordSignatureFromPeerKey = @"signatureUsingPeerKey";
47 static NSString* OTCKRecordBottle = @"bottle";
49 static NSString* OTCKRecordPeerID = @"peerID";
51 @interface OTLockStateNetworkingTests : OTTestsBase
52 @property (nonatomic, strong) OTBottledPeerRecord* fakeBottledPeerRecord;
55 @implementation OTLockStateNetworkingTests
60 self.continueAfterFailure = NO;
63 OTBottledPeer *bp = [[OTBottledPeer alloc]initWithPeerID:self.egoPeerID spID:self.sosPeerID peerSigningKey:self.peerSigningKey peerEncryptionKey:self.peerEncryptionKey escrowKeys:self.escrowKeys error:&error];
65 XCTAssertNotNil(bp, @"plaintext should not be nil");
66 XCTAssertNil(error, @"error should be nil");
67 XCTAssertNotNil(self.escrowKeys.signingKey, @"signing public key should not be nil");
68 XCTAssertNotNil(self.escrowKeys.encryptionKey, @"encryption public key should not be nil");
70 OTBottledPeerSigned *bpSigned = [[OTBottledPeerSigned alloc]initWithBottledPeer:bp escrowedSigningKey:self.escrowKeys.signingKey peerSigningKey:self.peerSigningKey error:&error];
72 self.fakeBottledPeerRecord = [bpSigned asRecord:self.sosPeerID];
74 [self createAndSaveFakeKeyHierarchy: self.keychainZoneID]; // Make life easy for this test.
85 -(void) testGrabbingBottleLocallyCheckPerfectConditions
87 [self setUpRampRecordsInCloudKitWithFeatureOn];
88 [self startCKKSSubsystem];
90 __block NSData* localEntropy = nil;
91 __block NSString* localBottleID = nil;
93 self.spiBlockExpectation = [self expectationWithDescription:@"preflight bottled peer fired"];
95 [self.otControl preflightBottledPeer:testContextID
97 reply:^(NSData * _Nullable entropy, NSString * _Nullable bottleID, NSData * _Nullable signingPublicKey, NSError * _Nullable error) {
98 [self.spiBlockExpectation fulfill];
99 localEntropy = entropy;
100 localBottleID = bottleID;
101 XCTAssertNotNil(entropy, "entropy should not be nil");
102 XCTAssertNotNil(bottleID, "bottle id should not be nil");
103 XCTAssertNotNil(signingPublicKey, "signing pub key should not be nil");
104 XCTAssertNil(error, "error should be nil");
106 [self waitForExpectationsWithTimeout:1.0 handler:nil];
108 self.spiBlockExpectation = [self expectationWithDescription:@"launch bottled peer fired"];
110 [self expectAddedCKModifyRecords:@{OTCKRecordBottledPeerType: @1} holdFetch:NO];
112 [self.otControl launchBottledPeer:testContextID bottleID:localBottleID reply:^(NSError * _Nullable error) {
113 [self.spiBlockExpectation fulfill];
114 XCTAssertNil(error, "error should be nil");
117 [self waitForExpectationsWithTimeout:1.0 handler:nil];
119 NSError* localError = nil;
120 XCTAssertTrue([self.context doesThisDeviceHaveABottle:&localError] == BOTTLE, @"should have a bottle");
121 XCTAssertNil(localError, "error should be nil");
124 -(void) testGrabbingBottleFromCloudKitCheckPerfectConditions
126 [self setUpRampRecordsInCloudKitWithFeatureOn];
128 CKRecord* newRecord = [[CKRecord alloc]initWithRecordType:OTCKRecordBottledPeerType];
129 newRecord[OTCKRecordPeerID] = self.fakeBottledPeerRecord.peerID;
130 newRecord[OTCKRecordSPID] = @"spID";
131 newRecord[OTCKRecordEscrowSigningSPKI] = self.fakeBottledPeerRecord.escrowedSigningSPKI;
132 newRecord[OTCKRecordPeerSigningSPKI] = self.fakeBottledPeerRecord.peerSigningSPKI;
133 newRecord[OTCKRecordEscrowRecordID] = self.fakeBottledPeerRecord.escrowRecordID;
134 newRecord[OTCKRecordBottle] = self.fakeBottledPeerRecord.bottle;
135 newRecord[OTCKRecordSignatureFromEscrow] = self.fakeBottledPeerRecord.signatureUsingEscrowKey;
136 newRecord[OTCKRecordSignatureFromPeerKey] = self.fakeBottledPeerRecord.signatureUsingPeerKey;
138 [self.otFakeZone addToZone:newRecord];
140 [self startCKKSSubsystem];
142 NSError* localError = nil;
143 XCTAssertTrue([self.context doesThisDeviceHaveABottle:&localError] == BOTTLE, @"should have a bottle");
144 XCTAssertNil(localError, "error should be nil");
147 -(void) testBottleCheckWhenLocked
149 NSError* error = nil;
150 self.aksLockState = true;
151 [self.lockStateTracker recheck];
152 [self setUpRampRecordsInCloudKitWithFeatureOff];
154 XCTAssertTrue([self.context doesThisDeviceHaveABottle:&error] == UNCLEAR, @"bottle check should return unclear");
156 XCTAssertNotNil(error, "error should not be nil");
157 XCTAssertEqual(error.code, -25308, @"error should be interaction not allowed");
160 -(void) testBottleCheckWithNoNetwork
162 NSError* error = nil;
163 self.accountStatus = CKAccountStatusAvailable;
164 [self startCKKSSubsystem];
166 [self.reachabilityTracker setNetworkReachability:false];
167 XCTAssertTrue([self.context doesThisDeviceHaveABottle:&error] == UNCLEAR, @"bottle check should return unclear");
168 XCTAssertEqual(error.code, OTErrorNoNetwork, @"should have returned no network error");
171 -(void) testBottleCheckWhenNotSignedIn
173 NSError* error = nil;
175 self.accountStatus = CKAccountStatusNoAccount;
176 [self startCKKSSubsystem];
178 XCTAssertTrue([self.context doesThisDeviceHaveABottle:&error] == UNCLEAR, @"bottle check should return unclear");
179 XCTAssertEqual(error.code, OTErrorNotSignedIn, @"should have returned not signed in error");
182 //Bottle Update tests
183 -(void)testBottleUpdateNotSignedIn
185 self.spiBlockExpectation = [self expectationWithDescription:@"handle identity change spis fired"];
187 [self setUpRampRecordsInCloudKitWithFeatureOn];
189 self.accountStatus = CKAccountStatusNoAccount;
191 [self startCKKSSubsystem];
193 SFECKeyPair* newSigningKey = [[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]];
195 SFECKeyPair* newEncryptionKey = [[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]];
198 [self.otControl handleIdentityChangeForSigningKey:newSigningKey
199 ForEncryptionKey:newEncryptionKey
200 ForPeerID:self.sosPeerID
201 reply:^(BOOL result, NSError* _Nullable error){
202 [self.spiBlockExpectation fulfill];
203 XCTAssertTrue(result == NO, @"should return NO");
204 XCTAssertEqual(error.code, OTErrorNotSignedIn, @"should have returned not signed in error");
206 [self waitForExpectationsWithTimeout:1.0 handler:nil];
209 -(void) testBottleUpdateWithNoNetwork
211 self.accountStatus = CKAccountStatusAvailable;
212 [self startCKKSSubsystem];
214 [self.reachabilityTracker setNetworkReachability:false];
216 SFECKeyPair* newSigningKey = [[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]];
218 SFECKeyPair* newEncryptionKey = [[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]];
221 [self.otControl handleIdentityChangeForSigningKey:newSigningKey
222 ForEncryptionKey:newEncryptionKey
223 ForPeerID:self.sosPeerID
224 reply:^(BOOL result, NSError* _Nullable error){
225 XCTAssertEqual(error.code, OTErrorNoNetwork, @"should have returned OTErrorNoNetwork in error");
229 -(void) testBottleUpdateWhenLocked
231 self.aksLockState = true;
232 [self.lockStateTracker recheck];
234 SFECKeyPair* newSigningKey = [[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]];
236 SFECKeyPair* newEncryptionKey = [[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]];
239 [self.otControl handleIdentityChangeForSigningKey:newSigningKey
240 ForEncryptionKey:newEncryptionKey
241 ForPeerID:self.sosPeerID
242 reply:^(BOOL result, NSError* _Nullable error){
243 XCTAssertEqual(error.code, errSecInteractionNotAllowed, @"should have returned errSecInteractionNotAllowed in error");
248 -(void)testPreflightNotSignedIn
250 self.spiBlockExpectation = [self expectationWithDescription:@"preflight bottled peer fired"];
252 [self setUpRampRecordsInCloudKitWithFeatureOn];
254 self.accountStatus = CKAccountStatusNoAccount;
256 [self startCKKSSubsystem];
258 [self.otControl preflightBottledPeer:testContextID
260 reply:^(NSData * _Nullable entropy, NSString * _Nullable bottleID, NSData * _Nullable signingPublicKey, NSError * _Nullable error) {
261 [self.spiBlockExpectation fulfill];
262 XCTAssertNil(entropy, "entropy should not be nil");
263 XCTAssertNil(bottleID, "bottle id should not be nil");
264 XCTAssertNil(signingPublicKey, "signing pub key should not be nil");
265 XCTAssertEqual(error.code, OTErrorNotSignedIn, @"should have returned not signed in error");
268 [self waitForExpectationsWithTimeout:1.0 handler:nil];
272 -(void) testPreflightWithNoNetwork
274 self.accountStatus = CKAccountStatusAvailable;
275 [self startCKKSSubsystem];
277 [self.reachabilityTracker setNetworkReachability:false];
279 [self.otControl preflightBottledPeer:testContextID
281 reply:^(NSData * _Nullable entropy, NSString * _Nullable bottleID, NSData * _Nullable signingPublicKey, NSError * _Nullable error) {
282 [self.spiBlockExpectation fulfill];
283 XCTAssertNil(entropy, "entropy should not be nil");
284 XCTAssertNil(bottleID, "bottle id should not be nil");
285 XCTAssertNil(signingPublicKey, "signing pub key should not be nil");
286 XCTAssertEqual(error.code, OTErrorNoNetwork, @"should have returned OTErrorNoNetwork in error");
291 -(void) testPreflightWhenLocked
293 self.aksLockState = true;
294 [self.lockStateTracker recheck];
296 [self.otControl preflightBottledPeer:testContextID
298 reply:^(NSData * _Nullable entropy, NSString * _Nullable bottleID, NSData * _Nullable signingPublicKey, NSError * _Nullable error) {
299 [self.spiBlockExpectation fulfill];
300 XCTAssertNil(entropy, "entropy should not be nil");
301 XCTAssertNil(bottleID, "bottle id should not be nil");
302 XCTAssertNil(signingPublicKey, "signing pub key should not be nil");
303 XCTAssertEqual(error.code, errSecInteractionNotAllowed, @"should have returned errSecInteractionNotAllowed in error");
307 //Launch Bottle tests
308 -(void)testLaunchNotSignedIn
310 [self setUpRampRecordsInCloudKitWithFeatureOn];
312 self.accountStatus = CKAccountStatusNoAccount;
314 [self startCKKSSubsystem];
316 self.spiBlockExpectation = [self expectationWithDescription:@"preflight bottled peer fired"];
318 [self.otControl preflightBottledPeer:OTDefaultContext
320 reply:^(NSData * _Nullable entropy, NSString * _Nullable bottleID, NSData * _Nullable signingPublicKey, NSError * _Nullable error) {
321 [self.spiBlockExpectation fulfill];
322 XCTAssertNil(entropy, "shouldn't return any entropy");
323 XCTAssertNil(bottleID, "shouldn't return a bottle ID");
324 XCTAssertNil(signingPublicKey, "shouldn't return a signingPublicKey");
325 XCTAssertEqual(error.code, OTErrorNotSignedIn, "should return a OTErrorNotSignedIn error");
327 OCMVerifyAllWithDelay(self.mockDatabase, 8);
328 [self waitForExpectationsWithTimeout:1.0 handler:nil];
330 self.spiBlockExpectation = [self expectationWithDescription:@"launch SPI fired"];
332 NSString* localBottleID = @"random bottle id";
333 [self.otControl launchBottledPeer:testContextID bottleID:localBottleID reply:^(NSError * _Nullable error) {
334 [self.spiBlockExpectation fulfill];
335 XCTAssertEqual(error.code, OTErrorNotSignedIn, "should return a OTErrorNotSignedIn error");
338 OCMVerifyAllWithDelay(self.mockDatabase, 8);
339 [self waitForExpectationsWithTimeout:1.0 handler:nil];
342 -(void) testLaunchWithNoNetwork
344 [self setUpRampRecordsInCloudKitWithFeatureOn];
346 self.accountStatus = CKAccountStatusAvailable;
347 [self startCKKSSubsystem];
349 [self.reachabilityTracker setNetworkReachability:false];
351 [self startCKKSSubsystem];
353 self.spiBlockExpectation = [self expectationWithDescription:@"preflight bottled peer fired"];
355 [self.otControl preflightBottledPeer:OTDefaultContext
357 reply:^(NSData * _Nullable entropy, NSString * _Nullable bottleID, NSData * _Nullable signingPublicKey, NSError * _Nullable error) {
358 [self.spiBlockExpectation fulfill];
359 XCTAssertNil(entropy, "shouldn't return any entropy");
360 XCTAssertNil(bottleID, "shouldn't return a bottle ID");
361 XCTAssertNil(signingPublicKey, "shouldn't return a signingPublicKey");
362 XCTAssertEqual(error.code, OTErrorNoNetwork, "should return a OTErrorNoNetwork error");
364 OCMVerifyAllWithDelay(self.mockDatabase, 8);
365 [self waitForExpectationsWithTimeout:1.0 handler:nil];
368 self.spiBlockExpectation = [self expectationWithDescription:@"launch SPI fired"];
370 NSString* localBottleID = @"random bottle id";
371 [self.otControl launchBottledPeer:testContextID bottleID:localBottleID reply:^(NSError * _Nullable error) {
372 [self.spiBlockExpectation fulfill];
373 XCTAssertEqual(error.code, OTErrorNoNetwork, "should return a OTErrorNoNetwork error");
376 OCMVerifyAllWithDelay(self.mockDatabase, 8);
377 [self waitForExpectationsWithTimeout:1.0 handler:nil];
380 -(void) testLaunchWhenLocked
382 [self setUpRampRecordsInCloudKitWithFeatureOn];
384 self.aksLockState = true;
385 [self.lockStateTracker recheck];
387 [self startCKKSSubsystem];
389 self.spiBlockExpectation = [self expectationWithDescription:@"preflight bottled peer fired"];
391 [self.otControl preflightBottledPeer:OTDefaultContext
393 reply:^(NSData * _Nullable entropy, NSString * _Nullable bottleID, NSData * _Nullable signingPublicKey, NSError * _Nullable error) {
394 [self.spiBlockExpectation fulfill];
395 XCTAssertNil(entropy, "shouldn't return any entropy");
396 XCTAssertNil(bottleID, "shouldn't return a bottle ID");
397 XCTAssertNil(signingPublicKey, "shouldn't return a signingPublicKey");
398 XCTAssertEqual(error.code, errSecInteractionNotAllowed, "should return a errSecInteractionNotAllowed error");
400 OCMVerifyAllWithDelay(self.mockDatabase, 8);
401 [self waitForExpectationsWithTimeout:1.0 handler:nil];
404 self.spiBlockExpectation = [self expectationWithDescription:@"launch SPI fired"];
406 NSString* localBottleID = @"random bottle id";
407 [self.otControl launchBottledPeer:testContextID bottleID:localBottleID reply:^(NSError * _Nullable error) {
408 [self.spiBlockExpectation fulfill];
409 XCTAssertEqual(error.code, errSecInteractionNotAllowed, "should return a errSecInteractionNotAllowed error");
412 OCMVerifyAllWithDelay(self.mockDatabase, 8);
413 [self waitForExpectationsWithTimeout:1.0 handler:nil];
417 -(void)testScrubNotSignedIn
419 [self setUpRampRecordsInCloudKitWithFeatureOn];
421 self.accountStatus = CKAccountStatusNoAccount;
422 [self startCKKSSubsystem];
424 self.spiBlockExpectation = [self expectationWithDescription:@"preflight bottled peer SPI fired"];
426 [self.otControl preflightBottledPeer:testContextID
428 reply:^(NSData * _Nullable entropy, NSString * _Nullable bottleID, NSData * _Nullable signingPublicKey, NSError * _Nullable error) {
429 [self.spiBlockExpectation fulfill];
430 XCTAssertNil(entropy, "entropy should be nil");
431 XCTAssertNil(bottleID, "bottle id should be nil");
432 XCTAssertNil(signingPublicKey, "signing pub key should be nil");
433 XCTAssertEqual(error.code, OTErrorNotSignedIn, "should return a OTErrorNotSignedIn error");
436 [self waitForExpectationsWithTimeout:1.0 handler:nil];
438 __block NSString* localBottleID = @"random bottle id";
439 self.spiBlockExpectation = [self expectationWithDescription:@"scrub bottled peer SPI fired"];
441 [self.otControl scrubBottledPeer:testContextID bottleID:localBottleID reply:^(NSError * _Nullable error) {
442 [self.spiBlockExpectation fulfill];
443 XCTAssertEqual(error.code, OTErrorNotSignedIn, "should return a OTErrorNotSignedIn error");
446 OCMVerifyAllWithDelay(self.mockDatabase, 8);
447 [self waitForExpectationsWithTimeout:1.0 handler:nil];
451 -(void) testScrubWithNoNetwork
453 [self setUpRampRecordsInCloudKitWithFeatureOn];
455 self.accountStatus = CKAccountStatusAvailable;
457 [self.reachabilityTracker setNetworkReachability:false];
459 [self startCKKSSubsystem];
461 self.spiBlockExpectation = [self expectationWithDescription:@"preflight bottled peer SPI fired"];
463 [self.otControl preflightBottledPeer:testContextID
465 reply:^(NSData * _Nullable entropy, NSString * _Nullable bottleID, NSData * _Nullable signingPublicKey, NSError * _Nullable error) {
466 [self.spiBlockExpectation fulfill];
467 XCTAssertNil(entropy, "entropy should be nil");
468 XCTAssertNil(bottleID, "bottle id should be nil");
469 XCTAssertNil(signingPublicKey, "signing pub key should be nil");
470 XCTAssertEqual(error.code, OTErrorNoNetwork, "should return a OTErrorNoNetwork error");
473 [self waitForExpectationsWithTimeout:1.0 handler:nil];
475 __block NSString* localBottleID = @"random bottle id";
476 self.spiBlockExpectation = [self expectationWithDescription:@"scrub bottled peer SPI fired"];
478 [self.otControl scrubBottledPeer:testContextID bottleID:localBottleID reply:^(NSError * _Nullable error) {
479 [self.spiBlockExpectation fulfill];
480 XCTAssertEqual(error.code, OTErrorNoNetwork, "should return a OTErrorNoNetwork error");
483 OCMVerifyAllWithDelay(self.mockDatabase, 8);
484 [self waitForExpectationsWithTimeout:1.0 handler:nil];
487 -(void) testScrubWhenLocked
489 [self setUpRampRecordsInCloudKitWithFeatureOn];
491 self.aksLockState = true;
492 [self.lockStateTracker recheck];
494 [self startCKKSSubsystem];
496 self.spiBlockExpectation = [self expectationWithDescription:@"preflight bottled peer SPI fired"];
498 [self.otControl preflightBottledPeer:testContextID
500 reply:^(NSData * _Nullable entropy, NSString * _Nullable bottleID, NSData * _Nullable signingPublicKey, NSError * _Nullable error) {
501 [self.spiBlockExpectation fulfill];
502 XCTAssertNil(entropy, "entropy should be nil");
503 XCTAssertNil(bottleID, "bottle id should be nil");
504 XCTAssertNil(signingPublicKey, "signing pub key should be nil");
505 XCTAssertEqual(error.code, errSecInteractionNotAllowed, "should return a errSecInteractionNotAllowed error");
508 [self waitForExpectationsWithTimeout:1.0 handler:nil];
510 __block NSString* localBottleID = @"random bottle id";
511 self.spiBlockExpectation = [self expectationWithDescription:@"scrub bottled peer SPI fired"];
513 [self.otControl scrubBottledPeer:testContextID bottleID:localBottleID reply:^(NSError * _Nullable error) {
514 [self.spiBlockExpectation fulfill];
515 XCTAssertEqual(error.code, errSecInteractionNotAllowed, "should return a errSecInteractionNotAllowed error");
518 OCMVerifyAllWithDelay(self.mockDatabase, 8);
519 [self waitForExpectationsWithTimeout:1.0 handler:nil];
523 -(void)testRestoreNotSignedIn
525 [self setUpRampRecordsInCloudKitWithFeatureOn];
527 self.accountStatus = CKAccountStatusNoAccount;
529 [self startCKKSSubsystem];
531 self.spiBlockExpectation = [self expectationWithDescription:@"restore SPI fired"];
533 [self.otControl restore:testContextID
536 escrowRecordID:self.sosPeerID
537 reply:^(NSData* signingKeyData, NSData* encryptionKeyData, NSError* _Nullable error) {
538 [self.spiBlockExpectation fulfill];
539 XCTAssertNil(signingKeyData, "Signing key data should be nil");
540 XCTAssertNil(encryptionKeyData, "encryption key data should be nil");
541 XCTAssertEqual(error.code, OTErrorNotSignedIn, "should return a OTErrorNotSignedIn error");
543 OCMVerifyAllWithDelay(self.mockDatabase, 8);
544 [self waitForExpectationsWithTimeout:1.0 handler:nil];
548 -(void) testRestoreWithNoNetwork
550 [self setUpRampRecordsInCloudKitWithFeatureOn];
552 self.accountStatus = CKAccountStatusAvailable;
554 [self.reachabilityTracker setNetworkReachability:false];
556 [self startCKKSSubsystem];
558 self.spiBlockExpectation = [self expectationWithDescription:@"restore SPI fired"];
560 [self.otControl restore:testContextID
563 escrowRecordID:self.sosPeerID
564 reply:^(NSData* signingKeyData, NSData* encryptionKeyData, NSError* _Nullable error) {
565 [self.spiBlockExpectation fulfill];
566 XCTAssertNil(signingKeyData, "Signing key data should be nil");
567 XCTAssertNil(encryptionKeyData, "encryption key data should be nil");
568 XCTAssertEqual(error.code, OTErrorNoNetwork, "should return a OTErrorNoNetwork error");
570 OCMVerifyAllWithDelay(self.mockDatabase, 8);
571 [self waitForExpectationsWithTimeout:1.0 handler:nil];
574 -(void) testRestoreWhenLocked
576 [self setUpRampRecordsInCloudKitWithFeatureOn];
578 self.aksLockState = true;
579 [self.lockStateTracker recheck];
581 [self startCKKSSubsystem];
583 self.spiBlockExpectation = [self expectationWithDescription:@"restore SPI fired"];
585 [self.otControl restore:testContextID
588 escrowRecordID:self.sosPeerID
589 reply:^(NSData* signingKeyData, NSData* encryptionKeyData, NSError* _Nullable error) {
590 [self.spiBlockExpectation fulfill];
591 XCTAssertNil(signingKeyData, "Signing key data should be nil");
592 XCTAssertNil(encryptionKeyData, "encryption key data should be nil");
593 XCTAssertEqual(error.code, errSecInteractionNotAllowed, "should return a errSecInteractionNotAllowed error");
595 OCMVerifyAllWithDelay(self.mockDatabase, 8);
596 [self waitForExpectationsWithTimeout:1.0 handler:nil];
600 -(void)testEnrollRampNotSignedIn
602 [self setUpRampRecordsInCloudKitWithFeatureOn];
604 NSError* error = nil;
605 NSInteger retryAfter = 0;
607 self.accountStatus = CKAccountStatusNoAccount;
608 [self startCKKSSubsystem];
610 [self.enroll checkRampState:&retryAfter networkBehavior:CKOperationDiscretionaryNetworkBehaviorNonDiscretionary error:&error];
612 XCTAssertEqual(error.code, OTErrorNotSignedIn, "should return a OTErrorNotSignedIn error");
616 -(void) testEnrollRampWithNoNetwork
618 [self setUpRampRecordsInCloudKitWithFeatureOn];
620 NSError* error = nil;
621 NSInteger retryAfter = 0;
623 self.accountStatus = CKAccountStatusAvailable;
624 [self startCKKSSubsystem];
626 [self.reachabilityTracker setNetworkReachability:false];
628 [self.enroll checkRampState:&retryAfter networkBehavior:CKOperationDiscretionaryNetworkBehaviorNonDiscretionary error:&error];
630 XCTAssertEqual(error.code, OTErrorNoNetwork, "should return a OTErrorNoNetwork error");
633 -(void) testEnrollRampWhenLocked
635 [self setUpRampRecordsInCloudKitWithFeatureOn];
637 NSError* error = nil;
638 NSInteger retryAfter = 0;
640 self.aksLockState = true;
641 [self.lockStateTracker recheck];
643 [self.enroll checkRampState:&retryAfter networkBehavior:CKOperationDiscretionaryNetworkBehaviorNonDiscretionary error:&error];
645 XCTAssertEqual(error.code, errSecInteractionNotAllowed, "should return a errSecInteractionNotAllowed error");
648 -(void) testTimeBetweenCFUAttempts
650 [self setUpRampRecordsInCloudKitWithFeatureOn];
652 NSError* error = nil;
654 [self startCKKSSubsystem];
656 [self.manager scheduledCloudKitRampCheck:&error];
657 XCTAssertNotNil(error, "Should have had an error scheduling a ramp check");
658 XCTAssertEqual(error.code, OTErrorNoBottlePeerRecords, "Error should be 'no bottled peer records'");
659 XCTAssertNotNil(self.manager.lastPostedCoreFollowUp, "core followup should have been posted");
660 NSDate* firstTime = self.manager.lastPostedCoreFollowUp;
665 [self.manager scheduledCloudKitRampCheck:&error];
666 XCTAssertNotNil(error, "Should have had an error scheduling a ramp check");
667 XCTAssertEqual(error.code, OTErrorNoBottlePeerRecords, "Error should be 'no bottled peer records'");
668 XCTAssertNotNil(self.manager.lastPostedCoreFollowUp, "core followup should have been posted");
669 NSDate* secondTime = self.manager.lastPostedCoreFollowUp;
671 XCTAssertTrue([secondTime timeIntervalSinceDate:firstTime] >= 2, "time difference should be slightly more than 2 seconds");