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(void);
25 extern NSSLOWKEYPublicKey *getDefaultRSAPublicKey(void);
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 *
CreateTimingContext(void)44 CreateTimingContext(void)
45 {
46 return PORT_Alloc(sizeof(TimingContext));
47 }
48
49 void
DestroyTimingContext(TimingContext * ctx)50 DestroyTimingContext(TimingContext *ctx)
51 {
52 PORT_Free(ctx);
53 }
54
55 void
TimingBegin(TimingContext * ctx,PRTime begin)56 TimingBegin(TimingContext *ctx, PRTime begin)
57 {
58 ctx->start = begin;
59 }
60
61 static void
timingUpdate(TimingContext * ctx)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
TimingEnd(TimingContext * ctx,PRTime end)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
TimingDivide(TimingContext * ctx,int divisor)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 *
TimingGenerateString(TimingContext * ctx)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
Usage(char * progName)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
dumpBytes(unsigned char * b,int l)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
dumpItem(SECItem * item,const char * description)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
printPrivKey(NSSLOWKEYPrivateKey * privKey)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
PK11_PublicKeyOp(SECKEYPublicKey * key,unsigned char * output,unsigned char * input)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
PK11_PrivateKeyOp(PK11Keys * keys,unsigned char * output,unsigned char * input)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
ThreadExecFunction(void * data)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
main(int argc,char ** argv)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 *rsaKey = 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 int calcThreads = 0;
347
348 progName = strrchr(argv[0], '/');
349 if (!progName)
350 progName = strrchr(argv[0], '\\');
351 progName = progName ? progName + 1 : argv[0];
352
353 optstate = PL_CreateOptState(argc, argv, "d:ef:gh:i:k:n:p:st:w:x:");
354 while ((optstatus = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
355 switch (optstate->option) {
356 case '?':
357 Usage(progName);
358 break;
359 case 'd':
360 secDir = PORT_Strdup(optstate->value);
361 break;
362 case 'i':
363 iters = INT_ARG(optstate->value, DEFAULT_ITERS);
364 doIters = PR_TRUE;
365 break;
366 case 's':
367 doPriv = PR_TRUE;
368 break;
369 case 'e':
370 doPub = PR_TRUE;
371 break;
372 case 'g':
373 doKeyGen = PR_TRUE;
374 break;
375 case 'n':
376 nickname = PORT_Strdup(optstate->value);
377 /* for compatibility, nickname of "none" means go to freebl */
378 if (nickname && strcmp(nickname, "none")) {
379 useTokenKey = PR_TRUE;
380 } else {
381 useBLKey = PR_TRUE;
382 }
383 break;
384 case 'p':
385 seconds = INT_ARG(optstate->value, DEFAULT_DURATION);
386 doTime = PR_TRUE;
387 break;
388 case 'h':
389 slotname = PORT_Strdup(optstate->value);
390 useSessionKey = PR_TRUE;
391 break;
392 case 'k':
393 keybits = INT_ARG(optstate->value, DEFAULT_KEY_BITS);
394 break;
395 case 'w':
396 pwData.data = PORT_Strdup(optstate->value);
397 ;
398 pwData.source = PW_PLAINTEXT;
399 break;
400 case 'f':
401 pwData.data = PORT_Strdup(optstate->value);
402 pwData.source = PW_FROMFILE;
403 break;
404 case 'x':
405 /* -x public exponent (for RSA keygen) */
406 publicExponent = INT_ARG(optstate->value, DEFAULT_EXPONENT);
407 break;
408 case 't':
409 threadNum = INT_ARG(optstate->value, DEFAULT_THREADS);
410 break;
411 }
412 }
413 if (optstatus == PL_OPT_BAD)
414 Usage(progName);
415
416 if ((doPriv && doPub) || (doIters && doTime) ||
417 ((useTokenKey + useSessionKey + useBLKey) != PR_TRUE) ||
418 (useTokenKey && keybits) || (useTokenKey && doKeyGen) ||
419 (keybits && (keybits < MIN_KEY_BITS || keybits > MAX_KEY_BITS))) {
420 Usage(progName);
421 }
422
423 if (doIters && doTime)
424 Usage(progName);
425
426 if (!doTime) {
427 doIters = PR_TRUE;
428 }
429
430 PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
431
432 PK11_SetPasswordFunc(SECU_GetModulePassword);
433 secDir = SECU_ConfigDirectory(secDir);
434
435 if (useTokenKey || useSessionKey) {
436 rv = NSS_Init(secDir);
437 if (rv != SECSuccess) {
438 fprintf(stderr, "NSS_Init failed.\n");
439 exit(1);
440 }
441 } else {
442 rv = NSS_NoDB_Init(NULL);
443 if (rv != SECSuccess) {
444 fprintf(stderr, "NSS_NoDB_Init failed.\n");
445 exit(1);
446 }
447 }
448
449 if (useTokenKey) {
450 CK_OBJECT_HANDLE kh = CK_INVALID_HANDLE;
451
452 cert = PK11_FindCertFromNickname(nickname, &pwData);
453 if (cert == NULL) {
454 fprintf(stderr,
455 "Can't find certificate by name \"%s\"\n", nickname);
456 exit(1);
457 }
458 pubHighKey = CERT_ExtractPublicKey(cert);
459 if (pubHighKey == NULL) {
460 fprintf(stderr, "Can't extract public key from certificate");
461 exit(1);
462 }
463
464 if (doPub) {
465 /* do public key ops */
466 fn = (RSAOp)PK11_PublicKeyOp;
467 rsaKey = (void *)pubHighKey;
468
469 kh = PK11_ImportPublicKey(cert->slot, pubHighKey, PR_FALSE);
470 if (CK_INVALID_HANDLE == kh) {
471 fprintf(stderr,
472 "Unable to import public key to certificate slot.");
473 exit(1);
474 }
475 pubHighKey->pkcs11Slot = PK11_ReferenceSlot(cert->slot);
476 pubHighKey->pkcs11ID = kh;
477 printf("Using PKCS#11 for RSA encryption with token %s.\n",
478 PK11_GetTokenName(cert->slot));
479 } else {
480 /* do private key ops */
481 privHighKey = PK11_FindKeyByAnyCert(cert, &pwData);
482 if (privHighKey == NULL) {
483 fprintf(stderr,
484 "Can't find private key by name \"%s\"\n", nickname);
485 exit(1);
486 }
487
488 SECKEY_CacheStaticFlags(privHighKey);
489 fn = (RSAOp)PK11_PrivateKeyOp;
490 keys.privKey = privHighKey;
491 keys.pubKey = pubHighKey;
492 rsaKey = (void *)&keys;
493 printf("Using PKCS#11 for RSA decryption with token %s.\n",
494 PK11_GetTokenName(privHighKey->pkcs11Slot));
495 }
496 } else
497
498 if (useSessionKey) {
499 /* use PKCS#11 session key objects */
500 PK11RSAGenParams rsaparams;
501 void *params;
502
503 slot = PK11_FindSlotByName(slotname); /* locate target slot */
504 if (!slot) {
505 fprintf(stderr, "Can't find slot \"%s\"\n", slotname);
506 exit(1);
507 }
508
509 /* do a temporary keygen in selected slot */
510 if (!keybits) {
511 keybits = DEFAULT_KEY_BITS;
512 }
513
514 printf("Using PKCS#11 with %ld bits session key in token %s.\n",
515 keybits, PK11_GetTokenName(slot));
516
517 rsaparams.keySizeInBits = keybits;
518 rsaparams.pe = publicExponent;
519 params = &rsaparams;
520
521 fprintf(stderr, "\nGenerating RSA key. This may take a few moments.\n");
522
523 privHighKey = PK11_GenerateKeyPair(slot, CKM_RSA_PKCS_KEY_PAIR_GEN,
524 params, &pubHighKey, PR_FALSE,
525 PR_FALSE, (void *)&pwData);
526 if (!privHighKey) {
527 fprintf(stderr,
528 "Key generation failed in token \"%s\"\n",
529 PK11_GetTokenName(slot));
530 exit(1);
531 }
532
533 SECKEY_CacheStaticFlags(privHighKey);
534
535 fprintf(stderr, "Keygen completed.\n");
536
537 if (doPub) {
538 /* do public key operations */
539 fn = (RSAOp)PK11_PublicKeyOp;
540 rsaKey = (void *)pubHighKey;
541 } else {
542 /* do private key operations */
543 fn = (RSAOp)PK11_PrivateKeyOp;
544 keys.privKey = privHighKey;
545 keys.pubKey = pubHighKey;
546 rsaKey = (void *)&keys;
547 }
548 } else
549
550 {
551 /* use freebl directly */
552 if (!keybits) {
553 keybits = DEFAULT_KEY_BITS;
554 }
555 if (!doKeyGen) {
556 if (keybits != DEFAULT_KEY_BITS) {
557 doKeyGen = PR_TRUE;
558 }
559 }
560 printf("Using freebl with %ld bits key.\n", keybits);
561 if (doKeyGen) {
562 fprintf(stderr, "\nGenerating RSA key. "
563 "This may take a few moments.\n");
564 for (i = 0; i < 4; i++) {
565 if (peCount || (publicExponent & ((unsigned long)0xff000000L >>
566 (i * 8)))) {
567 pubEx[peCount] = (CK_BYTE)((publicExponent >>
568 (3 - i) * 8) &
569 0xff);
570 peCount++;
571 }
572 }
573 pe.len = peCount;
574 pe.data = &pubEx[0];
575 pe.type = siBuffer;
576
577 rsaKey = RSA_NewKey(keybits, &pe);
578 fprintf(stderr, "Keygen completed.\n");
579 } else {
580 /* use a hardcoded key */
581 printf("Using hardcoded %ld bits key.\n", keybits);
582 if (doPub) {
583 pubKey = getDefaultRSAPublicKey();
584 } else {
585 privKey = getDefaultRSAPrivateKey();
586 }
587 }
588
589 if (doPub) {
590 /* do public key operations */
591 fn = (RSAOp)RSA_PublicKeyOp;
592 if (rsaKey) {
593 /* convert the RSAPrivateKey to RSAPublicKey */
594 pubKeyStr.arena = NULL;
595 pubKeyStr.modulus = ((RSAPrivateKey *)rsaKey)->modulus;
596 pubKeyStr.publicExponent =
597 ((RSAPrivateKey *)rsaKey)->publicExponent;
598 rsaKey = &pubKeyStr;
599 } else {
600 /* convert NSSLOWKeyPublicKey to RSAPublicKey */
601 rsaKey = (void *)(&pubKey->u.rsa);
602 }
603 PORT_Assert(rsaKey);
604 } else {
605 /* do private key operations */
606 fn = (RSAOp)RSA_PrivateKeyOp;
607 if (privKey) {
608 /* convert NSSLOWKeyPrivateKey to RSAPrivateKey */
609 rsaKey = (void *)(&privKey->u.rsa);
610 }
611 PORT_Assert(rsaKey);
612 }
613 }
614
615 memset(buf, 1, sizeof buf);
616 rv = fn(rsaKey, buf2, buf);
617 if (rv != SECSuccess) {
618 PRErrorCode errNum;
619 const char *errStr = NULL;
620
621 errNum = PORT_GetError();
622 if (errNum)
623 errStr = SECU_Strerror(errNum);
624 else
625 errNum = rv;
626 if (!errStr)
627 errStr = "(null)";
628 fprintf(stderr, "Error in RSA operation: %d : %s\n", errNum, errStr);
629 exit(1);
630 }
631
632 threadsArr = (PRThread **)PORT_Alloc(threadNum * sizeof(PRThread *));
633 runDataArr = (ThreadRunData **)PORT_Alloc(threadNum * sizeof(ThreadRunData *));
634 timeCtx = CreateTimingContext();
635 TimingBegin(timeCtx, PR_Now());
636 for (i = 0; i < threadNum; i++) {
637 runDataArr[i] = (ThreadRunData *)PORT_Alloc(sizeof(ThreadRunData));
638 runDataArr[i]->fn = fn;
639 runDataArr[i]->buf = buf;
640 runDataArr[i]->doIters = &doIters;
641 runDataArr[i]->rsaKey = rsaKey;
642 runDataArr[i]->seconds = seconds;
643 runDataArr[i]->iters = iters;
644 threadsArr[i] =
645 PR_CreateThread(PR_USER_THREAD,
646 ThreadExecFunction,
647 (void *)runDataArr[i],
648 PR_PRIORITY_NORMAL,
649 PR_GLOBAL_THREAD,
650 PR_JOINABLE_THREAD,
651 0);
652 }
653 iters = 0;
654 calcThreads = 0;
655 for (i = 0; i < threadNum; i++, calcThreads++) {
656 PR_JoinThread(threadsArr[i]);
657 if (runDataArr[i]->status != SECSuccess) {
658 const char *errStr = SECU_Strerror(runDataArr[i]->errNum);
659 fprintf(stderr, "Thread %d: Error in RSA operation: %d : %s\n",
660 i, runDataArr[i]->errNum, errStr);
661 calcThreads -= 1;
662 } else {
663 iters += runDataArr[i]->iterRes;
664 }
665 PORT_Free((void *)runDataArr[i]);
666 }
667 PORT_Free(runDataArr);
668 PORT_Free(threadsArr);
669
670 TimingEnd(timeCtx, PR_Now());
671
672 printf("%ld iterations in %s\n",
673 iters, TimingGenerateString(timeCtx));
674 printf("%.2f operations/s .\n", ((double)(iters) * (double)1000000.0) / (double)timeCtx->interval);
675 TimingDivide(timeCtx, iters);
676 printf("one operation every %s\n", TimingGenerateString(timeCtx));
677
678 if (pubHighKey) {
679 SECKEY_DestroyPublicKey(pubHighKey);
680 }
681
682 if (privHighKey) {
683 SECKEY_DestroyPrivateKey(privHighKey);
684 }
685
686 if (cert) {
687 CERT_DestroyCertificate(cert);
688 }
689
690 if (NSS_Shutdown() != SECSuccess) {
691 exit(1);
692 }
693
694 return 0;
695 }
696