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

#include <stdio.h>
#include <string.h>
#include "secutil.h"

#if defined(XP_UNIX)
#include <unistd.h>
#include <sys/time.h>
#include <termios.h>
#endif

#if defined(XP_WIN) || defined(XP_PC)
#include <time.h>
#include <conio.h>
#endif

#include "nspr.h"
#include "prtypes.h"
#include "prtime.h"
#include "prlong.h"

#include "pk11func.h"

#define NUM_KEYSTROKES 120
#define RAND_BUF_SIZE 60

#define ERROR_BREAK  \
    rv = SECFailure; \
    break;

const SEC_ASN1Template SECKEY_PQGParamsTemplate[] = {
    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SECKEYPQGParams) },
    { SEC_ASN1_INTEGER, offsetof(SECKEYPQGParams, prime) },
    { SEC_ASN1_INTEGER, offsetof(SECKEYPQGParams, subPrime) },
    { SEC_ASN1_INTEGER, offsetof(SECKEYPQGParams, base) },
    { 0 }
};

/* returns 0 for success, -1 for failure (EOF encountered) */
static int
UpdateRNG(void)
{
    char randbuf[RAND_BUF_SIZE];
    int fd;
    int c;
    int rv = 0;
    size_t count;
#ifdef XP_UNIX
    cc_t orig_cc_min;
    cc_t orig_cc_time;
    tcflag_t orig_lflag;
    struct termios tio;
#endif
    char meter[] = {
        "\r|                                                            |"
    };

#define FPS fprintf(stderr,
    FPS "\n");
    FPS "A random seed must be generated that will be used in the\n");
    FPS "creation of your key.  One of the easiest ways to create a\n");
    FPS "random seed is to use the timing of keystrokes on a keyboard.\n");
    FPS "\n");
    FPS "To begin, type keys on the keyboard until this progress meter\n");
    FPS "is full.  DO NOT USE THE AUTOREPEAT FUNCTION ON YOUR KEYBOARD!\n");
    FPS "\n");
    FPS "\n");
    FPS "Continue typing until the progress meter is full:\n\n");
    FPS "%s", meter);
    FPS "\r|");

    /* turn off echo on stdin & return on 1 char instead of NL */
    fd = fileno(stdin);

#if defined(XP_UNIX)
    tcgetattr(fd, &tio);
    orig_lflag = tio.c_lflag;
    orig_cc_min = tio.c_cc[VMIN];
    orig_cc_time = tio.c_cc[VTIME];
    tio.c_lflag &= ~ECHO;
    tio.c_lflag &= ~ICANON;
    tio.c_cc[VMIN] = 1;
    tio.c_cc[VTIME] = 0;
    tcsetattr(fd, TCSAFLUSH, &tio);
#endif

    /* Get random noise from keyboard strokes */
    count = 0;
    while (count < sizeof randbuf) {
#if defined(XP_UNIX)
        c = getc(stdin);
#else
        c = getch();
#endif
        if (c == EOF) {
            rv = -1;
            break;
        }
        randbuf[count] = c;
        if (count == 0 || c != randbuf[count - 1]) {
            count++;
            FPS "*");
        }
    }
    PK11_RandomUpdate(randbuf, sizeof randbuf);
    memset(randbuf, 0, sizeof randbuf);

    FPS "\n\n");
    FPS "Finished.  Press enter to continue: ");
    while ((c = getc(stdin)) != '\n' && c != EOF)
        ;
    if (c == EOF)
        rv = -1;
    FPS "\n");

#undef FPS

#if defined(XP_UNIX)
    /* set back termio the way it was */
    tio.c_lflag = orig_lflag;
    tio.c_cc[VMIN] = orig_cc_min;
    tio.c_cc[VTIME] = orig_cc_time;
    tcsetattr(fd, TCSAFLUSH, &tio);
#endif
    return rv;
}

