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

#ifdef FREEBL_NO_DEPEND
#include "stubs.h"
#endif

#include "prerror.h"
#include "secerr.h"

#include "prtypes.h"
#include "prinit.h"
#include "blapi.h"
#include "blapii.h"
#include "nssilock.h"
#include "secitem.h"
#include "sha_fast.h"
#include "sha256.h"
#include "secrng.h" /* for RNG_SystemRNG() */
#include "secmpi.h"

/* PRNG_SEEDLEN defined in NIST SP 800-90 section 10.1
 * for SHA-1, SHA-224, and SHA-256 it's 440 bits.
 * for SHA-384 and SHA-512 it's 888 bits */
#define PRNG_SEEDLEN (440 / PR_BITS_PER_BYTE)
#define PRNG_MAX_ADDITIONAL_BYTES PR_INT64(0x100000000)
/* 2^35 bits or 2^32 bytes */
#define PRNG_MAX_REQUEST_SIZE 0x10000             /* 2^19 bits or 2^16 bytes */
#define PRNG_ADDITONAL_DATA_CACHE_SIZE (8 * 1024) /* must be less than          \
                                                   *  PRNG_MAX_ADDITIONAL_BYTES \
                                                   */
#define PRNG_ENTROPY_BLOCK_SIZE SHA256_LENGTH

/* RESEED_COUNT is how many calls to the prng before we need to reseed
 * under normal NIST rules, you must return an error. In the NSS case, we
 * self-reseed with RNG_SystemRNG(). Count can be a large number. For code
 * simplicity, we specify count with 2 components: RESEED_BYTE (which is
 * the same as LOG256(RESEED_COUNT)) and RESEED_VALUE (which is the same as
 * RESEED_COUNT / (256 ^ RESEED_BYTE)). Another way to look at this is
 * RESEED_COUNT = RESEED_VALUE * (256 ^ RESEED_BYTE). For Hash based DRBG
 * we use the maximum count value, 2^48, or RESEED_BYTE=6 and RESEED_VALUE=1
 */
#define RESEED_BYTE 6
#define RESEED_VALUE 1

#define PRNG_RESET_RESEED_COUNT(rng)                                    \
    PORT_Memset((rng)->reseed_counter, 0, sizeof(rng)->reseed_counter); \
    (rng)->reseed_counter[RESEED_BYTE] = 1;

/*
 * The actual values of this enum are specified in SP 800-90, 10.1.1.*
 * The spec does not name the types, it only uses bare values
 */
typedef enum {
    prngCGenerateType = 0,      /* used when creating a new 'C' */
    prngReseedType = 1,         /* used in reseeding */
    prngAdditionalDataType = 2, /* used in mixing additional data */
    prngGenerateByteType = 3    /* used when mixing internal state while
                 * generating bytes */
} prngVTypes;

/*
 * Global RNG context
 */
struct RNGContextStr {
    PZLock *lock; /* Lock to serialize access to global rng */
    /*
     * NOTE, a number of steps in the drbg algorithm need to hash
     * V_type || V. The code, therefore, depends on the V array following
     * immediately after V_type to avoid extra copies. To accomplish this
     * in a way that compiliers can't perturb, we declare V_type and V
     * as a V_Data array and reference them by macros */
    PRUint8 V_Data[PRNG_SEEDLEN + 1]; /* internal state variables */
#define V_type V_Data[0]
#define V(rng) (((rng)->V_Data) + 1)
#define VSize(rng) ((sizeof(rng)->V_Data) - 1)
    PRUint8 C[PRNG_SEEDLEN]; /* internal state variables */
    /* If we get calls for the PRNG to return less than the length of our
     * hash, we extend the request for a full hash (since we'll be doing
     * the full hash anyway). Future requests for random numbers are fulfilled
     * from the remainder of the bytes we generated. Requests for bytes longer
     * than the hash size are fulfilled directly from the HashGen function
     * of the random number generator. */
    PRUint8 reseed_counter[RESEED_BYTE + 1]; /* number of requests since the
                                              * last reseed. Need only be
                                              * big enough to hold the whole
                                              * reseed count */
    PRUint8 data[SHA256_LENGTH];             /* when we request less than a block
                                              * save the rest of the rng output for
                                              * another partial block */
    PRUint8 dataAvail;                       /* # bytes of output available in our cache,
                                              * [0...SHA256_LENGTH] */
    /* store additional data that has been shovelled off to us by
     * RNG_RandomUpdate. */
    PRUint8 additionalDataCache[PRNG_ADDITONAL_DATA_CACHE_SIZE];
    PRUint32 additionalAvail;
    PRBool isValid;   /* false if RNG reaches an invalid state */
    PRBool isKatTest; /* true if running NIST PRNG KAT tests */
    /* for continuous entropy check */
    PRUint8 previousEntropyHash[SHA256_LENGTH];
};

typedef struct RNGContextStr RNGContext;
static RNGContext *globalrng = NULL;
static RNGContext theGlobalRng;

/*
 * The next several functions are derived from the NIST SP 800-90
 * spec. In these functions, an attempt was made to use names consistent
 * with the names in the spec, even if they differ from normal NSS usage.
 */

