/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include <stdio.h>
#include <stdlib.h>

#include "blapi.h"
#include "secrng.h"
#include "prmem.h"
#include "prprf.h"
#include "prtime.h"
#include "prsystem.h"
#include "plstr.h"
#include "nssb64.h"
#include "basicutil.h"
#include "plgetopt.h"
#include "softoken.h"
#include "nspr.h"
#include "secport.h"
#include "secoid.h"
#include "nssutil.h"

#ifndef NSS_DISABLE_ECC
#include "ecl-curve.h"
SECStatus EC_DecodeParams(const SECItem *encodedParams, 
	ECParams **ecparams);
SECStatus EC_CopyParams(PLArenaPool *arena, ECParams *dstParams,
	      const ECParams *srcParams);
#endif

char *progName;
char *testdir = NULL;

#define BLTEST_DEFAULT_CHUNKSIZE 4096

#define WORDSIZE sizeof(unsigned long)

#define CHECKERROR(rv, ln) \
    if (rv) { \
	PRErrorCode prerror = PR_GetError(); \
	PR_fprintf(PR_STDERR, "%s: ERR %d (%s) at line %d.\n", progName, \
	prerror, PORT_ErrorToString(prerror), ln); \
	exit(-1); \
    }

/* Macros for performance timing. */
#define TIMESTART() \
    time1 = PR_IntervalNow();

#define TIMEFINISH(time, reps) \
    time2 = (PRIntervalTime)(PR_IntervalNow() - time1); \
    time1 = PR_IntervalToMilliseconds(time2); \
    time = ((double)(time1))/reps;

#define TIMEMARK(seconds) \
    time1 = PR_SecondsToInterval(seconds); \
    { \
        PRInt64 tmp; \
        if (time2 == 0) { \
            time2 = 1; \
        } \
        LL_DIV(tmp, time1, time2); \
        if (tmp < 10) { \
            if (tmp == 0) { \
                opsBetweenChecks = 1; \
            } else { \
                LL_L2I(opsBetweenChecks, tmp); \
            } \
        } else { \
            opsBetweenChecks = 10; \
        } \
    } \
    time2 = time1; \
    time1 = PR_IntervalNow();

#define TIMETOFINISH() \
    PR_IntervalNow() - time1 >= time2

static void Usage()
{
#define PRINTUSAGE(subject, option, predicate) \
    fprintf(stderr, "%10s %s\t%s\n", subject, option, predicate);
    fprintf(stderr, "\n");
    PRINTUSAGE(progName, "[-DEHSVR]", "List available cipher modes"); /* XXX */
    fprintf(stderr, "\n");
    PRINTUSAGE(progName, "-E -m mode ", "Encrypt a buffer");
    PRINTUSAGE("",	"", "[-i plaintext] [-o ciphertext] [-k key] [-v iv]");
    PRINTUSAGE("",	"", "[-b bufsize] [-g keysize] [-e exp] [-r rounds]");
    PRINTUSAGE("",	"", "[-w wordsize] [-p repetitions | -5 time_interval]");
    PRINTUSAGE("",	"", "[-4 th_num]");
    PRINTUSAGE("",	"-m", "cipher mode to use");
    PRINTUSAGE("",	"-i", "file which contains input buffer");
    PRINTUSAGE("",	"-o", "file for output buffer");
    PRINTUSAGE("",	"-k", "file which contains key");
    PRINTUSAGE("",	"-v", "file which contains initialization vector");
    PRINTUSAGE("",	"-b", "size of input buffer");
    PRINTUSAGE("",	"-g", "key size (in bytes)");
    PRINTUSAGE("",	"-p", "do performance test");
    PRINTUSAGE("",	"-4", "run test in multithread mode. th_num number of parallel threads");
    PRINTUSAGE("",	"-5", "run test for specified time interval(in seconds)");
    PRINTUSAGE("",	"--aad", "File with contains additional auth data");
    PRINTUSAGE("(rsa)", "-e", "rsa public exponent");
    PRINTUSAGE("(rc5)", "-r", "number of rounds");
    PRINTUSAGE("(rc5)", "-w", "wordsize (32 or 64)");
    fprintf(stderr, "\n");
    PRINTUSAGE(progName, "-D -m mode", "Decrypt a buffer");
    PRINTUSAGE("",	"", "[-i plaintext] [-o ciphertext] [-k key] [-v iv]");
    PRINTUSAGE("",	"", "[-p repetitions | -5 time_interval] [-4 th_num]");
    PRINTUSAGE("",	"-m", "cipher mode to use");
    PRINTUSAGE("",	"-i", "file which contains input buffer");
    PRINTUSAGE("",	"-o", "file for output buffer");
    PRINTUSAGE("",	"-k", "file which contains key");
    PRINTUSAGE("",	"-v", "file which contains initialization vector");
    PRINTUSAGE("",	"-p", "do performance test");
    PRINTUSAGE("",	"-4", "run test in multithread mode. th_num number of parallel threads");
    PRINTUSAGE("",	"-5", "run test for specified time interval(in seconds)");
    PRINTUSAGE("",	"--aad", "File with contains additional auth data");
    fprintf(stderr, "\n");
    PRINTUSAGE(progName, "-H -m mode", "Hash a buffer");
    PRINTUSAGE("",	"", "[-i plaintext] [-o hash]");
    PRINTUSAGE("",	"", "[-b bufsize]");
    PRINTUSAGE("",	"", "[-p repetitions | -5 time_interval] [-4 th_num]");
    PRINTUSAGE("",	"-m", "cipher mode to use");
    PRINTUSAGE("",	"-i", "file which contains input buffer");
    PRINTUSAGE("",	"-o", "file for hash");
    PRINTUSAGE("",	"-b", "size of input buffer");
    PRINTUSAGE("",	"-p", "do performance test");
    PRINTUSAGE("",	"-4", "run test in multithread mode. th_num number of parallel threads");
    PRINTUSAGE("",	"-5", "run test for specified time interval(in seconds)");
    fprintf(stderr, "\n");
    PRINTUSAGE(progName, "-S -m mode", "Sign a buffer");
    PRINTUSAGE("",	"", "[-i plaintext] [-o signature] [-k key]");
    PRINTUSAGE("",	"", "[-b bufsize]");
#ifndef NSS_DISABLE_ECC
    PRINTUSAGE("",	"", "[-n curvename]");
#endif
    PRINTUSAGE("",	"", "[-p repetitions | -5 time_interval] [-4 th_num]");
    PRINTUSAGE("",	"-m", "cipher mode to use");
    PRINTUSAGE("",	"-i", "file which contains input buffer");
    PRINTUSAGE("",	"-o", "file for signature");
    PRINTUSAGE("",	"-k", "file which contains key");
#ifndef NSS_DISABLE_ECC
    PRINTUSAGE("",	"-n", "name of curve for EC key generation; one of:");
    PRINTUSAGE("",  "",   "  sect163k1, nistk163, sect163r1, sect163r2,");
    PRINTUSAGE("",  "",   "  nistb163, sect193r1, sect193r2, sect233k1, nistk233,");
    PRINTUSAGE("",  "",   "  sect233r1, nistb233, sect239k1, sect283k1, nistk283,");
    PRINTUSAGE("",  "",   "  sect283r1, nistb283, sect409k1, nistk409, sect409r1,");
    PRINTUSAGE("",  "",   "  nistb409, sect571k1, nistk571, sect571r1, nistb571,");
    PRINTUSAGE("",  "",   "  secp160k1, secp160r1, secp160r2, secp192k1, secp192r1,");
    PRINTUSAGE("",  "",   "  nistp192, secp224k1, secp224r1, nistp224, secp256k1,");
    PRINTUSAGE("",  "",   "  secp256r1, nistp256, secp384r1, nistp384, secp521r1,");
    PRINTUSAGE("",  "",   "  nistp521, prime192v1, prime192v2, prime192v3,");
    PRINTUSAGE("",  "",   "  prime239v1, prime239v2, prime239v3, c2pnb163v1,");
    PRINTUSAGE("",  "",   "  c2pnb163v2, c2pnb163v3, c2pnb176v1, c2tnb191v1,");
    PRINTUSAGE("",  "",   "  c2tnb191v2, c2tnb191v3, c2onb191v4, c2onb191v5,");
    PRINTUSAGE("",  "",   "  c2pnb208w1, c2tnb239v1, c2tnb239v2, c2tnb239v3,");
    PRINTUSAGE("",  "",   "  c2onb239v4, c2onb239v5, c2pnb272w1, c2pnb304w1,");
    PRINTUSAGE("",  "",   "  c2tnb359w1, c2pnb368w1, c2tnb431r1, secp112r1,");
    PRINTUSAGE("",  "",   "  secp112r2, secp128r1, secp128r2, sect113r1, sect113r2,");
    PRINTUSAGE("",  "",   "  sect131r1, sect131r2");
#endif
    PRINTUSAGE("",	"-p", "do performance test");
    PRINTUSAGE("",	"-4", "run test in multithread mode. th_num number of parallel threads");
    PRINTUSAGE("",	"-5", "run test for specified time interval(in seconds)");
    fprintf(stderr, "\n");
    PRINTUSAGE(progName, "-V -m mode", "Verify a signed buffer");
    PRINTUSAGE("",	"", "[-i plaintext] [-s signature] [-k key]");
    PRINTUSAGE("",	"", "[-p repetitions | -5 time_interval] [-4 th_num]");
    PRINTUSAGE("",	"-m", "cipher mode to use");
    PRINTUSAGE("",	"-i", "file which contains input buffer");
    PRINTUSAGE("",	"-s", "file which contains signature of input buffer");
    PRINTUSAGE("",	"-k", "file which contains key");
    PRINTUSAGE("",	"-p", "do performance test");
    PRINTUSAGE("",	"-4", "run test in multithread mode. th_num number of parallel threads");
    PRINTUSAGE("",	"-5", "run test for specified time interval(in seconds)");
    fprintf(stderr, "\n");
    PRINTUSAGE(progName, "-N -m mode -b bufsize", 
                                            "Create a nonce plaintext and key");
    PRINTUSAGE("",      "", "[-g keysize] [-u cxreps]");
    PRINTUSAGE("",	"-g", "key size (in bytes)");
    PRINTUSAGE("",      "-u", "number of repetitions of context creation");
    fprintf(stderr, "\n");
    PRINTUSAGE(progName, "-R [-g keysize] [-e exp]", 
                                            "Test the RSA populate key function");
    PRINTUSAGE("",      "", "[-r repetitions]");
    PRINTUSAGE("",	"-g", "key size (in bytes)");
    PRINTUSAGE("", 	"-e", "rsa public exponent");
    PRINTUSAGE("", 	"-r", "repetitions of the test");
    fprintf(stderr, "\n");
    PRINTUSAGE(progName, "-F", "Run the FIPS self-test");
    fprintf(stderr, "\n");
    PRINTUSAGE(progName, "-T [-m mode1,mode2...]", "Run the BLAPI self-test");
    fprintf(stderr, "\n");
    exit(1);
}

/*  Helper functions for ascii<-->binary conversion/reading/writing */

/* XXX argh */
struct item_with_arena {
    SECItem	*item;
    PLArenaPool *arena;
};

static PRInt32
get_binary(void *arg, const unsigned char *ibuf, PRInt32 size)
{
    struct item_with_arena *it = arg;
    SECItem *binary = it->item;
    SECItem *tmp;
    int index;
    if (binary->data == NULL) {
	tmp = SECITEM_AllocItem(it->arena, NULL, size);
	binary->data = tmp->data;
	binary->len = tmp->len;
	index = 0;
    } else {
	SECITEM_ReallocItem(NULL, binary, binary->len, binary->len + size);
	index = binary->len;
    }
    PORT_Memcpy(&binary->data[index], ibuf, size);
    return binary->len;
}

static SECStatus
atob(SECItem *ascii, SECItem *binary, PLArenaPool *arena)
{
    SECStatus status;
    NSSBase64Decoder *cx;
    struct item_with_arena it;
    int len;
    binary->data = NULL;
    binary->len = 0;
    it.item = binary;
    it.arena = arena;
    len = (strncmp((const char *)&ascii->data[ascii->len-2],"\r\n",2)) ?
           ascii->len : ascii->len-2;
    cx = NSSBase64Decoder_Create(get_binary, &it);
    status = NSSBase64Decoder_Update(cx, (const char *)ascii->data, len);
    status = NSSBase64Decoder_Destroy(cx, PR_FALSE);
    return status;
}

static PRInt32
output_ascii(void *arg, const char *obuf, PRInt32 size)
{
    PRFileDesc *outfile = arg;
    PRInt32 nb = PR_Write(outfile, obuf, size);
    if (nb != size) {
	PORT_SetError(SEC_ERROR_IO);
	return -1;
    }
    return nb;
}

static SECStatus
btoa_file(SECItem *binary, PRFileDesc *outfile)
{
    SECStatus status;
    NSSBase64Encoder *cx;
    if (binary->len == 0)
	return SECSuccess;
    cx = NSSBase64Encoder_Create(output_ascii, outfile);
    status = NSSBase64Encoder_Update(cx, binary->data, binary->len);
    status = NSSBase64Encoder_Destroy(cx, PR_FALSE);
    status = PR_Write(outfile, "\r\n", 2);
    return status;
}

SECStatus
hex_from_2char(unsigned char *c2, unsigned char *byteval)
{
    int i;
    unsigned char offset;
    *byteval = 0;
    for (i=0; i<2; i++) {
	if (c2[i] >= '0' && c2[i] <= '9') {
	    offset = c2[i] - '0';
	    *byteval |= offset << 4*(1-i);
	} else if (c2[i] >= 'a' && c2[i] <= 'f') {
	    offset = c2[i] - 'a';
	    *byteval |= (offset + 10) << 4*(1-i);
	} else if (c2[i] >= 'A' && c2[i] <= 'F') {
	    offset = c2[i] - 'A';
	    *byteval |= (offset + 10) << 4*(1-i);
	} else {
	    return SECFailure;
	}
    }
    return SECSuccess;
}

SECStatus
char2_from_hex(unsigned char byteval, char *c2)
{
    int i;
    unsigned char offset;
    for (i=0; i<2; i++) {
	offset = (byteval >> 4*(1-i)) & 0x0f;
	if (offset < 10) {
	    c2[i] = '0' + offset;
	} else {
	    c2[i] = 'A' + offset - 10;
	}
    }
    return SECSuccess;
}

void
serialize_key(SECItem *it, int ni, PRFileDesc *file)
{
    unsigned char len[4];
    int i;
    NSSBase64Encoder *cx;
    cx = NSSBase64Encoder_Create(output_ascii, file);
    for (i=0; i<ni; i++, it++) {
	len[0] = (it->len >> 24) & 0xff;
	len[1] = (it->len >> 16) & 0xff;
	len[2] = (it->len >>  8) & 0xff;
	len[3] = (it->len	 & 0xff);
	NSSBase64Encoder_Update(cx, len, 4);
	NSSBase64Encoder_Update(cx, it->data, it->len);
    }
    NSSBase64Encoder_Destroy(cx, PR_FALSE);
    PR_Write(file, "\r\n", 2);
}

void
key_from_filedata(PLArenaPool *arena, SECItem *it, int ns, int ni, SECItem *filedata)
{
    int fpos = 0;
    int i, len;
    unsigned char *buf = filedata->data;
    for (i=0; i<ni; i++) {
	len  = (buf[fpos++] & 0xff) << 24;
	len |= (buf[fpos++] & 0xff) << 16;
	len |= (buf[fpos++] & 0xff) <<  8;
	len |= (buf[fpos++] & 0xff);
	if (ns <= i) {
	    if (len > 0) {
		it->len = len;
		it->data = PORT_ArenaAlloc(arena, it->len);
		PORT_Memcpy(it->data, &buf[fpos], it->len);
	    } else {
		it->len = 0;
		it->data = NULL;
	    }
	    it++;
	}
	fpos += len;
    }
}

static RSAPrivateKey *
rsakey_from_filedata(SECItem *filedata)
{
    RSAPrivateKey *key;
    PLArenaPool *arena;
    arena = PORT_NewArena(BLTEST_DEFAULT_CHUNKSIZE);
    key = (RSAPrivateKey *)PORT_ArenaZAlloc(arena, sizeof(RSAPrivateKey));
    key->arena = arena;
    key_from_filedata(arena, &key->version, 0, 9, filedata);
    return key;
}

static PQGParams *
pqg_from_filedata(SECItem *filedata)
{
    PQGParams *pqg;
    PLArenaPool *arena;
    arena = PORT_NewArena(BLTEST_DEFAULT_CHUNKSIZE);
    pqg = (PQGParams *)PORT_ArenaZAlloc(arena, sizeof(PQGParams));
    pqg->arena = arena;
    key_from_filedata(arena, &pqg->prime, 0, 3, filedata);
    return pqg;
}

static DSAPrivateKey *
dsakey_from_filedata(SECItem *filedata)
{
    DSAPrivateKey *key;
    PLArenaPool *arena;
    arena = PORT_NewArena(BLTEST_DEFAULT_CHUNKSIZE);
    key = (DSAPrivateKey *)PORT_ArenaZAlloc(arena, sizeof(DSAPrivateKey));
    key->params.arena = arena;
    key_from_filedata(arena, &key->params.prime, 0, 5, filedata);
    return key;
}

#ifndef NSS_DISABLE_ECC
static ECPrivateKey *
eckey_from_filedata(SECItem *filedata)
{
    ECPrivateKey *key;
    PLArenaPool *arena;
    SECStatus rv;
    ECParams *tmpECParams = NULL;
    arena = PORT_NewArena(BLTEST_DEFAULT_CHUNKSIZE);
    key = (ECPrivateKey *)PORT_ArenaZAlloc(arena, sizeof(ECPrivateKey));
    /* read and convert params */
    key->ecParams.arena = arena;
    key_from_filedata(arena, &key->ecParams.DEREncoding, 0, 1, filedata);
    rv = SECOID_Init();
    CHECKERROR(rv, __LINE__);
    rv = EC_DecodeParams(&key->ecParams.DEREncoding, &tmpECParams);
    CHECKERROR(rv, __LINE__);
    rv = EC_CopyParams(key->ecParams.arena, &key->ecParams, tmpECParams);
    CHECKERROR(rv, __LINE__);
    rv = SECOID_Shutdown();
    CHECKERROR(rv, __LINE__);
    PORT_FreeArena(tmpECParams->arena, PR_TRUE);
    /* read key */
    key_from_filedata(arena, &key->publicValue, 1, 3, filedata);
    return key;
}

typedef struct curveNameTagPairStr {
    char *curveName;
    SECOidTag curveOidTag;
} CurveNameTagPair;

#define DEFAULT_CURVE_OID_TAG  SEC_OID_SECG_EC_SECP192R1
/* #define DEFAULT_CURVE_OID_TAG  SEC_OID_SECG_EC_SECP160R1 */

