]> git.saurik.com Git - apple/security.git/blob - keychain/ckks/tests/CKKSSOSTests.m
Security-59306.11.20.tar.gz
[apple/security.git] / keychain / ckks / tests / CKKSSOSTests.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 <CloudKit/CloudKit.h>
27 #import <XCTest/XCTest.h>
28 #import <OCMock/OCMock.h>
29 #import <notify.h>
30
31 #include <Security/SecItemPriv.h>
32
33 #import "keychain/ckks/tests/CloudKitMockXCTest.h"
34 #import "keychain/ckks/tests/CloudKitKeychainSyncingMockXCTest.h"
35 #import "keychain/ckks/tests/CKKSTests+MultiZone.h"
36
37 #import "keychain/ckks/CKKS.h"
38 #import "keychain/ckks/CKKSKeychainView.h"
39 #import "keychain/ckks/CKKSCurrentKeyPointer.h"
40 #import "keychain/ckks/CKKSItemEncrypter.h"
41 #import "keychain/ckks/CKKSKey.h"
42 #import "keychain/ckks/CKKSOutgoingQueueEntry.h"
43 #import "keychain/ckks/CKKSIncomingQueueEntry.h"
44 #import "keychain/ckks/CKKSSynchronizeOperation.h"
45 #import "keychain/ckks/CKKSViewManager.h"
46 #import "keychain/ckks/CKKSZoneStateEntry.h"
47 #import "keychain/ckks/CKKSManifest.h"
48
49 #import "keychain/ckks/tests/MockCloudKit.h"
50
51 #pragma clang diagnostic push
52 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
53 #include <Security/SecureObjectSync/SOSCloudCircle.h>
54 #include "keychain/SecureObjectSync/SOSAccountPriv.h"
55 #include "keychain/SecureObjectSync/SOSAccount.h"
56 #include "keychain/SecureObjectSync/SOSInternal.h"
57 #include "keychain/SecureObjectSync/SOSFullPeerInfo.h"
58 #pragma clang diagnostic pop
59
60 #include <Security/SecKey.h>
61 #include <Security/SecKeyPriv.h>
62 #pragma clang diagnostic pop
63
64 @interface CloudKitKeychainSyncingSOSIntegrationTests : CloudKitKeychainSyncingMultiZoneTestsBase
65 @end
66
67 @implementation CloudKitKeychainSyncingSOSIntegrationTests
68
69 - (void)testFindManateePiggyTLKs {
70 [self saveFakeKeyHierarchyToLocalDatabase:self.manateeZoneID];
71 [self saveTLKMaterialToKeychain:self.manateeZoneID];
72
73 NSDictionary* piggyTLKs = [self SOSPiggyBackCopyFromKeychain];
74
75 [self deleteTLKMaterialFromKeychain:self.manateeZoneID];
76
77 [self SOSPiggyBackAddToKeychain:piggyTLKs];
78
79 NSError* error = nil;
80 [self.manateeZoneKeys.tlk loadKeyMaterialFromKeychain:&error];
81 XCTAssertNil(error, "No error loading tlk from piggy contents");
82 }
83
84 - (void)testFindPiggyTLKs {
85 [self putFakeKeyHierachiesInCloudKit];
86 [self putFakeDeviceStatusesInCloudKit];
87 [self saveTLKsToKeychain];
88
89 NSDictionary* piggyTLKs = [self SOSPiggyBackCopyFromKeychain];
90
91 [self deleteTLKMaterialsFromKeychain];
92
93 [self SOSPiggyBackAddToKeychain:piggyTLKs];
94
95 NSError* error = nil;
96 [self.manateeZoneKeys.tlk loadKeyMaterialFromKeychain:&error];
97 XCTAssertNil(error, "No error loading manatee tlk from piggy contents");
98
99 [self.engramZoneKeys.tlk loadKeyMaterialFromKeychain:&error];
100 XCTAssertNil(error, "No error loading engram tlk from piggy contents");
101
102 [self.autoUnlockZoneKeys.tlk loadKeyMaterialFromKeychain:&error];
103 XCTAssertNil(error, "No error loading AutoUnlock tlk from piggy contents");
104
105 [self.healthZoneKeys.tlk loadKeyMaterialFromKeychain:&error];
106 XCTAssertNil(error, "No error loading Health tlk from piggy contents");
107
108 [self.applepayZoneKeys.tlk loadKeyMaterialFromKeychain:&error];
109 XCTAssertNil(error, "No error loading ApplePay tlk from piggy contents");
110
111 [self.homeZoneKeys.tlk loadKeyMaterialFromKeychain:&error];
112 XCTAssertNil(error, "No error loading Home tlk from piggy contents");
113 }
114
115 -(NSString*)fileForStorage
116 {
117 static dispatch_once_t onceToken;
118 static NSString *tempPath = NULL;
119 dispatch_once(&onceToken, ^{
120 tempPath = [[[[NSFileManager defaultManager] temporaryDirectory] URLByAppendingPathComponent:@"PiggyPacket"] path];
121
122 });
123 return tempPath;
124 }
125
126 -(void)testPiggybackingData{
127 [self putFakeKeyHierachiesInCloudKit];
128 [self saveTLKsToKeychain];
129
130 for(CKRecordZoneID* zoneID in self.ckksZones) {
131 [self expectCKKSTLKSelfShareUpload:zoneID];
132 }
133 [self startCKKSSubsystem];
134
135 [self waitForKeyHierarchyReadinesses];
136
137 OCMVerifyAllWithDelay(self.mockDatabase, 20);
138
139 /*
140 * Pull data from keychain and view manager
141 */
142
143 NSDictionary* piggydata = [self SOSPiggyBackCopyFromKeychain];
144 NSArray<NSData *>* icloudidentities = piggydata[@"idents"];
145 NSArray<NSDictionary *>* tlks = piggydata[@"tlk"];
146
147 XCTAssertEqual([tlks count], [[self.injectedManager viewList] count], "TLKs not same as views");
148
149 XCTAssertNotNil(tlks, "tlks not set");
150 XCTAssertNotEqual([tlks count], (NSUInteger)0, "0 tlks");
151 XCTAssertNotNil(icloudidentities, "idents not set");
152 XCTAssertNotEqual([icloudidentities count], (NSUInteger)0, "0 icloudidentities");
153
154 NSData *initial = SOSPiggyCreateInitialSyncData(icloudidentities, tlks);
155
156 XCTAssertNotNil(initial, "Initial not set");
157 BOOL writeStatus = [initial writeToFile:[self fileForStorage] options:NSDataWritingAtomic error: nil];
158 XCTAssertTrue(writeStatus, "had trouble writing to disk");
159 XCTAssertNotEqual((int)[initial length], 0, "initial sync data is greater than 0");
160
161 /*
162 * Check that they make it accross
163 */
164
165 const uint8_t* der = [initial bytes];
166 const uint8_t *der_end = der + [initial length];
167
168 NSDictionary *result = SOSPiggyCopyInitialSyncData(&der, der_end);
169 XCTAssertNotNil(result, "Initial not set");
170 NSArray *copiedTLKs = result[@"tlks"];
171 XCTAssertNotNil(copiedTLKs, "tlks not set");
172 XCTAssertEqual([copiedTLKs count], 5u, "piggybacking should have gotten 5 TLKs across (but we have more than that elsewhere)");
173
174 NSArray *copiediCloudidentities = result[@"idents"];
175 XCTAssertNotNil(copiediCloudidentities, "idents not set");
176 XCTAssertEqual([copiediCloudidentities count], [icloudidentities count], "ident count not same");
177 }
178
179 -(void)testVerifyTLKSorting {
180 char key[32*2] = {0};
181 NSArray<NSDictionary *> *tlks = @[
182 @{
183 @"acct" : @"11111111",
184 @"srvr" : @"Manatee",
185 @"v_Data" : [NSData dataWithBytes:key length:sizeof(key)],
186 @"auth" : @YES,
187 },
188 @{
189 @"acct" : @"55555555",
190 @"srvr" : @"Health",
191 @"v_Data" : [NSData dataWithBytes:key length:sizeof(key)],
192 },
193 @{
194 @"acct" : @"22222222",
195 @"srvr" : @"Engram",
196 @"v_Data" : [NSData dataWithBytes:key length:sizeof(key)],
197 @"auth" : @YES,
198 },
199 @{
200 @"acct" : @"44444444",
201 @"srvr" : @"Manatee",
202 @"v_Data" : [NSData dataWithBytes:key length:sizeof(key)],
203 },
204 @{
205 @"acct" : @"33333333",
206 @"srvr" : @"Health",
207 @"v_Data" : [NSData dataWithBytes:key length:sizeof(key)],
208 @"auth" : @YES,
209 },
210 @{
211 @"acct" : @"66666666",
212 @"srvr" : @"Home",
213 @"v_Data" : [NSData dataWithBytes:key length:sizeof(key)],
214 @"auth" : @YES,
215 },
216 ];
217
218 NSArray<NSDictionary *>* sortedTLKs = SOSAccountSortTLKS(tlks);
219 XCTAssertNotNil(sortedTLKs, "sortedTLKs not set");
220
221 // Home gets sorted into the middle, as the other Health and Manatee TLKs aren't 'authoritative'
222 NSArray<NSString *> *expectedOrder = @[ @"11111111", @"22222222", @"33333333", @"66666666", @"44444444", @"55555555"];
223 [sortedTLKs enumerateObjectsUsingBlock:^(NSDictionary *tlk, NSUInteger idx, BOOL * _Nonnull stop) {
224 NSString *uuid = tlk[@"acct"];
225 XCTAssertEqualObjects(uuid, expectedOrder[idx], "wrong order");
226 }];
227 }
228
229
230 - (void)testAcceptExistingPiggyKeyHierarchy {
231 // Test starts with no keys in CKKS database, but one in our fake CloudKit.
232 // Test also begins with the TLK having arrived in the local keychain (via SOS)
233 [self putFakeKeyHierachiesInCloudKit];
234 [self saveTLKsToKeychain];
235 NSDictionary* piggyTLKS = [self SOSPiggyBackCopyFromKeychain];
236 [self SOSPiggyBackAddToKeychain:piggyTLKS];
237 [self deleteTLKMaterialsFromKeychain];
238
239 // The CKKS subsystem should write a TLK Share for each view
240 for(CKRecordZoneID* zoneID in self.ckksZones) {
241 [self expectCKKSTLKSelfShareUpload:zoneID];
242 }
243
244 // Spin up CKKS subsystem.
245 [self startCKKSSubsystem];
246
247 [self.manateeView waitForKeyHierarchyReadiness];
248
249 OCMVerifyAllWithDelay(self.mockDatabase, 20);
250
251 // Verify that there are three local keys, and three local current key records
252 __weak __typeof(self) weakSelf = self;
253 [self.manateeView dispatchSync: ^bool{
254 __strong __typeof(weakSelf) strongSelf = weakSelf;
255 XCTAssertNotNil(strongSelf, "self exists");
256
257 NSError* error = nil;
258
259 NSArray<CKKSKey*>* keys = [CKKSKey localKeys:strongSelf.manateeZoneID error:&error];
260 XCTAssertNil(error, "no error fetching keys");
261 XCTAssertEqual(keys.count, 3u, "Three keys in local database");
262
263 NSArray<CKKSCurrentKeyPointer*>* currentkeys = [CKKSCurrentKeyPointer all:strongSelf.manateeZoneID error:&error];
264 XCTAssertNil(error, "no error fetching current keys");
265 XCTAssertEqual(currentkeys.count, 3u, "Three current key pointers in local database");
266
267 // Ensure that the manatee syncable TLK is created from a piggy
268 NSDictionary* query = @{
269 (id)kSecClass : (id)kSecClassInternetPassword,
270 (id)kSecUseDataProtectionKeychain : @YES,
271 (id)kSecAttrAccessGroup : @"com.apple.security.ckks",
272 (id)kSecAttrDescription: SecCKKSKeyClassTLK,
273 (id)kSecAttrAccount: strongSelf.manateeZoneKeys.tlk.uuid,
274 (id)kSecAttrServer: strongSelf.manateeZoneID.zoneName,
275 (id)kSecAttrSynchronizable: @YES,
276 (id)kSecReturnAttributes: @YES,
277 (id)kSecReturnData: @YES,
278 };
279 CFTypeRef result = nil;
280 XCTAssertEqual(errSecSuccess, SecItemCopyMatching((__bridge CFDictionaryRef)query, &result), "Found a syncable TLK");
281 XCTAssertNotNil((__bridge id) result, "Received a result from SecItemCopyMatching");
282 CFReleaseNull(result);
283
284 return false;
285 }];
286 }
287
288 -(void)testAcceptExistingAndUsePiggyKeyHierarchy {
289 // Test starts with nothing in database, but one in our fake CloudKit.
290 [self putFakeKeyHierachiesInCloudKit];
291 [self putFakeDeviceStatusesInCloudKit];
292 [self saveTLKsToKeychain];
293 NSDictionary* piggyData = [self SOSPiggyBackCopyFromKeychain];
294 [self deleteTLKMaterialsFromKeychain];
295
296 // Spin up CKKS subsystem.
297 [self startCKKSSubsystem];
298
299 // The CKKS subsystem should not try to write anything to the CloudKit database.
300 XCTAssertEqual(0, [self.manateeView.keyHierarchyConditions[SecCKKSZoneKeyStateWaitForTLK] wait:20*NSEC_PER_SEC], "CKKS entered waitfortlk");
301
302 OCMVerifyAllWithDelay(self.mockDatabase, 20);
303
304 // Now, save the TLKs to the keychain (to simulate them coming in later via piggybacking).
305 for(CKRecordZoneID* zoneID in self.ckksZones) {
306 [self expectCKKSTLKSelfShareUpload:zoneID];
307 }
308
309 [self SOSPiggyBackAddToKeychain:piggyData];
310 [self waitForKeyHierarchyReadinesses];
311
312 // We expect a single record to be uploaded for each key class
313 [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.manateeZoneID checkItem: [self checkClassCBlock:self.manateeZoneID message:@"Object was encrypted under class C key in hierarchy"]];
314 [self addGenericPassword: @"data" account: @"account-delete-me-manatee" viewHint:(id)kSecAttrViewHintManatee];
315
316 OCMVerifyAllWithDelay(self.mockDatabase, 20);
317
318 [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.manateeZoneID checkItem: [self checkClassABlock:self.manateeZoneID message:@"Object was encrypted under class A key in hierarchy"]];
319
320 [self addGenericPassword:@"asdf"
321 account:@"account-class-A"
322 viewHint:(id)kSecAttrViewHintManatee
323 access:(id)kSecAttrAccessibleWhenUnlocked
324 expecting:errSecSuccess
325 message:@"Adding class A item"];
326 OCMVerifyAllWithDelay(self.mockDatabase, 20);
327 }
328 @end
329
330 #endif // OCTAGON
331