/*
 * Hash Derive function defined in NISP SP 800-90 Section 10.4.1.
 * This function is used in the Instantiate and Reseed functions.
 *
 * NOTE: requested_bytes cannot overlap with input_string_1 or input_string_2.
 * input_string_1 and input_string_2 are logically concatentated.
 * input_string_1 must be supplied.
 * if input_string_2 is not supplied, NULL should be passed for this parameter.
 */
static SECStatus
prng_Hash_df(PRUint8 *requested_bytes, unsigned int no_of_bytes_to_return,
             const PRUint8 *input_string_1, unsigned int input_string_1_len,
             const PRUint8 *input_string_2, unsigned int input_string_2_len)
{
    SHA256Context ctx;
    PRUint32 tmp;
    PRUint8 counter;

    tmp = SHA_HTONL(no_of_bytes_to_return * 8);

    for (counter = 1; no_of_bytes_to_return > 0; counter++) {
        unsigned int hash_return_len;
        SHA256_Begin(&ctx);
        SHA256_Update(&ctx, &counter, 1);
        SHA256_Update(&ctx, (unsigned char *)&tmp, sizeof tmp);
        SHA256_Update(&ctx, input_string_1, input_string_1_len);
        if (input_string_2) {
            SHA256_Update(&ctx, input_string_2, input_string_2_len);
        }
        SHA256_End(&ctx, requested_bytes, &hash_return_len,
                   no_of_bytes_to_return);
        requested_bytes += hash_return_len;
        no_of_bytes_to_return -= hash_return_len;
    }
    SHA256_DestroyContext(&ctx, PR_FALSE);
    return SECSuccess;
}

/*
 * Hash_DRBG Instantiate NIST SP 800-90 10.1.1.2
 *
 * NOTE: bytes & len are entropy || nonce || personalization_string. In
 * normal operation, NSS calculates them all together in a single call.
 */
static SECStatus
prng_instantiate(RNGContext *rng, const PRUint8 *bytes, unsigned int len)
{
    if (!rng->isKatTest && len < PRNG_SEEDLEN) {
        /* If the seedlen is too small, it's probably because we failed to get
         * enough random data.
         * This is stricter than NIST SP800-90A requires. Don't enforce it for
         * tests. */
        PORT_SetError(SEC_ERROR_NEED_RANDOM);
        return SECFailure;
    }
    prng_Hash_df(V(rng), VSize(rng), bytes, len, NULL, 0);
    rng->V_type = prngCGenerateType;
    prng_Hash_df(rng->C, sizeof rng->C, rng->V_Data, sizeof rng->V_Data, NULL, 0);
    PRNG_RESET_RESEED_COUNT(rng)
    return SECSuccess;
}

static PRCallOnceType coRNGInitEntropy;

static PRStatus
prng_initEntropy(void)
{
    size_t length;
    PRUint8 block[PRNG_ENTROPY_BLOCK_SIZE];
    SHA256Context ctx;

    /* For FIPS 140-2 4.9.2 continuous random number generator test,
     * fetch the initial entropy from the system RNG and keep it for
     * later comparison. */
    length = RNG_SystemRNG(block, sizeof(block));
    if (length == 0) {
        return PR_FAILURE; /* error is already set */
    }
    PORT_Assert(length == sizeof(block));

    /* Store the hash of the entropy block rather than the block
     * itself for backward secrecy. */
    SHA256_Begin(&ctx);
    SHA256_Update(&ctx, block, sizeof(block));
    SHA256_End(&ctx, globalrng->previousEntropyHash, NULL,
               sizeof(globalrng->previousEntropyHash));
    PORT_Memset(block, 0, sizeof(block));
    SHA256_DestroyContext(&ctx, PR_FALSE);
    return PR_SUCCESS;
}

static SECStatus
prng_getEntropy(PRUint8 *buffer, size_t requestLength)
{
    size_t total = 0;
    PRUint8 block[PRNG_ENTROPY_BLOCK_SIZE];
    PRUint8 hash[SHA256_LENGTH];
    SHA256Context ctx;
    SECStatus rv = SECSuccess;

    if (PR_CallOnce(&coRNGInitEntropy, prng_initEntropy) != PR_SUCCESS) {
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }

    /* For FIPS 140-2 4.9.2 continuous random generator test,
     * iteratively fetch fixed sized blocks from the system and
     * compare consecutive blocks. */
    while (total < requestLength) {
        size_t length = RNG_SystemRNG(block, sizeof(block));
        if (length == 0) {
            rv = SECFailure; /* error is already set */
            goto out;
        }
        PORT_Assert(length == sizeof(block));

        /* Store the hash of the entropy block rather than the block
         * itself for backward secrecy. */
        SHA256_Begin(&ctx);
        SHA256_Update(&ctx, block, sizeof(block));
        SHA256_End(&ctx, hash, NULL, sizeof(hash));

        if (PORT_Memcmp(globalrng->previousEntropyHash, hash, sizeof(hash)) == 0) {
            PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
            rv = SECFailure;
            goto out;
        }
        PORT_Memcpy(globalrng->previousEntropyHash, hash, sizeof(hash));
        length = PR_MIN(requestLength - total, sizeof(block));
        PORT_Memcpy(buffer, block, length);
        total += length;
        buffer += length;
    }

out:
    PORT_Memset(hash, 0, sizeof hash);
    PORT_Memset(block, 0, sizeof block);
    return rv;
}