static const unsigned char P[] = { 0,
                                   0xc6, 0x2a, 0x47, 0x73, 0xea, 0x78, 0xfa, 0x65,
                                   0x47, 0x69, 0x39, 0x10, 0x08, 0x55, 0x6a, 0xdd,
                                   0xbf, 0x77, 0xe1, 0x9a, 0x69, 0x73, 0xba, 0x66,
                                   0x37, 0x08, 0x93, 0x9e, 0xdb, 0x5d, 0x01, 0x08,
                                   0xb8, 0x3a, 0x73, 0xe9, 0x85, 0x5f, 0xa7, 0x2b,
                                   0x63, 0x7f, 0xd0, 0xc6, 0x4c, 0xdc, 0xfc, 0x8b,
                                   0xa6, 0x03, 0xc9, 0x9c, 0x80, 0x5e, 0xec, 0xc6,
                                   0x21, 0x23, 0xf7, 0x8e, 0xa4, 0x7b, 0x77, 0x83,
                                   0x02, 0x44, 0xf8, 0x05, 0xd7, 0x36, 0x52, 0x13,
                                   0x57, 0x78, 0x97, 0xf3, 0x7b, 0xcf, 0x1f, 0xc9,
                                   0x2a, 0xa4, 0x71, 0x9d, 0xa8, 0xd8, 0x5d, 0xc5,
                                   0x3b, 0x64, 0x3a, 0x72, 0x60, 0x62, 0xb0, 0xb8,
                                   0xf3, 0xb1, 0xe7, 0xb9, 0x76, 0xdf, 0x74, 0xbe,
                                   0x87, 0x6a, 0xd2, 0xf1, 0xa9, 0x44, 0x8b, 0x63,
                                   0x76, 0x4f, 0x5d, 0x21, 0x63, 0xb5, 0x4f, 0x3c,
                                   0x7b, 0x61, 0xb2, 0xf3, 0xea, 0xc5, 0xd8, 0xef,
                                   0x30, 0x50, 0x59, 0x33, 0x61, 0xc0, 0xf3, 0x6e,
                                   0x21, 0xcf, 0x15, 0x35, 0x4a, 0x87, 0x2b, 0xc3,
                                   0xf6, 0x5a, 0x1f, 0x24, 0x22, 0xc5, 0xeb, 0x47,
                                   0x34, 0x4a, 0x1b, 0xb5, 0x2e, 0x71, 0x52, 0x8f,
                                   0x2d, 0x7d, 0xa9, 0x96, 0x8a, 0x7c, 0x61, 0xdb,
                                   0xc0, 0xdc, 0xf1, 0xca, 0x28, 0x69, 0x1c, 0x97,
                                   0xad, 0xea, 0x0d, 0x9e, 0x02, 0xe6, 0xe5, 0x7d,
                                   0xad, 0xe0, 0x42, 0x91, 0x4d, 0xfa, 0xe2, 0x81,
                                   0x16, 0x2b, 0xc2, 0x96, 0x3b, 0x32, 0x8c, 0x20,
                                   0x69, 0x8b, 0x5b, 0x17, 0x3c, 0xf9, 0x13, 0x6c,
                                   0x98, 0x27, 0x1c, 0xca, 0xcf, 0x33, 0xaa, 0x93,
                                   0x21, 0xaf, 0x17, 0x6e, 0x5e, 0x00, 0x37, 0xd9,
                                   0x34, 0x8a, 0x47, 0xd2, 0x1c, 0x67, 0x32, 0x60,
                                   0xb6, 0xc7, 0xb0, 0xfd, 0x32, 0x90, 0x93, 0x32,
                                   0xaa, 0x11, 0xba, 0x23, 0x19, 0x39, 0x6a, 0x42,
                                   0x7c, 0x1f, 0xb7, 0x28, 0xdb, 0x64, 0xad, 0xd9 };
static const unsigned char Q[] = { 0,
                                   0xe6, 0xa3, 0xc9, 0xc6, 0x51, 0x92, 0x8b, 0xb3,
                                   0x98, 0x8f, 0x97, 0xb8, 0x31, 0x0d, 0x4a, 0x03,
                                   0x1e, 0xba, 0x4e, 0xe6, 0xc8, 0x90, 0x98, 0x1d,
                                   0x3a, 0x95, 0xf4, 0xf1 };