static CurveNameTagPair nameTagPair[] =
{ 
  { "sect163k1", SEC_OID_SECG_EC_SECT163K1},
  { "nistk163", SEC_OID_SECG_EC_SECT163K1},
  { "sect163r1", SEC_OID_SECG_EC_SECT163R1},
  { "sect163r2", SEC_OID_SECG_EC_SECT163R2},
  { "nistb163", SEC_OID_SECG_EC_SECT163R2},
  { "sect193r1", SEC_OID_SECG_EC_SECT193R1},
  { "sect193r2", SEC_OID_SECG_EC_SECT193R2},
  { "sect233k1", SEC_OID_SECG_EC_SECT233K1},
  { "nistk233", SEC_OID_SECG_EC_SECT233K1},
  { "sect233r1", SEC_OID_SECG_EC_SECT233R1},
  { "nistb233", SEC_OID_SECG_EC_SECT233R1},
  { "sect239k1", SEC_OID_SECG_EC_SECT239K1},
  { "sect283k1", SEC_OID_SECG_EC_SECT283K1},
  { "nistk283", SEC_OID_SECG_EC_SECT283K1},
  { "sect283r1", SEC_OID_SECG_EC_SECT283R1},
  { "nistb283", SEC_OID_SECG_EC_SECT283R1},
  { "sect409k1", SEC_OID_SECG_EC_SECT409K1},
  { "nistk409", SEC_OID_SECG_EC_SECT409K1},
  { "sect409r1", SEC_OID_SECG_EC_SECT409R1},
  { "nistb409", SEC_OID_SECG_EC_SECT409R1},
  { "sect571k1", SEC_OID_SECG_EC_SECT571K1},
  { "nistk571", SEC_OID_SECG_EC_SECT571K1},
  { "sect571r1", SEC_OID_SECG_EC_SECT571R1},
  { "nistb571", SEC_OID_SECG_EC_SECT571R1},
  { "secp160k1", SEC_OID_SECG_EC_SECP160K1},
  { "secp160r1", SEC_OID_SECG_EC_SECP160R1},
  { "secp160r2", SEC_OID_SECG_EC_SECP160R2},
  { "secp192k1", SEC_OID_SECG_EC_SECP192K1},
  { "secp192r1", SEC_OID_SECG_EC_SECP192R1},
  { "nistp192", SEC_OID_SECG_EC_SECP192R1},
  { "secp224k1", SEC_OID_SECG_EC_SECP224K1},
  { "secp224r1", SEC_OID_SECG_EC_SECP224R1},
  { "nistp224", SEC_OID_SECG_EC_SECP224R1},
  { "secp256k1", SEC_OID_SECG_EC_SECP256K1},
  { "secp256r1", SEC_OID_SECG_EC_SECP256R1},
  { "nistp256", SEC_OID_SECG_EC_SECP256R1},
  { "secp384r1", SEC_OID_SECG_EC_SECP384R1},
  { "nistp384", SEC_OID_SECG_EC_SECP384R1},
  { "secp521r1", SEC_OID_SECG_EC_SECP521R1},
  { "nistp521", SEC_OID_SECG_EC_SECP521R1},

  { "prime192v1", SEC_OID_ANSIX962_EC_PRIME192V1 },
  { "prime192v2", SEC_OID_ANSIX962_EC_PRIME192V2 },
  { "prime192v3", SEC_OID_ANSIX962_EC_PRIME192V3 },
  { "prime239v1", SEC_OID_ANSIX962_EC_PRIME239V1 },
  { "prime239v2", SEC_OID_ANSIX962_EC_PRIME239V2 },
  { "prime239v3", SEC_OID_ANSIX962_EC_PRIME239V3 },

  { "c2pnb163v1", SEC_OID_ANSIX962_EC_C2PNB163V1 },
  { "c2pnb163v2", SEC_OID_ANSIX962_EC_C2PNB163V2 },
  { "c2pnb163v3", SEC_OID_ANSIX962_EC_C2PNB163V3 },
  { "c2pnb176v1", SEC_OID_ANSIX962_EC_C2PNB176V1 },
  { "c2tnb191v1", SEC_OID_ANSIX962_EC_C2TNB191V1 },
  { "c2tnb191v2", SEC_OID_ANSIX962_EC_C2TNB191V2 },
  { "c2tnb191v3", SEC_OID_ANSIX962_EC_C2TNB191V3 },
  { "c2onb191v4", SEC_OID_ANSIX962_EC_C2ONB191V4 },
  { "c2onb191v5", SEC_OID_ANSIX962_EC_C2ONB191V5 },
  { "c2pnb208w1", SEC_OID_ANSIX962_EC_C2PNB208W1 },
  { "c2tnb239v1", SEC_OID_ANSIX962_EC_C2TNB239V1 },
  { "c2tnb239v2", SEC_OID_ANSIX962_EC_C2TNB239V2 },
  { "c2tnb239v3", SEC_OID_ANSIX962_EC_C2TNB239V3 },
  { "c2onb239v4", SEC_OID_ANSIX962_EC_C2ONB239V4 },
  { "c2onb239v5", SEC_OID_ANSIX962_EC_C2ONB239V5 },
  { "c2pnb272w1", SEC_OID_ANSIX962_EC_C2PNB272W1 },
  { "c2pnb304w1", SEC_OID_ANSIX962_EC_C2PNB304W1 },
  { "c2tnb359v1", SEC_OID_ANSIX962_EC_C2TNB359V1 },
  { "c2pnb368w1", SEC_OID_ANSIX962_EC_C2PNB368W1 },
  { "c2tnb431r1", SEC_OID_ANSIX962_EC_C2TNB431R1 },

  { "secp112r1", SEC_OID_SECG_EC_SECP112R1},
  { "secp112r2", SEC_OID_SECG_EC_SECP112R2},
  { "secp128r1", SEC_OID_SECG_EC_SECP128R1},
  { "secp128r2", SEC_OID_SECG_EC_SECP128R2},

  { "sect113r1", SEC_OID_SECG_EC_SECT113R1},
  { "sect113r2", SEC_OID_SECG_EC_SECT113R2},
  { "sect131r1", SEC_OID_SECG_EC_SECT131R1},
  { "sect131r2", SEC_OID_SECG_EC_SECT131R2},
};

static SECItem * 
getECParams(const char *curve)
{
    SECItem *ecparams;
    SECOidData *oidData = NULL;
    SECOidTag curveOidTag = SEC_OID_UNKNOWN; /* default */
    int i, numCurves;

    if (curve != NULL) {
        numCurves = sizeof(nameTagPair)/sizeof(CurveNameTagPair);
	for (i = 0; ((i < numCurves) && (curveOidTag == SEC_OID_UNKNOWN)); 
	     i++) {
	    if (PL_strcmp(curve, nameTagPair[i].curveName) == 0)
	        curveOidTag = nameTagPair[i].curveOidTag;
	}
    }

    /* Return NULL if curve name is not recognized */
    if ((curveOidTag == SEC_OID_UNKNOWN) || 
	(oidData = SECOID_FindOIDByTag(curveOidTag)) == NULL) {
        fprintf(stderr, "Unrecognized elliptic curve %s\n", curve);
	return NULL;
    }

    ecparams = SECITEM_AllocItem(NULL, NULL, (2 + oidData->oid.len));

    /* 
     * ecparams->data needs to contain the ASN encoding of an object ID (OID)
     * representing the named curve. The actual OID is in 
     * oidData->oid.data so we simply prepend 0x06 and OID length
     */
    ecparams->data[0] = SEC_ASN1_OBJECT_ID;
    ecparams->data[1] = oidData->oid.len;
    memcpy(ecparams->data + 2, oidData->oid.data, oidData->oid.len);

    return ecparams;
}
#endif /* NSS_DISABLE_ECC */

static void
dump_pqg(PQGParams *pqg)
{
    SECU_PrintInteger(stdout, &pqg->prime, "PRIME:", 0);
    SECU_PrintInteger(stdout, &pqg->subPrime, "SUBPRIME:", 0);
    SECU_PrintInteger(stdout, &pqg->base, "BASE:", 0);
}

static void
dump_dsakey(DSAPrivateKey *key)
{
    dump_pqg(&key->params);
    SECU_PrintInteger(stdout, &key->publicValue, "PUBLIC VALUE:", 0);
    SECU_PrintInteger(stdout, &key->privateValue, "PRIVATE VALUE:", 0);
}

#ifndef NSS_DISABLE_ECC
static void
dump_ecp(ECParams *ecp)
{
    /* TODO other fields */
    SECU_PrintInteger(stdout, &ecp->base, "BASE POINT:", 0);
}

static void
dump_eckey(ECPrivateKey *key)
{
    dump_ecp(&key->ecParams);
    SECU_PrintInteger(stdout, &key->publicValue, "PUBLIC VALUE:", 0);
    SECU_PrintInteger(stdout, &key->privateValue, "PRIVATE VALUE:", 0);
}
#endif

static void
dump_rsakey(RSAPrivateKey *key)
{
    SECU_PrintInteger(stdout, &key->version, "VERSION:", 0);
    SECU_PrintInteger(stdout, &key->modulus, "MODULUS:", 0);
    SECU_PrintInteger(stdout, &key->publicExponent, "PUBLIC EXP:", 0);
    SECU_PrintInteger(stdout, &key->privateExponent, "PRIVATE EXP:", 0);
    SECU_PrintInteger(stdout, &key->prime1, "CRT PRIME 1:", 0);
    SECU_PrintInteger(stdout, &key->prime2, "CRT PRIME 2:", 0);
    SECU_PrintInteger(stdout, &key->exponent1, "CRT EXP 1:", 0);
    SECU_PrintInteger(stdout, &key->exponent2, "CRT EXP 2:", 0);
    SECU_PrintInteger(stdout, &key->coefficient, "CRT COEFFICIENT:", 0);
}

typedef enum {
    bltestBase64Encoded,       /* Base64 encoded ASCII */
    bltestBinary,	       /* straight binary */
    bltestHexSpaceDelim,       /* 0x12 0x34 0xab 0xCD ... */
    bltestHexStream 	       /* 1234abCD ... */
} bltestIOMode;

typedef struct
{
    SECItem	   buf;
    SECItem	   pBuf;
    bltestIOMode   mode;
    PRFileDesc*	   file;
} bltestIO;

typedef SECStatus (* bltestSymmCipherFn)(void *cx,
					 unsigned char *output,
					 unsigned int *outputLen,
					 unsigned int maxOutputLen,
					 const unsigned char *input,
					 unsigned int inputLen);

typedef SECStatus (* bltestAEADFn)(void *cx,
                                   unsigned char *output,
                                   unsigned int *outputLen,
                                   unsigned int maxOutputLen,
                                   const unsigned char *input,
                                   unsigned int inputLen,
                                   const unsigned char *nonce,
                                   unsigned int nonceLen,
                                   const unsigned char *ad,
                                   unsigned int adLen);

typedef SECStatus (* bltestPubKeyCipherFn)(void *key,
					   SECItem *output,
					   const SECItem *input);

typedef SECStatus (* bltestHashCipherFn)(unsigned char *dest,
					 const unsigned char *src,
					 PRUint32 src_length);

/* Note: Algorithms are grouped in order to support is_symmkeyCipher /
 * is_pubkeyCipher / is_hashCipher / is_sigCipher
 */
typedef enum {
    bltestINVALID = -1,
    bltestDES_ECB,	  /* Symmetric Key Ciphers */
    bltestDES_CBC,	  /* .			   */
    bltestDES_EDE_ECB,	  /* .			   */
    bltestDES_EDE_CBC,	  /* .			   */
    bltestRC2_ECB,	  /* .			   */
    bltestRC2_CBC,	  /* .			   */
    bltestRC4,		  /* .			   */
#ifdef NSS_SOFTOKEN_DOES_RC5
    bltestRC5_ECB,	  /* .			   */
    bltestRC5_CBC,	  /* .			   */
#endif
    bltestAES_ECB,        /* .                     */
    bltestAES_CBC,        /* .                     */
    bltestAES_CTS,        /* .                     */
    bltestAES_CTR,        /* .                     */
    bltestAES_GCM,        /* .                     */
    bltestCAMELLIA_ECB,   /* .                     */
    bltestCAMELLIA_CBC,   /* .                     */
    bltestSEED_ECB,       /* SEED algorithm	   */
    bltestSEED_CBC,       /* SEED algorithm	   */
    bltestCHACHA20,       /* ChaCha20 + Poly1305   */
    bltestRSA,            /* Public Key Ciphers    */
    bltestRSA_OAEP,       /* . (Public Key Enc.)   */
    bltestRSA_PSS,        /* . (Public Key Sig.)   */
#ifndef NSS_DISABLE_ECC
    bltestECDSA,          /* . (Public Key Sig.)   */
#endif
    bltestDSA,            /* . (Public Key Sig.)   */
    bltestMD2,		  /* Hash algorithms	   */
    bltestMD5,		  /* .			   */
    bltestSHA1,           /* .			   */
    bltestSHA224,         /* .			   */
    bltestSHA256,         /* .			   */
    bltestSHA384,         /* .			   */
    bltestSHA512,         /* .			   */
    NUMMODES
} bltestCipherMode;

static char *mode_strings[] =
{
    "des_ecb",
    "des_cbc",
    "des3_ecb",
    "des3_cbc",
    "rc2_ecb",
    "rc2_cbc",
    "rc4",
#ifdef NSS_SOFTOKEN_DOES_RC5
    "rc5_ecb",
    "rc5_cbc",
#endif
    "aes_ecb",
    "aes_cbc",
    "aes_cts",
    "aes_ctr",
    "aes_gcm",
    "camellia_ecb",
    "camellia_cbc",
    "seed_ecb",
    "seed_cbc",
    "chacha20_poly1305",
    "rsa",
    "rsa_oaep",
    "rsa_pss",
#ifndef NSS_DISABLE_ECC
    "ecdsa",
#endif
    /*"pqg",*/
    "dsa",
    "md2",
    "md5",
    "sha1",
    "sha224",
    "sha256",
    "sha384",
    "sha512",
};

typedef struct
{
    bltestIO key;
    bltestIO iv;
} bltestSymmKeyParams;

typedef struct
{
    bltestSymmKeyParams sk; /* must be first */
    bltestIO aad;
} bltestAuthSymmKeyParams;

typedef struct
{
    bltestIO key;
    bltestIO iv;
    int	     rounds;
    int	     wordsize;
} bltestRC5Params;

typedef struct
{
    bltestIO key;
    int keysizeInBits;

    /* OAEP & PSS */
    HASH_HashType hashAlg;
    HASH_HashType maskHashAlg;
    bltestIO      seed; /* salt if PSS */
} bltestRSAParams;

typedef struct
{
    bltestIO   pqgdata;
    unsigned int keysize;
    bltestIO   keyseed;
    bltestIO   sigseed;
    PQGParams *pqg;
} bltestDSAParams;

#ifndef NSS_DISABLE_ECC
typedef struct
{
    char      *curveName;
    bltestIO   sigseed;
} bltestECDSAParams;
#endif

typedef struct
{
    bltestIO key;
    void *   privKey;
    void *   pubKey;
    bltestIO sig; /* if doing verify, the signature (which may come
                   * from sigfile. */

    union {
        bltestRSAParams rsa;
        bltestDSAParams dsa;
#ifndef NSS_DISABLE_ECC
        bltestECDSAParams ecdsa;
#endif
    } cipherParams;
} bltestAsymKeyParams;

typedef struct
{
    bltestIO   key; /* unused */
    PRBool     restart;
} bltestHashParams;

typedef union
{
    bltestIO		key;
    bltestSymmKeyParams sk;
    bltestAuthSymmKeyParams ask;
    bltestRC5Params	rc5;
    bltestAsymKeyParams	asymk;
    bltestHashParams	hash;
} bltestParams;

typedef struct bltestCipherInfoStr bltestCipherInfo;

struct  bltestCipherInfoStr {
    PLArenaPool *arena;
    /* link to next in multithreaded test */
    bltestCipherInfo *next;
    PRThread         *cipherThread;

    /* MonteCarlo test flag*/
    PRBool mCarlo;
    /* cipher context */
    void *cx;
    /* I/O streams */
    bltestIO input;
    bltestIO output;
    /* Cipher-specific parameters */
    bltestParams params;
    /* Cipher mode */
    bltestCipherMode  mode;
    /* Cipher function (encrypt/decrypt/sign/verify/hash) */
    union {
	bltestSymmCipherFn   symmkeyCipher;
	bltestAEADFn         aeadCipher;
	bltestPubKeyCipherFn pubkeyCipher;
	bltestHashCipherFn   hashCipher;
    } cipher;
    /* performance testing */
    int   repetitionsToPerfom;
    int   seconds;
    int	  repetitions;
    int   cxreps;
    double cxtime;
    double optime;
};

PRBool
is_symmkeyCipher(bltestCipherMode mode)
{
    /* change as needed! */
    if (mode >= bltestDES_ECB && mode <= bltestSEED_CBC)
	return PR_TRUE;
    return PR_FALSE;
}

PRBool
is_aeadCipher(bltestCipherMode mode)
{
    /* change as needed! */
    switch (mode) {
	case bltestCHACHA20:
	    return PR_TRUE;
	default:
	    return PR_FALSE;
    }
}

PRBool
is_authCipher(bltestCipherMode mode)
{
    /* change as needed! */
    switch (mode) {
	case bltestAES_GCM:
	case bltestCHACHA20:
	    return PR_TRUE;
	default:
	    return PR_FALSE;
    }
}


PRBool
is_singleShotCipher(bltestCipherMode mode)
{
    /* change as needed! */
    switch (mode) {
	case bltestAES_GCM:
	case bltestAES_CTS:
	case bltestCHACHA20:
	    return PR_TRUE;
	default:
	    return PR_FALSE;
    }
}

PRBool
is_pubkeyCipher(bltestCipherMode mode)
{
    /* change as needed! */
    if (mode >= bltestRSA && mode <= bltestDSA)
	return PR_TRUE;
    return PR_FALSE;
}

PRBool
is_hashCipher(bltestCipherMode mode)
{
    /* change as needed! */
    if (mode >= bltestMD2 && mode <= bltestSHA512)
	return PR_TRUE;
    return PR_FALSE;
}

PRBool
is_sigCipher(bltestCipherMode mode)
{
    /* change as needed! */
    if (mode >= bltestRSA_PSS && mode <= bltestDSA)
       return PR_TRUE;
    return PR_FALSE;
}

PRBool
cipher_requires_IV(bltestCipherMode mode)
{
    /* change as needed! */
    switch (mode) {
	case bltestDES_CBC:
	case bltestDES_EDE_CBC:
	case bltestRC2_CBC:
#ifdef NSS_SOFTOKEN_DOES_RC5
	case bltestRC5_CBC:
#endif
	case bltestAES_CBC:
	case bltestAES_CTS:
	case bltestAES_CTR:
	case bltestAES_GCM:
	case bltestCAMELLIA_CBC:
	case bltestSEED_CBC:
	case bltestCHACHA20:
	    return PR_TRUE;
	default:
	    return PR_FALSE;
    }
}

SECStatus finishIO(bltestIO *output, PRFileDesc *file);

SECStatus
setupIO(PLArenaPool *arena, bltestIO *input, PRFileDesc *file,
	char *str, int numBytes)
{
    SECStatus rv = SECSuccess;
    SECItem fileData;
    SECItem *in;
    unsigned char *tok;
    unsigned int i, j;

    if (file && (numBytes == 0 || file == PR_STDIN)) {
	/* grabbing data from a file */
	rv = SECU_FileToItem(&fileData, file);
	if (rv != SECSuccess)
	    return SECFailure;
	in = &fileData;
    } else if (str) {
	/* grabbing data from command line */
	fileData.data = (unsigned char *)str;
	fileData.len = PL_strlen(str);
	in = &fileData;
    } else if (file) {
	/* create nonce */
	SECITEM_AllocItem(arena, &input->buf, numBytes);
	RNG_GenerateGlobalRandomBytes(input->buf.data, numBytes);
	return finishIO(input, file);
    } else {
	return SECFailure;
    }

    switch (input->mode) {
    case bltestBase64Encoded:
	if (in->len == 0) {
	    input->buf.data = NULL;
	    input->buf.len = 0;
	    break;
	}
	rv = atob(in, &input->buf, arena);
	break;
    case bltestBinary:
	if (in->len == 0) {
	    input->buf.data = NULL;
	    input->buf.len = 0;
	    break;
	}
	if (in->data[in->len-1] == '\n') --in->len;
	if (in->data[in->len-1] == '\r') --in->len;
	SECITEM_CopyItem(arena, &input->buf, in);
	break;
    case bltestHexSpaceDelim:
	SECITEM_AllocItem(arena, &input->buf, in->len/5);
	for (i=0, j=0; i<in->len; i+=5, j++) {
	    tok = &in->data[i];
	    if (tok[0] != '0' || tok[1] != 'x' || tok[4] != ' ')
		/* bad hex token */
		break;

	    rv = hex_from_2char(&tok[2], input->buf.data + j);
	    if (rv)
		break;
	}
	break;
    case bltestHexStream:
	SECITEM_AllocItem(arena, &input->buf, in->len/2);
	for (i=0, j=0; i<in->len; i+=2, j++) {
	    tok = &in->data[i];
	    rv = hex_from_2char(tok, input->buf.data + j);
	    if (rv)
		break;
	}
	break;
    }

    if (file)
	SECITEM_FreeItem(&fileData, PR_FALSE);
    return rv;
}

SECStatus
finishIO(bltestIO *output, PRFileDesc *file)
{
    SECStatus rv = SECSuccess;
    PRInt32 nb;
    unsigned char byteval;
    SECItem *it;
    char hexstr[5];
    unsigned int i;
    if (output->pBuf.len > 0) {
	it = &output->pBuf;
    } else {
	it = &output->buf;
    }
    switch (output->mode) {
    case bltestBase64Encoded:
	rv = btoa_file(it, file);
	break;
    case bltestBinary:
	nb = PR_Write(file, it->data, it->len);
	rv = (nb == (PRInt32)it->len) ? SECSuccess : SECFailure;
	break;
    case bltestHexSpaceDelim:
	hexstr[0] = '0';
	hexstr[1] = 'x';
	hexstr[4] = ' ';
	for (i=0; i<it->len; i++) {
	    byteval = it->data[i];
	    rv = char2_from_hex(byteval, hexstr + 2);
	    nb = PR_Write(file, hexstr, 5);
	    if (rv)
		break;
	}
	PR_Write(file, "\n", 1);
	break;
    case bltestHexStream:
	for (i=0; i<it->len; i++) {
	    byteval = it->data[i];
	    rv = char2_from_hex(byteval, hexstr);
	    if (rv)
		break;
	    nb = PR_Write(file, hexstr, 2);
	}
	PR_Write(file, "\n", 1);
	break;
    }
    return rv;
}