/*
 * Update the global random number generator with more seeding
 * material. Use the Hash_DRBG reseed algorithm from NIST SP-800-90
 * section 10.1.1.3
 *
 * If entropy is NULL, it is fetched from the noise generator.
 */
static SECStatus
prng_reseed(RNGContext *rng, const PRUint8 *entropy, unsigned int entropy_len,
            const PRUint8 *additional_input, unsigned int additional_input_len)
{
    PRUint8 noiseData[(sizeof rng->V_Data) + PRNG_SEEDLEN];
    PRUint8 *noise = &noiseData[0];
    SECStatus rv;

    /* if entropy wasn't supplied, fetch it. (normal operation case) */
    if (entropy == NULL) {
        entropy_len = PRNG_SEEDLEN;
        rv = prng_getEntropy(&noiseData[sizeof rng->V_Data], entropy_len);
        if (rv != SECSuccess) {
            return SECFailure; /* error is already set */
        }
    } else {
        /* NOTE: this code is only available for testing, not to applications */
        /* if entropy was too big for the stack variable, get it from malloc */
        if (entropy_len > PRNG_SEEDLEN) {
            noise = PORT_Alloc(entropy_len + (sizeof rng->V_Data));
            if (noise == NULL) {
                return SECFailure;
            }
        }
        PORT_Memcpy(&noise[sizeof rng->V_Data], entropy, entropy_len);
    }

    if (entropy_len < 256 / PR_BITS_PER_BYTE) {
        /* noise == &noiseData[0] at this point, so nothing to free */
        PORT_SetError(SEC_ERROR_NEED_RANDOM);
        return SECFailure;
    }

    rng->V_type = prngReseedType;
    PORT_Memcpy(noise, rng->V_Data, sizeof rng->V_Data);
    prng_Hash_df(V(rng), VSize(rng), noise, (sizeof rng->V_Data) + entropy_len,
                 additional_input, additional_input_len);
    /* clear potential CSP */
    PORT_Memset(noise, 0, (sizeof rng->V_Data) + entropy_len);
    rng->V_type = prngCGenerateType;
    prng_Hash_df(rng->C, sizeof rng->C, rng->V_Data, sizeof rng->V_Data, NULL, 0);
    PRNG_RESET_RESEED_COUNT(rng)

    if (noise != &noiseData[0]) {
        PORT_Free(noise);
    }
    return SECSuccess;
}

/*
 * SP 800-90 requires we rerun our health tests on reseed
 */
static SECStatus
prng_reseed_test(RNGContext *rng, const PRUint8 *entropy,
                 unsigned int entropy_len, const PRUint8 *additional_input,
                 unsigned int additional_input_len)
{
    SECStatus rv;

    /* do health checks in FIPS mode */
    rv = PRNGTEST_RunHealthTests();
    if (rv != SECSuccess) {
        /* error set by PRNGTEST_RunHealTests() */
        rng->isValid = PR_FALSE;
        return SECFailure;
    }
    return prng_reseed(rng, entropy, entropy_len,
                       additional_input, additional_input_len);
}

/*
 * build some fast inline functions for adding.
 */
#define PRNG_ADD_CARRY_ONLY(dest, start, carry)    \
    {                                              \
        int k1;                                    \
        for (k1 = start; carry && k1 >= 0; k1--) { \
            carry = !(++dest[k1]);                 \
        }                                          \
    }

/*
 * NOTE: dest must be an array for the following to work.
 */
#define PRNG_ADD_BITS(dest, dest_len, add, len, carry)               \
    carry = 0;                                                       \
    PORT_Assert((dest_len) >= (len));                                \
    {                                                                \
        int k1, k2;                                                  \
        for (k1 = dest_len - 1, k2 = len - 1; k2 >= 0; --k1, --k2) { \
            carry += dest[k1] + add[k2];                             \
            dest[k1] = (PRUint8)carry;                               \
            carry >>= 8;                                             \
        }                                                            \
    }

#define PRNG_ADD_BITS_AND_CARRY(dest, dest_len, add, len, carry) \
    PRNG_ADD_BITS(dest, dest_len, add, len, carry)               \
    PRNG_ADD_CARRY_ONLY(dest, dest_len - len - 1, carry)

/*
 * This function expands the internal state of the prng to fulfill any number
 * of bytes we need for this request. We only use this call if we need more
 * than can be supplied by a single call to SHA256_HashBuf.
 *
 * This function is specified in NIST SP 800-90 section 10.1.1.4, Hashgen
 */
static void
prng_Hashgen(RNGContext *rng, PRUint8 *returned_bytes,
             unsigned int no_of_returned_bytes)
{
    PRUint8 data[VSize(rng)];
    PRUint8 thisHash[SHA256_LENGTH];

    PORT_Memcpy(data, V(rng), VSize(rng));
    while (no_of_returned_bytes) {
        SHA256Context ctx;
        unsigned int len;
        unsigned int carry;

        SHA256_Begin(&ctx);
        SHA256_Update(&ctx, data, sizeof data);
        SHA256_End(&ctx, thisHash, &len, SHA256_LENGTH);
        if (no_of_returned_bytes < SHA256_LENGTH) {
            len = no_of_returned_bytes;
        }
        PORT_Memcpy(returned_bytes, thisHash, len);
        returned_bytes += len;
        no_of_returned_bytes -= len;
        /* The carry parameter is a bool (increment or not).
     * This increments data if no_of_returned_bytes is not zero */
        carry = no_of_returned_bytes;
        PRNG_ADD_CARRY_ONLY(data, (sizeof data) - 1, carry);
        SHA256_DestroyContext(&ctx, PR_FALSE);
    }
    PORT_Memset(data, 0, sizeof data);
    PORT_Memset(thisHash, 0, sizeof thisHash);
}

