tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }