/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is the Netscape security libraries.
 *
 * The Initial Developer of the Original Code is
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 1994-2000
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Douglas Stebila <douglas@stebila.ca>, Sun Microsystems Laboratories
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#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 "secutil.h"
#include "plgetopt.h"
#include "softoken.h"
#include "nspr.h"
#include "nss.h"

#ifdef NSS_ENABLE_ECC
#include "ecl-curve.h"
SECStatus EC_DecodeParams(const SECItem *encodedParams, 
	ECParams **ecparams);
SECStatus EC_CopyParams(PRArenaPool *arena, ECParams *dstParams,
	      const ECParams *srcParams);
SECStatus secoid_Init(void);
#endif

/* Temporary - add debugging ouput on windows for RSA to track QA failure */
#ifdef _WIN32
#define TRACK_BLTEST_BUG
    char __bltDBG[] = "BLTEST DEBUG";
#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, SECU_Strerror(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, L100; \
        LL_I2L(L100, 100); \
        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, "[-DEHSV]", "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("(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)");
    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]");
#ifdef NSS_ENABLE_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");
#ifdef NSS_ENABLE_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("",  "",   "  secp169k1, 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, "-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;
    PRArenaPool *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, PRArenaPool *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 = (strcmp(&ascii->data[ascii->len-2],"\r\n")) ? 
           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;
    SECItem ascii;
    ascii.data = NULL;
    ascii.len = 0;
    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, unsigned 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;
    SECStatus status;
    NSSBase64Encoder *cx;
    SECItem ascii;
    ascii.data = NULL;
    ascii.len = 0;
    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);
	status = NSSBase64Encoder_Update(cx, len, 4);
	status = NSSBase64Encoder_Update(cx, it->data, it->len);
    }
    status = NSSBase64Encoder_Destroy(cx, PR_FALSE);
    status = PR_Write(file, "\r\n", 2);
}

void
key_from_filedata(PRArenaPool *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;
    PRArenaPool *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;
    PRArenaPool *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;
    PRArenaPool *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;
}

#ifdef NSS_ENABLE_ECC
static ECPrivateKey *
eckey_from_filedata(SECItem *filedata)
{
    ECPrivateKey *key;
    PRArenaPool *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 SECKEYECParams * 
getECParams(char *curve)
{
    SECKEYECParams *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_ENABLE_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);
}

#ifdef NSS_ENABLE_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 (* bltestPubKeyCipherFn)(void *key,
					   SECItem *output,
					   const SECItem *input);

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

typedef enum {
    bltestINVALID = -1,
    bltestDES_ECB,	  /* Symmetric Key Ciphers */
    bltestDES_CBC,	  /* .			   */
    bltestDES_EDE_ECB,	  /* .			   */
    bltestDES_EDE_CBC,	  /* .			   */
    bltestRC2_ECB,	  /* .			   */
    bltestRC2_CBC,	  /* .			   */
    bltestRC4,		  /* .			   */
    bltestRC5_ECB,	  /* .			   */
    bltestRC5_CBC,	  /* .			   */
    bltestAES_ECB,        /* .                     */
    bltestAES_CBC,        /* .                     */
    bltestRSA,		  /* Public Key Ciphers	   */
#ifdef NSS_ENABLE_ECC
    bltestECDSA,	  /* . (Public Key Sig.)   */
#endif
    bltestDSA,		  /* .                     */
    bltestMD2,		  /* Hash algorithms	   */
    bltestMD5,		  /* .			   */
    bltestSHA1,           /* .			   */
    bltestSHA256,         /* .			   */
    bltestSHA384,         /* .			   */
    bltestSHA512,         /* .			   */
    NUMMODES
} bltestCipherMode;

static char *mode_strings[] =
{
    "des_ecb",
    "des_cbc",
    "des3_ecb",
    "des3_cbc",
    "rc2_ecb",
    "rc2_cbc",
    "rc4",
    "rc5_ecb",
    "rc5_cbc",
    "aes_ecb",
    "aes_cbc",
    "rsa",
#ifdef NSS_ENABLE_ECC
    "ecdsa",
#endif
    /*"pqg",*/
    "dsa",
    "md2",
    "md5",
    "sha1",
    "sha256",
    "sha384",
    "sha512",
};

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

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

typedef struct
{
    bltestIO key;
    int	     keysizeInBits;
    RSAPrivateKey *rsakey;
} bltestRSAParams;

typedef struct
{
    bltestIO   key;
    bltestIO   pqgdata;
    unsigned int j;
    bltestIO   keyseed;
    bltestIO   sigseed;
    bltestIO   sig; /* if doing verify, have additional input */
    PQGParams *pqg;
    DSAPrivateKey *dsakey;
} bltestDSAParams;

#ifdef NSS_ENABLE_ECC
typedef struct
{
    bltestIO   key;
    char      *curveName;
    bltestIO   sigseed;
    bltestIO   sig; /* if doing verify, have additional input */
    ECPrivateKey *eckey;
} bltestECDSAParams;
#endif

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

