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 <XCTest/XCTest.h>
27 #import "keychain/ckks/CKKS.h"
28 #import "keychain/ckks/CKKSKey.h"
29 #import "keychain/ckks/CKKSTLKShare.h"
30 #import "keychain/ckks/CKKSPeer.h"
31 #import "keychain/ckks/tests/CloudKitMockXCTest.h"
33 #import <SecurityFoundation/SFSigningOperation.h>
34 #import <SecurityFoundation/SFKey.h>
35 #import <SecurityFoundation/SFKey_Private.h>
36 #import <SecurityFoundation/SFDigestOperation.h>
38 @interface CloudKitKeychainTLKSharingEncryptionTests : CloudKitMockXCTest
39 @property CKKSKey* tlk;
40 @property CKKSSOSSelfPeer* localPeer;
41 @property CKKSSOSSelfPeer* remotePeer;
42 @property CKKSSOSSelfPeer* remotePeer2;
45 @implementation CloudKitKeychainTLKSharingEncryptionTests
48 // We don't really want to spin up the whole machinery for the encryption tests
52 self.tlk = [CKKSKey randomKeyWrappedBySelf:[[CKRecordZoneID alloc] initWithZoneName:@"testzone" ownerName:CKCurrentUserDefaultName] error:&error];
53 XCTAssertNil(error, "Shouldn't be an error creating a new TLK");
55 self.localPeer = [[CKKSSOSSelfPeer alloc] initWithSOSPeerID:@"local"
56 encryptionKey:[[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]]
57 signingKey:[[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]]];
58 XCTAssertNotNil(self.localPeer, "Should be able to make a new local peer");
60 self.remotePeer = [[CKKSSOSSelfPeer alloc] initWithSOSPeerID:@"remote"
61 encryptionKey:[[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]]
62 signingKey:[[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]]];
63 XCTAssertNotNil(self.remotePeer, "Should be able to make a new remote peer");
65 self.remotePeer2 = [[CKKSSOSSelfPeer alloc] initWithSOSPeerID:@"remote"
66 encryptionKey:[[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]]
67 signingKey:[[SFECKeyPair alloc] initRandomKeyPairWithSpecifier:[[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384]]];
68 XCTAssertNotNil(self.remotePeer2, "Should be able to make a new remote peer");
77 - (void)testKeyWrapAndUnwrap {
79 CKKSTLKShare* share = [CKKSTLKShare share:self.tlk
85 XCTAssertNil(error, "Should have been no error sharing a CKKSKey");
87 XCTAssertEqual(self.tlk.uuid, share.tlkUUID, "TLK shares should know which key they hold");
89 CKKSKey* unwrappedKey = [share unwrapUsing:self.remotePeer error:&error];
90 XCTAssertNil(error, "Should have been no error unwrapping a CKKSKey");
91 XCTAssertEqualObjects(self.tlk, unwrappedKey, "CKKSKeys should be identical after wrapping/unwrapping through a TLK Share record");
94 - (void)testTLKShareSignAndVerify {
96 CKKSTLKShare* share = [CKKSTLKShare share:self.tlk
102 XCTAssertNil(error, "Should have been no error sharing a CKKSKey");
104 NSData* signature = [share signRecord:self.localPeer.signingKey error:&error];
105 XCTAssertNil(error, "Should have been no error signing a CKKSTLKShare");
106 XCTAssertNotNil(signature, "Should have received a signature blob");
108 XCTAssertTrue([share verifySignature:signature verifyingPeer:self.localPeer error:&error], "Signature should verify using the local peer's signing key");
111 - (void)testTLKShareSignAndFailVerify {
112 NSError* error = nil;
113 CKKSTLKShare* share = [CKKSTLKShare share:self.tlk
119 XCTAssertNil(error, "Should have been no error sharing a CKKSKey");
121 NSData* signature = [share signRecord:self.localPeer.signingKey error:&error];
122 XCTAssertNil(error, "Should have been no error signing a CKKSTLKShare");
123 XCTAssertNotNil(signature, "Should have received a signature blob");
125 CKKSTLKShare* shareEpoch = [share copy];
126 XCTAssertTrue([shareEpoch verifySignature:signature verifyingPeer:self.localPeer error:&error], "Signature should verify using the local peer's signing key");
128 shareEpoch.epoch = 1;
129 XCTAssertFalse([shareEpoch verifySignature:signature verifyingPeer:self.localPeer error:&error], "After epoch change, signature should no longer verify");
130 XCTAssertNotNil(error, "Signature verification after epoch change should have produced an error");
133 CKKSTLKShare* sharePoisoned = [share copy];
134 XCTAssertTrue([sharePoisoned verifySignature:signature verifyingPeer:self.localPeer error:&error], "Signature should verify using the local peer's signing key");
136 sharePoisoned.poisoned = 1;
137 XCTAssertFalse([sharePoisoned verifySignature:signature verifyingPeer:self.localPeer error:&error], "After poison change, signature should no longer verify");
138 XCTAssertNotNil(error, "Signature verification after poison change should have produced an error");
141 CKKSTLKShare* shareData = [share copy];
142 XCTAssertTrue([shareData verifySignature:signature verifyingPeer:self.localPeer error:&error], "Signature should verify using the local peer's signing key");
144 shareData.wrappedTLK = [NSMutableData dataWithLength:shareData.wrappedTLK.length];
145 XCTAssertFalse([shareData verifySignature:signature verifyingPeer:self.localPeer error:&error], "After data change, signature should no longer verify");
146 XCTAssertNotNil(error, "Signature verification due to data change should have produced an error");
150 - (void)testKeyShareAndRecover {
151 NSError* error = nil;
152 CKKSTLKShare* share = [CKKSTLKShare share:self.tlk
158 XCTAssertNil(error, "Should have been no error sharing a CKKSKey");
160 NSSet* peers = [NSSet setWithObject:self.localPeer];
161 CKKSKey* key = [share recoverTLK:self.remotePeer trustedPeers:peers error:&error];
162 XCTAssertNil(error, "Should have been no error unwrapping a CKKSKey");
163 XCTAssertEqualObjects(self.tlk, key, "CKKSKeys should be identical after wrapping/unwrapping through a TLK Share record");
166 - (void)testKeyShareAndFailRecovery {
167 NSError* error = nil;
169 CKKSTLKShare* share = [CKKSTLKShare share:self.tlk
175 XCTAssertNil(error, "Should have been no error sharing a CKKSKey");
177 NSSet* peers = [NSSet setWithObject:self.localPeer];
179 key = [share recoverTLK:self.remotePeer trustedPeers:[NSSet set] error:&error];
180 XCTAssertNil(key, "No key should have been extracted when no trusted peers exist");
181 XCTAssertNotNil(error, "Should have produced an error when failing to extract a key with no trusted peers");
184 key = [share recoverTLK:self.remotePeer2 trustedPeers:peers error:&error];
185 XCTAssertNil(key, "No key should have been extracted when using the wrong key");
186 XCTAssertNotNil(error, "Should have produced an error when failing to extract with the wrong key");
189 CKKSTLKShare* shareSignature = [share copy];
190 shareSignature.signature = [NSMutableData dataWithLength:shareSignature.signature.length];
191 key = [shareSignature recoverTLK:self.remotePeer trustedPeers:peers error:&error];
192 XCTAssertNil(key, "No key should have been extracted when signature fails to verify");
193 XCTAssertNotNil(error, "Should have produced an error when failing to extract a key with an invalid signature");
196 CKKSTLKShare* shareUUID = [share copy];
197 shareUUID.tlkUUID = [[NSUUID UUID] UUIDString];
198 key = [shareUUID recoverTLK:self.remotePeer trustedPeers:peers error:&error];
199 XCTAssertNil(key, "No key should have been extracted when uuid has changed");
200 XCTAssertNotNil(error, "Should have produced an error when failing to extract a key after uuid has changed");
204 - (void)testKeyShareSaveAndLoad {
205 NSError* error = nil;
206 CKKSTLKShare* share = [CKKSTLKShare share:self.tlk
212 XCTAssertNil(error, "Should have been no error sharing a CKKSKey");
214 [share saveToDatabase:&error];
215 XCTAssertNil(error, "Shouldn't be an error saving a TLKShare record to the database");
217 CKKSTLKShare* loadedShare = [CKKSTLKShare fromDatabase:self.tlk.uuid
218 receiverPeerID:self.remotePeer.peerID
219 senderPeerID:self.localPeer.peerID
220 zoneID:self.tlk.zoneID
222 XCTAssertNil(error, "Shouldn't get an error loading the share from the db");
223 XCTAssertNotNil(loadedShare, "Should've gotten a TLK share object back from the database");
225 XCTAssertEqualObjects(share, loadedShare, "Re-loaded TLK share object should be equivalent to the original");
227 CKRecord* record = [share CKRecordWithZoneID:self.tlk.zoneID];
228 XCTAssertNotNil(record, "Should be able to turn a share into a CKRecord");
229 XCTAssertTrue([share matchesCKRecord: record], "Should be able to compare a CKRecord with a TLKShare");
231 CKKSTLKShare* fromCKRecord = [[CKKSTLKShare alloc] initWithCKRecord:record];
232 XCTAssertNotNil(fromCKRecord, "Should be able to turn a CKRecord into a TLK share");
234 XCTAssertEqualObjects(share, fromCKRecord, "TLK shares sent through CloudKit should be identical");
237 - (void)testKeyShareSignExtraFieldsInCKRecord {
238 NSError* error = nil;
239 CKKSTLKShare* share = [CKKSTLKShare share:self.tlk
245 XCTAssertNil(error, "Should have been no error sharing a CKKSKey");
247 CKRecord* record = [share CKRecordWithZoneID:self.tlk.zoneID];
248 XCTAssertNotNil(record, "Should be able to turn a share into a CKRecord");
249 XCTAssertTrue([share matchesCKRecord: record], "Should be able to compare a CKRecord with a TLKShare");
251 // Add another few fields to the record
252 record[@"extra_field"] = @"asdf";
253 record[@"another_field"] = [NSNumber numberWithInt:5];
254 record[@"data"] = [@"asdfdata" dataUsingEncoding:NSUTF8StringEncoding];
255 CKKSTLKShare* share2 = [share copy];
256 share2.storedCKRecord = record;
258 XCTAssertNotNil([share dataForSigning], "TLKShares should be able to produce some data to sign");
259 XCTAssertNotNil([share2 dataForSigning], "TLKShares should be able to produce some data to sign (that includes extra fields)");
260 XCTAssertNotEqualObjects([share dataForSigning], [share2 dataForSigning], "TLKShares should prepare to sign extra unknown data");
262 share2.signature = [share2 signRecord:self.localPeer.signingKey error:&error];
263 XCTAssertNil(error, "Shouldn't be an error signing a record with extra fields");
264 XCTAssertNotEqualObjects(share.signature, share2.signature, "Signatures should be different for different data");
266 XCTAssert([share2 verifySignature:share2.signature verifyingPeer:self.localPeer error:&error], "Signature with extra data should verify");
268 // Now, change some of the extra data and see how that works
269 CKRecord* changedDataRecord = [record copy];
270 changedDataRecord[@"extra_field"] = @"no signature here";
271 share2.storedCKRecord = changedDataRecord;
272 XCTAssertFalse([share2 verifySignature:share2.signature verifyingPeer:self.localPeer error:&error], "Signature with extra data shouldn't verify if the data changes");
274 CKRecord* addedDataRecord = [record copy];
275 addedDataRecord[@"anotherfieldaltogether"] = @"asdfasdf";
276 share2.storedCKRecord = addedDataRecord;
277 XCTAssertFalse([share2 verifySignature:share2.signature verifyingPeer:self.localPeer error:&error], "Signature with extra data shouldn't verify if extra data is added");
279 // And verify that saving to disk and reloading is successful
280 share2.storedCKRecord = record;
281 XCTAssert([share2 verifySignature:share2.signature verifyingPeer:self.localPeer error:&error], "Signature with extra data should verify");
282 [share2 saveToDatabase:&error];
283 XCTAssertNil(error, "No error saving share2 to database");
285 CKKSTLKShare* loadedShare2 = [CKKSTLKShare tryFromDatabaseFromCKRecordID:record.recordID error:&error];
286 XCTAssertNil(error, "No error loading loadedShare2 from database");
287 XCTAssertNotNil(loadedShare2, "Should have received a CKKSTLKShare from the database");
289 XCTAssert([loadedShare2 verifySignature:loadedShare2.signature verifyingPeer:self.localPeer error:&error], "Signature with extra data should verify after save/load");