/*
 * Generates new random bytes and advances the internal prng state.
 * additional bytes are only used in algorithm testing.
 *
 * This function is specified in NIST SP 800-90 section 10.1.1.4
 */
static SECStatus
prng_generateNewBytes(RNGContext *rng,
                      PRUint8 *returned_bytes, unsigned int no_of_returned_bytes,
                      const PRUint8 *additional_input,
                      unsigned int additional_input_len)
{
    PRUint8 H[SHA256_LENGTH]; /* both H and w since they
                   * aren't used concurrently */
    unsigned int carry;

    if (!rng->isValid) {
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }
    /* This code only triggers during tests, normal
     * prng operation does not use additional_input */
    if (additional_input) {
        SHA256Context ctx;
/* NIST SP 800-90 defines two temporaries in their calculations,
     * w and H. These temporaries are the same lengths, and used
     * at different times, so we use the following macro to collapse
     * them to the same variable, but keeping their unique names for
     * easy comparison to the spec */
#define w H
        rng->V_type = prngAdditionalDataType;
        SHA256_Begin(&ctx);
        SHA256_Update(&ctx, rng->V_Data, sizeof rng->V_Data);
        SHA256_Update(&ctx, additional_input, additional_input_len);
        SHA256_End(&ctx, w, NULL, sizeof w);
        PRNG_ADD_BITS_AND_CARRY(V(rng), VSize(rng), w, sizeof w, carry)
        PORT_Memset(w, 0, sizeof w);
        SHA256_DestroyContext(&ctx, PR_FALSE);
#undef w
    }

    if (no_of_returned_bytes == SHA256_LENGTH) {
        /* short_cut to hashbuf and a couple of copies and clears */
        SHA256_HashBuf(returned_bytes, V(rng), VSize(rng));
    } else {
        prng_Hashgen(rng, returned_bytes, no_of_returned_bytes);
    }
    /* advance our internal state... */
    rng->V_type = prngGenerateByteType;
    SHA256_HashBuf(H, rng->V_Data, sizeof rng->V_Data);
    PRNG_ADD_BITS_AND_CARRY(V(rng), VSize(rng), H, sizeof H, carry)
    PRNG_ADD_BITS(V(rng), VSize(rng), rng->C, sizeof rng->C, carry);
    PRNG_ADD_BITS_AND_CARRY(V(rng), VSize(rng), rng->reseed_counter,
                            sizeof rng->reseed_counter, carry)
    carry = 1;
    PRNG_ADD_CARRY_ONLY(rng->reseed_counter, (sizeof rng->reseed_counter) - 1, carry);

    /* if the prng failed, don't return any output, signal softoken */
    PORT_Memset(H, 0, sizeof H);
    if (!rng->isValid) {
        PORT_Memset(returned_bytes, 0, no_of_returned_bytes);
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }
    return SECSuccess;
}

/* Use NSPR to prevent RNG_RNGInit from being called from separate
 * threads, creating a race condition.
 */
static const PRCallOnceType pristineCallOnce;
static PRCallOnceType coRNGInit;
static PRStatus
rng_init(void)
{
    PRUint8 bytes[PRNG_SEEDLEN * 2]; /* entropy + nonce */
    SECStatus rv = SECSuccess;

    if (globalrng == NULL) {
        /* bytes needs to have enough space to hold
     * a SHA256 hash value. Blow up at compile time if this isn't true */
        PR_STATIC_ASSERT(sizeof(bytes) >= SHA256_LENGTH);
        /* create a new global RNG context */
        globalrng = &theGlobalRng;
        PORT_Assert(NULL == globalrng->lock);
        /* create a lock for it */
        globalrng->lock = PZ_NewLock(nssILockOther);
        if (globalrng->lock == NULL) {
            globalrng = NULL;
            PORT_SetError(PR_OUT_OF_MEMORY_ERROR);
            return PR_FAILURE;
        }

        /* Try to get some seed data for the RNG */
        rv = prng_getEntropy(bytes, sizeof bytes);
        if (rv == SECSuccess) {
            /* if this is our first call,  instantiate, otherwise reseed
             * prng_instantiate gets a new clean state, we want to mix
             * any previous entropy we may have collected */
            if (V(globalrng)[0] == 0) {
                rv = prng_instantiate(globalrng, bytes, sizeof bytes);
            } else {
                rv = prng_reseed_test(globalrng, bytes, sizeof bytes, NULL, 0);
            }
            memset(bytes, 0, sizeof bytes);
        } else {
            PZ_DestroyLock(globalrng->lock);
            globalrng->lock = NULL;
            globalrng = NULL;
            return PR_FAILURE;
        }
        if (rv != SECSuccess) {
            return PR_FAILURE;
        }

        /* the RNG is in a valid state */
        globalrng->isValid = PR_TRUE;
        globalrng->isKatTest = PR_FALSE;

        /* fetch one random value so that we can populate rng->oldV for our
         * continous random number test. */
        prng_generateNewBytes(globalrng, bytes, SHA256_LENGTH, NULL, 0);

        /* Fetch more entropy into the PRNG */
        RNG_SystemInfoForRNG();
    }
    return PR_SUCCESS;
}