void
bltestCopyIO(PLArenaPool *arena, bltestIO *dest, bltestIO *src)
{
    SECITEM_CopyItem(arena, &dest->buf, &src->buf);
    if (src->pBuf.len > 0) {
	dest->pBuf.len = src->pBuf.len;
	dest->pBuf.data = dest->buf.data + (src->pBuf.data - src->buf.data);
    }
    dest->mode = src->mode;
    dest->file = src->file;
}

void
misalignBuffer(PLArenaPool *arena, bltestIO *io, int off)
{
    ptrdiff_t offset = (ptrdiff_t)io->buf.data % WORDSIZE;
    int length = io->buf.len;
    if (offset != off) {
	SECITEM_ReallocItemV2(arena, &io->buf, length + 2*WORDSIZE);
	/* offset may have changed? */
	offset = (ptrdiff_t)io->buf.data % WORDSIZE;
	if (offset != off) {
	    memmove(io->buf.data + off, io->buf.data, length);
	    io->pBuf.data = io->buf.data + off;
	    io->pBuf.len = length;
	} else {
	    io->pBuf.data = io->buf.data;
	    io->pBuf.len = length;
	}
    } else {
	io->pBuf.data = io->buf.data;
	io->pBuf.len = length;
    }
}

SECStatus
des_Encrypt(void *cx, unsigned char *output, unsigned int *outputLen,
            unsigned int maxOutputLen, const unsigned char *input,
            unsigned int inputLen)
{
    return DES_Encrypt((DESContext *)cx, output, outputLen, maxOutputLen,
                       input, inputLen);
}

SECStatus
des_Decrypt(void *cx, unsigned char *output, unsigned int *outputLen,
            unsigned int maxOutputLen, const unsigned char *input,
            unsigned int inputLen)
{
    return DES_Decrypt((DESContext *)cx, output, outputLen, maxOutputLen,
                       input, inputLen);
}

SECStatus
rc2_Encrypt(void *cx, unsigned char *output, unsigned int *outputLen,
            unsigned int maxOutputLen, const unsigned char *input,
            unsigned int inputLen)
{
    return RC2_Encrypt((RC2Context *)cx, output, outputLen, maxOutputLen,
                       input, inputLen);
}

SECStatus
rc2_Decrypt(void *cx, unsigned char *output, unsigned int *outputLen,
            unsigned int maxOutputLen, const unsigned char *input,
            unsigned int inputLen)
{
    return RC2_Decrypt((RC2Context *)cx, output, outputLen, maxOutputLen,
                       input, inputLen);
}

SECStatus
rc4_Encrypt(void *cx, unsigned char *output, unsigned int *outputLen,
            unsigned int maxOutputLen, const unsigned char *input,
            unsigned int inputLen)
{
    return RC4_Encrypt((RC4Context *)cx, output, outputLen, maxOutputLen,
                       input, inputLen);
}

SECStatus
rc4_Decrypt(void *cx, unsigned char *output, unsigned int *outputLen,
            unsigned int maxOutputLen, const unsigned char *input,
            unsigned int inputLen)
{
    return RC4_Decrypt((RC4Context *)cx, output, outputLen, maxOutputLen,
                       input, inputLen);
}

SECStatus
aes_Encrypt(void *cx, unsigned char *output, unsigned int *outputLen,
            unsigned int maxOutputLen, const unsigned char *input,
            unsigned int inputLen)
{
    return AES_Encrypt((AESContext *)cx, output, outputLen, maxOutputLen,
                       input, inputLen);
}

SECStatus
aes_Decrypt(void *cx, unsigned char *output, unsigned int *outputLen,
            unsigned int maxOutputLen, const unsigned char *input,
            unsigned int inputLen)
{
    return AES_Decrypt((AESContext *)cx, output, outputLen, maxOutputLen,
                       input, inputLen);
}

SECStatus
chacha20_poly1305_Encrypt(void *cx, unsigned char *output,
                         unsigned int *outputLen, unsigned int maxOutputLen,
                         const unsigned char *input, unsigned int inputLen,
                         const unsigned char *nonce, unsigned int nonceLen,
                         const unsigned char *ad, unsigned int adLen)
{
    return ChaCha20Poly1305_Seal((ChaCha20Poly1305Context *)cx, output,
                                 outputLen, maxOutputLen, input, inputLen,
                                 nonce, nonceLen, ad, adLen);
}

SECStatus
chacha20_poly1305_Decrypt(void *cx, unsigned char *output,
                         unsigned int *outputLen, unsigned int maxOutputLen,
                         const unsigned char *input, unsigned int inputLen,
                         const unsigned char *nonce, unsigned int nonceLen,
                         const unsigned char *ad, unsigned int adLen)
{
    return ChaCha20Poly1305_Open((ChaCha20Poly1305Context *)cx, output,
                                 outputLen, maxOutputLen, input, inputLen,
                                 nonce, nonceLen, ad, adLen);
}

SECStatus
camellia_Encrypt(void *cx, unsigned char *output, unsigned int *outputLen,
		 unsigned int maxOutputLen, const unsigned char *input,
		 unsigned int inputLen)
{
    return Camellia_Encrypt((CamelliaContext *)cx, output, outputLen,
			    maxOutputLen,
			    input, inputLen);
}

SECStatus
camellia_Decrypt(void *cx, unsigned char *output, unsigned int *outputLen,
		 unsigned int maxOutputLen, const unsigned char *input,
		 unsigned int inputLen)
{
    return Camellia_Decrypt((CamelliaContext *)cx, output, outputLen,
			    maxOutputLen,
			    input, inputLen);
}

SECStatus
seed_Encrypt(void *cx, unsigned char *output, unsigned int *outputLen,
            unsigned int maxOutputLen, const unsigned char *input,
            unsigned int inputLen)
{
    return SEED_Encrypt((SEEDContext *)cx, output, outputLen, maxOutputLen,
                       input, inputLen);
}

SECStatus
seed_Decrypt(void *cx, unsigned char *output, unsigned int *outputLen,
            unsigned int maxOutputLen, const unsigned char *input,
            unsigned int inputLen)
{
    return SEED_Decrypt((SEEDContext *)cx, output, outputLen, maxOutputLen,
                       input, inputLen);
}

SECStatus
rsa_PublicKeyOp(void *cx, SECItem *output, const SECItem *input)
{
    bltestAsymKeyParams *params = (bltestAsymKeyParams *)cx;
    RSAPublicKey *pubKey = (RSAPublicKey *)params->pubKey;
    SECStatus rv = RSA_PublicKeyOp(pubKey, output->data, input->data);
    if (rv == SECSuccess) {
        output->len = pubKey->modulus.data[0] ? pubKey->modulus.len :
                                                pubKey->modulus.len - 1;
    }
    return rv;
}

SECStatus
rsa_PrivateKeyOp(void *cx, SECItem *output, const SECItem *input)
{
    bltestAsymKeyParams *params = (bltestAsymKeyParams *)cx;
    RSAPrivateKey *privKey = (RSAPrivateKey *)params->privKey;
    SECStatus rv = RSA_PrivateKeyOp(privKey, output->data, input->data);
    if (rv == SECSuccess) {
        output->len = privKey->modulus.data[0] ? privKey->modulus.len :
                                                 privKey->modulus.len - 1;
    }
    return rv;
}

SECStatus
rsa_signDigestPSS(void *cx, SECItem *output, const SECItem *input)
{
    bltestAsymKeyParams *params = (bltestAsymKeyParams *)cx;
    bltestRSAParams *rsaParams = &params->cipherParams.rsa;
    return RSA_SignPSS((RSAPrivateKey *)params->privKey,
                       rsaParams->hashAlg,
                       rsaParams->maskHashAlg,
                       rsaParams->seed.buf.data,
                       rsaParams->seed.buf.len,
                       output->data, &output->len, output->len,
                       input->data, input->len);
}

SECStatus
rsa_verifyDigestPSS(void *cx, SECItem *output, const SECItem *input)
{
    bltestAsymKeyParams *params = (bltestAsymKeyParams *)cx;
    bltestRSAParams *rsaParams = &params->cipherParams.rsa;
    return RSA_CheckSignPSS((RSAPublicKey *)params->pubKey,
                            rsaParams->hashAlg,
                            rsaParams->maskHashAlg,
                            rsaParams->seed.buf.len,
                            output->data, output->len,
                            input->data, input->len);
}

SECStatus
rsa_encryptOAEP(void *cx, SECItem *output, const SECItem *input)
{
    bltestAsymKeyParams *params = (bltestAsymKeyParams *)cx;
    bltestRSAParams *rsaParams = &params->cipherParams.rsa;
    return RSA_EncryptOAEP((RSAPublicKey *)params->pubKey,
                           rsaParams->hashAlg,
                           rsaParams->maskHashAlg,
                           NULL, 0,
                           rsaParams->seed.buf.data,
                           rsaParams->seed.buf.len,
                           output->data, &output->len, output->len,
                           input->data, input->len);
}

SECStatus
rsa_decryptOAEP(void *cx, SECItem *output, const SECItem *input)
{
    bltestAsymKeyParams *params = (bltestAsymKeyParams *)cx;
    bltestRSAParams *rsaParams = &params->cipherParams.rsa;
    return RSA_DecryptOAEP((RSAPrivateKey *)params->privKey,
                           rsaParams->hashAlg,
                           rsaParams->maskHashAlg,
                           NULL, 0,
                           output->data, &output->len, output->len,
                           input->data, input->len);
}

SECStatus
dsa_signDigest(void *cx, SECItem *output, const SECItem *input)
{
    bltestAsymKeyParams *params = (bltestAsymKeyParams *)cx;
    if (params->cipherParams.dsa.sigseed.buf.len > 0) {
        return DSA_SignDigestWithSeed((DSAPrivateKey *)params->privKey,
                                      output, input,
                                      params->cipherParams.dsa.sigseed.buf.data);
    }
    return DSA_SignDigest((DSAPrivateKey *)params->privKey, output, input);
}

SECStatus
dsa_verifyDigest(void *cx, SECItem *output, const SECItem *input)
{
    bltestAsymKeyParams *params = (bltestAsymKeyParams *)cx;
    return DSA_VerifyDigest((DSAPublicKey *)params->pubKey, output, input);
}

#ifndef NSS_DISABLE_ECC
SECStatus
ecdsa_signDigest(void *cx, SECItem *output, const SECItem *input)
{
    bltestAsymKeyParams *params = (bltestAsymKeyParams *)cx;
    if (params->cipherParams.ecdsa.sigseed.buf.len > 0) {
        return ECDSA_SignDigestWithSeed(
                        (ECPrivateKey *)params->privKey,
                        output, input,
                        params->cipherParams.ecdsa.sigseed.buf.data,
                        params->cipherParams.ecdsa.sigseed.buf.len);
    }
    return ECDSA_SignDigest((ECPrivateKey *)params->privKey, output, input);
}

SECStatus
ecdsa_verifyDigest(void *cx, SECItem *output, const SECItem *input)
{
    bltestAsymKeyParams *params = (bltestAsymKeyParams *)cx;
    return ECDSA_VerifyDigest((ECPublicKey *)params->pubKey, output, input);
}
#endif

SECStatus
bltest_des_init(bltestCipherInfo *cipherInfo, PRBool encrypt)
{
    PRIntervalTime time1, time2;
    bltestSymmKeyParams *desp = &cipherInfo->params.sk;
    int minorMode;
    int i;
    switch (cipherInfo->mode) {
    case bltestDES_ECB:	    minorMode = NSS_DES;	  break;
    case bltestDES_CBC:	    minorMode = NSS_DES_CBC;	  break;
    case bltestDES_EDE_ECB: minorMode = NSS_DES_EDE3;	  break;
    case bltestDES_EDE_CBC: minorMode = NSS_DES_EDE3_CBC; break;
    default:
	return SECFailure;
    }
    cipherInfo->cx = (void*)DES_CreateContext(desp->key.buf.data,
					      desp->iv.buf.data,
					      minorMode, encrypt);
    if (cipherInfo->cxreps > 0) {
	DESContext **dummycx;
	dummycx = PORT_Alloc(cipherInfo->cxreps * sizeof(DESContext *));
	TIMESTART();
	for (i=0; i<cipherInfo->cxreps; i++) {
	    dummycx[i] = (void*)DES_CreateContext(desp->key.buf.data,
					          desp->iv.buf.data,
					          minorMode, encrypt);
	}
	TIMEFINISH(cipherInfo->cxtime, 1.0);
	for (i=0; i<cipherInfo->cxreps; i++) {
	    DES_DestroyContext(dummycx[i], PR_TRUE);
	}
	PORT_Free(dummycx);
    }
    if (encrypt)
	cipherInfo->cipher.symmkeyCipher = des_Encrypt;
    else
	cipherInfo->cipher.symmkeyCipher = des_Decrypt;
    return SECSuccess;
}

SECStatus
bltest_rc2_init(bltestCipherInfo *cipherInfo, PRBool encrypt)
{
    PRIntervalTime time1, time2;
    bltestSymmKeyParams *rc2p = &cipherInfo->params.sk;
    int minorMode;
    int i;
    switch (cipherInfo->mode) {
    case bltestRC2_ECB: minorMode = NSS_RC2;	 break;
    case bltestRC2_CBC: minorMode = NSS_RC2_CBC; break;
    default:
	return SECFailure;
    }
    cipherInfo->cx = (void*)RC2_CreateContext(rc2p->key.buf.data,
					      rc2p->key.buf.len,
					      rc2p->iv.buf.data,
					      minorMode,
					      rc2p->key.buf.len);
    if (cipherInfo->cxreps > 0) {
	RC2Context **dummycx;
	dummycx = PORT_Alloc(cipherInfo->cxreps * sizeof(RC2Context *));
	TIMESTART();
	for (i=0; i<cipherInfo->cxreps; i++) {
	    dummycx[i] = (void*)RC2_CreateContext(rc2p->key.buf.data,
	                                          rc2p->key.buf.len,
	                                          rc2p->iv.buf.data,
	                                          minorMode,
	                                          rc2p->key.buf.len);
	}
	TIMEFINISH(cipherInfo->cxtime, 1.0);
	for (i=0; i<cipherInfo->cxreps; i++) {
	    RC2_DestroyContext(dummycx[i], PR_TRUE);
	}
	PORT_Free(dummycx);
    }
    if (encrypt)
	cipherInfo->cipher.symmkeyCipher = rc2_Encrypt;
    else
	cipherInfo->cipher.symmkeyCipher = rc2_Decrypt;
    return SECSuccess;
}

SECStatus
bltest_rc4_init(bltestCipherInfo *cipherInfo, PRBool encrypt)
{
    PRIntervalTime time1, time2;
    int i;
    bltestSymmKeyParams *rc4p = &cipherInfo->params.sk;
    cipherInfo->cx = (void*)RC4_CreateContext(rc4p->key.buf.data,
					      rc4p->key.buf.len);
    if (cipherInfo->cxreps > 0) {
	RC4Context **dummycx;
	dummycx = PORT_Alloc(cipherInfo->cxreps * sizeof(RC4Context *));
	TIMESTART();
	for (i=0; i<cipherInfo->cxreps; i++) {
	    dummycx[i] = (void*)RC4_CreateContext(rc4p->key.buf.data,
	                                          rc4p->key.buf.len);
	}
	TIMEFINISH(cipherInfo->cxtime, 1.0);
	for (i=0; i<cipherInfo->cxreps; i++) {
	    RC4_DestroyContext(dummycx[i], PR_TRUE);
	}
	PORT_Free(dummycx);
    }
    if (encrypt)
	cipherInfo->cipher.symmkeyCipher = rc4_Encrypt;
    else
	cipherInfo->cipher.symmkeyCipher = rc4_Decrypt;
    return SECSuccess;
}

SECStatus
bltest_rc5_init(bltestCipherInfo *cipherInfo, PRBool encrypt)
{
#ifdef NSS_SOFTOKEN_DOES_RC5
    PRIntervalTime time1, time2;
    bltestRC5Params *rc5p = &cipherInfo->params.rc5;
    int minorMode;
    switch (cipherInfo->mode) {
    case bltestRC5_ECB: minorMode = NSS_RC5;	 break;
    case bltestRC5_CBC: minorMode = NSS_RC5_CBC; break;
    default:
	return SECFailure;
    }
    TIMESTART();
    cipherInfo->cx = (void*)RC5_CreateContext(&rc5p->key.buf,
					      rc5p->rounds, rc5p->wordsize,
					      rc5p->iv.buf.data, minorMode);
    TIMEFINISH(cipherInfo->cxtime, 1.0);
    if (encrypt)
	cipherInfo->cipher.symmkeyCipher = RC5_Encrypt;
    else
	cipherInfo->cipher.symmkeyCipher = RC5_Decrypt;
    return SECSuccess;
#else
    return SECFailure;
#endif
}

SECStatus
bltest_aes_init(bltestCipherInfo *cipherInfo, PRBool encrypt)
{
    bltestSymmKeyParams *aesp = &cipherInfo->params.sk;
    bltestAuthSymmKeyParams *gcmp = &cipherInfo->params.ask;
    int minorMode;
    int i;
    int keylen   = aesp->key.buf.len;
    unsigned int blocklen = AES_BLOCK_SIZE;
    PRIntervalTime time1, time2;
    unsigned char *params;
    int len;
    CK_AES_CTR_PARAMS ctrParams;
    CK_GCM_PARAMS gcmParams;

    params = aesp->iv.buf.data;
    switch (cipherInfo->mode) {
    case bltestAES_ECB:	    minorMode = NSS_AES;	  break;
    case bltestAES_CBC:	    minorMode = NSS_AES_CBC;	  break;
    case bltestAES_CTS:	    minorMode = NSS_AES_CTS;	  break;
    case bltestAES_CTR:	    
	minorMode = NSS_AES_CTR;
	ctrParams.ulCounterBits = 32;
	len = PR_MIN(aesp->iv.buf.len, blocklen);
	PORT_Memset(ctrParams.cb, 0, blocklen);
	PORT_Memcpy(ctrParams.cb, aesp->iv.buf.data, len);
	params = (unsigned char *)&ctrParams;
	break;
    case bltestAES_GCM:
	minorMode = NSS_AES_GCM;
	gcmParams.pIv = gcmp->sk.iv.buf.data;
	gcmParams.ulIvLen = gcmp->sk.iv.buf.len;
	gcmParams.pAAD = gcmp->aad.buf.data;
	gcmParams.ulAADLen = gcmp->aad.buf.len;
	gcmParams.ulTagBits = blocklen*8;
	params = (unsigned char *)&gcmParams;
	break;
    default:
	return SECFailure;
    }
    cipherInfo->cx = (void*)AES_CreateContext(aesp->key.buf.data,
					      params,
					      minorMode, encrypt, 
                                              keylen, blocklen);
    if (cipherInfo->cxreps > 0) {
	AESContext **dummycx;
	dummycx = PORT_Alloc(cipherInfo->cxreps * sizeof(AESContext *));
	TIMESTART();
	for (i=0; i<cipherInfo->cxreps; i++) {
	    dummycx[i] = (void*)AES_CreateContext(aesp->key.buf.data,
					          params,
					          minorMode, encrypt,
	                                          keylen, blocklen);
	}
	TIMEFINISH(cipherInfo->cxtime, 1.0);
	for (i=0; i<cipherInfo->cxreps; i++) {
	    AES_DestroyContext(dummycx[i], PR_TRUE);
	}
	PORT_Free(dummycx);
    }
    if (encrypt)
	cipherInfo->cipher.symmkeyCipher = aes_Encrypt;
    else
	cipherInfo->cipher.symmkeyCipher = aes_Decrypt;
    return SECSuccess;
}

