]> git.saurik.com Git - apple/security.git/blob - secdxctests/KeychainCryptoTests.m
Security-58286.200.222.tar.gz
[apple/security.git] / secdxctests / KeychainCryptoTests.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 #import "KeychainXCTest.h"
25 #import "SecDbKeychainItem.h"
26 #import "SecdTestKeychainUtilities.h"
27 #import "CKKS.h"
28 #import "SecDbKeychainItemV7.h"
29 #import "SecItemPriv.h"
30 #import "SecItemServer.h"
31 #import "spi.h"
32 #import "SecDbKeychainSerializedItemV7.h"
33 #import "SecDbKeychainSerializedMetadata.h"
34 #import "SecDbKeychainSerializedSecretData.h"
35 #import "SecDbKeychainSerializedAKSWrappedKey.h"
36 #import <utilities/SecCFWrappers.h>
37 #import <SecurityFoundation/SFEncryptionOperation.h>
38 #import <SecurityFoundation/SFCryptoServicesErrors.h>
39 #import <XCTest/XCTest.h>
40 #import <OCMock/OCMock.h>
41 #import <notify.h>
42
43 @interface SecDbKeychainItemV7 ()
44
45 + (SFAESKeySpecifier*)keySpecifier;
46
47 @end
48
49 #if USE_KEYSTORE
50 #import <libaks.h>
51
52 @interface KeychainCryptoTests : KeychainXCTest
53 @end
54
55 @implementation KeychainCryptoTests
56
57 static keyclass_t parse_keyclass(CFTypeRef value) {
58 if (!value || CFGetTypeID(value) != CFStringGetTypeID()) {
59 return 0;
60 }
61
62 if (CFEqual(value, kSecAttrAccessibleWhenUnlocked)) {
63 return key_class_ak;
64 }
65 else if (CFEqual(value, kSecAttrAccessibleAfterFirstUnlock)) {
66 return key_class_ck;
67 }
68 else if (CFEqual(value, kSecAttrAccessibleAlwaysPrivate)) {
69 return key_class_dk;
70 }
71 else if (CFEqual(value, kSecAttrAccessibleWhenUnlockedThisDeviceOnly)) {
72 return key_class_aku;
73 }
74 else if (CFEqual(value, kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly)) {
75 return key_class_cku;
76 }
77 else if (CFEqual(value, kSecAttrAccessibleAlwaysThisDeviceOnlyPrivate)) {
78 return key_class_dku;
79 }
80 else if (CFEqual(value, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly)) {
81 return key_class_akpu;
82 }
83 else {
84 return 0;
85 }
86 }
87
88 - (NSDictionary* _Nullable)addTestItemExpecting:(OSStatus)code account:(NSString*)account accessible:(NSString*)accessible
89 {
90 NSDictionary* addQuery = @{ (id)kSecClass : (id)kSecClassGenericPassword,
91 (id)kSecValueData : [@"password" dataUsingEncoding:NSUTF8StringEncoding],
92 (id)kSecAttrAccount : account,
93 (id)kSecAttrService : [NSString stringWithFormat:@"%@-Service", account],
94 (id)kSecAttrAccessible : (id)accessible,
95 (id)kSecAttrNoLegacy : @(YES),
96 (id)kSecReturnAttributes : @(YES),
97 };
98 CFTypeRef result = NULL;
99
100 if(code == errSecSuccess) {
101 XCTAssertEqual(SecItemAdd((__bridge CFDictionaryRef)addQuery, &result), code, @"Should have succeeded in adding test item to keychain");
102 XCTAssertNotNil((__bridge id)result, @"Should have received a dictionary back from SecItemAdd");
103 } else {
104 XCTAssertEqual(SecItemAdd((__bridge CFDictionaryRef)addQuery, &result), code, @"Should have failed to adding test item to keychain with code %d", code);
105 XCTAssertNil((__bridge id)result, @"Should not have received a dictionary back from SecItemAdd");
106 }
107
108 return CFBridgingRelease(result);
109 }
110
111 - (NSDictionary* _Nullable)findTestItemExpecting:(OSStatus)code account:(NSString*)account
112 {
113 NSDictionary* findQuery = @{ (id)kSecClass : (id)kSecClassGenericPassword,
114 (id)kSecAttrAccount : account,
115 (id)kSecAttrService : [NSString stringWithFormat:@"%@-Service", account],
116 (id)kSecAttrNoLegacy : @(YES),
117 (id)kSecReturnAttributes : @(YES),
118 };
119 CFTypeRef result = NULL;
120
121 if(code == errSecSuccess) {
122 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)findQuery, &result), code, @"Should have succeeded in finding test tiem");
123 XCTAssertNotNil((__bridge id)result, @"Should have received a dictionary back from SecItemCopyMatching");
124 } else {
125 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)findQuery, &result), code, @"Should have failed to find items in keychain with code %d", code);
126 XCTAssertNotNil((__bridge id)result, @"Should not have received a dictionary back from SecItemCopyMatching");
127 }
128
129 return CFBridgingRelease(result);
130 }
131
132
133 - (void)testBasicEncryptDecrypt
134 {
135 CFDataRef enc = NULL;
136 CFErrorRef error = NULL;
137 SecAccessControlRef ac = NULL;
138
139 NSDictionary* secretData = @{(id)kSecValueData : @"secret here"};
140
141 ac = SecAccessControlCreate(NULL, &error);
142 XCTAssertNotNil((__bridge id)ac, @"failed to create access control with error: %@", (__bridge id)error);
143 XCTAssertNil((__bridge id)error, @"encountered error attempting to create access control: %@", (__bridge id)error);
144 XCTAssertTrue(SecAccessControlSetProtection(ac, kSecAttrAccessibleWhenUnlocked, &error), @"failed to set access control protection with error: %@", error);
145 XCTAssertNil((__bridge id)error, @"encountered error attempting to set access control protection: %@", (__bridge id)error);
146
147 XCTAssertTrue(ks_encrypt_data(KEYBAG_DEVICE, ac, NULL, (__bridge CFDictionaryRef)secretData, (__bridge CFDictionaryRef)@{}, NULL, &enc, true, &error), @"failed to encrypt data with error: %@", error);
148 XCTAssertTrue(enc != NULL, @"failed to get encrypted data from encryption function");
149 XCTAssertNil((__bridge id)error, @"encountered error attempting to encrypt data: %@", (__bridge id)error);
150 CFReleaseNull(ac);
151
152 CFMutableDictionaryRef attributes = NULL;
153 uint32_t version = 0;
154
155 keyclass_t keyclass = 0;
156 XCTAssertTrue(ks_decrypt_data(KEYBAG_DEVICE, kAKSKeyOpDecrypt, &ac, NULL, enc, NULL, NULL, &attributes, &version, true, &keyclass, &error), @"failed to decrypt data with error: %@", error);
157 XCTAssertNil((__bridge id)error, @"encountered error attempting to decrypt data: %@", (__bridge id)error);
158 XCTAssertEqual(keyclass, key_class_ak, @"failed to get back the keyclass from decryption");
159
160 CFTypeRef aclProtection = ac ? SecAccessControlGetProtection(ac) : NULL;
161 XCTAssertNotNil((__bridge id)aclProtection, @"failed to get ACL from keychain item decryption");
162 if (aclProtection) {
163 XCTAssertTrue(CFEqual(aclProtection, kSecAttrAccessibleWhenUnlocked), @"the acl we got back from decryption does not match what we put in");
164 }
165 CFReleaseNull(ac);
166
167 CFReleaseNull(error);
168 CFReleaseNull(enc);
169 }
170
171 - (void)testGetMetadataThenData
172 {
173 NSDictionary* item = @{ (id)kSecClass : (id)kSecClassGenericPassword,
174 (id)kSecValueData : [@"password" dataUsingEncoding:NSUTF8StringEncoding],
175 (id)kSecAttrAccount : @"TestAccount",
176 (id)kSecAttrService : @"TestService",
177 (id)kSecAttrNoLegacy : @(YES) };
178
179 OSStatus result = SecItemAdd((__bridge CFDictionaryRef)item, NULL);
180 XCTAssertEqual(result, 0, @"failed to add test item to keychain");
181
182 NSMutableDictionary* metadataQuery = item.mutableCopy;
183 [metadataQuery removeObjectForKey:(id)kSecValueData];
184 metadataQuery[(id)kSecReturnAttributes] = @(YES);
185 CFTypeRef foundItem = NULL;
186 result = SecItemCopyMatching((__bridge CFDictionaryRef)metadataQuery, &foundItem);
187 XCTAssertEqual(result, 0, @"failed to find the metadata for the item we just added in the keychain");
188
189 NSMutableDictionary* dataQuery = [(__bridge NSDictionary*)foundItem mutableCopy];
190 dataQuery[(id)kSecReturnData] = @(YES);
191 dataQuery[(id)kSecClass] = (id)kSecClassGenericPassword;
192 dataQuery[(id)kSecAttrNoLegacy] = @(YES);
193 result = SecItemCopyMatching((__bridge CFDictionaryRef)dataQuery, &foundItem);
194 XCTAssertEqual(result, 0, @"failed to find the data for the item we just added to the keychain");
195
196 NSData* foundData = (__bridge NSData*)foundItem;
197 if ([foundData isKindOfClass:[NSData class]]) {
198 NSString* foundPassword = [[NSString alloc] initWithData:(__bridge NSData*)foundItem encoding:NSUTF8StringEncoding];
199 XCTAssertEqualObjects(foundPassword, @"password", @"found password (%@) does not match the expected password", foundPassword);
200 }
201 else {
202 XCTAssertTrue(false, @"found data is not the expected class: %@", foundData);
203 }
204 }
205
206 - (void)testGetReference
207 {
208 NSDictionary* keyParams = @{ (id)kSecAttrKeyType : (id)kSecAttrKeyTypeRSA, (id)kSecAttrKeySizeInBits : @(1024) };
209 SecKeyRef key = SecKeyCreateRandomKey((__bridge CFDictionaryRef)keyParams, NULL);
210 NSDictionary* item = @{ (id)kSecClass : (id)kSecClassKey,
211 (id)kSecValueRef : (__bridge id)key,
212 (id)kSecAttrLabel : @"TestLabel",
213 (id)kSecAttrNoLegacy : @(YES) };
214
215 OSStatus result = SecItemAdd((__bridge CFDictionaryRef)item, NULL);
216 XCTAssertEqual(result, 0, @"failed to add test item to keychain");
217
218 NSMutableDictionary* refQuery = item.mutableCopy;
219 [refQuery removeObjectForKey:(id)kSecValueData];
220 refQuery[(id)kSecReturnRef] = @(YES);
221 CFTypeRef foundItem = NULL;
222 result = SecItemCopyMatching((__bridge CFDictionaryRef)refQuery, &foundItem);
223 XCTAssertEqual(result, 0, @"failed to find the reference for the item we just added in the keychain");
224
225 NSData* originalKeyData = (__bridge_transfer NSData*)SecKeyCopyExternalRepresentation(key, NULL);
226 NSData* foundKeyData = (__bridge_transfer NSData*)SecKeyCopyExternalRepresentation((SecKeyRef)foundItem, NULL);
227 XCTAssertEqualObjects(originalKeyData, foundKeyData, @"found key does not match the key we put in the keychain");
228 }
229
230 - (void)testMetadataQueriesDoNotGetSecret
231 {
232 NSDictionary* item = @{ (id)kSecClass : (id)kSecClassGenericPassword,
233 (id)kSecValueData : [@"password" dataUsingEncoding:NSUTF8StringEncoding],
234 (id)kSecAttrAccount : @"TestAccount",
235 (id)kSecAttrService : @"TestService",
236 (id)kSecAttrNoLegacy : @(YES) };
237
238 OSStatus result = SecItemAdd((__bridge CFDictionaryRef)item, NULL);
239 XCTAssertEqual(result, 0, @"failed to add test item to keychain");
240
241 NSMutableDictionary* metadataQuery = item.mutableCopy;
242 [metadataQuery removeObjectForKey:(id)kSecValueData];
243 metadataQuery[(id)kSecReturnAttributes] = @(YES);
244 CFTypeRef foundItem = NULL;
245 result = SecItemCopyMatching((__bridge CFDictionaryRef)metadataQuery, &foundItem);
246 XCTAssertEqual(result, 0, @"failed to find the metadata for the item we just added in the keychain");
247
248 NSData* data = [(__bridge NSDictionary*)foundItem valueForKey:(id)kSecValueData];
249 XCTAssertNil(data, @"unexpectedly found data in a metadata query");
250 }
251
252 - (void)testDeleteItem
253 {
254 NSDictionary* item = @{ (id)kSecClass : (id)kSecClassGenericPassword,
255 (id)kSecValueData : [@"password" dataUsingEncoding:NSUTF8StringEncoding],
256 (id)kSecAttrAccount : @"TestAccount",
257 (id)kSecAttrService : @"TestService",
258 (id)kSecAttrNoLegacy : @(YES) };
259
260 OSStatus result = SecItemAdd((__bridge CFDictionaryRef)item, NULL);
261 XCTAssertEqual(result, 0, @"failed to add test item to keychain");
262
263 NSMutableDictionary* dataQuery = item.mutableCopy;
264 [dataQuery removeObjectForKey:(id)kSecValueData];
265 dataQuery[(id)kSecReturnData] = @(YES);
266 CFTypeRef foundItem = NULL;
267 result = SecItemCopyMatching((__bridge CFDictionaryRef)dataQuery, &foundItem);
268 XCTAssertEqual(result, 0, @"failed to find the data for the item we just added in the keychain");
269
270 result = SecItemDelete((__bridge CFDictionaryRef)dataQuery);
271 XCTAssertEqual(result, 0, @"failed to delete item");
272 }
273
274 - (SecDbKeychainSerializedItemV7*)serializedItemWithPassword:(NSString*)password metadataAttributes:(NSDictionary*)metadata
275 {
276 SecDbKeychainItemV7* item = [[SecDbKeychainItemV7 alloc] initWithSecretAttributes:@{(id)kSecValueData : password} metadataAttributes:metadata tamperCheck:[[NSUUID UUID] UUIDString] keyclass:9];
277 [item encryptMetadataWithKeybag:0 error:nil];
278 [item encryptSecretDataWithKeybag:0 accessControl:SecAccessControlCreate(NULL, NULL) acmContext:nil error:nil];
279 SecDbKeychainSerializedItemV7* serializedItem = [[SecDbKeychainSerializedItemV7 alloc] init];
280 serializedItem.encryptedMetadata = item.encryptedMetadataBlob;
281 serializedItem.encryptedSecretData = item.encryptedSecretDataBlob;
282 serializedItem.keyclass = 9;
283 return serializedItem;
284 }
285
286 - (void)testTamperChecksThwartTampering
287 {
288 SecDbKeychainSerializedItemV7* serializedItem1 = [self serializedItemWithPassword:@"first password" metadataAttributes:nil];
289 SecDbKeychainSerializedItemV7* serializedItem2 = [self serializedItemWithPassword:@"second password" metadataAttributes:nil];
290
291 serializedItem1.encryptedSecretData = serializedItem2.encryptedSecretData;
292 NSData* tamperedSerializedItemBlob = serializedItem1.data;
293
294 NSError* error = nil;
295 SecDbKeychainItemV7* item = [[SecDbKeychainItemV7 alloc] initWithData:tamperedSerializedItemBlob decryptionKeybag:0 error:&error];
296 XCTAssertNil(item, @"unexpectedly deserialized an item blob which has been tampered");
297 XCTAssertNotNil(error, @"failed to get an error when deserializing tampered item blob");
298 }
299
300 - (void)testCacheExpiration
301 {
302
303 NSDictionary* item = @{ (id)kSecClass : (id)kSecClassGenericPassword,
304 (id)kSecValueData : [@"password" dataUsingEncoding:NSUTF8StringEncoding],
305 (id)kSecAttrAccount : @"TestAccount",
306 (id)kSecAttrService : @"TestService",
307 (id)kSecAttrAccessible : (id)kSecAttrAccessibleWhenUnlocked,
308 (id)kSecAttrNoLegacy : @YES };
309
310 OSStatus result = SecItemAdd((__bridge CFDictionaryRef)item, NULL);
311 XCTAssertEqual(result, 0, @"failed to add test item to keychain");
312
313 NSMutableDictionary* dataQuery = item.mutableCopy;
314 [dataQuery removeObjectForKey:(id)kSecValueData];
315 dataQuery[(id)kSecReturnData] = @(YES);
316
317 CFTypeRef foundItem = NULL;
318
319 result = SecItemCopyMatching((__bridge CFDictionaryRef)dataQuery, &foundItem);
320 XCTAssertEqual(result, 0, @"failed to find the data for the item we just added in the keychain");
321 CFReleaseNull(foundItem);
322
323 self.lockState = LockStateLockedAndDisallowAKS;
324
325 result = SecItemCopyMatching((__bridge CFDictionaryRef)dataQuery, &foundItem);
326 XCTAssertEqual(result, errSecInteractionNotAllowed, @"get the lock error");
327 XCTAssertEqual(foundItem, NULL, @"got item anyway: %@", foundItem);
328
329 self.lockState = LockStateUnlocked;
330
331 result = SecItemCopyMatching((__bridge CFDictionaryRef)dataQuery, &foundItem);
332 XCTAssertEqual(result, 0, @"failed to find the data for the item we just added in the keychain");
333 CFReleaseNull(foundItem);
334
335 result = SecItemDelete((__bridge CFDictionaryRef)dataQuery);
336 XCTAssertEqual(result, 0, @"failed to delete item");
337 }
338
339 - (void)trashMetadataClassAKey
340 {
341 CFErrorRef cferror = NULL;
342
343 kc_with_dbt(true, &cferror, ^bool(SecDbConnectionRef dbt) {
344 CFErrorRef errref = NULL;
345 SecDbExec(dbt, CFSTR("DELETE FROM metadatakeys WHERE keyclass = '6'"), &errref);
346 XCTAssertEqual(errref, NULL, "Should be no error deleting class A metadatakey");
347 CFReleaseNull(errref);
348 return true;
349 });
350 CFReleaseNull(cferror);
351
352 [[SecDbKeychainMetadataKeyStore sharedStore] dropClassAKeys];
353 }
354
355 - (void)checkDatabaseExistenceOfMetadataKey:(keyclass_t)keyclass shouldExist:(bool)shouldExist
356 {
357 CFErrorRef cferror = NULL;
358
359 kc_with_dbt(true, &cferror, ^bool(SecDbConnectionRef dbt) {
360 __block CFErrorRef errref = NULL;
361
362 NSString* sql = [NSString stringWithFormat:@"SELECT data, actualKeyclass FROM metadatakeys WHERE keyclass = %d", keyclass];
363 __block bool ok = true;
364 __block bool keyExists = false;
365 ok &= SecDbPrepare(dbt, (__bridge CFStringRef)sql, &errref, ^(sqlite3_stmt *stmt) {
366 ok &= SecDbStep(dbt, stmt, &errref, ^(bool *stop) {
367 NSData* wrappedKeyData = [[NSData alloc] initWithBytes:sqlite3_column_blob(stmt, 0) length:sqlite3_column_bytes(stmt, 0)];
368 NSMutableData* unwrappedKeyData = [NSMutableData dataWithLength:wrappedKeyData.length];
369
370 keyExists = !!unwrappedKeyData;
371 });
372 });
373
374 XCTAssertTrue(ok, "Should have completed all operations correctly");
375 XCTAssertEqual(errref, NULL, "Should be no error deleting class A metadatakey");
376
377 if(shouldExist) {
378 XCTAssertTrue(keyExists, "Metadata class key should exist");
379 } else {
380 XCTAssertFalse(keyExists, "Metadata class key should not exist");
381 }
382 CFReleaseNull(errref);
383 return true;
384 });
385 CFReleaseNull(cferror);
386 }
387
388 - (void)testKeychainCorruptionCopyMatching
389 {
390 NSDictionary* item = @{ (id)kSecClass : (id)kSecClassGenericPassword,
391 (id)kSecValueData : [@"password" dataUsingEncoding:NSUTF8StringEncoding],
392 (id)kSecAttrAccount : @"TestAccount",
393 (id)kSecAttrService : @"TestService",
394 (id)kSecAttrAccessible : (id)kSecAttrAccessibleWhenUnlocked,
395 (id)kSecAttrNoLegacy : @YES };
396
397 OSStatus result = SecItemAdd((__bridge CFDictionaryRef)item, NULL);
398 XCTAssertEqual(result, 0, @"failed to add test item to keychain");
399 [self checkDatabaseExistenceOfMetadataKey:key_class_ak shouldExist:true];
400
401 NSMutableDictionary* dataQuery = item.mutableCopy;
402 [dataQuery removeObjectForKey:(id)kSecValueData];
403 dataQuery[(id)kSecReturnData] = @(YES);
404
405 CFTypeRef foundItem = NULL;
406
407 result = SecItemCopyMatching((__bridge CFDictionaryRef)dataQuery, &foundItem);
408 XCTAssertEqual(result, 0, @"failed to find the data for the item we just added in the keychain");
409 CFReleaseNull(foundItem);
410
411 [self trashMetadataClassAKey];
412 [self checkDatabaseExistenceOfMetadataKey:key_class_ak shouldExist:false];
413
414 /* when metadata corrupted, we should not find the item */
415 result = SecItemCopyMatching((__bridge CFDictionaryRef)dataQuery, &foundItem);
416 XCTAssertEqual(result, errSecItemNotFound, @"failed to find the data for the item we just added in the keychain");
417 CFReleaseNull(foundItem);
418
419 // Just calling SecItemCopyMatching shouldn't have created a new metdata key
420 [self checkDatabaseExistenceOfMetadataKey:key_class_ak shouldExist:false];
421
422 /* semantics are odd, we should be able to delete it */
423 result = SecItemDelete((__bridge CFDictionaryRef)dataQuery);
424 XCTAssertEqual(result, 0, @"failed to delete item");
425 }
426
427 - (void)testKeychainCorruptionAddOverCorruptedEntry
428 {
429 CFTypeRef foundItem = NULL;
430 NSDictionary* item = @{ (id)kSecClass : (id)kSecClassGenericPassword,
431 (id)kSecValueData : [@"password" dataUsingEncoding:NSUTF8StringEncoding],
432 (id)kSecAttrAccount : @"TestAccount",
433 (id)kSecAttrService : @"TestService",
434 (id)kSecAttrAccessible : (id)kSecAttrAccessibleWhenUnlocked,
435 (id)kSecAttrNoLegacy : @YES };
436
437 OSStatus result = SecItemAdd((__bridge CFDictionaryRef)item, NULL);
438 XCTAssertEqual(result, 0, @"failed to add test item to keychain");
439
440 NSMutableDictionary* dataQuery = item.mutableCopy;
441 [dataQuery removeObjectForKey:(id)kSecValueData];
442 dataQuery[(id)kSecReturnData] = @(YES);
443
444 result = SecItemCopyMatching((__bridge CFDictionaryRef)dataQuery, &foundItem);
445 XCTAssertEqual(result, 0, @"failed to find the data for the item we just added in the keychain");
446 CFReleaseNull(foundItem);
447
448 [self trashMetadataClassAKey];
449
450 result = SecItemAdd((__bridge CFDictionaryRef)item, NULL);
451 XCTAssertEqual(result, 0, @"failed to add test item to keychain");
452
453 result = SecItemDelete((__bridge CFDictionaryRef)dataQuery);
454 XCTAssertEqual(result, 0, @"failed to delete item");
455 }
456
457 - (void)testKeychainCorruptionUpdateCorruptedEntry
458 {
459 CFTypeRef foundItem = NULL;
460 NSDictionary* item = @{ (id)kSecClass : (id)kSecClassGenericPassword,
461 (id)kSecValueData : [@"password" dataUsingEncoding:NSUTF8StringEncoding],
462 (id)kSecAttrAccount : @"TestAccount",
463 (id)kSecAttrService : @"TestService",
464 (id)kSecAttrAccessible : (id)kSecAttrAccessibleWhenUnlocked,
465 (id)kSecAttrNoLegacy : @YES };
466
467 OSStatus result = SecItemAdd((__bridge CFDictionaryRef)item, NULL);
468 XCTAssertEqual(result, 0, @"failed to add test item to keychain");
469
470 NSMutableDictionary* dataQuery = item.mutableCopy;
471 [dataQuery removeObjectForKey:(id)kSecValueData];
472 dataQuery[(id)kSecReturnData] = @(YES);
473
474 result = SecItemCopyMatching((__bridge CFDictionaryRef)dataQuery, &foundItem);
475 XCTAssertEqual(result, 0, @"failed to find the data for the item we just added in the keychain");
476 CFReleaseNull(foundItem);
477
478 [self trashMetadataClassAKey];
479
480 NSMutableDictionary* updateQuery = item.mutableCopy;
481 updateQuery[(id)kSecValueData] = NULL;
482 NSDictionary *updateData = @{
483 (id)kSecValueData : [@"foo" dataUsingEncoding:NSUTF8StringEncoding],
484 };
485
486 result = SecItemUpdate((__bridge CFDictionaryRef)updateQuery,
487 (__bridge CFDictionaryRef)updateData );
488 XCTAssertEqual(result, errSecItemNotFound, @"failed to add test item to keychain");
489
490 result = SecItemDelete((__bridge CFDictionaryRef)dataQuery);
491 XCTAssertEqual(result, 0, @"failed to delete item");
492 }
493
494 - (id)encryptionOperation
495 {
496 return nil;
497 }
498
499 - (void)testNoCrashWhenMetadataDecryptionFails
500 {
501 CFDataRef enc = NULL;
502 CFErrorRef error = NULL;
503 SecAccessControlRef ac = NULL;
504
505 self.allowDecryption = NO;
506
507 NSDictionary* secretData = @{(id)kSecValueData : @"secret here"};
508
509 ac = SecAccessControlCreate(NULL, &error);
510 XCTAssertNotNil((__bridge id)ac, @"failed to create access control with error: %@", (__bridge id)error);
511 XCTAssertNil((__bridge id)error, @"encountered error attempting to create access control: %@", (__bridge id)error);
512 XCTAssertTrue(SecAccessControlSetProtection(ac, kSecAttrAccessibleWhenUnlocked, &error), @"failed to set access control protection with error: %@", error);
513 XCTAssertNil((__bridge id)error, @"encountered error attempting to set access control protection: %@", (__bridge id)error);
514
515 XCTAssertTrue(ks_encrypt_data(KEYBAG_DEVICE, ac, NULL, (__bridge CFDictionaryRef)secretData, (__bridge CFDictionaryRef)@{}, NULL, &enc, true, &error), @"failed to encrypt data with error: %@", error);
516 XCTAssertTrue(enc != NULL, @"failed to get encrypted data from encryption function");
517 XCTAssertNil((__bridge id)error, @"encountered error attempting to encrypt data: %@", (__bridge id)error);
518 CFReleaseNull(ac);
519
520 CFMutableDictionaryRef attributes = NULL;
521 uint32_t version = 0;
522
523 keyclass_t keyclass = 0;
524 XCTAssertNoThrow(ks_decrypt_data(KEYBAG_DEVICE, kAKSKeyOpDecrypt, &ac, NULL, enc, NULL, NULL, &attributes, &version, true, &keyclass, &error), @"unexpected exception when decryption fails");
525 XCTAssertEqual(keyclass, key_class_ak, @"failed to get back the keyclass when decryption failed");
526
527 self.allowDecryption = YES;
528 }
529
530 #if 0
531 // these tests fail until we address <rdar://problem/37523001> Fix keychain lock state check to be both secure and fast for EDU mode
532 - (void)testOperationsDontUseCachedKeysWhileLockedWithAKSAvailable // simulating the backup situation
533 {
534 self.lockState = LockStateLockedAndAllowAKS;
535
536 NSDictionary* item = @{ (id)kSecClass : (id)kSecClassGenericPassword,
537 (id)kSecValueData : [@"password" dataUsingEncoding:NSUTF8StringEncoding],
538 (id)kSecAttrAccount : @"TestAccount",
539 (id)kSecAttrService : @"TestService",
540 (id)kSecAttrNoLegacy : @(YES) };
541
542 OSStatus result = SecItemAdd((__bridge CFDictionaryRef)item, NULL);
543 XCTAssertEqual(result, 0, @"failed to add test item to keychain");
544
545 NSMutableDictionary* metadataQuery = item.mutableCopy;
546 [metadataQuery removeObjectForKey:(id)kSecValueData];
547 metadataQuery[(id)kSecReturnAttributes] = @(YES);
548 CFTypeRef foundItem = NULL;
549 result = SecItemCopyMatching((__bridge CFDictionaryRef)metadataQuery, &foundItem);
550 XCTAssertEqual(result, 0, @"failed to find the metadata for the item we just added in the keychain");
551
552 XCTAssertTrue(self.didAKSDecrypt, @"we did not go through AKS to decrypt the metadata key while locked - bad!");
553
554 NSMutableDictionary* dataQuery = item.mutableCopy;
555 dataQuery[(id)kSecReturnData] = @(YES);
556 result = SecItemCopyMatching((__bridge CFDictionaryRef)dataQuery, &foundItem);
557 XCTAssertEqual(result, 0, @"failed to find the data for the item we just added to the keychain");
558
559 NSData* foundData = (__bridge NSData*)foundItem;
560 if ([foundData isKindOfClass:[NSData class]]) {
561 NSString* foundPassword = [[NSString alloc] initWithData:(__bridge NSData*)foundItem encoding:NSUTF8StringEncoding];
562 XCTAssertEqualObjects(foundPassword, @"password", @"found password (%@) does not match the expected password", foundPassword);
563 }
564 else {
565 XCTAssertTrue(false, @"found data is not the expected class: %@", foundData);
566 }
567 }
568
569 - (void)testNoResultsWhenLocked
570 {
571 NSDictionary* item = @{ (id)kSecClass : (id)kSecClassGenericPassword,
572 (id)kSecValueData : [@"password" dataUsingEncoding:NSUTF8StringEncoding],
573 (id)kSecAttrAccount : @"TestAccount",
574 (id)kSecAttrService : @"TestService",
575 (id)kSecAttrNoLegacy : @(YES) };
576
577 OSStatus result = SecItemAdd((__bridge CFDictionaryRef)item, NULL);
578 XCTAssertEqual(result, 0, @"failed to add test item to keychain");
579
580 self.lockState = LockStateLockedAndDisallowAKS;
581
582 NSMutableDictionary* metadataQuery = item.mutableCopy;
583 [metadataQuery removeObjectForKey:(id)kSecValueData];
584 metadataQuery[(id)kSecReturnAttributes] = @(YES);
585 CFTypeRef foundItem = NULL;
586 result = SecItemCopyMatching((__bridge CFDictionaryRef)metadataQuery, &foundItem);
587 XCTAssertEqual(foundItem, NULL, @"somehow still got results when AKS was locked");
588 }
589 #endif
590
591 - (void)testRecoverFromBadMetadataKey
592 {
593 // Disable caching, so we can change AKS encrypt/decrypt
594 id mockSecDbKeychainMetadataKeyStore = OCMClassMock([SecDbKeychainMetadataKeyStore class]);
595 OCMStub([mockSecDbKeychainMetadataKeyStore cachingEnabled]).andReturn(false);
596
597 NSDictionary* addQuery = @{ (id)kSecClass : (id)kSecClassGenericPassword,
598 (id)kSecValueData : [@"password" dataUsingEncoding:NSUTF8StringEncoding],
599 (id)kSecAttrAccount : @"TestAccount",
600 (id)kSecAttrService : @"TestService",
601 (id)kSecAttrNoLegacy : @(YES),
602 (id)kSecReturnAttributes : @(YES),
603 };
604
605 NSDictionary* findQuery = @{ (id)kSecClass : (id)kSecClassGenericPassword,
606 (id)kSecAttrAccount : @"TestAccount",
607 (id)kSecAttrService : @"TestService",
608 (id)kSecAttrNoLegacy : @(YES),
609 (id)kSecReturnAttributes : @(YES),
610 };
611
612 #if TARGET_OS_OSX
613 NSDictionary* updateQuery = findQuery;
614 #else
615 // iOS won't tolerate kSecReturnAttributes in SecItemUpdate
616 NSDictionary* updateQuery = @{ (id)kSecClass : (id)kSecClassGenericPassword,
617 (id)kSecAttrAccount : @"TestAccount",
618 (id)kSecAttrService : @"TestService",
619 (id)kSecAttrNoLegacy : @(YES),
620 };
621 #endif
622
623 NSDictionary* addQuery2 = @{ (id)kSecClass : (id)kSecClassGenericPassword,
624 (id)kSecValueData : [@"password" dataUsingEncoding:NSUTF8StringEncoding],
625 (id)kSecAttrAccount : @"TestAccount-second",
626 (id)kSecAttrService : @"TestService-second",
627 (id)kSecAttrNoLegacy : @(YES),
628 (id)kSecReturnAttributes : @(YES),
629 };
630
631 NSDictionary* findQuery2 = @{ (id)kSecClass : (id)kSecClassGenericPassword,
632 (id)kSecAttrAccount : @"TestAccount-second",
633 (id)kSecAttrService : @"TestService-second",
634 (id)kSecAttrNoLegacy : @(YES),
635 (id)kSecReturnAttributes : @(YES),
636 };
637
638 CFTypeRef result = NULL;
639
640 // Add the item
641 XCTAssertEqual(SecItemAdd((__bridge CFDictionaryRef)addQuery, &result), errSecSuccess, @"Should have succeeded in adding test item to keychain");
642 XCTAssertNotNil((__bridge id)result, @"Should have received a dictionary back from SecItemAdd");
643 CFReleaseNull(result);
644
645 // Add a second item, for fun and profit
646 XCTAssertEqual(SecItemAdd((__bridge CFDictionaryRef)addQuery2, &result),
647 errSecSuccess,
648 @"Should have succeeded in adding test2 item to keychain");
649
650 // And we can find te item
651 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)findQuery, &result), errSecSuccess, @"Should be able to find item");
652 XCTAssertNotNil((__bridge id)result, @"Should have received a dictionary back from SecItemCopyMatching");
653 CFReleaseNull(result);
654
655 // And we can update the item
656 XCTAssertEqual(SecItemUpdate((__bridge CFDictionaryRef)updateQuery,
657 (__bridge CFDictionaryRef)@{(id)kSecValueData: [@"otherpassword" dataUsingEncoding:NSUTF8StringEncoding]}),
658 errSecSuccess,
659 "Should be able to update an item");
660
661 // And find it again
662 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)findQuery, &result), errSecSuccess, @"Should be able to find item");
663 XCTAssertNotNil((__bridge id)result, @"Should have received a dictionary back from SecItemCopyMatching");
664 CFReleaseNull(result);
665
666 // And we can find the second item
667 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)findQuery2, &result),
668 errSecSuccess, @"Should be able to find second item");
669 XCTAssertNotNil((__bridge id)result, @"Should have received a dictionary back from SecItemCopyMatching for item 2");
670 CFReleaseNull(result);
671
672 ///////////////////////////////////////////////////////////////////////////////////
673 // Now, the metadata keys go corrupt (fake that by changing the underlying AKS key)
674 [self setNewFakeAKSKey:[NSData dataWithBytes:"1234567890123456789000" length:32]];
675
676 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)findQuery, &result), errSecItemNotFound,
677 "should have received errSecItemNotFound when metadata keys are invalid");
678 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)findQuery, &result), errSecItemNotFound,
679 "Multiple finds of the same item should receive errSecItemNotFound when metadata keys are invalid");
680 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)findQuery, &result), errSecItemNotFound,
681 "Multiple finds of the same item should receive errSecItemNotFound when metadata keys are invalid");
682
683 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)findQuery2, &result),
684 errSecItemNotFound, @"Should not be able to find corrupt second item");
685 XCTAssertNil((__bridge id)result, @"Should have received no data back from SICM for corrupt item");
686
687 // Updating the now-corrupt item should fail
688 XCTAssertEqual(SecItemUpdate((__bridge CFDictionaryRef)updateQuery,
689 (__bridge CFDictionaryRef)@{ (id)kSecValueData: [@"otherpassword" dataUsingEncoding:NSUTF8StringEncoding] }),
690 errSecItemNotFound,
691 "Should not be able to update a corrupt item");
692
693 // Re-add the item (should succeed)
694 XCTAssertEqual(SecItemAdd((__bridge CFDictionaryRef)addQuery, &result), errSecSuccess, @"Should have succeeded in adding test item to keychain");
695 XCTAssertNotNil((__bridge id)result, @"Should have received a dictionary back from SecItemAdd");
696 CFReleaseNull(result);
697
698 // And we can find it again
699 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)findQuery, &result), errSecSuccess, @"Should be able to find item");
700 XCTAssertNotNil((__bridge id)result, @"Should have received a dictionary back from SecItemAdd");
701 CFReleaseNull(result);
702
703 // And update it
704 XCTAssertEqual(SecItemUpdate((__bridge CFDictionaryRef)updateQuery,
705 (__bridge CFDictionaryRef)@{ (id)kSecValueData: [@"otherpassword" dataUsingEncoding:NSUTF8StringEncoding] }),
706 errSecSuccess,
707 "Should be able to update a fixed item");
708
709 /////////////
710 // And our second item, which is wrapped under an old key, can't be found
711 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)findQuery2, &result),
712 errSecItemNotFound, @"Should not be able to find corrupt second item");
713 XCTAssertNil((__bridge id)result, @"Should have received no data back from SICM for corrupt item");
714
715 // But can be re-added
716 XCTAssertEqual(SecItemAdd((__bridge CFDictionaryRef)addQuery2, &result),
717 errSecSuccess,
718 @"Should have succeeded in adding test2 item to keychain after corruption");
719 XCTAssertNotNil((__bridge id)result, @"Should have received a dictionary back from SecItemAdd for item 2 (after corruption)");
720 CFReleaseNull(result);
721
722 // And we can find the second item again
723 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)findQuery2, &result),
724 errSecSuccess, @"Should be able to find second item after re-add");
725 XCTAssertNotNil((__bridge id)result, @"Should have received a dictionary back from SecItemCopyMatching for item 2 (after re-add)");
726 CFReleaseNull(result);
727
728 [mockSecDbKeychainMetadataKeyStore stopMocking];
729 }
730
731 // If a metadata key is created during a database transaction which is later rolled back, it shouldn't be cached for use later.
732 - (void)testMetadataKeyDoesntOutliveTxionRollback {
733 NSString* testAccount = @"TestAccount";
734 NSString* otherAccount = @"OtherAccount";
735 NSString* thirdAccount = @"ThirdAccount";
736 [self addTestItemExpecting:errSecSuccess account:testAccount accessible:(id)kSecAttrAccessibleAfterFirstUnlock];
737 [self checkDatabaseExistenceOfMetadataKey:key_class_ck shouldExist:true];
738 [self checkDatabaseExistenceOfMetadataKey:key_class_cku shouldExist:false];
739
740 // This should fail, and not create a CKU metadata key
741 [self addTestItemExpecting:errSecDuplicateItem account:testAccount accessible:(id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly];
742 [self checkDatabaseExistenceOfMetadataKey:key_class_ck shouldExist:true];
743 [self checkDatabaseExistenceOfMetadataKey:key_class_cku shouldExist:false];
744
745 // But successfully creating a new CKU item should create the key
746 [self addTestItemExpecting:errSecSuccess account:otherAccount accessible:(id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly];
747 [self checkDatabaseExistenceOfMetadataKey:key_class_ck shouldExist:true];
748 [self checkDatabaseExistenceOfMetadataKey:key_class_cku shouldExist:true];
749
750 // Drop all metadata key caches
751 [SecDbKeychainMetadataKeyStore resetSharedStore];
752
753 [self findTestItemExpecting:errSecSuccess account:testAccount];
754 [self findTestItemExpecting:errSecSuccess account:otherAccount];
755
756 // Adding another CKU item now should be fine
757 [self addTestItemExpecting:errSecSuccess account:thirdAccount accessible:(id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly];
758 [self checkDatabaseExistenceOfMetadataKey:key_class_ck shouldExist:true];
759 [self checkDatabaseExistenceOfMetadataKey:key_class_cku shouldExist:true];
760
761 // Drop all metadata key caches once more, to ensure we can find all three items from the persisted keys
762 [SecDbKeychainMetadataKeyStore resetSharedStore];
763
764 [self findTestItemExpecting:errSecSuccess account:testAccount];
765 [self findTestItemExpecting:errSecSuccess account:otherAccount];
766 [self findTestItemExpecting:errSecSuccess account:thirdAccount];
767 }
768
769 - (void)testRecoverDataFromBadKeyclassStorage
770 {
771 NSDictionary* metadataAttributesInput = @{@"TestMetadata" : @"TestValue"};
772 SecDbKeychainSerializedItemV7* serializedItem = [self serializedItemWithPassword:@"password" metadataAttributes:metadataAttributesInput];
773 serializedItem.keyclass = (serializedItem.keyclass | key_class_last + 1);
774
775 NSError* error = nil;
776 SecDbKeychainItemV7* item = [[SecDbKeychainItemV7 alloc] initWithData:serializedItem.data decryptionKeybag:0 error:&error];
777 NSDictionary* metadataAttributesOut = [item metadataAttributesWithError:&error];
778 XCTAssertEqualObjects(metadataAttributesOut, metadataAttributesInput, @"failed to retrieve metadata with error: %@", error);
779 XCTAssertNil(error, @"error encountered attempting to retrieve metadata: %@", error);
780 }
781
782 - (NSData*)performItemEncryptionWithAccessibility:(CFStringRef)accessibility
783 {
784 SecAccessControlRef ac = NULL;
785 CFDataRef enc = NULL;
786 CFErrorRef error = NULL;
787
788 NSDictionary* secretData = @{(id)kSecValueData : @"secret here"};
789
790 ac = SecAccessControlCreate(NULL, &error);
791 XCTAssertNotNil((__bridge id)ac, @"failed to create access control with error: %@", (__bridge id)error);
792 XCTAssertNil((__bridge id)error, @"encountered error attempting to create access control: %@", (__bridge id)error);
793 XCTAssertTrue(SecAccessControlSetProtection(ac, accessibility, &error), @"failed to set access control protection with error: %@", error);
794 XCTAssertNil((__bridge id)error, @"encountered error attempting to set access control protection: %@", (__bridge id)error);
795
796 XCTAssertTrue(ks_encrypt_data(KEYBAG_DEVICE, ac, NULL, (__bridge CFDictionaryRef)secretData, (__bridge CFDictionaryRef)@{}, NULL, &enc, true, &error), @"failed to encrypt data with error: %@", error);
797 XCTAssertTrue(enc != NULL, @"failed to get encrypted data from encryption function");
798 XCTAssertNil((__bridge id)error, @"encountered error attempting to encrypt data: %@", (__bridge id)error);
799 CFReleaseNull(ac);
800
801 return (__bridge_transfer NSData*)enc;
802 }
803
804 - (void)performMetadataDecryptionOfData:(NSData*)encryptedData verifyingAccessibility:(CFStringRef)accessibility
805 {
806 CFErrorRef error = NULL;
807 CFMutableDictionaryRef attributes = NULL;
808 uint32_t version = 0;
809
810 SecAccessControlRef ac = SecAccessControlCreate(NULL, &error);
811 XCTAssertNotNil((__bridge id)ac, @"failed to create access control with error: %@", (__bridge id)error);
812 XCTAssertNil((__bridge id)error, @"encountered error attempting to create access control: %@", (__bridge id)error);
813 XCTAssertTrue(SecAccessControlSetProtection(ac, accessibility, &error), @"failed to set access control protection with error: %@", error);
814 XCTAssertNil((__bridge id)error, @"encountered error attempting to set access control protection: %@", (__bridge id)error);
815
816 keyclass_t keyclass = 0;
817 XCTAssertTrue(ks_decrypt_data(KEYBAG_DEVICE, kAKSKeyOpDecrypt, &ac, NULL, (__bridge CFDataRef)encryptedData, NULL, NULL, &attributes, &version, false, &keyclass, &error), @"failed to decrypt data with error: %@", error);
818 XCTAssertNil((__bridge id)error, @"encountered error attempting to decrypt data: %@", (__bridge id)error);
819 XCTAssertEqual(keyclass & key_class_last, parse_keyclass(accessibility), @"failed to get back the keyclass from decryption");
820
821 CFReleaseNull(error);
822 }
823
824 - (void)performMetadataEncryptDecryptWithAccessibility:(CFStringRef)accessibility
825 {
826 NSData* encryptedData = [self performItemEncryptionWithAccessibility:accessibility];
827
828 [SecDbKeychainMetadataKeyStore resetSharedStore];
829
830 [self performMetadataDecryptionOfData:encryptedData verifyingAccessibility:accessibility];
831 }
832
833 - (void)testMetadataClassKeyDecryptionWithSimulatedAKSRolledKeys
834 {
835 self.simulateRolledAKSKey = YES;
836
837 [self performMetadataEncryptDecryptWithAccessibility:kSecAttrAccessibleWhenUnlocked];
838 XCTAssertEqual(self.keyclassUsedForAKSDecryption, key_class_ak | key_class_last + 1);
839
840 [self performMetadataEncryptDecryptWithAccessibility:kSecAttrAccessibleAfterFirstUnlock];
841 XCTAssertEqual(self.keyclassUsedForAKSDecryption, key_class_ck | key_class_last + 1);
842
843 #pragma clang diagnostic push
844 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
845 [self performMetadataEncryptDecryptWithAccessibility:kSecAttrAccessibleAlways];
846 XCTAssertEqual(self.keyclassUsedForAKSDecryption, key_class_dk | key_class_last + 1);
847 #pragma clang diagnostic pop
848
849 [self performMetadataEncryptDecryptWithAccessibility:kSecAttrAccessibleWhenUnlockedThisDeviceOnly];
850 XCTAssertEqual(self.keyclassUsedForAKSDecryption, key_class_aku | key_class_last + 1);
851
852 [self performMetadataEncryptDecryptWithAccessibility:kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly];
853 XCTAssertEqual(self.keyclassUsedForAKSDecryption, key_class_cku | key_class_last + 1);
854
855 #pragma clang diagnostic push
856 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
857 [self performMetadataEncryptDecryptWithAccessibility:kSecAttrAccessibleAlwaysThisDeviceOnly];
858 XCTAssertEqual(self.keyclassUsedForAKSDecryption, key_class_dku | key_class_last + 1);
859 #pragma clang diagnostic pop
860
861 [self performMetadataEncryptDecryptWithAccessibility:kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly];
862 XCTAssertEqual(self.keyclassUsedForAKSDecryption, key_class_akpu | key_class_last + 1);
863 }
864
865 - (void)testUpgradingMetadataKeyEntry
866 {
867 // first, force the creation of a metadata key
868 NSData* encryptedData = [self performItemEncryptionWithAccessibility:kSecAttrAccessibleWhenUnlocked];
869
870 // now let's jury-rig this metadata key to look like an old one with no actualKeyclass information
871 __block CFErrorRef error = NULL;
872 __block bool ok = true;
873 ok &= kc_with_dbt(true, &error, ^bool(SecDbConnectionRef dbt) {
874 NSString* sql = [NSString stringWithFormat:@"UPDATE metadatakeys SET actualKeyclass = %d WHERE keyclass = %d", 0, key_class_ak];
875 ok &= SecDbPrepare(dbt, (__bridge CFStringRef)sql, &error, ^(sqlite3_stmt* stmt) {
876 ok &= SecDbStep(dbt, stmt, &error, ^(bool* stop) {
877 // woohoo
878 });
879 });
880
881 return ok;
882 });
883
884 // now, let's simulate AKS rejecting the decryption, and see if we recover and also update the database
885 self.simulateRolledAKSKey = YES;
886 [SecDbKeychainMetadataKeyStore resetSharedStore];
887 [self performMetadataDecryptionOfData:encryptedData verifyingAccessibility:kSecAttrAccessibleWhenUnlocked];
888 }
889
890 @end
891
892 #endif