1/* 2 * kcTime.cpp - measure performance of keychain ops 3 */ 4 5#include <Security/Security.h> 6#include <security_cdsa_utils/cuFileIo.h> 7#include <stdio.h> 8#include <stdlib.h> 9#include <string.h> 10#include <unistd.h> 11#include <CoreFoundation/CoreFoundation.h> 12#include <utilLib/common.h> 13#include <utilLib/cputime.h> 14#include <Security/SecImportExport.h> 15 16/* Hard coded test params */ 17 18/* 19 * Number of keychains to create/search 20 */ 21#define KT_NUM_KEYCHAINS 10 22 23/* 24 * Certs and P12 blobs to add to keychain 25 */ 26#define KT_CERT0_NAME "amazon_v3.100.cer" 27#define KT_CERT1_NAME "SecureServer.509.cer" 28#define KT_P12_PFX "test1.p12" 29#define KT_P12_PASSWORD "password" 30 31/* 32 * Base name of keychains we create. We delete them before we start. 33 * They're in the user's home directory to faciliate testing NFS vs. local. 34 */ 35#define KT_KC_NAME "kcTime_test_" 36 37static void usage(char **argv) 38{ 39 printf("Usage: %s [option ...]\n", argv[0]); 40 printf("Options:\n"); 41 printf(" v verbose\n"); 42 printf(" h help\n"); 43 exit(1); 44} 45 46static void printAllTimes( 47 bool verbose, 48 double *delta, 49 unsigned numSamples) 50{ 51 if(!verbose) { 52 return; 53 } 54 for(unsigned dex=0; dex<numSamples; dex++) { 55 printf(" sample[%u] %8.4f\n", dex, delta[dex]); 56 } 57} 58 59static void printSecErr( 60 const char *op, 61 OSStatus ortn) 62{ 63 cssmPerror(op, ortn); 64} 65 66int main(int argc, char **argv) 67{ 68 SecKeychainRef kcRefs[KT_NUM_KEYCHAINS]; 69 OSStatus ortn; 70 int arg; 71 char *argp; 72 char kcName[KT_NUM_KEYCHAINS][80]; 73 unsigned dex; 74 unsigned char *cert0Data; 75 unsigned cert0Len; 76 unsigned char *cert1Data; 77 unsigned cert1Len; 78 SecCertificateRef certRef0[KT_NUM_KEYCHAINS]; 79 SecCertificateRef certRef1[KT_NUM_KEYCHAINS]; 80 CFArrayRef savedSearchList; 81 CFMutableArrayRef ourSearchList; 82 int irtn; 83 CPUTime startTime, endTime; 84 double deltaMs[KT_NUM_KEYCHAINS * 2]; 85 double deltaMs2[KT_NUM_KEYCHAINS]; 86 double deltaMs3[KT_NUM_KEYCHAINS]; 87 SecKeychainSearchRef srchRef; 88 CFStringRef p12Pwd; 89 CFDataRef p12Data; 90 bool verbose = false; 91 92 memset(kcRefs, 0, sizeof(SecKeychainRef) * KT_NUM_KEYCHAINS); 93 94 for(arg=1; arg<argc; arg++) { 95 argp = argv[arg]; 96 switch(argp[0]) { 97 case 'v': 98 verbose = true; 99 break; 100 default: 101 usage(argv); 102 } 103 } 104 105 printf("Starting kcTime test using %d keychains\n", KT_NUM_KEYCHAINS); 106 107 /* generate keychain names */ 108 for(dex=0; dex<KT_NUM_KEYCHAINS; dex++) { 109 sprintf(kcName[dex], "%s%d", KT_KC_NAME, dex); 110 } 111 112 /* delete existing keychains we created in the past */ 113 for(dex=0; dex<KT_NUM_KEYCHAINS; dex++) { 114 /* 115 * It may or may not exist, and may or may not be in the search 116 * list. Brute force and ignore errors. 117 */ 118 SecKeychainRef kc; 119 ortn = SecKeychainOpen(kcName[dex], &kc); 120 if(ortn == noErr) { 121 SecKeychainDelete(kc); 122 } 123 124 /* brute force UNIX file delete too */ 125 char *userHome = getenv("HOME"); 126 char fullPath[1024]; 127 if(userHome == NULL) { 128 userHome = (char *)""; 129 } 130 sprintf(fullPath, "%s/Library/Keychains/%s", 131 userHome, kcName[dex]); 132 unlink(fullPath); 133 } 134 135 /* read in certificate data */ 136 irtn = readFile(KT_CERT0_NAME, &cert0Data, &cert0Len); 137 if(irtn) { 138 printf("I cannot find file %s in cwd.\n", KT_CERT0_NAME); 139 exit(1); 140 } 141 irtn = readFile(KT_CERT1_NAME, &cert1Data, &cert1Len); 142 if(irtn) { 143 printf("I cannot find file %s in cwd.\n", KT_CERT1_NAME); 144 exit(1); 145 } 146 147 /* save KC search list; we'll restore it at end */ 148 ortn = SecKeychainCopySearchList(&savedSearchList); 149 if(ortn) { 150 printSecErr("SecKeychainCopySearchList", ortn); 151 return 1; 152 } 153 /* subsequent errors to errOut: to restore searchList! */ 154 155 /* TIME: create n keychains */ 156 for(dex=0; dex<KT_NUM_KEYCHAINS; dex++) { 157 unsigned pwdLen = strlen(kcName[dex]); 158 startTime = CPUTimeRead(); 159 ortn = SecKeychainCreate(kcName[dex], 160 pwdLen, 161 kcName[dex], 162 false, // promptUser, 163 nil, // initialAccess 164 &kcRefs[dex]); 165 endTime = CPUTimeRead(); 166 if(ortn) { 167 printSecErr("SecKeychainCreate", ortn); 168 goto errOut; 169 } 170 deltaMs[dex] = CPUTimeDeltaMs(startTime, endTime); 171 } 172 printf("Create : %7.3f ms per op\n", 173 CPUTimeAvg(deltaMs, KT_NUM_KEYCHAINS)); 174 printAllTimes(verbose, deltaMs, KT_NUM_KEYCHAINS); 175 176 /* set KC search list to only the ones we just created */ 177 ourSearchList = CFArrayCreateMutable(NULL, KT_NUM_KEYCHAINS, NULL); 178 for(dex=0; dex<KT_NUM_KEYCHAINS; dex++) { 179 CFArrayInsertValueAtIndex(ourSearchList, dex, kcRefs[dex]); 180 } 181 ortn = SecKeychainSetSearchList(ourSearchList); 182 if(ortn) { 183 printSecErr("SecKeychainSetSearchList", ortn); 184 goto errOut; 185 } 186 CFRelease(ourSearchList); 187 188 /* TIME: close all of them */ 189 for(dex=0; dex<KT_NUM_KEYCHAINS; dex++) { 190 startTime = CPUTimeRead(); 191 CFRelease(kcRefs[dex]); 192 endTime = CPUTimeRead(); 193 deltaMs[dex] = CPUTimeDeltaMs(startTime, endTime); 194 } 195 printf("Close : %7.3f ms per op\n", 196 CPUTimeAvg(deltaMs, KT_NUM_KEYCHAINS)); 197 printAllTimes(verbose, deltaMs, KT_NUM_KEYCHAINS); 198 199 /* TIME: open all of them */ 200 /* This is so fast that we just measure the total time */ 201 startTime = CPUTimeRead(); 202 for(dex=0; dex<KT_NUM_KEYCHAINS; dex++) { 203 ortn = SecKeychainOpen(kcName[dex], &kcRefs[dex]); 204 if(ortn) { 205 printSecErr("SecKeychainOpen", ortn); 206 goto errOut; 207 } 208 } 209 endTime = CPUTimeRead(); 210 deltaMs[0] = CPUTimeDeltaMs(startTime, endTime); 211 printf("Open : %7.3f ms per op\n", 212 deltaMs[0] / KT_NUM_KEYCHAINS); 213 if(verbose) { 214 printf(" total time %7.3f ms\n", deltaMs[0]); 215 } 216 217 /* TIME: get status on all of them */ 218 for(dex=0; dex<KT_NUM_KEYCHAINS; dex++) { 219 SecKeychainStatus status; 220 startTime = CPUTimeRead(); 221 ortn = SecKeychainGetStatus(kcRefs[dex], &status); 222 endTime = CPUTimeRead(); 223 if(ortn) { 224 printSecErr("SecKeychainGetStatus", ortn); 225 goto errOut; 226 } 227 deltaMs[dex] = CPUTimeDeltaMs(startTime, endTime); 228 } 229 printf("Get Status : %7.3f ms per op\n", 230 CPUTimeAvg(deltaMs, KT_NUM_KEYCHAINS)); 231 printAllTimes(verbose, deltaMs, KT_NUM_KEYCHAINS); 232 233 /* TIME: unlock each keychain */ 234 for(dex=0; dex<KT_NUM_KEYCHAINS; dex++) { 235 unsigned pwdLen = strlen(kcName[dex]); 236 startTime = CPUTimeRead(); 237 ortn = SecKeychainUnlock(kcRefs[dex], 238 pwdLen, kcName[dex], true); 239 endTime = CPUTimeRead(); 240 if(ortn) { 241 printSecErr("SecKeychainUnlock", ortn); 242 goto errOut; 243 } 244 deltaMs[dex] = CPUTimeDeltaMs(startTime, endTime); 245 } 246 printf("Unlock : %7.3f ms per op\n", 247 CPUTimeAvg(deltaMs, KT_NUM_KEYCHAINS)); 248 printAllTimes(verbose, deltaMs, KT_NUM_KEYCHAINS); 249 250 /* TIME: create two SecCertificateRefs for each KC */ 251 /* this is fast, just measure total time */ 252 startTime = CPUTimeRead(); 253 for(dex=0; dex<KT_NUM_KEYCHAINS; dex++) { 254 CSSM_DATA cdata = {cert0Len, cert0Data}; 255 ortn = SecCertificateCreateFromData(&cdata, 256 CSSM_CERT_X_509v3, 257 CSSM_CERT_ENCODING_DER, 258 &certRef0[dex]); 259 if(ortn) { 260 printSecErr("SecCertificateCreateFromData", ortn); 261 goto errOut; 262 } 263 cdata.Length = cert1Len; 264 cdata.Data = cert1Data; 265 ortn = SecCertificateCreateFromData(&cdata, 266 CSSM_CERT_X_509v3, 267 CSSM_CERT_ENCODING_DER, 268 &certRef1[dex]); 269 if(ortn) { 270 printSecErr("SecCertificateCreateFromData", ortn); 271 goto errOut; 272 } 273 } 274 endTime = CPUTimeRead(); 275 deltaMs[0] = CPUTimeDeltaMs(startTime, endTime); 276 /* we created KT_NUM_KEYCHAINS*2 certs in KT_NUM_KEYCHAINS samples */ 277 printf("SecCertificateCreateFromData : %7.3f ms per op\n", 278 deltaMs[0] / (KT_NUM_KEYCHAINS * 2.0)); 279 if(verbose) { 280 printf(" total time %7.3f ms\n", deltaMs[0]); 281 } 282 free(cert0Data); 283 free(cert1Data); 284 285 /* TIME: add two certs to each keychain */ 286 for(dex=0; dex<KT_NUM_KEYCHAINS; dex++) { 287 startTime = CPUTimeRead(); 288 ortn = SecCertificateAddToKeychain(certRef0[dex], kcRefs[dex]); 289 if(ortn) { 290 printSecErr("SecCertificateAddToKeychain", ortn); 291 goto errOut; 292 } 293 ortn = SecCertificateAddToKeychain(certRef1[dex], kcRefs[dex]); 294 endTime = CPUTimeRead(); 295 if(ortn) { 296 printSecErr("SecCertificateAddToKeychain", ortn); 297 goto errOut; 298 } 299 deltaMs[dex] = CPUTimeDeltaMs(startTime, endTime); 300 } 301 /* we added KT_NUM_KEYCHAINS*2 certs in KT_NUM_KEYCHAINS samples */ 302 printf("SecCertificateAddToKeychain : %7.3f ms per op\n", 303 CPUTimeAvg(deltaMs, KT_NUM_KEYCHAINS) / 2.0); 304 printAllTimes(verbose, deltaMs, KT_NUM_KEYCHAINS); 305 306 /* free the certRefs */ 307 for(dex=0; dex<KT_NUM_KEYCHAINS; dex++) { 308 CFRelease(certRef0[dex]); 309 CFRelease(certRef1[dex]); 310 } 311 312 /* 313 �* Three TIMES: 314 * -- search for the first cert 315 * -- search for the next one 316 * -- search once more to end (not found) 317 */ 318 for(dex=0; dex<KT_NUM_KEYCHAINS; dex++) { 319 SecKeychainItemRef certRef = NULL; 320 321 /* step 1. set up search, get first item */ 322 startTime = CPUTimeRead(); 323 ortn = SecKeychainSearchCreateFromAttributes(kcRefs[dex], 324 kSecCertificateItemClass, 325 NULL, // no attrs 326 &srchRef); 327 if(ortn) { 328 printSecErr("SecKeychainSearchCreateFromAttributes", ortn); 329 goto errOut; 330 } 331 ortn = SecKeychainSearchCopyNext(srchRef, &certRef); 332 /* might be necessary to actually bring in the cert */ 333 if(SecCertificateGetTypeID() != CFGetTypeID(certRef)) { 334 printf("***Unexpected CFType on cert search\n"); 335 goto errOut; 336 } 337 endTime = CPUTimeRead(); 338 if(ortn) { 339 printSecErr("SecKeychainSearchCopyNext", ortn); 340 goto errOut; 341 } 342 deltaMs[dex] = CPUTimeDeltaMs(startTime, endTime); 343 CFRelease(certRef); 344 345 /* step 2. get next item */ 346 startTime = CPUTimeRead(); 347 ortn = SecKeychainSearchCopyNext(srchRef, &certRef); 348 if(SecCertificateGetTypeID() != CFGetTypeID(certRef)) { 349 printf("***Unexpected CFType on cert search\n"); 350 goto errOut; 351 } 352 endTime = CPUTimeRead(); 353 if(ortn) { 354 printSecErr("SecKeychainSearchCreateFromAttributes", ortn); 355 goto errOut; 356 } 357 deltaMs2[dex] = CPUTimeDeltaMs(startTime, endTime); 358 CFRelease(certRef); 359 360 /* step 3. one more search for nonexistent item */ 361 startTime = CPUTimeRead(); 362 ortn = SecKeychainSearchCopyNext(srchRef, &certRef); 363 endTime = CPUTimeRead(); 364 if(ortn != errSecItemNotFound) { 365 if(ortn == noErr) { 366 printf("***SecKeychainSearchCopyNext got noErr, " 367 "expected notFound\n"); 368 } 369 else { 370 printSecErr("SecKeychainSearchCopyNext", ortn); 371 } 372 goto errOut; 373 } 374 deltaMs3[dex] = CPUTimeDeltaMs(startTime, endTime); 375 CFRelease(srchRef); 376 } 377 printf("SecKeychainSearch first item : %7.3f ms per op\n", 378 CPUTimeAvg(deltaMs, KT_NUM_KEYCHAINS)); 379 printAllTimes(verbose, deltaMs, KT_NUM_KEYCHAINS); 380 printf("SecKeychainSearch next item : %7.3f ms per op\n", 381 CPUTimeAvg(deltaMs2, KT_NUM_KEYCHAINS)); 382 printAllTimes(verbose, deltaMs2, KT_NUM_KEYCHAINS); 383 printf("SecKeychainSearch end of list : %7.3f ms per op\n", 384 CPUTimeAvg(deltaMs3, KT_NUM_KEYCHAINS)); 385 printAllTimes(verbose, deltaMs3, KT_NUM_KEYCHAINS); 386 387 /* 388 * TIME: search all certs in all keychains 389 * This search create is outside the loop.... 390 */ 391 ortn = SecKeychainSearchCreateFromAttributes(NULL, 392 kSecCertificateItemClass, 393 NULL, // no attrs 394 &srchRef); 395 if(ortn) { 396 printSecErr("SecKeychainSearchCreateFromAttributes", ortn); 397 goto errOut; 398 } 399 /* we have 2 certs per keychain, search all of them */ 400 for(dex=0; dex<KT_NUM_KEYCHAINS*2; dex++) { 401 SecKeychainItemRef certRef = NULL; 402 403 startTime = CPUTimeRead(); 404 ortn = SecKeychainSearchCopyNext(srchRef, &certRef); 405 /* might be necessary to actually bring in the cert */ 406 if(SecCertificateGetTypeID() != CFGetTypeID(certRef)) { 407 printf("***Unexpected CFType on cert search\n"); 408 goto errOut; 409 } 410 endTime = CPUTimeRead(); 411 deltaMs[dex] = CPUTimeDeltaMs(startTime, endTime); 412 CFRelease(certRef); 413 } 414 printf("SecKeychainSearch all KCs : %7.3f ms per op\n", 415 CPUTimeAvg(deltaMs, KT_NUM_KEYCHAINS * 2)); 416 printAllTimes(verbose, deltaMs, KT_NUM_KEYCHAINS * 2); 417 418 /* 419 * TIME: import a p12 identity into each keychain 420 * 421 * First, read the file 422 */ 423 irtn = readFile(KT_P12_PFX, &cert0Data, &cert0Len); 424 if(irtn) { 425 printf("I cannot find file %s in cwd.\n", KT_P12_PFX); 426 exit(1); 427 } 428 p12Data = CFDataCreate(NULL, cert0Data, cert0Len); 429 p12Pwd = CFStringCreateWithCString(NULL, KT_P12_PASSWORD, 430 kCFStringEncodingASCII); 431 432 for(dex=0; dex<KT_NUM_KEYCHAINS; dex++) { 433 SecExternalFormat format = kSecFormatPKCS12; 434 SecExternalItemType itemType = kSecItemTypeAggregate; 435 SecKeyImportExportParameters keyParams; 436 memset(&keyParams, 0, sizeof(keyParams)); 437 keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION; 438 keyParams.flags = kSecKeyNoAccessControl; 439 keyParams.passphrase = p12Pwd; 440 441 keyParams.keyAttributes = CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_SENSITIVE | 442 CSSM_KEYATTR_EXTRACTABLE; 443 444 startTime = CPUTimeRead(); 445 ortn = SecKeychainItemImport(p12Data, 446 NULL, // fileNameOrExtension 447 &format, &itemType, 448 0, // flags 449 &keyParams, 450 kcRefs[dex], 451 NULL); // outItems 452 if(ortn) { 453 printSecErr("SecKeychainItemImport(p12)", ortn); 454 goto errOut; 455 } 456 endTime = CPUTimeRead(); 457 deltaMs[dex] = CPUTimeDeltaMs(startTime, endTime); 458 } 459 CFRelease(p12Data); 460 CFRelease(p12Pwd); 461 printf("P12 Import : %7.3f ms per op\n", 462 CPUTimeAvg(deltaMs, KT_NUM_KEYCHAINS)); 463 printAllTimes(verbose, deltaMs, KT_NUM_KEYCHAINS); 464 465 /* TIME: identity search in each keychain */ 466 for(dex=0; dex<KT_NUM_KEYCHAINS; dex++) { 467 SecIdentitySearchRef idSrch; 468 SecIdentityRef idRef; 469 startTime = CPUTimeRead(); 470 ortn = SecIdentitySearchCreate(kcRefs[dex], 0, &idSrch); 471 if(ortn) { 472 printSecErr("SecIdentitySearchCreate", ortn); 473 goto errOut; 474 } 475 ortn = SecIdentitySearchCopyNext(idSrch, &idRef); 476 endTime = CPUTimeRead(); 477 if(ortn) { 478 printSecErr("SecIdentitySearchCopyNext", ortn); 479 goto errOut; 480 } 481 deltaMs[dex] = CPUTimeDeltaMs(startTime, endTime); 482 CFRelease(idSrch); 483 CFRelease(idRef); 484 } 485 printf("SecIdentity search : %7.3f ms per op\n", 486 CPUTimeAvg(deltaMs, KT_NUM_KEYCHAINS)); 487 printAllTimes(verbose, deltaMs, KT_NUM_KEYCHAINS); 488 489errOut: 490 ortn = SecKeychainSetSearchList(savedSearchList); 491 if(ortn) { 492 printSecErr("SecKeychainSetSearchList", ortn); 493 } 494 495 /* delete all the keychains we created */ 496 for(dex=0; dex<KT_NUM_KEYCHAINS; dex++) { 497 if(kcRefs[dex] != NULL) { 498 SecKeychainDelete(kcRefs[dex]); 499 } 500 } 501 502 /* other cleanup if anyone cares */ 503 return 0; 504} 505