static const unsigned char G[] = {
    0x70, 0x32, 0x58, 0x5d, 0xb3, 0xbf, 0xc3, 0x62,
    0x63, 0x0b, 0xf8, 0xa5, 0xe1, 0xed, 0xeb, 0x79,
    0xac, 0x18, 0x41, 0x64, 0xb3, 0xda, 0x4c, 0xa7,
    0x92, 0x63, 0xb1, 0x33, 0x7c, 0xcb, 0x43, 0xdc,
    0x1f, 0x38, 0x63, 0x5e, 0x0e, 0x6d, 0x45, 0xd1,
    0xc9, 0x67, 0xf3, 0xcf, 0x3d, 0x2d, 0x16, 0x4e,
    0x92, 0x16, 0x06, 0x59, 0x29, 0x89, 0x6f, 0x54,
    0xff, 0xc5, 0x71, 0xc8, 0x3a, 0x95, 0x84, 0xb6,
    0x7e, 0x7b, 0x1e, 0x8b, 0x47, 0x9d, 0x7a, 0x3a,
    0x36, 0x9b, 0x70, 0x2f, 0xd1, 0xbd, 0xef, 0xe8,
    0x3a, 0x41, 0xd4, 0xf3, 0x1f, 0x81, 0xc7, 0x1f,
    0x96, 0x7c, 0x30, 0xab, 0xf4, 0x7a, 0xac, 0x93,
    0xed, 0x6f, 0x67, 0xb0, 0xc9, 0x5b, 0xf3, 0x83,
    0x9d, 0xa0, 0xd7, 0xb9, 0x01, 0xed, 0x28, 0xae,
    0x1c, 0x6e, 0x2e, 0x48, 0xac, 0x9f, 0x7d, 0xf3,
    0x00, 0x48, 0xee, 0x0e, 0xfb, 0x7e, 0x5e, 0xcb,
    0xf5, 0x39, 0xd8, 0x92, 0x90, 0x61, 0x2d, 0x1e,
    0x3c, 0xd3, 0x55, 0x0d, 0x34, 0xd1, 0x81, 0xc4,
    0x89, 0xea, 0x94, 0x2b, 0x56, 0x33, 0x73, 0x58,
    0x48, 0xbf, 0x23, 0x72, 0x19, 0x5f, 0x19, 0xac,
    0xff, 0x09, 0xc8, 0xcd, 0xab, 0x71, 0xef, 0x9e,
    0x20, 0xfd, 0xe3, 0xb8, 0x27, 0x9e, 0x65, 0xb1,
    0x85, 0xcd, 0x88, 0xfe, 0xd4, 0xd7, 0x64, 0x4d,
    0xe1, 0xe8, 0xa6, 0xe5, 0x96, 0xc8, 0x5d, 0x9c,
    0xc6, 0x70, 0x6b, 0xba, 0x77, 0x4e, 0x90, 0x4a,
    0xb0, 0x96, 0xc5, 0xa0, 0x9e, 0x2c, 0x01, 0x03,
    0xbe, 0xbd, 0x71, 0xba, 0x0a, 0x6f, 0x9f, 0xe5,
    0xdb, 0x04, 0x08, 0xf2, 0x9e, 0x0f, 0x1b, 0xac,
    0xcd, 0xbb, 0x65, 0x12, 0xcf, 0x77, 0xc9, 0x7d,
    0xbe, 0x94, 0x4b, 0x9c, 0x5b, 0xde, 0x0d, 0xfa,
    0x57, 0xdd, 0x77, 0x32, 0xf0, 0x5b, 0x34, 0xfd,
    0x19, 0x95, 0x33, 0x60, 0x87, 0xe2, 0xa2, 0xf4
};