SECStatus
bltest_camellia_init(bltestCipherInfo *cipherInfo, PRBool encrypt)
{
    bltestSymmKeyParams *camelliap = &cipherInfo->params.sk;
    int minorMode;
    int i;
    int keylen   = camelliap->key.buf.len;
    PRIntervalTime time1, time2;
    
    switch (cipherInfo->mode) {
    case bltestCAMELLIA_ECB:	    minorMode = NSS_CAMELLIA;	  break;
    case bltestCAMELLIA_CBC:	    minorMode = NSS_CAMELLIA_CBC;  break;
    default:
	return SECFailure;
    }
    cipherInfo->cx = (void*)Camellia_CreateContext(camelliap->key.buf.data,
						   camelliap->iv.buf.data,
						   minorMode, encrypt, 
						   keylen);
    if (cipherInfo->cxreps > 0) {
	CamelliaContext **dummycx;
	dummycx = PORT_Alloc(cipherInfo->cxreps * sizeof(CamelliaContext *));
	TIMESTART();
	for (i=0; i<cipherInfo->cxreps; i++) {
	    dummycx[i] = (void*)Camellia_CreateContext(camelliap->key.buf.data,
						       camelliap->iv.buf.data,
						       minorMode, encrypt,
						       keylen);
	}
	TIMEFINISH(cipherInfo->cxtime, 1.0);
	for (i=0; i<cipherInfo->cxreps; i++) {
	    Camellia_DestroyContext(dummycx[i], PR_TRUE);
	}
	PORT_Free(dummycx);
    }
    if (encrypt)
	cipherInfo->cipher.symmkeyCipher = camellia_Encrypt;
    else
	cipherInfo->cipher.symmkeyCipher = camellia_Decrypt;
    return SECSuccess;
}

SECStatus
bltest_seed_init(bltestCipherInfo *cipherInfo, PRBool encrypt)
{
    PRIntervalTime time1, time2;
    bltestSymmKeyParams *seedp = &cipherInfo->params.sk;
    int minorMode;
    int i;

    switch (cipherInfo->mode) {
    case bltestSEED_ECB:	minorMode = NSS_SEED;		break;
    case bltestSEED_CBC:	minorMode = NSS_SEED_CBC;	break;
    default:
	return SECFailure;
    }
    cipherInfo->cx = (void*)SEED_CreateContext(seedp->key.buf.data,
					      seedp->iv.buf.data,
					      minorMode, encrypt);
    if (cipherInfo->cxreps > 0) {
	SEEDContext **dummycx;
	dummycx = PORT_Alloc(cipherInfo->cxreps * sizeof(SEEDContext *));
	TIMESTART();
	for (i=0; i<cipherInfo->cxreps; i++) {
	    dummycx[i] = (void*)SEED_CreateContext(seedp->key.buf.data,
					          seedp->iv.buf.data,
					          minorMode, encrypt);
	}
	TIMEFINISH(cipherInfo->cxtime, 1.0);
	for (i=0; i<cipherInfo->cxreps; i++) {
	    SEED_DestroyContext(dummycx[i], PR_TRUE);
	}
	PORT_Free(dummycx);
    }
    if (encrypt)
	cipherInfo->cipher.symmkeyCipher = seed_Encrypt;
    else
	cipherInfo->cipher.symmkeyCipher = seed_Decrypt;
	
	return SECSuccess;
}

SECStatus
bltest_chacha20_init(bltestCipherInfo *cipherInfo, PRBool encrypt)
{
    const unsigned int tagLen = 16;
    const bltestSymmKeyParams *sk = &cipherInfo->params.sk;
    cipherInfo->cx = ChaCha20Poly1305_CreateContext(sk->key.buf.data,
                                                    sk->key.buf.len, tagLen);

    if (encrypt)
	cipherInfo->cipher.aeadCipher = chacha20_poly1305_Encrypt;
    else
	cipherInfo->cipher.aeadCipher = chacha20_poly1305_Decrypt;
    return SECSuccess;
}

SECStatus
bltest_rsa_init(bltestCipherInfo *cipherInfo, PRBool encrypt)
{
    int i;
    RSAPrivateKey **dummyKey;
    RSAPrivateKey *privKey;
    RSAPublicKey *pubKey;
    PRIntervalTime time1, time2;

    bltestAsymKeyParams *asymk = &cipherInfo->params.asymk;
    bltestRSAParams *rsap = &asymk->cipherParams.rsa;

    /* RSA key gen was done during parameter setup */
    cipherInfo->cx = asymk;
    privKey = (RSAPrivateKey *)asymk->privKey;

    /* For performance testing */
    if (cipherInfo->cxreps > 0) {
	/* Create space for n private key objects */
	dummyKey = (RSAPrivateKey **)PORT_Alloc(cipherInfo->cxreps *
						sizeof(RSAPrivateKey *));
	/* Time n keygens, storing in the array */
	TIMESTART();
	for (i=0; i<cipherInfo->cxreps; i++)
	    dummyKey[i] = RSA_NewKey(rsap->keysizeInBits, 
	                             &privKey->publicExponent);
	TIMEFINISH(cipherInfo->cxtime, cipherInfo->cxreps);
	/* Free the n key objects */
	for (i=0; i<cipherInfo->cxreps; i++)
	    PORT_FreeArena(dummyKey[i]->arena, PR_TRUE);
	PORT_Free(dummyKey);
    }

    if ((encrypt && !is_sigCipher(cipherInfo->mode)) ||
        (!encrypt && is_sigCipher(cipherInfo->mode))) {
	/* Have to convert private key to public key.  Memory
	 * is freed with private key's arena  */
	pubKey = (RSAPublicKey *)PORT_ArenaAlloc(privKey->arena,
						 sizeof(RSAPublicKey));
	pubKey->modulus.len = privKey->modulus.len;
	pubKey->modulus.data = privKey->modulus.data;
	pubKey->publicExponent.len = privKey->publicExponent.len;
	pubKey->publicExponent.data = privKey->publicExponent.data;
	asymk->pubKey = (void *)pubKey;
    }
    switch (cipherInfo->mode) {
        case bltestRSA:
            cipherInfo->cipher.pubkeyCipher = encrypt ? rsa_PublicKeyOp
                                                      : rsa_PrivateKeyOp;
            break;
        case bltestRSA_PSS:
            cipherInfo->cipher.pubkeyCipher = encrypt ? rsa_signDigestPSS
                                                      : rsa_verifyDigestPSS;
            break;
        case bltestRSA_OAEP:
            cipherInfo->cipher.pubkeyCipher = encrypt ? rsa_encryptOAEP
                                                      : rsa_decryptOAEP;
            break;
        default:
            break;
    }
    return SECSuccess;
}

SECStatus
blapi_pqg_param_gen(unsigned int keysize, PQGParams **pqg, PQGVerify **vfy)
{
    if (keysize < 1024) {
	int j = PQG_PBITS_TO_INDEX(keysize);
	return PQG_ParamGen(j, pqg, vfy);
    }
    return PQG_ParamGenV2(keysize, 0, 0, pqg, vfy);
}

SECStatus
bltest_pqg_init(bltestDSAParams *dsap)
{
    SECStatus rv, res;
    PQGVerify *vfy = NULL;
    rv = blapi_pqg_param_gen(dsap->keysize, &dsap->pqg, &vfy);
    CHECKERROR(rv, __LINE__);
    rv = PQG_VerifyParams(dsap->pqg, vfy, &res);
    CHECKERROR(res, __LINE__);
    CHECKERROR(rv, __LINE__);
    return rv;
}

SECStatus
bltest_dsa_init(bltestCipherInfo *cipherInfo, PRBool encrypt)
{
    int i;
    DSAPrivateKey **dummyKey;
    PQGParams *dummypqg;
    PRIntervalTime time1, time2;
    bltestAsymKeyParams *asymk = &cipherInfo->params.asymk;
    bltestDSAParams *dsap = &asymk->cipherParams.dsa;
    PQGVerify *ignore = NULL;
    cipherInfo->cx = asymk;
    /* For performance testing */
    if (cipherInfo->cxreps > 0) {
	/* Create space for n private key objects */
	dummyKey = (DSAPrivateKey **)PORT_ZAlloc(cipherInfo->cxreps *
	                                         sizeof(DSAPrivateKey *));
	/* Time n keygens, storing in the array */
	TIMESTART();
	for (i=0; i<cipherInfo->cxreps; i++) {
	    dummypqg = NULL;
	    blapi_pqg_param_gen(dsap->keysize, &dummypqg, &ignore);
	    DSA_NewKey(dummypqg, &dummyKey[i]);
	}
	TIMEFINISH(cipherInfo->cxtime, cipherInfo->cxreps);
	/* Free the n key objects */
	for (i=0; i<cipherInfo->cxreps; i++)
	    PORT_FreeArena(dummyKey[i]->params.arena, PR_TRUE);
	PORT_Free(dummyKey);
    }
    if (!dsap->pqg && dsap->pqgdata.buf.len > 0) {
	dsap->pqg = pqg_from_filedata(&dsap->pqgdata.buf);
    }
    if (!asymk->privKey && asymk->key.buf.len > 0) {
	asymk->privKey = dsakey_from_filedata(&asymk->key.buf);
    }
    if (encrypt) {
	cipherInfo->cipher.pubkeyCipher = dsa_signDigest;
    } else {
	/* Have to convert private key to public key.  Memory
	 * is freed with private key's arena  */
	DSAPublicKey *pubkey;
	DSAPrivateKey *key = (DSAPrivateKey *)asymk->privKey;
	pubkey = (DSAPublicKey *)PORT_ArenaZAlloc(key->params.arena,
						  sizeof(DSAPublicKey));
	pubkey->params.prime.len = key->params.prime.len;
	pubkey->params.prime.data = key->params.prime.data;
	pubkey->params.subPrime.len = key->params.subPrime.len;
	pubkey->params.subPrime.data = key->params.subPrime.data;
	pubkey->params.base.len = key->params.base.len;
	pubkey->params.base.data = key->params.base.data;
	pubkey->publicValue.len = key->publicValue.len;
	pubkey->publicValue.data = key->publicValue.data;
	asymk->pubKey = pubkey;
	cipherInfo->cipher.pubkeyCipher = dsa_verifyDigest;
    }
    return SECSuccess;
}

#ifndef NSS_DISABLE_ECC
SECStatus
bltest_ecdsa_init(bltestCipherInfo *cipherInfo, PRBool encrypt)
{
    int i;
    ECPrivateKey **dummyKey;
    PRIntervalTime time1, time2;
    bltestAsymKeyParams *asymk = &cipherInfo->params.asymk;
    cipherInfo->cx = asymk;
    /* For performance testing */
    if (cipherInfo->cxreps > 0) {
	/* Create space for n private key objects */
	dummyKey = (ECPrivateKey **)PORT_ZAlloc(cipherInfo->cxreps *
	                                         sizeof(ECPrivateKey *));
	/* Time n keygens, storing in the array */
	TIMESTART();
	for (i=0; i<cipherInfo->cxreps; i++) {
	    EC_NewKey(&((ECPrivateKey *)asymk->privKey)->ecParams, &dummyKey[i]);
	}
	TIMEFINISH(cipherInfo->cxtime, cipherInfo->cxreps);
	/* Free the n key objects */
	for (i=0; i<cipherInfo->cxreps; i++)
	    PORT_FreeArena(dummyKey[i]->ecParams.arena, PR_TRUE);
	PORT_Free(dummyKey);
    }
    if (!asymk->privKey && asymk->key.buf.len > 0) {
        asymk->privKey = eckey_from_filedata(&asymk->key.buf);
    }
    if (encrypt) {
	cipherInfo->cipher.pubkeyCipher = ecdsa_signDigest;
    } else {
	/* Have to convert private key to public key.  Memory
	 * is freed with private key's arena  */
	ECPublicKey *pubkey;
	ECPrivateKey *key = (ECPrivateKey *)asymk->privKey;
	pubkey = (ECPublicKey *)PORT_ArenaZAlloc(key->ecParams.arena,
						  sizeof(ECPublicKey));
	pubkey->ecParams.type = key->ecParams.type;
	pubkey->ecParams.fieldID.size = key->ecParams.fieldID.size;
	pubkey->ecParams.fieldID.type = key->ecParams.fieldID.type;
	pubkey->ecParams.fieldID.u.prime.len = key->ecParams.fieldID.u.prime.len;
	pubkey->ecParams.fieldID.u.prime.data = key->ecParams.fieldID.u.prime.data;
	pubkey->ecParams.fieldID.k1 = key->ecParams.fieldID.k1;
	pubkey->ecParams.fieldID.k2 = key->ecParams.fieldID.k2;
	pubkey->ecParams.fieldID.k3 = key->ecParams.fieldID.k3;
	pubkey->ecParams.curve.a.len = key->ecParams.curve.a.len;
	pubkey->ecParams.curve.a.data = key->ecParams.curve.a.data;
	pubkey->ecParams.curve.b.len = key->ecParams.curve.b.len;
	pubkey->ecParams.curve.b.data = key->ecParams.curve.b.data;
	pubkey->ecParams.curve.seed.len = key->ecParams.curve.seed.len;
	pubkey->ecParams.curve.seed.data = key->ecParams.curve.seed.data;
	pubkey->ecParams.base.len = key->ecParams.base.len;
	pubkey->ecParams.base.data = key->ecParams.base.data;
	pubkey->ecParams.order.len = key->ecParams.order.len;
	pubkey->ecParams.order.data = key->ecParams.order.data;
	pubkey->ecParams.cofactor = key->ecParams.cofactor;
	pubkey->ecParams.DEREncoding.len = key->ecParams.DEREncoding.len;
	pubkey->ecParams.DEREncoding.data = key->ecParams.DEREncoding.data;
	pubkey->ecParams.name= key->ecParams.name;
	pubkey->publicValue.len = key->publicValue.len;
	pubkey->publicValue.data = key->publicValue.data;
	asymk->pubKey = pubkey;
	cipherInfo->cipher.pubkeyCipher = ecdsa_verifyDigest;
    }
    return SECSuccess;
}
#endif

/* XXX unfortunately, this is not defined in blapi.h */
SECStatus
md2_HashBuf(unsigned char *dest, const unsigned char *src, PRUint32 src_length)
{
    unsigned int len;
    MD2Context *cx = MD2_NewContext();
    if (cx == NULL) return SECFailure;
    MD2_Begin(cx);
    MD2_Update(cx, src, src_length);
    MD2_End(cx, dest, &len, MD2_LENGTH);
    MD2_DestroyContext(cx, PR_TRUE);
    return SECSuccess;
}

SECStatus
md2_restart(unsigned char *dest, const unsigned char *src, PRUint32 src_length)
{
    MD2Context *cx, *cx_cpy;
    unsigned char *cxbytes;
    unsigned int len;
    unsigned int i, quarter;
    SECStatus rv = SECSuccess;
    cx = MD2_NewContext();
    MD2_Begin(cx);
    /* divide message by 4, restarting 3 times */
    quarter = (src_length + 3)/ 4;
    for (i=0; i < 4 && src_length > 0; i++) {
	MD2_Update(cx, src + i*quarter, PR_MIN(quarter, src_length));
	len = MD2_FlattenSize(cx);
	cxbytes = PORT_Alloc(len);
	MD2_Flatten(cx, cxbytes);
	cx_cpy = MD2_Resurrect(cxbytes, NULL);
	if (!cx_cpy) {
	    PR_fprintf(PR_STDERR, "%s: MD2_Resurrect failed!\n", progName);
	    goto finish;
	}
	rv = PORT_Memcmp(cx, cx_cpy, len);
	if (rv) {
	    MD2_DestroyContext(cx_cpy, PR_TRUE);
	    PR_fprintf(PR_STDERR, "%s: MD2_restart failed!\n", progName);
	    goto finish;
	}
	MD2_DestroyContext(cx_cpy, PR_TRUE);
	PORT_Free(cxbytes);
	src_length -= quarter;
    }
    MD2_End(cx, dest, &len, MD2_LENGTH);
finish:
    MD2_DestroyContext(cx, PR_TRUE);
    return rv;
}

SECStatus
md5_restart(unsigned char *dest, const unsigned char *src, PRUint32 src_length)
{
    SECStatus rv = SECSuccess;
    MD5Context *cx, *cx_cpy;
    unsigned char *cxbytes;
    unsigned int len;
    unsigned int i, quarter;
    cx = MD5_NewContext();
    MD5_Begin(cx);
    /* divide message by 4, restarting 3 times */
    quarter = (src_length + 3)/ 4;
    for (i=0; i < 4 && src_length > 0; i++) {
	MD5_Update(cx, src + i*quarter, PR_MIN(quarter, src_length));
	len = MD5_FlattenSize(cx);
	cxbytes = PORT_Alloc(len);
	MD5_Flatten(cx, cxbytes);
	cx_cpy = MD5_Resurrect(cxbytes, NULL);
	if (!cx_cpy) {
	    PR_fprintf(PR_STDERR, "%s: MD5_Resurrect failed!\n", progName);
	    rv = SECFailure;
	    goto finish;
	}
	rv = PORT_Memcmp(cx, cx_cpy, len);
	if (rv) {
	    MD5_DestroyContext(cx_cpy, PR_TRUE);
	    PR_fprintf(PR_STDERR, "%s: MD5_restart failed!\n", progName);
	    goto finish;
	}
	MD5_DestroyContext(cx_cpy, PR_TRUE);
	PORT_Free(cxbytes);
	src_length -= quarter;
    }
    MD5_End(cx, dest, &len, MD5_LENGTH);
finish:
    MD5_DestroyContext(cx, PR_TRUE);
    return rv;
}

SECStatus
sha1_restart(unsigned char *dest, const unsigned char *src, PRUint32 src_length)
{
    SECStatus rv = SECSuccess;
    SHA1Context *cx, *cx_cpy;
    unsigned char *cxbytes;
    unsigned int len;
    unsigned int i, quarter;
    cx = SHA1_NewContext();
    SHA1_Begin(cx);
    /* divide message by 4, restarting 3 times */
    quarter = (src_length + 3)/ 4;
    for (i=0; i < 4 && src_length > 0; i++) {
	SHA1_Update(cx, src + i*quarter, PR_MIN(quarter, src_length));
	len = SHA1_FlattenSize(cx);
	cxbytes = PORT_Alloc(len);
	SHA1_Flatten(cx, cxbytes);
	cx_cpy = SHA1_Resurrect(cxbytes, NULL);
	if (!cx_cpy) {
	    PR_fprintf(PR_STDERR, "%s: SHA1_Resurrect failed!\n", progName);
	    rv = SECFailure;
	    goto finish;
	}
	rv = PORT_Memcmp(cx, cx_cpy, len);
	if (rv) {
	    SHA1_DestroyContext(cx_cpy, PR_TRUE);
	    PR_fprintf(PR_STDERR, "%s: SHA1_restart failed!\n", progName);
	    goto finish;
	}
	SHA1_DestroyContext(cx_cpy, PR_TRUE);
	PORT_Free(cxbytes);
	src_length -= quarter;
    }
    SHA1_End(cx, dest, &len, MD5_LENGTH);
finish:
    SHA1_DestroyContext(cx, PR_TRUE);
    return rv;
}

SECStatus
SHA224_restart(unsigned char *dest, const unsigned char *src, PRUint32 src_length)
{
    SECStatus rv = SECSuccess;
    SHA224Context *cx, *cx_cpy;
    unsigned char *cxbytes;
    unsigned int len;
    unsigned int i, quarter;
    cx = SHA224_NewContext();
    SHA224_Begin(cx);
    /* divide message by 4, restarting 3 times */
    quarter = (src_length + 3) / 4;
    for (i=0; i < 4 && src_length > 0; i++) {
	SHA224_Update(cx, src + i*quarter, PR_MIN(quarter, src_length));
	len = SHA224_FlattenSize(cx);
	cxbytes = PORT_Alloc(len);
	SHA224_Flatten(cx, cxbytes);
	cx_cpy = SHA224_Resurrect(cxbytes, NULL);
	if (!cx_cpy) {
	    PR_fprintf(PR_STDERR, "%s: SHA224_Resurrect failed!\n", progName);
	    rv = SECFailure;
	    goto finish;
	}
	rv = PORT_Memcmp(cx, cx_cpy, len);
	if (rv) {
	    SHA224_DestroyContext(cx_cpy, PR_TRUE);
	    PR_fprintf(PR_STDERR, "%s: SHA224_restart failed!\n", progName);
	    goto finish;
	}
	
	SHA224_DestroyContext(cx_cpy, PR_TRUE);
	PORT_Free(cxbytes);
	src_length -= quarter;
    }
    SHA224_End(cx, dest, &len, MD5_LENGTH);
finish:
    SHA224_DestroyContext(cx, PR_TRUE);
    return rv;
}