/*
 * Clean up the global RNG context
 */
static void
prng_freeRNGContext(RNGContext *rng)
{
    PRUint8 inputhash[VSize(rng) + (sizeof rng->C)];

    /* destroy context lock */
    SKIP_AFTER_FORK(PZ_DestroyLock(globalrng->lock));

    /* zero global RNG context except for C & V to preserve entropy */
    prng_Hash_df(inputhash, sizeof rng->C, rng->C, sizeof rng->C, NULL, 0);
    prng_Hash_df(&inputhash[sizeof rng->C], VSize(rng), V(rng), VSize(rng),
                 NULL, 0);
    memset(rng, 0, sizeof *rng);
    memcpy(rng->C, inputhash, sizeof rng->C);
    memcpy(V(rng), &inputhash[sizeof rng->C], VSize(rng));

    memset(inputhash, 0, sizeof inputhash);
}

/*
 * Public functions
 */

/*
 * Initialize the global RNG context and give it some seed input taken
 * from the system.  This function is thread-safe and will only allow
 * the global context to be initialized once.  The seed input is likely
 * small, so it is imperative that RNG_RandomUpdate() be called with
 * additional seed data before the generator is used.  A good way to
 * provide the generator with additional entropy is to call
 * RNG_SystemInfoForRNG().  Note that C_Initialize() does exactly that.
 */
SECStatus
RNG_RNGInit(void)
{
    /* Allow only one call to initialize the context */
    PR_CallOnce(&coRNGInit, rng_init);
    /* Make sure there is a context */
    return (globalrng != NULL) ? SECSuccess : SECFailure;
}

/*
** Update the global random number generator with more seeding
** material.
*/
SECStatus
RNG_RandomUpdate(const void *data, size_t bytes)
{
    SECStatus rv;

    /* Make sure our assumption that size_t is unsigned is true */
    PR_STATIC_ASSERT(((size_t)-1) > (size_t)1);

#if defined(NS_PTR_GT_32) || (defined(NSS_USE_64) && !defined(NS_PTR_LE_32))
    /*
     * NIST 800-90 requires us to verify our inputs. This value can
     * come from the application, so we need to make sure it's within the
     * spec. The spec says it must be less than 2^32 bytes (2^35 bits).
     * This can only happen if size_t is greater than 32 bits (i.e. on
     * most 64 bit platforms). The 90% case (perhaps 100% case), size_t
     * is less than or equal to 32 bits if the platform is not 64 bits, and
     * greater than 32 bits if it is a 64 bit platform. The corner
     * cases are handled with explicit defines NS_PTR_GT_32 and NS_PTR_LE_32.
     *
     * In general, neither NS_PTR_GT_32 nor NS_PTR_LE_32 will need to be
     * defined. If you trip over the next two size ASSERTS at compile time,
     * you will need to define them for your platform.
     *
     * if 'sizeof(size_t) > 4' is triggered it means that we were expecting
     *   sizeof(size_t) to be greater than 4, but it wasn't. Setting
     *   NS_PTR_LE_32 will correct that mistake.
     *
     * if 'sizeof(size_t) <= 4' is triggered, it means that we were expecting
     *   sizeof(size_t) to be less than or equal to 4, but it wasn't. Setting
     *   NS_PTR_GT_32 will correct that mistake.
     */

    PR_STATIC_ASSERT(sizeof(size_t) > 4);

    if (bytes > (size_t)PRNG_MAX_ADDITIONAL_BYTES) {
        bytes = PRNG_MAX_ADDITIONAL_BYTES;
    }
#else
    PR_STATIC_ASSERT(sizeof(size_t) <= 4);
#endif

    PZ_Lock(globalrng->lock);
    /* if we're passed more than our additionalDataCache, simply
     * call reseed with that data */
    if (bytes > sizeof(globalrng->additionalDataCache)) {
        rv = prng_reseed_test(globalrng, NULL, 0, data, (unsigned int)bytes);
        /* if we aren't going to fill or overflow the buffer, just cache it */
    } else if (bytes < ((sizeof globalrng->additionalDataCache) - globalrng->additionalAvail)) {
        PORT_Memcpy(globalrng->additionalDataCache + globalrng->additionalAvail,
                    data, bytes);
        globalrng->additionalAvail += (PRUint32)bytes;
        rv = SECSuccess;
    } else {
        /* we are going to fill or overflow the buffer. In this case we will
         * fill the entropy buffer, reseed with it, start a new buffer with the
         * remainder. We know the remainder will fit in the buffer because
         * we already handled the case where bytes > the size of the buffer.
         */
        size_t bufRemain = (sizeof globalrng->additionalDataCache) - globalrng->additionalAvail;
        /* fill the rest of the buffer */
        if (bufRemain) {
            PORT_Memcpy(globalrng->additionalDataCache + globalrng->additionalAvail,
                        data, bufRemain);
            data = ((unsigned char *)data) + bufRemain;
            bytes -= bufRemain;
        }
        /* reseed from buffer */
        rv = prng_reseed_test(globalrng, NULL, 0,
                              globalrng->additionalDataCache,
                              sizeof globalrng->additionalDataCache);

        /* copy the rest into the cache */
        PORT_Memcpy(globalrng->additionalDataCache, data, bytes);
        globalrng->additionalAvail = (PRUint32)bytes;
    }

    PZ_Unlock(globalrng->lock);
    return rv;
}