typedef union
{
    bltestIO		key;
    bltestSymmKeyParams sk;
    bltestRC5Params	rc5;
    bltestRSAParams	rsa;
    bltestDSAParams	dsa;
#ifdef NSS_ENABLE_ECC
    bltestECDSAParams	ecdsa;
#endif
    bltestHashParams	hash;
} bltestParams;

typedef struct bltestCipherInfoStr bltestCipherInfo;

struct  bltestCipherInfoStr {
    PRArenaPool *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;
	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 <= bltestAES_CBC)
	return PR_TRUE;
    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! */
#ifdef NSS_ENABLE_ECC
    if (mode >= bltestECDSA && mode <= bltestDSA)
#else
    if (mode >= bltestDSA && mode <= bltestDSA)
#endif
	return PR_TRUE;
    return PR_FALSE;
}

PRBool
cipher_requires_IV(bltestCipherMode mode)
{
    /* change as needed! */
    if (mode == bltestDES_CBC || mode == bltestDES_EDE_CBC ||
	mode == bltestRC2_CBC || mode == bltestRC5_CBC     ||
        mode == bltestAES_CBC)
	return PR_TRUE;
    return PR_FALSE;
}

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

SECStatus
setupIO(PRArenaPool *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) {
	    PR_Close(file);
	    return SECFailure;
	}
	in = &fileData;
    } else if (str) {
	/* grabbing data from command line */
	fileData.data = 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:
	rv = atob(in, &input->buf, arena);
	break;
    case bltestBinary:
	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(PRArenaPool *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(PRArenaPool *arena, bltestIO *io, int off)
{
    ptrdiff_t offset = (ptrdiff_t)io->buf.data % WORDSIZE;
    int length = io->buf.len;
    if (offset != off) {
	SECITEM_ReallocItem(arena, &io->buf, length, length + 2*WORDSIZE);
	io->buf.len = length + 2*WORDSIZE; /* why doesn't realloc do this? */
	/* 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
rsa_PublicKeyOp(void *key, SECItem *output, const SECItem *input)
{
    return RSA_PublicKeyOp((RSAPublicKey *)key, output->data, input->data);
}

SECStatus
rsa_PrivateKeyOp(void *key, SECItem *output, const SECItem *input)
{
    return RSA_PrivateKeyOp((RSAPrivateKey *)key, output->data, input->data);
}

SECStatus
dsa_signDigest(void *key, SECItem *output, const SECItem *input)
{
    return DSA_SignDigest((DSAPrivateKey *)key, output, input);
}

SECStatus
dsa_verifyDigest(void *key, SECItem *output, const SECItem *input)
{
    return DSA_VerifyDigest((DSAPublicKey *)key, output, input);
}

#ifdef NSS_ENABLE_ECC
SECStatus
ecdsa_signDigest(void *key, SECItem *output, const SECItem *input)
{
    return ECDSA_SignDigest((ECPrivateKey *)key, output, input);
}

SECStatus
ecdsa_verifyDigest(void *key, SECItem *output, const SECItem *input)
{
    return ECDSA_VerifyDigest((ECPublicKey *)key, 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)
{
#if 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;
    int minorMode;
    int i;
    int keylen   = aesp->key.buf.len;
    int blocklen = AES_BLOCK_SIZE; 
    PRIntervalTime time1, time2;

    switch (cipherInfo->mode) {
    case bltestAES_ECB:	    minorMode = NSS_AES;	  break;
    case bltestAES_CBC:	    minorMode = NSS_AES_CBC;	  break;
    default:
	return SECFailure;
    }
    cipherInfo->cx = (void*)AES_CreateContext(aesp->key.buf.data,
					      aesp->iv.buf.data,
					      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,
					          aesp->iv.buf.data,
					          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_rsa_init(bltestCipherInfo *cipherInfo, PRBool encrypt)
{
    int i;
    RSAPrivateKey **dummyKey;
    PRIntervalTime time1, time2;
    bltestRSAParams *rsap = &cipherInfo->params.rsa;
    /* RSA key gen was done during parameter setup */
    cipherInfo->cx = cipherInfo->params.rsa.rsakey;
    /* 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, 
	                             &rsap->rsakey->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) {
	/* Have to convert private key to public key.  Memory
	 * is freed with private key's arena  */
	RSAPublicKey *pubkey;
	RSAPrivateKey *key = (RSAPrivateKey *)cipherInfo->cx;
	pubkey = (RSAPublicKey *)PORT_ArenaAlloc(key->arena,
						 sizeof(RSAPublicKey));
	pubkey->modulus.len = key->modulus.len;
	pubkey->modulus.data = key->modulus.data;
	pubkey->publicExponent.len = key->publicExponent.len;
	pubkey->publicExponent.data = key->publicExponent.data;
	cipherInfo->cx = (void *)pubkey;
	cipherInfo->cipher.pubkeyCipher = rsa_PublicKeyOp;
    } else {
	cipherInfo->cipher.pubkeyCipher = rsa_PrivateKeyOp;
    }
    return SECSuccess;
}

SECStatus
bltest_pqg_init(bltestDSAParams *dsap)
{
    SECStatus rv, res;
    PQGVerify *vfy = NULL;
    rv = PQG_ParamGen(dsap->j, &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;
    bltestDSAParams *dsap = &cipherInfo->params.dsa;
    PQGVerify *ignore = NULL;
    /* DSA key gen was done during parameter setup */
    cipherInfo->cx = cipherInfo->params.dsa.dsakey;
    /* 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;
	    PQG_ParamGen(dsap->j, &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 (!cipherInfo->cx && dsap->key.buf.len > 0) {
	cipherInfo->cx = dsakey_from_filedata(&dsap->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 *)cipherInfo->cx;
	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;
	cipherInfo->cipher.pubkeyCipher = dsa_verifyDigest;
    }
    return SECSuccess;
}

#ifdef NSS_ENABLE_ECC
SECStatus
bltest_ecdsa_init(bltestCipherInfo *cipherInfo, PRBool encrypt)
{
    int i;
    ECPrivateKey **dummyKey;
    PRIntervalTime time1, time2;
    bltestECDSAParams *ecdsap = &cipherInfo->params.ecdsa;
    /* ECDSA key gen was done during parameter setup */
    cipherInfo->cx = cipherInfo->params.ecdsa.eckey;
    /* 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(&ecdsap->eckey->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 (!cipherInfo->cx && ecdsap->key.buf.len > 0) {
	cipherInfo->cx = eckey_from_filedata(&ecdsap->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 *)cipherInfo->cx;
	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;
	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, uint32 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, uint32 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, uint32 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, uint32 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
SHA256_restart(unsigned char *dest, const unsigned char *src, uint32 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, uint32 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, uint32 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,
#ifdef NSS_ENABLE_ECC
	      int keysize, int exponent, char *curveName)
#else
	      int keysize, int exponent)
#endif
{
    int i;
    SECStatus rv = SECSuccess;
    bltestRSAParams *rsap;
    bltestDSAParams *dsap;
#ifdef NSS_ENABLE_ECC
    bltestECDSAParams *ecdsap;
    SECItem *tmpECParamsDER;
    ECParams *tmpECParams = NULL;
    SECItem ecSerialize[3];
#endif
    switch (cipherInfo->mode) {
    case bltestRSA:
	rsap = &cipherInfo->params.rsa;
	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));
	    rsap->rsakey = RSA_NewKey(keysize * 8, &expitem);
	    serialize_key(&rsap->rsakey->version, 9, file);
	    rsap->keysizeInBits = keysize * 8;
	} else {
	    setupIO(cipherInfo->arena, &cipherInfo->params.key, file, NULL, 0);
	    rsap->rsakey = rsakey_from_filedata(&cipherInfo->params.key.buf);
	    rsap->keysizeInBits = rsap->rsakey->modulus.len * 8;
	}
	break;
    case bltestDSA:
	dsap = &cipherInfo->params.dsa;
	if (keysize > 0) {
	    dsap->j = PQG_PBITS_TO_INDEX(8*keysize);
	    if (!dsap->pqg)
		bltest_pqg_init(dsap);
	    rv = DSA_NewKey(dsap->pqg, &dsap->dsakey);
	    CHECKERROR(rv, __LINE__);
	    serialize_key(&dsap->dsakey->params.prime, 5, file);
	} else {
	    setupIO(cipherInfo->arena, &cipherInfo->params.key, file, NULL, 0);
	    dsap->dsakey = dsakey_from_filedata(&cipherInfo->params.key.buf);
	    dsap->j = PQG_PBITS_TO_INDEX(8*dsap->dsakey->params.prime.len);
	}
	break;
#ifdef NSS_ENABLE_ECC
    case bltestECDSA:
	ecdsap = &cipherInfo->params.ecdsa;
	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, &ecdsap->eckey);
	    CHECKERROR(rv, __LINE__);
	    ecSerialize[0].type = tmpECParamsDER->type;
	    ecSerialize[0].data = tmpECParamsDER->data;
	    ecSerialize[0].len  = tmpECParamsDER->len;
	    ecSerialize[1].type = ecdsap->eckey->publicValue.type;
	    ecSerialize[1].data = ecdsap->eckey->publicValue.data;
	    ecSerialize[1].len  = ecdsap->eckey->publicValue.len;
	    ecSerialize[2].type = ecdsap->eckey->privateValue.type;
	    ecSerialize[2].data = ecdsap->eckey->privateValue.data;
	    ecSerialize[2].len  = ecdsap->eckey->privateValue.len;
	    serialize_key(&(ecSerialize[0]), 3, file);
	    free(tmpECParamsDER);
	    PORT_FreeArena(tmpECParams->arena, PR_TRUE);
	    rv = SECOID_Shutdown();
	    CHECKERROR(rv, __LINE__);
	} else {
	    setupIO(cipherInfo->arena, &cipherInfo->params.key, file, NULL, 0);
	    ecdsap->eckey = eckey_from_filedata(&cipherInfo->params.key.buf);
	}
	break;
#endif
    default:
	return SECFailure;
    }
    return SECSuccess;
}

SECStatus
cipherInit(bltestCipherInfo *cipherInfo, PRBool encrypt)
{
    PRBool restart;
    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;
    case bltestRC5_ECB:
    case bltestRC5_CBC:
#if NSS_SOFTOKEN_DOES_RC5
	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:
	SECITEM_AllocItem(cipherInfo->arena, &cipherInfo->output.buf,
			  cipherInfo->input.pBuf.len);
	return bltest_aes_init(cipherInfo, encrypt);
	break;
    case bltestRSA:
	SECITEM_AllocItem(cipherInfo->arena, &cipherInfo->output.buf,
			  cipherInfo->input.pBuf.len);
	return bltest_rsa_init(cipherInfo, encrypt);
	break;
    case bltestDSA:
	SECITEM_AllocItem(cipherInfo->arena, &cipherInfo->output.buf,
			  DSA_SIGNATURE_LEN);
	return bltest_dsa_init(cipherInfo, encrypt);
	break;
#ifdef NSS_ENABLE_ECC
    case bltestECDSA:
	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 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
dsaOp(bltestCipherInfo *cipherInfo)
{
    PRIntervalTime time1, time2;
    SECStatus rv = SECSuccess;
    int i;
    int maxLen = cipherInfo->output.pBuf.len;
    SECItem dummyOut = { 0, 0, 0 };
    SECITEM_AllocItem(NULL, &dummyOut, maxLen);
    if (cipherInfo->cipher.pubkeyCipher == dsa_signDigest) {
	if (cipherInfo->params.dsa.sigseed.buf.len > 0) {
            bltestDSAParams *dsa = &cipherInfo->params.dsa;
            DSAPrivateKey *key = (DSAPrivateKey *)cipherInfo->cx;

            TIMESTART();
            rv = DSA_SignDigestWithSeed(key,
                                        &cipherInfo->output.pBuf,
                                        &cipherInfo->input.pBuf,
                                        dsa->sigseed.buf.data);
            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++) {
                    rv = DSA_SignDigestWithSeed(key, &dummyOut,
                                                &cipherInfo->input.pBuf,
                                                dsa->sigseed.buf.data);
                    CHECKERROR(rv, __LINE__);
                }
            } else {
                int opsBetweenChecks = 0;
                TIMEMARK(cipherInfo->seconds);
                while (! (TIMETOFINISH())) {
                    int j = 0;
                    for (;j < opsBetweenChecks;j++) {
                        rv = DSA_SignDigestWithSeed(key, &dummyOut,
                                                    &cipherInfo->input.pBuf,
                                                    dsa->sigseed.buf.data);
                        CHECKERROR(rv, __LINE__);
                    }
                    cipherInfo->repetitions += j;
                }
            }
            TIMEFINISH(cipherInfo->optime, 1.0);
        } else {
            TIMESTART();
            rv = DSA_SignDigest((DSAPrivateKey *)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++) {
                    rv = DSA_SignDigest((DSAPrivateKey *)cipherInfo->cx,
                                        &dummyOut,
                                        &cipherInfo->input.pBuf);
                    CHECKERROR(rv, __LINE__);
                }
            } else {
                int opsBetweenChecks = 0;
                TIMEMARK(cipherInfo->seconds);
                while (! (TIMETOFINISH())) {
                    int j = 0;
                    for (;j < opsBetweenChecks;j++) {
                        rv = DSA_SignDigest((DSAPrivateKey *)cipherInfo->cx,
                                            &dummyOut,
                                            &cipherInfo->input.pBuf);
                        CHECKERROR(rv, __LINE__);
                    }
                    cipherInfo->repetitions += j;
                }
            }
            TIMEFINISH(cipherInfo->optime, 1.0);
        }
        bltestCopyIO(cipherInfo->arena, &cipherInfo->params.dsa.sig, 
                     &cipherInfo->output);
    } else {
        TIMESTART();
        rv = DSA_VerifyDigest((DSAPublicKey *)cipherInfo->cx,
                              &cipherInfo->params.dsa.sig.buf,
                              &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++) {
                rv = DSA_VerifyDigest((DSAPublicKey *)cipherInfo->cx,
                                      &cipherInfo->params.dsa.sig.buf,
                                      &cipherInfo->input.pBuf);
                CHECKERROR(rv, __LINE__);
            }
        } else {
            int opsBetweenChecks = 0;
            TIMEMARK(cipherInfo->seconds);
            while (! (TIMETOFINISH())) {
                int j = 0;
                for (;j < opsBetweenChecks;j++) {
                    rv = DSA_VerifyDigest((DSAPublicKey *)cipherInfo->cx,
                                          &cipherInfo->params.dsa.sig.buf,
                                          &cipherInfo->input.pBuf);
                    CHECKERROR(rv, __LINE__);
                }
                cipherInfo->repetitions += j;
            }
        }
        TIMEFINISH(cipherInfo->optime, 1.0);
    }
    SECITEM_FreeItem(&dummyOut, PR_FALSE);
    return rv;
}

#ifdef NSS_ENABLE_ECC
SECStatus
ecdsaOp(bltestCipherInfo *cipherInfo)
{
    PRIntervalTime time1, time2;
    SECStatus rv = SECSuccess;
    int i;
    int maxLen = cipherInfo->output.pBuf.len;
    SECItem dummyOut = { 0, 0, 0 };
    SECITEM_AllocItem(NULL, &dummyOut, maxLen);
    if (cipherInfo->cipher.pubkeyCipher == ecdsa_signDigest) {
        if (cipherInfo->params.ecdsa.sigseed.buf.len > 0) {
            ECPrivateKey *key = (ECPrivateKey *)cipherInfo->cx;
            bltestECDSAParams *ecdsa = &cipherInfo->params.ecdsa;

            TIMESTART();
            rv = ECDSA_SignDigestWithSeed(key,
                                          &cipherInfo->output.pBuf,
                                          &cipherInfo->input.pBuf,
                                          ecdsa->sigseed.buf.data,
                                          ecdsa->sigseed.buf.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++) {
                    rv = ECDSA_SignDigestWithSeed(key, &dummyOut,
                                                  &cipherInfo->input.pBuf,
                                                  ecdsa->sigseed.buf.data,
                                                  ecdsa->sigseed.buf.len);
                    CHECKERROR(rv, __LINE__);
                }
            } else {
                int opsBetweenChecks = 0;
                TIMEMARK(cipherInfo->seconds);
                while (! (TIMETOFINISH())) {
                    int j = 0;
                    for (;j < opsBetweenChecks;j++) {
                        rv = ECDSA_SignDigestWithSeed(key, &dummyOut,
                                                      &cipherInfo->input.pBuf,
                                                      ecdsa->sigseed.buf.data,
                                                      ecdsa->sigseed.buf.len);
                        CHECKERROR(rv, __LINE__);
                    }
                    cipherInfo->repetitions += j;
                }
            }
            TIMEFINISH(cipherInfo->optime, 1.0);
        } else {
            TIMESTART();
            rv = ECDSA_SignDigest((ECPrivateKey *)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++) {
                    rv = ECDSA_SignDigest((ECPrivateKey *)cipherInfo->cx,
                                          &dummyOut,
                                          &cipherInfo->input.pBuf);
                    CHECKERROR(rv, __LINE__);
                }
            } else {
                int opsBetweenChecks = 0;
                TIMEMARK(cipherInfo->seconds);
                while (! (TIMETOFINISH())) {
                    int j = 0;
                    for (;j < opsBetweenChecks;j++) {
                        rv = ECDSA_SignDigest((ECPrivateKey *)cipherInfo->cx,
                                              &dummyOut,
                                              &cipherInfo->input.pBuf);
                        CHECKERROR(rv, __LINE__);
                    }
                    cipherInfo->repetitions += j;
                }
            }
            TIMEFINISH(cipherInfo->optime, 1.0);
        }
        bltestCopyIO(cipherInfo->arena, &cipherInfo->params.ecdsa.sig, 
                     &cipherInfo->output);
    } else {
        TIMESTART();
        rv = ECDSA_VerifyDigest((ECPublicKey *)cipherInfo->cx,
                                &cipherInfo->params.ecdsa.sig.buf,
                                &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++) {
                rv = ECDSA_VerifyDigest((ECPublicKey *)cipherInfo->cx,
                                        &cipherInfo->params.ecdsa.sig.buf,
                                        &cipherInfo->input.pBuf);
                CHECKERROR(rv, __LINE__);
            }
        } else {
            int opsBetweenChecks = 0;
            TIMEMARK(cipherInfo->seconds);
            while (! (TIMETOFINISH())) {
                int j = 0;
                for (;j < opsBetweenChecks;j++) {
                    rv = ECDSA_VerifyDigest((ECPublicKey *)cipherInfo->cx,
                                            &cipherInfo->params.ecdsa.sig.buf,
                                            &cipherInfo->input.pBuf);
                    CHECKERROR(rv, __LINE__);
                }
                cipherInfo->repetitions += j;
            }
        }
        TIMEFINISH(cipherInfo->optime, 1.0);
    }
    SECITEM_FreeItem(&dummyOut, PR_FALSE);
    return rv;
}
#endif

SECStatus
cipherDoOp(bltestCipherInfo *cipherInfo)
{
    PRIntervalTime time1, time2;
    SECStatus rv = SECSuccess;
    int i, len;
    int maxLen = cipherInfo->output.pBuf.len;
    unsigned char *dummyOut;
    if (cipherInfo->mode == bltestDSA)
	return dsaOp(cipherInfo);
#ifdef NSS_ENABLE_ECC
    else if (cipherInfo->mode == bltestECDSA)
	return ecdsaOp(cipherInfo);
#endif
    dummyOut = PORT_Alloc(maxLen);
    if (is_symmkeyCipher(cipherInfo->mode)) {
        TIMESTART();
        rv = (*cipherInfo->cipher.symmkeyCipher)(cipherInfo->cx,
                                                 cipherInfo->output.pBuf.data,
                                                 &len, maxLen,
                                                 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.symmkeyCipher)(cipherInfo->cx, dummyOut,
                                                    &len, maxLen,
                                                    cipherInfo->input.pBuf.data,
                                                    cipherInfo->input.pBuf.len);
                
                CHECKERROR(rv, __LINE__);
            }
        } else {
            int opsBetweenChecks = 0;
            bltestIO *input = &cipherInfo->input;
            TIMEMARK(cipherInfo->seconds);
            while (! (TIMETOFINISH())) {
                int j = 0;
                for (;j < opsBetweenChecks;j++) {
                    (*cipherInfo->cipher.symmkeyCipher)(cipherInfo->cx,
                                                        dummyOut,
                                                        &len, maxLen,
                                                        input->pBuf.data,
                                                        input->pBuf.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)
{
    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_ECB:
    case bltestAES_CBC:
	AES_DestroyContext((AESContext *)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;
#if 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 bltestDSA: /* will be freed with it. */
#ifdef NSS_ENABLE_ECC
    case bltestECDSA:
#endif
    case bltestMD2: /* hash contexts are ephemeral */
    case bltestMD5:
    case bltestSHA1:
    case bltestSHA256:
    case bltestSHA384:
    case bltestSHA512:
	return SECSuccess;
	break;
    default:
	return SECFailure;
    }
    return SECSuccess;
}

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 {
        int prnRes;
        LL_L2I(prnRes, rNum);
        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 += info->input.buf.len * 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 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;
#if 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:
          if (td) {
              fprintf(stdout, "%8s", "rsa_mod");
              fprintf(stdout, "%12s", "rsa_pe");
          } else {
              fprintf(stdout, "%8d", info->params.rsa.keysizeInBits);
              print_exponent(&info->params.rsa.rsakey->publicExponent);
          }
          break;
      case bltestDSA:
          if (td)
              fprintf(stdout, "%8s", "pqg_mod");
          else
              fprintf(stdout, "%8d", PQG_INDEX_TO_PBITS(info->params.dsa.j));
          break;
#ifdef NSS_ENABLE_ECC
      case bltestECDSA:
          if (td)
              fprintf(stdout, "%12s", "ec_curve");
          else
              fprintf(stdout, "%12s",
                      ecCurve_map[info->params.ecdsa.eckey->ecParams.name]->text);
          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(PRArenaPool *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);
}

void
get_params(PRArenaPool *arena, bltestParams *params, 
	   bltestCipherMode mode, int j)
{
    char filename[256];
    char *modestr = mode_strings[mode];
#if NSS_SOFTOKEN_DOES_RC5
    FILE *file;
    char *mark, *param, *val;
    int index = 0;
#endif
    switch (mode) {
    case bltestDES_CBC:
    case bltestDES_EDE_CBC:
    case bltestRC2_CBC:
    case bltestAES_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:
	sprintf(filename, "%s/tests/%s/%s%d", testdir, modestr, "key", j);
	load_file_data(arena, &params->sk.key, filename, bltestBinary);
	break;
#if 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:
	sprintf(filename, "%s/tests/%s/%s%d", testdir, modestr, "key", j);
	load_file_data(arena, &params->rsa.key, filename, bltestBase64Encoded);
	params->rsa.rsakey = rsakey_from_filedata(&params->key.buf);
	break;
    case bltestDSA:
	sprintf(filename, "%s/tests/%s/%s%d", testdir, modestr, "key", j);
	load_file_data(arena, &params->dsa.key, filename, bltestBase64Encoded);
	params->dsa.dsakey = dsakey_from_filedata(&params->key.buf);
	sprintf(filename, "%s/tests/%s/%s%d", testdir, modestr, "pqg", j);
	load_file_data(arena, &params->dsa.pqgdata, filename,
		       bltestBase64Encoded);
	params->dsa.pqg = pqg_from_filedata(&params->dsa.pqgdata.buf);
	sprintf(filename, "%s/tests/%s/%s%d", testdir, modestr, "keyseed", j);
	load_file_data(arena, &params->dsa.keyseed, filename, 
	               bltestBase64Encoded);
	sprintf(filename, "%s/tests/%s/%s%d", testdir, modestr, "sigseed", j);
	load_file_data(arena, &params->dsa.sigseed, filename, 
	               bltestBase64Encoded);
	sprintf(filename, "%s/tests/%s/%s%d", testdir, modestr, "ciphertext",j);
	load_file_data(arena, &params->dsa.sig, filename, bltestBase64Encoded);
	break;
#ifdef NSS_ENABLE_ECC
    case bltestECDSA:
	sprintf(filename, "%s/tests/%s/%s%d", testdir, modestr, "key", j);
	load_file_data(arena, &params->ecdsa.key, filename, bltestBase64Encoded);
	params->ecdsa.eckey = eckey_from_filedata(&params->key.buf);
	sprintf(filename, "%s/tests/%s/%s%d", testdir, modestr, "sigseed", j);
	load_file_data(arena, &params->ecdsa.sigseed, filename, 
	               bltestBase64Encoded);
	sprintf(filename, "%s/tests/%s/%s%d", testdir, modestr, "ciphertext",j);
	load_file_data(arena, &params->ecdsa.sig, filename, bltestBase64Encoded);
	break;
#endif
    case bltestMD2:
    case bltestMD5:
    case bltestSHA1:
    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)
{
    int res;
    char *modestr = mode_strings[mode];
    res = SECITEM_CompareItem(&result->pBuf, &cmp->buf);
    if (is_sigCipher(mode)) {
	if (forward) {
	    if (res == 0) {
		printf("Signature self-test for %s passed.\n", modestr);
	    } else {
		printf("Signature self-test for %s failed!\n", modestr);
	    }
	} 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 (res == 0) {
	    printf("Hash self-test for %s passed.\n", modestr);
	} else {
	    printf("Hash self-test for %s failed!\n", modestr);
	}
    } else {
	if (forward) {
	    if (res == 0) {
		printf("Encryption self-test for %s passed.\n", modestr);
	    } else {
		printf("Encryption self-test for %s failed!\n", modestr);
	    }
	} else {
	    if (res == 0) {
		printf("Decryption self-test for %s passed.\n", modestr);
	    } else {
		printf("Decryption self-test for %s failed!\n", modestr);
	    }
	}
    }
    return (res != 0);
}

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;
    int i, j, nummodes, numtests;
    char *modestr;
    char filename[256];
    PRFileDesc *file;
    PRArenaPool *arena;
    SECItem item;
    PRBool finished;
    SECStatus rv = SECSuccess, srv;

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

    finished = PR_FALSE;
    nummodes = (numModes == 0) ? NUMMODES : numModes;
    for (i=0; i < nummodes && !finished; i++) {
	if (i == bltestRC5_ECB || i == bltestRC5_CBC) continue;
	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;
#ifdef TRACK_BLTEST_BUG
	if (mode == bltestRSA) {
	    fprintf(stderr, "[%s] Self-Testing RSA\n", __bltDBG);
	}
#endif
	/* get the number of tests in the directory */
	sprintf(filename, "%s/tests/%s/%s", testdir, modestr, "numtests");
	file = PR_Open(filename, PR_RDONLY, 00660);
	if (!file) {
	    fprintf(stderr, "%s: File %s does not exist.\n", progName,filename);
	    return SECFailure;
	}
	rv = SECU_FileToItem(&item, file);
#ifdef TRACK_BLTEST_BUG
	if (mode == bltestRSA) {
	    fprintf(stderr, "[%s] Loaded data from %s\n", __bltDBG, filename);
	}
#endif
	PR_Close(file);
	/* 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++) {
#ifdef TRACK_BLTEST_BUG
	    if (mode == bltestRSA) {
		fprintf(stderr, "[%s] Executing self-test #%d\n", __bltDBG, j);
	    }
#endif
	    sprintf(filename, "%s/tests/%s/%s%d", testdir, modestr,
			      "plaintext", j);
	    load_file_data(arena, &pt, filename, 
#ifdef NSS_ENABLE_ECC
	                   ((mode == bltestDSA) || (mode == bltestECDSA))
#else
	                   (mode == bltestDSA)
#endif
	                   ? bltestBase64Encoded : bltestBinary);
	    sprintf(filename, "%s/tests/%s/%s%d", testdir, modestr,
			      "ciphertext", j);
	    load_file_data(arena, &ct, filename, bltestBase64Encoded);
#ifdef TRACK_BLTEST_BUG
	    if (mode == bltestRSA) {
		fprintf(stderr, "[%s] Loaded data for  self-test #%d\n", __bltDBG, j);
	    }
#endif
	    get_params(arena, params, mode, j);
#ifdef TRACK_BLTEST_BUG
	    if (mode == bltestRSA) {
		fprintf(stderr, "[%s] Got parameters for #%d\n", __bltDBG, j);
	    }
#endif
	    /* Forward Operation (Encrypt/Sign/Hash)
	    ** Align the input buffer (plaintext) according to request
	    ** then perform operation and compare to ciphertext
	    */
	    /* XXX for now */
	    rv = SECSuccess;
	    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);
#ifdef TRACK_BLTEST_BUG
		if (mode == bltestRSA) {
		    fprintf(stderr, "[%s] Inited cipher context and buffers for #%d\n", __bltDBG, j);
		}
#endif
		rv |= cipherDoOp(&cipherInfo);
#ifdef TRACK_BLTEST_BUG
		if (mode == bltestRSA) {
		    fprintf(stderr, "[%s] Performed encrypt for #%d\n", __bltDBG, j);
		}
#endif
		rv |= cipherFinish(&cipherInfo);
#ifdef TRACK_BLTEST_BUG
		if (mode == bltestRSA) {
		    fprintf(stderr, "[%s] Finished encrypt for #%d\n", __bltDBG, j);
		}
#endif
		rv |= verify_self_test(&cipherInfo.output, 
		                       &ct, mode, PR_TRUE, 0);
#ifdef TRACK_BLTEST_BUG
		if (mode == bltestRSA) {
		    fprintf(stderr, "[%s] Verified self-test for #%d\n", __bltDBG, j);
		}
#endif
		/* If testing hash, only one op to test */
		if (is_hashCipher(mode))
		    continue;
		/*if (rv) return rv;*/
	    }
	    if (!decrypt)
		continue;
	    /* XXX for now */
	    rv = SECSuccess;
	    /* Reverse Operation (Decrypt/Verify)
	    ** Align the input buffer (ciphertext) according to request
	    ** then perform operation and compare to plaintext
	    */
#ifdef NSS_ENABLE_ECC
	    if ((mode != bltestDSA) && (mode != bltestECDSA))
#else
	    if (mode != bltestDSA)
#endif
		bltestCopyIO(arena, &cipherInfo.input, &ct);
	    else
		bltestCopyIO(arena, &cipherInfo.input, &pt);
	    misalignBuffer(arena, &cipherInfo.input, inoff);
	    memset(&cipherInfo.output.buf, 0, sizeof cipherInfo.output.buf);
	    rv |= cipherInit(&cipherInfo, PR_FALSE);
	    misalignBuffer(arena, &cipherInfo.output, outoff);
#ifdef TRACK_BLTEST_BUG
	    if (mode == bltestRSA) {
		fprintf(stderr, "[%s] Inited cipher context and buffers for #%d\n", __bltDBG, j);
	    }
#endif
	    srv = SECSuccess;
	    srv |= cipherDoOp(&cipherInfo);
#ifdef TRACK_BLTEST_BUG
	    if (mode == bltestRSA) {
		fprintf(stderr, "[%s] Performed decrypt for #%d\n", __bltDBG, j);
	    }
#endif
	    rv |= cipherFinish(&cipherInfo);
#ifdef TRACK_BLTEST_BUG
	    if (mode == bltestRSA) {
		fprintf(stderr, "[%s] Finished decrypt for #%d\n", __bltDBG, j);
	    }
#endif
	    rv |= verify_self_test(&cipherInfo.output, 
	                           &pt, mode, PR_FALSE, srv);
#ifdef TRACK_BLTEST_BUG
	    if (mode == bltestRSA) {
		fprintf(stderr, "[%s] Verified self-test for #%d\n", __bltDBG, j);
	    }
#endif
	    /*if (rv) return rv;*/
	}
    }
    return rv;
}