SECStatus
SHA256_restart(unsigned char *dest, const unsigned char *src, PRUint32 src_length)
{
    SECStatus rv = SECSuccess;
    SHA256Context *cx, *cx_cpy;
    unsigned char *cxbytes;
    unsigned int len;
    unsigned int i, quarter;
    cx = SHA256_NewContext();
    SHA256_Begin(cx);
    /* divide message by 4, restarting 3 times */
    quarter = (src_length + 3)/ 4;
    for (i=0; i < 4 && src_length > 0; i++) {
	SHA256_Update(cx, src + i*quarter, PR_MIN(quarter, src_length));
	len = SHA256_FlattenSize(cx);
	cxbytes = PORT_Alloc(len);
	SHA256_Flatten(cx, cxbytes);
	cx_cpy = SHA256_Resurrect(cxbytes, NULL);
	if (!cx_cpy) {
	    PR_fprintf(PR_STDERR, "%s: SHA256_Resurrect failed!\n", progName);
	    rv = SECFailure;
	    goto finish;
	}
	rv = PORT_Memcmp(cx, cx_cpy, len);
	if (rv) {
	    SHA256_DestroyContext(cx_cpy, PR_TRUE);
	    PR_fprintf(PR_STDERR, "%s: SHA256_restart failed!\n", progName);
	    goto finish;
	}
	SHA256_DestroyContext(cx_cpy, PR_TRUE);
	PORT_Free(cxbytes);
	src_length -= quarter;
    }
    SHA256_End(cx, dest, &len, MD5_LENGTH);
finish:
    SHA256_DestroyContext(cx, PR_TRUE);
    return rv;
}

SECStatus
SHA384_restart(unsigned char *dest, const unsigned char *src, PRUint32 src_length)
{
    SECStatus rv = SECSuccess;
    SHA384Context *cx, *cx_cpy;
    unsigned char *cxbytes;
    unsigned int len;
    unsigned int i, quarter;
    cx = SHA384_NewContext();
    SHA384_Begin(cx);
    /* divide message by 4, restarting 3 times */
    quarter = (src_length + 3)/ 4;
    for (i=0; i < 4 && src_length > 0; i++) {
	SHA384_Update(cx, src + i*quarter, PR_MIN(quarter, src_length));
	len = SHA384_FlattenSize(cx);
	cxbytes = PORT_Alloc(len);
	SHA384_Flatten(cx, cxbytes);
	cx_cpy = SHA384_Resurrect(cxbytes, NULL);
	if (!cx_cpy) {
	    PR_fprintf(PR_STDERR, "%s: SHA384_Resurrect failed!\n", progName);
	    rv = SECFailure;
	    goto finish;
	}
	rv = PORT_Memcmp(cx, cx_cpy, len);
	if (rv) {
	    SHA384_DestroyContext(cx_cpy, PR_TRUE);
	    PR_fprintf(PR_STDERR, "%s: SHA384_restart failed!\n", progName);
	    goto finish;
	}
	SHA384_DestroyContext(cx_cpy, PR_TRUE);
	PORT_Free(cxbytes);
	src_length -= quarter;
    }
    SHA384_End(cx, dest, &len, MD5_LENGTH);
finish:
    SHA384_DestroyContext(cx, PR_TRUE);
    return rv;
}

SECStatus
SHA512_restart(unsigned char *dest, const unsigned char *src, PRUint32 src_length)
{
    SECStatus rv = SECSuccess;
    SHA512Context *cx, *cx_cpy;
    unsigned char *cxbytes;
    unsigned int len;
    unsigned int i, quarter;
    cx = SHA512_NewContext();
    SHA512_Begin(cx);
    /* divide message by 4, restarting 3 times */
    quarter = (src_length + 3)/ 4;
    for (i=0; i < 4 && src_length > 0; i++) {
	SHA512_Update(cx, src + i*quarter, PR_MIN(quarter, src_length));
	len = SHA512_FlattenSize(cx);
	cxbytes = PORT_Alloc(len);
	SHA512_Flatten(cx, cxbytes);
	cx_cpy = SHA512_Resurrect(cxbytes, NULL);
	if (!cx_cpy) {
	    PR_fprintf(PR_STDERR, "%s: SHA512_Resurrect failed!\n", progName);
	    rv = SECFailure;
	    goto finish;
	}
	rv = PORT_Memcmp(cx, cx_cpy, len);
	if (rv) {
	    SHA512_DestroyContext(cx_cpy, PR_TRUE);
	    PR_fprintf(PR_STDERR, "%s: SHA512_restart failed!\n", progName);
	    goto finish;
	}
	SHA512_DestroyContext(cx_cpy, PR_TRUE);
	PORT_Free(cxbytes);
	src_length -= quarter;
    }
    SHA512_End(cx, dest, &len, MD5_LENGTH);
finish:
    SHA512_DestroyContext(cx, PR_TRUE);
    return rv;
}

SECStatus
pubkeyInitKey(bltestCipherInfo *cipherInfo, PRFileDesc *file,
#ifndef NSS_DISABLE_ECC
	      int keysize, int exponent, char *curveName)
#else
	      int keysize, int exponent)
#endif
{
    int i;
    SECStatus rv = SECSuccess;
    bltestAsymKeyParams *asymk = &cipherInfo->params.asymk;
    bltestRSAParams *rsap;
    RSAPrivateKey **rsaKey = NULL;
    bltestDSAParams *dsap;
    DSAPrivateKey **dsaKey = NULL;
#ifndef NSS_DISABLE_ECC
    SECItem *tmpECParamsDER;
    ECParams *tmpECParams = NULL;
    SECItem ecSerialize[3];
    ECPrivateKey **ecKey = NULL;
#endif
    switch (cipherInfo->mode) {
    case bltestRSA:
    case bltestRSA_PSS:
    case bltestRSA_OAEP:
	rsap = &asymk->cipherParams.rsa;
        rsaKey = (RSAPrivateKey **)&asymk->privKey;
	if (keysize > 0) {
	    SECItem expitem = { 0, 0, 0 };
	    SECITEM_AllocItem(cipherInfo->arena, &expitem, sizeof(int));
	    for (i = 1; i <= sizeof(int); i++)
		expitem.data[i-1] = exponent >> (8*(sizeof(int) - i));
	    *rsaKey = RSA_NewKey(keysize * 8, &expitem);
	    serialize_key(&(*rsaKey)->version, 9, file);
	    rsap->keysizeInBits = keysize * 8;
	} else {
	    setupIO(cipherInfo->arena, &asymk->key, file, NULL, 0);
	    *rsaKey = rsakey_from_filedata(&asymk->key.buf);
	    rsap->keysizeInBits = (*rsaKey)->modulus.len * 8;
	}
	break;
    case bltestDSA:
	dsap = &asymk->cipherParams.dsa;
	dsaKey = (DSAPrivateKey **)&asymk->privKey;
	if (keysize > 0) {
	    dsap->keysize = keysize*8;
	    if (!dsap->pqg)
		bltest_pqg_init(dsap);
	    rv = DSA_NewKey(dsap->pqg, dsaKey);
	    CHECKERROR(rv, __LINE__);
	    serialize_key(&(*dsaKey)->params.prime, 5, file);
	} else {
	    setupIO(cipherInfo->arena, &asymk->key, file, NULL, 0);
	    *dsaKey = dsakey_from_filedata(&asymk->key.buf);
	    dsap->keysize = (*dsaKey)->params.prime.len*8;
	}
	break;
#ifndef NSS_DISABLE_ECC
    case bltestECDSA:
	ecKey = (ECPrivateKey **)&asymk->privKey;
	if (curveName != NULL) {
	    tmpECParamsDER = getECParams(curveName);
	    rv = SECOID_Init();
	    CHECKERROR(rv, __LINE__);
	    rv = EC_DecodeParams(tmpECParamsDER, &tmpECParams) == SECFailure;
	    CHECKERROR(rv, __LINE__);
	    rv = EC_NewKey(tmpECParams, ecKey);
	    CHECKERROR(rv, __LINE__);
	    ecSerialize[0].type = tmpECParamsDER->type;
	    ecSerialize[0].data = tmpECParamsDER->data;
	    ecSerialize[0].len  = tmpECParamsDER->len;
	    ecSerialize[1].type = (*ecKey)->publicValue.type;
	    ecSerialize[1].data = (*ecKey)->publicValue.data;
	    ecSerialize[1].len  = (*ecKey)->publicValue.len;
	    ecSerialize[2].type = (*ecKey)->privateValue.type;
	    ecSerialize[2].data = (*ecKey)->privateValue.data;
	    ecSerialize[2].len  = (*ecKey)->privateValue.len;
	    serialize_key(&(ecSerialize[0]), 3, file);
	    SECITEM_FreeItem(tmpECParamsDER, PR_TRUE);
	    PORT_FreeArena(tmpECParams->arena, PR_TRUE);
	    rv = SECOID_Shutdown();
	    CHECKERROR(rv, __LINE__);
	} else {
	    setupIO(cipherInfo->arena, &asymk->key, file, NULL, 0);
	    *ecKey = eckey_from_filedata(&asymk->key.buf);
	}
	break;
#endif
    default:
	return SECFailure;
    }
    return SECSuccess;
}

SECStatus
cipherInit(bltestCipherInfo *cipherInfo, PRBool encrypt)
{
    PRBool restart;
    int outlen;
    switch (cipherInfo->mode) {
    case bltestDES_ECB:
    case bltestDES_CBC:
    case bltestDES_EDE_ECB:
    case bltestDES_EDE_CBC:
	SECITEM_AllocItem(cipherInfo->arena, &cipherInfo->output.buf,
			  cipherInfo->input.pBuf.len);
	return bltest_des_init(cipherInfo, encrypt);
	break;
    case bltestRC2_ECB:
    case bltestRC2_CBC:
	SECITEM_AllocItem(cipherInfo->arena, &cipherInfo->output.buf,
			  cipherInfo->input.pBuf.len);
	return bltest_rc2_init(cipherInfo, encrypt);
	break;
    case bltestRC4:
	SECITEM_AllocItem(cipherInfo->arena, &cipherInfo->output.buf,
			  cipherInfo->input.pBuf.len);
	return bltest_rc4_init(cipherInfo, encrypt);
	break;
#ifdef NSS_SOFTOKEN_DOES_RC5
    case bltestRC5_ECB:
    case bltestRC5_CBC:
	SECITEM_AllocItem(cipherInfo->arena, &cipherInfo->output.buf,
			  cipherInfo->input.pBuf.len);
#endif
	return bltest_rc5_init(cipherInfo, encrypt);
	break;
    case bltestAES_ECB:
    case bltestAES_CBC:
    case bltestAES_CTS:
    case bltestAES_CTR:
    case bltestAES_GCM:
	outlen = cipherInfo->input.pBuf.len;
	if (cipherInfo->mode == bltestAES_GCM && encrypt) {
	    outlen += 16;
	}
	SECITEM_AllocItem(cipherInfo->arena, &cipherInfo->output.buf, outlen);
	return bltest_aes_init(cipherInfo, encrypt);
	break;
    case bltestCAMELLIA_ECB:
    case bltestCAMELLIA_CBC:
	SECITEM_AllocItem(cipherInfo->arena, &cipherInfo->output.buf,
			  cipherInfo->input.pBuf.len);
	return bltest_camellia_init(cipherInfo, encrypt);
	break;
    case bltestSEED_ECB:
    case bltestSEED_CBC:
	SECITEM_AllocItem(cipherInfo->arena, &cipherInfo->output.buf,
			  cipherInfo->input.pBuf.len);
	return bltest_seed_init(cipherInfo, encrypt);
	break;
    case bltestCHACHA20:
	outlen = cipherInfo->input.pBuf.len + (encrypt ? 16 : 0);
	SECITEM_AllocItem(cipherInfo->arena, &cipherInfo->output.buf, outlen);
	return bltest_chacha20_init(cipherInfo, encrypt);
	break;
    case bltestRSA:
    case bltestRSA_OAEP:
    case bltestRSA_PSS:
	if (encrypt || cipherInfo->mode != bltestRSA_PSS) {
		/* Don't allocate a buffer for PSS in verify mode, as no actual
		 * output is produced. */
		SECITEM_AllocItem(cipherInfo->arena, &cipherInfo->output.buf,
		                  RSA_MAX_MODULUS_BITS / 8);
	}
	return bltest_rsa_init(cipherInfo, encrypt);
	break;
    case bltestDSA:
	if (encrypt) {
		SECITEM_AllocItem(cipherInfo->arena, &cipherInfo->output.buf,
		                  DSA_MAX_SIGNATURE_LEN);
	}
	return bltest_dsa_init(cipherInfo, encrypt);
	break;
#ifndef NSS_DISABLE_ECC
    case bltestECDSA:
	if (encrypt) {
		SECITEM_AllocItem(cipherInfo->arena, &cipherInfo->output.buf,
		                  2 * MAX_ECKEY_LEN);
	}
	return bltest_ecdsa_init(cipherInfo, encrypt);
	break;
#endif
    case bltestMD2:
	restart = cipherInfo->params.hash.restart;
	SECITEM_AllocItem(cipherInfo->arena, &cipherInfo->output.buf,
			  MD2_LENGTH);
	cipherInfo->cipher.hashCipher = (restart) ? md2_restart : md2_HashBuf;
	return SECSuccess;
	break;
    case bltestMD5:
	restart = cipherInfo->params.hash.restart;
	SECITEM_AllocItem(cipherInfo->arena, &cipherInfo->output.buf,
			  MD5_LENGTH);
	cipherInfo->cipher.hashCipher = (restart) ? md5_restart : MD5_HashBuf;
	return SECSuccess;
	break;
    case bltestSHA1:
	restart = cipherInfo->params.hash.restart;
	SECITEM_AllocItem(cipherInfo->arena, &cipherInfo->output.buf,
			  SHA1_LENGTH);
	cipherInfo->cipher.hashCipher = (restart) ? sha1_restart : SHA1_HashBuf;
	return SECSuccess;
	break;
    case bltestSHA224:
	restart = cipherInfo->params.hash.restart;
	SECITEM_AllocItem(cipherInfo->arena, &cipherInfo->output.buf,
			  SHA224_LENGTH);
	cipherInfo->cipher.hashCipher = (restart) ? SHA224_restart 
	                                          : SHA224_HashBuf;
	return SECSuccess;
	break;
    case bltestSHA256:
	restart = cipherInfo->params.hash.restart;
	SECITEM_AllocItem(cipherInfo->arena, &cipherInfo->output.buf,
			  SHA256_LENGTH);
	cipherInfo->cipher.hashCipher = (restart) ? SHA256_restart 
	                                          : SHA256_HashBuf;
	return SECSuccess;
	break;
    case bltestSHA384:
	restart = cipherInfo->params.hash.restart;
	SECITEM_AllocItem(cipherInfo->arena, &cipherInfo->output.buf,
			  SHA384_LENGTH);
	cipherInfo->cipher.hashCipher = (restart) ? SHA384_restart 
	                                          : SHA384_HashBuf;
	return SECSuccess;
	break;
    case bltestSHA512:
	restart = cipherInfo->params.hash.restart;
	SECITEM_AllocItem(cipherInfo->arena, &cipherInfo->output.buf,
			  SHA512_LENGTH);
	cipherInfo->cipher.hashCipher = (restart) ? SHA512_restart 
	                                          : SHA512_HashBuf;
	return SECSuccess;
	break;
    default:
	return SECFailure;
    }
    return SECSuccess;
}

SECStatus
cipherDoOp(bltestCipherInfo *cipherInfo)
{
    PRIntervalTime time1, time2;
    SECStatus rv = SECSuccess;
    int i;
    unsigned int len;
    unsigned int maxLen = cipherInfo->output.pBuf.len;
    unsigned char *dummyOut;
    dummyOut = PORT_Alloc(maxLen);
    if (is_symmkeyCipher(cipherInfo->mode)) {
        const unsigned char *input = cipherInfo->input.pBuf.data;
        unsigned int inputLen = is_singleShotCipher(cipherInfo->mode) ?
                 cipherInfo->input.pBuf.len :
                 PR_MIN(cipherInfo->input.pBuf.len, 16);
        unsigned char *output = cipherInfo->output.pBuf.data;
        unsigned int outputLen = maxLen;
        unsigned int totalOutputLen = 0;
        TIMESTART();
        rv = (*cipherInfo->cipher.symmkeyCipher)(cipherInfo->cx,
                                                 output, &len, outputLen,
                                                 input, inputLen);
        CHECKERROR(rv, __LINE__);
        totalOutputLen += len;
        if (cipherInfo->input.pBuf.len > inputLen) {
            input += inputLen;
            inputLen = cipherInfo->input.pBuf.len - inputLen;
            output += len;
            outputLen -= len;
            rv = (*cipherInfo->cipher.symmkeyCipher)(cipherInfo->cx,
                                                     output, &len, outputLen,
                                                     input, inputLen);
            CHECKERROR(rv, __LINE__);
	    totalOutputLen += len;
        }
	cipherInfo->output.pBuf.len = totalOutputLen;
        TIMEFINISH(cipherInfo->optime, 1.0);
        cipherInfo->repetitions = 0;
        if (cipherInfo->repetitionsToPerfom != 0) {
            TIMESTART();
            for (i=0; i<cipherInfo->repetitionsToPerfom; i++,
                     cipherInfo->repetitions++) {
                (*cipherInfo->cipher.symmkeyCipher)(cipherInfo->cx, dummyOut,
                                                    &len, maxLen,
                                                    cipherInfo->input.pBuf.data,
                                                    cipherInfo->input.pBuf.len);
                
                CHECKERROR(rv, __LINE__);
            }
        } else {
            int opsBetweenChecks = 0;
            TIMEMARK(cipherInfo->seconds);
            while (! (TIMETOFINISH())) {
                int j = 0;
                for (;j < opsBetweenChecks;j++) {
                    (*cipherInfo->cipher.symmkeyCipher)(
                        cipherInfo->cx, dummyOut, &len, maxLen,
                        cipherInfo->input.pBuf.data,
                        cipherInfo->input.pBuf.len);
                }
                cipherInfo->repetitions += j;
            }
        }
        TIMEFINISH(cipherInfo->optime, 1.0);
    } else if (is_aeadCipher(cipherInfo->mode)) {
        const unsigned char *input = cipherInfo->input.pBuf.data;
        unsigned int inputLen = cipherInfo->input.pBuf.len;
        unsigned char *output = cipherInfo->output.pBuf.data;
        unsigned int outputLen;
        bltestSymmKeyParams *sk = &cipherInfo->params.sk;
        bltestAuthSymmKeyParams *ask = &cipherInfo->params.ask;

        TIMESTART();
        rv = (*cipherInfo->cipher.aeadCipher)(
                cipherInfo->cx,
                output, &outputLen, maxLen,
                input, inputLen,
                sk->iv.buf.data, sk->iv.buf.len,
                ask->aad.buf.data, ask->aad.buf.len);
        CHECKERROR(rv, __LINE__);
        cipherInfo->output.pBuf.len = outputLen;
        TIMEFINISH(cipherInfo->optime, 1.0);

        cipherInfo->repetitions = 0;
        if (cipherInfo->repetitionsToPerfom != 0) {
            TIMESTART();
            for (i=0; i<cipherInfo->repetitionsToPerfom; i++,
                     cipherInfo->repetitions++) {
                rv = (*cipherInfo->cipher.aeadCipher)(
                        cipherInfo->cx,
                        output, &outputLen, maxLen,
                        input, inputLen,
                        sk->iv.buf.data, sk->iv.buf.len,
                        ask->aad.buf.data, ask->aad.buf.len);
                CHECKERROR(rv, __LINE__);
            }
        } else {
            int opsBetweenChecks = 0;
            TIMEMARK(cipherInfo->seconds);
            while (! (TIMETOFINISH())) {
                int j = 0;
                for (;j < opsBetweenChecks;j++) {
                    (*cipherInfo->cipher.aeadCipher)(
                            cipherInfo->cx,
                            output, &outputLen, maxLen,
                            input, inputLen,
                            sk->iv.buf.data, sk->iv.buf.len,
                            ask->aad.buf.data, ask->aad.buf.len);
                }
                cipherInfo->repetitions += j;
            }
        }
        TIMEFINISH(cipherInfo->optime, 1.0);
    } else if (is_pubkeyCipher(cipherInfo->mode)) {
        TIMESTART();
        rv = (*cipherInfo->cipher.pubkeyCipher)(cipherInfo->cx,
                                                &cipherInfo->output.pBuf,
                                                &cipherInfo->input.pBuf);
        TIMEFINISH(cipherInfo->optime, 1.0);
        CHECKERROR(rv, __LINE__);
        cipherInfo->repetitions = 0;
        if (cipherInfo->repetitionsToPerfom != 0) {
            TIMESTART();
            for (i=0; i<cipherInfo->repetitionsToPerfom;
                 i++, cipherInfo->repetitions++) {
                SECItem dummy;
                dummy.data = dummyOut;
                dummy.len = maxLen;
                (*cipherInfo->cipher.pubkeyCipher)(cipherInfo->cx, &dummy, 
                                                   &cipherInfo->input.pBuf);
                CHECKERROR(rv, __LINE__);
            }
        } else {
            int opsBetweenChecks = 0;
            TIMEMARK(cipherInfo->seconds);
            while (! (TIMETOFINISH())) {
                int j = 0;
                for (;j < opsBetweenChecks;j++) {
                    SECItem dummy;
                    dummy.data = dummyOut;
                    dummy.len = maxLen;
                    (*cipherInfo->cipher.pubkeyCipher)(cipherInfo->cx, &dummy,
                                                       &cipherInfo->input.pBuf);
                    CHECKERROR(rv, __LINE__);
                }
                cipherInfo->repetitions += j;
            }
        }
        TIMEFINISH(cipherInfo->optime, 1.0);
    } else if (is_hashCipher(cipherInfo->mode)) {
        TIMESTART();
        rv = (*cipherInfo->cipher.hashCipher)(cipherInfo->output.pBuf.data,
                                              cipherInfo->input.pBuf.data,
                                              cipherInfo->input.pBuf.len);
        TIMEFINISH(cipherInfo->optime, 1.0);
        CHECKERROR(rv, __LINE__);
        cipherInfo->repetitions = 0;
        if (cipherInfo->repetitionsToPerfom != 0) {
            TIMESTART();
            for (i=0; i<cipherInfo->repetitionsToPerfom;
                 i++, cipherInfo->repetitions++) {
                (*cipherInfo->cipher.hashCipher)(dummyOut,
                                                 cipherInfo->input.pBuf.data,
                                                 cipherInfo->input.pBuf.len);
                CHECKERROR(rv, __LINE__);
            }
        } else {
            int opsBetweenChecks = 0;
            TIMEMARK(cipherInfo->seconds);
            while (! (TIMETOFINISH())) {
                int j = 0;
                for (;j < opsBetweenChecks;j++) {
                    bltestIO *input = &cipherInfo->input;
                    (*cipherInfo->cipher.hashCipher)(dummyOut,
                                                     input->pBuf.data,
                                                     input->pBuf.len);
                    CHECKERROR(rv, __LINE__);
                }
                cipherInfo->repetitions += j;
            }
        }
        TIMEFINISH(cipherInfo->optime, 1.0);
    }
    PORT_Free(dummyOut);
    return rv;
}

