2 // Copyright 2016 Apple. All rights reserved.
6 * This is to fool os services to not provide the Keychain manager
7 * interface tht doens't work since we don't have unified headers
8 * between iOS and OS X. rdar://23405418/
10 #define __KEYCHAINCORE__ 1
12 #include <Foundation/Foundation.h>
13 #include <Security/Security.h>
14 #include <Security/SecItemPriv.h>
15 #include <Security/SecBasePriv.h>
16 #include <Security/SecIdentityPriv.h>
22 * Becuase this file uses the iOS headers and we have no unified the headers
23 * yet, its not possible to include <Security/SecKeychain.h> here because
24 * of missing type. Pull in prototype needed.
26 extern OSStatus SecKeychainUnlock(CFTypeRef keychain, UInt32 passwordLength, const void *password, Boolean usePassword);
30 fail(const char *fmt, ...) __printflike(1, 2) __attribute__((noreturn));
34 fail(const char *fmt, ...)
44 * Create item w/o data, try to make sure we end up in the OS X keychain
48 CheckItemAddDeleteMaybeLegacyKeychainNoData(void)
52 printf("[TEST] %s\n", __FUNCTION__);
54 NSDictionary *query = @{
55 (id)kSecClass : (id)kSecClassGenericPassword,
56 (id)kSecAttrAccount : @"item-delete-me",
57 (id)kSecAttrAccessible : (id)kSecAttrAccessibleAfterFirstUnlock,
59 status = SecItemDelete((__bridge CFDictionaryRef)query);
60 if (status != errSecSuccess && status != errSecItemNotFound)
61 fail("cleanup item: %d", (int)status);
64 * now check add notification
67 status = SecItemAdd((__bridge CFDictionaryRef)query, NULL);
68 if (status != errSecSuccess)
69 fail("add item: %d: %s", (int)status, [[query description] UTF8String]);
75 status = SecItemDelete((__bridge CFDictionaryRef)query);
76 if (status != errSecSuccess)
77 fail("cleanup2 item: %d", (int)status);
80 printf("[PASS] %s\n", __FUNCTION__);
85 CheckItemAddDeleteNoData(void)
89 printf("[TEST] %s\n", __FUNCTION__);
91 NSDictionary *query = @{
92 (id)kSecClass : (id)kSecClassGenericPassword,
93 (id)kSecAttrAccessGroup : @"keychain-test1",
94 (id)kSecAttrAccount : @"item-delete-me",
95 (id)kSecAttrAccessible : (id)kSecAttrAccessibleAfterFirstUnlock,
97 status = SecItemDelete((__bridge CFDictionaryRef)query);
98 if (status != errSecSuccess && status != errSecItemNotFound)
99 fail("cleanup item: %d", (int)status);
105 status = SecItemAdd((__bridge CFDictionaryRef)query, NULL);
106 if (status != errSecSuccess)
107 fail("add item: %d: %s", (int)status, [[query description] UTF8String]);
113 status = SecItemDelete((__bridge CFDictionaryRef)query);
114 if (status != errSecSuccess)
115 fail("cleanup2 item: %d", (int)status);
117 printf("[PASS] %s\n", __FUNCTION__);
121 CheckItemUpdateAccessGroupGENP(void)
125 printf("[TEST] %s\n", __FUNCTION__);
127 NSDictionary *clean1 = @{
128 (id)kSecClass : (id)kSecClassGenericPassword,
129 (id)kSecAttrAccessGroup : @"keychain-test1",
131 NSDictionary *clean2 = @{
132 (id)kSecClass : (id)kSecClassGenericPassword,
133 (id)kSecAttrAccessGroup : @"keychain-test2",
136 (void)SecItemDelete((__bridge CFDictionaryRef)clean1);
137 (void)SecItemDelete((__bridge CFDictionaryRef)clean2);
143 NSDictionary *add = @{
144 (id)kSecClass : (id)kSecClassGenericPassword,
145 (id)kSecAttrAccessGroup : @"keychain-test1",
146 (id)kSecAttrAccount : @"item-delete-me",
147 (id)kSecAttrNoLegacy : (id)kCFBooleanTrue,
148 (id)kSecAttrAccessible : (id)kSecAttrAccessibleAfterFirstUnlock,
150 status = SecItemAdd((__bridge CFDictionaryRef)add, NULL);
151 if (status != errSecSuccess)
152 fail("add item: %d: %s", (int)status, [[add description] UTF8String]);
155 * Update access group
157 NSDictionary *query = @{
158 (id)kSecClass : (id)kSecClassGenericPassword,
159 (id)kSecAttrAccessGroup : @"keychain-test1",
160 (id)kSecAttrAccount : @"item-delete-me",
161 (id)kSecAttrNoLegacy : (id)kCFBooleanTrue,
163 NSDictionary *modified = @{
164 (id)kSecAttrAccessGroup : @"keychain-test2",
167 status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)modified);
168 if (status != errSecSuccess)
169 fail("cleanup2 item: %d", (int)status);
174 NSDictionary *check1 = @{
175 (id)kSecClass : (id)kSecClassGenericPassword,
176 (id)kSecAttrAccessGroup : @"keychain-test1",
177 (id)kSecAttrAccount : @"item-delete-me",
178 (id)kSecAttrNoLegacy : (id)kCFBooleanTrue,
180 status = SecItemCopyMatching((__bridge CFDictionaryRef)check1, NULL);
181 if (status != errSecItemNotFound)
182 fail("check1 item: %d", (int)status);
185 NSDictionary *check2 = @{
186 (id)kSecClass : (id)kSecClassGenericPassword,
187 (id)kSecAttrAccessGroup : @"keychain-test2",
188 (id)kSecAttrAccount : @"item-delete-me",
189 (id)kSecAttrNoLegacy : (id)kCFBooleanTrue,
191 status = SecItemCopyMatching((__bridge CFDictionaryRef)check2, NULL);
192 if (status != errSecSuccess)
193 fail("check2 item: %d", (int)status);
198 (void)SecItemDelete((__bridge CFDictionaryRef)clean1);
199 (void)SecItemDelete((__bridge CFDictionaryRef)clean2);
201 printf("[PASS] %s\n", __FUNCTION__);
204 static NSString *certDataBase64 = @"\
205 MIIEQjCCAyqgAwIBAgIJAJdFadWqNIfiMA0GCSqGSIb3DQEBBQUAMHMxCzAJBgNVBAYTAkNaMQ8wDQYD\
206 VQQHEwZQcmFndWUxFTATBgNVBAoTDENvc21vcywgSW5jLjEXMBUGA1UEAxMOc3VuLmNvc21vcy5nb2Qx\
207 IzAhBgkqhkiG9w0BCQEWFHRoaW5nQHN1bi5jb3Ntb3MuZ29kMB4XDTE2MDIyNjE0NTQ0OVoXDTE4MTEy\
208 MjE0NTQ0OVowczELMAkGA1UEBhMCQ1oxDzANBgNVBAcTBlByYWd1ZTEVMBMGA1UEChMMQ29zbW9zLCBJ\
209 bmMuMRcwFQYDVQQDEw5zdW4uY29zbW9zLmdvZDEjMCEGCSqGSIb3DQEJARYUdGhpbmdAc3VuLmNvc21v\
210 cy5nb2QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5u9gnYEDzQIVu7yC40VcXTZ01D9CJ\
211 oD/mH62tebEHEdfVPLWKeq+uAHnJ6fTIJQvksaISOxwiOosFjtI30mbe6LZ/oK22wYX+OUwKhAYjZQPy\
212 RYfuaJe/52F0zmfUSJ+KTbUZrXbVVFma4xPfpg4bptvtGkFJWnufvEEHimOGmO5O69lXA0Hit1yLU0/A\
213 MQrIMmZT8gb8LMZGPZearT90KhCbTHAxjcBfswZYeL8q3xuEVHXC7EMs6mq8IgZL7mzSBmrCfmBAIO0V\
214 jW2kvmy0NFxkjIeHUShtYb11oYYyfHuz+1vr1y6FIoLmDejKVnwfcuNb545m26o+z/m9Lv9bAgMBAAGj\
215 gdgwgdUwHQYDVR0OBBYEFGDdpPELS92xT+Hkh/7lcc+4G56VMIGlBgNVHSMEgZ0wgZqAFGDdpPELS92x\
216 T+Hkh/7lcc+4G56VoXekdTBzMQswCQYDVQQGEwJDWjEPMA0GA1UEBxMGUHJhZ3VlMRUwEwYDVQQKEwxD\
217 b3Ntb3MsIEluYy4xFzAVBgNVBAMTDnN1bi5jb3Ntb3MuZ29kMSMwIQYJKoZIhvcNAQkBFhR0aGluZ0Bz\
218 dW4uY29zbW9zLmdvZIIJAJdFadWqNIfiMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAFYi\
219 Zu/dfAMOrD51bYxP88Wu6iDGBe9nMG/0lkKgnX5JQKCxfxFMk875rfa+pljdUMOaPxegOXq1DrYmQB9O\
220 /pHI+t7ozuWHRj2zKkVgMWAygNWDPcoqBEus53BdAgA644aPN2JvnE4NEPCllOMKftPoIWbd/5ZjCx3a\
221 bCuxBdXq5YSmiEnOdGfKeXjeeEiIDgARb4tLgH5rkOpB1uH/ZCWn1hkiajBhrGhhPhpA0zbkZg2Ug+8g\
222 XPlx1yQB1VOJkj2Z8dUEXCaRRijInCJ2eU+pgJvwLV7mxmSED7DEJ+b+opxJKYrsdKBU6RmYpPrDa+KC\
223 /Yfu88P9hKKj0LmBiREA\
226 static NSString *keyDataBase64 = @"\
227 MIIEogIBAAKCAQEAubvYJ2BA80CFbu8guNFXF02dNQ/QiaA/5h+trXmxBxHX1Ty1inqvrgB5yen0yCUL\
228 5LGiEjscIjqLBY7SN9Jm3ui2f6CttsGF/jlMCoQGI2UD8kWH7miXv+dhdM5n1Eifik21Ga121VRZmuMT\
229 36YOG6bb7RpBSVp7n7xBB4pjhpjuTuvZVwNB4rdci1NPwDEKyDJmU/IG/CzGRj2Xmq0/dCoQm0xwMY3A\
230 X7MGWHi/Kt8bhFR1wuxDLOpqvCIGS+5s0gZqwn5gQCDtFY1tpL5stDRcZIyHh1EobWG9daGGMnx7s/tb\
231 69cuhSKC5g3oylZ8H3LjW+eOZtuqPs/5vS7/WwIDAQABAoIBAGcwmQAPdyZus3OVwa1NCUD2KyB+39KG\
232 yNmWwgx+br9Jx4s+RnJghVh8BS4MIKZOBtSRaEUOuCvAMNrupZbD+8leq34vDDRcQpCizr+M6Egj6FRj\
233 Ewl+7Mh+yeN2hbMoghL552MTv9D4Iyxteu4nuPDd/JQ3oQwbDFIL6mlBFtiBDUr9ndemmcJ0WKuzor6a\
234 3rgsygLs8SPyMefwIKjh5rJZls+iv3AyVEoBdCbHBz0HKgLVE9ZNmY/gWqda2dzAcJxxMdafeNVwHovv\
235 BtyyRGnA7Yikx2XT4WLgKfuUsYLnDWs4GdAa738uxPBfiddQNeRjN7jRT1GZIWCk0P29rMECgYEA8jWi\
236 g1Dph+4VlESPOffTEt1aCYQQWtHs13Qex95HrXX/L49fs6cOE7pvBh7nVzaKwBnPRh5+3bCPsPmRVb7h\
237 k/GreOriCjTZtyt2XGp8eIfstfirofB7c1lNBjT61BhgjJ8Moii5c2ksNIOOZnKtD53n47mf7hiarYkw\
238 xFEgU6ECgYEAxE8Js3gIPOBjsSw47XHuvsjP880nZZx/oiQ4IeJb/0rkoDMVJjU69WQu1HTNNAnMg4/u\
239 RXo31h+gDZOlE9t9vSXHdrn3at67KAVmoTbRknGxZ+8tYpRJpPj1hyufynBGcKwevv3eHJHnE5eDqbHx\
240 ynZFkXemzT9aMy3R4CCFMXsCgYAYyZpnG/m6WohE0zthMFaeoJ6dSLGvyboWVqDrzXjCbMf/4wllRlxv\
241 cm34T2NXjpJmlH2c7HQJVg9uiivwfYdyb5If3tHhP4VkdIM5dABnCWoVOWy/NvA7XtE+KF/fItuGqKRP\
242 WCGaiRHoEeqZ23SQm5VmvdF7OXNi/R5LiQ3o4QKBgAGX8qg2TTrRR33ksgGbbyi1UJrWC3/TqWWTjbEY\
243 uU51OS3jvEQ3ImdjjM3EtPW7LqHSxUhjGZjvYMk7bZefrIGgkOHx2IRRkotcn9ynKURbD+mcE249beuc\
244 6cFTJVTrXGcFvqomPWtV895A2JzECQZvt1ja88uuu/i2YoHDQdGJAoGAL2TEgiMXiunb6PzYMMKKa+mx\
245 mFnagF0Ek3UJ9ByXKoLz3HFEl7cADIkqyenXFsAER/ifMyCoZp/PDBd6ZkpqLTdH0jQ2Yo4SllLykoiZ\
246 fBWMfjRu4iw9E0MbPB3blmtzfv53BtWKy0LUOlN4juvpqryA7TgaUlZkfMT+T1TC7xU=\
250 static SecIdentityRef
251 CreateTestIdentity(void)
253 NSData *certData = [[NSData alloc] initWithBase64EncodedString:certDataBase64 options:0];
254 SecCertificateRef cert = SecCertificateCreateWithData(kCFAllocatorDefault, (CFDataRef)certData);
256 fail("create certificate from data");
258 NSData *keyData = [[NSData alloc] initWithBase64EncodedString:keyDataBase64 options:0];
259 NSDictionary *keyAttrs = @{
260 (id)kSecAttrKeyType: (id)kSecAttrKeyTypeRSA,
261 (id)kSecAttrKeySizeInBits: @2048,
262 (id)kSecAttrKeyClass: (id)kSecAttrKeyClassPrivate
264 SecKeyRef privateKey = SecKeyCreateWithData((CFDataRef)keyData, (CFDictionaryRef)keyAttrs, NULL);
265 if (privateKey == NULL)
266 fail("create private key from data");
268 // Create identity from certificate and private key.
269 SecIdentityRef identity = SecIdentityCreate(kCFAllocatorDefault, cert, privateKey);
270 CFRelease(privateKey);
277 CheckIdentityItem(NSString *accessGroup, OSStatus expectedStatus)
281 NSDictionary *check = @{
282 (id)kSecClass : (id)kSecClassIdentity,
283 (id)kSecAttrAccessGroup : accessGroup,
284 (id)kSecAttrLabel : @"item-delete-me",
285 (id)kSecAttrNoLegacy : (id)kCFBooleanTrue,
287 status = SecItemCopyMatching((__bridge CFDictionaryRef)check, NULL);
288 if (status != expectedStatus)
289 fail("check %s for %d item: %d", [accessGroup UTF8String], (int)expectedStatus, (int)status);
293 CheckItemUpdateAccessGroupIdentity(void)
296 CFTypeRef ref = NULL;
298 printf("[TEST] %s\n", __FUNCTION__);
300 NSDictionary *clean1 = @{
301 (id)kSecClass : (id)kSecClassIdentity,
302 (id)kSecAttrAccessGroup : @"keychain-test1",
304 NSDictionary *clean2 = @{
305 (id)kSecClass : (id)kSecClassIdentity,
306 (id)kSecAttrAccessGroup : @"keychain-test2",
309 (void)SecItemDelete((__bridge CFDictionaryRef)clean1);
310 (void)SecItemDelete((__bridge CFDictionaryRef)clean2);
312 CheckIdentityItem(@"keychain-test1", errSecItemNotFound);
313 CheckIdentityItem(@"keychain-test2", errSecItemNotFound);
315 SecIdentityRef identity = CreateTestIdentity();
316 if (identity == NULL)
317 fail("create private key from data");
324 NSDictionary *add = @{
325 (id)kSecValueRef : (__bridge id)identity,
326 (id)kSecAttrAccessGroup : @"keychain-test1",
327 (id)kSecAttrLabel : @"item-delete-me",
328 (id)kSecAttrAccessible : (id)kSecAttrAccessibleAfterFirstUnlock,
329 (id)kSecAttrNoLegacy : (id)kCFBooleanTrue,
330 (id)kSecReturnPersistentRef: (id)kCFBooleanTrue,
332 status = SecItemAdd((__bridge CFDictionaryRef)add, &ref);
333 if (status != errSecSuccess)
334 fail("add item: %d: %s", (int)status, [[add description] UTF8String]);
339 CheckIdentityItem(@"keychain-test1", errSecSuccess);
340 CheckIdentityItem(@"keychain-test2", errSecItemNotFound);
344 * Update access group
346 NSDictionary *query = @{
347 (id)kSecClass : (id)kSecClassIdentity,
348 (id)kSecAttrAccessGroup : @"keychain-test1",
349 (id)kSecAttrLabel : @"item-delete-me",
350 (id)kSecAttrNoLegacy : (id)kCFBooleanTrue,
352 NSDictionary *modified = @{
353 (id)kSecAttrAccessGroup : @"keychain-test2",
356 status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)modified);
357 if (status != errSecSuccess)
358 fail("cleanup2 item: %d", (int)status);
364 CheckIdentityItem(@"keychain-test1", errSecItemNotFound);
365 CheckIdentityItem(@"keychain-test2", errSecSuccess);
370 CFDataRef data = NULL;
372 NSDictionary *prefQuery = @{
373 (id)kSecClass : (id)kSecClassIdentity,
374 (id)kSecAttrAccessGroup : @"keychain-test2",
375 (id)kSecAttrLabel : @"item-delete-me",
376 (id)kSecAttrNoLegacy : (id)kCFBooleanTrue,
377 (id)kSecReturnPersistentRef : (id)kCFBooleanTrue,
379 status = SecItemCopyMatching((__bridge CFDictionaryRef)prefQuery, (CFTypeRef *)&data);
380 if (status != errSecSuccess)
381 fail("prefQuery item: %d", (int)status);
384 * Update access group for identity
386 NSDictionary *query2 = @{
387 (id)kSecValuePersistentRef : (__bridge id)data,
388 (id)kSecAttrNoLegacy : (id)kCFBooleanTrue,
390 NSDictionary *modified2 = @{
391 (id)kSecAttrAccessGroup : @"keychain-test1",
394 status = SecItemUpdate((__bridge CFDictionaryRef)query2, (__bridge CFDictionaryRef)modified2);
395 if (status != errSecInternal)
396 fail("update identity with pref fails differntly: %d", (int)status);
399 CheckIdentityItem(@"keychain-test1", errSecSuccess);
400 CheckIdentityItem(@"keychain-test2", errSecItemNotFound);
407 (void)SecItemDelete((__bridge CFDictionaryRef)clean1);
408 (void)SecItemDelete((__bridge CFDictionaryRef)clean2);
412 CheckIdentityItem(@"keychain-test1", errSecItemNotFound);
413 CheckIdentityItem(@"keychain-test2", errSecItemNotFound);
416 printf("[PASS] %s\n", __FUNCTION__);
420 CheckFindIdentityByReference(void)
423 CFDataRef pref = NULL, pref2 = NULL;
425 printf("[TEST] %s\n", __FUNCTION__);
430 NSDictionary *clean1 = @{
431 (id)kSecClass : (id)kSecClassIdentity,
432 (id)kSecAttrAccessGroup : @"keychain-test1",
434 (void)SecItemDelete((__bridge CFDictionaryRef)clean1);
439 SecIdentityRef identity = CreateTestIdentity();
440 if (identity == NULL)
441 fail("create private key from data");
444 NSDictionary *add = @{
445 (id)kSecValueRef : (__bridge id)identity,
446 (id)kSecAttrAccessGroup : @"keychain-test1",
447 (id)kSecAttrLabel : @"CheckItemReference",
448 (id)kSecAttrAccessible : (id)kSecAttrAccessibleAfterFirstUnlock,
449 (id)kSecAttrNoLegacy : (id)kCFBooleanTrue,
450 (id)kSecReturnPersistentRef: (id)kCFBooleanTrue,
452 status = SecItemAdd((__bridge CFDictionaryRef)add, (CFTypeRef *)&pref);
453 if (status != errSecSuccess)
454 fail("add item: %d: %s", (int)status, [[add description] UTF8String]);
456 if (pref == NULL || CFGetTypeID(pref) != CFDataGetTypeID())
457 fail("no pref returned");
463 NSDictionary *query = @{
464 (id)kSecValueRef : (__bridge id)identity,
465 (id)kSecReturnPersistentRef: (id)kCFBooleanTrue,
467 status = SecItemCopyMatching((CFDictionaryRef)query, (CFTypeRef *)&pref2);
469 fail("SecItemCopyMatching: %d", (int)status);
471 if (pref2 == NULL || CFGetTypeID(pref2) != CFDataGetTypeID())
472 fail("no pref2 returned");
475 if (!CFEqual(pref, pref2))
476 fail("prefs not same");
484 NSDictionary *query2 = @{
485 (id)kSecClass : (id)kSecClassIdentity,
486 (id)kSecAttrAccessGroup : @"keychain-test1",
487 (id)kSecAttrLabel : @"CheckItemReference",
488 (id)kSecReturnPersistentRef: (id)kCFBooleanTrue,
490 status = SecItemCopyMatching((CFDictionaryRef)query2, (CFTypeRef *)&pref2);
492 fail("SecItemCopyMatching: %d", (int)status);
494 if (pref2 == NULL || CFGetTypeID(pref2) != CFDataGetTypeID())
495 fail("no pref2 returned");
498 if (!CFEqual(pref, pref2))
499 fail("prefs not same");
504 * Find by label + reference
507 NSDictionary *query3 = @{
508 (id)kSecAttrAccessGroup : @"keychain-test1",
509 (id)kSecAttrLabel : @"CheckItemReference",
510 (id)kSecValueRef : (__bridge id)identity,
511 (id)kSecReturnPersistentRef: (id)kCFBooleanTrue,
513 status = SecItemCopyMatching((CFDictionaryRef)query3, (CFTypeRef *)&pref2);
515 fail("SecItemCopyMatching: %d", (int)status);
517 if (pref2 == NULL || CFGetTypeID(pref2) != CFDataGetTypeID())
518 fail("no pref2 returned");
521 if (!CFEqual(pref, pref2))
522 fail("prefs not same");
532 printf("[PASS] %s\n", __FUNCTION__);
536 main(int argc, const char ** argv)
538 #if !TARGET_OS_IPHONE
539 char *user = getenv("USER");
540 if (user && strcmp("bats", user) == 0) {
541 (void)SecKeychainUnlock(NULL, 4, "bats", true);
544 CheckFindIdentityByReference();
546 if (random() == 17) {
547 CheckItemAddDeleteMaybeLegacyKeychainNoData();
548 CheckItemAddDeleteNoData();
549 CheckItemUpdateAccessGroupGENP();
550 CheckItemUpdateAccessGroupIdentity();