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"
32 #import "keychain/ot/OTConstants.h"
34 static NSString* const testContextID = @"Foo";
35 static NSString* const testDSID = @"123456789";
37 static NSString* OTCKRecordBottledPeerType = @"OTBottledPeer";
39 @interface OTRampingUnitTests : OTTestsBase
43 @implementation OTRampingUnitTests
47 self.continueAfterFailure = NO;
54 -(void) testPreflightWithFeatureOnOn
56 [self setUpRampRecordsInCloudKitWithFeatureOn];
58 [self startCKKSSubsystem];
60 [self.otControl preflightBottledPeer:testContextID
62 reply:^(NSData * _Nullable entropy, NSString * _Nullable bottleID, NSData * _Nullable signingPublicKey, NSError * _Nullable error) {
63 XCTAssertNotNil(entropy, "entropy should not be nil");
64 XCTAssertNotNil(bottleID, "bottle id should not be nil");
65 XCTAssertNotNil(signingPublicKey, "signing pub key should not be nil");
66 XCTAssertNil(error, "error should be nil");
71 -(void) testLaunchWithRampOn
73 [self setUpRampRecordsInCloudKitWithFeatureOn];
74 [self startCKKSSubsystem];
76 __block NSData* localEntropy = nil;
77 __block NSString* localBottleID = nil;
79 self.spiBlockExpectation = [self expectationWithDescription:@"preflight bottled peer fired"];
81 [self.otControl preflightBottledPeer:testContextID
83 reply:^(NSData * _Nullable entropy, NSString * _Nullable bottleID, NSData * _Nullable signingPublicKey, NSError * _Nullable error) {
84 [self.spiBlockExpectation fulfill];
85 localEntropy = entropy;
86 localBottleID = bottleID;
87 XCTAssertNotNil(entropy, "entropy should not be nil");
88 XCTAssertNotNil(bottleID, "bottle id should not be nil");
89 XCTAssertNotNil(signingPublicKey, "signing pub key should not be nil");
90 XCTAssertNil(error, "error should be nil");
92 [self waitForExpectationsWithTimeout:1.0 handler:nil];
94 self.spiBlockExpectation = [self expectationWithDescription:@"launch bottled peer fired"];
96 NSMutableDictionary* recordDictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:[[NSNumber alloc] initWithInt:1], OTCKRecordBottledPeerType, nil];
98 [self expectAddedCKModifyRecords:recordDictionary holdFetch:NO];
100 [self.otControl launchBottledPeer:testContextID bottleID:localBottleID reply:^(NSError * _Nullable error) {
101 [self.spiBlockExpectation fulfill];
102 XCTAssertNil(error, "error should be nil");
105 [self waitForExpectationsWithTimeout:1.0 handler:nil];
108 -(void) testRestoreWithRampOn
110 [self setUpRampRecordsInCloudKitWithFeatureOn];
111 [self startCKKSSubsystem];
113 __block NSData* localEntropy = nil;
114 __block NSString* localBottleID = nil;
116 self.spiBlockExpectation = [self expectationWithDescription:@"preflight bottled peer fired"];
118 [self.otControl preflightBottledPeer:OTDefaultContext
120 reply:^(NSData * _Nullable entropy, NSString * _Nullable bottleID, NSData * _Nullable signingPublicKey, NSError * _Nullable error) {
121 [self.spiBlockExpectation fulfill];
122 localEntropy = entropy;
123 localBottleID = bottleID;
124 XCTAssertNotNil(entropy, "entropy should not be nil");
125 XCTAssertNotNil(bottleID, "bottle id should not be nil");
126 XCTAssertNotNil(signingPublicKey, "signing pub key should not be nil");
127 XCTAssertNil(error, "error should be nil");
129 [self waitForExpectationsWithTimeout:1.0 handler:nil];
131 __block NSData* localSigningKeyData = nil;
132 __block NSData* localEncryptionKeyData = nil;
134 self.spiBlockExpectation = [self expectationWithDescription:@"preflight bottled peer fired"];
136 [self.otControl restore:testContextID
139 escrowRecordID:self.sosPeerID
140 reply:^(NSData* signingKeyData, NSData* encryptionKeyData, NSError* _Nullable error) {
141 [self.spiBlockExpectation fulfill];
142 localSigningKeyData = signingKeyData;
143 localEncryptionKeyData = encryptionKeyData;
144 XCTAssertNotNil(signingKeyData, "Signing key data should not be nil");
145 XCTAssertNotNil(encryptionKeyData, "encryption key data should not be nil");
146 XCTAssertNil(error, "error should not be nil");
148 [self waitForExpectationsWithTimeout:1.0 handler:nil];
149 NSError* localError = nil;
151 OTIdentity *ourSelf = [self currentIdentity:&localError];
152 XCTAssertTrue([localSigningKeyData isEqualToData:[ourSelf.peerSigningKey.publicKey keyData]], @"signing keys should be equal!");
153 XCTAssertTrue([localEncryptionKeyData isEqualToData:[ourSelf.peerEncryptionKey.publicKey keyData]], @"signing keys should be equal!");
156 -(void) testScrubWithRampOn
158 [self setUpRampRecordsInCloudKitWithFeatureOn];
159 [self startCKKSSubsystem];
161 __block NSString* localBottleID = nil;
163 self.spiBlockExpectation = [self expectationWithDescription:@"preflight bottled peer fired"];
165 [self.otControl preflightBottledPeer:testContextID
167 reply:^(NSData * _Nullable entropy, NSString * _Nullable bottleID, NSData * _Nullable signingPublicKey, NSError * _Nullable error) {
168 [self.spiBlockExpectation fulfill];
169 localBottleID = bottleID;
170 XCTAssertNotNil(entropy, "entropy should not be nil");
171 XCTAssertNotNil(bottleID, "bottle id should not be nil");
172 XCTAssertNotNil(signingPublicKey, "signing pub key should not be nil");
173 XCTAssertNil(error, "error should be nil");
176 [self waitForExpectationsWithTimeout:1.0 handler:nil];
178 self.spiBlockExpectation = [self expectationWithDescription:@"scrub scheduler fired"];
180 [self.otControl scrubBottledPeer:testContextID bottleID:localBottleID reply:^(NSError * _Nullable error) {
181 [self.spiBlockExpectation fulfill];
182 XCTAssertNil(error, "error should be nil");
184 [self waitForExpectationsWithTimeout:1.0 handler:nil];
186 NSError* localError = nil;
187 NSArray* bottles = [self.localStore readAllLocalBottledPeerRecords:&localError];
188 XCTAssertNotNil(localError, "error should not be nil");
189 XCTAssertTrue([bottles count] == 0, "should be 0 bottles");
192 -(void) testPreflightWithRampOff
194 [self setUpRampRecordsInCloudKitWithFeatureOff];
196 [self startCKKSSubsystem];
198 self.spiBlockExpectation = [self expectationWithDescription:@"preflight bottled peer fired"];
200 [self.otControl preflightBottledPeer:OTDefaultContext
202 reply:^(NSData * _Nullable entropy, NSString * _Nullable bottleID, NSData * _Nullable signingPublicKey, NSError * _Nullable error) {
203 [self.spiBlockExpectation fulfill];
204 XCTAssertNil(entropy, "shouldn't return any entropy");
205 XCTAssertNil(bottleID, "shouldn't return a bottle ID");
206 XCTAssertNil(signingPublicKey, "shouldn't return a signingPublicKey");
207 XCTAssertTrue(error.code == OTErrorFeatureNotEnabled, "should return a OTErrorFeatureNotEnabled error");
209 [self waitForCKModifications];
210 OCMVerifyAllWithDelay(self.mockDatabase, 8);
211 [self waitForExpectationsWithTimeout:1.0 handler:nil];
214 -(void) testPreflightWithRecordNotThere
216 [self startCKKSSubsystem];
218 self.spiBlockExpectation = [self expectationWithDescription:@"preflight bottled peer fired"];
220 [self.otControl preflightBottledPeer:OTDefaultContext
222 reply:^(NSData * _Nullable entropy, NSString * _Nullable bottleID, NSData * _Nullable signingPublicKey, NSError * _Nullable error) {
223 [self.spiBlockExpectation fulfill];
224 XCTAssertNil(entropy, "shouldn't return any entropy");
225 XCTAssertNil(bottleID, "shouldn't return a bottle ID");
226 XCTAssertNil(signingPublicKey, "shouldn't return a signingPublicKey");
227 XCTAssertNotNil(error, "should not be nil");
229 [self waitForCKModifications];
230 OCMVerifyAllWithDelay(self.mockDatabase, 8);
231 [self waitForExpectationsWithTimeout:1.0 handler:nil];
234 -(void) testLaunchWithRampOff
236 [self setUpRampRecordsInCloudKitWithFeatureOff];
238 [self startCKKSSubsystem];
240 self.spiBlockExpectation = [self expectationWithDescription:@"preflight bottled peer fired"];
242 [self.otControl preflightBottledPeer:OTDefaultContext
244 reply:^(NSData * _Nullable entropy, NSString * _Nullable bottleID, NSData * _Nullable signingPublicKey, NSError * _Nullable error) {
245 [self.spiBlockExpectation fulfill];
246 XCTAssertNil(entropy, "shouldn't return any entropy");
247 XCTAssertNil(bottleID, "shouldn't return a bottle ID");
248 XCTAssertNil(signingPublicKey, "shouldn't return a signingPublicKey");
249 XCTAssertTrue(error.code == OTErrorFeatureNotEnabled, "should return a OTErrorFeatureNotEnabled error");
251 [self waitForCKModifications];
252 OCMVerifyAllWithDelay(self.mockDatabase, 8);
253 [self waitForExpectationsWithTimeout:1.0 handler:nil];
256 self.spiBlockExpectation = [self expectationWithDescription:@"launch SPI fired"];
258 NSString* localBottleID = @"random bottle id";
259 [self.otControl launchBottledPeer:testContextID bottleID:localBottleID reply:^(NSError * _Nullable error) {
260 [self.spiBlockExpectation fulfill];
261 XCTAssertTrue(error.code == OTErrorFeatureNotEnabled, "should return a OTErrorFeatureNotEnabled error");
264 [self waitForCKModifications];
265 OCMVerifyAllWithDelay(self.mockDatabase, 8);
266 [self waitForExpectationsWithTimeout:1.0 handler:nil];
268 -(void) testRestoreWithRampOff
270 [self setUpRampRecordsInCloudKitWithFeatureOff];
271 [self startCKKSSubsystem];
273 self.spiBlockExpectation = [self expectationWithDescription:@"restore SPI fired"];
275 [self.otControl restore:testContextID
278 escrowRecordID:self.sosPeerID
279 reply:^(NSData* signingKeyData, NSData* encryptionKeyData, NSError* _Nullable error) {
280 [self.spiBlockExpectation fulfill];
281 XCTAssertNil(signingKeyData, "Signing key data should be nil");
282 XCTAssertNil(encryptionKeyData, "encryption key data should be nil");
283 XCTAssertTrue(error.code == OTErrorFeatureNotEnabled, "should return a OTErrorFeatureNotEnabled error");
285 [self waitForCKModifications];
286 OCMVerifyAllWithDelay(self.mockDatabase, 8);
287 [self waitForExpectationsWithTimeout:1.0 handler:nil];
290 -(void) testScrubWithRampOff
292 [self setUpRampRecordsInCloudKitWithFeatureOff];
293 [self startCKKSSubsystem];
295 self.spiBlockExpectation = [self expectationWithDescription:@"preflight bottled peer SPI fired"];
297 [self.otControl preflightBottledPeer:testContextID
299 reply:^(NSData * _Nullable entropy, NSString * _Nullable bottleID, NSData * _Nullable signingPublicKey, NSError * _Nullable error) {
300 [self.spiBlockExpectation fulfill];
301 XCTAssertNil(entropy, "entropy should be nil");
302 XCTAssertNil(bottleID, "bottle id should be nil");
303 XCTAssertNil(signingPublicKey, "signing pub key should be nil");
304 XCTAssertTrue(error.code == OTErrorFeatureNotEnabled, "should return a OTErrorFeatureNotEnabled error");
307 [self waitForExpectationsWithTimeout:1.0 handler:nil];
309 __block NSString* localBottleID = @"random bottle id";
310 self.spiBlockExpectation = [self expectationWithDescription:@"scrub bottled peer SPI fired"];
312 [self.otControl scrubBottledPeer:testContextID bottleID:localBottleID reply:^(NSError * _Nullable error) {
313 [self.spiBlockExpectation fulfill];
314 XCTAssertTrue(error.code == OTErrorFeatureNotEnabled, "should return a OTErrorFeatureNotEnabled error");
317 [self waitForCKModifications];
318 OCMVerifyAllWithDelay(self.mockDatabase, 8);
319 [self waitForExpectationsWithTimeout:1.0 handler:nil];
321 [self createAndSaveFakeKeyHierarchy: self.keychainZoneID]; // Make life easy for this test.
322 [self startCKKSSubsystem];
324 XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateReady] wait:4*NSEC_PER_SEC], @"Key state should have arrived at ready");
327 -(void) testRampFetchTimeout
329 [self startCKKSSubsystem];
331 __block NSError* localError = nil;
333 [self holdCloudKitFetches];
335 [self.otControl preflightBottledPeer:OTDefaultContext
337 reply:^(NSData * _Nullable entropy, NSString * _Nullable bottleID, NSData * _Nullable signingPublicKey, NSError * _Nullable error) {
339 XCTAssertNil(entropy, "shouldn't return any entropy");
340 XCTAssertNil(bottleID, "shouldn't return a bottle ID");
341 XCTAssertNil(signingPublicKey, "shouldn't return a signingPublicKey");
342 XCTAssertTrue(error.code == OTErrorCKTimeOut, "should return a OTErrorCKTimeout error");
346 -(void)testCFUWithRampOn
348 NSError* localError = nil;
349 NSInteger retryAfterInSeconds = 0;
351 [self setUpRampRecordsInCloudKitWithFeatureOn];
353 XCTAssertTrue([self.cfu checkRampState:&retryAfterInSeconds qos:NSQualityOfServiceUserInitiated error:&localError], @"should be true");
356 -(void)testCFUWithRampOff
358 NSError* localError = nil;
359 NSInteger retryAfterInSeconds = 0;
360 [self setUpRampRecordsInCloudKitWithFeatureOff];
362 XCTAssertTrue(![self.cfu checkRampState:&retryAfterInSeconds qos:NSQualityOfServiceUserInitiated error:&localError], @"should be false");
364 XCTAssertTrue(retryAfterInSeconds != 0, @"should be asked to retry later");
367 -(void)testCFUWithNonExistentRampRecord
369 NSError* localError = nil;
370 NSInteger retryAfterInSeconds = 0;
371 XCTAssertTrue(![self.cfu checkRampState:&retryAfterInSeconds qos:NSQualityOfServiceUserInitiated error:&localError], @"should be false");