SECStatus
dump_file(bltestCipherMode mode, char *filename)
{
    bltestIO keydata;
    PRArenaPool *arena = NULL;
    arena = PORT_NewArena(BLTEST_DEFAULT_CHUNKSIZE);
    if (mode == bltestRSA) {
	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);
#ifdef NSS_ENABLE_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);
}

/* bltest commands */
enum {
    cmd_Decrypt = 0,
    cmd_Encrypt,
    cmd_FIPS,
    cmd_Hash,
    cmd_Nonce,
    cmd_Dump,
    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,
#ifdef NSS_ENABLE_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_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_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 },
#ifdef NSS_ENABLE_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_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)
{
    char *infileName, *outfileName, *keyfileName, *ivfileName;
    SECStatus rv = SECFailure;

    double              totalTime;
    PRIntervalTime      time1, time2;
    PRFileDesc          *outfile;           
    bltestCipherInfo    *cipherInfoListHead, *cipherInfo;
    bltestIOMode        ioMode;
    int                 bufsize, exponent, curThrdNum;
#ifdef NSS_ENABLE_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 = RNG_RNGInit();
    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;
    /* set some defaults */
    infileName = outfileName = keyfileName = ivfileName = NULL;

    /* 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
     */

    /* 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;
    }

    /* 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;

#ifdef NSS_ENABLE_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:
        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;
        PRArenaPool    *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)) {
            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;
#ifdef NSS_ENABLE_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;
            if (cipherInfo->mode == bltestRC5_CBC)
                skp = (bltestSymmKeyParams *)&params->rc5;
            else
                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);
            }
        }
        
        if (bltest.commands[cmd_Verify].activated) {
            file = PR_Open(bltest.options[opt_SigFile].arg, PR_RDONLY, 00660);
            if (cipherInfo->mode == bltestDSA) {
                memset(&cipherInfo->params.dsa.sig, 0, sizeof(bltestIO));
                cipherInfo->params.dsa.sig.mode = ioMode;
                setupIO(cipherInfo->arena, &cipherInfo->params.dsa.sig,
                        file, NULL, 0);
#ifdef NSS_ENABLE_ECC
            } else if (cipherInfo->mode == bltestECDSA) {
                memset(&cipherInfo->params.ecdsa.sig, 0, sizeof(bltestIO));
                cipherInfo->params.ecdsa.sig.mode = ioMode;
                setupIO(cipherInfo->arena, &cipherInfo->params.ecdsa.sig,
                        file, NULL, 0);
#endif
            }
            if (file) {
                PR_Close(file);
            }
        }
        
        if (bltest.options[opt_PQGFile].activated) {
            file = PR_Open(bltest.options[opt_PQGFile].arg, PR_RDONLY, 00660);
            params->dsa.pqgdata.mode = bltestBase64Encoded;
            setupIO(cipherInfo->arena, &params->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;
}