/* P, Q, G have been generated using the NSS makepqg utility:
 *         makepqg -l 2048 -g 224 -r
 * (see also: bug 1170322)
 *
 *   h: 1 (0x1)
 *   SEED:
 *       d2:0b:c5:63:1b:af:dc:36:b7:7c:b9:3e:36:01:a0:8f:
 *       0e:be:d0:38:e4:78:d5:3c:7c:9e:a9:9a:d2:0b:c5:63:
 *       1b:af:dc:36:b7:7c:b9:3e:36:01:a0:8f:0e:be:d0:38:
 *       e4:78:d5:3c:7c:9e:c7:70:d2:0b:c5:63:1b:af:dc:36:
 *       b7:7c:b9:3e:36:01:a0:8f:0e:be:d0:38:e4:78:d5:3c:
 *       7c:9e:aa:3e
 *   g:       672
 *   counter: 0
 */

static const SECKEYPQGParams default_pqg_params = {
    NULL,
    { 0, (unsigned char *)P, sizeof(P) },
    { 0, (unsigned char *)Q, sizeof(Q) },
    { 0, (unsigned char *)G, sizeof(G) }
};

static SECKEYPQGParams *
decode_pqg_params(const char *str)
{
    char *buf;
    unsigned int len;
    PLArenaPool *arena;
    SECKEYPQGParams *params;
    SECStatus status;

    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    if (arena == NULL)
        return NULL;

    params = PORT_ArenaZAlloc(arena, sizeof(SECKEYPQGParams));
    if (params == NULL)
        goto loser;
    params->arena = arena;

    buf = (char *)ATOB_AsciiToData(str, &len);
    if ((buf == NULL) || (len == 0))
        goto loser;

    status = SEC_ASN1Decode(arena, params, SECKEY_PQGParamsTemplate, buf, len);
    if (status != SECSuccess)
        goto loser;

    return params;

loser:
    if (arena != NULL)
        PORT_FreeArena(arena, PR_FALSE);
    return NULL;
}

void
CERTUTIL_DestroyParamsPQG(SECKEYPQGParams *params)
{
    if (params->arena) {
        PORT_FreeArena(params->arena, PR_FALSE);
    }
}

static int
pqg_prime_bits(const SECKEYPQGParams *params)
{
    int primeBits = 0;

    if (params != NULL) {
        int i;
        for (i = 0; params->prime.data[i] == 0; i++) {
            /* empty */;
        }
        primeBits = (params->prime.len - i) * 8;
    }

    return primeBits;
}

static char *
getPQGString(const char *filename)
{
    unsigned char *buf = NULL;
    PRFileDesc *src;
    PRInt32 numBytes;
    PRStatus prStatus;
    PRFileInfo info;

    src = PR_Open(filename, PR_RDONLY, 0);
    if (!src) {
        fprintf(stderr, "Failed to open PQG file %s\n", filename);
        return NULL;
    }

    prStatus = PR_GetOpenFileInfo(src, &info);

    if (prStatus == PR_SUCCESS) {
        buf = (unsigned char *)PORT_Alloc(info.size + 1);
    }
    if (!buf) {
        PR_Close(src);
        fprintf(stderr, "Failed to read PQG file %s\n", filename);
        return NULL;
    }

    numBytes = PR_Read(src, buf, info.size);
    PR_Close(src);
    if (numBytes != info.size) {
        PORT_Free(buf);
        fprintf(stderr, "Failed to read PQG file %s\n", filename);
        PORT_SetError(SEC_ERROR_IO);
        return NULL;
    }

    if (buf[numBytes - 1] == '\n')
        numBytes--;
    if (buf[numBytes - 1] == '\r')
        numBytes--;
    buf[numBytes] = 0;

    return (char *)buf;
}

