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>
 
  17 #include <mach/mach_time.h>
 
  21 #if SEC_OS_OSX_INCLUDES
 
  22 #include <Security/SecKeychain.h>
 
  26 fail(const char *fmt, ...) __printflike(1, 2) __attribute__((noreturn));
 
  30 fail(const char *fmt, ...)
 
  42  * Create item w/o data, try to make sure we end up in the OS X keychain
 
  46 CheckItemAddDeleteMaybeLegacyKeychainNoData(void)
 
  50     printf("[BEGIN] %s\n", __FUNCTION__);
 
  52     NSDictionary *query = @{
 
  53         (id)kSecClass : (id)kSecClassGenericPassword,
 
  54         (id)kSecAttrAccount : @"item-delete-me",
 
  55         (id)kSecAttrAccessible : (id)kSecAttrAccessibleAfterFirstUnlock,
 
  57     status = SecItemDelete((__bridge CFDictionaryRef)query);
 
  58     if (status != errSecSuccess && status != errSecItemNotFound)
 
  59         fail("cleanup item: %d", (int)status);
 
  62      * now check add notification
 
  65     status = SecItemAdd((__bridge CFDictionaryRef)query, NULL);
 
  66     if (status != errSecSuccess)
 
  67         fail("add item: %d: %s", (int)status, [[query description] UTF8String]);
 
  73     status = SecItemDelete((__bridge CFDictionaryRef)query);
 
  74     if (status != errSecSuccess)
 
  75         fail("cleanup2 item: %d", (int)status);
 
  78     printf("[PASS] %s\n", __FUNCTION__);
 
  83 CheckItemAddDeleteNoData(void)
 
  87     printf("[BEGIN] %s\n", __FUNCTION__);
 
  89     NSDictionary *query = @{
 
  90         (id)kSecClass : (id)kSecClassGenericPassword,
 
  91         (id)kSecAttrAccessGroup : @"keychain-test1",
 
  92         (id)kSecAttrAccount : @"item-delete-me",
 
  93         (id)kSecAttrAccessible : (id)kSecAttrAccessibleAfterFirstUnlock,
 
  95     status = SecItemDelete((__bridge CFDictionaryRef)query);
 
  96     if (status != errSecSuccess && status != errSecItemNotFound)
 
  97         fail("cleanup item: %d", (int)status);
 
 103     status = SecItemAdd((__bridge CFDictionaryRef)query, NULL);
 
 104     if (status != errSecSuccess)
 
 105         fail("add item: %d: %s", (int)status, [[query description] UTF8String]);
 
 111     status = SecItemDelete((__bridge CFDictionaryRef)query);
 
 112     if (status != errSecSuccess)
 
 113         fail("cleanup2 item: %d", (int)status);
 
 115     printf("[PASS] %s\n", __FUNCTION__);
 
 119 CheckItemUpdateAccessGroupGENP(void)
 
 123     printf("[BEGIN] %s\n", __FUNCTION__);
 
 125     NSDictionary *clean1 = @{
 
 126         (id)kSecClass : (id)kSecClassGenericPassword,
 
 127         (id)kSecAttrAccessGroup : @"keychain-test1",
 
 129     NSDictionary *clean2 = @{
 
 130         (id)kSecClass : (id)kSecClassGenericPassword,
 
 131         (id)kSecAttrAccessGroup : @"keychain-test2",
 
 134     (void)SecItemDelete((__bridge CFDictionaryRef)clean1);
 
 135     (void)SecItemDelete((__bridge CFDictionaryRef)clean2);
 
 141     NSDictionary *add = @{
 
 142         (id)kSecClass : (id)kSecClassGenericPassword,
 
 143         (id)kSecAttrAccessGroup : @"keychain-test1",
 
 144         (id)kSecAttrAccount : @"item-delete-me",
 
 145         (id)kSecAttrNoLegacy : (id)kCFBooleanTrue,
 
 146         (id)kSecAttrAccessible : (id)kSecAttrAccessibleAfterFirstUnlock,
 
 148     status = SecItemAdd((__bridge CFDictionaryRef)add, NULL);
 
 149     if (status != errSecSuccess)
 
 150         fail("add item: %d: %s", (int)status, [[add description] UTF8String]);
 
 153      * Update access group
 
 155     NSDictionary *query = @{
 
 156         (id)kSecClass : (id)kSecClassGenericPassword,
 
 157         (id)kSecAttrAccessGroup : @"keychain-test1",
 
 158         (id)kSecAttrAccount : @"item-delete-me",
 
 159         (id)kSecAttrNoLegacy : (id)kCFBooleanTrue,
 
 161     NSDictionary *modified = @{
 
 162         (id)kSecAttrAccessGroup : @"keychain-test2",
 
 165     status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)modified);
 
 166     if (status != errSecSuccess)
 
 167         fail("cleanup2 item: %d", (int)status);
 
 172     NSDictionary *check1 = @{
 
 173         (id)kSecClass : (id)kSecClassGenericPassword,
 
 174         (id)kSecAttrAccessGroup : @"keychain-test1",
 
 175         (id)kSecAttrAccount : @"item-delete-me",
 
 176         (id)kSecAttrNoLegacy : (id)kCFBooleanTrue,
 
 178     status = SecItemCopyMatching((__bridge CFDictionaryRef)check1, NULL);
 
 179     if (status != errSecItemNotFound)
 
 180         fail("check1 item: %d", (int)status);
 
 183     NSDictionary *check2 = @{
 
 184         (id)kSecClass : (id)kSecClassGenericPassword,
 
 185         (id)kSecAttrAccessGroup : @"keychain-test2",
 
 186         (id)kSecAttrAccount : @"item-delete-me",
 
 187         (id)kSecAttrNoLegacy : (id)kCFBooleanTrue,
 
 189     status = SecItemCopyMatching((__bridge CFDictionaryRef)check2, NULL);
 
 190     if (status != errSecSuccess)
 
 191         fail("check2 item: %d", (int)status);
 
 196     (void)SecItemDelete((__bridge CFDictionaryRef)clean1);
 
 197     (void)SecItemDelete((__bridge CFDictionaryRef)clean2);
 
 199     printf("[PASS] %s\n", __FUNCTION__);
 
 202 static NSString *certDataBase64 = @"\
 
 203 MIIEQjCCAyqgAwIBAgIJAJdFadWqNIfiMA0GCSqGSIb3DQEBBQUAMHMxCzAJBgNVBAYTAkNaMQ8wDQYD\
 
 204 VQQHEwZQcmFndWUxFTATBgNVBAoTDENvc21vcywgSW5jLjEXMBUGA1UEAxMOc3VuLmNvc21vcy5nb2Qx\
 
 205 IzAhBgkqhkiG9w0BCQEWFHRoaW5nQHN1bi5jb3Ntb3MuZ29kMB4XDTE2MDIyNjE0NTQ0OVoXDTE4MTEy\
 
 206 MjE0NTQ0OVowczELMAkGA1UEBhMCQ1oxDzANBgNVBAcTBlByYWd1ZTEVMBMGA1UEChMMQ29zbW9zLCBJ\
 
 207 bmMuMRcwFQYDVQQDEw5zdW4uY29zbW9zLmdvZDEjMCEGCSqGSIb3DQEJARYUdGhpbmdAc3VuLmNvc21v\
 
 208 cy5nb2QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5u9gnYEDzQIVu7yC40VcXTZ01D9CJ\
 
 209 oD/mH62tebEHEdfVPLWKeq+uAHnJ6fTIJQvksaISOxwiOosFjtI30mbe6LZ/oK22wYX+OUwKhAYjZQPy\
 
 210 RYfuaJe/52F0zmfUSJ+KTbUZrXbVVFma4xPfpg4bptvtGkFJWnufvEEHimOGmO5O69lXA0Hit1yLU0/A\
 
 211 MQrIMmZT8gb8LMZGPZearT90KhCbTHAxjcBfswZYeL8q3xuEVHXC7EMs6mq8IgZL7mzSBmrCfmBAIO0V\
 
 212 jW2kvmy0NFxkjIeHUShtYb11oYYyfHuz+1vr1y6FIoLmDejKVnwfcuNb545m26o+z/m9Lv9bAgMBAAGj\
 
 213 gdgwgdUwHQYDVR0OBBYEFGDdpPELS92xT+Hkh/7lcc+4G56VMIGlBgNVHSMEgZ0wgZqAFGDdpPELS92x\
 
 214 T+Hkh/7lcc+4G56VoXekdTBzMQswCQYDVQQGEwJDWjEPMA0GA1UEBxMGUHJhZ3VlMRUwEwYDVQQKEwxD\
 
 215 b3Ntb3MsIEluYy4xFzAVBgNVBAMTDnN1bi5jb3Ntb3MuZ29kMSMwIQYJKoZIhvcNAQkBFhR0aGluZ0Bz\
 
 216 dW4uY29zbW9zLmdvZIIJAJdFadWqNIfiMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAFYi\
 
 217 Zu/dfAMOrD51bYxP88Wu6iDGBe9nMG/0lkKgnX5JQKCxfxFMk875rfa+pljdUMOaPxegOXq1DrYmQB9O\
 
 218 /pHI+t7ozuWHRj2zKkVgMWAygNWDPcoqBEus53BdAgA644aPN2JvnE4NEPCllOMKftPoIWbd/5ZjCx3a\
 
 219 bCuxBdXq5YSmiEnOdGfKeXjeeEiIDgARb4tLgH5rkOpB1uH/ZCWn1hkiajBhrGhhPhpA0zbkZg2Ug+8g\
 
 220 XPlx1yQB1VOJkj2Z8dUEXCaRRijInCJ2eU+pgJvwLV7mxmSED7DEJ+b+opxJKYrsdKBU6RmYpPrDa+KC\
 
 221 /Yfu88P9hKKj0LmBiREA\
 
 224 static NSString *keyDataBase64 = @"\
 
 225 MIIEogIBAAKCAQEAubvYJ2BA80CFbu8guNFXF02dNQ/QiaA/5h+trXmxBxHX1Ty1inqvrgB5yen0yCUL\
 
 226 5LGiEjscIjqLBY7SN9Jm3ui2f6CttsGF/jlMCoQGI2UD8kWH7miXv+dhdM5n1Eifik21Ga121VRZmuMT\
 
 227 36YOG6bb7RpBSVp7n7xBB4pjhpjuTuvZVwNB4rdci1NPwDEKyDJmU/IG/CzGRj2Xmq0/dCoQm0xwMY3A\
 
 228 X7MGWHi/Kt8bhFR1wuxDLOpqvCIGS+5s0gZqwn5gQCDtFY1tpL5stDRcZIyHh1EobWG9daGGMnx7s/tb\
 
 229 69cuhSKC5g3oylZ8H3LjW+eOZtuqPs/5vS7/WwIDAQABAoIBAGcwmQAPdyZus3OVwa1NCUD2KyB+39KG\
 
 230 yNmWwgx+br9Jx4s+RnJghVh8BS4MIKZOBtSRaEUOuCvAMNrupZbD+8leq34vDDRcQpCizr+M6Egj6FRj\
 
 231 Ewl+7Mh+yeN2hbMoghL552MTv9D4Iyxteu4nuPDd/JQ3oQwbDFIL6mlBFtiBDUr9ndemmcJ0WKuzor6a\
 
 232 3rgsygLs8SPyMefwIKjh5rJZls+iv3AyVEoBdCbHBz0HKgLVE9ZNmY/gWqda2dzAcJxxMdafeNVwHovv\
 
 233 BtyyRGnA7Yikx2XT4WLgKfuUsYLnDWs4GdAa738uxPBfiddQNeRjN7jRT1GZIWCk0P29rMECgYEA8jWi\
 
 234 g1Dph+4VlESPOffTEt1aCYQQWtHs13Qex95HrXX/L49fs6cOE7pvBh7nVzaKwBnPRh5+3bCPsPmRVb7h\
 
 235 k/GreOriCjTZtyt2XGp8eIfstfirofB7c1lNBjT61BhgjJ8Moii5c2ksNIOOZnKtD53n47mf7hiarYkw\
 
 236 xFEgU6ECgYEAxE8Js3gIPOBjsSw47XHuvsjP880nZZx/oiQ4IeJb/0rkoDMVJjU69WQu1HTNNAnMg4/u\
 
 237 RXo31h+gDZOlE9t9vSXHdrn3at67KAVmoTbRknGxZ+8tYpRJpPj1hyufynBGcKwevv3eHJHnE5eDqbHx\
 
 238 ynZFkXemzT9aMy3R4CCFMXsCgYAYyZpnG/m6WohE0zthMFaeoJ6dSLGvyboWVqDrzXjCbMf/4wllRlxv\
 
 239 cm34T2NXjpJmlH2c7HQJVg9uiivwfYdyb5If3tHhP4VkdIM5dABnCWoVOWy/NvA7XtE+KF/fItuGqKRP\
 
 240 WCGaiRHoEeqZ23SQm5VmvdF7OXNi/R5LiQ3o4QKBgAGX8qg2TTrRR33ksgGbbyi1UJrWC3/TqWWTjbEY\
 
 241 uU51OS3jvEQ3ImdjjM3EtPW7LqHSxUhjGZjvYMk7bZefrIGgkOHx2IRRkotcn9ynKURbD+mcE249beuc\
 
 242 6cFTJVTrXGcFvqomPWtV895A2JzECQZvt1ja88uuu/i2YoHDQdGJAoGAL2TEgiMXiunb6PzYMMKKa+mx\
 
 243 mFnagF0Ek3UJ9ByXKoLz3HFEl7cADIkqyenXFsAER/ifMyCoZp/PDBd6ZkpqLTdH0jQ2Yo4SllLykoiZ\
 
 244 fBWMfjRu4iw9E0MbPB3blmtzfv53BtWKy0LUOlN4juvpqryA7TgaUlZkfMT+T1TC7xU=\
 
 248 static SecIdentityRef
 
 249 CreateTestIdentity(void)
 
 251     NSData *certData = [[NSData alloc] initWithBase64EncodedString:certDataBase64 options:0];
 
 252     SecCertificateRef cert = SecCertificateCreateWithData(kCFAllocatorDefault, (CFDataRef)certData);
 
 254         fail("create certificate from data");
 
 256     NSData *keyData = [[NSData alloc] initWithBase64EncodedString:keyDataBase64 options:0];
 
 257     NSDictionary *keyAttrs = @{
 
 258                                (id)kSecAttrKeyType: (id)kSecAttrKeyTypeRSA,
 
 259                                (id)kSecAttrKeySizeInBits: @2048,
 
 260                                (id)kSecAttrKeyClass: (id)kSecAttrKeyClassPrivate
 
 262     SecKeyRef privateKey = SecKeyCreateWithData((CFDataRef)keyData, (CFDictionaryRef)keyAttrs, NULL);
 
 263     if (privateKey == NULL)
 
 264         fail("create private key from data");
 
 266     // Create identity from certificate and private key.
 
 267     SecIdentityRef identity = SecIdentityCreate(kCFAllocatorDefault, cert, privateKey);
 
 268     CFRelease(privateKey);
 
 275 CheckIdentityItem(NSString *accessGroup, OSStatus expectedStatus)
 
 279     NSDictionary *check = @{
 
 280                              (id)kSecClass : (id)kSecClassIdentity,
 
 281                              (id)kSecAttrAccessGroup : accessGroup,
 
 282                              (id)kSecAttrLabel : @"item-delete-me",
 
 283                              (id)kSecAttrNoLegacy : (id)kCFBooleanTrue,
 
 285     status = SecItemCopyMatching((__bridge CFDictionaryRef)check, NULL);
 
 286     if (status != expectedStatus)
 
 287         fail("check %s for %d item: %d", [accessGroup UTF8String], (int)expectedStatus, (int)status);
 
 291 CheckItemUpdateAccessGroupIdentity(void)
 
 294     CFTypeRef ref = NULL;
 
 296     printf("[BEGIN] %s\n", __FUNCTION__);
 
 298     NSDictionary *clean1 = @{
 
 299         (id)kSecClass : (id)kSecClassIdentity,
 
 300         (id)kSecAttrAccessGroup : @"keychain-test1",
 
 302     NSDictionary *clean2 = @{
 
 303         (id)kSecClass : (id)kSecClassIdentity,
 
 304         (id)kSecAttrAccessGroup : @"keychain-test2",
 
 307     (void)SecItemDelete((__bridge CFDictionaryRef)clean1);
 
 308     (void)SecItemDelete((__bridge CFDictionaryRef)clean2);
 
 310     CheckIdentityItem(@"keychain-test1", errSecItemNotFound);
 
 311     CheckIdentityItem(@"keychain-test2", errSecItemNotFound);
 
 313     SecIdentityRef identity = CreateTestIdentity();
 
 314     if (identity == NULL)
 
 315         fail("create private key from data");
 
 322     NSDictionary *add = @{
 
 323         (id)kSecValueRef : (__bridge id)identity,
 
 324         (id)kSecAttrAccessGroup : @"keychain-test1",
 
 325         (id)kSecAttrLabel : @"item-delete-me",
 
 326         (id)kSecAttrAccessible : (id)kSecAttrAccessibleAfterFirstUnlock,
 
 327         (id)kSecAttrNoLegacy : (id)kCFBooleanTrue,
 
 328         (id)kSecReturnPersistentRef: (id)kCFBooleanTrue,
 
 330     status = SecItemAdd((__bridge CFDictionaryRef)add, &ref);
 
 331     if (status != errSecSuccess)
 
 332         fail("add item: %d: %s", (int)status, [[add description] UTF8String]);
 
 337     CheckIdentityItem(@"keychain-test1", errSecSuccess);
 
 338     CheckIdentityItem(@"keychain-test2", errSecItemNotFound);
 
 342      * Update access group
 
 344     NSDictionary *query = @{
 
 345         (id)kSecClass : (id)kSecClassIdentity,
 
 346         (id)kSecAttrAccessGroup : @"keychain-test1",
 
 347         (id)kSecAttrLabel : @"item-delete-me",
 
 348         (id)kSecAttrNoLegacy : (id)kCFBooleanTrue,
 
 350     NSDictionary *modified = @{
 
 351         (id)kSecAttrAccessGroup : @"keychain-test2",
 
 354     status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)modified);
 
 355     if (status != errSecSuccess)
 
 356         fail("cleanup2 item: %d", (int)status);
 
 362     CheckIdentityItem(@"keychain-test1", errSecItemNotFound);
 
 363     CheckIdentityItem(@"keychain-test2", errSecSuccess);
 
 368     CFDataRef data = NULL;
 
 370     NSDictionary *prefQuery = @{
 
 371         (id)kSecClass : (id)kSecClassIdentity,
 
 372         (id)kSecAttrAccessGroup : @"keychain-test2",
 
 373         (id)kSecAttrLabel : @"item-delete-me",
 
 374         (id)kSecAttrNoLegacy : (id)kCFBooleanTrue,
 
 375         (id)kSecReturnPersistentRef : (id)kCFBooleanTrue,
 
 377     status = SecItemCopyMatching((__bridge CFDictionaryRef)prefQuery, (CFTypeRef *)&data);
 
 378     if (status != errSecSuccess)
 
 379         fail("prefQuery item: %d", (int)status);
 
 382      * Update access group for identity
 
 384     NSDictionary *query2 = @{
 
 385         (id)kSecValuePersistentRef : (__bridge id)data,
 
 386         (id)kSecAttrNoLegacy : (id)kCFBooleanTrue,
 
 388     NSDictionary *modified2 = @{
 
 389         (id)kSecAttrAccessGroup : @"keychain-test1",
 
 392     status = SecItemUpdate((__bridge CFDictionaryRef)query2, (__bridge CFDictionaryRef)modified2);
 
 393     if (status != errSecInternal)
 
 394         fail("update identity with pref fails differntly: %d", (int)status);
 
 397     CheckIdentityItem(@"keychain-test1", errSecSuccess);
 
 398     CheckIdentityItem(@"keychain-test2", errSecItemNotFound);
 
 405     (void)SecItemDelete((__bridge CFDictionaryRef)clean1);
 
 406     (void)SecItemDelete((__bridge CFDictionaryRef)clean2);
 
 410     CheckIdentityItem(@"keychain-test1", errSecItemNotFound);
 
 411     CheckIdentityItem(@"keychain-test2", errSecItemNotFound);
 
 414     printf("[PASS] %s\n", __FUNCTION__);
 
 418 CheckFindIdentityByReference(void)
 
 421     CFDataRef pref = NULL, pref2 = NULL;
 
 423     printf("[BEGIN] %s\n", __FUNCTION__);
 
 428     NSDictionary *clean1 = @{
 
 429         (id)kSecClass : (id)kSecClassIdentity,
 
 430         (id)kSecAttrAccessGroup : @"keychain-test1",
 
 432     (void)SecItemDelete((__bridge CFDictionaryRef)clean1);
 
 437     SecIdentityRef identity = CreateTestIdentity();
 
 438     if (identity == NULL)
 
 439         fail("create private key from data");
 
 442     NSDictionary *add = @{
 
 443         (id)kSecValueRef : (__bridge id)identity,
 
 444         (id)kSecAttrAccessGroup : @"keychain-test1",
 
 445         (id)kSecAttrLabel : @"CheckItemReference",
 
 446         (id)kSecAttrAccessible : (id)kSecAttrAccessibleAfterFirstUnlock,
 
 447         (id)kSecAttrNoLegacy : (id)kCFBooleanTrue,
 
 448         (id)kSecReturnPersistentRef: (id)kCFBooleanTrue,
 
 450     status = SecItemAdd((__bridge CFDictionaryRef)add, (CFTypeRef *)&pref);
 
 451     if (status != errSecSuccess)
 
 452         fail("add item: %d: %s", (int)status, [[add description] UTF8String]);
 
 454     if (pref == NULL || CFGetTypeID(pref) != CFDataGetTypeID())
 
 455         fail("no pref returned");
 
 461     NSDictionary *query = @{
 
 462         (id)kSecValueRef : (__bridge id)identity,
 
 463         (id)kSecReturnPersistentRef: (id)kCFBooleanTrue,
 
 465     status = SecItemCopyMatching((CFDictionaryRef)query, (CFTypeRef *)&pref2);
 
 467         fail("SecItemCopyMatching: %d", (int)status);
 
 469     if (pref2 == NULL || CFGetTypeID(pref2) != CFDataGetTypeID())
 
 470         fail("no pref2 returned");
 
 473     if (!CFEqual(pref, pref2))
 
 474         fail("prefs not same");
 
 482     NSDictionary *query2 = @{
 
 483         (id)kSecClass : (id)kSecClassIdentity,
 
 484         (id)kSecAttrAccessGroup : @"keychain-test1",
 
 485         (id)kSecAttrLabel : @"CheckItemReference",
 
 486         (id)kSecReturnPersistentRef: (id)kCFBooleanTrue,
 
 488     status = SecItemCopyMatching((CFDictionaryRef)query2, (CFTypeRef *)&pref2);
 
 490         fail("SecItemCopyMatching: %d", (int)status);
 
 492     if (pref2 == NULL || CFGetTypeID(pref2) != CFDataGetTypeID())
 
 493         fail("no pref2 returned");
 
 496     if (!CFEqual(pref, pref2))
 
 497         fail("prefs not same");
 
 502      * Find by label + reference
 
 505     NSDictionary *query3 = @{
 
 506         (id)kSecAttrAccessGroup : @"keychain-test1",
 
 507         (id)kSecAttrLabel : @"CheckItemReference",
 
 508         (id)kSecValueRef : (__bridge id)identity,
 
 509         (id)kSecReturnPersistentRef: (id)kCFBooleanTrue,
 
 511     status = SecItemCopyMatching((CFDictionaryRef)query3, (CFTypeRef *)&pref2);
 
 513         fail("SecItemCopyMatching: %d", (int)status);
 
 515     if (pref2 == NULL || CFGetTypeID(pref2) != CFDataGetTypeID())
 
 516         fail("no pref2 returned");
 
 519     if (!CFEqual(pref, pref2))
 
 520         fail("prefs not same");
 
 534     printf("[PASS] %s\n", __FUNCTION__);
 
 538 timeDiff(uint64_t start, uint64_t stop)
 
 540     static uint64_t time_overhead_measured = 0;
 
 541     static double timebase_factor = 0;
 
 543     if (time_overhead_measured == 0) {
 
 544         uint64_t t0 = mach_absolute_time();
 
 545         time_overhead_measured = mach_absolute_time() - t0;
 
 547         struct mach_timebase_info timebase_info = {};
 
 548         mach_timebase_info(&timebase_info);
 
 549         timebase_factor = ((double)timebase_info.numer)/((double)timebase_info.denom);
 
 552     return ((stop - start - time_overhead_measured) * timebase_factor) / NSEC_PER_USEC;
 
 556 RunCopyPerfTest(NSString *name, NSDictionary *query)
 
 558     uint64_t start = mach_absolute_time();
 
 560     CFTypeRef result = NULL;
 
 562     status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
 
 563     uint64_t stop = mach_absolute_time();
 
 566         printf("SecItemCopyMatching failed with: %d\n", (int)status);
 
 574     uint64_t us = timeDiff(start, stop);
 
 576     puts([[NSString stringWithFormat:@"[RESULT_KEY] SecItemCopyMatching-%@\n[RESULT_VALUE] %lu\n",
 
 577            name, (unsigned long)us] UTF8String]);
 
 581 RunDigestPerfTest(NSString *name, NSString *itemClass, NSString *accessGroup, NSUInteger expectedCount)
 
 583     uint64_t start = mach_absolute_time();
 
 584     dispatch_semaphore_t sema = dispatch_semaphore_create(0);
 
 585     __block uint64_t stop;
 
 587     _SecItemFetchDigests(itemClass, accessGroup, ^(NSArray *items, NSError *error) {
 
 588         stop = mach_absolute_time();
 
 590             printf("_SecItemFetchDigests failed with: %ld\n", (long)error.code);
 
 594         dispatch_semaphore_signal(sema);
 
 596         if (expectedCount != [items count]) {
 
 597             printf("_SecItemFetchDigests didn't return expected items: %ld\n", (long)[items count]);
 
 602     dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
 
 605     uint64_t us = timeDiff(start, stop);
 
 607     puts([[NSString stringWithFormat:@"[RESULT_KEY] SecItemCopyDigest-%@\n[RESULT_VALUE] %lu\n",
 
 608            name, (unsigned long)us] UTF8String]);
 
 613 CheckItemPerformance(void)
 
 617     printf("[BEGIN] %s\n", __FUNCTION__);
 
 622     NSDictionary *clean1 = @{
 
 623         (id)kSecClass : (id)kSecClassGenericPassword,
 
 624         (id)kSecAttrService : @"service",
 
 625         (id)kSecAttrAccessGroup : @"keychain-test1",
 
 626         (id)kSecAttrNoLegacy : (id)kCFBooleanTrue,
 
 628     (void)SecItemDelete((__bridge CFDictionaryRef)clean1);
 
 630     NSData *data = [NSData dataWithBytes:"password" length:8];
 
 632     for (n = 0; n < 1000; n++) {
 
 633         NSDictionary *item = @{
 
 634             (id)kSecClass : (id)kSecClassGenericPassword,
 
 635             (id)kSecAttrAccount : [NSString stringWithFormat:@"account-%d", n],
 
 636             (id)kSecAttrService : @"service",
 
 637             (id)kSecAttrNoLegacy : (id)kCFBooleanTrue,
 
 638             (id)kSecAttrAccessGroup : @"keychain-test1",
 
 639             (id)kSecAttrNoLegacy : (id)kCFBooleanTrue,
 
 640             (id)kSecValueData : data,
 
 642         SecItemAdd((__bridge CFDictionaryRef)item, NULL);
 
 646     RunCopyPerfTest(@"FindOneItemLimit", @{
 
 647         (id)kSecClass : (id)kSecClassGenericPassword,
 
 648         (id)kSecAttrService : @"service",
 
 649         (id)kSecMatchLimit : (id)kSecMatchLimitOne,
 
 651     RunCopyPerfTest(@"FindOneItemUnique", @{
 
 652         (id)kSecClass : (id)kSecClassGenericPassword,
 
 653         (id)kSecAttrAccount : @"account-0",
 
 654         (id)kSecAttrService : @"service",
 
 655         (id)kSecMatchLimit : (id)kSecMatchLimitAll,
 
 657     RunCopyPerfTest(@"Find1000Items", @{
 
 658         (id)kSecClass : (id)kSecClassGenericPassword,
 
 659         (id)kSecAttrService : @"service",
 
 660         (id)kSecMatchLimit : (id)kSecMatchLimitAll,
 
 662     RunDigestPerfTest(@"Digest1000Items", (id)kSecClassGenericPassword, @"keychain-test1", 1000);
 
 663     RunCopyPerfTest(@"GetAttrOneItemUnique", @{
 
 664         (id)kSecClass : (id)kSecClassGenericPassword,
 
 665         (id)kSecAttrAccount : @"account-0",
 
 666         (id)kSecAttrService : @"service",
 
 667         (id)kSecReturnAttributes : (id)kCFBooleanTrue,
 
 668         (id)kSecMatchLimit : (id)kSecMatchLimitAll,
 
 670     RunCopyPerfTest(@"GetData1000Items", @{
 
 671         (id)kSecClass : (id)kSecClassGenericPassword,
 
 672         (id)kSecAttrService : @"service",
 
 673         (id)kSecReturnData : (id)kCFBooleanTrue,
 
 674         (id)kSecMatchLimit : (id)kSecMatchLimitAll,
 
 676     RunCopyPerfTest(@"GetDataOneItemUnique", @{
 
 677         (id)kSecClass : (id)kSecClassGenericPassword,
 
 678         (id)kSecAttrAccount : @"account-0",
 
 679         (id)kSecAttrService : @"service",
 
 680         (id)kSecReturnData : (id)kCFBooleanTrue,
 
 681         (id)kSecMatchLimit : (id)kSecMatchLimitAll,
 
 683     RunCopyPerfTest(@"GetDataAttrOneItemUnique", @{
 
 684         (id)kSecClass : (id)kSecClassGenericPassword,
 
 685         (id)kSecAttrAccount : @"account-0",
 
 686         (id)kSecAttrService : @"service",
 
 687         (id)kSecReturnData : (id)kCFBooleanTrue,
 
 688         (id)kSecReturnAttributes : (id)kCFBooleanTrue,
 
 689         (id)kSecMatchLimit : (id)kSecMatchLimitAll,
 
 691 #if TARGET_OS_IPHONE /* macOS doesn't support fetching data for more then one item */
 
 692     RunCopyPerfTest(@"GetData1000Items", @{
 
 693         (id)kSecClass : (id)kSecClassGenericPassword,
 
 694         (id)kSecAttrService : @"service",
 
 695         (id)kSecReturnData : (id)kCFBooleanTrue,
 
 696         (id)kSecMatchLimit : (id)kSecMatchLimitAll,
 
 698     RunCopyPerfTest(@"GetDataAttr1000Items", @{
 
 699         (id)kSecClass : (id)kSecClassGenericPassword,
 
 700         (id)kSecAttrService : @"service",
 
 701         (id)kSecReturnData : (id)kCFBooleanTrue,
 
 702         (id)kSecReturnAttributes : (id)kCFBooleanTrue,
 
 703         (id)kSecMatchLimit : (id)kSecMatchLimitAll,
 
 707     (void)SecItemDelete((__bridge CFDictionaryRef)clean1);
 
 710     printf("[PASS] %s\n", __FUNCTION__);
 
 714 main(int argc, const char ** argv)
 
 716     printf("[TEST] secitemfunctionality\n");
 
 719     char *user = getenv("USER");
 
 720     if (user && strcmp("bats", user) == 0) {
 
 721         (void)SecKeychainUnlock(NULL, 4, "bats", true);
 
 724     CheckItemPerformance();
 
 726     CheckFindIdentityByReference();
 
 728     CheckItemAddDeleteMaybeLegacyKeychainNoData();
 
 729     CheckItemAddDeleteNoData();
 
 730     CheckItemUpdateAccessGroupGENP();
 
 731     CheckItemUpdateAccessGroupIdentity();
 
 733     printf("[SUMMARY]\n");
 
 734     printf("test completed\n");