]> git.saurik.com Git - apple/security.git/blob - keychain/ot/tests/OTRampingTests.m
Security-59306.11.20.tar.gz
[apple/security.git] / keychain / ot / tests / OTRampingTests.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 <Foundation/Foundation.h>
27
28 #import <XCTest/XCTest.h>
29 #import <OCMock/OCMock.h>
30
31 #import "OTTestsBase.h"
32 #import "keychain/ot/OTConstants.h"
33
34 static NSString* const testContextID = @"Foo";
35 static NSString* const testDSID = @"123456789";
36
37 static NSString* OTCKRecordBottledPeerType = @"OTBottledPeer";
38
39 @interface OTRampingUnitTests : OTTestsBase
40
41 @end
42
43 @implementation OTRampingUnitTests
44
45 - (void)setUp {
46 [super setUp];
47 self.continueAfterFailure = NO;
48 }
49
50 - (void)tearDown {
51 [super tearDown];
52 }
53
54 -(void) testPreflightWithFeatureOnOn
55 {
56 [self setUpRampRecordsInCloudKitWithFeatureOn];
57
58 [self startCKKSSubsystem];
59
60 [self.otControl preflightBottledPeer:testContextID
61 dsid:testDSID
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");
67 }];
68
69 }
70
71 -(void) testBottleUpdateWithFeatureOnOn
72 {
73 __block NSData* localEntropy = nil;
74 __block NSString* localBottleID = nil;
75
76 [self setUpRampRecordsInCloudKitWithFeatureOn];
77 [self createAndSaveFakeKeyHierarchy:self.keychainZoneID];
78 [self putSelfTLKSharesInCloudKit:self.keychainZoneID];
79
80 [self startCKKSSubsystem];
81
82 //create a bottle
83 [self.otControl preflightBottledPeer:testContextID
84 dsid:testDSID
85 reply:^(NSData * _Nullable entropy, NSString * _Nullable bottleID, NSData * _Nullable signingPublicKey, NSError * _Nullable error) {
86 localEntropy = entropy;
87 localBottleID = bottleID;
88 XCTAssertNotNil(entropy, "entropy should not be nil");
89 XCTAssertNotNil(bottleID, "bottle id should not be nil");
90 XCTAssertNotNil(signingPublicKey, "signing pub key should not be nil");
91 XCTAssertNil(error, "error should be nil");
92 }];
93
94 NSMutableDictionary* recordDictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:[[NSNumber alloc] initWithInt:1], OTCKRecordBottledPeerType, nil];
95
96 [self expectAddedCKModifyRecords:recordDictionary holdFetch:NO];
97
98 //launch it
99 [self.otControl launchBottledPeer:testContextID bottleID:localBottleID reply:^(NSError * _Nullable error) {
100 XCTAssertNil(error, "error should be nil");
101 }];
102
103 [self waitForCKModifications];
104 OCMVerifyAllWithDelay(self.mockDatabase, 8);
105 [self releaseCloudKitFetchHold];
106
107 [self expectCKFetch];
108
109 SFECKeyPair* newSigningKey = [[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]];
110
111 SFECKeyPair* newEncryptionKey = [[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]];
112
113 [self expectAddedCKModifyRecords:recordDictionary holdFetch:NO];
114
115 //update bottle
116 [self.otControl handleIdentityChangeForSigningKey:newSigningKey
117 ForEncryptionKey:newEncryptionKey
118 ForPeerID:self.sosPeerID
119 reply:^(BOOL result, NSError* _Nullable error){
120 XCTAssertNil(error, "error should be nil");
121 }];
122 }
123
124 -(void) testLaunchWithRampOn
125 {
126 [self setUpRampRecordsInCloudKitWithFeatureOn];
127 [self createAndSaveFakeKeyHierarchy:self.keychainZoneID];
128 [self putSelfTLKSharesInCloudKit:self.keychainZoneID];
129 [self startCKKSSubsystem];
130
131 __block NSData* localEntropy = nil;
132 __block NSString* localBottleID = nil;
133
134 self.spiBlockExpectation = [self expectationWithDescription:@"preflight bottled peer fired"];
135
136 [self.otControl preflightBottledPeer:testContextID
137 dsid:testDSID
138 reply:^(NSData * _Nullable entropy, NSString * _Nullable bottleID, NSData * _Nullable signingPublicKey, NSError * _Nullable error) {
139 [self.spiBlockExpectation fulfill];
140 localEntropy = entropy;
141 localBottleID = bottleID;
142 XCTAssertNotNil(entropy, "entropy should not be nil");
143 XCTAssertNotNil(bottleID, "bottle id should not be nil");
144 XCTAssertNotNil(signingPublicKey, "signing pub key should not be nil");
145 XCTAssertNil(error, "error should be nil");
146 }];
147 [self waitForExpectationsWithTimeout:1.0 handler:nil];
148
149 self.spiBlockExpectation = [self expectationWithDescription:@"launch bottled peer fired"];
150
151 NSMutableDictionary* recordDictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:[[NSNumber alloc] initWithInt:1], OTCKRecordBottledPeerType, nil];
152
153 [self expectAddedCKModifyRecords:recordDictionary holdFetch:NO];
154
155 [self.otControl launchBottledPeer:testContextID bottleID:localBottleID reply:^(NSError * _Nullable error) {
156 [self.spiBlockExpectation fulfill];
157 XCTAssertNil(error, "error should be nil");
158 }];
159
160 [self waitForExpectationsWithTimeout:1.0 handler:nil];
161 }
162
163 -(void) testRestoreWithRampOn
164 {
165 [self setUpRampRecordsInCloudKitWithFeatureOn];
166 [self createAndSaveFakeKeyHierarchy:self.keychainZoneID];
167 [self putSelfTLKSharesInCloudKit:self.keychainZoneID];
168 [self startCKKSSubsystem];
169
170 __block NSData* localEntropy = nil;
171 __block NSString* localBottleID = nil;
172
173 self.spiBlockExpectation = [self expectationWithDescription:@"preflight bottled peer fired"];
174
175 [self.otControl preflightBottledPeer:OTDefaultContext
176 dsid:@"dsid"
177 reply:^(NSData * _Nullable entropy, NSString * _Nullable bottleID, NSData * _Nullable signingPublicKey, NSError * _Nullable error) {
178 [self.spiBlockExpectation fulfill];
179 localEntropy = entropy;
180 localBottleID = bottleID;
181 XCTAssertNotNil(entropy, "entropy should not be nil");
182 XCTAssertNotNil(bottleID, "bottle id should not be nil");
183 XCTAssertNotNil(signingPublicKey, "signing pub key should not be nil");
184 XCTAssertNil(error, "error should be nil");
185 }];
186 [self waitForExpectationsWithTimeout:1.0 handler:nil];
187
188 __block NSData* localSigningKeyData = nil;
189 __block NSData* localEncryptionKeyData = nil;
190
191 self.spiBlockExpectation = [self expectationWithDescription:@"preflight bottled peer fired"];
192
193 [self.otControl restore:testContextID
194 dsid:testDSID
195 secret:localEntropy
196 escrowRecordID:self.sosPeerID
197 reply:^(NSData* signingKeyData, NSData* encryptionKeyData, NSError* _Nullable error) {
198 [self.spiBlockExpectation fulfill];
199 localSigningKeyData = signingKeyData;
200 localEncryptionKeyData = encryptionKeyData;
201 XCTAssertNotNil(signingKeyData, "Signing key data should not be nil");
202 XCTAssertNotNil(encryptionKeyData, "encryption key data should not be nil");
203 XCTAssertNil(error, "error should not be nil");
204 }];
205 [self waitForExpectationsWithTimeout:1.0 handler:nil];
206 NSError* localError = nil;
207
208 OTIdentity *ourSelf = [self currentIdentity:&localError];
209 XCTAssertTrue([localSigningKeyData isEqualToData:[ourSelf.peerSigningKey.publicKey keyData]], @"signing keys should be equal!");
210 XCTAssertTrue([localEncryptionKeyData isEqualToData:[ourSelf.peerEncryptionKey.publicKey keyData]], @"signing keys should be equal!");
211 }
212
213 -(void) testScrubWithRampOn
214 {
215 [self setUpRampRecordsInCloudKitWithFeatureOn];
216 [self createAndSaveFakeKeyHierarchy:self.keychainZoneID];
217 [self putSelfTLKSharesInCloudKit:self.keychainZoneID];
218 [self startCKKSSubsystem];
219
220 __block NSString* localBottleID = nil;
221
222 self.spiBlockExpectation = [self expectationWithDescription:@"preflight bottled peer fired"];
223
224 [self.otControl preflightBottledPeer:testContextID
225 dsid:testDSID
226 reply:^(NSData * _Nullable entropy, NSString * _Nullable bottleID, NSData * _Nullable signingPublicKey, NSError * _Nullable error) {
227 [self.spiBlockExpectation fulfill];
228 localBottleID = bottleID;
229 XCTAssertNotNil(entropy, "entropy should not be nil");
230 XCTAssertNotNil(bottleID, "bottle id should not be nil");
231 XCTAssertNotNil(signingPublicKey, "signing pub key should not be nil");
232 XCTAssertNil(error, "error should be nil");
233 }];
234
235 [self waitForExpectationsWithTimeout:1.0 handler:nil];
236
237 self.spiBlockExpectation = [self expectationWithDescription:@"scrub scheduler fired"];
238
239 [self.otControl scrubBottledPeer:testContextID bottleID:localBottleID reply:^(NSError * _Nullable error) {
240 [self.spiBlockExpectation fulfill];
241 XCTAssertNil(error, "error should be nil");
242 }];
243 [self waitForExpectationsWithTimeout:1.0 handler:nil];
244
245 NSError* localError = nil;
246 NSArray* bottles = [self.localStore readAllLocalBottledPeerRecords:&localError];
247 XCTAssertNotNil(localError, "error should not be nil");
248 XCTAssertTrue([bottles count] == 0, "should be 0 bottles");
249 }
250
251 -(void) testPreflightWithRampOff
252 {
253 [self setUpRampRecordsInCloudKitWithFeatureOff];
254 [self createAndSaveFakeKeyHierarchy:self.keychainZoneID];
255 [self putSelfTLKSharesInCloudKit:self.keychainZoneID];
256 [self startCKKSSubsystem];
257
258 self.spiBlockExpectation = [self expectationWithDescription:@"preflight bottled peer fired"];
259
260 [self.otControl preflightBottledPeer:OTDefaultContext
261 dsid:@"dsid"
262 reply:^(NSData * _Nullable entropy, NSString * _Nullable bottleID, NSData * _Nullable signingPublicKey, NSError * _Nullable error) {
263 [self.spiBlockExpectation fulfill];
264 XCTAssertNil(entropy, "shouldn't return any entropy");
265 XCTAssertNil(bottleID, "shouldn't return a bottle ID");
266 XCTAssertNil(signingPublicKey, "shouldn't return a signingPublicKey");
267 XCTAssertEqual(error.code, OTErrorFeatureNotEnabled, "should return a OTErrorFeatureNotEnabled error");
268 }];
269 [self waitForCKModifications];
270 OCMVerifyAllWithDelay(self.mockDatabase, 8);
271 [self waitForExpectationsWithTimeout:1.0 handler:nil];
272 }
273
274 -(void) testBottleUpdateWithFeatureOff
275 {
276 [self setUpRampRecordsInCloudKitWithFeatureOff];
277 [self createAndSaveFakeKeyHierarchy:self.keychainZoneID];
278 [self putSelfTLKSharesInCloudKit:self.keychainZoneID];
279 [self startCKKSSubsystem];
280
281 SFECKeyPair* newSigningKey = [[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]];
282
283 SFECKeyPair* newEncryptionKey = [[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]];
284
285 //update bottle
286 [self.otControl handleIdentityChangeForSigningKey:newSigningKey
287 ForEncryptionKey:newEncryptionKey
288 ForPeerID:self.sosPeerID
289 reply:^(BOOL result, NSError* _Nullable error){
290 XCTAssertNotNil(error, "error should be nil");
291 XCTAssertEqual(error.code, OTErrorFeatureNotEnabled, "should return a OTErrorFeatureNotEnabled error");
292 }];
293
294 }
295
296 -(void) testPreflightWithRecordNotThere
297 {
298 [self createAndSaveFakeKeyHierarchy:self.keychainZoneID];
299 [self putSelfTLKSharesInCloudKit:self.keychainZoneID];
300 [self startCKKSSubsystem];
301
302 self.spiBlockExpectation = [self expectationWithDescription:@"preflight bottled peer fired"];
303
304 [self.otControl preflightBottledPeer:OTDefaultContext
305 dsid:@"dsid"
306 reply:^(NSData * _Nullable entropy, NSString * _Nullable bottleID, NSData * _Nullable signingPublicKey, NSError * _Nullable error) {
307 [self.spiBlockExpectation fulfill];
308 XCTAssertNil(entropy, "shouldn't return any entropy");
309 XCTAssertNil(bottleID, "shouldn't return a bottle ID");
310 XCTAssertNil(signingPublicKey, "shouldn't return a signingPublicKey");
311 XCTAssertNotNil(error, "should not be nil");
312 }];
313 [self waitForCKModifications];
314 OCMVerifyAllWithDelay(self.mockDatabase, 8);
315 [self waitForExpectationsWithTimeout:1.0 handler:nil];
316 }
317
318 -(void) testLaunchWithRampOff
319 {
320 [self setUpRampRecordsInCloudKitWithFeatureOff];
321 [self createAndSaveFakeKeyHierarchy:self.keychainZoneID];
322 [self putSelfTLKSharesInCloudKit:self.keychainZoneID];
323 [self startCKKSSubsystem];
324
325 self.spiBlockExpectation = [self expectationWithDescription:@"preflight bottled peer fired"];
326
327 [self.otControl preflightBottledPeer:OTDefaultContext
328 dsid:@"dsid"
329 reply:^(NSData * _Nullable entropy, NSString * _Nullable bottleID, NSData * _Nullable signingPublicKey, NSError * _Nullable error) {
330 [self.spiBlockExpectation fulfill];
331 XCTAssertNil(entropy, "shouldn't return any entropy");
332 XCTAssertNil(bottleID, "shouldn't return a bottle ID");
333 XCTAssertNil(signingPublicKey, "shouldn't return a signingPublicKey");
334 XCTAssertEqual(error.code, OTErrorFeatureNotEnabled, "should return a OTErrorFeatureNotEnabled error");
335 }];
336 [self waitForCKModifications];
337 OCMVerifyAllWithDelay(self.mockDatabase, 8);
338 [self waitForExpectationsWithTimeout:1.0 handler:nil];
339
340
341 self.spiBlockExpectation = [self expectationWithDescription:@"launch SPI fired"];
342
343 NSString* localBottleID = @"random bottle id";
344 [self.otControl launchBottledPeer:testContextID bottleID:localBottleID reply:^(NSError * _Nullable error) {
345 [self.spiBlockExpectation fulfill];
346 XCTAssertEqual(error.code, OTErrorFeatureNotEnabled, "should return a OTErrorFeatureNotEnabled error");
347 }];
348
349 [self waitForCKModifications];
350 OCMVerifyAllWithDelay(self.mockDatabase, 8);
351 [self waitForExpectationsWithTimeout:1.0 handler:nil];
352 }
353 -(void) testRestoreWithRampOff
354 {
355 [self setUpRampRecordsInCloudKitWithFeatureOff];
356 [self createAndSaveFakeKeyHierarchy:self.keychainZoneID];
357 [self putSelfTLKSharesInCloudKit:self.keychainZoneID];
358 [self startCKKSSubsystem];
359
360 self.spiBlockExpectation = [self expectationWithDescription:@"restore SPI fired"];
361
362 [self.otControl restore:testContextID
363 dsid:testDSID
364 secret:self.secret
365 escrowRecordID:self.sosPeerID
366 reply:^(NSData* signingKeyData, NSData* encryptionKeyData, NSError* _Nullable error) {
367 [self.spiBlockExpectation fulfill];
368 XCTAssertNil(signingKeyData, "Signing key data should be nil");
369 XCTAssertNil(encryptionKeyData, "encryption key data should be nil");
370 XCTAssertEqual(error.code, OTErrorFeatureNotEnabled, "should return a OTErrorFeatureNotEnabled error");
371 }];
372 [self waitForCKModifications];
373 OCMVerifyAllWithDelay(self.mockDatabase, 8);
374 [self waitForExpectationsWithTimeout:1.0 handler:nil];
375 }
376
377 -(void) testScrubWithRampOff
378 {
379 [self setUpRampRecordsInCloudKitWithFeatureOff];
380 [self createAndSaveFakeKeyHierarchy:self.keychainZoneID];
381 [self putSelfTLKSharesInCloudKit:self.keychainZoneID];
382 [self startCKKSSubsystem];
383
384 self.spiBlockExpectation = [self expectationWithDescription:@"preflight bottled peer SPI fired"];
385
386 [self.otControl preflightBottledPeer:testContextID
387 dsid:testDSID
388 reply:^(NSData * _Nullable entropy, NSString * _Nullable bottleID, NSData * _Nullable signingPublicKey, NSError * _Nullable error) {
389 [self.spiBlockExpectation fulfill];
390 XCTAssertNil(entropy, "entropy should be nil");
391 XCTAssertNil(bottleID, "bottle id should be nil");
392 XCTAssertNil(signingPublicKey, "signing pub key should be nil");
393 XCTAssertEqual(error.code, OTErrorFeatureNotEnabled, "should return a OTErrorFeatureNotEnabled error");
394 }];
395
396 [self waitForExpectationsWithTimeout:1.0 handler:nil];
397
398 __block NSString* localBottleID = @"random bottle id";
399 self.spiBlockExpectation = [self expectationWithDescription:@"scrub bottled peer SPI fired"];
400
401 [self.otControl scrubBottledPeer:testContextID bottleID:localBottleID reply:^(NSError * _Nullable error) {
402 [self.spiBlockExpectation fulfill];
403 XCTAssertEqual(error.code, OTErrorFeatureNotEnabled, "should return a OTErrorFeatureNotEnabled error");
404 }];
405
406 [self waitForCKModifications];
407 OCMVerifyAllWithDelay(self.mockDatabase, 8);
408 [self waitForExpectationsWithTimeout:1.0 handler:nil];
409
410 [self createAndSaveFakeKeyHierarchy: self.keychainZoneID]; // Make life easy for this test.
411 [self startCKKSSubsystem];
412
413 XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateReady] wait:4*NSEC_PER_SEC], @"Key state should have arrived at ready");
414 }
415
416 -(void) testRampFetchTimeout
417 {
418 [self createAndSaveFakeKeyHierarchy:self.keychainZoneID];
419 [self putSelfTLKSharesInCloudKit:self.keychainZoneID];
420 [self startCKKSSubsystem];
421
422 __block NSError* localError = nil;
423
424 [self holdCloudKitFetches];
425
426 [self.otControl preflightBottledPeer:OTDefaultContext
427 dsid:@"dsid"
428 reply:^(NSData * _Nullable entropy, NSString * _Nullable bottleID, NSData * _Nullable signingPublicKey, NSError * _Nullable error) {
429 localError = error;
430 XCTAssertNil(entropy, "shouldn't return any entropy");
431 XCTAssertNil(bottleID, "shouldn't return a bottle ID");
432 XCTAssertNil(signingPublicKey, "shouldn't return a signingPublicKey");
433 XCTAssertEqual(error.code, OTErrorCKTimeOut, "should return a OTErrorCKTimeout error");
434 }];
435 }
436
437 -(void)testCFUWithRampOn
438 {
439 NSError* localError = nil;
440
441 [self setUpRampRecordsInCloudKitWithFeatureOn];
442
443 [self startCKKSSubsystem];
444
445 XCTAssertTrue([self.cfu checkRampStateWithError:&localError], @"should be true");
446 XCTAssertNil(localError, "Should not have gotten an error checking ramp state (and getting true)");
447 }
448
449 -(void)testCFUWithRampOff
450 {
451 NSError* localError = nil;
452 [self setUpRampRecordsInCloudKitWithFeatureOff];
453
454 [self startCKKSSubsystem];
455
456 XCTAssertFalse([self.cfu checkRampStateWithError:&localError], @"should be false");
457 XCTAssertNil(localError, "Should not have gotten an error checking ramp state (and getting false)");
458 }
459
460 -(void)testCFUWithNonExistentRampRecord
461 {
462 NSError* localError = nil;
463
464 [self startCKKSSubsystem];
465
466 XCTAssertFalse([self.cfu checkRampStateWithError:&localError], @"should be false");
467 XCTAssertNotNil(localError, "Should have gotten an error checking ramp state (and getting false)");
468 XCTAssertEqual(localError.code, OTErrorRecordNotFound, "Error should be 'record not found'");
469 }
470
471 @end
472
473 #endif /* OCTAGON */
474