static SECKEYPQGParams *
getpqgfromfile(int keyBits, const char *pqgFile)
{
    char *end, *str, *pqgString;
    SECKEYPQGParams *params = NULL;

    str = pqgString = getPQGString(pqgFile);
    if (!str)
        return NULL;

    do {
        end = PORT_Strchr(str, ',');
        if (end)
            *end = '\0';
        params = decode_pqg_params(str);
        if (params) {
            int primeBits = pqg_prime_bits(params);
            if (keyBits == primeBits)
                break;
            CERTUTIL_DestroyParamsPQG(params);
            params = NULL;
        }
        if (end)
            str = end + 1;
    } while (end);

    PORT_Free(pqgString);
    return params;
}

static SECStatus
CERTUTIL_FileForRNG(const char *noise)
{
    char buf[2048];
    PRFileDesc *fd;
    PRInt32 count;

    fd = PR_Open(noise, PR_RDONLY, 0);
    if (!fd) {
        fprintf(stderr, "failed to open noise file.");
        return SECFailure;
    }

    do {
        count = PR_Read(fd, buf, sizeof(buf));
        if (count > 0) {
            PK11_RandomUpdate(buf, count);
        }
    } while (count > 0);

    PR_Close(fd);
    return SECSuccess;
}

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

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 },
      { "curve25519", SEC_OID_CURVE25519 },
    };

static SECKEYECParams *
getECParams(const 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;
}

SECKEYPrivateKey *
CERTUTIL_GeneratePrivateKey(KeyType keytype, PK11SlotInfo *slot, int size,
                            int publicExponent, const char *noise,
                            SECKEYPublicKey **pubkeyp, const char *pqgFile,
                            PK11AttrFlags attrFlags, CK_FLAGS opFlagsOn,
                            CK_FLAGS opFlagsOff, secuPWData *pwdata)
{
    CK_MECHANISM_TYPE mechanism;
    PK11RSAGenParams rsaparams;
    SECKEYPQGParams *dsaparams = NULL;
    void *params;
    SECKEYPrivateKey *privKey = NULL;

    if (slot == NULL)
        return NULL;

    if (PK11_Authenticate(slot, PR_TRUE, pwdata) != SECSuccess)
        return NULL;

    /*
     * Do some random-number initialization.
     */

    if (noise) {
        SECStatus rv = CERTUTIL_FileForRNG(noise);
        if (rv != SECSuccess) {
            PORT_SetError(PR_END_OF_FILE_ERROR); /* XXX */
            return NULL;
        }
    } else {
        int rv = UpdateRNG();
        if (rv) {
            PORT_SetError(PR_END_OF_FILE_ERROR);
            return NULL;
        }
    }

    switch (keytype) {
        case rsaKey:
            rsaparams.keySizeInBits = size;
            rsaparams.pe = publicExponent;
            mechanism = CKM_RSA_PKCS_KEY_PAIR_GEN;
            params = &rsaparams;
            break;
        case dsaKey:
            mechanism = CKM_DSA_KEY_PAIR_GEN;
            if (pqgFile) {
                dsaparams = getpqgfromfile(size, pqgFile);
                if (dsaparams == NULL)
                    return NULL;
                params = dsaparams;
            } else {
                /* cast away const, and don't set dsaparams */
                params = (void *)&default_pqg_params;
            }
            break;
        case ecKey:
            mechanism = CKM_EC_KEY_PAIR_GEN;
            /* For EC keys, PQGFile determines EC parameters */
            if ((params = (void *)getECParams(pqgFile)) == NULL)
                return NULL;
            break;
        default:
            return NULL;
    }

    fprintf(stderr, "\n\n");
    fprintf(stderr, "Generating key.  This may take a few moments...\n\n");

    privKey = PK11_GenerateKeyPairWithOpFlags(slot, mechanism, params, pubkeyp,
                                              attrFlags, opFlagsOn, opFlagsOn | opFlagsOff,
                                              pwdata /*wincx*/);
    /* free up the params */
    switch (keytype) {
        case dsaKey:
            if (dsaparams)
                CERTUTIL_DestroyParamsPQG(dsaparams);
            break;
        case ecKey:
            SECITEM_FreeItem((SECItem *)params, PR_TRUE);
            break;
        default: /* nothing to free */
            break;
    }
    return privKey;
}
