nss: import at 3.0.1 beta 1
[mozilla-nss.git] / security / nss / cmd / rsaperf / rsaperf.c
blob635f6c0b67c50c2eaa9bc9aae25607314228e460
1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
12 * License.
14 * The Original Code is the Netscape security libraries.
16 * The Initial Developer of the Original Code is
17 * Netscape Communications Corporation.
18 * Portions created by the Initial Developer are Copyright (C) 1998-2000
19 * the Initial Developer. All Rights Reserved.
21 * Contributor(s):
23 * Alternatively, the contents of this file may be used under the terms of
24 * either the GNU General Public License Version 2 or later (the "GPL"), or
25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 * in which case the provisions of the GPL or the LGPL are applicable instead
27 * of those above. If you wish to allow use of your version of this file only
28 * under the terms of either the GPL or the LGPL, and not to allow others to
29 * use your version of this file under the terms of the MPL, indicate your
30 * decision by deleting the provisions above and replace them with the notice
31 * and other provisions required by the GPL or the LGPL. If you do not delete
32 * the provisions above, a recipient may use your version of this file under
33 * the terms of any one of the MPL, the GPL or the LGPL.
35 * ***** END LICENSE BLOCK ***** */
37 #include "seccomon.h"
38 #include "cert.h"
39 #include "secutil.h"
40 #include "nspr.h"
41 #include "nss.h"
42 #include "blapi.h"
43 #include "plgetopt.h"
44 #include "lowkeyi.h"
45 #include "pk11pub.h"
48 #define DEFAULT_ITERS 10
49 #define DEFAULT_DURATION 10
50 #define DEFAULT_KEY_BITS 1024
51 #define MIN_KEY_BITS 512
52 #define MAX_KEY_BITS 65536
53 #define BUFFER_BYTES MAX_KEY_BITS / 8
54 #define DEFAULT_THREADS 1
55 #define DEFAULT_EXPONENT 0x10001
57 extern NSSLOWKEYPrivateKey * getDefaultRSAPrivateKey(void);
58 extern NSSLOWKEYPublicKey * getDefaultRSAPublicKey(void);
60 secuPWData pwData = { PW_NONE, NULL };
62 typedef struct TimingContextStr TimingContext;
64 struct TimingContextStr {
65 PRTime start;
66 PRTime end;
67 PRTime interval;
69 long days;
70 int hours;
71 int minutes;
72 int seconds;
73 int millisecs;
76 TimingContext *CreateTimingContext(void) {
77 return PORT_Alloc(sizeof(TimingContext));
80 void DestroyTimingContext(TimingContext *ctx) {
81 PORT_Free(ctx);
84 void TimingBegin(TimingContext *ctx, PRTime begin) {
85 ctx->start = begin;
88 static void timingUpdate(TimingContext *ctx) {
89 PRInt64 tmp, remaining;
90 PRInt64 L1000,L60,L24;
92 LL_I2L(L1000,1000);
93 LL_I2L(L60,60);
94 LL_I2L(L24,24);
96 LL_DIV(remaining, ctx->interval, L1000);
97 LL_MOD(tmp, remaining, L1000);
98 LL_L2I(ctx->millisecs, tmp);
99 LL_DIV(remaining, remaining, L1000);
100 LL_MOD(tmp, remaining, L60);
101 LL_L2I(ctx->seconds, tmp);
102 LL_DIV(remaining, remaining, L60);
103 LL_MOD(tmp, remaining, L60);
104 LL_L2I(ctx->minutes, tmp);
105 LL_DIV(remaining, remaining, L60);
106 LL_MOD(tmp, remaining, L24);
107 LL_L2I(ctx->hours, tmp);
108 LL_DIV(remaining, remaining, L24);
109 LL_L2I(ctx->days, remaining);
112 void TimingEnd(TimingContext *ctx, PRTime end) {
113 ctx->end = end;
114 LL_SUB(ctx->interval, ctx->end, ctx->start);
115 PORT_Assert(LL_GE_ZERO(ctx->interval));
116 timingUpdate(ctx);
119 void TimingDivide(TimingContext *ctx, int divisor) {
120 PRInt64 tmp;
122 LL_I2L(tmp, divisor);
123 LL_DIV(ctx->interval, ctx->interval, tmp);
125 timingUpdate(ctx);
128 char *TimingGenerateString(TimingContext *ctx) {
129 char *buf = NULL;
131 if (ctx->days != 0) {
132 buf = PR_sprintf_append(buf, "%d days", ctx->days);
134 if (ctx->hours != 0) {
135 if (buf != NULL) buf = PR_sprintf_append(buf, ", ");
136 buf = PR_sprintf_append(buf, "%d hours", ctx->hours);
138 if (ctx->minutes != 0) {
139 if (buf != NULL) buf = PR_sprintf_append(buf, ", ");
140 buf = PR_sprintf_append(buf, "%d minutes", ctx->minutes);
142 if (buf != NULL) buf = PR_sprintf_append(buf, ", and ");
143 if (!buf && ctx->seconds == 0) {
144 int interval;
145 LL_L2I(interval, ctx->interval);
146 if (ctx->millisecs < 100)
147 buf = PR_sprintf_append(buf, "%d microseconds", interval);
148 else
149 buf = PR_sprintf_append(buf, "%d milliseconds", ctx->millisecs);
150 } else if (ctx->millisecs == 0) {
151 buf = PR_sprintf_append(buf, "%d seconds", ctx->seconds);
152 } else {
153 buf = PR_sprintf_append(buf, "%d.%03d seconds",
154 ctx->seconds, ctx->millisecs);
156 return buf;
159 void
160 Usage(char *progName)
162 fprintf(stderr, "Usage: %s [-s | -e] [-i iterations | -p period] "
163 "[-t threads]\n[-n none [-k keylength] [ [-g] -x exponent] |\n"
164 " -n token:nickname [-d certdir] [-w password] |\n"
165 " -h token [-d certdir] [-w password] [-g] [-k keylength] "
166 "[-x exponent] ]\n",
167 progName);
168 fprintf(stderr, "%-20s Cert database directory (default is ~/.netscape)\n",
169 "-d certdir");
170 fprintf(stderr, "%-20s How many operations to perform\n", "-i iterations");
171 fprintf(stderr, "%-20s How many seconds to run\n", "-p period");
172 fprintf(stderr, "%-20s Perform signing (private key) operations\n", "-s");
173 fprintf(stderr, "%-20s Perform encryption (public key) operations\n","-e");
174 fprintf(stderr, "%-20s Nickname of certificate or key, prefixed "
175 "by optional token name\n", "-n nickname");
176 fprintf(stderr, "%-20s PKCS#11 token to perform operation with.\n",
177 "-h token");
178 fprintf(stderr, "%-20s key size in bits, from %d to %d\n", "-k keylength",
179 MIN_KEY_BITS, MAX_KEY_BITS);
180 fprintf(stderr, "%-20s token password\n", "-w password");
181 fprintf(stderr, "%-20s temporary key generation. Not for token keys.\n",
182 "-g");
183 fprintf(stderr, "%-20s set public exponent for keygen\n", "-x");
184 fprintf(stderr, "%-20s Number of execution threads (default 1)\n",
185 "-t threads");
186 exit(-1);
189 static void
190 dumpBytes( unsigned char * b, int l)
192 int i;
193 if (l <= 0)
194 return;
195 for (i = 0; i < l; ++i) {
196 if (i % 16 == 0)
197 printf("\t");
198 printf(" %02x", b[i]);
199 if (i % 16 == 15)
200 printf("\n");
202 if ((i % 16) != 0)
203 printf("\n");
206 static void
207 dumpItem( SECItem * item, const char * description)
209 if (item->len & 1 && item->data[0] == 0) {
210 printf("%s: (%d bytes)\n", description, item->len - 1);
211 dumpBytes(item->data + 1, item->len - 1);
212 } else {
213 printf("%s: (%d bytes)\n", description, item->len);
214 dumpBytes(item->data, item->len);
218 void
219 printPrivKey(NSSLOWKEYPrivateKey * privKey)
221 RSAPrivateKey *rsa = &privKey->u.rsa;
223 dumpItem( &rsa->modulus, "n");
224 dumpItem( &rsa->publicExponent, "e");
225 dumpItem( &rsa->privateExponent, "d");
226 dumpItem( &rsa->prime1, "P");
227 dumpItem( &rsa->prime2, "Q");
228 dumpItem( &rsa->exponent1, "d % (P-1)");
229 dumpItem( &rsa->exponent2, "d % (Q-1)");
230 dumpItem( &rsa->coefficient, "(Q ** -1) % P");
231 puts("");
234 typedef SECStatus (* RSAOp)(void * key,
235 unsigned char * output,
236 unsigned char * input);
238 typedef struct {
239 SECKEYPublicKey* pubKey;
240 SECKEYPrivateKey* privKey;
241 } PK11Keys;
244 SECStatus PK11_PublicKeyOp (SECKEYPublicKey* key,
245 unsigned char * output,
246 unsigned char * input)
248 return PK11_PubEncryptRaw(key, output, input, key->u.rsa.modulus.len,
249 NULL);
252 SECStatus PK11_PrivateKeyOp (PK11Keys* keys,
253 unsigned char * output,
254 unsigned char * input)
256 unsigned outLen = 0;
257 return PK11_PrivDecryptRaw(keys->privKey,
258 output, &outLen,
259 keys->pubKey->u.rsa.modulus.len, input,
260 keys->pubKey->u.rsa.modulus.len);
262 typedef struct ThreadRunDataStr ThreadRunData;
264 struct ThreadRunDataStr {
265 const PRBool *doIters;
266 const void *rsaKey;
267 const unsigned char *buf;
268 RSAOp fn;
269 int seconds;
270 long iters;
271 long iterRes;
272 PRErrorCode errNum;
273 SECStatus status;
277 void ThreadExecFunction(void *data)
279 ThreadRunData *tdata = (ThreadRunData*)data;
280 unsigned char buf2[BUFFER_BYTES];
282 tdata->status = SECSuccess;
283 if (*tdata->doIters) {
284 long i = tdata->iters;
285 tdata->iterRes = 0;
286 while (i--) {
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;
294 tdata->iterRes++;
296 } else {
297 PRIntervalTime total = PR_SecondsToInterval(tdata->seconds);
298 PRIntervalTime start = PR_IntervalNow();
299 tdata->iterRes = 0;
300 while (PR_IntervalNow() - start < total) {
301 SECStatus rv = tdata->fn((void*)tdata->rsaKey, buf2,
302 (unsigned char*)tdata->buf);
303 if (rv != SECSuccess) {
304 tdata->errNum = PORT_GetError();
305 tdata->status = rv;
306 break;
308 tdata->iterRes++;
313 #define INT_ARG(arg,def) atol(arg)>0?atol(arg):def
316 main(int argc, char **argv)
318 TimingContext * timeCtx = NULL;
319 SECKEYPublicKey * pubHighKey = NULL;
320 SECKEYPrivateKey * privHighKey = NULL;
321 NSSLOWKEYPrivateKey * privKey = NULL;
322 NSSLOWKEYPublicKey * pubKey = NULL;
323 CERTCertificate * cert = NULL;
324 char * progName = NULL;
325 char * secDir = NULL;
326 char * nickname = NULL;
327 char * slotname = NULL;
328 char * password = NULL;
329 long keybits = 0;
330 RSAOp fn;
331 void * rsaKey = NULL;
332 PLOptState * optstate;
333 PLOptStatus optstatus;
334 long iters = DEFAULT_ITERS;
335 int i;
336 PRBool doPriv = PR_FALSE;
337 PRBool doPub = PR_FALSE;
338 int rv;
339 unsigned char buf[BUFFER_BYTES];
340 unsigned char buf2[BUFFER_BYTES];
341 int seconds = DEFAULT_DURATION;
342 PRBool doIters = PR_FALSE;
343 PRBool doTime = PR_FALSE;
344 PRBool useTokenKey = PR_FALSE; /* use PKCS#11 token
345 object key */
346 PRBool useSessionKey = PR_FALSE; /* use PKCS#11 session
347 object key */
348 PRBool useBLKey = PR_FALSE; /* use freebl */
349 PK11SlotInfo* slot = NULL; /* slot for session
350 object key operations */
351 PRBool doKeyGen = PR_FALSE;
352 int publicExponent = DEFAULT_EXPONENT;
353 PK11Keys keys;
354 int peCount = 0;
355 CK_BYTE pubEx[4];
356 SECItem pe;
357 RSAPublicKey pubKeyStr;
358 int threadNum = DEFAULT_THREADS;
359 ThreadRunData ** runDataArr = NULL;
360 PRThread ** threadsArr = NULL;
361 int calcThreads = 0;
363 progName = strrchr(argv[0], '/');
364 if (!progName)
365 progName = strrchr(argv[0], '\\');
366 progName = progName ? progName+1 : argv[0];
368 optstate = PL_CreateOptState(argc, argv, "d:i:sen:p:t:h:k:w:gx:");
369 while ((optstatus = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
370 switch (optstate->option) {
371 case '?':
372 Usage(progName);
373 break;
374 case 'd':
375 secDir = PORT_Strdup(optstate->value);
376 break;
377 case 'i':
378 iters = INT_ARG(optstate->value, DEFAULT_ITERS);
379 doIters = PR_TRUE;
380 break;
381 case 's':
382 doPriv = PR_TRUE;
383 break;
384 case 'e':
385 doPub = PR_TRUE;
386 break;
387 case 'g':
388 doKeyGen = PR_TRUE;
389 break;
390 case 'n':
391 nickname = PORT_Strdup(optstate->value);
392 /* for compatibility, nickname of "none" means go to freebl */
393 if (nickname && strcmp(nickname, "none")) {
394 useTokenKey = PR_TRUE;
395 } else {
396 useBLKey = PR_TRUE;
398 break;
399 case 'p':
400 seconds = INT_ARG(optstate->value, DEFAULT_DURATION);
401 doTime = PR_TRUE;
402 break;
403 case 'h':
404 slotname = PORT_Strdup(optstate->value);
405 useSessionKey = PR_TRUE;
406 break;
407 case 'k':
408 keybits = INT_ARG(optstate->value, DEFAULT_KEY_BITS);
409 break;
410 case 'w':
411 password = PORT_Strdup(optstate->value);
412 pwData.data = password;
413 pwData.source = PW_PLAINTEXT;
414 break;
415 case 'x':
416 /* -x public exponent (for RSA keygen) */
417 publicExponent = INT_ARG(optstate->value, DEFAULT_EXPONENT);
418 break;
419 case 't':
420 threadNum = INT_ARG(optstate->value, DEFAULT_THREADS);
421 break;
424 if (optstatus == PL_OPT_BAD)
425 Usage(progName);
427 if ((doPriv && doPub) || (doIters && doTime) ||
428 ((useTokenKey + useSessionKey + useBLKey) != PR_TRUE) ||
429 (useTokenKey && keybits) || (useTokenKey && doKeyGen) ||
430 (keybits && (keybits<MIN_KEY_BITS || keybits>MAX_KEY_BITS))) {
431 Usage(progName);
434 if (!doPriv && !doPub) doPriv = PR_TRUE;
436 if (doIters && doTime) Usage(progName);
438 if (!doTime) {
439 doIters = PR_TRUE;
442 PR_Init( PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
444 PK11_SetPasswordFunc(SECU_GetModulePassword);
445 secDir = SECU_ConfigDirectory(secDir);
447 if (useTokenKey || useSessionKey) {
448 rv = NSS_Init(secDir);
449 if (rv != SECSuccess) {
450 fprintf(stderr, "NSS_Init failed.\n");
451 exit(1);
453 } else {
454 rv = NSS_NoDB_Init(NULL);
455 if (rv != SECSuccess) {
456 fprintf(stderr, "NSS_NoDB_Init failed.\n");
457 exit(1);
461 if (useTokenKey) {
462 CK_OBJECT_HANDLE kh = CK_INVALID_HANDLE;
463 CERTCertDBHandle* certdb = NULL;
464 certdb = CERT_GetDefaultCertDB();
466 cert = PK11_FindCertFromNickname(nickname, &pwData);
467 if (cert == NULL) {
468 fprintf(stderr,
469 "Can't find certificate by name \"%s\"\n", nickname);
470 exit(1);
472 pubHighKey = CERT_ExtractPublicKey(cert);
473 if (pubHighKey == NULL) {
474 fprintf(stderr, "Can't extract public key from certificate");
475 exit(1);
478 if (doPub) {
479 /* do public key ops */
480 fn = (RSAOp)PK11_PublicKeyOp;
481 rsaKey = (void *) pubHighKey;
483 kh = PK11_ImportPublicKey(cert->slot, pubHighKey, PR_FALSE);
484 if (CK_INVALID_HANDLE == kh) {
485 fprintf(stderr,
486 "Unable to import public key to certificate slot.");
487 exit(1);
489 pubHighKey->pkcs11Slot = PK11_ReferenceSlot(cert->slot);
490 pubHighKey->pkcs11ID = kh;
491 printf("Using PKCS#11 for RSA encryption with token %s.\n",
492 PK11_GetTokenName(cert->slot));
493 } else {
494 /* do private key ops */
495 privHighKey = PK11_FindKeyByAnyCert(cert, &pwData);
496 if (privHighKey == NULL) {
497 fprintf(stderr,
498 "Can't find private key by name \"%s\"\n", nickname);
499 exit(1);
502 SECKEY_CacheStaticFlags(privHighKey);
503 fn = (RSAOp)PK11_PrivateKeyOp;
504 keys.privKey = privHighKey;
505 keys.pubKey = pubHighKey;
506 rsaKey = (void *) &keys;
507 printf("Using PKCS#11 for RSA decryption with token %s.\n",
508 PK11_GetTokenName(privHighKey->pkcs11Slot));
510 } else
512 if (useSessionKey) {
513 /* use PKCS#11 session key objects */
514 PK11RSAGenParams rsaparams;
515 void * params;
517 slot = PK11_FindSlotByName(slotname); /* locate target slot */
518 if (!slot) {
519 fprintf(stderr, "Can't find slot \"%s\"\n", slotname);
520 exit(1);
523 doKeyGen = PR_TRUE; /* Always do a keygen for session keys.
524 Import of hardcoded key is not supported */
525 /* do a temporary keygen in selected slot */
526 if (!keybits) {
527 keybits = DEFAULT_KEY_BITS;
530 printf("Using PKCS#11 with %ld bits session key in token %s.\n",
531 keybits, PK11_GetTokenName(slot));
533 rsaparams.keySizeInBits = keybits;
534 rsaparams.pe = publicExponent;
535 params = &rsaparams;
537 fprintf(stderr,"\nGenerating RSA key. This may take a few moments.\n");
539 privHighKey = PK11_GenerateKeyPair(slot, CKM_RSA_PKCS_KEY_PAIR_GEN,
540 params, &pubHighKey, PR_FALSE,
541 PR_FALSE, (void*)&pwData);
542 if (!privHighKey) {
543 fprintf(stderr,
544 "Key generation failed in token \"%s\"\n",
545 PK11_GetTokenName(slot));
546 exit(1);
549 SECKEY_CacheStaticFlags(privHighKey);
551 fprintf(stderr,"Keygen completed.\n");
553 if (doPub) {
554 /* do public key operations */
555 fn = (RSAOp)PK11_PublicKeyOp;
556 rsaKey = (void *) pubHighKey;
557 } else {
558 /* do private key operations */
559 fn = (RSAOp)PK11_PrivateKeyOp;
560 keys.privKey = privHighKey;
561 keys.pubKey = pubHighKey;
562 rsaKey = (void *) &keys;
564 } else
567 /* use freebl directly */
568 if (!keybits) {
569 keybits = DEFAULT_KEY_BITS;
571 if (!doKeyGen) {
572 if (keybits != DEFAULT_KEY_BITS) {
573 doKeyGen = PR_TRUE;
576 printf("Using freebl with %ld bits key.\n", keybits);
577 if (doKeyGen) {
578 fprintf(stderr,"\nGenerating RSA key. "
579 "This may take a few moments.\n");
580 for (i=0; i < 4; i++) {
581 if (peCount || (publicExponent & ((unsigned long)0xff000000L >>
582 (i*8)))) {
583 pubEx[peCount] = (CK_BYTE)((publicExponent >>
584 (3-i)*8) & 0xff);
585 peCount++;
588 pe.len = peCount;
589 pe.data = &pubEx[0];
590 pe.type = siBuffer;
592 rsaKey = RSA_NewKey(keybits, &pe);
593 fprintf(stderr,"Keygen completed.\n");
594 } else {
595 /* use a hardcoded key */
596 printf("Using hardcoded %ld bits key.\n", keybits);
597 if (doPub) {
598 pubKey = getDefaultRSAPublicKey();
599 } else {
600 privKey = getDefaultRSAPrivateKey();
604 if (doPub) {
605 /* do public key operations */
606 fn = (RSAOp)RSA_PublicKeyOp;
607 if (rsaKey) {
608 /* convert the RSAPrivateKey to RSAPublicKey */
609 pubKeyStr.arena = NULL;
610 pubKeyStr.modulus = ((RSAPrivateKey*)rsaKey)->modulus;
611 pubKeyStr.publicExponent =
612 ((RSAPrivateKey*)rsaKey)->publicExponent;
613 rsaKey = &pubKeyStr;
614 } else {
615 /* convert NSSLOWKeyPublicKey to RSAPublicKey */
616 rsaKey = (void *)(&pubKey->u.rsa);
618 PORT_Assert(rsaKey);
619 } else {
620 /* do private key operations */
621 fn = (RSAOp)RSA_PrivateKeyOp;
622 if (privKey) {
623 /* convert NSSLOWKeyPrivateKey to RSAPrivateKey */
624 rsaKey = (void *)(&privKey->u.rsa);
626 PORT_Assert(rsaKey);
630 memset(buf, 1, sizeof buf);
631 rv = fn(rsaKey, buf2, buf);
632 if (rv != SECSuccess) {
633 PRErrorCode errNum;
634 const char * errStr = NULL;
636 errNum = PORT_GetError();
637 if (errNum)
638 errStr = SECU_Strerror(errNum);
639 else
640 errNum = rv;
641 if (!errStr)
642 errStr = "(null)";
643 fprintf(stderr, "Error in RSA operation: %d : %s\n", errNum, errStr);
644 exit(1);
647 threadsArr = (PRThread**)PORT_Alloc(threadNum*sizeof(PRThread*));
648 runDataArr = (ThreadRunData**)PORT_Alloc(threadNum*sizeof(ThreadRunData*));
649 timeCtx = CreateTimingContext();
650 TimingBegin(timeCtx, PR_Now());
651 for (i = 0;i < threadNum;i++) {
652 runDataArr[i] = (ThreadRunData*)PORT_Alloc(sizeof(ThreadRunData));
653 runDataArr[i]->fn = fn;
654 runDataArr[i]->buf = buf;
655 runDataArr[i]->doIters = &doIters;
656 runDataArr[i]->rsaKey = rsaKey;
657 runDataArr[i]->seconds = seconds;
658 runDataArr[i]->iters = iters;
659 threadsArr[i] =
660 PR_CreateThread(PR_USER_THREAD,
661 ThreadExecFunction,
662 (void*) runDataArr[i],
663 PR_PRIORITY_NORMAL,
664 PR_GLOBAL_THREAD,
665 PR_JOINABLE_THREAD,
668 iters = 0;
669 calcThreads = 0;
670 for (i = 0;i < threadNum;i++, calcThreads++)
672 PR_JoinThread(threadsArr[i]);
673 if (runDataArr[i]->status != SECSuccess) {
674 const char * errStr = SECU_Strerror(runDataArr[i]->errNum);
675 fprintf(stderr, "Thread %d: Error in RSA operation: %d : %s\n",
676 i, runDataArr[i]->errNum, errStr);
677 calcThreads -= 1;
678 } else {
679 iters += runDataArr[i]->iterRes;
681 PORT_Free((void*)runDataArr[i]);
683 PORT_Free(runDataArr);
684 PORT_Free(threadsArr);
686 TimingEnd(timeCtx, PR_Now());
688 printf("%ld iterations in %s\n",
689 iters, TimingGenerateString(timeCtx));
690 printf("%.2f operations/s .\n", ((double)(iters)*(double)1000000.0) /
691 (double)timeCtx->interval );
692 TimingDivide(timeCtx, iters);
693 printf("one operation every %s\n", TimingGenerateString(timeCtx));
695 if (pubHighKey) {
696 SECKEY_DestroyPublicKey(pubHighKey);
699 if (privHighKey) {
700 SECKEY_DestroyPrivateKey(privHighKey);
703 if (cert) {
704 CERT_DestroyCertificate(cert);
707 if (NSS_Shutdown() != SECSuccess) {
708 exit(1);
711 return 0;