/*
** Generate some random bytes, using the global random number generator
** object.
*/
static SECStatus
prng_GenerateGlobalRandomBytes(RNGContext *rng,
                               void *dest, size_t len)
{
    SECStatus rv = SECSuccess;
    PRUint8 *output = dest;
    /* check for a valid global RNG context */
    PORT_Assert(rng != NULL);
    if (rng == NULL) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return SECFailure;
    }
    /* FIPS limits the amount of entropy available in a single request */
    if (len > PRNG_MAX_REQUEST_SIZE) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return SECFailure;
    }
    /* --- LOCKED --- */
    PZ_Lock(rng->lock);
    /* Check the amount of seed data in the generator.  If not enough,
     * don't produce any data.
     */
    if (rng->reseed_counter[0] >= RESEED_VALUE) {
        rv = prng_reseed_test(rng, NULL, 0, NULL, 0);
        PZ_Unlock(rng->lock);
        if (rv != SECSuccess) {
            return rv;
        }
        RNG_SystemInfoForRNG();
        PZ_Lock(rng->lock);
    }
    /*
     * see if we have enough bytes to fulfill the request.
     */
    if (len <= rng->dataAvail) {
        memcpy(output, rng->data + ((sizeof rng->data) - rng->dataAvail), len);
        memset(rng->data + ((sizeof rng->data) - rng->dataAvail), 0, len);
        rng->dataAvail -= len;
        rv = SECSuccess;
        /* if we are asking for a small number of bytes, cache the rest of
     * the bytes */
    } else if (len < sizeof rng->data) {
        rv = prng_generateNewBytes(rng, rng->data, sizeof rng->data,
                                   rng->additionalAvail ? rng->additionalDataCache : NULL,
                                   rng->additionalAvail);
        rng->additionalAvail = 0;
        if (rv == SECSuccess) {
            memcpy(output, rng->data, len);
            memset(rng->data, 0, len);
            rng->dataAvail = (sizeof rng->data) - len;
        }
        /* we are asking for lots of bytes, just ask the generator to pass them */
    } else {
        rv = prng_generateNewBytes(rng, output, len,
                                   rng->additionalAvail ? rng->additionalDataCache : NULL,
                                   rng->additionalAvail);
        rng->additionalAvail = 0;
    }
    PZ_Unlock(rng->lock);
    /* --- UNLOCKED --- */
    return rv;
}

/*
** Generate some random bytes, using the global random number generator
** object.
*/
SECStatus
RNG_GenerateGlobalRandomBytes(void *dest, size_t len)
{
    return prng_GenerateGlobalRandomBytes(globalrng, dest, len);
}

void
RNG_RNGShutdown(void)
{
    /* check for a valid global RNG context */
    PORT_Assert(globalrng != NULL);
    if (globalrng == NULL) {
        /* Should set a "not initialized" error code. */
        PORT_SetError(SEC_ERROR_NO_MEMORY);
        return;
    }
    /* clear */
    prng_freeRNGContext(globalrng);
    globalrng = NULL;
    /* reset the callonce struct to allow a new call to RNG_RNGInit() */
    coRNGInit = pristineCallOnce;
}

/*
 * Test case interface. used by fips testing and power on self test
 */
/* make sure the test context is separate from the global context, This
  * allows us to test the internal random number generator without losing
  * entropy we may have previously collected. */
RNGContext testContext;

SECStatus
PRNGTEST_Instantiate_Kat(const PRUint8 *entropy, unsigned int entropy_len,
                         const PRUint8 *nonce, unsigned int nonce_len,
                         const PRUint8 *personal_string, unsigned int ps_len)
{
    testContext.isKatTest = PR_TRUE;
    return PRNGTEST_Instantiate(entropy, entropy_len,
                                nonce, nonce_len,
                                personal_string, ps_len);
}

/*
 * Test vector API. Use NIST SP 800-90 general interface so one of the
 * other NIST SP 800-90 algorithms may be used in the future.
 */
SECStatus
PRNGTEST_Instantiate(const PRUint8 *entropy, unsigned int entropy_len,
                     const PRUint8 *nonce, unsigned int nonce_len,
                     const PRUint8 *personal_string, unsigned int ps_len)
{
    int bytes_len = entropy_len + nonce_len + ps_len;
    PRUint8 *bytes = NULL;
    SECStatus rv;

    if (entropy_len < 256 / PR_BITS_PER_BYTE) {
        PORT_SetError(SEC_ERROR_NEED_RANDOM);
        return SECFailure;
    }

    bytes = PORT_Alloc(bytes_len);
    if (bytes == NULL) {
        PORT_SetError(SEC_ERROR_NO_MEMORY);
        return SECFailure;
    }
    /* concatenate the various inputs, internally NSS only instantiates with
    * a single long string */
    PORT_Memcpy(bytes, entropy, entropy_len);
    if (nonce) {
        PORT_Memcpy(&bytes[entropy_len], nonce, nonce_len);
    } else {
        PORT_Assert(nonce_len == 0);
    }
    if (personal_string) {
        PORT_Memcpy(&bytes[entropy_len + nonce_len], personal_string, ps_len);
    } else {
        PORT_Assert(ps_len == 0);
    }
    rv = prng_instantiate(&testContext, bytes, bytes_len);
    PORT_ZFree(bytes, bytes_len);
    if (rv == SECFailure) {
        return SECFailure;
    }
    testContext.isValid = PR_TRUE;
    return SECSuccess;
}

