2 * Copyright (c) 2017 Apple Inc. All Rights Reserved.
4 * @APPLEself.LICENSEself.HEADERself.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 * @APPLEself.LICENSEself.HEADERself.END@
26 #import "OTTestsBase.h"
28 @interface BottledPeerRestoreTLKTests : OTTestsBase
29 @property CKKSSOSSelfPeer* remotePeer1;
30 @property CKKSSOSPeer* remotePeer2;
31 @property CKKSSOSSelfPeer* untrustedPeer;
35 @implementation BottledPeerRestoreTLKTests
41 //set up a bottled peer and stick it in localStore
44 self.remotePeer1 = [[CKKSSOSSelfPeer alloc] initWithSOSPeerID:self.sosPeerID
45 encryptionKey:self.peerEncryptionKey
46 signingKey:self.peerSigningKey];
48 [self.currentPeers addObject:self.remotePeer1];
50 OTBottledPeer *bp = [[OTBottledPeer alloc]initWithPeerID:self.egoPeerID spID:self.sosPeerID peerSigningKey:self.peerSigningKey peerEncryptionKey:self.peerEncryptionKey escrowKeys:self.escrowKeys error:&error];
52 XCTAssertNotNil(bp, @"plaintext should not be nil");
53 XCTAssertNil(error, @"error should be nil");
54 XCTAssertNotNil(self.escrowKeys.signingKey, @"signing public key should not be nil");
55 XCTAssertNotNil(self.escrowKeys.encryptionKey, @"encryption public key should not be nil");
57 OTBottledPeerSigned *bpSigned = [[OTBottledPeerSigned alloc]initWithBottledPeer:bp escrowedSigningKey:self.escrowKeys.signingKey peerSigningKey:self.peerSigningKey error:&error];
59 OTBottledPeerRecord* record = [bpSigned asRecord:[self currentIdentity:&error].spID];
60 self.recordName = record.recordName;
62 OTIdentity* identity = [self currentIdentity:&error];
63 [self.localStore insertBottledPeerRecord:record escrowRecordID:identity.spID error:&error];
65 self.remotePeer1 = [[CKKSSOSSelfPeer alloc] initWithSOSPeerID:@"remote-peer1"
66 encryptionKey:[[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]]
67 signingKey:[[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]]];
69 self.remotePeer2 = [[CKKSSOSPeer alloc] initWithSOSPeerID:@"remote-peer2"
70 encryptionPublicKey:[[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]].publicKey
71 signingPublicKey:[[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]].publicKey];
73 // Local SOS trusts these peers
74 [self.currentPeers addObject:self.remotePeer1];
75 [self.currentPeers addObject:self.remotePeer2];
77 self.untrustedPeer = [[CKKSSOSSelfPeer alloc] initWithSOSPeerID:@"untrusted-peer"
78 encryptionKey:[[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]]
79 signingKey:[[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]]];
90 -(void) testTLKSharingWithRestoredBottledPeer
94 OTBottledPeerRecord *rec = [self.localStore readLocalBottledPeerRecordWithRecordID:self.recordName error:&error];
95 XCTAssertNotNil(rec, @"rec should not be nil: %@", error);
96 XCTAssertNil(error, @"error should be nil: %@", error);
98 OTBottledPeerSigned *bps = [[OTBottledPeerSigned alloc] initWithBottledPeerRecord:rec
99 escrowKeys:self.escrowKeys
101 XCTAssertNil(error, @"error should be nil: %@", error);
102 XCTAssertNotNil(bps, @"signed bottled peer should not be nil: %@", error);
103 XCTAssertTrue([bps.bp.peerEncryptionKey isEqual:self.peerEncryptionKey], @"enrolled and restored peer encryption keys should match");
104 XCTAssertTrue([bps.bp.peerSigningKey isEqual:self.peerSigningKey], @"enrolled and restored peer signing keys should match");
107 CKKSSelves* selves = [[CKKSViewManager manager] fetchSelfPeers:&error];
108 XCTAssertNotNil(selves, @"selves should not be nil: %@", error);
110 XCTAssertTrue([selves.allSelves count] == 2, @"should have 2 selves");
111 NSArray *arrayOfSelves = [selves.allSelves allObjects];
112 XCTAssertNotNil(arrayOfSelves, @"arrayOfSelves should not be nil: %@", error);
114 CKKSSOSSelfPeer *ourRestoredPeer = [arrayOfSelves objectAtIndex:0];
115 if([ourRestoredPeer.peerID isEqualToString:@"spid-local-peer"]){
116 ourRestoredPeer = [arrayOfSelves objectAtIndex:1];
119 XCTAssertTrue([ourRestoredPeer.peerID containsString:self.sosPeerID], @"peer ids should match!");
120 XCTAssertTrue([ourRestoredPeer.signingKey isEqual:self.peerSigningKey], @"signing keys should match");
121 XCTAssertTrue([ourRestoredPeer.encryptionKey isEqual:self.peerEncryptionKey], @"encryption keys should match");
123 [self putFakeKeyHierarchyInCloudKit:self.keychainZoneID];
124 [self putFakeDeviceStatusInCloudKit:self.keychainZoneID];
125 [self startCKKSSubsystem];
127 // The CKKS subsystem should not try to write anything to the CloudKit database, but it should enter waitfortlk
128 XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateWaitForTLK] wait:20*NSEC_PER_SEC], "Key state should become waitfortlk");
130 // peer1 arrives to save the day
131 // The CKKS subsystem should accept the keys, and share the TLK back to itself
132 [self expectCKModifyKeyRecords:0 currentKeyPointerRecords:0 tlkShareRecords:1 zoneID:self.keychainZoneID];
134 [self putTLKSharesInCloudKit:self.keychainZoneKeys.tlk from:ourRestoredPeer zoneID:self.keychainZoneID];
135 [self.keychainView notifyZoneChange:nil];
136 [self.keychainView waitForFetchAndIncomingQueueProcessing];
138 XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateReady] wait:20*NSEC_PER_SEC], "Key state should become ready");
140 // We expect a single record to be uploaded for each key class
141 [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID
142 checkItem: [self checkClassCBlock:self.keychainZoneID message:@"Object was encrypted under class C key in hierarchy"]];
143 [self addGenericPassword: @"data" account: @"account-delete-me"];
144 OCMVerifyAllWithDelay(self.mockDatabase, 8);
146 [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID
147 checkItem: [self checkClassABlock:self.keychainZoneID message:@"Object was encrypted under class A key in hierarchy"]];
148 [self addGenericPassword:@"asdf"
149 account:@"account-class-A"
151 access:(id)kSecAttrAccessibleWhenUnlocked
152 expecting:errSecSuccess
153 message:@"Adding class A item"];
154 OCMVerifyAllWithDelay(self.mockDatabase, 8);
157 - (nullable OTIdentity *)currentIdentity:(NSError * _Nullable __autoreleasing * _Nullable)error {
158 return [[OTIdentity alloc]initWithPeerID:@"ego peer id" spID:self.sosPeerID peerSigningKey:self.peerSigningKey peerEncryptionkey:self.peerEncryptionKey error:error];