secitemfunctionality.m
1 // 2 // Copyright 2016 Apple. All rights reserved. 3 // 4 5 /* 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/ 9 */ 10 #define __KEYCHAINCORE__ 1 11 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> 18 #include <err.h> 19 #include <strings.h> 20 21 #if SEC_OS_OSX_INCLUDES 22 #include <Security/SecKeychain.h> 23 #endif 24 25 static void 26 fail(const char *fmt, ...) __printflike(1, 2) __attribute__((noreturn)); 27 28 29 static void 30 fail(const char *fmt, ...) 31 { 32 va_list ap; 33 printf("[FAIL]\n"); 34 fflush(stdout); 35 36 va_start(ap, fmt); 37 verrx(1, fmt, ap); 38 va_end(ap); 39 } 40 41 #if 0 42 /* 43 * Create item w/o data, try to make sure we end up in the OS X keychain 44 */ 45 46 static void 47 CheckItemAddDeleteMaybeLegacyKeychainNoData(void) 48 { 49 OSStatus status; 50 51 printf("[BEGIN] %s\n", __FUNCTION__); 52 53 NSDictionary *query = @{ 54 (id)kSecClass : (id)kSecClassGenericPassword, 55 (id)kSecAttrAccount : @"item-delete-me", 56 (id)kSecAttrAccessible : (id)kSecAttrAccessibleAfterFirstUnlock, 57 }; 58 status = SecItemDelete((__bridge CFDictionaryRef)query); 59 if (status != errSecSuccess && status != errSecItemNotFound) 60 fail("cleanup item: %d", (int)status); 61 62 /* 63 * now check add notification 64 */ 65 66 status = SecItemAdd((__bridge CFDictionaryRef)query, NULL); 67 if (status != errSecSuccess) 68 fail("add item: %d: %s", (int)status, [[query description] UTF8String]); 69 70 /* 71 * clean up 72 */ 73 74 status = SecItemDelete((__bridge CFDictionaryRef)query); 75 if (status != errSecSuccess) 76 fail("cleanup2 item: %d", (int)status); 77 78 79 printf("[PASS] %s\n", __FUNCTION__); 80 81 } 82 #endif 83 84 static void 85 CheckItemAddDeleteNoData(void) 86 { 87 OSStatus status; 88 89 printf("[BEGIN] %s\n", __FUNCTION__); 90 91 NSDictionary *query = @{ 92 (id)kSecClass : (id)kSecClassGenericPassword, 93 (id)kSecAttrAccessGroup : @"keychain-test1", 94 (id)kSecAttrAccount : @"item-delete-me", 95 (id)kSecAttrAccessible : (id)kSecAttrAccessibleAfterFirstUnlock, 96 (id)kSecUseDataProtectionKeychain: @YES, 97 }; 98 status = SecItemDelete((__bridge CFDictionaryRef)query); 99 if (status != errSecSuccess && status != errSecItemNotFound) 100 fail("cleanup item: %d", (int)status); 101 102 /* 103 * Add item 104 */ 105 106 status = SecItemAdd((__bridge CFDictionaryRef)query, NULL); 107 if (status != errSecSuccess) 108 fail("add item: %d: %s", (int)status, [[query description] UTF8String]); 109 110 /* 111 * clean up 112 */ 113 114 status = SecItemDelete((__bridge CFDictionaryRef)query); 115 if (status != errSecSuccess) 116 fail("cleanup2 item: %d", (int)status); 117 118 printf("[PASS] %s\n", __FUNCTION__); 119 } 120 121 static void 122 CheckItemUpdateAccessGroupGENP(void) 123 { 124 OSStatus status; 125 126 printf("[BEGIN] %s\n", __FUNCTION__); 127 128 NSDictionary *clean1 = @{ 129 (id)kSecClass : (id)kSecClassGenericPassword, 130 (id)kSecAttrAccessGroup : @"keychain-test1", 131 (id)kSecUseDataProtectionKeychain: @YES, 132 }; 133 NSDictionary *clean2 = @{ 134 (id)kSecClass : (id)kSecClassGenericPassword, 135 (id)kSecAttrAccessGroup : @"keychain-test2", 136 (id)kSecUseDataProtectionKeychain: @YES, 137 }; 138 139 (void)SecItemDelete((__bridge CFDictionaryRef)clean1); 140 (void)SecItemDelete((__bridge CFDictionaryRef)clean2); 141 142 /* 143 * Add item 144 */ 145 146 NSDictionary *add = @{ 147 (id)kSecClass : (id)kSecClassGenericPassword, 148 (id)kSecAttrAccessGroup : @"keychain-test1", 149 (id)kSecAttrAccount : @"item-delete-me", 150 (id)kSecUseDataProtectionKeychain : (id)kCFBooleanTrue, 151 (id)kSecAttrAccessible : (id)kSecAttrAccessibleAfterFirstUnlock, 152 (id)kSecUseDataProtectionKeychain: @YES, 153 }; 154 status = SecItemAdd((__bridge CFDictionaryRef)add, NULL); 155 if (status != errSecSuccess) 156 fail("add item: %d: %s", (int)status, [[add description] UTF8String]); 157 158 /* 159 * Update access group 160 */ 161 NSDictionary *query = @{ 162 (id)kSecClass : (id)kSecClassGenericPassword, 163 (id)kSecAttrAccessGroup : @"keychain-test1", 164 (id)kSecAttrAccount : @"item-delete-me", 165 (id)kSecUseDataProtectionKeychain: @YES, 166 }; 167 NSDictionary *modified = @{ 168 (id)kSecAttrAccessGroup : @"keychain-test2", 169 }; 170 171 status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)modified); 172 if (status != errSecSuccess) 173 fail("cleanup2 item: %d", (int)status); 174 175 /* 176 * 177 */ 178 NSDictionary *check1 = @{ 179 (id)kSecClass : (id)kSecClassGenericPassword, 180 (id)kSecAttrAccessGroup : @"keychain-test1", 181 (id)kSecAttrAccount : @"item-delete-me", 182 (id)kSecUseDataProtectionKeychain: @YES, 183 }; 184 status = SecItemCopyMatching((__bridge CFDictionaryRef)check1, NULL); 185 if (status != errSecItemNotFound) 186 fail("check1 item: %d", (int)status); 187 188 189 NSDictionary *check2 = @{ 190 (id)kSecClass : (id)kSecClassGenericPassword, 191 (id)kSecAttrAccessGroup : @"keychain-test2", 192 (id)kSecAttrAccount : @"item-delete-me", 193 (id)kSecUseDataProtectionKeychain: @YES, 194 }; 195 status = SecItemCopyMatching((__bridge CFDictionaryRef)check2, NULL); 196 if (status != errSecSuccess) 197 fail("check2 item: %d", (int)status); 198 199 /* 200 * Clean 201 */ 202 (void)SecItemDelete((__bridge CFDictionaryRef)clean1); 203 (void)SecItemDelete((__bridge CFDictionaryRef)clean2); 204 205 printf("[PASS] %s\n", __FUNCTION__); 206 } 207 208 static NSString *certDataBase64 = @"\ 209 MIIEQjCCAyqgAwIBAgIJAJdFadWqNIfiMA0GCSqGSIb3DQEBBQUAMHMxCzAJBgNVBAYTAkNaMQ8wDQYD\ 210 VQQHEwZQcmFndWUxFTATBgNVBAoTDENvc21vcywgSW5jLjEXMBUGA1UEAxMOc3VuLmNvc21vcy5nb2Qx\ 211 IzAhBgkqhkiG9w0BCQEWFHRoaW5nQHN1bi5jb3Ntb3MuZ29kMB4XDTE2MDIyNjE0NTQ0OVoXDTE4MTEy\ 212 MjE0NTQ0OVowczELMAkGA1UEBhMCQ1oxDzANBgNVBAcTBlByYWd1ZTEVMBMGA1UEChMMQ29zbW9zLCBJ\ 213 bmMuMRcwFQYDVQQDEw5zdW4uY29zbW9zLmdvZDEjMCEGCSqGSIb3DQEJARYUdGhpbmdAc3VuLmNvc21v\ 214 cy5nb2QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5u9gnYEDzQIVu7yC40VcXTZ01D9CJ\ 215 oD/mH62tebEHEdfVPLWKeq+uAHnJ6fTIJQvksaISOxwiOosFjtI30mbe6LZ/oK22wYX+OUwKhAYjZQPy\ 216 RYfuaJe/52F0zmfUSJ+KTbUZrXbVVFma4xPfpg4bptvtGkFJWnufvEEHimOGmO5O69lXA0Hit1yLU0/A\ 217 MQrIMmZT8gb8LMZGPZearT90KhCbTHAxjcBfswZYeL8q3xuEVHXC7EMs6mq8IgZL7mzSBmrCfmBAIO0V\ 218 jW2kvmy0NFxkjIeHUShtYb11oYYyfHuz+1vr1y6FIoLmDejKVnwfcuNb545m26o+z/m9Lv9bAgMBAAGj\ 219 gdgwgdUwHQYDVR0OBBYEFGDdpPELS92xT+Hkh/7lcc+4G56VMIGlBgNVHSMEgZ0wgZqAFGDdpPELS92x\ 220 T+Hkh/7lcc+4G56VoXekdTBzMQswCQYDVQQGEwJDWjEPMA0GA1UEBxMGUHJhZ3VlMRUwEwYDVQQKEwxD\ 221 b3Ntb3MsIEluYy4xFzAVBgNVBAMTDnN1bi5jb3Ntb3MuZ29kMSMwIQYJKoZIhvcNAQkBFhR0aGluZ0Bz\ 222 dW4uY29zbW9zLmdvZIIJAJdFadWqNIfiMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAFYi\ 223 Zu/dfAMOrD51bYxP88Wu6iDGBe9nMG/0lkKgnX5JQKCxfxFMk875rfa+pljdUMOaPxegOXq1DrYmQB9O\ 224 /pHI+t7ozuWHRj2zKkVgMWAygNWDPcoqBEus53BdAgA644aPN2JvnE4NEPCllOMKftPoIWbd/5ZjCx3a\ 225 bCuxBdXq5YSmiEnOdGfKeXjeeEiIDgARb4tLgH5rkOpB1uH/ZCWn1hkiajBhrGhhPhpA0zbkZg2Ug+8g\ 226 XPlx1yQB1VOJkj2Z8dUEXCaRRijInCJ2eU+pgJvwLV7mxmSED7DEJ+b+opxJKYrsdKBU6RmYpPrDa+KC\ 227 /Yfu88P9hKKj0LmBiREA\ 228 "; 229 230 static NSString *keyDataBase64 = @"\ 231 MIIEogIBAAKCAQEAubvYJ2BA80CFbu8guNFXF02dNQ/QiaA/5h+trXmxBxHX1Ty1inqvrgB5yen0yCUL\ 232 5LGiEjscIjqLBY7SN9Jm3ui2f6CttsGF/jlMCoQGI2UD8kWH7miXv+dhdM5n1Eifik21Ga121VRZmuMT\ 233 36YOG6bb7RpBSVp7n7xBB4pjhpjuTuvZVwNB4rdci1NPwDEKyDJmU/IG/CzGRj2Xmq0/dCoQm0xwMY3A\ 234 X7MGWHi/Kt8bhFR1wuxDLOpqvCIGS+5s0gZqwn5gQCDtFY1tpL5stDRcZIyHh1EobWG9daGGMnx7s/tb\ 235 69cuhSKC5g3oylZ8H3LjW+eOZtuqPs/5vS7/WwIDAQABAoIBAGcwmQAPdyZus3OVwa1NCUD2KyB+39KG\ 236 yNmWwgx+br9Jx4s+RnJghVh8BS4MIKZOBtSRaEUOuCvAMNrupZbD+8leq34vDDRcQpCizr+M6Egj6FRj\ 237 Ewl+7Mh+yeN2hbMoghL552MTv9D4Iyxteu4nuPDd/JQ3oQwbDFIL6mlBFtiBDUr9ndemmcJ0WKuzor6a\ 238 3rgsygLs8SPyMefwIKjh5rJZls+iv3AyVEoBdCbHBz0HKgLVE9ZNmY/gWqda2dzAcJxxMdafeNVwHovv\ 239 BtyyRGnA7Yikx2XT4WLgKfuUsYLnDWs4GdAa738uxPBfiddQNeRjN7jRT1GZIWCk0P29rMECgYEA8jWi\ 240 g1Dph+4VlESPOffTEt1aCYQQWtHs13Qex95HrXX/L49fs6cOE7pvBh7nVzaKwBnPRh5+3bCPsPmRVb7h\ 241 k/GreOriCjTZtyt2XGp8eIfstfirofB7c1lNBjT61BhgjJ8Moii5c2ksNIOOZnKtD53n47mf7hiarYkw\ 242 xFEgU6ECgYEAxE8Js3gIPOBjsSw47XHuvsjP880nZZx/oiQ4IeJb/0rkoDMVJjU69WQu1HTNNAnMg4/u\ 243 RXo31h+gDZOlE9t9vSXHdrn3at67KAVmoTbRknGxZ+8tYpRJpPj1hyufynBGcKwevv3eHJHnE5eDqbHx\ 244 ynZFkXemzT9aMy3R4CCFMXsCgYAYyZpnG/m6WohE0zthMFaeoJ6dSLGvyboWVqDrzXjCbMf/4wllRlxv\ 245 cm34T2NXjpJmlH2c7HQJVg9uiivwfYdyb5If3tHhP4VkdIM5dABnCWoVOWy/NvA7XtE+KF/fItuGqKRP\ 246 WCGaiRHoEeqZ23SQm5VmvdF7OXNi/R5LiQ3o4QKBgAGX8qg2TTrRR33ksgGbbyi1UJrWC3/TqWWTjbEY\ 247 uU51OS3jvEQ3ImdjjM3EtPW7LqHSxUhjGZjvYMk7bZefrIGgkOHx2IRRkotcn9ynKURbD+mcE249beuc\ 248 6cFTJVTrXGcFvqomPWtV895A2JzECQZvt1ja88uuu/i2YoHDQdGJAoGAL2TEgiMXiunb6PzYMMKKa+mx\ 249 mFnagF0Ek3UJ9ByXKoLz3HFEl7cADIkqyenXFsAER/ifMyCoZp/PDBd6ZkpqLTdH0jQ2Yo4SllLykoiZ\ 250 fBWMfjRu4iw9E0MbPB3blmtzfv53BtWKy0LUOlN4juvpqryA7TgaUlZkfMT+T1TC7xU=\ 251 "; 252 253 254 static SecIdentityRef 255 CreateTestIdentity(void) 256 { 257 NSData *certData = [[NSData alloc] initWithBase64EncodedString:certDataBase64 options:0]; 258 SecCertificateRef cert = SecCertificateCreateWithData(kCFAllocatorDefault, (CFDataRef)certData); 259 if (cert == NULL) 260 fail("create certificate from data"); 261 262 NSData *keyData = [[NSData alloc] initWithBase64EncodedString:keyDataBase64 options:0]; 263 NSDictionary *keyAttrs = @{ 264 (id)kSecAttrKeyType: (id)kSecAttrKeyTypeRSA, 265 (id)kSecAttrKeySizeInBits: @2048, 266 (id)kSecAttrKeyClass: (id)kSecAttrKeyClassPrivate 267 }; 268 SecKeyRef privateKey = SecKeyCreateWithData((CFDataRef)keyData, (CFDictionaryRef)keyAttrs, NULL); 269 if (privateKey == NULL) 270 fail("create private key from data"); 271 272 // Create identity from certificate and private key. 273 SecIdentityRef identity = SecIdentityCreate(kCFAllocatorDefault, cert, privateKey); 274 CFRelease(privateKey); 275 CFRelease(cert); 276 277 return identity; 278 } 279 280 static void 281 CheckIdentityItem(NSString *accessGroup, OSStatus expectedStatus) 282 { 283 OSStatus status; 284 285 NSDictionary *check = @{ 286 (id)kSecClass : (id)kSecClassIdentity, 287 (id)kSecAttrAccessGroup : accessGroup, 288 (id)kSecAttrLabel : @"item-delete-me", 289 (id)kSecUseDataProtectionKeychain: @YES, 290 }; 291 status = SecItemCopyMatching((__bridge CFDictionaryRef)check, NULL); 292 if (status != expectedStatus) 293 fail("check %s for %d item: %d", [accessGroup UTF8String], (int)expectedStatus, (int)status); 294 } 295 296 static void 297 CheckItemUpdateAccessGroupIdentity(void) 298 { 299 OSStatus status; 300 CFTypeRef ref = NULL; 301 302 printf("[BEGIN] %s\n", __FUNCTION__); 303 304 NSDictionary *clean1 = @{ 305 (id)kSecClass : (id)kSecClassIdentity, 306 (id)kSecAttrAccessGroup : @"keychain-test1", 307 }; 308 NSDictionary *clean2 = @{ 309 (id)kSecClass : (id)kSecClassIdentity, 310 (id)kSecAttrAccessGroup : @"keychain-test2", 311 }; 312 313 (void)SecItemDelete((__bridge CFDictionaryRef)clean1); 314 (void)SecItemDelete((__bridge CFDictionaryRef)clean2); 315 316 CheckIdentityItem(@"keychain-test1", errSecItemNotFound); 317 CheckIdentityItem(@"keychain-test2", errSecItemNotFound); 318 319 SecIdentityRef identity = CreateTestIdentity(); 320 if (identity == NULL) 321 fail("create private key from data"); 322 323 324 /* 325 * Add item 326 */ 327 328 NSDictionary *add = @{ 329 (id)kSecValueRef : (__bridge id)identity, 330 (id)kSecAttrAccessGroup : @"keychain-test1", 331 (id)kSecAttrLabel : @"item-delete-me", 332 (id)kSecAttrAccessible : (id)kSecAttrAccessibleAfterFirstUnlock, 333 (id)kSecUseDataProtectionKeychain: @YES, 334 (id)kSecReturnPersistentRef: (id)kCFBooleanTrue, 335 }; 336 status = SecItemAdd((__bridge CFDictionaryRef)add, &ref); 337 if (status != errSecSuccess) 338 fail("add item: %d: %s", (int)status, [[add description] UTF8String]); 339 340 /* 341 * 342 */ 343 CheckIdentityItem(@"keychain-test1", errSecSuccess); 344 CheckIdentityItem(@"keychain-test2", errSecItemNotFound); 345 346 347 /* 348 * Update access group 349 */ 350 NSDictionary *query = @{ 351 (id)kSecClass : (id)kSecClassIdentity, 352 (id)kSecAttrAccessGroup : @"keychain-test1", 353 (id)kSecAttrLabel : @"item-delete-me", 354 (id)kSecUseDataProtectionKeychain : (id)kCFBooleanTrue, 355 }; 356 NSDictionary *modified = @{ 357 (id)kSecAttrAccessGroup : @"keychain-test2", 358 }; 359 360 status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)modified); 361 if (status != errSecSuccess) 362 fail("cleanup2 item: %d", (int)status); 363 364 /* 365 * 366 */ 367 368 CheckIdentityItem(@"keychain-test1", errSecItemNotFound); 369 CheckIdentityItem(@"keychain-test2", errSecSuccess); 370 371 /* 372 * Check pref 373 */ 374 CFDataRef data = NULL; 375 376 NSDictionary *prefQuery = @{ 377 (id)kSecClass : (id)kSecClassIdentity, 378 (id)kSecAttrAccessGroup : @"keychain-test2", 379 (id)kSecAttrLabel : @"item-delete-me", 380 (id)kSecUseDataProtectionKeychain: @YES, 381 (id)kSecReturnPersistentRef : (id)kCFBooleanTrue, 382 }; 383 status = SecItemCopyMatching((__bridge CFDictionaryRef)prefQuery, (CFTypeRef *)&data); 384 if (status != errSecSuccess) 385 fail("prefQuery item: %d", (int)status); 386 387 /* 388 * Update access group for identity 389 */ 390 NSDictionary *query2 = @{ 391 (id)kSecValuePersistentRef : (__bridge id)data, 392 (id)kSecUseDataProtectionKeychain : (id)kCFBooleanTrue, 393 }; 394 NSDictionary *modified2 = @{ 395 (id)kSecAttrAccessGroup : @"keychain-test1", 396 }; 397 398 status = SecItemUpdate((__bridge CFDictionaryRef)query2, (__bridge CFDictionaryRef)modified2); 399 if (status != errSecInternal) 400 fail("update identity with pref fails differntly: %d", (int)status); 401 402 /* 403 CheckIdentityItem(@"keychain-test1", errSecSuccess); 404 CheckIdentityItem(@"keychain-test2", errSecItemNotFound); 405 */ 406 407 408 /* 409 * Clean 410 */ 411 (void)SecItemDelete((__bridge CFDictionaryRef)clean1); 412 (void)SecItemDelete((__bridge CFDictionaryRef)clean2); 413 414 CFRelease(identity); 415 416 CheckIdentityItem(@"keychain-test1", errSecItemNotFound); 417 CheckIdentityItem(@"keychain-test2", errSecItemNotFound); 418 419 420 printf("[PASS] %s\n", __FUNCTION__); 421 } 422 423 static void 424 CheckFindIdentityByReference(void) 425 { 426 OSStatus status; 427 CFDataRef pref = NULL, pref2 = NULL; 428 429 printf("[BEGIN] %s\n", __FUNCTION__); 430 431 /* 432 * Clean identities 433 */ 434 NSDictionary *clean1 = @{ 435 (id)kSecClass : (id)kSecClassIdentity, 436 (id)kSecAttrAccessGroup : @"keychain-test1", 437 }; 438 (void)SecItemDelete((__bridge CFDictionaryRef)clean1); 439 440 /* 441 * Add 442 */ 443 SecIdentityRef identity = CreateTestIdentity(); 444 if (identity == NULL) 445 fail("create private key from data"); 446 447 448 NSDictionary *add = @{ 449 (id)kSecValueRef : (__bridge id)identity, 450 (id)kSecAttrAccessGroup : @"keychain-test1", 451 (id)kSecAttrLabel : @"CheckItemReference", 452 (id)kSecAttrAccessible : (id)kSecAttrAccessibleAfterFirstUnlock, 453 (id)kSecUseDataProtectionKeychain: @YES, 454 (id)kSecReturnPersistentRef: (id)kCFBooleanTrue, 455 }; 456 status = SecItemAdd((__bridge CFDictionaryRef)add, (CFTypeRef *)&pref); 457 if (status != errSecSuccess) 458 fail("add item: %d: %s", (int)status, [[add description] UTF8String]); 459 460 if (pref == NULL || CFGetTypeID(pref) != CFDataGetTypeID()) 461 fail("no pref returned"); 462 463 /* 464 * Find by identity 465 */ 466 467 NSDictionary *query = @{ 468 (id)kSecValueRef : (__bridge id)identity, 469 (id)kSecReturnPersistentRef: (id)kCFBooleanTrue, 470 }; 471 status = SecItemCopyMatching((CFDictionaryRef)query, (CFTypeRef *)&pref2); 472 if (status) 473 fail("SecItemCopyMatching: %d", (int)status); 474 475 if (pref2 == NULL || CFGetTypeID(pref2) != CFDataGetTypeID()) 476 fail("no pref2 returned"); 477 478 479 if (!CFEqual(pref, pref2)) 480 fail("prefs not same"); 481 482 CFRelease(pref2); 483 484 /* 485 * Find by label 486 */ 487 488 NSDictionary *query2 = @{ 489 (id)kSecClass : (id)kSecClassIdentity, 490 (id)kSecAttrAccessGroup : @"keychain-test1", 491 (id)kSecAttrLabel : @"CheckItemReference", 492 (id)kSecUseDataProtectionKeychain: @YES, 493 (id)kSecReturnPersistentRef: (id)kCFBooleanTrue, 494 }; 495 status = SecItemCopyMatching((CFDictionaryRef)query2, (CFTypeRef *)&pref2); 496 if (status) 497 fail("SecItemCopyMatching: %d", (int)status); 498 499 if (pref2 == NULL || CFGetTypeID(pref2) != CFDataGetTypeID()) 500 fail("no pref2 returned"); 501 502 503 if (!CFEqual(pref, pref2)) 504 fail("prefs not same"); 505 506 CFRelease(pref2); 507 508 /* 509 * Find by label + reference 510 */ 511 512 NSDictionary *query3 = @{ 513 (id)kSecAttrAccessGroup : @"keychain-test1", 514 (id)kSecAttrLabel : @"CheckItemReference", 515 (id)kSecValueRef : (__bridge id)identity, 516 (id)kSecUseDataProtectionKeychain: @YES, 517 (id)kSecReturnPersistentRef: (id)kCFBooleanTrue, 518 }; 519 status = SecItemCopyMatching((CFDictionaryRef)query3, (CFTypeRef *)&pref2); 520 if (status) 521 fail("SecItemCopyMatching: %d", (int)status); 522 523 if (pref2 == NULL || CFGetTypeID(pref2) != CFDataGetTypeID()) 524 fail("no pref2 returned"); 525 526 527 if (!CFEqual(pref, pref2)) 528 fail("prefs not same"); 529 530 CFRelease(pref2); 531 532 /* 533 * Free stuff 534 */ 535 536 CFRelease(pref); 537 if(identity) { 538 CFRelease(identity); 539 identity = NULL; 540 } 541 542 printf("[PASS] %s\n", __FUNCTION__); 543 } 544 545 static uint64_t 546 timeDiff(uint64_t start, uint64_t stop) 547 { 548 static uint64_t time_overhead_measured = 0; 549 static double timebase_factor = 0; 550 551 if (time_overhead_measured == 0) { 552 uint64_t t0 = mach_absolute_time(); 553 time_overhead_measured = mach_absolute_time() - t0; 554 555 struct mach_timebase_info timebase_info = {}; 556 mach_timebase_info(&timebase_info); 557 timebase_factor = ((double)timebase_info.numer)/((double)timebase_info.denom); 558 } 559 560 return ((stop - start - time_overhead_measured) * timebase_factor) / NSEC_PER_USEC; 561 } 562 563 static void 564 RunCopyPerfTest(NSString *name, NSDictionary *query) 565 { 566 uint64_t start = mach_absolute_time(); 567 OSStatus status; 568 CFTypeRef result = NULL; 569 570 status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result); 571 uint64_t stop = mach_absolute_time(); 572 573 if (status != 0) { 574 printf("SecItemCopyMatching failed with: %d\n", (int)status); 575 fflush(stdout); 576 abort(); 577 } 578 579 if (result) 580 CFRelease(result); 581 582 uint64_t us = timeDiff(start, stop); 583 584 puts([[NSString stringWithFormat:@"[RESULT_KEY] SecItemCopyMatching-%@\n[RESULT_VALUE] %lu\n", 585 name, (unsigned long)us] UTF8String]); 586 } 587 588 static void 589 RunDigestPerfTest(NSString *name, NSString *itemClass, NSString *accessGroup, NSUInteger expectedCount) 590 { 591 uint64_t start = mach_absolute_time(); 592 dispatch_semaphore_t sema = dispatch_semaphore_create(0); 593 __block uint64_t stop; 594 595 _SecItemFetchDigests(itemClass, accessGroup, ^(NSArray *items, NSError *error) { 596 stop = mach_absolute_time(); 597 if (error) { 598 printf("%s: _SecItemFetchDigests failed with: %ld\n", [name UTF8String], (long)error.code); 599 fflush(stdout); 600 abort(); 601 } 602 dispatch_semaphore_signal(sema); 603 604 if (expectedCount != [items count]) { 605 printf("%s: _SecItemFetchDigests didn't return expected items: %ld\n", [name UTF8String], (long)[items count]); 606 fflush(stdout); 607 abort(); 608 } 609 }); 610 dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); 611 612 613 uint64_t us = timeDiff(start, stop); 614 615 puts([[NSString stringWithFormat:@"[RESULT_KEY] SecItemCopyDigest-%@\n[RESULT_VALUE] %lu\n", 616 name, (unsigned long)us] UTF8String]); 617 } 618 619 620 static void 621 CheckItemPerformance(void) 622 { 623 unsigned n; 624 625 printf("[BEGIN] %s\n", __FUNCTION__); 626 627 /* 628 * Clean identities 629 */ 630 NSDictionary *clean1 = @{ 631 (id)kSecClass : (id)kSecClassGenericPassword, 632 (id)kSecAttrService : @"service", 633 (id)kSecAttrAccessGroup : @"keychain-test1", 634 (id)kSecUseDataProtectionKeychain : (id)kCFBooleanTrue, 635 }; 636 (void)SecItemDelete((__bridge CFDictionaryRef)clean1); 637 638 NSData *data = [NSData dataWithBytes:"password" length:8]; 639 640 for (n = 0; n < 1000; n++) { 641 NSDictionary *item = @{ 642 (id)kSecClass : (id)kSecClassGenericPassword, 643 (id)kSecAttrAccount : [NSString stringWithFormat:@"account-%d", n], 644 (id)kSecAttrService : @"service", 645 (id)kSecUseDataProtectionKeychain : (id)kCFBooleanTrue, 646 (id)kSecAttrAccessGroup : @"keychain-test1", 647 (id)kSecUseDataProtectionKeychain: @YES, 648 (id)kSecValueData : data, 649 }; 650 SecItemAdd((__bridge CFDictionaryRef)item, NULL); 651 } 652 653 654 RunCopyPerfTest(@"FindOneItemLimit", @{ 655 (id)kSecClass : (id)kSecClassGenericPassword, 656 (id)kSecAttrService : @"service", 657 (id)kSecMatchLimit : (id)kSecMatchLimitOne, 658 (id)kSecUseDataProtectionKeychain: @YES, 659 }); 660 RunCopyPerfTest(@"FindOneItemUnique", @{ 661 (id)kSecClass : (id)kSecClassGenericPassword, 662 (id)kSecAttrAccount : @"account-0", 663 (id)kSecAttrService : @"service", 664 (id)kSecMatchLimit : (id)kSecMatchLimitAll, 665 (id)kSecUseDataProtectionKeychain: @YES, 666 }); 667 RunCopyPerfTest(@"Find1000Items", @{ 668 (id)kSecClass : (id)kSecClassGenericPassword, 669 (id)kSecAttrService : @"service", 670 (id)kSecMatchLimit : (id)kSecMatchLimitAll, 671 (id)kSecUseDataProtectionKeychain: @YES, 672 }); 673 RunDigestPerfTest(@"Digest1000Items", (id)kSecClassGenericPassword, @"keychain-test1", 1000); 674 RunCopyPerfTest(@"GetAttrOneItemUnique", @{ 675 (id)kSecClass : (id)kSecClassGenericPassword, 676 (id)kSecAttrAccount : @"account-0", 677 (id)kSecAttrService : @"service", 678 (id)kSecReturnAttributes : (id)kCFBooleanTrue, 679 (id)kSecMatchLimit : (id)kSecMatchLimitAll, 680 (id)kSecUseDataProtectionKeychain: @YES, 681 }); 682 RunCopyPerfTest(@"GetData1000Items", @{ 683 (id)kSecClass : (id)kSecClassGenericPassword, 684 (id)kSecAttrService : @"service", 685 (id)kSecReturnData : (id)kCFBooleanTrue, 686 (id)kSecMatchLimit : (id)kSecMatchLimitAll, 687 (id)kSecUseDataProtectionKeychain: @YES, 688 }); 689 RunCopyPerfTest(@"GetDataOneItemUnique", @{ 690 (id)kSecClass : (id)kSecClassGenericPassword, 691 (id)kSecAttrAccount : @"account-0", 692 (id)kSecAttrService : @"service", 693 (id)kSecReturnData : (id)kCFBooleanTrue, 694 (id)kSecMatchLimit : (id)kSecMatchLimitAll, 695 (id)kSecUseDataProtectionKeychain: @YES, 696 }); 697 RunCopyPerfTest(@"GetDataAttrOneItemUnique", @{ 698 (id)kSecClass : (id)kSecClassGenericPassword, 699 (id)kSecAttrAccount : @"account-0", 700 (id)kSecAttrService : @"service", 701 (id)kSecReturnData : (id)kCFBooleanTrue, 702 (id)kSecReturnAttributes : (id)kCFBooleanTrue, 703 (id)kSecMatchLimit : (id)kSecMatchLimitAll, 704 (id)kSecUseDataProtectionKeychain: @YES, 705 }); 706 #if TARGET_OS_IPHONE /* macOS doesn't support fetching data for more then one item */ 707 RunCopyPerfTest(@"GetData1000Items", @{ 708 (id)kSecClass : (id)kSecClassGenericPassword, 709 (id)kSecAttrService : @"service", 710 (id)kSecReturnData : (id)kCFBooleanTrue, 711 (id)kSecMatchLimit : (id)kSecMatchLimitAll, 712 (id)kSecUseDataProtectionKeychain: @YES, 713 }); 714 RunCopyPerfTest(@"GetDataAttr1000Items", @{ 715 (id)kSecClass : (id)kSecClassGenericPassword, 716 (id)kSecAttrService : @"service", 717 (id)kSecReturnData : (id)kCFBooleanTrue, 718 (id)kSecReturnAttributes : (id)kCFBooleanTrue, 719 (id)kSecMatchLimit : (id)kSecMatchLimitAll, 720 (id)kSecUseDataProtectionKeychain: @YES, 721 }); 722 #endif 723 724 (void)SecItemDelete((__bridge CFDictionaryRef)clean1); 725 726 727 printf("[PASS] %s\n", __FUNCTION__); 728 } 729 730 int 731 main(int argc, const char ** argv) 732 { 733 printf("[TEST] secitemfunctionality\n"); 734 735 CheckItemPerformance(); 736 737 CheckFindIdentityByReference(); 738 739 //CheckItemAddDeleteMaybeLegacyKeychainNoData(); 740 CheckItemAddDeleteNoData(); 741 CheckItemUpdateAccessGroupGENP(); 742 CheckItemUpdateAccessGroupIdentity(); 743 744 printf("[SUMMARY]\n"); 745 printf("test completed\n"); 746 747 return 0; 748 }