/ RegressionTests / secitemfunctionality / secitemfunctionality.m
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  }