]> git.saurik.com Git - apple/security.git/blob - keychain/ckks/tests/CKKSTLKSharingEncryptionTests.m
Security-59754.80.3.tar.gz
[apple/security.git] / keychain / ckks / tests / CKKSTLKSharingEncryptionTests.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 <XCTest/XCTest.h>
27 #import "keychain/ckks/CKKS.h"
28 #import "keychain/ckks/CKKSKey.h"
29 #import "keychain/ckks/CKKSTLKShareRecord.h"
30 #import "keychain/ckks/CKKSSQLDatabaseObject.h"
31 #import "keychain/ckks/CKKSPeer.h"
32 #import "keychain/ckks/tests/CloudKitMockXCTest.h"
33
34 #import <SecurityFoundation/SFSigningOperation.h>
35 #import <SecurityFoundation/SFKey.h>
36 #import <SecurityFoundation/SFKey_Private.h>
37 #import <SecurityFoundation/SFDigestOperation.h>
38
39 @interface CloudKitKeychainTLKSharingEncryptionTests : CloudKitMockXCTest
40 @property CKKSKey* tlk;
41 @property CKKSSOSSelfPeer* localPeer;
42 @property CKKSSOSSelfPeer* remotePeer;
43 @property CKKSSOSSelfPeer* remotePeer2;
44 @end
45
46 @implementation CloudKitKeychainTLKSharingEncryptionTests
47
48 - (void)setUp {
49 // We don't really want to spin up the whole machinery for the encryption tests
50 SecCKKSDisable();
51
52 NSError* error = nil;
53 self.tlk = [CKKSKey randomKeyWrappedBySelf:[[CKRecordZoneID alloc] initWithZoneName:@"testzone" ownerName:CKCurrentUserDefaultName] error:&error];
54 XCTAssertNil(error, "Shouldn't be an error creating a new TLK");
55
56 self.localPeer = [[CKKSSOSSelfPeer alloc] initWithSOSPeerID:@"local"
57 encryptionKey:[[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]]
58 signingKey:[[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]]
59 viewList:self.managedViewList];
60 XCTAssertNotNil(self.localPeer, "Should be able to make a new local peer");
61
62 self.remotePeer = [[CKKSSOSSelfPeer alloc] initWithSOSPeerID:@"remote1"
63 encryptionKey:[[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]]
64 signingKey:[[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]]
65 viewList:self.managedViewList];
66 XCTAssertNotNil(self.remotePeer, "Should be able to make a new remote peer");
67
68 self.remotePeer2 = [[CKKSSOSSelfPeer alloc] initWithSOSPeerID:@"remote2"
69 encryptionKey:[[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]]
70 signingKey:[[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]]
71 viewList:self.managedViewList];
72 XCTAssertNotNil(self.remotePeer2, "Should be able to make a new remote peer");
73
74 [super setUp];
75 }
76
77 - (void)tearDown {
78 [super tearDown];
79 }
80
81 - (void)testKeyWrapAndUnwrap {
82 NSError* error = nil;
83 CKKSTLKShareRecord* share = [CKKSTLKShareRecord share:self.tlk
84 as:self.localPeer
85 to:self.remotePeer
86 epoch:-1
87 poisoned:0
88 error:&error];
89 XCTAssertNil(error, "Should have been no error sharing a CKKSKey");
90
91 XCTAssertEqual(self.tlk.uuid, share.tlkUUID, "TLK shares should know which key they hold");
92
93 CKKSKey* unwrappedKey = [share unwrapUsing:self.remotePeer error:&error];
94 XCTAssertNil(error, "Should have been no error unwrapping a CKKSKey");
95 XCTAssertEqualObjects(self.tlk, unwrappedKey, "CKKSKeys should be identical after wrapping/unwrapping through a TLK Share record");
96 }
97
98 - (void)testTLKShareSignAndVerify {
99 NSError* error = nil;
100 CKKSTLKShareRecord* share = [CKKSTLKShareRecord share:self.tlk
101 as:self.localPeer
102 to:self.remotePeer
103 epoch:-1
104 poisoned:0
105 error:&error];
106 XCTAssertNil(error, "Should have been no error sharing a CKKSKey");
107
108 NSData* signature = [share signRecord:self.localPeer.signingKey error:&error];
109 XCTAssertNil(error, "Should have been no error signing a CKKSTLKShare");
110 XCTAssertNotNil(signature, "Should have received a signature blob");
111
112 XCTAssertTrue([share verifySignature:signature verifyingPeer:self.localPeer error:&error], "Signature should verify using the local peer's signing key");
113 }
114
115 - (void)testTLKShareSignAndFailVerify {
116 NSError* error = nil;
117 CKKSTLKShareRecord* share = [CKKSTLKShareRecord share:self.tlk
118 as:self.localPeer
119 to:self.remotePeer
120 epoch:-1
121 poisoned:0
122 error:&error];
123 XCTAssertNil(error, "Should have been no error sharing a CKKSKey");
124
125 NSData* signature = [share signRecord:self.localPeer.signingKey error:&error];
126 XCTAssertNil(error, "Should have been no error signing a CKKSTLKShare");
127 XCTAssertNotNil(signature, "Should have received a signature blob");
128
129 CKKSTLKShareRecord* shareEpoch = [share copy];
130 XCTAssertTrue([shareEpoch verifySignature:signature verifyingPeer:self.localPeer error:&error], "Signature should verify using the local peer's signing key");
131 error = nil;
132 shareEpoch.share.epoch = 1;
133 XCTAssertFalse([shareEpoch verifySignature:signature verifyingPeer:self.localPeer error:&error], "After epoch change, signature should no longer verify");
134 XCTAssertNotNil(error, "Signature verification after epoch change should have produced an error");
135 error = nil;
136
137 CKKSTLKShareRecord* sharePoisoned = [share copy];
138 XCTAssertTrue([sharePoisoned verifySignature:signature verifyingPeer:self.localPeer error:&error], "Signature should verify using the local peer's signing key");
139 error = nil;
140 sharePoisoned.share.poisoned = 1;
141 XCTAssertFalse([sharePoisoned verifySignature:signature verifyingPeer:self.localPeer error:&error], "After poison change, signature should no longer verify");
142 XCTAssertNotNil(error, "Signature verification after poison change should have produced an error");
143 error = nil;
144
145 CKKSTLKShareRecord* shareData = [share copy];
146 XCTAssertTrue([shareData verifySignature:signature verifyingPeer:self.localPeer error:&error], "Signature should verify using the local peer's signing key");
147 error = nil;
148 shareData.share.wrappedTLK = [NSMutableData dataWithLength:shareData.wrappedTLK.length];
149 XCTAssertFalse([shareData verifySignature:signature verifyingPeer:self.localPeer error:&error], "After data change, signature should no longer verify");
150 XCTAssertNotNil(error, "Signature verification due to data change should have produced an error");
151 error = nil;
152 }
153
154 - (void)testKeyShareAndRecover {
155 NSError* error = nil;
156 CKKSTLKShareRecord* share = [CKKSTLKShareRecord share:self.tlk
157 as:self.localPeer
158 to:self.remotePeer
159 epoch:-1
160 poisoned:0
161 error:&error];
162 XCTAssertNil(error, "Should have been no error sharing a CKKSKey");
163
164 NSSet* peers = [NSSet setWithObject:self.localPeer];
165 CKKSKey* key = [share recoverTLK:self.remotePeer trustedPeers:peers error:&error];
166 XCTAssertNil(error, "Should have been no error unwrapping a CKKSKey");
167 XCTAssertEqualObjects(self.tlk, key, "CKKSKeys should be identical after wrapping/unwrapping through a TLK Share record");
168 }
169
170 - (void)testKeyShareAndFailRecovery {
171 NSError* error = nil;
172 CKKSKey* key = nil;
173 CKKSTLKShareRecord* share = [CKKSTLKShareRecord share:self.tlk
174 as:self.localPeer
175 to:self.remotePeer
176 epoch:-1
177 poisoned:0
178 error:&error];
179 XCTAssertNil(error, "Should have been no error sharing a CKKSKey");
180
181 NSSet* peers = [NSSet setWithObject:self.localPeer];
182
183 key = [share recoverTLK:self.remotePeer trustedPeers:[NSSet set] error:&error];
184 XCTAssertNil(key, "No key should have been extracted when no trusted peers exist");
185 XCTAssertNotNil(error, "Should have produced an error when failing to extract a key with no trusted peers");
186 error = nil;
187
188 key = [share recoverTLK:self.remotePeer2 trustedPeers:peers error:&error];
189 XCTAssertNil(key, "No key should have been extracted when using the wrong key");
190 XCTAssertNotNil(error, "Should have produced an error when failing to extract with the wrong key");
191 error = nil;
192
193 CKKSTLKShareRecord* shareSignature = [share copy];
194 shareSignature.share.signature = [NSMutableData dataWithLength:shareSignature.signature.length];
195 key = [shareSignature recoverTLK:self.remotePeer trustedPeers:peers error:&error];
196 XCTAssertNil(key, "No key should have been extracted when signature fails to verify");
197 XCTAssertNotNil(error, "Should have produced an error when failing to extract a key with an invalid signature");
198 error = nil;
199
200 CKKSTLKShareRecord* shareUUID = [share copy];
201 shareUUID.share.tlkUUID = [[NSUUID UUID] UUIDString];
202 key = [shareUUID recoverTLK:self.remotePeer trustedPeers:peers error:&error];
203 XCTAssertNil(key, "No key should have been extracted when uuid has changed");
204 XCTAssertNotNil(error, "Should have produced an error when failing to extract a key after uuid has changed");
205 error = nil;
206 }
207
208 - (void)testKeyShareSaveAndLoad {
209 NSError* error = nil;
210 CKKSTLKShareRecord* share = [CKKSTLKShareRecord share:self.tlk
211 as:self.localPeer
212 to:self.remotePeer
213 epoch:-1
214 poisoned:0
215 error:&error];
216 XCTAssertNil(error, "Should have been no error sharing a CKKSKey");
217
218 [CKKSSQLDatabaseObject performCKKSTransaction:^CKKSDatabaseTransactionResult {
219 NSError* saveError = nil;
220 [share saveToDatabase:&saveError];
221 XCTAssertNil(saveError, "Shouldn't be an error saving a TLKShare record to the database");
222 return CKKSDatabaseTransactionCommit;
223 }];
224
225 CKKSTLKShareRecord* loadedShare = [CKKSTLKShareRecord fromDatabase:self.tlk.uuid
226 receiverPeerID:self.remotePeer.peerID
227 senderPeerID:self.localPeer.peerID
228 zoneID:self.tlk.zoneID
229 error:&error];
230 XCTAssertNil(error, "Shouldn't get an error loading the share from the db");
231 XCTAssertNotNil(loadedShare, "Should've gotten a TLK share object back from the database");
232
233 XCTAssertEqualObjects(share, loadedShare, "Re-loaded TLK share object should be equivalent to the original");
234
235 CKRecord* record = [share CKRecordWithZoneID:self.tlk.zoneID];
236 XCTAssertNotNil(record, "Should be able to turn a share into a CKRecord");
237 XCTAssertTrue([share matchesCKRecord: record], "Should be able to compare a CKRecord with a TLKShare");
238
239 CKKSTLKShareRecord* fromCKRecord = [[CKKSTLKShareRecord alloc] initWithCKRecord:record];
240 XCTAssertNotNil(fromCKRecord, "Should be able to turn a CKRecord into a TLK share");
241
242 XCTAssertEqualObjects(share, fromCKRecord, "TLK shares sent through CloudKit should be identical");
243 }
244
245 - (void)testKeyExtractFromTrustState {
246 NSError* error = nil;
247 CKKSTLKShareRecord* share = [CKKSTLKShareRecord share:self.tlk
248 as:self.remotePeer
249 to:self.localPeer
250 epoch:-1
251 poisoned:0
252 error:&error];
253 XCTAssertNotNil(share, "Should have a TLKShare");
254 XCTAssertNil(error, "Should have been no error sharing a CKKSKey from a remote peer to a local peer");
255
256 CKKSPeerProviderState* trustState = [[CKKSPeerProviderState alloc] initWithPeerProviderID:@"test-provider"
257 essential:YES
258 selfPeers:[[CKKSSelves alloc] initWithCurrent:self.localPeer allSelves:nil]
259 selfPeersError:nil
260 trustedPeers:[NSSet setWithArray:@[
261 self.localPeer,
262 self.remotePeer,
263 self.remotePeer2,
264 ]]
265 trustedPeersError:nil];
266 CKKSKey* shareExtraction = [share recoverTLK:self.localPeer
267 trustedPeers:trustState.currentTrustedPeers
268 error:&error];
269 XCTAssertNotNil(shareExtraction, "Should be able to recover the share from the currently trusted peers");
270 XCTAssertNil(error, "Should be no error extracting TLK");
271
272 BOOL extracted = [trustState unwrapKey:self.tlk
273 fromShares:@[share]
274 error:&error];
275
276 XCTAssertTrue(extracted, "Should be able to extract the TLK via a share");
277 XCTAssertNil(error, "Should be no error extracting TLK");
278
279 CKKSPeerProviderState* remotePeer2TrustState = [[CKKSPeerProviderState alloc] initWithPeerProviderID:@"test-provider"
280 essential:YES
281 selfPeers:[[CKKSSelves alloc] initWithCurrent:self.remotePeer2 allSelves:nil]
282 selfPeersError:nil
283 trustedPeers:[NSSet setWithArray:@[
284 self.localPeer,
285 self.remotePeer,
286 self.remotePeer2,
287 ]]
288 trustedPeersError:nil];
289
290 BOOL remotePeerExtracted = [remotePeer2TrustState unwrapKey:self.tlk
291 fromShares:@[share]
292 error:&error];
293 XCTAssertFalse(remotePeerExtracted, "Should not be able to extract the TLK if there's no share to the self peer");
294 XCTAssertNotNil(error, "Should be an error when failing to extract TLK");
295 }
296
297 - (void)testKeyShareSignExtraFieldsInCKRecord {
298 NSError* error = nil;
299 CKKSTLKShareRecord* share = [CKKSTLKShareRecord share:self.tlk
300 as:self.localPeer
301 to:self.remotePeer
302 epoch:-1
303 poisoned:0
304 error:&error];
305 XCTAssertNil(error, "Should have been no error sharing a CKKSKey");
306
307 CKRecord* record = [share CKRecordWithZoneID:self.tlk.zoneID];
308 XCTAssertNotNil(record, "Should be able to turn a share into a CKRecord");
309 XCTAssertTrue([share matchesCKRecord: record], "Should be able to compare a CKRecord with a TLKShare");
310
311 // Add another few fields to the record
312 record[@"extra_field"] = @"asdf";
313 record[@"another_field"] = [NSNumber numberWithInt:5];
314 record[@"data"] = [@"asdfdata" dataUsingEncoding:NSUTF8StringEncoding];
315 CKKSTLKShareRecord* share2 = [share copy];
316 share2.storedCKRecord = record;
317
318 XCTAssertNotNil([share dataForSigning], "TLKShares should be able to produce some data to sign");
319 XCTAssertNotNil([share2 dataForSigning], "TLKShares should be able to produce some data to sign (that includes extra fields)");
320 XCTAssertNotEqualObjects([share dataForSigning], [share2 dataForSigning], "TLKShares should prepare to sign extra unknown data");
321
322 share2.share.signature = [share2 signRecord:self.localPeer.signingKey error:&error];
323 XCTAssertNil(error, "Shouldn't be an error signing a record with extra fields");
324 XCTAssertNotEqualObjects(share.signature, share2.signature, "Signatures should be different for different data");
325
326 XCTAssert([share2 verifySignature:share2.signature verifyingPeer:self.localPeer error:&error], "Signature with extra data should verify");
327
328 // Now, change some of the extra data and see how that works
329 CKRecord* changedDataRecord = [record copy];
330 changedDataRecord[@"extra_field"] = @"no signature here";
331 share2.storedCKRecord = changedDataRecord;
332 XCTAssertFalse([share2 verifySignature:share2.signature verifyingPeer:self.localPeer error:&error], "Signature with extra data shouldn't verify if the data changes");
333
334 CKRecord* addedDataRecord = [record copy];
335 addedDataRecord[@"anotherfieldaltogether"] = @"asdfasdf";
336 share2.storedCKRecord = addedDataRecord;
337 XCTAssertFalse([share2 verifySignature:share2.signature verifyingPeer:self.localPeer error:&error], "Signature with extra data shouldn't verify if extra data is added");
338
339 // And verify that saving to disk and reloading is successful
340 share2.storedCKRecord = record;
341 XCTAssert([share2 verifySignature:share2.signature verifyingPeer:self.localPeer error:&error], "Signature with extra data should verify");
342
343 [CKKSSQLDatabaseObject performCKKSTransaction:^CKKSDatabaseTransactionResult {
344 NSError* saveError = nil;
345 [share2 saveToDatabase:&saveError];
346 XCTAssertNil(saveError, "No error saving share2 to database");
347 return CKKSDatabaseTransactionCommit;
348 }];
349
350 CKKSTLKShareRecord* loadedShare2 = [CKKSTLKShareRecord tryFromDatabaseFromCKRecordID:record.recordID error:&error];
351 XCTAssertNil(error, "No error loading loadedShare2 from database");
352 XCTAssertNotNil(loadedShare2, "Should have received a CKKSTLKShare from the database");
353
354 XCTAssert([loadedShare2 verifySignature:loadedShare2.signature verifyingPeer:self.localPeer error:&error], "Signature with extra data should verify after save/load");
355 }
356
357 @end
358
359 #endif // OCTAGON