SECStatus
PRNGTEST_Reseed(const PRUint8 *entropy, unsigned int entropy_len,
                const PRUint8 *additional, unsigned int additional_len)
{
    if (!testContext.isValid) {
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }
    /* This magic input tells us to set the reseed count to it's max count,
     * so we can simulate PRNGTEST_Generate reaching max reseed count */
    if ((entropy == NULL) && (entropy_len == 0) &&
        (additional == NULL) && (additional_len == 0)) {
        testContext.reseed_counter[0] = RESEED_VALUE;
        return SECSuccess;
    }
    return prng_reseed(&testContext, entropy, entropy_len, additional,
                       additional_len);
}

SECStatus
PRNGTEST_Generate(PRUint8 *bytes, unsigned int bytes_len,
                  const PRUint8 *additional, unsigned int additional_len)
{
    SECStatus rv;
    if (!testContext.isValid) {
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }
    /* replicate reseed test from prng_GenerateGlobalRandomBytes */
    if (testContext.reseed_counter[0] >= RESEED_VALUE) {
        rv = prng_reseed(&testContext, NULL, 0, NULL, 0);
        if (rv != SECSuccess) {
            return rv;
        }
    }
    return prng_generateNewBytes(&testContext, bytes, bytes_len,
                                 additional, additional_len);
}

SECStatus
PRNGTEST_Uninstantiate()
{
    if (!testContext.isValid) {
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }
    PORT_Memset(&testContext, 0, sizeof testContext);
    return SECSuccess;
}

