]> git.saurik.com Git - apple/security.git/blob - tests/secdmockaks/secdmockaks.m
Security-58286.60.28.tar.gz
[apple/security.git] / tests / secdmockaks / secdmockaks.m
1 /*
2 * Copyright (c) 2018 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 #import "SecDbKeychainItem.h"
25 #import "SecdTestKeychainUtilities.h"
26 #import "CKKS.h"
27 #import "SecItemPriv.h"
28 #import "SecItemServer.h"
29 #import "spi.h"
30 #import <utilities/SecCFWrappers.h>
31 #import <utilities/SecFileLocations.h>
32 #import <SecurityFoundation/SFEncryptionOperation.h>
33 #import <XCTest/XCTest.h>
34 #import <OCMock/OCMock.h>
35 #import <libaks.h>
36 #import <sqlite3.h>
37 #import "mockaks.h"
38
39 #import "secdmock_db_version_10_5.h"
40 #import "secdmock_db_version_11_1.h"
41
42 @interface secdmockaks : XCTestCase
43 @property NSString *testHomeDirectory;
44 @property long lockCounter;
45 @end
46
47 @implementation secdmockaks
48
49 + (void)setUp
50 {
51 [super setUp];
52
53 SecCKKSDisable();
54 /*
55 * Disable all of SOS syncing since that triggers retains of database
56 * and these tests muck around with the database over and over again, so
57 * that leads to the vnode delete kevent trap triggering for sqlite
58 * over and over again.
59 */
60 SecCKKSTestSetDisableSOS(true);
61 //securityd_init(NULL);
62 }
63
64 - (void)createKeychainDirectory
65 {
66 [[NSFileManager defaultManager] createDirectoryAtPath:[NSString stringWithFormat: @"%@/Library/Keychains", self.testHomeDirectory] withIntermediateDirectories:YES attributes:nil error:NULL];
67 }
68
69 - (void)removeHomeDirectory
70 {
71 if (self.testHomeDirectory) {
72 [[NSFileManager defaultManager] removeItemAtPath:self.testHomeDirectory error:NULL];
73 }
74 }
75
76 - (void)setUp {
77 [super setUp];
78
79 NSString* testName = [self.name componentsSeparatedByString:@" "][1];
80 testName = [testName stringByReplacingOccurrencesOfString:@"]" withString:@""];
81 secnotice("ckkstest", "Beginning test %@", testName);
82
83 // Make a new fake keychain
84 self.testHomeDirectory = [NSString stringWithFormat: @"/tmp/%@.%X", testName, arc4random()];
85 [self createKeychainDirectory];
86
87 SetCustomHomeURLString((__bridge CFStringRef) self.testHomeDirectory);
88 static dispatch_once_t onceToken;
89 dispatch_once(&onceToken, ^{
90 securityd_init(NULL);
91 });
92 SecKeychainDbReset(NULL);
93
94 // Actually load the database.
95 kc_with_dbt(true, NULL, ^bool (SecDbConnectionRef dbt) { return false; });
96 }
97
98 - (void)tearDown
99 {
100 SetCustomHomeURLString(NULL);
101 SecKeychainDbReset(^{
102 [self removeHomeDirectory];
103 self.testHomeDirectory = nil;
104 });
105 //kc_with_dbt(true, NULL, ^bool (SecDbConnectionRef dbt) { return false; });
106 }
107
108 - (void)testAddKeyByReference
109 {
110 NSDictionary* keyParams = @{ (id)kSecAttrKeyType : (id)kSecAttrKeyTypeRSA, (id)kSecAttrKeySizeInBits : @(1024) };
111 SecKeyRef key = SecKeyCreateRandomKey((__bridge CFDictionaryRef)keyParams, NULL);
112 NSDictionary* item = @{ (id)kSecClass : (id)kSecClassKey,
113 (id)kSecValueRef : (__bridge id)key,
114 (id)kSecAttrLabel : @"TestLabel",
115 (id)kSecAttrNoLegacy : @(YES) };
116
117 OSStatus result = SecItemAdd((__bridge CFDictionaryRef)item, NULL);
118 XCTAssertEqual(result, 0, @"failed to add test item to keychain");
119
120 NSMutableDictionary* refQuery = item.mutableCopy;
121 [refQuery removeObjectForKey:(id)kSecValueData];
122 refQuery[(id)kSecReturnRef] = @(YES);
123 CFTypeRef foundItem = NULL;
124 result = SecItemCopyMatching((__bridge CFDictionaryRef)refQuery, &foundItem);
125 XCTAssertEqual(result, 0, @"failed to find the reference for the item we just added in the keychain");
126
127 NSData* originalKeyData = (__bridge_transfer NSData*)SecKeyCopyExternalRepresentation(key, NULL);
128 NSData* foundKeyData = (__bridge_transfer NSData*)SecKeyCopyExternalRepresentation((SecKeyRef)foundItem, NULL);
129 XCTAssertEqualObjects(originalKeyData, foundKeyData, @"found key does not match the key we put in the keychain");
130
131 result = SecItemDelete((__bridge CFDictionaryRef)refQuery);
132 XCTAssertEqual(result, 0, @"failed to delete key");
133 }
134
135
136 - (void)testAddDeleteItem
137 {
138 NSDictionary* item = @{ (id)kSecClass : (id)kSecClassGenericPassword,
139 (id)kSecValueData : [@"password" dataUsingEncoding:NSUTF8StringEncoding],
140 (id)kSecAttrAccount : @"TestAccount",
141 (id)kSecAttrService : @"TestService",
142 (id)kSecAttrNoLegacy : @(YES) };
143
144 OSStatus result = SecItemAdd((__bridge CFDictionaryRef)item, NULL);
145 XCTAssertEqual(result, 0, @"failed to add test item to keychain");
146
147 NSMutableDictionary* dataQuery = item.mutableCopy;
148 [dataQuery removeObjectForKey:(id)kSecValueData];
149 dataQuery[(id)kSecReturnData] = @(YES);
150 CFTypeRef foundItem = NULL;
151 result = SecItemCopyMatching((__bridge CFDictionaryRef)dataQuery, &foundItem);
152 XCTAssertEqual(result, 0, @"failed to find the data for the item we just added in the keychain");
153
154 result = SecItemDelete((__bridge CFDictionaryRef)dataQuery);
155 XCTAssertEqual(result, 0, @"failed to delete item");
156 }
157
158
159 - (void)createManyItems
160 {
161 unsigned n;
162 for (n = 0; n < 50; n++) {
163 NSDictionary* item = @{
164 (id)kSecClass : (id)kSecClassGenericPassword,
165 (id)kSecValueData : [@"password" dataUsingEncoding:NSUTF8StringEncoding],
166 (id)kSecAttrAccount : [NSString stringWithFormat:@"TestAccount-%u", n],
167 (id)kSecAttrService : @"TestService",
168 (id)kSecAttrNoLegacy : @(YES)
169 };
170 OSStatus result = SecItemAdd((__bridge CFDictionaryRef)item, NULL);
171 XCTAssertEqual(result, 0, @"failed to add test item to keychain: %u", n);
172 }
173 }
174
175 - (void)findManyItems:(unsigned)searchLimit
176 {
177 unsigned n;
178 for (n = 0; n < searchLimit; n++) {
179 NSDictionary* item = @{
180 (id)kSecClass : (id)kSecClassGenericPassword,
181 (id)kSecAttrAccount : [NSString stringWithFormat:@"TestAccount-%u", n],
182 (id)kSecAttrService : @"TestService",
183 (id)kSecAttrNoLegacy : @(YES)
184 };
185 OSStatus result = SecItemCopyMatching((__bridge CFDictionaryRef)item, NULL);
186 XCTAssertEqual(result, 0, @"failed to find test item to keychain: %u", n);
187 }
188 }
189
190 - (void)createManyKeys
191 {
192 unsigned n;
193 for (n = 0; n < 50; n++) {
194 NSDictionary* keyParams = @{
195 (id)kSecAttrKeyType : (id)kSecAttrKeyTypeRSA,
196 (id)kSecAttrKeySizeInBits : @(1024)
197 };
198 SecKeyRef key = SecKeyCreateRandomKey((__bridge CFDictionaryRef)keyParams, NULL);
199 NSDictionary* item = @{ (id)kSecClass : (id)kSecClassKey,
200 (id)kSecValueRef : (__bridge id)key,
201 (id)kSecAttrLabel : [NSString stringWithFormat:@"TestLabel-%u", n],
202 (id)kSecAttrNoLegacy : @(YES) };
203
204 OSStatus result = SecItemAdd((__bridge CFDictionaryRef)item, NULL);
205 XCTAssertEqual(result, 0, @"failed to add test key to keychain: %u", n);
206 }
207 }
208
209
210 - (void)testBackupRestoreItem
211 {
212 [self createManyItems];
213 [self createManyKeys];
214
215
216 NSDictionary* item = @{ (id)kSecClass : (id)kSecClassGenericPassword,
217 (id)kSecValueData : [@"password" dataUsingEncoding:NSUTF8StringEncoding],
218 (id)kSecAttrAccount : @"TestAccount",
219 (id)kSecAttrService : @"TestService",
220 (id)kSecAttrNoLegacy : @(YES) };
221
222 OSStatus result = SecItemAdd((__bridge CFDictionaryRef)item, NULL);
223 XCTAssertEqual(result, 0, @"failed to add test item to keychain");
224
225 NSMutableDictionary* dataQuery = item.mutableCopy;
226 [dataQuery removeObjectForKey:(id)kSecValueData];
227 dataQuery[(id)kSecReturnData] = @(YES);
228 CFTypeRef foundItem = NULL;
229
230 /*
231 * Create backup
232 */
233
234 CFDataRef keybag = CFDataCreate(kCFAllocatorDefault, NULL, 0);
235 CFDataRef password = CFDataCreate(kCFAllocatorDefault, NULL, 0);
236
237 CFDataRef backup = _SecKeychainCopyBackup(keybag, password);
238 XCTAssert(backup, "expected to have a backup");
239
240 result = SecItemDelete((__bridge CFDictionaryRef)dataQuery);
241 XCTAssertEqual(result, 0, @"failed to delete item");
242
243 result = SecItemCopyMatching((__bridge CFDictionaryRef)dataQuery, &foundItem);
244 XCTAssertEqual(result, errSecItemNotFound,
245 @"failed to find the data for the item we just added in the keychain");
246 CFReleaseNull(foundItem);
247
248 /*
249 * Restore backup and see that item is resurected
250 */
251
252 XCTAssertEqual(0, _SecKeychainRestoreBackup(backup, keybag, password));
253
254 CFReleaseNull(backup);
255 CFReleaseNull(password);
256 CFReleaseNull(keybag);
257
258 result = SecItemCopyMatching((__bridge CFDictionaryRef)dataQuery, &foundItem);
259 XCTAssertEqual(result, 0, @"failed to find the data for the item we just added in the keychain");
260 CFReleaseNull(foundItem);
261
262 result = SecItemDelete((__bridge CFDictionaryRef)dataQuery);
263 XCTAssertEqual(result, 0, @"failed to delete item");
264 }
265
266 - (void)testCreateSampleDatabase
267 {
268 id mock = OCMClassMock([SecMockAKS class]);
269 OCMStub([mock useGenerationCount]).andReturn(true);
270
271 [self createManyItems];
272 [self createManyKeys];
273
274 /*
275 sleep(600);
276 lsof -p $(pgrep xctest)
277 sqlite3 database
278 .output mydatabase.h
279 .dump
280
281 add header and footer
282 */
283
284 [self findManyItems:50];
285 }
286
287 - (void)testTestAKSGenerationCount
288 {
289 id mock = OCMClassMock([SecMockAKS class]);
290 OCMStub([mock useGenerationCount]).andReturn(true);
291
292 [self createManyItems];
293 [self findManyItems:50];
294 }
295
296
297 - (void)loadDatabase:(const char **)dumpstring
298 {
299 const char *s;
300 unsigned n = 0;
301
302 [self removeHomeDirectory];
303 [self createKeychainDirectory];
304
305 NSString *path = CFBridgingRelease(__SecKeychainCopyPath());
306 sqlite3 *handle = NULL;
307
308 XCTAssertEqual(SQLITE_OK, sqlite3_open([path UTF8String], &handle), "create keychain");
309
310 while ((s = dumpstring[n++]) != NULL) {
311 char * errmsg = NULL;
312 XCTAssertEqual(SQLITE_OK, sqlite3_exec(handle, s, NULL, NULL, &errmsg),
313 "exec: %s: %s", s, errmsg);
314 if (errmsg) {
315 sqlite3_free(errmsg);
316 }
317 }
318 XCTAssertEqual(SQLITE_OK, sqlite3_close(handle), "close sqlite");
319 }
320
321 - (void)testUpgradeFromVersion10_5
322 {
323 SecKeychainDbReset(^{
324 NSLog(@"resetting database");
325 [self loadDatabase:secdmock_db_version10_5];
326 });
327
328 NSLog(@"find items from old database");
329 [self findManyItems:50];
330 }
331
332 - (void)testUpgradeFromVersion11_1
333 {
334 SecKeychainDbReset(^{
335 NSLog(@"resetting database");
336 [self loadDatabase:secdmock_db_version11_1];
337 });
338
339 NSLog(@"find items from old database");
340 [self findManyItems:50];
341 }
342
343 - (bool)isLockedSoon:(keyclass_t)key_class
344 {
345 if (key_class == key_class_d || key_class == key_class_dku)
346 return false;
347 if (self.lockCounter <= 0)
348 return true;
349 self.lockCounter--;
350 return false;
351 }
352
353
354 /*
355 * Lock in the middle of migration
356 */
357 - (void)testUpgradeFromVersion10_5WhileLocked
358 {
359 OSStatus result = 0;
360 id mock = OCMClassMock([SecMockAKS class]);
361 [[[[mock stub] andCall:@selector(isLockedSoon:) onObject:self] ignoringNonObjectArgs] isLocked:0];
362
363 SecKeychainDbReset(^{
364 NSLog(@"resetting database");
365 [self loadDatabase:secdmock_db_version10_5];
366 });
367
368 self.lockCounter = 0;
369
370 NSDictionary* item = @{
371 (id)kSecClass : (id)kSecClassGenericPassword,
372 (id)kSecAttrAccount : @"TestAccount-11",
373 (id)kSecAttrService : @"TestService",
374 (id)kSecReturnData : @YES,
375 (id)kSecAttrNoLegacy : @YES
376 };
377 result = SecItemCopyMatching((__bridge CFDictionaryRef)item, NULL);
378 XCTAssertEqual(result, errSecInteractionNotAllowed, @"SEP not locked?");
379
380 XCTAssertEqual(self.lockCounter, 0, "Device didn't lock");
381
382 NSLog(@"user unlock");
383 [mock stopMocking];
384
385
386 result = SecItemCopyMatching((__bridge CFDictionaryRef)item, NULL);
387 XCTAssertEqual(result, 0, @"can't find item");
388
389
390 NSLog(@"find items from old database");
391 [self findManyItems:50];
392 }
393
394
395 - (void)testUpgradeFromVersion10_5HungSEP
396 {
397 id mock = OCMClassMock([SecMockAKS class]);
398 OSStatus result = 0;
399
400 OCMStub([mock isSEPDown]).andReturn(true);
401
402 SecKeychainDbReset(^{
403 NSLog(@"resetting database");
404 [self loadDatabase:secdmock_db_version10_5];
405 });
406
407 NSDictionary* item = @{
408 (id)kSecClass : (id)kSecClassGenericPassword,
409 (id)kSecAttrAccount : @"TestAccount-0",
410 (id)kSecAttrService : @"TestService",
411 (id)kSecAttrNoLegacy : @(YES)
412 };
413 result = SecItemCopyMatching((__bridge CFDictionaryRef)item, NULL);
414 XCTAssertEqual(result, errSecNotAvailable, @"SEP not down?");
415
416 kc_with_dbt(true, NULL, ^bool (SecDbConnectionRef dbt) {
417 CFErrorRef error = NULL;
418 int version = 0;
419 SecKeychainDbGetVersion(dbt, &version, &error);
420 XCTAssertEqual(error, NULL, "error getting version");
421 XCTAssertEqual(version, 0x50a, "managed to upgrade when we shouldn't have");
422 });
423
424 /* user got the SEP out of DFU */
425 NSLog(@"SEP alive");
426 [mock stopMocking];
427
428 result = SecItemCopyMatching((__bridge CFDictionaryRef)item, NULL);
429 XCTAssertEqual(result, 0, @"failed to find test item to keychain");
430
431 kc_with_dbt(true, NULL, ^bool (SecDbConnectionRef dbt) {
432 CFErrorRef error = NULL;
433 int version = 0;
434 SecKeychainDbGetVersion(dbt, &version, &error);
435 XCTAssertEqual(error, NULL, "error getting version");
436 XCTAssertEqual(version, 0x20b, "didnt managed to upgrade");
437 });
438
439 NSLog(@"find items from old database");
440 [self findManyItems:50];
441 }
442
443
444 @end