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 OTUpdateBottlesTests : OTTestsBase
43 @implementation OTUpdateBottlesTests
47 self.continueAfterFailure = NO;
48 [self createAndSaveFakeKeyHierarchy: self.keychainZoneID]; // Make life easy for this test.
55 -(void)testUpdatingExistingBottle
57 __block NSData* localEntropy = nil;
58 __block NSString* localBottleID = nil;
60 self.spiBlockExpectation = [self expectationWithDescription:@"preflight SPI should fire"];
62 [self setUpRampRecordsInCloudKitWithFeatureOn];
64 [self startCKKSSubsystem];
67 [self.otControl preflightBottledPeer:testContextID
69 reply:^(NSData * _Nullable entropy, NSString * _Nullable bottleID, NSData * _Nullable signingPublicKey, NSError * _Nullable error) {
70 [self.spiBlockExpectation fulfill];
71 localEntropy = entropy;
72 localBottleID = bottleID;
73 XCTAssertNotNil(entropy, "entropy should not be nil");
74 XCTAssertNotNil(bottleID, "bottle id should not be nil");
75 XCTAssertNotNil(signingPublicKey, "signing pub key should not be nil");
76 XCTAssertNil(error, "error should be nil");
79 [self waitForExpectationsWithTimeout:1.0 handler:nil];
81 NSMutableDictionary* recordDictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:[[NSNumber alloc] initWithInt:1], OTCKRecordBottledPeerType, nil];
83 [self expectAddedCKModifyRecords:recordDictionary holdFetch:NO];
85 self.spiBlockExpectation = [self expectationWithDescription:@"launch bottle SPI should fire"];
88 [self.otControl launchBottledPeer:testContextID bottleID:localBottleID reply:^(NSError * _Nullable error) {
89 [self.spiBlockExpectation fulfill];
90 XCTAssertNil(error, "error should be nil");
93 [self waitForExpectationsWithTimeout:1.0 handler:nil];
95 [self waitForCKModifications];
96 OCMVerifyAllWithDelay(self.mockDatabase, 8);
97 [self releaseCloudKitFetchHold];
101 SFECKeyPair* newSigningKey = [[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]];
103 SFECKeyPair* newEncryptionKey = [[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]];
105 [self expectAddedCKModifyRecords:recordDictionary holdFetch:NO];
107 self.spiBlockExpectation = [self expectationWithDescription:@"updating identity SPI should fire"];
110 [self.otControl handleIdentityChangeForSigningKey:newSigningKey
111 ForEncryptionKey:newEncryptionKey
112 ForPeerID:self.sosPeerID
113 reply:^(BOOL result, NSError* _Nullable error){
114 [self.spiBlockExpectation fulfill];
115 XCTAssertTrue(result, "result should be YES");
116 XCTAssertNil(error, "error should be nil");
118 [self waitForExpectationsWithTimeout:1.0 handler:nil];
121 -(void)testUpdatingNonExistentBottles
123 self.spiBlockExpectation = [self expectationWithDescription:@"updating identity SPI should fire"];
125 [self setUpRampRecordsInCloudKitWithFeatureOn];
127 [self startCKKSSubsystem];
129 SFECKeyPair* newSigningKey = [[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]];
131 SFECKeyPair* newEncryptionKey = [[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]];
134 [self.otControl handleIdentityChangeForSigningKey:newSigningKey
135 ForEncryptionKey:newEncryptionKey
136 ForPeerID:self.sosPeerID
137 reply:^(BOOL result, NSError* _Nullable error){
138 [self.spiBlockExpectation fulfill];
139 XCTAssertNotNil(error, "error should not be nil");
140 XCTAssertFalse(result, "result should be NO");
142 [self waitForExpectationsWithTimeout:1.0 handler:nil];
145 -(void)testUpdatingBottleMissingKeys
147 __block NSData* localEntropy = nil;
148 __block NSString* localBottleID = nil;
149 NSError *localError = nil;
150 self.spiBlockExpectation = [self expectationWithDescription:@"preflight SPI should fire"];
152 [self setUpRampRecordsInCloudKitWithFeatureOn];
154 [self startCKKSSubsystem];
157 [self.otControl preflightBottledPeer:testContextID
159 reply:^(NSData * _Nullable entropy, NSString * _Nullable bottleID, NSData * _Nullable signingPublicKey, NSError * _Nullable error) {
160 [self.spiBlockExpectation fulfill];
161 localEntropy = entropy;
162 localBottleID = bottleID;
163 XCTAssertNotNil(entropy, "entropy should not be nil");
164 XCTAssertNotNil(bottleID, "bottle id should not be nil");
165 XCTAssertNotNil(signingPublicKey, "signing pub key should not be nil");
166 XCTAssertNil(error, "error should be nil");
169 [self waitForExpectationsWithTimeout:1.0 handler:nil];
171 NSMutableDictionary* recordDictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:[[NSNumber alloc] initWithInt:1], OTCKRecordBottledPeerType, nil];
173 [self expectAddedCKModifyRecords:recordDictionary holdFetch:NO];
175 self.spiBlockExpectation = [self expectationWithDescription:@"launch bottle SPI should fire"];
178 [self.otControl launchBottledPeer:testContextID bottleID:localBottleID reply:^(NSError * _Nullable error) {
179 [self.spiBlockExpectation fulfill];
180 XCTAssertNil(error, "error should be nil");
183 [self waitForExpectationsWithTimeout:1.0 handler:nil];
185 [self waitForCKModifications];
186 OCMVerifyAllWithDelay(self.mockDatabase, 8);
187 [self releaseCloudKitFetchHold];
189 [self expectCKFetch];
192 OTEscrowKeys* keySet = [[OTEscrowKeys alloc] initWithSecret:localEntropy dsid:testDSID error:&localError];
193 XCTAssertNotNil(keySet, "keySet should not be nil");
194 XCTAssertNil(localError, "localError should be nil");
196 NSMutableDictionary* query = [@{
197 (id)kSecClass : (id)kSecClassKey,
198 (id)kSecAttrAccessGroup : @"com.apple.security.ckks",
199 (id)kSecReturnAttributes: @YES,
200 (id)kSecAttrSynchronizable : (id)kCFBooleanFalse,
203 OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query);
204 XCTAssertEqual(status, 0, @"status should be 0");
206 SFECKeyPair* newSigningKey = [[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]];
208 SFECKeyPair* newEncryptionKey = [[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]];
210 [self expectAddedCKModifyRecords:recordDictionary holdFetch:NO];
212 self.spiBlockExpectation = [self expectationWithDescription:@"updating bottle SPI should fire"];
215 [self.otControl handleIdentityChangeForSigningKey:newSigningKey
216 ForEncryptionKey:newEncryptionKey
217 ForPeerID:self.sosPeerID
218 reply:^(BOOL result, NSError* _Nullable error){
219 [self.spiBlockExpectation fulfill];
220 XCTAssertFalse(result, "result should be NO");
221 XCTAssertNotNil(error, "error should not be nil");
223 [self waitForExpectationsWithTimeout:1.0 handler:nil];
227 -(void)testUpdatingMultipleBottlesForSamePeer
229 [self setUpRampRecordsInCloudKitWithFeatureOn];
231 [self startCKKSSubsystem];
233 __block NSData* localEntropy1 = nil;
234 __block NSString* localBottleID1 = nil;
236 self.spiBlockExpectation = [self expectationWithDescription:@"preflight SPI should fire"];
238 [self.otControl preflightBottledPeer:testContextID
240 reply:^(NSData * _Nullable entropy, NSString * _Nullable bottleID, NSData * _Nullable signingPublicKey, NSError * _Nullable error) {
241 [self.spiBlockExpectation fulfill];
242 localEntropy1 = entropy;
243 localBottleID1 = bottleID;
244 XCTAssertNotNil(entropy, "entropy should not be nil");
245 XCTAssertNotNil(bottleID, "bottle id should not be nil");
246 XCTAssertNotNil(signingPublicKey, "signing pub key should not be nil");
247 XCTAssertNil(error, "error should be nil");
249 [self waitForExpectationsWithTimeout:1.0 handler:nil];
251 NSMutableDictionary* recordDictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:[[NSNumber alloc] initWithInt:1], OTCKRecordBottledPeerType, nil];
253 [self expectAddedCKModifyRecords:recordDictionary holdFetch:NO];
255 self.spiBlockExpectation = [self expectationWithDescription:@"launching bottle SPI should fire"];
257 [self.otControl launchBottledPeer:testContextID bottleID:localBottleID1 reply:^(NSError * _Nullable error) {
258 [self.spiBlockExpectation fulfill];
259 XCTAssertNil(error, "error should be nil");
262 [self waitForExpectationsWithTimeout:1.0 handler:nil];
264 [self waitForCKModifications];
265 OCMVerifyAllWithDelay(self.mockDatabase, 8);
267 __block NSData* localEntropy2 = nil;
268 __block NSString* localBottleID2 = nil;
270 self.spiBlockExpectation = [self expectationWithDescription:@"preflight SPI should fire"];
272 [self.otControl preflightBottledPeer:testContextID
274 reply:^(NSData * _Nullable entropy, NSString * _Nullable bottleID, NSData * _Nullable signingPublicKey, NSError * _Nullable error) {
275 [self.spiBlockExpectation fulfill];
276 localEntropy2 = entropy;
277 localBottleID2 = bottleID;
278 XCTAssertNotNil(entropy, "entropy should not be nil");
279 XCTAssertNotNil(bottleID, "bottle id should not be nil");
280 XCTAssertNotNil(signingPublicKey, "signing pub key should not be nil");
281 XCTAssertNil(error, "error should be nil");
283 [self waitForExpectationsWithTimeout:1.0 handler:nil];
285 [self expectAddedCKModifyRecords:recordDictionary holdFetch:NO];
287 self.spiBlockExpectation = [self expectationWithDescription:@"launching bottle SPI should fire"];
289 [self.otControl launchBottledPeer:testContextID bottleID:localBottleID2 reply:^(NSError * _Nullable error) {
290 [self.spiBlockExpectation fulfill];
291 XCTAssertNil(error, "error should be nil");
294 [self waitForCKModifications];
296 SFECKeyPair* newSigningKey = [[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]];
298 SFECKeyPair* newEncryptionKey = [[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]];
300 [self expectAddedCKModifyRecords:recordDictionary holdFetch:NO];
301 [self expectAddedCKModifyRecords:recordDictionary holdFetch:NO];
303 self.spiBlockExpectation = [self expectationWithDescription:@"updating bottle SPI should fire"];
306 [self.otControl handleIdentityChangeForSigningKey:newSigningKey
307 ForEncryptionKey:newEncryptionKey
308 ForPeerID:self.sosPeerID
309 reply:^(BOOL result, NSError* _Nullable error){
310 [self.spiBlockExpectation fulfill];
311 XCTAssertTrue(result, "result should be YES");
312 XCTAssertNil(error, "error should be nil");
314 [self waitForExpectationsWithTimeout:1.0 handler:nil];
316 [self waitForCKModifications];
317 OCMVerifyAllWithDelay(self.mockDatabase, 8);