rsaperf.c (21464B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #include "seccomon.h" 6 #include "cert.h" 7 #include "secutil.h" 8 #include "nspr.h" 9 #include "nss.h" 10 #include "blapi.h" 11 #include "plgetopt.h" 12 #include "lowkeyi.h" 13 #include "pk11pub.h" 14 15 #define DEFAULT_ITERS 10 16 #define DEFAULT_DURATION 10 17 #define DEFAULT_KEY_BITS 1024 18 #define MIN_KEY_BITS 512 19 #define MAX_KEY_BITS 65536 20 #define BUFFER_BYTES MAX_KEY_BITS / 8 21 #define DEFAULT_THREADS 1 22 #define DEFAULT_EXPONENT 0x10001 23 24 extern NSSLOWKEYPrivateKey *getDefaultRSAPrivateKey(int); 25 extern NSSLOWKEYPublicKey *getDefaultRSAPublicKey(int); 26 27 secuPWData pwData = { PW_NONE, NULL }; 28 29 typedef struct TimingContextStr TimingContext; 30 31 struct TimingContextStr { 32 PRTime start; 33 PRTime end; 34 PRTime interval; 35 36 long days; 37 int hours; 38 int minutes; 39 int seconds; 40 int millisecs; 41 }; 42 43 TimingContext * 44 CreateTimingContext(void) 45 { 46 return PORT_Alloc(sizeof(TimingContext)); 47 } 48 49 void 50 DestroyTimingContext(TimingContext *ctx) 51 { 52 PORT_Free(ctx); 53 } 54 55 void 56 TimingBegin(TimingContext *ctx, PRTime begin) 57 { 58 ctx->start = begin; 59 } 60 61 static void 62 timingUpdate(TimingContext *ctx) 63 { 64 PRInt64 tmp, remaining; 65 PRInt64 L1000, L60, L24; 66 67 LL_I2L(L1000, 1000); 68 LL_I2L(L60, 60); 69 LL_I2L(L24, 24); 70 71 LL_DIV(remaining, ctx->interval, L1000); 72 LL_MOD(tmp, remaining, L1000); 73 LL_L2I(ctx->millisecs, tmp); 74 LL_DIV(remaining, remaining, L1000); 75 LL_MOD(tmp, remaining, L60); 76 LL_L2I(ctx->seconds, tmp); 77 LL_DIV(remaining, remaining, L60); 78 LL_MOD(tmp, remaining, L60); 79 LL_L2I(ctx->minutes, tmp); 80 LL_DIV(remaining, remaining, L60); 81 LL_MOD(tmp, remaining, L24); 82 LL_L2I(ctx->hours, tmp); 83 LL_DIV(remaining, remaining, L24); 84 LL_L2I(ctx->days, remaining); 85 } 86 87 void 88 TimingEnd(TimingContext *ctx, PRTime end) 89 { 90 ctx->end = end; 91 LL_SUB(ctx->interval, ctx->end, ctx->start); 92 PORT_Assert(LL_GE_ZERO(ctx->interval)); 93 timingUpdate(ctx); 94 } 95 96 void 97 TimingDivide(TimingContext *ctx, int divisor) 98 { 99 PRInt64 tmp; 100 101 LL_I2L(tmp, divisor); 102 LL_DIV(ctx->interval, ctx->interval, tmp); 103 104 timingUpdate(ctx); 105 } 106 107 char * 108 TimingGenerateString(TimingContext *ctx) 109 { 110 char *buf = NULL; 111 112 if (ctx->days != 0) { 113 buf = PR_sprintf_append(buf, "%d days", ctx->days); 114 } 115 if (ctx->hours != 0) { 116 if (buf != NULL) 117 buf = PR_sprintf_append(buf, ", "); 118 buf = PR_sprintf_append(buf, "%d hours", ctx->hours); 119 } 120 if (ctx->minutes != 0) { 121 if (buf != NULL) 122 buf = PR_sprintf_append(buf, ", "); 123 buf = PR_sprintf_append(buf, "%d minutes", ctx->minutes); 124 } 125 if (buf != NULL) 126 buf = PR_sprintf_append(buf, ", and "); 127 if (!buf && ctx->seconds == 0) { 128 int interval; 129 LL_L2I(interval, ctx->interval); 130 if (ctx->millisecs < 100) 131 buf = PR_sprintf_append(buf, "%d microseconds", interval); 132 else 133 buf = PR_sprintf_append(buf, "%d milliseconds", ctx->millisecs); 134 } else if (ctx->millisecs == 0) { 135 buf = PR_sprintf_append(buf, "%d seconds", ctx->seconds); 136 } else { 137 buf = PR_sprintf_append(buf, "%d.%03d seconds", 138 ctx->seconds, ctx->millisecs); 139 } 140 return buf; 141 } 142 143 void 144 Usage(char *progName) 145 { 146 fprintf(stderr, "Usage: %s [-s | -e] [-i iterations | -p period] " 147 "[-t threads]\n[-n none [-k keylength] [ [-g] -x exponent] |\n" 148 " -n token:nickname [-d certdir] [-w password] |\n" 149 " -h token [-d certdir] [-w password] [-g] [-k keylength] " 150 "[-x exponent] [-f pwfile]\n", 151 progName); 152 fprintf(stderr, "%-20s Cert database directory (default is ~/.netscape)\n", 153 "-d certdir"); 154 fprintf(stderr, "%-20s How many operations to perform\n", "-i iterations"); 155 fprintf(stderr, "%-20s How many seconds to run\n", "-p period"); 156 fprintf(stderr, "%-20s Perform signing (private key) operations\n", "-s"); 157 fprintf(stderr, "%-20s Perform encryption (public key) operations\n", "-e"); 158 fprintf(stderr, "%-20s Nickname of certificate or key, prefixed " 159 "by optional token name\n", 160 "-n nickname"); 161 fprintf(stderr, "%-20s PKCS#11 token to perform operation with.\n", 162 "-h token"); 163 fprintf(stderr, "%-20s key size in bits, from %d to %d\n", "-k keylength", 164 MIN_KEY_BITS, MAX_KEY_BITS); 165 fprintf(stderr, "%-20s token password\n", "-w password"); 166 fprintf(stderr, "%-20s temporary key generation. Not for token keys.\n", 167 "-g"); 168 fprintf(stderr, "%-20s set public exponent for keygen\n", "-x"); 169 fprintf(stderr, "%-20s Number of execution threads (default 1)\n", 170 "-t threads"); 171 exit(-1); 172 } 173 174 static void 175 dumpBytes(unsigned char *b, int l) 176 { 177 int i; 178 if (l <= 0) 179 return; 180 for (i = 0; i < l; ++i) { 181 if (i % 16 == 0) 182 printf("\t"); 183 printf(" %02x", b[i]); 184 if (i % 16 == 15) 185 printf("\n"); 186 } 187 if ((i % 16) != 0) 188 printf("\n"); 189 } 190 191 static void 192 dumpItem(SECItem *item, const char *description) 193 { 194 if (item->len & 1 && item->data[0] == 0) { 195 printf("%s: (%d bytes)\n", description, item->len - 1); 196 dumpBytes(item->data + 1, item->len - 1); 197 } else { 198 printf("%s: (%d bytes)\n", description, item->len); 199 dumpBytes(item->data, item->len); 200 } 201 } 202 203 void 204 printPrivKey(NSSLOWKEYPrivateKey *privKey) 205 { 206 RSAPrivateKey *rsa = &privKey->u.rsa; 207 208 dumpItem(&rsa->modulus, "n"); 209 dumpItem(&rsa->publicExponent, "e"); 210 dumpItem(&rsa->privateExponent, "d"); 211 dumpItem(&rsa->prime1, "P"); 212 dumpItem(&rsa->prime2, "Q"); 213 dumpItem(&rsa->exponent1, "d % (P-1)"); 214 dumpItem(&rsa->exponent2, "d % (Q-1)"); 215 dumpItem(&rsa->coefficient, "(Q ** -1) % P"); 216 puts(""); 217 } 218 219 typedef SECStatus (*RSAOp)(void *key, 220 unsigned char *output, 221 unsigned char *input); 222 223 typedef struct { 224 SECKEYPublicKey *pubKey; 225 SECKEYPrivateKey *privKey; 226 } PK11Keys; 227 228 SECStatus 229 PK11_PublicKeyOp(SECKEYPublicKey *key, 230 unsigned char *output, 231 unsigned char *input) 232 { 233 return PK11_PubEncryptRaw(key, output, input, key->u.rsa.modulus.len, 234 NULL); 235 } 236 237 SECStatus 238 PK11_PrivateKeyOp(PK11Keys *keys, 239 unsigned char *output, 240 unsigned char *input) 241 { 242 unsigned outLen = 0; 243 return PK11_PrivDecryptRaw(keys->privKey, 244 output, &outLen, 245 keys->pubKey->u.rsa.modulus.len, input, 246 keys->pubKey->u.rsa.modulus.len); 247 } 248 typedef struct ThreadRunDataStr ThreadRunData; 249 250 struct ThreadRunDataStr { 251 const PRBool *doIters; 252 const void *rsaKey; 253 const unsigned char *buf; 254 RSAOp fn; 255 int seconds; 256 long iters; 257 long iterRes; 258 PRErrorCode errNum; 259 SECStatus status; 260 }; 261 262 void 263 ThreadExecFunction(void *data) 264 { 265 ThreadRunData *tdata = (ThreadRunData *)data; 266 unsigned char buf2[BUFFER_BYTES]; 267 268 tdata->status = SECSuccess; 269 if (*tdata->doIters) { 270 long i = tdata->iters; 271 tdata->iterRes = 0; 272 while (i--) { 273 SECStatus rv = tdata->fn((void *)tdata->rsaKey, buf2, 274 (unsigned char *)tdata->buf); 275 if (rv != SECSuccess) { 276 tdata->errNum = PORT_GetError(); 277 tdata->status = rv; 278 break; 279 } 280 tdata->iterRes++; 281 } 282 } else { 283 PRIntervalTime total = PR_SecondsToInterval(tdata->seconds); 284 PRIntervalTime start = PR_IntervalNow(); 285 tdata->iterRes = 0; 286 while (PR_IntervalNow() - start < total) { 287 SECStatus rv = tdata->fn((void *)tdata->rsaKey, buf2, 288 (unsigned char *)tdata->buf); 289 if (rv != SECSuccess) { 290 tdata->errNum = PORT_GetError(); 291 tdata->status = rv; 292 break; 293 } 294 tdata->iterRes++; 295 } 296 } 297 } 298 299 #define INT_ARG(arg, def) atol(arg) > 0 ? atol(arg) : def 300 301 int 302 main(int argc, char **argv) 303 { 304 TimingContext *timeCtx = NULL; 305 SECKEYPublicKey *pubHighKey = NULL; 306 SECKEYPrivateKey *privHighKey = NULL; 307 NSSLOWKEYPrivateKey *privKey = NULL; 308 NSSLOWKEYPublicKey *pubKey = NULL; 309 CERTCertificate *cert = NULL; 310 char *progName = NULL; 311 char *secDir = NULL; 312 char *nickname = NULL; 313 char *slotname = NULL; 314 long keybits = 0; 315 RSAOp fn; 316 void *rsaKeyPtr = NULL; 317 PLOptState *optstate; 318 PLOptStatus optstatus; 319 long iters = DEFAULT_ITERS; 320 int i; 321 PRBool doPriv = PR_FALSE; 322 PRBool doPub = PR_FALSE; 323 int rv; 324 unsigned char buf[BUFFER_BYTES]; 325 unsigned char buf2[BUFFER_BYTES]; 326 int seconds = DEFAULT_DURATION; 327 PRBool doIters = PR_FALSE; 328 PRBool doTime = PR_FALSE; 329 PRBool useTokenKey = PR_FALSE; /* use PKCS#11 token 330 object key */ 331 PRBool useSessionKey = PR_FALSE; /* use PKCS#11 session 332 object key */ 333 PRBool useBLKey = PR_FALSE; /* use freebl */ 334 PK11SlotInfo *slot = NULL; /* slot for session 335 object key operations */ 336 PRBool doKeyGen = PR_FALSE; 337 int publicExponent = DEFAULT_EXPONENT; 338 PK11Keys keys; 339 int peCount = 0; 340 CK_BYTE pubEx[4]; 341 SECItem pe; 342 RSAPublicKey pubKeyStr; 343 int threadNum = DEFAULT_THREADS; 344 ThreadRunData **runDataArr = NULL; 345 PRThread **threadsArr = NULL; 346 347 progName = strrchr(argv[0], '/'); 348 if (!progName) 349 progName = strrchr(argv[0], '\\'); 350 progName = progName ? progName + 1 : argv[0]; 351 352 optstate = PL_CreateOptState(argc, argv, "d:ef:gh:i:k:n:p:st:w:x:"); 353 while ((optstatus = PL_GetNextOpt(optstate)) == PL_OPT_OK) { 354 switch (optstate->option) { 355 case '?': 356 Usage(progName); 357 break; 358 case 'd': 359 secDir = PORT_Strdup(optstate->value); 360 break; 361 case 'i': 362 iters = INT_ARG(optstate->value, DEFAULT_ITERS); 363 doIters = PR_TRUE; 364 break; 365 case 's': 366 doPriv = PR_TRUE; 367 break; 368 case 'e': 369 doPub = PR_TRUE; 370 break; 371 case 'g': 372 doKeyGen = PR_TRUE; 373 break; 374 case 'n': 375 nickname = PORT_Strdup(optstate->value); 376 /* for compatibility, nickname of "none" means go to freebl */ 377 if (nickname && strcmp(nickname, "none")) { 378 useTokenKey = PR_TRUE; 379 } else { 380 useBLKey = PR_TRUE; 381 } 382 break; 383 case 'p': 384 seconds = INT_ARG(optstate->value, DEFAULT_DURATION); 385 doTime = PR_TRUE; 386 break; 387 case 'h': 388 slotname = PORT_Strdup(optstate->value); 389 useSessionKey = PR_TRUE; 390 break; 391 case 'k': 392 keybits = INT_ARG(optstate->value, DEFAULT_KEY_BITS); 393 break; 394 case 'w': 395 pwData.data = PORT_Strdup(optstate->value); 396 ; 397 pwData.source = PW_PLAINTEXT; 398 break; 399 case 'f': 400 pwData.data = PORT_Strdup(optstate->value); 401 pwData.source = PW_FROMFILE; 402 break; 403 case 'x': 404 /* -x public exponent (for RSA keygen) */ 405 publicExponent = INT_ARG(optstate->value, DEFAULT_EXPONENT); 406 break; 407 case 't': 408 threadNum = INT_ARG(optstate->value, DEFAULT_THREADS); 409 break; 410 } 411 } 412 if (optstatus == PL_OPT_BAD) 413 Usage(progName); 414 415 if ((doPriv && doPub) || (doIters && doTime) || 416 ((useTokenKey + useSessionKey + useBLKey) != PR_TRUE) || 417 (useTokenKey && keybits) || (useTokenKey && doKeyGen) || 418 (keybits && (keybits < MIN_KEY_BITS || keybits > MAX_KEY_BITS))) { 419 Usage(progName); 420 } 421 422 if (doIters && doTime) 423 Usage(progName); 424 425 if (!doTime) { 426 doIters = PR_TRUE; 427 } 428 429 PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); 430 431 PK11_SetPasswordFunc(SECU_GetModulePassword); 432 secDir = SECU_ConfigDirectory(secDir); 433 434 if (useTokenKey || useSessionKey) { 435 rv = NSS_Init(secDir); 436 if (rv != SECSuccess) { 437 fprintf(stderr, "NSS_Init failed.\n"); 438 exit(1); 439 } 440 } else { 441 rv = NSS_NoDB_Init(NULL); 442 if (rv != SECSuccess) { 443 fprintf(stderr, "NSS_NoDB_Init failed.\n"); 444 exit(1); 445 } 446 } 447 448 if (useTokenKey) { 449 CK_OBJECT_HANDLE kh = CK_INVALID_HANDLE; 450 451 cert = PK11_FindCertFromNickname(nickname, &pwData); 452 if (cert == NULL) { 453 fprintf(stderr, 454 "Can't find certificate by name \"%s\"\n", nickname); 455 exit(1); 456 } 457 pubHighKey = CERT_ExtractPublicKey(cert); 458 if (pubHighKey == NULL) { 459 fprintf(stderr, "Can't extract public key from certificate"); 460 exit(1); 461 } 462 463 if (doPub) { 464 /* do public key ops */ 465 fn = (RSAOp)PK11_PublicKeyOp; 466 rsaKeyPtr = (void *)pubHighKey; 467 468 kh = PK11_ImportPublicKey(cert->slot, pubHighKey, PR_FALSE); 469 if (CK_INVALID_HANDLE == kh) { 470 fprintf(stderr, 471 "Unable to import public key to certificate slot."); 472 exit(1); 473 } 474 pubHighKey->pkcs11Slot = PK11_ReferenceSlot(cert->slot); 475 pubHighKey->pkcs11ID = kh; 476 printf("Using PKCS#11 for RSA encryption with token %s.\n", 477 PK11_GetTokenName(cert->slot)); 478 } else { 479 /* do private key ops */ 480 privHighKey = PK11_FindKeyByAnyCert(cert, &pwData); 481 if (privHighKey == NULL) { 482 fprintf(stderr, 483 "Can't find private key by name \"%s\"\n", nickname); 484 exit(1); 485 } 486 487 SECKEY_CacheStaticFlags(privHighKey); 488 fn = (RSAOp)PK11_PrivateKeyOp; 489 keys.privKey = privHighKey; 490 keys.pubKey = pubHighKey; 491 rsaKeyPtr = (void *)&keys; 492 printf("Using PKCS#11 for RSA decryption with token %s.\n", 493 PK11_GetTokenName(privHighKey->pkcs11Slot)); 494 } 495 } else 496 497 if (useSessionKey) { 498 /* use PKCS#11 session key objects */ 499 PK11RSAGenParams rsaparams; 500 void *params; 501 502 slot = PK11_FindSlotByName(slotname); /* locate target slot */ 503 if (!slot) { 504 fprintf(stderr, "Can't find slot \"%s\"\n", slotname); 505 exit(1); 506 } 507 508 /* do a temporary keygen in selected slot */ 509 if (!keybits) { 510 keybits = DEFAULT_KEY_BITS; 511 } 512 513 printf("Using PKCS#11 with %ld bits session key in token %s.\n", 514 keybits, PK11_GetTokenName(slot)); 515 516 rsaparams.keySizeInBits = keybits; 517 rsaparams.pe = publicExponent; 518 params = &rsaparams; 519 520 fprintf(stderr, "\nGenerating RSA key. This may take a few moments.\n"); 521 522 privHighKey = PK11_GenerateKeyPair(slot, CKM_RSA_PKCS_KEY_PAIR_GEN, 523 params, &pubHighKey, PR_FALSE, 524 PR_FALSE, (void *)&pwData); 525 if (!privHighKey) { 526 fprintf(stderr, 527 "Key generation failed in token \"%s\"\n", 528 PK11_GetTokenName(slot)); 529 exit(1); 530 } 531 532 SECKEY_CacheStaticFlags(privHighKey); 533 534 fprintf(stderr, "Keygen completed.\n"); 535 536 if (doPub) { 537 /* do public key operations */ 538 fn = (RSAOp)PK11_PublicKeyOp; 539 rsaKeyPtr = (void *)pubHighKey; 540 } else { 541 /* do private key operations */ 542 fn = (RSAOp)PK11_PrivateKeyOp; 543 keys.privKey = privHighKey; 544 keys.pubKey = pubHighKey; 545 rsaKeyPtr = (void *)&keys; 546 } 547 } else 548 549 { 550 /* use freebl directly */ 551 if (!keybits) { 552 keybits = DEFAULT_KEY_BITS; 553 } 554 if (!doKeyGen) { 555 if (keybits != DEFAULT_KEY_BITS) { 556 doKeyGen = PR_TRUE; 557 } 558 } 559 printf("Using freebl with %ld bits key.\n", keybits); 560 if (doKeyGen) { 561 fprintf(stderr, "\nGenerating RSA key. " 562 "This may take a few moments.\n"); 563 for (i = 0; i < 4; i++) { 564 if (peCount || (publicExponent & ((unsigned long)0xff000000L >> 565 (i * 8)))) { 566 pubEx[peCount] = (CK_BYTE)((publicExponent >> 567 (3 - i) * 8) & 568 0xff); 569 peCount++; 570 } 571 } 572 pe.len = peCount; 573 pe.data = &pubEx[0]; 574 pe.type = siBuffer; 575 576 rsaKeyPtr = RSA_NewKey(keybits, &pe); 577 fprintf(stderr, "Keygen completed.\n"); 578 } else { 579 /* use a hardcoded key */ 580 printf("Using hardcoded %ld bits key.\n", keybits); 581 if (doPub) { 582 pubKey = getDefaultRSAPublicKey(keybits); 583 } else { 584 privKey = getDefaultRSAPrivateKey(keybits); 585 } 586 } 587 588 if (doPub) { 589 /* do public key operations */ 590 fn = (RSAOp)RSA_PublicKeyOp; 591 if (rsaKeyPtr) { 592 /* convert the RSAPrivateKey to RSAPublicKey */ 593 pubKeyStr.arena = NULL; 594 pubKeyStr.modulus = ((RSAPrivateKey *)rsaKeyPtr)->modulus; 595 pubKeyStr.publicExponent = 596 ((RSAPrivateKey *)rsaKeyPtr)->publicExponent; 597 rsaKeyPtr = &pubKeyStr; 598 } else { 599 /* convert NSSLOWKeyPublicKey to RSAPublicKey */ 600 rsaKeyPtr = (void *)(&pubKey->u.rsa); 601 } 602 PORT_Assert(rsaKeyPtr); 603 } else { 604 /* do private key operations */ 605 fn = (RSAOp)RSA_PrivateKeyOp; 606 if (privKey) { 607 /* convert NSSLOWKeyPrivateKey to RSAPrivateKey */ 608 rsaKeyPtr = (void *)(&privKey->u.rsa); 609 } 610 PORT_Assert(rsaKeyPtr); 611 } 612 } 613 614 memset(buf, 1, sizeof buf); 615 rv = fn(rsaKeyPtr, buf2, buf); 616 if (rv != SECSuccess) { 617 PRErrorCode errNum; 618 const char *errStr = NULL; 619 620 errNum = PORT_GetError(); 621 if (errNum) 622 errStr = SECU_Strerror(errNum); 623 else 624 errNum = rv; 625 if (!errStr) 626 errStr = "(null)"; 627 fprintf(stderr, "Error in RSA operation: %d : %s\n", errNum, errStr); 628 exit(1); 629 } 630 631 threadsArr = (PRThread **)PORT_Alloc(threadNum * sizeof(PRThread *)); 632 runDataArr = (ThreadRunData **)PORT_Alloc(threadNum * sizeof(ThreadRunData *)); 633 timeCtx = CreateTimingContext(); 634 TimingBegin(timeCtx, PR_Now()); 635 for (i = 0; i < threadNum; i++) { 636 runDataArr[i] = (ThreadRunData *)PORT_Alloc(sizeof(ThreadRunData)); 637 runDataArr[i]->fn = fn; 638 runDataArr[i]->buf = buf; 639 runDataArr[i]->doIters = &doIters; 640 runDataArr[i]->rsaKey = rsaKeyPtr; 641 runDataArr[i]->seconds = seconds; 642 runDataArr[i]->iters = iters; 643 threadsArr[i] = 644 PR_CreateThread(PR_USER_THREAD, 645 ThreadExecFunction, 646 (void *)runDataArr[i], 647 PR_PRIORITY_NORMAL, 648 PR_GLOBAL_THREAD, 649 PR_JOINABLE_THREAD, 650 0); 651 } 652 iters = 0; 653 for (i = 0; i < threadNum; i++) { 654 PR_JoinThread(threadsArr[i]); 655 if (runDataArr[i]->status != SECSuccess) { 656 const char *errStr = SECU_Strerror(runDataArr[i]->errNum); 657 fprintf(stderr, "Thread %d: Error in RSA operation: %d : %s\n", 658 i, runDataArr[i]->errNum, errStr); 659 } else { 660 iters += runDataArr[i]->iterRes; 661 } 662 PORT_Free((void *)runDataArr[i]); 663 } 664 PORT_Free(runDataArr); 665 PORT_Free(threadsArr); 666 667 TimingEnd(timeCtx, PR_Now()); 668 669 printf("%ld iterations in %s\n", 670 iters, TimingGenerateString(timeCtx)); 671 printf("%.2f operations/s .\n", ((double)(iters) * (double)1000000.0) / (double)timeCtx->interval); 672 TimingDivide(timeCtx, iters); 673 printf("one operation every %s\n", TimingGenerateString(timeCtx)); 674 675 if (pubHighKey) { 676 SECKEY_DestroyPublicKey(pubHighKey); 677 } 678 679 if (privHighKey) { 680 SECKEY_DestroyPrivateKey(privHighKey); 681 } 682 683 if (cert) { 684 CERT_DestroyCertificate(cert); 685 } 686 687 if (NSS_Shutdown() != SECSuccess) { 688 exit(1); 689 } 690 691 return 0; 692 }