SECStatus
cipherFinish(bltestCipherInfo *cipherInfo)
{
    SECStatus rv = SECSuccess;

    switch (cipherInfo->mode) {
    case bltestDES_ECB:
    case bltestDES_CBC:
    case bltestDES_EDE_ECB:
    case bltestDES_EDE_CBC:
	DES_DestroyContext((DESContext *)cipherInfo->cx, PR_TRUE);
	break;
    case bltestAES_GCM:
    case bltestAES_ECB:
    case bltestAES_CBC:
    case bltestAES_CTS:
    case bltestAES_CTR:
	AES_DestroyContext((AESContext *)cipherInfo->cx, PR_TRUE);
	break;
    case bltestCAMELLIA_ECB:
    case bltestCAMELLIA_CBC:
	Camellia_DestroyContext((CamelliaContext *)cipherInfo->cx, PR_TRUE);
	break;
    case bltestSEED_ECB:
    case bltestSEED_CBC:
	SEED_DestroyContext((SEEDContext *)cipherInfo->cx, PR_TRUE);
	break;
    case bltestCHACHA20:
	ChaCha20Poly1305_DestroyContext((ChaCha20Poly1305Context *)
                                        cipherInfo->cx, PR_TRUE);
	break;
    case bltestRC2_ECB:
    case bltestRC2_CBC:
	RC2_DestroyContext((RC2Context *)cipherInfo->cx, PR_TRUE);
	break;
    case bltestRC4:
	RC4_DestroyContext((RC4Context *)cipherInfo->cx, PR_TRUE);
	break;
#ifdef NSS_SOFTOKEN_DOES_RC5
    case bltestRC5_ECB:
    case bltestRC5_CBC:
	RC5_DestroyContext((RC5Context *)cipherInfo->cx, PR_TRUE);
	break;
#endif
    case bltestRSA:      /* keys are alloc'ed within cipherInfo's arena, */
    case bltestRSA_PSS:  /* will be freed with it. */
    case bltestRSA_OAEP:
    case bltestDSA:
#ifndef NSS_DISABLE_ECC
    case bltestECDSA:
#endif
    case bltestMD2: /* hash contexts are ephemeral */
    case bltestMD5:
    case bltestSHA1:
    case bltestSHA224:
    case bltestSHA256:
    case bltestSHA384:
    case bltestSHA512:
	return SECSuccess;
	break;
    default:
	return SECFailure;
    }
    return rv;
}

void
print_exponent(SECItem *exp)
{
    int i;
    int e = 0;
    if (exp->len <= 4) {
	for (i=exp->len; i >=0; --i) e |= exp->data[exp->len-i] << 8*(i-1);
	fprintf(stdout, "%12d", e);
    } else {
	e = 8*exp->len;
	fprintf(stdout, "~2**%-8d", e);
    }
}

static void
splitToReportUnit(PRInt64 res, int *resArr, int *del, int size)
{
    PRInt64 remaining = res, tmp = 0;
    PRInt64 Ldel;
    int i = -1;

    while (remaining > 0 && ++i < size) {
        LL_I2L(Ldel, del[i]);
        LL_MOD(tmp, remaining, Ldel);
        LL_L2I(resArr[i], tmp);
        LL_DIV(remaining, remaining, Ldel);
    }
}

static char*
getHighUnitBytes(PRInt64 res)
{
    int spl[] = {0, 0, 0, 0};
    int del[] = {1024, 1024, 1024, 1024};
    char *marks[] = {"b", "Kb", "Mb", "Gb"};
    int i = 3;

    splitToReportUnit(res, spl, del, 4);

    for (;i>0;i--) {
        if (spl[i] != 0) {
            break;
        }
    }

    return PR_smprintf("%d%s", spl[i], marks[i]);
}


static void
printPR_smpString(const char *sformat, char *reportStr,
                  const char *nformat, PRInt64 rNum)
{
    if (reportStr) {
        fprintf(stdout, sformat, reportStr);
        PR_smprintf_free(reportStr);
    } else {
        fprintf(stdout, nformat, rNum);
    }
}

static char*
getHighUnitOps(PRInt64 res)
{
    int spl[] = {0, 0, 0, 0};
    int del[] = {1000, 1000, 1000, 1000};
    char *marks[] = {"", "T", "M", "B"};
    int i = 3;

    splitToReportUnit(res, spl, del, 4);

    for (;i>0;i--) {
        if (spl[i] != 0) {
            break;
        }
    }

    return PR_smprintf("%d%s", spl[i], marks[i]);
}

void
dump_performance_info(bltestCipherInfo *infoList, double totalTimeInt,
                      PRBool encrypt, PRBool cxonly)
{
    bltestCipherInfo *info = infoList;
    
    PRInt64 totalIn = 0;
    PRBool td = PR_TRUE;

    int   repetitions = 0;
    int   cxreps = 0;
    double cxtime = 0;
    double optime = 0;
    while (info != NULL) {
        repetitions += info->repetitions;
        cxreps += info->cxreps;
        cxtime += info->cxtime;
        optime += info->optime;
        totalIn += (PRInt64) info->input.buf.len * (PRInt64) info->repetitions;
        
        info = info->next;
    }
    info = infoList;

    fprintf(stdout, "#%9s", "mode");
    fprintf(stdout, "%12s", "in");
print_td:
    switch (info->mode) {
      case bltestDES_ECB:
      case bltestDES_CBC:
      case bltestDES_EDE_ECB:
      case bltestDES_EDE_CBC:
      case bltestAES_ECB:
      case bltestAES_CBC:
      case bltestAES_CTS:
      case bltestAES_CTR:
      case bltestAES_GCM:
      case bltestCAMELLIA_ECB:
      case bltestCAMELLIA_CBC:
      case bltestSEED_ECB:
      case bltestSEED_CBC:
      case bltestRC2_ECB:
      case bltestRC2_CBC:
      case bltestRC4:
          if (td)
              fprintf(stdout, "%8s", "symmkey");
          else
              fprintf(stdout, "%8d", 8*info->params.sk.key.buf.len);
          break;
#ifdef NSS_SOFTOKEN_DOES_RC5
      case bltestRC5_ECB:
      case bltestRC5_CBC:
          if (info->params.sk.key.buf.len > 0)
              printf("symmetric key(bytes)=%d,", info->params.sk.key.buf.len);
          if (info->rounds > 0)
              printf("rounds=%d,", info->params.rc5.rounds);
          if (info->wordsize > 0)
              printf("wordsize(bytes)=%d,", info->params.rc5.wordsize);
          break;
#endif
      case bltestRSA:
      case bltestRSA_PSS:
      case bltestRSA_OAEP:
          if (td) {
              fprintf(stdout, "%8s", "rsa_mod");
              fprintf(stdout, "%12s", "rsa_pe");
          } else {
              bltestAsymKeyParams *asymk = &info->params.asymk;
              fprintf(stdout, "%8d", asymk->cipherParams.rsa.keysizeInBits);
              print_exponent(
                  &((RSAPrivateKey *)asymk->privKey)->publicExponent);
          }
          break;
      case bltestDSA:
          if (td) {
              fprintf(stdout, "%8s", "pqg_mod");
          } else {
              fprintf(stdout, "%8d", info->params.asymk.cipherParams.dsa.keysize);
          }
          break;
#ifndef NSS_DISABLE_ECC
      case bltestECDSA:
          if (td) {
              fprintf(stdout, "%12s", "ec_curve");
          } else {
              ECPrivateKey *key = (ECPrivateKey*)info->params.asymk.privKey;
              ECCurveName curveName = key->ecParams.name;
              fprintf(stdout, "%12s",
                      ecCurve_map[curveName]? ecCurve_map[curveName]->text:
                      "Unsupported curve");
          }
          break;
#endif
      case bltestMD2:
      case bltestMD5:
      case bltestSHA1:
      case bltestSHA256:
      case bltestSHA384:
      case bltestSHA512:
      default:
          break;
    }
    if (!td) {
        PRInt64 totalThroughPut;

        printPR_smpString("%8s", getHighUnitOps(repetitions),
                          "%8d", repetitions);

        printPR_smpString("%8s", getHighUnitOps(cxreps), "%8d", cxreps);

        fprintf(stdout, "%12.3f", cxtime);
        fprintf(stdout, "%12.3f", optime);
        fprintf(stdout, "%12.03f", totalTimeInt / 1000);

        totalThroughPut = (PRInt64)(totalIn / totalTimeInt * 1000);
        printPR_smpString("%12s", getHighUnitBytes(totalThroughPut),
                          "%12d", totalThroughPut);

        fprintf(stdout, "\n");
        return;
    }
    
    fprintf(stdout, "%8s", "opreps");
    fprintf(stdout, "%8s", "cxreps");
    fprintf(stdout, "%12s", "context");
    fprintf(stdout, "%12s", "op");
    fprintf(stdout, "%12s", "time(sec)");
    fprintf(stdout, "%12s", "thrgput");
    fprintf(stdout, "\n");
    fprintf(stdout, "%8s", mode_strings[info->mode]);
    fprintf(stdout, "_%c", (cxonly) ? 'c' : (encrypt) ? 'e' : 'd');
    printPR_smpString("%12s", getHighUnitBytes(totalIn), "%12d", totalIn);
    
    td = !td;
    goto print_td;
}

void
printmodes()
{
    bltestCipherMode mode;
    int nummodes = sizeof(mode_strings) / sizeof(char *);
    fprintf(stderr, "%s: Available modes (specify with -m):\n", progName);
    for (mode=0; mode<nummodes; mode++)
	fprintf(stderr, "%s\n", mode_strings[mode]);
}

bltestCipherMode
get_mode(const char *modestring)
{
    bltestCipherMode mode;
    int nummodes = sizeof(mode_strings) / sizeof(char *);
    for (mode=0; mode<nummodes; mode++)
	if (PL_strcmp(modestring, mode_strings[mode]) == 0)
	    return mode;
    fprintf(stderr, "%s: invalid mode: %s\n", progName, modestring);
    return bltestINVALID;
}

void
load_file_data(PLArenaPool *arena, bltestIO *data,
	       char *fn, bltestIOMode ioMode)
{
    PRFileDesc *file;
    data->mode = ioMode;
    data->file = NULL; /* don't use -- not saving anything */
    data->pBuf.data = NULL;
    data->pBuf.len = 0;
    file = PR_Open(fn, PR_RDONLY, 00660);
    if (file) {
	setupIO(arena, data, file, NULL, 0);
	PR_Close(file);
    }
}

HASH_HashType
mode_str_to_hash_alg(const SECItem *modeStr)
{
    bltestCipherMode mode;
    char* tempModeStr = NULL;
    if (!modeStr || modeStr->len == 0)
        return HASH_AlgNULL;
    tempModeStr = PORT_Alloc(modeStr->len + 1);
    if (!tempModeStr)
        return HASH_AlgNULL;
    memcpy(tempModeStr, modeStr->data, modeStr->len);
    tempModeStr[modeStr->len] = '\0';
    mode = get_mode(tempModeStr);
    PORT_Free(tempModeStr);
    switch (mode) {
        case bltestMD2:    return HASH_AlgMD2;
        case bltestMD5:    return HASH_AlgMD5;
        case bltestSHA1:   return HASH_AlgSHA1;
        case bltestSHA224: return HASH_AlgSHA224;
        case bltestSHA256: return HASH_AlgSHA256;
        case bltestSHA384: return HASH_AlgSHA384;
        case bltestSHA512: return HASH_AlgSHA512;
        default: return HASH_AlgNULL;
    }
}

void
get_params(PLArenaPool *arena, bltestParams *params,
	   bltestCipherMode mode, int j)
{
    char filename[256];
    char *modestr = mode_strings[mode];
    bltestIO tempIO;

#ifdef NSS_SOFTOKEN_DOES_RC5
    FILE *file;
    char *mark, *param, *val;
    int index = 0;
#endif
    switch (mode) {
    case bltestAES_GCM:
    case bltestCHACHA20:
	sprintf(filename, "%s/tests/%s/%s%d", testdir, modestr, "aad", j);
	load_file_data(arena, &params->ask.aad, filename, bltestBinary);
    case bltestDES_CBC:
    case bltestDES_EDE_CBC:
    case bltestRC2_CBC:
    case bltestAES_CBC:
    case bltestAES_CTS:
    case bltestAES_CTR:
    case bltestCAMELLIA_CBC:
    case bltestSEED_CBC: 
	sprintf(filename, "%s/tests/%s/%s%d", testdir, modestr, "iv", j);
	load_file_data(arena, &params->sk.iv, filename, bltestBinary);
    case bltestDES_ECB:
    case bltestDES_EDE_ECB:
    case bltestRC2_ECB:
    case bltestRC4:
    case bltestAES_ECB:
    case bltestCAMELLIA_ECB:
    case bltestSEED_ECB:
	sprintf(filename, "%s/tests/%s/%s%d", testdir, modestr, "key", j);
	load_file_data(arena, &params->sk.key, filename, bltestBinary);
	break;
#ifdef NSS_SOFTOKEN_DOES_RC5
    case bltestRC5_ECB:
    case bltestRC5_CBC:
	sprintf(filename, "%s/tests/%s/%s%d", testdir, modestr, "iv", j);
	load_file_data(arena, &params->sk.iv, filename, bltestBinary);
	sprintf(filename, "%s/tests/%s/%s%d", testdir, modestr, "key", j);
	load_file_data(arena, &params->sk.key, filename, bltestBinary);
	    sprintf(filename, "%s/tests/%s/%s%d", testdir, modestr,
			      "params", j);
	file = fopen(filename, "r");
	if (!file) return;
	param = malloc(100);
	len = fread(param, 1, 100, file);
	while (index < len) {
	    mark = PL_strchr(param, '=');
	    *mark = '\0';
	    val = mark + 1;
	    mark = PL_strchr(val, '\n');
	    *mark = '\0';
	    if (PL_strcmp(param, "rounds") == 0) {
		params->rc5.rounds = atoi(val);
	    } else if (PL_strcmp(param, "wordsize") == 0) {
		params->rc5.wordsize = atoi(val);
	    }
	    index += PL_strlen(param) + PL_strlen(val) + 2;
	    param = mark + 1;
	}
	break;
#endif
    case bltestRSA_PSS:
	sprintf(filename, "%s/tests/%s/%s%d", testdir, modestr, "ciphertext", j);
	load_file_data(arena, &params->asymk.sig, filename, bltestBase64Encoded);
	/* fall through */
    case bltestRSA_OAEP:
	sprintf(filename, "%s/tests/%s/%s%d", testdir, modestr, "seed", j);
	load_file_data(arena, &params->asymk.cipherParams.rsa.seed,
	               filename, bltestBase64Encoded);

	sprintf(filename, "%s/tests/%s/%s%d", testdir, modestr, "hash", j);
	load_file_data(arena, &tempIO, filename, bltestBinary);
	params->asymk.cipherParams.rsa.hashAlg =
	    mode_str_to_hash_alg(&tempIO.buf);

	sprintf(filename, "%s/tests/%s/%s%d", testdir, modestr, "maskhash", j);
	load_file_data(arena, &tempIO, filename, bltestBinary);
	params->asymk.cipherParams.rsa.maskHashAlg =
	    mode_str_to_hash_alg(&tempIO.buf);
	/* fall through */
    case bltestRSA:
	sprintf(filename, "%s/tests/%s/%s%d", testdir, modestr, "key", j);
	load_file_data(arena, &params->asymk.key, filename,
	               bltestBase64Encoded);
	params->asymk.privKey =
	    (void *)rsakey_from_filedata(&params->asymk.key.buf);
	break;
    case bltestDSA:
	sprintf(filename, "%s/tests/%s/%s%d", testdir, modestr, "key", j);
	load_file_data(arena, &params->asymk.key, filename, bltestBase64Encoded);
	params->asymk.privKey =
	     (void *)dsakey_from_filedata(&params->asymk.key.buf);
	sprintf(filename, "%s/tests/%s/%s%d", testdir, modestr, "pqg", j);
	load_file_data(arena, &params->asymk.cipherParams.dsa.pqgdata, filename,
	               bltestBase64Encoded);
	params->asymk.cipherParams.dsa.pqg =
	      pqg_from_filedata(&params->asymk.cipherParams.dsa.pqgdata.buf);
	sprintf(filename, "%s/tests/%s/%s%d", testdir, modestr, "keyseed", j);
	load_file_data(arena, &params->asymk.cipherParams.dsa.keyseed, filename,
	               bltestBase64Encoded);
	sprintf(filename, "%s/tests/%s/%s%d", testdir, modestr, "sigseed", j);
	load_file_data(arena, &params->asymk.cipherParams.dsa.sigseed, filename,
	               bltestBase64Encoded);
	sprintf(filename, "%s/tests/%s/%s%d", testdir, modestr, "ciphertext",j);
	load_file_data(arena, &params->asymk.sig, filename, bltestBase64Encoded);
	break;
#ifndef NSS_DISABLE_ECC
    case bltestECDSA:
	sprintf(filename, "%s/tests/%s/%s%d", testdir, modestr, "key", j);
	load_file_data(arena, &params->asymk.key, filename, bltestBase64Encoded);
	params->asymk.privKey =
	      (void *)eckey_from_filedata(&params->asymk.key.buf);
	sprintf(filename, "%s/tests/%s/%s%d", testdir, modestr, "sigseed", j);
	load_file_data(arena, &params->asymk.cipherParams.ecdsa.sigseed,
	               filename, bltestBase64Encoded);
	sprintf(filename, "%s/tests/%s/%s%d", testdir, modestr, "ciphertext",j);
	load_file_data(arena, &params->asymk.sig, filename, bltestBase64Encoded);
	break;
#endif
    case bltestMD2:
    case bltestMD5:
    case bltestSHA1:
    case bltestSHA224:
    case bltestSHA256:
    case bltestSHA384:
    case bltestSHA512:
	/*params->hash.restart = PR_TRUE;*/
	params->hash.restart = PR_FALSE;
	break;
    default:
	break;
    }
}