SECStatus
PRNGTEST_RunHealthTests()
{
    static const PRUint8 entropy[] = {
        0x8e, 0x9c, 0x0d, 0x25, 0x75, 0x22, 0x04, 0xf9,
        0xc5, 0x79, 0x10, 0x8b, 0x23, 0x79, 0x37, 0x14,
        0x9f, 0x2c, 0xc7, 0x0b, 0x39, 0xf8, 0xee, 0xef,
        0x95, 0x0c, 0x97, 0x59, 0xfc, 0x0a, 0x85, 0x41,
        0x76, 0x9d, 0x6d, 0x67, 0x00, 0x4e, 0x19, 0x12,
        0x02, 0x16, 0x53, 0xea, 0xf2, 0x73, 0xd7, 0xd6,
        0x7f, 0x7e, 0xc8, 0xae, 0x9c, 0x09, 0x99, 0x7d,
        0xbb, 0x9e, 0x48, 0x7f, 0xbb, 0x96, 0x46, 0xb3,
        0x03, 0x75, 0xf8, 0xc8, 0x69, 0x45, 0x3f, 0x97,
        0x5e, 0x2e, 0x48, 0xe1, 0x5d, 0x58, 0x97, 0x4c
    };
    static const PRUint8 rng_known_result[] = {
        0x16, 0xe1, 0x8c, 0x57, 0x21, 0xd8, 0xf1, 0x7e,
        0x5a, 0xa0, 0x16, 0x0b, 0x7e, 0xa6, 0x25, 0xb4,
        0x24, 0x19, 0xdb, 0x54, 0xfa, 0x35, 0x13, 0x66,
        0xbb, 0xaa, 0x2a, 0x1b, 0x22, 0x33, 0x2e, 0x4a,
        0x14, 0x07, 0x9d, 0x52, 0xfc, 0x73, 0x61, 0x48,
        0xac, 0xc1, 0x22, 0xfc, 0xa4, 0xfc, 0xac, 0xa4,
        0xdb, 0xda, 0x5b, 0x27, 0x33, 0xc4, 0xb3
    };
    static const PRUint8 reseed_entropy[] = {
        0xc6, 0x0b, 0x0a, 0x30, 0x67, 0x07, 0xf4, 0xe2,
        0x24, 0xa7, 0x51, 0x6f, 0x5f, 0x85, 0x3e, 0x5d,
        0x67, 0x97, 0xb8, 0x3b, 0x30, 0x9c, 0x7a, 0xb1,
        0x52, 0xc6, 0x1b, 0xc9, 0x46, 0xa8, 0x62, 0x79
    };
    static const PRUint8 additional_input[] = {
        0x86, 0x82, 0x28, 0x98, 0xe7, 0xcb, 0x01, 0x14,
        0xae, 0x87, 0x4b, 0x1d, 0x99, 0x1b, 0xc7, 0x41,
        0x33, 0xff, 0x33, 0x66, 0x40, 0x95, 0x54, 0xc6,
        0x67, 0x4d, 0x40, 0x2a, 0x1f, 0xf9, 0xeb, 0x65
    };
    static const PRUint8 rng_reseed_result[] = {
        0x02, 0x0c, 0xc6, 0x17, 0x86, 0x49, 0xba, 0xc4,
        0x7b, 0x71, 0x35, 0x05, 0xf0, 0xdb, 0x4a, 0xc2,
        0x2c, 0x38, 0xc1, 0xa4, 0x42, 0xe5, 0x46, 0x4a,
        0x7d, 0xf0, 0xbe, 0x47, 0x88, 0xb8, 0x0e, 0xc6,
        0x25, 0x2b, 0x1d, 0x13, 0xef, 0xa6, 0x87, 0x96,
        0xa3, 0x7d, 0x5b, 0x80, 0xc2, 0x38, 0x76, 0x61,
        0xc7, 0x80, 0x5d, 0x0f, 0x05, 0x76, 0x85
    };
    static const PRUint8 rng_no_reseed_result[] = {
        0xc4, 0x40, 0x41, 0x8c, 0xbf, 0x2f, 0x70, 0x23,
        0x88, 0xf2, 0x7b, 0x30, 0xc3, 0xca, 0x1e, 0xf3,
        0xef, 0x53, 0x81, 0x5d, 0x30, 0xed, 0x4c, 0xf1,
        0xff, 0x89, 0xa5, 0xee, 0x92, 0xf8, 0xc0, 0x0f,
        0x88, 0x53, 0xdf, 0xb6, 0x76, 0xf0, 0xaa, 0xd3,
        0x2e, 0x1d, 0x64, 0x37, 0x3e, 0xe8, 0x4a, 0x02,
        0xff, 0x0a, 0x7f, 0xe5, 0xe9, 0x2b, 0x6d
    };

    SECStatus rng_status = SECSuccess;
    PR_STATIC_ASSERT(sizeof(rng_known_result) >= sizeof(rng_reseed_result));
    PRUint8 result[sizeof(rng_known_result)];

    /********************************************/
    /*   First test instantiate error path.     */
    /*   In this case we supply enough entropy, */
    /*   but not enough seed. This will trigger */
    /*   the code that checks for a entropy     */
    /*   source failure.                        */
    /********************************************/
    rng_status = PRNGTEST_Instantiate(entropy, 256 / PR_BITS_PER_BYTE,
                                      NULL, 0, NULL, 0);
    if (rng_status == SECSuccess) {
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }
    if (PORT_GetError() != SEC_ERROR_NEED_RANDOM) {
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }
    /* we failed with the proper error code, we can continue */

    /********************************************/
    /* Generate random bytes with a known seed. */
    /********************************************/
    rng_status = PRNGTEST_Instantiate(entropy, sizeof entropy,
                                      NULL, 0, NULL, 0);
    if (rng_status != SECSuccess) {
        /* Error set by PRNGTEST_Instantiate */
        return SECFailure;
    }
    rng_status = PRNGTEST_Generate(result, sizeof rng_known_result, NULL, 0);
    if ((rng_status != SECSuccess) ||
        (PORT_Memcmp(result, rng_known_result,
                     sizeof rng_known_result) != 0)) {
        PRNGTEST_Uninstantiate();
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }
    rng_status = PRNGTEST_Reseed(reseed_entropy, sizeof reseed_entropy,
                                 additional_input, sizeof additional_input);
    if (rng_status != SECSuccess) {
        /* Error set by PRNG_Reseed */
        PRNGTEST_Uninstantiate();
        return SECFailure;
    }
    rng_status = PRNGTEST_Generate(result, sizeof rng_reseed_result, NULL, 0);
    if ((rng_status != SECSuccess) ||
        (PORT_Memcmp(result, rng_reseed_result,
                     sizeof rng_reseed_result) != 0)) {
        PRNGTEST_Uninstantiate();
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }
    /* This magic forces the reseed count to it's max count, so we can see if
     * PRNGTEST_Generate will actually when it reaches it's count */
    rng_status = PRNGTEST_Reseed(NULL, 0, NULL, 0);
    if (rng_status != SECSuccess) {
        PRNGTEST_Uninstantiate();
        /* Error set by PRNG_Reseed */
        return SECFailure;
    }
    /* This generate should now reseed */
    rng_status = PRNGTEST_Generate(result, sizeof rng_reseed_result, NULL, 0);
    if ((rng_status != SECSuccess) ||
        /* NOTE we fail if the result is equal to the no_reseed_result.
         * no_reseed_result is the value we would have gotten if we didn't
         * do an automatic reseed in PRNGTEST_Generate */
        (PORT_Memcmp(result, rng_no_reseed_result,
                     sizeof rng_no_reseed_result) == 0)) {
        PRNGTEST_Uninstantiate();
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }
    /* make sure reseed fails when we don't supply enough entropy */
    rng_status = PRNGTEST_Reseed(reseed_entropy, 4, NULL, 0);
    if (rng_status == SECSuccess) {
        PRNGTEST_Uninstantiate();
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }
    if (PORT_GetError() != SEC_ERROR_NEED_RANDOM) {
        PRNGTEST_Uninstantiate();
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }
    rng_status = PRNGTEST_Uninstantiate();
    if (rng_status != SECSuccess) {
        /* Error set by PRNG_Uninstantiate */
        return rng_status;
    }
    /* make sure uninstantiate fails if the contest is not initiated (also tests
     * if the context was cleared in the previous Uninstantiate) */
    rng_status = PRNGTEST_Uninstantiate();
    if (rng_status == SECSuccess) {
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }
    if (PORT_GetError() != SEC_ERROR_LIBRARY_FAILURE) {
        return rng_status;
    }

    return SECSuccess;
}
