/* ***** 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 Network Security Services libraries.
 *
 * The Initial Developer of the Original Code is Red Hat, Inc.
 * Portions created by the Initial Developer are Copyright (C) 2009
 * the Initial Developer. All Rights Reserved.
 *
 * Portions created by Netscape Communications Corporation
 * are Copyright (C) 1994-2000 Netscape Communications Corporation.
 * All Rights Reserved.
 *
 * Contributor(s):
 *
 * 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 ***** */
/* $Id: drbg.c,v 1.9 2009/06/10 03:24:01 rrelyea%redhat.com Exp $ */

#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)
static const PRInt64 PRNG_MAX_ADDITIONAL_BYTES = LL_INIT(0x1, 0x0);
						/* 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
						 */


/* 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 */
    PRUint8  oldV[PRNG_SEEDLEN];     /* for continuous rng checking */
    /* 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 */
};

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;
    }
    return SECSuccess;
}


/*
 * Hash_DRBG Instantiate NIST SP 800-80 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, PRUint8 *bytes, unsigned int len)
{
    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;
}
    

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

    /* if entropy wasn't supplied, fetch it. (normal operation case) */
    if (entropy == NULL) {
    	entropy_len = (unsigned int) RNG_SystemRNG(
			&noiseData[sizeof rng->V_Data], PRNG_SEEDLEN);
    } 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);
    }

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

/*
 * build some fast inline functions for adding.
 */
#define PRNG_ADD_CARRY_ONLY(dest, start, cy) \
   carry = cy; \
   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 = 0; \
    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) \
    PRNG_ADD_BITS(dest, dest_len, add, len) \
    PRNG_ADD_CARRY_ONLY(dest, k1, 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)];

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

 	SHA256_Begin(&ctx);
 	SHA256_Update(&ctx, data, sizeof data);
	SHA256_End(&ctx, returned_bytes, &len, no_of_returned_bytes);
	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 */
	PRNG_ADD_CARRY_ONLY(data, (sizeof data)- 1, no_of_returned_bytes);
    }
    PORT_Memset(data, 0, sizeof data); 
}

/* 
 * 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;
    int k1, k2;

    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)
	PORT_Memset(w, 0, sizeof w);
#undef w 
    }

    if (no_of_returned_bytes == SHA256_LENGTH) {
	/* short_cut to hashbuf and save a copy and a clear */
	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)
    PRNG_ADD_BITS(V(rng), VSize(rng), rng->C, sizeof rng->C);
    PRNG_ADD_BITS_AND_CARRY(V(rng), VSize(rng), rng->reseed_counter, 
					sizeof rng->reseed_counter)
    PRNG_ADD_CARRY_ONLY(rng->reseed_counter,(sizeof rng->reseed_counter)-1, 1);

    /* continuous rng check */
    if (memcmp(V(rng), rng->oldV, sizeof rng->oldV) == 0) {
	rng->isValid = PR_FALSE;
	PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
	return SECFailure;
    }
    PORT_Memcpy(rng->oldV, V(rng), sizeof rng->oldV);
    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 */
    unsigned int numBytes;
    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 */
	numBytes = (unsigned int) RNG_SystemRNG(bytes, sizeof bytes);
	PORT_Assert(numBytes == 0 || numBytes == sizeof bytes);
	if (numBytes != 0) {
	    /* 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) {
		prng_instantiate(globalrng, bytes, numBytes);
	    } else {
		prng_reseed(globalrng, bytes, numBytes, NULL, 0);
	    }
	    memset(bytes, 0, numBytes);
	} else {
	    PZ_DestroyLock(globalrng->lock);
	    globalrng->lock = NULL;
	    globalrng = NULL;
	    return PR_FAILURE;
	}
	/* the RNG is in a valid state */
	globalrng->isValid = PR_TRUE;

	/* 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) ? PR_SUCCESS : PR_FAILURE;
}

/*
** 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 > 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(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(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(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_LIBRARY_FAILURE);
	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;

/*
 * 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 = PORT_Alloc(bytes_len);

   if (bytes == NULL) {
	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);
   }
   prng_instantiate(&testContext, bytes, bytes_len);
   testContext.isValid = PR_TRUE;
   PORT_ZFree(bytes, bytes_len);
   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;
    }
    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)
{
    if (!testContext.isValid) {
	PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
	return SECFailure;
    }
    return prng_generateNewBytes(&testContext, bytes, bytes_len,
			additional, additional_len);

}

SECStatus
PRNGTEST_Uninstantiate()
{
   PORT_Memset(&testContext, 0, sizeof testContext);
   return SECSuccess;
}