SECStatus
verify_self_test(bltestIO *result, bltestIO *cmp, bltestCipherMode mode,
		 PRBool forward, SECStatus sigstatus)
{
    PRBool equal;
    char *modestr = mode_strings[mode];
    equal = SECITEM_ItemsAreEqual(&result->pBuf, &cmp->buf);
    if (is_sigCipher(mode)) {
	if (forward) {
	    if (equal) {
		printf("Signature self-test for %s passed.\n", modestr);
	    } else {
		printf("Signature self-test for %s failed!\n", modestr);
	    }
	    return equal ? SECSuccess : SECFailure;
	} else {
	    if (sigstatus == SECSuccess) {
		printf("Verification self-test for %s passed.\n", modestr);
	    } else {
		printf("Verification self-test for %s failed!\n", modestr);
	    }
	    return sigstatus;
	}
    } else if (is_hashCipher(mode)) {
	if (equal) {
	    printf("Hash self-test for %s passed.\n", modestr);
	} else {
	    printf("Hash self-test for %s failed!\n", modestr);
	}
    } else {
	if (forward) {
	    if (equal) {
		printf("Encryption self-test for %s passed.\n", modestr);
	    } else {
		printf("Encryption self-test for %s failed!\n", modestr);
	    }
	} else {
	    if (equal) {
		printf("Decryption self-test for %s passed.\n", modestr);
	    } else {
		printf("Decryption self-test for %s failed!\n", modestr);
	    }
	}
    }
    return equal ? SECSuccess : SECFailure;
}

static SECStatus
ReadFileToItem(SECItem *dst, const char *filename)
{
    PRFileDesc *file;
    SECStatus rv;

    file = PR_Open(filename, PR_RDONLY, 00660);
    if (!file) {
	return SECFailure;
    }
    rv = SECU_FileToItem(dst, file);
    PR_Close(file);
    return rv;
}

static SECStatus
blapi_selftest(bltestCipherMode *modes, int numModes, int inoff, int outoff,
               PRBool encrypt, PRBool decrypt)
{
    bltestCipherInfo cipherInfo;
    bltestIO pt, ct;
    bltestCipherMode mode;
    bltestParams *params;
    unsigned int i, j, nummodes, numtests;
    char *modestr;
    char filename[256];
    PLArenaPool *arena;
    SECItem item;
    SECStatus rv = SECSuccess, srv;

    PORT_Memset(&cipherInfo, 0, sizeof(cipherInfo));
    arena = PORT_NewArena(BLTEST_DEFAULT_CHUNKSIZE);
    cipherInfo.arena = arena;

    nummodes = (numModes == 0) ? NUMMODES : numModes;
    for (i=0; i < nummodes; i++) {
	if (numModes > 0)
	    mode = modes[i];
	else
	    mode = i;
	if (mode == bltestINVALID) {
	    fprintf(stderr, "%s: Skipping invalid mode.\n",progName);
	    continue;
	}
	modestr = mode_strings[mode];
	cipherInfo.mode = mode;
	params = &cipherInfo.params;
	/* get the number of tests in the directory */
	sprintf(filename, "%s/tests/%s/%s", testdir, modestr, "numtests");
	if (ReadFileToItem(&item, filename) != SECSuccess) {
	    fprintf(stderr, "%s: Cannot read file %s.\n", progName, filename);
	    rv = SECFailure;
	    continue;
	}
	/* loop over the tests in the directory */
	numtests = 0;
	for (j=0; j<item.len; j++) {
	    if (!isdigit(item.data[j])) {
		break;
	    }
	    numtests *= 10;
	    numtests += (int) (item.data[j] - '0');
	}
	for (j=0; j<numtests; j++) {
	    sprintf(filename, "%s/tests/%s/%s%d", testdir, modestr,
	            "plaintext", j);
	    load_file_data(arena, &pt, filename,
	                   is_sigCipher(mode) ? bltestBase64Encoded
	                                      : bltestBinary);
	    sprintf(filename, "%s/tests/%s/%s%d", testdir, modestr,
	            "ciphertext", j);
	    load_file_data(arena, &ct, filename, bltestBase64Encoded);

	    get_params(arena, params, mode, j);
	    /* Forward Operation (Encrypt/Sign/Hash)
	    ** Align the input buffer (plaintext) according to request
	    ** then perform operation and compare to ciphertext
	    */
	    if (encrypt) {
		bltestCopyIO(arena, &cipherInfo.input, &pt);
		misalignBuffer(arena, &cipherInfo.input, inoff);
		memset(&cipherInfo.output.buf, 0, sizeof cipherInfo.output.buf);
		rv |= cipherInit(&cipherInfo, PR_TRUE);
		misalignBuffer(arena, &cipherInfo.output, outoff);
		rv |= cipherDoOp(&cipherInfo);
		rv |= cipherFinish(&cipherInfo);
		rv |= verify_self_test(&cipherInfo.output, 
		                       &ct, mode, PR_TRUE, SECSuccess);
		/* If testing hash, only one op to test */
		if (is_hashCipher(mode))
		    continue;
		if (is_sigCipher(mode)) {
		    /* Verify operations support detached signature files. For
		    ** consistency between tests that run Sign/Verify back to
		    ** back (eg: self-tests) and tests that are only running
		    ** verify operations, copy the output into the sig buf,
		    ** and then copy the sig buf back out when verifying. For
		    ** self-tests, this is unnecessary copying, but for
		    ** verify-only operations, this ensures that the output
		    ** buffer is properly configured
		    */
		    bltestCopyIO(arena, &params->asymk.sig, &cipherInfo.output);
		}
	    }
	    if (!decrypt)
		continue;
	    /* Reverse Operation (Decrypt/Verify)
	    ** Align the input buffer (ciphertext) according to request
	    ** then perform operation and compare to plaintext
	    */
	    if (is_sigCipher(mode)) {
		bltestCopyIO(arena, &cipherInfo.input, &pt);
		bltestCopyIO(arena, &cipherInfo.output, &params->asymk.sig);
	    } else {
		bltestCopyIO(arena, &cipherInfo.input, &ct);
		memset(&cipherInfo.output.buf, 0, sizeof cipherInfo.output.buf);
	    }
	    misalignBuffer(arena, &cipherInfo.input, inoff);
	    rv |= cipherInit(&cipherInfo, PR_FALSE);
	    misalignBuffer(arena, &cipherInfo.output, outoff);
	    srv = SECSuccess;
	    srv |= cipherDoOp(&cipherInfo);
	    rv |= cipherFinish(&cipherInfo);
	    rv |= verify_self_test(&cipherInfo.output, 
	                           &pt, mode, PR_FALSE, srv);
	}
    }
    return rv;
}

SECStatus
dump_file(bltestCipherMode mode, char *filename)
{
    bltestIO keydata;
    PLArenaPool *arena = NULL;
    arena = PORT_NewArena(BLTEST_DEFAULT_CHUNKSIZE);
    if (mode == bltestRSA || mode == bltestRSA_PSS || mode == bltestRSA_OAEP) {
	RSAPrivateKey *key;
	load_file_data(arena, &keydata, filename, bltestBase64Encoded);
	key = rsakey_from_filedata(&keydata.buf);
	dump_rsakey(key);
    } else if (mode == bltestDSA) {
#if 0
	PQGParams *pqg;
	get_file_data(filename, &item, PR_TRUE);
	pqg = pqg_from_filedata(&item);
	dump_pqg(pqg);
#endif
	DSAPrivateKey *key;
	load_file_data(arena, &keydata, filename, bltestBase64Encoded);
	key = dsakey_from_filedata(&keydata.buf);
	dump_dsakey(key);
#ifndef NSS_DISABLE_ECC
    } else if (mode == bltestECDSA) {
	ECPrivateKey *key;
	load_file_data(arena, &keydata, filename, bltestBase64Encoded);
	key = eckey_from_filedata(&keydata.buf);
	dump_eckey(key);
#endif
    }
    PORT_FreeArena(arena, PR_FALSE);
    return SECFailure;
}

void ThreadExecTest(void *data)
{
    bltestCipherInfo *cipherInfo = (bltestCipherInfo*)data;

    if (cipherInfo->mCarlo == PR_TRUE) {
        int mciter;
        for (mciter=0; mciter<10000; mciter++) {
            cipherDoOp(cipherInfo);
            memcpy(cipherInfo->input.buf.data,
                   cipherInfo->output.buf.data,
                   cipherInfo->input.buf.len);
        }
    } else {
        cipherDoOp(cipherInfo);
    }
    cipherFinish(cipherInfo);
}

static void rsaPrivKeyReset(RSAPrivateKey *tstKey)
{
    PLArenaPool *arena;

    tstKey->version.data = NULL;
    tstKey->version.len = 0;
    tstKey->modulus.data = NULL;
    tstKey->modulus.len = 0;
    tstKey->publicExponent.data = NULL;
    tstKey->publicExponent.len = 0;
    tstKey->privateExponent.data = NULL;
    tstKey->privateExponent.len = 0;
    tstKey->prime1.data = NULL;
    tstKey->prime1.len = 0;
    tstKey->prime2.data = NULL;
    tstKey->prime2.len = 0;
    tstKey->exponent1.data = NULL;
    tstKey->exponent1.len = 0;
    tstKey->exponent2.data = NULL;
    tstKey->exponent2.len = 0;
    tstKey->coefficient.data = NULL;
    tstKey->coefficient.len = 0;

    arena = tstKey->arena;
    tstKey->arena = NULL;
    if (arena) {
	PORT_FreeArena(arena, PR_TRUE);
    }
}


#define RSA_TEST_EQUAL(comp) \
    if (!SECITEM_ItemsAreEqual(&(src->comp),&(dest->comp))) { \
	fprintf(stderr, "key->" #comp " not equal"); \
	if (src->comp.len != dest->comp.len) { \
	    fprintf(stderr, "src_len = %d, dest_len = %d",  \
					src->comp.len, dest->comp.len); \
	} \
	fprintf(stderr, "\n"); \
	areEqual = PR_FALSE; \
    }
	    

static PRBool rsaPrivKeysAreEqual(RSAPrivateKey *src, RSAPrivateKey *dest)
{
    PRBool areEqual = PR_TRUE;
    RSA_TEST_EQUAL(modulus)
    RSA_TEST_EQUAL(publicExponent)
    RSA_TEST_EQUAL(privateExponent)
    RSA_TEST_EQUAL(prime1)
    RSA_TEST_EQUAL(prime2)
    RSA_TEST_EQUAL(exponent1)
    RSA_TEST_EQUAL(exponent2)
    RSA_TEST_EQUAL(coefficient)
    if (!areEqual) {
	fprintf(stderr, "original key:\n");
	dump_rsakey(src);
	fprintf(stderr, "recreated key:\n");
	dump_rsakey(dest);
    }
    return areEqual;
}

/*
 * Test the RSA populate command to see that it can really build
 * keys from it's components.
 */
static int doRSAPopulateTest(unsigned int keySize, unsigned long exponent)
{
    RSAPrivateKey *srcKey;
    RSAPrivateKey tstKey = { 0 };
    SECItem expitem = { 0, 0, 0 };
    SECStatus rv;
    unsigned char pubExp[4];
    int expLen = 0;
    int failed = 0;
    int i;

    for (i=0; i < sizeof(unsigned long); i++) {
	int shift = (sizeof(unsigned long) - i -1 ) * 8;
	if (expLen || (exponent && ((unsigned long)0xffL << shift))) {
	    pubExp[expLen] = (unsigned char) ((exponent >> shift) & 0xff);
	    expLen++;
        }
    }

    expitem.data = pubExp;
    expitem.len = expLen;

    srcKey = RSA_NewKey(keySize, &expitem);
    if (srcKey == NULL) {
	fprintf(stderr, "RSA Key Gen failed");
	return -1;
    }

    /* test the basic case - most common, public exponent, modulus, prime */
    tstKey.arena = NULL;
    rsaPrivKeyReset(&tstKey);

    tstKey.publicExponent = srcKey->publicExponent;
    tstKey.modulus = srcKey->modulus;
    tstKey.prime1 = srcKey->prime1;

    rv = RSA_PopulatePrivateKey(&tstKey);
    if (rv != SECSuccess) {
	fprintf(stderr, "RSA Populate failed: pubExp mod p\n");
	failed = 1;
    } else if (!rsaPrivKeysAreEqual(&tstKey, srcKey)) {
	fprintf(stderr, "RSA Populate key mismatch: pubExp mod p\n");
	failed = 1;
    }

    /* test the basic2 case, public exponent, modulus, prime2 */
    rsaPrivKeyReset(&tstKey);

    tstKey.publicExponent = srcKey->publicExponent;
    tstKey.modulus = srcKey->modulus;
    tstKey.prime1 = srcKey->prime2; /* test with q in the prime1 position */

    rv = RSA_PopulatePrivateKey(&tstKey);
    if (rv != SECSuccess) {
	fprintf(stderr, "RSA Populate failed: pubExp mod q\n");
	failed = 1;
    } else if (!rsaPrivKeysAreEqual(&tstKey, srcKey)) {
	fprintf(stderr, "RSA Populate key mismatch: pubExp mod q\n");
	failed = 1;
    }

    /* test the medium case, private exponent, prime1, prime2 */
    rsaPrivKeyReset(&tstKey);

    tstKey.privateExponent = srcKey->privateExponent;
    tstKey.prime1 = srcKey->prime2; /* purposefully swap them to make */
    tstKey.prime2 = srcKey->prime1; /* sure populated swaps them back */

    rv = RSA_PopulatePrivateKey(&tstKey);
    if (rv != SECSuccess) {
	fprintf(stderr, "RSA Populate failed: privExp p q\n");
	failed = 1;
    } else if (!rsaPrivKeysAreEqual(&tstKey, srcKey)) {
	fprintf(stderr, "RSA Populate key mismatch: privExp  p q\n");
	failed = 1;
    }

    /* test the advanced case, public exponent, private exponent, prime2 */
    rsaPrivKeyReset(&tstKey);

    tstKey.privateExponent = srcKey->privateExponent;
    tstKey.publicExponent = srcKey->publicExponent;
    tstKey.prime2 = srcKey->prime2; /* use q in the prime2 position */

    rv = RSA_PopulatePrivateKey(&tstKey);
    if (rv != SECSuccess) {
	fprintf(stderr, "RSA Populate failed: pubExp privExp q\n");
	fprintf(stderr, " - not fatal\n");
	/* it's possible that we can't uniquely determine the original key
	 * from just the exponents and prime. Populate returns an error rather
	 * than return the wrong key. */
    } else if (!rsaPrivKeysAreEqual(&tstKey, srcKey)) {
	/* if we returned a key, it *must* be correct */
	fprintf(stderr, "RSA Populate key mismatch: pubExp privExp  q\n");
	rv = RSA_PrivateKeyCheck(&tstKey);
	failed = 1;
    }

    /* test the advanced case2, public exponent, private exponent, modulus */
    rsaPrivKeyReset(&tstKey);

    tstKey.privateExponent = srcKey->privateExponent;
    tstKey.publicExponent = srcKey->publicExponent;
    tstKey.modulus = srcKey->modulus;

    rv = RSA_PopulatePrivateKey(&tstKey);
    if (rv != SECSuccess) {
	fprintf(stderr, "RSA Populate failed: pubExp privExp mod\n");
	failed = 1;
    } else if (!rsaPrivKeysAreEqual(&tstKey, srcKey)) {
	fprintf(stderr, "RSA Populate key mismatch: pubExp privExp  mod\n");
	failed = 1;
    }

    return failed ? -1 : 0;
}



/* bltest commands */
enum {
    cmd_Decrypt = 0,
    cmd_Encrypt,
    cmd_FIPS,
    cmd_Hash,
    cmd_Nonce,
    cmd_Dump,
    cmd_RSAPopulate,
    cmd_Sign,
    cmd_SelfTest,
    cmd_Verify
};

/* bltest options */
enum {
    opt_B64 = 0,
    opt_BufSize,
    opt_Restart,
    opt_SelfTestDir,
    opt_Exponent,
    opt_SigFile,
    opt_KeySize,
    opt_Hex,
    opt_Input,
    opt_PQGFile,
    opt_Key,
    opt_HexWSpc,
    opt_Mode,
#ifndef NSS_DISABLE_ECC
    opt_CurveName,
#endif
    opt_Output,
    opt_Repetitions,
    opt_ZeroBuf,
    opt_Rounds,
    opt_Seed,
    opt_SigSeedFile,
    opt_CXReps,
    opt_IV,
    opt_WordSize,
    opt_UseSeed,
    opt_UseSigSeed,
    opt_SeedFile,
    opt_AAD,
    opt_InputOffset,
    opt_OutputOffset,
    opt_MonteCarlo,
    opt_ThreadNum,
    opt_SecondsToRun,
    opt_CmdLine
};

static secuCommandFlag bltest_commands[] =
{
    { /* cmd_Decrypt	*/ 'D', PR_FALSE, 0, PR_FALSE },
    { /* cmd_Encrypt	*/ 'E', PR_FALSE, 0, PR_FALSE },
    { /* cmd_FIPS	*/ 'F', PR_FALSE, 0, PR_FALSE },
    { /* cmd_Hash	*/ 'H', PR_FALSE, 0, PR_FALSE },
    { /* cmd_Nonce      */ 'N', PR_FALSE, 0, PR_FALSE },
    { /* cmd_Dump	*/ 'P', PR_FALSE, 0, PR_FALSE },
    { /* cmd_RSAPopulate*/ 'R', PR_FALSE, 0, PR_FALSE },
    { /* cmd_Sign	*/ 'S', PR_FALSE, 0, PR_FALSE },
    { /* cmd_SelfTest	*/ 'T', PR_FALSE, 0, PR_FALSE },
    { /* cmd_Verify	*/ 'V', PR_FALSE, 0, PR_FALSE }
};

static secuCommandFlag bltest_options[] =
{
    { /* opt_B64	  */ 'a', PR_FALSE, 0, PR_FALSE },
    { /* opt_BufSize	  */ 'b', PR_TRUE,  0, PR_FALSE },
    { /* opt_Restart	  */ 'c', PR_FALSE, 0, PR_FALSE },
    { /* opt_SelfTestDir  */ 'd', PR_TRUE,  0, PR_FALSE },
    { /* opt_Exponent	  */ 'e', PR_TRUE,  0, PR_FALSE },
    { /* opt_SigFile      */ 'f', PR_TRUE,  0, PR_FALSE },
    { /* opt_KeySize	  */ 'g', PR_TRUE,  0, PR_FALSE },
    { /* opt_Hex	  */ 'h', PR_FALSE, 0, PR_FALSE },
    { /* opt_Input	  */ 'i', PR_TRUE,  0, PR_FALSE },
    { /* opt_PQGFile	  */ 'j', PR_TRUE,  0, PR_FALSE },
    { /* opt_Key	  */ 'k', PR_TRUE,  0, PR_FALSE },
    { /* opt_HexWSpc	  */ 'l', PR_FALSE, 0, PR_FALSE },
    { /* opt_Mode	  */ 'm', PR_TRUE,  0, PR_FALSE },
#ifndef NSS_DISABLE_ECC
    { /* opt_CurveName	  */ 'n', PR_TRUE,  0, PR_FALSE },
#endif
    { /* opt_Output	  */ 'o', PR_TRUE,  0, PR_FALSE },
    { /* opt_Repetitions  */ 'p', PR_TRUE,  0, PR_FALSE },
    { /* opt_ZeroBuf	  */ 'q', PR_FALSE, 0, PR_FALSE },
    { /* opt_Rounds	  */ 'r', PR_TRUE,  0, PR_FALSE },
    { /* opt_Seed	  */ 's', PR_TRUE,  0, PR_FALSE },
    { /* opt_SigSeedFile  */ 't', PR_TRUE,  0, PR_FALSE },
    { /* opt_CXReps       */ 'u', PR_TRUE,  0, PR_FALSE },
    { /* opt_IV		  */ 'v', PR_TRUE,  0, PR_FALSE },
    { /* opt_WordSize	  */ 'w', PR_TRUE,  0, PR_FALSE },
    { /* opt_UseSeed	  */ 'x', PR_FALSE, 0, PR_FALSE },
    { /* opt_UseSigSeed	  */ 'y', PR_FALSE, 0, PR_FALSE },
    { /* opt_SeedFile	  */ 'z', PR_FALSE, 0, PR_FALSE },
    { /* opt_AAD	  */  0 , PR_TRUE,  0, PR_FALSE, "aad" },
    { /* opt_InputOffset  */ '1', PR_TRUE,  0, PR_FALSE },
    { /* opt_OutputOffset */ '2', PR_TRUE,  0, PR_FALSE },
    { /* opt_MonteCarlo   */ '3', PR_FALSE, 0, PR_FALSE },
    { /* opt_ThreadNum    */ '4', PR_TRUE,  0, PR_FALSE },
    { /* opt_SecondsToRun */ '5', PR_TRUE,  0, PR_FALSE },
    { /* opt_CmdLine	  */ '-', PR_FALSE, 0, PR_FALSE }
};

int main(int argc, char **argv)
{
    SECStatus rv = SECFailure;

    double              totalTime = 0.0;
    PRIntervalTime      time1, time2;
    PRFileDesc          *outfile = NULL;
    bltestCipherInfo    *cipherInfoListHead, *cipherInfo = NULL;
    bltestIOMode        ioMode;
    int                 bufsize, exponent, curThrdNum;
#ifndef NSS_DISABLE_ECC
    char		*curveName = NULL;
#endif
    int			 i, commandsEntered;
    int			 inoff, outoff;
    int                  threads = 1;

    secuCommand bltest;
    bltest.numCommands = sizeof(bltest_commands) / sizeof(secuCommandFlag);
    bltest.numOptions = sizeof(bltest_options) / sizeof(secuCommandFlag);
    bltest.commands = bltest_commands;
    bltest.options = bltest_options;

    progName = strrchr(argv[0], '/');
    if (!progName) 
	progName = strrchr(argv[0], '\\');
    progName = progName ? progName+1 : argv[0];

    rv = NSS_InitializePRErrorTable();
    if (rv != SECSuccess) {
	SECU_PrintPRandOSError(progName);
	return -1;
    }
    rv = RNG_RNGInit();
    if (rv != SECSuccess) {
	SECU_PrintPRandOSError(progName);
	return -1;
    }
    rv = BL_Init();
    if (rv != SECSuccess) {
	SECU_PrintPRandOSError(progName);
	return -1;
    }
    RNG_SystemInfoForRNG();


    rv = SECU_ParseCommandLine(argc, argv, progName, &bltest);
    if (rv == SECFailure) {
	fprintf(stderr, "%s: command line parsing error!\n", progName);
	goto print_usage;
    }
    rv = SECFailure;

    cipherInfo = PORT_ZNew(bltestCipherInfo);
    cipherInfoListHead = cipherInfo;

    /* Check the number of commands entered on the command line. */
    commandsEntered = 0;
    for (i=0; i<bltest.numCommands; i++)
	if (bltest.commands[i].activated)
	    commandsEntered++;

    if (commandsEntered > 1 &&
	!(commandsEntered == 2 && bltest.commands[cmd_SelfTest].activated)) {
	fprintf(stderr, "%s: one command at a time!\n", progName);
        goto print_usage;
    }

    if (commandsEntered == 0) {
	fprintf(stderr, "%s: you must enter a command!\n", progName);
        goto print_usage;
    }


    if (bltest.commands[cmd_Sign].activated)
	bltest.commands[cmd_Encrypt].activated = PR_TRUE;
    if (bltest.commands[cmd_Verify].activated)
	bltest.commands[cmd_Decrypt].activated = PR_TRUE;
    if (bltest.commands[cmd_Hash].activated)
	bltest.commands[cmd_Encrypt].activated = PR_TRUE;

    inoff = outoff = 0;
    if (bltest.options[opt_InputOffset].activated)
	inoff = PORT_Atoi(bltest.options[opt_InputOffset].arg);
    if (bltest.options[opt_OutputOffset].activated)
	outoff = PORT_Atoi(bltest.options[opt_OutputOffset].arg);

    testdir = (bltest.options[opt_SelfTestDir].activated) ? 
                 strdup(bltest.options[opt_SelfTestDir].arg) : ".";

    /*
     * Handle three simple cases first
     */

    /* test the RSA_PopulatePrivateKey function */
    if (bltest.commands[cmd_RSAPopulate].activated) {
	unsigned int keySize = 1024;
	unsigned long exponent = 65537;
	int rounds = 1;
	int ret = -1;
	
	if (bltest.options[opt_KeySize].activated) {
	    keySize = PORT_Atoi(bltest.options[opt_KeySize].arg);
	}
	if (bltest.options[opt_Rounds].activated) {
	    rounds = PORT_Atoi(bltest.options[opt_Rounds].arg);
	}
	if (bltest.options[opt_Exponent].activated) {
	    exponent = PORT_Atoi(bltest.options[opt_Exponent].arg);
	}

	for (i=0; i < rounds; i++) {
	    printf("Running RSA Populate test round %d\n",i);
	    ret = doRSAPopulateTest(keySize,exponent);
	    if (ret != 0) {
		break;
	    }
	}
	if (ret != 0) {
	    fprintf(stderr,"RSA Populate test round %d: FAILED\n",i);
	}
	return ret;
    }

    /* Do BLAPI self-test */
    if (bltest.commands[cmd_SelfTest].activated) {
	PRBool encrypt = PR_TRUE, decrypt = PR_TRUE;
	/* user may specified a set of ciphers to test.  parse them. */
	bltestCipherMode modesToTest[NUMMODES];
	int numModesToTest = 0;
	char *tok, *str;
	str = bltest.options[opt_Mode].arg;
	while (str) {
	    tok = strchr(str, ',');
	    if (tok) *tok = '\0';
	    modesToTest[numModesToTest++] = get_mode(str);
	    if (tok) {
		*tok = ',';
		str = tok + 1;
	    } else {
		break;
	    }
	}
	if (bltest.commands[cmd_Decrypt].activated &&
	    !bltest.commands[cmd_Encrypt].activated)
	    encrypt = PR_FALSE;
	if (bltest.commands[cmd_Encrypt].activated &&
	    !bltest.commands[cmd_Decrypt].activated)
	    decrypt = PR_FALSE;
	rv = blapi_selftest(modesToTest, numModesToTest, inoff, outoff,
	                    encrypt, decrypt);
	PORT_Free(cipherInfo);
	return rv == SECSuccess ? 0 : 1;
    }

    /* Do FIPS self-test */
    if (bltest.commands[cmd_FIPS].activated) {
	CK_RV ckrv = sftk_fipsPowerUpSelfTest();
	fprintf(stdout, "CK_RV: %ld.\n", ckrv);
        PORT_Free(cipherInfo);
        if (ckrv == CKR_OK)
            return SECSuccess;
        return SECFailure;
    }

    /*
     * Check command line arguments for Encrypt/Decrypt/Hash/Sign/Verify
     */

    if ((bltest.commands[cmd_Decrypt].activated ||
	 bltest.commands[cmd_Verify].activated) &&
	bltest.options[opt_BufSize].activated) {
	fprintf(stderr, "%s: Cannot use a nonce as input to decrypt/verify.\n",
			 progName);
        goto print_usage;
    }

    if (bltest.options[opt_Mode].activated) {
	cipherInfo->mode = get_mode(bltest.options[opt_Mode].arg);
	if (cipherInfo->mode == bltestINVALID) {
            goto print_usage;
	}
    } else {
	fprintf(stderr, "%s: You must specify a cipher mode with -m.\n",
			 progName);
        goto print_usage;
    }

    
    if (bltest.options[opt_Repetitions].activated &&
        bltest.options[opt_SecondsToRun].activated) {
        fprintf(stderr, "%s: Operation time should be defined in either "
                "repetitions(-p) or seconds(-5) not both",
                progName);
        goto print_usage;
    }

    if (bltest.options[opt_Repetitions].activated) {
        cipherInfo->repetitionsToPerfom =
            PORT_Atoi(bltest.options[opt_Repetitions].arg);
    } else {
        cipherInfo->repetitionsToPerfom = 0;
    }

    if (bltest.options[opt_SecondsToRun].activated) {
        cipherInfo->seconds = PORT_Atoi(bltest.options[opt_SecondsToRun].arg);
    } else {
        cipherInfo->seconds = 0;
    }


    if (bltest.options[opt_CXReps].activated) {
        cipherInfo->cxreps = PORT_Atoi(bltest.options[opt_CXReps].arg);
    } else {
        cipherInfo->cxreps = 0;
    }

    if (bltest.options[opt_ThreadNum].activated) {
        threads = PORT_Atoi(bltest.options[opt_ThreadNum].arg);
        if (threads <= 0) {
            threads = 1;
        }
    }

    /* Dump a file (rsakey, dsakey, etc.) */
    if (bltest.commands[cmd_Dump].activated) {
        rv = dump_file(cipherInfo->mode, bltest.options[opt_Input].arg);
        PORT_Free(cipherInfo);
        return rv;
    }

    /* default input mode is binary */
    ioMode = (bltest.options[opt_B64].activated)     ? bltestBase64Encoded :
	     (bltest.options[opt_Hex].activated)     ? bltestHexStream :
	     (bltest.options[opt_HexWSpc].activated) ? bltestHexSpaceDelim :
						       bltestBinary;

    if (bltest.options[opt_Exponent].activated)
	exponent = PORT_Atoi(bltest.options[opt_Exponent].arg);
    else
	exponent = 65537;

#ifndef NSS_DISABLE_ECC
    if (bltest.options[opt_CurveName].activated)
	curveName = PORT_Strdup(bltest.options[opt_CurveName].arg);
    else
	curveName = NULL;
#endif

    if (bltest.commands[cmd_Verify].activated &&
        !bltest.options[opt_SigFile].activated) {
        fprintf(stderr, "%s: You must specify a signature file with -f.\n",
                progName);

print_usage:
        if (cipherInfo) {
            PORT_Free(cipherInfo);
        }
        Usage();
    }

    if (bltest.options[opt_MonteCarlo].activated) {
        cipherInfo->mCarlo = PR_TRUE;
    } else {
        cipherInfo->mCarlo = PR_FALSE;
    }

    for (curThrdNum = 0;curThrdNum < threads;curThrdNum++) {
        int            keysize = 0;
        PRFileDesc     *file = NULL, *infile;
        bltestParams   *params;
        char           *instr = NULL;
        PLArenaPool    *arena;

        if (curThrdNum > 0) {
            bltestCipherInfo *newCInfo = PORT_ZNew(bltestCipherInfo);
            if (!newCInfo) {
                fprintf(stderr, "%s: Can not allocate  memory.\n", progName);
                goto exit_point;
            }
            newCInfo->mode = cipherInfo->mode;
            newCInfo->mCarlo = cipherInfo->mCarlo;
            newCInfo->repetitionsToPerfom =
                cipherInfo->repetitionsToPerfom;
            newCInfo->seconds = cipherInfo->seconds;
            newCInfo->cxreps = cipherInfo->cxreps;
            cipherInfo->next = newCInfo;
            cipherInfo = newCInfo;
        }
        arena = PORT_NewArena(BLTEST_DEFAULT_CHUNKSIZE);
        if (!arena) {
            fprintf(stderr, "%s: Can not allocate memory.\n", progName);
            goto exit_point;
        }
        cipherInfo->arena = arena;
        params = &cipherInfo->params;
        
        /* Set up an encryption key. */
        keysize = 0;
        file = NULL;
        if (is_symmkeyCipher(cipherInfo->mode) ||
	    is_aeadCipher(cipherInfo->mode)) {
            char *keystr = NULL;  /* if key is on command line */
            if (bltest.options[opt_Key].activated) {
                if (bltest.options[opt_CmdLine].activated) {
                    keystr = bltest.options[opt_Key].arg;
                } else {
                    file = PR_Open(bltest.options[opt_Key].arg,
                                   PR_RDONLY, 00660);
                }
            } else {
                if (bltest.options[opt_KeySize].activated)
                    keysize = PORT_Atoi(bltest.options[opt_KeySize].arg);
                else
                    keysize = 8; /* use 64-bit default (DES) */
                /* save the random key for reference */
                file = PR_Open("tmp.key", PR_WRONLY|PR_CREATE_FILE, 00660);
            }
            params->key.mode = ioMode;
            setupIO(cipherInfo->arena, &params->key, file, keystr, keysize);
            if (file)
                PR_Close(file);
        } else if (is_pubkeyCipher(cipherInfo->mode)) {
            if (bltest.options[opt_Key].activated) {
                file = PR_Open(bltest.options[opt_Key].arg, PR_RDONLY, 00660);
            } else {
                if (bltest.options[opt_KeySize].activated)
                    keysize = PORT_Atoi(bltest.options[opt_KeySize].arg);
                else
                    keysize = 64; /* use 512-bit default */
                file = PR_Open("tmp.key", PR_WRONLY|PR_CREATE_FILE, 00660);
            }
            params->key.mode = bltestBase64Encoded;
#ifndef NSS_DISABLE_ECC
            pubkeyInitKey(cipherInfo, file, keysize, exponent, curveName);
#else
            pubkeyInitKey(cipherInfo, file, keysize, exponent);
#endif
            PR_Close(file);
        }

        /* set up an initialization vector. */
        if (cipher_requires_IV(cipherInfo->mode)) {
            char *ivstr = NULL;
            bltestSymmKeyParams *skp;
            file = NULL;
#ifdef NSS_SOFTOKEN_DOES_RC5
            if (cipherInfo->mode == bltestRC5_CBC)
                skp = (bltestSymmKeyParams *)&params->rc5;
            else
#endif
                skp = &params->sk;
            if (bltest.options[opt_IV].activated) {
                if (bltest.options[opt_CmdLine].activated) {
                    ivstr = bltest.options[opt_IV].arg;
                } else {
                    file = PR_Open(bltest.options[opt_IV].arg,
                                   PR_RDONLY, 00660);
                }
            } else {
                /* save the random iv for reference */
                file = PR_Open("tmp.iv", PR_WRONLY|PR_CREATE_FILE, 00660);
            }
            memset(&skp->iv, 0, sizeof skp->iv);
            skp->iv.mode = ioMode;
            setupIO(cipherInfo->arena, &skp->iv, file, ivstr, keysize);
            if (file) {
                PR_Close(file);
            }
        }

        /* set up an initialization vector. */
        if (is_authCipher(cipherInfo->mode)) {
            char *aadstr = NULL;
            bltestAuthSymmKeyParams *askp;
            file = NULL;
            askp = &params->ask;
            if (bltest.options[opt_AAD].activated) {
                if (bltest.options[opt_CmdLine].activated) {
                    aadstr = bltest.options[opt_AAD].arg;
                } else {
                    file = PR_Open(bltest.options[opt_AAD].arg,
                                   PR_RDONLY, 00660);
                }
            } else {
                file = NULL;
            }
            memset(&askp->aad, 0, sizeof askp->aad);
            askp->aad.mode = ioMode;
            setupIO(cipherInfo->arena, &askp->aad, file, aadstr, 0);
            if (file) {
                PR_Close(file);
            }
        }
        
        if (bltest.commands[cmd_Verify].activated) {
            file = PR_Open(bltest.options[opt_SigFile].arg, PR_RDONLY, 00660);
            if (is_sigCipher(cipherInfo->mode)) {
                memset(&params->asymk.sig, 0, sizeof(bltestIO));
                params->asymk.sig.mode = ioMode;
                setupIO(cipherInfo->arena, &params->asymk.sig, file, NULL, 0);
            }
            if (file) {
                PR_Close(file);
            }
        }
        
        if (bltest.options[opt_PQGFile].activated) {
            file = PR_Open(bltest.options[opt_PQGFile].arg, PR_RDONLY, 00660);
            params->asymk.cipherParams.dsa.pqgdata.mode = bltestBase64Encoded;
            setupIO(cipherInfo->arena, &params->asymk.cipherParams.dsa.pqgdata,
                    file, NULL, 0);
            if (file) {
                PR_Close(file);
            }
        }

        /* Set up the input buffer */
        if (bltest.options[opt_Input].activated) {
            if (bltest.options[opt_CmdLine].activated) {
                instr = bltest.options[opt_Input].arg;
                infile = NULL;
            } else {
                /* form file name from testdir and input arg. */
                char * filename = bltest.options[opt_Input].arg;
                if (bltest.options[opt_SelfTestDir].activated && 
                    testdir && filename && filename[0] != '/') {
                    filename = PR_smprintf("%s/tests/%s/%s", testdir, 
                                           mode_strings[cipherInfo->mode],
                                           filename);
                    if (!filename) {
                        fprintf(stderr, "%s: Can not allocate memory.\n",
                                progName);
                        goto exit_point;
                    }
                    infile = PR_Open(filename, PR_RDONLY, 00660);
                    PR_smprintf_free(filename);
                } else {
                    infile = PR_Open(filename, PR_RDONLY, 00660);
                }
            }
        } else if (bltest.options[opt_BufSize].activated) {
            /* save the random plaintext for reference */
            char *tmpFName = PR_smprintf("tmp.in.%d", curThrdNum);
            if (!tmpFName) {
                fprintf(stderr, "%s: Can not allocate memory.\n", progName);
                goto exit_point;
            }
            infile = PR_Open(tmpFName, PR_WRONLY|PR_CREATE_FILE, 00660);
            PR_smprintf_free(tmpFName);
        } else {
            infile = PR_STDIN;
        }
        if (!infile) {
            fprintf(stderr, "%s: Failed to open input file.\n", progName);
            goto exit_point;
        }
        cipherInfo->input.mode = ioMode;

        /* Set up the output stream */
        if (bltest.options[opt_Output].activated) {
            /* form file name from testdir and input arg. */
            char * filename = bltest.options[opt_Output].arg;
            if (bltest.options[opt_SelfTestDir].activated && 
                testdir && filename && filename[0] != '/') {
                filename = PR_smprintf("%s/tests/%s/%s", testdir, 
                                       mode_strings[cipherInfo->mode],
                                       filename);
                if (!filename) {
                    fprintf(stderr, "%s: Can not allocate memory.\n", progName);
                    goto exit_point;
                }
                outfile = PR_Open(filename, PR_WRONLY|PR_CREATE_FILE, 00660);
                PR_smprintf_free(filename);
            } else {
                outfile = PR_Open(filename, PR_WRONLY|PR_CREATE_FILE, 00660);
            }
        } else {
            outfile = PR_STDOUT;
        }
        if (!outfile) {
            fprintf(stderr, "%s: Failed to open output file.\n", progName);
            rv = SECFailure;
            goto exit_point;
        }
        cipherInfo->output.mode = ioMode;
        if (bltest.options[opt_SelfTestDir].activated && ioMode == bltestBinary)
            cipherInfo->output.mode = bltestBase64Encoded;

        if (is_hashCipher(cipherInfo->mode))
            cipherInfo->params.hash.restart =
                bltest.options[opt_Restart].activated;

        bufsize = 0;
        if (bltest.options[opt_BufSize].activated)
            bufsize = PORT_Atoi(bltest.options[opt_BufSize].arg);

        /*infile = NULL;*/
        setupIO(cipherInfo->arena, &cipherInfo->input, infile, instr, bufsize);
        if (infile && infile != PR_STDIN)
            PR_Close(infile);
        misalignBuffer(cipherInfo->arena, &cipherInfo->input, inoff);

        cipherInit(cipherInfo, bltest.commands[cmd_Encrypt].activated);
        misalignBuffer(cipherInfo->arena, &cipherInfo->output, outoff);
    }

    if (!bltest.commands[cmd_Nonce].activated) {
        TIMESTART();
        cipherInfo = cipherInfoListHead;
        while (cipherInfo != NULL) {
            cipherInfo->cipherThread = 
                PR_CreateThread(PR_USER_THREAD,
                                    ThreadExecTest,
                                    cipherInfo,
                                    PR_PRIORITY_NORMAL,
                                    PR_GLOBAL_THREAD,
                                    PR_JOINABLE_THREAD,
                                    0);
            cipherInfo = cipherInfo->next;
        } 

        cipherInfo = cipherInfoListHead;
        while (cipherInfo != NULL) {
            PR_JoinThread(cipherInfo->cipherThread);
            finishIO(&cipherInfo->output, outfile);
            cipherInfo = cipherInfo->next;
        }
        TIMEFINISH(totalTime, 1);
    }
    
    cipherInfo = cipherInfoListHead;
    if (cipherInfo->repetitions > 0 || cipherInfo->cxreps > 0 ||
        threads > 1)
        dump_performance_info(cipherInfoListHead, totalTime,
                              bltest.commands[cmd_Encrypt].activated,
	                      (cipherInfo->repetitions == 0));
    
    rv = SECSuccess;

  exit_point:
    if (outfile && outfile != PR_STDOUT)
	PR_Close(outfile);
    cipherInfo = cipherInfoListHead;
    while (cipherInfo != NULL) {
        bltestCipherInfo *tmpInfo = cipherInfo;

        if (cipherInfo->arena)
            PORT_FreeArena(cipherInfo->arena, PR_TRUE);
        cipherInfo = cipherInfo->next;
        PORT_Free(tmpInfo);
    }

    /*NSS_Shutdown();*/

    return SECSuccess;
}

