andre@0: /* This Source Code Form is subject to the terms of the Mozilla Public andre@0: * License, v. 2.0. If a copy of the MPL was not distributed with this andre@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ andre@0: andre@0: #ifdef FREEBL_NO_DEPEND andre@0: #include "stubs.h" andre@0: #endif andre@0: andre@0: #include "prerror.h" andre@0: #include "secerr.h" andre@0: andre@0: #include "prtypes.h" andre@0: #include "prinit.h" andre@0: #include "blapi.h" andre@0: #include "blapii.h" andre@0: #include "nssilock.h" andre@0: #include "secitem.h" andre@0: #include "sha_fast.h" andre@0: #include "sha256.h" andre@0: #include "secrng.h" /* for RNG_SystemRNG() */ andre@0: #include "secmpi.h" andre@0: andre@0: /* PRNG_SEEDLEN defined in NIST SP 800-90 section 10.1 andre@0: * for SHA-1, SHA-224, and SHA-256 it's 440 bits. andre@0: * for SHA-384 and SHA-512 it's 888 bits */ andre@0: #define PRNG_SEEDLEN (440/PR_BITS_PER_BYTE) andre@0: static const PRInt64 PRNG_MAX_ADDITIONAL_BYTES = LL_INIT(0x1, 0x0); andre@0: /* 2^35 bits or 2^32 bytes */ andre@0: #define PRNG_MAX_REQUEST_SIZE 0x10000 /* 2^19 bits or 2^16 bytes */ andre@0: #define PRNG_ADDITONAL_DATA_CACHE_SIZE (8*1024) /* must be less than andre@0: * PRNG_MAX_ADDITIONAL_BYTES andre@0: */ andre@0: andre@0: /* RESEED_COUNT is how many calls to the prng before we need to reseed andre@0: * under normal NIST rules, you must return an error. In the NSS case, we andre@0: * self-reseed with RNG_SystemRNG(). Count can be a large number. For code andre@0: * simplicity, we specify count with 2 components: RESEED_BYTE (which is andre@0: * the same as LOG256(RESEED_COUNT)) and RESEED_VALUE (which is the same as andre@0: * RESEED_COUNT / (256 ^ RESEED_BYTE)). Another way to look at this is andre@0: * RESEED_COUNT = RESEED_VALUE * (256 ^ RESEED_BYTE). For Hash based DRBG andre@0: * we use the maximum count value, 2^48, or RESEED_BYTE=6 and RESEED_VALUE=1 andre@0: */ andre@0: #define RESEED_BYTE 6 andre@0: #define RESEED_VALUE 1 andre@0: andre@0: #define PRNG_RESET_RESEED_COUNT(rng) \ andre@0: PORT_Memset((rng)->reseed_counter, 0, sizeof (rng)->reseed_counter); \ andre@0: (rng)->reseed_counter[RESEED_BYTE] = 1; andre@0: andre@0: andre@0: /* andre@0: * The actual values of this enum are specified in SP 800-90, 10.1.1.* andre@0: * The spec does not name the types, it only uses bare values andre@0: */ andre@0: typedef enum { andre@0: prngCGenerateType = 0, /* used when creating a new 'C' */ andre@0: prngReseedType = 1, /* used in reseeding */ andre@0: prngAdditionalDataType = 2, /* used in mixing additional data */ andre@0: prngGenerateByteType = 3 /* used when mixing internal state while andre@0: * generating bytes */ andre@0: } prngVTypes; andre@0: andre@0: /* andre@0: * Global RNG context andre@0: */ andre@0: struct RNGContextStr { andre@0: PZLock *lock; /* Lock to serialize access to global rng */ andre@0: /* andre@0: * NOTE, a number of steps in the drbg algorithm need to hash andre@0: * V_type || V. The code, therefore, depends on the V array following andre@0: * immediately after V_type to avoid extra copies. To accomplish this andre@0: * in a way that compiliers can't perturb, we declare V_type and V andre@0: * as a V_Data array and reference them by macros */ andre@0: PRUint8 V_Data[PRNG_SEEDLEN+1]; /* internal state variables */ andre@0: #define V_type V_Data[0] andre@0: #define V(rng) (((rng)->V_Data)+1) andre@0: #define VSize(rng) ((sizeof (rng)->V_Data) -1) andre@0: PRUint8 C[PRNG_SEEDLEN]; /* internal state variables */ andre@0: PRUint8 oldV[PRNG_SEEDLEN]; /* for continuous rng checking */ andre@0: /* If we get calls for the PRNG to return less than the length of our andre@0: * hash, we extend the request for a full hash (since we'll be doing andre@0: * the full hash anyway). Future requests for random numbers are fulfilled andre@0: * from the remainder of the bytes we generated. Requests for bytes longer andre@0: * than the hash size are fulfilled directly from the HashGen function andre@0: * of the random number generator. */ andre@0: PRUint8 reseed_counter[RESEED_BYTE+1]; /* number of requests since the andre@0: * last reseed. Need only be andre@0: * big enough to hold the whole andre@0: * reseed count */ andre@0: PRUint8 data[SHA256_LENGTH]; /* when we request less than a block andre@0: * save the rest of the rng output for andre@0: * another partial block */ andre@0: PRUint8 dataAvail; /* # bytes of output available in our cache, andre@0: * [0...SHA256_LENGTH] */ andre@0: /* store additional data that has been shovelled off to us by andre@0: * RNG_RandomUpdate. */ andre@0: PRUint8 additionalDataCache[PRNG_ADDITONAL_DATA_CACHE_SIZE]; andre@0: PRUint32 additionalAvail; andre@0: PRBool isValid; /* false if RNG reaches an invalid state */ andre@0: }; andre@0: andre@0: typedef struct RNGContextStr RNGContext; andre@0: static RNGContext *globalrng = NULL; andre@0: static RNGContext theGlobalRng; andre@0: andre@0: andre@0: /* andre@0: * The next several functions are derived from the NIST SP 800-90 andre@0: * spec. In these functions, an attempt was made to use names consistent andre@0: * with the names in the spec, even if they differ from normal NSS usage. andre@0: */ andre@0: andre@0: /* andre@0: * Hash Derive function defined in NISP SP 800-90 Section 10.4.1. andre@0: * This function is used in the Instantiate and Reseed functions. andre@0: * andre@0: * NOTE: requested_bytes cannot overlap with input_string_1 or input_string_2. andre@0: * input_string_1 and input_string_2 are logically concatentated. andre@0: * input_string_1 must be supplied. andre@0: * if input_string_2 is not supplied, NULL should be passed for this parameter. andre@0: */ andre@0: static SECStatus andre@0: prng_Hash_df(PRUint8 *requested_bytes, unsigned int no_of_bytes_to_return, andre@0: const PRUint8 *input_string_1, unsigned int input_string_1_len, andre@0: const PRUint8 *input_string_2, unsigned int input_string_2_len) andre@0: { andre@0: SHA256Context ctx; andre@0: PRUint32 tmp; andre@0: PRUint8 counter; andre@0: andre@0: tmp=SHA_HTONL(no_of_bytes_to_return*8); andre@0: andre@0: for (counter = 1 ; no_of_bytes_to_return > 0; counter++) { andre@0: unsigned int hash_return_len; andre@0: SHA256_Begin(&ctx); andre@0: SHA256_Update(&ctx, &counter, 1); andre@0: SHA256_Update(&ctx, (unsigned char *)&tmp, sizeof tmp); andre@0: SHA256_Update(&ctx, input_string_1, input_string_1_len); andre@0: if (input_string_2) { andre@0: SHA256_Update(&ctx, input_string_2, input_string_2_len); andre@0: } andre@0: SHA256_End(&ctx, requested_bytes, &hash_return_len, andre@0: no_of_bytes_to_return); andre@0: requested_bytes += hash_return_len; andre@0: no_of_bytes_to_return -= hash_return_len; andre@0: } andre@0: return SECSuccess; andre@0: } andre@0: andre@0: andre@0: /* andre@0: * Hash_DRBG Instantiate NIST SP 800-80 10.1.1.2 andre@0: * andre@0: * NOTE: bytes & len are entropy || nonce || personalization_string. In andre@0: * normal operation, NSS calculates them all together in a single call. andre@0: */ andre@0: static SECStatus andre@0: prng_instantiate(RNGContext *rng, const PRUint8 *bytes, unsigned int len) andre@0: { andre@0: if (len < PRNG_SEEDLEN) { andre@0: /* if the seedlen is to small, it's probably because we failed to get andre@0: * enough random data */ andre@0: PORT_SetError(SEC_ERROR_NEED_RANDOM); andre@0: return SECFailure; andre@0: } andre@0: prng_Hash_df(V(rng), VSize(rng), bytes, len, NULL, 0); andre@0: rng->V_type = prngCGenerateType; andre@0: prng_Hash_df(rng->C,sizeof rng->C,rng->V_Data,sizeof rng->V_Data,NULL,0); andre@0: PRNG_RESET_RESEED_COUNT(rng) andre@0: return SECSuccess; andre@0: } andre@0: andre@0: andre@0: /* andre@0: * Update the global random number generator with more seeding andre@0: * material. Use the Hash_DRBG reseed algorithm from NIST SP-800-90 andre@0: * section 10.1.1.3 andre@0: * andre@0: * If entropy is NULL, it is fetched from the noise generator. andre@0: */ andre@0: static SECStatus andre@0: prng_reseed(RNGContext *rng, const PRUint8 *entropy, unsigned int entropy_len, andre@0: const PRUint8 *additional_input, unsigned int additional_input_len) andre@0: { andre@0: PRUint8 noiseData[(sizeof rng->V_Data)+PRNG_SEEDLEN]; andre@0: PRUint8 *noise = &noiseData[0]; andre@0: andre@0: /* if entropy wasn't supplied, fetch it. (normal operation case) */ andre@0: if (entropy == NULL) { andre@0: entropy_len = (unsigned int) RNG_SystemRNG( andre@0: &noiseData[sizeof rng->V_Data], PRNG_SEEDLEN); andre@0: } else { andre@0: /* NOTE: this code is only available for testing, not to applications */ andre@0: /* if entropy was too big for the stack variable, get it from malloc */ andre@0: if (entropy_len > PRNG_SEEDLEN) { andre@0: noise = PORT_Alloc(entropy_len + (sizeof rng->V_Data)); andre@0: if (noise == NULL) { andre@0: return SECFailure; andre@0: } andre@0: } andre@0: PORT_Memcpy(&noise[sizeof rng->V_Data],entropy, entropy_len); andre@0: } andre@0: andre@0: if (entropy_len < 256/PR_BITS_PER_BYTE) { andre@0: /* noise == &noiseData[0] at this point, so nothing to free */ andre@0: PORT_SetError(SEC_ERROR_NEED_RANDOM); andre@0: return SECFailure; andre@0: } andre@0: andre@0: rng->V_type = prngReseedType; andre@0: PORT_Memcpy(noise, rng->V_Data, sizeof rng->V_Data); andre@0: prng_Hash_df(V(rng), VSize(rng), noise, (sizeof rng->V_Data) + entropy_len, andre@0: additional_input, additional_input_len); andre@0: /* clear potential CSP */ andre@0: PORT_Memset(noise, 0, (sizeof rng->V_Data) + entropy_len); andre@0: rng->V_type = prngCGenerateType; andre@0: prng_Hash_df(rng->C,sizeof rng->C,rng->V_Data,sizeof rng->V_Data,NULL,0); andre@0: PRNG_RESET_RESEED_COUNT(rng) andre@0: andre@0: if (noise != &noiseData[0]) { andre@0: PORT_Free(noise); andre@0: } andre@0: return SECSuccess; andre@0: } andre@0: andre@0: /* andre@0: * SP 800-90 requires we rerun our health tests on reseed andre@0: */ andre@0: static SECStatus andre@0: prng_reseed_test(RNGContext *rng, const PRUint8 *entropy, andre@0: unsigned int entropy_len, const PRUint8 *additional_input, andre@0: unsigned int additional_input_len) andre@0: { andre@0: SECStatus rv; andre@0: andre@0: /* do health checks in FIPS mode */ andre@0: rv = PRNGTEST_RunHealthTests(); andre@0: if (rv != SECSuccess) { andre@0: /* error set by PRNGTEST_RunHealTests() */ andre@0: rng->isValid = PR_FALSE; andre@0: return SECFailure; andre@0: } andre@0: return prng_reseed(rng, entropy, entropy_len, andre@0: additional_input, additional_input_len); andre@0: } andre@0: andre@0: /* andre@0: * build some fast inline functions for adding. andre@0: */ andre@0: #define PRNG_ADD_CARRY_ONLY(dest, start, cy) \ andre@0: carry = cy; \ andre@0: for (k1=start; carry && k1 >=0 ; k1--) { \ andre@0: carry = !(++dest[k1]); \ andre@0: } andre@0: andre@0: /* andre@0: * NOTE: dest must be an array for the following to work. andre@0: */ andre@0: #define PRNG_ADD_BITS(dest, dest_len, add, len) \ andre@0: carry = 0; \ andre@0: for (k1=dest_len -1, k2=len-1; k2 >= 0; --k1, --k2) { \ andre@0: carry += dest[k1]+ add[k2]; \ andre@0: dest[k1] = (PRUint8) carry; \ andre@0: carry >>= 8; \ andre@0: } andre@0: andre@0: #define PRNG_ADD_BITS_AND_CARRY(dest, dest_len, add, len) \ andre@0: PRNG_ADD_BITS(dest, dest_len, add, len) \ andre@0: PRNG_ADD_CARRY_ONLY(dest, k1, carry) andre@0: andre@0: /* andre@0: * This function expands the internal state of the prng to fulfill any number andre@0: * of bytes we need for this request. We only use this call if we need more andre@0: * than can be supplied by a single call to SHA256_HashBuf. andre@0: * andre@0: * This function is specified in NIST SP 800-90 section 10.1.1.4, Hashgen andre@0: */ andre@0: static void andre@0: prng_Hashgen(RNGContext *rng, PRUint8 *returned_bytes, andre@0: unsigned int no_of_returned_bytes) andre@0: { andre@0: PRUint8 data[VSize(rng)]; andre@0: andre@0: PORT_Memcpy(data, V(rng), VSize(rng)); andre@0: while (no_of_returned_bytes) { andre@0: SHA256Context ctx; andre@0: unsigned int len; andre@0: unsigned int carry; andre@0: int k1; andre@0: andre@0: SHA256_Begin(&ctx); andre@0: SHA256_Update(&ctx, data, sizeof data); andre@0: SHA256_End(&ctx, returned_bytes, &len, no_of_returned_bytes); andre@0: returned_bytes += len; andre@0: no_of_returned_bytes -= len; andre@0: /* The carry parameter is a bool (increment or not). andre@0: * This increments data if no_of_returned_bytes is not zero */ andre@0: PRNG_ADD_CARRY_ONLY(data, (sizeof data)- 1, no_of_returned_bytes); andre@0: } andre@0: PORT_Memset(data, 0, sizeof data); andre@0: } andre@0: andre@0: /* andre@0: * Generates new random bytes and advances the internal prng state. andre@0: * additional bytes are only used in algorithm testing. andre@0: * andre@0: * This function is specified in NIST SP 800-90 section 10.1.1.4 andre@0: */ andre@0: static SECStatus andre@0: prng_generateNewBytes(RNGContext *rng, andre@0: PRUint8 *returned_bytes, unsigned int no_of_returned_bytes, andre@0: const PRUint8 *additional_input, andre@0: unsigned int additional_input_len) andre@0: { andre@0: PRUint8 H[SHA256_LENGTH]; /* both H and w since they andre@0: * aren't used concurrently */ andre@0: unsigned int carry; andre@0: int k1, k2; andre@0: andre@0: if (!rng->isValid) { andre@0: PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); andre@0: return SECFailure; andre@0: } andre@0: /* This code only triggers during tests, normal andre@0: * prng operation does not use additional_input */ andre@0: if (additional_input){ andre@0: SHA256Context ctx; andre@0: /* NIST SP 800-90 defines two temporaries in their calculations, andre@0: * w and H. These temporaries are the same lengths, and used andre@0: * at different times, so we use the following macro to collapse andre@0: * them to the same variable, but keeping their unique names for andre@0: * easy comparison to the spec */ andre@0: #define w H andre@0: rng->V_type = prngAdditionalDataType; andre@0: SHA256_Begin(&ctx); andre@0: SHA256_Update(&ctx, rng->V_Data, sizeof rng->V_Data); andre@0: SHA256_Update(&ctx, additional_input, additional_input_len); andre@0: SHA256_End(&ctx, w, NULL, sizeof w); andre@0: PRNG_ADD_BITS_AND_CARRY(V(rng), VSize(rng), w, sizeof w) andre@0: PORT_Memset(w, 0, sizeof w); andre@0: #undef w andre@0: } andre@0: andre@0: if (no_of_returned_bytes == SHA256_LENGTH) { andre@0: /* short_cut to hashbuf and save a copy and a clear */ andre@0: SHA256_HashBuf(returned_bytes, V(rng), VSize(rng) ); andre@0: } else { andre@0: prng_Hashgen(rng, returned_bytes, no_of_returned_bytes); andre@0: } andre@0: /* advance our internal state... */ andre@0: rng->V_type = prngGenerateByteType; andre@0: SHA256_HashBuf(H, rng->V_Data, sizeof rng->V_Data); andre@0: PRNG_ADD_BITS_AND_CARRY(V(rng), VSize(rng), H, sizeof H) andre@0: PRNG_ADD_BITS(V(rng), VSize(rng), rng->C, sizeof rng->C); andre@0: PRNG_ADD_BITS_AND_CARRY(V(rng), VSize(rng), rng->reseed_counter, andre@0: sizeof rng->reseed_counter) andre@0: PRNG_ADD_CARRY_ONLY(rng->reseed_counter,(sizeof rng->reseed_counter)-1, 1); andre@0: andre@0: /* continuous rng check */ andre@0: if (memcmp(V(rng), rng->oldV, sizeof rng->oldV) == 0) { andre@0: rng->isValid = PR_FALSE; andre@0: PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); andre@0: return SECFailure; andre@0: } andre@0: PORT_Memcpy(rng->oldV, V(rng), sizeof rng->oldV); andre@0: return SECSuccess; andre@0: } andre@0: andre@0: /* Use NSPR to prevent RNG_RNGInit from being called from separate andre@0: * threads, creating a race condition. andre@0: */ andre@0: static const PRCallOnceType pristineCallOnce; andre@0: static PRCallOnceType coRNGInit; andre@0: static PRStatus rng_init(void) andre@0: { andre@0: PRUint8 bytes[PRNG_SEEDLEN*2]; /* entropy + nonce */ andre@0: unsigned int numBytes; andre@0: SECStatus rv = SECSuccess; andre@0: andre@0: if (globalrng == NULL) { andre@0: /* bytes needs to have enough space to hold andre@0: * a SHA256 hash value. Blow up at compile time if this isn't true */ andre@0: PR_STATIC_ASSERT(sizeof(bytes) >= SHA256_LENGTH); andre@0: /* create a new global RNG context */ andre@0: globalrng = &theGlobalRng; andre@0: PORT_Assert(NULL == globalrng->lock); andre@0: /* create a lock for it */ andre@0: globalrng->lock = PZ_NewLock(nssILockOther); andre@0: if (globalrng->lock == NULL) { andre@0: globalrng = NULL; andre@0: PORT_SetError(PR_OUT_OF_MEMORY_ERROR); andre@0: return PR_FAILURE; andre@0: } andre@0: andre@0: /* Try to get some seed data for the RNG */ andre@0: numBytes = (unsigned int) RNG_SystemRNG(bytes, sizeof bytes); andre@0: PORT_Assert(numBytes == 0 || numBytes == sizeof bytes); andre@0: if (numBytes != 0) { andre@0: /* if this is our first call, instantiate, otherwise reseed andre@0: * prng_instantiate gets a new clean state, we want to mix andre@0: * any previous entropy we may have collected */ andre@0: if (V(globalrng)[0] == 0) { andre@0: rv = prng_instantiate(globalrng, bytes, numBytes); andre@0: } else { andre@0: rv = prng_reseed_test(globalrng, bytes, numBytes, NULL, 0); andre@0: } andre@0: memset(bytes, 0, numBytes); andre@0: } else { andre@0: PZ_DestroyLock(globalrng->lock); andre@0: globalrng->lock = NULL; andre@0: globalrng = NULL; andre@0: return PR_FAILURE; andre@0: } andre@0: andre@0: if (rv != SECSuccess) { andre@0: return PR_FAILURE; andre@0: } andre@0: /* the RNG is in a valid state */ andre@0: globalrng->isValid = PR_TRUE; andre@0: andre@0: /* fetch one random value so that we can populate rng->oldV for our andre@0: * continous random number test. */ andre@0: prng_generateNewBytes(globalrng, bytes, SHA256_LENGTH, NULL, 0); andre@0: andre@0: /* Fetch more entropy into the PRNG */ andre@0: RNG_SystemInfoForRNG(); andre@0: } andre@0: return PR_SUCCESS; andre@0: } andre@0: andre@0: /* andre@0: * Clean up the global RNG context andre@0: */ andre@0: static void andre@0: prng_freeRNGContext(RNGContext *rng) andre@0: { andre@0: PRUint8 inputhash[VSize(rng) + (sizeof rng->C)]; andre@0: andre@0: /* destroy context lock */ andre@0: SKIP_AFTER_FORK(PZ_DestroyLock(globalrng->lock)); andre@0: andre@0: /* zero global RNG context except for C & V to preserve entropy */ andre@0: prng_Hash_df(inputhash, sizeof rng->C, rng->C, sizeof rng->C, NULL, 0); andre@0: prng_Hash_df(&inputhash[sizeof rng->C], VSize(rng), V(rng), VSize(rng), andre@0: NULL, 0); andre@0: memset(rng, 0, sizeof *rng); andre@0: memcpy(rng->C, inputhash, sizeof rng->C); andre@0: memcpy(V(rng), &inputhash[sizeof rng->C], VSize(rng)); andre@0: andre@0: memset(inputhash, 0, sizeof inputhash); andre@0: } andre@0: andre@0: /* andre@0: * Public functions andre@0: */ andre@0: andre@0: /* andre@0: * Initialize the global RNG context and give it some seed input taken andre@0: * from the system. This function is thread-safe and will only allow andre@0: * the global context to be initialized once. The seed input is likely andre@0: * small, so it is imperative that RNG_RandomUpdate() be called with andre@0: * additional seed data before the generator is used. A good way to andre@0: * provide the generator with additional entropy is to call andre@0: * RNG_SystemInfoForRNG(). Note that C_Initialize() does exactly that. andre@0: */ andre@0: SECStatus andre@0: RNG_RNGInit(void) andre@0: { andre@0: /* Allow only one call to initialize the context */ andre@0: PR_CallOnce(&coRNGInit, rng_init); andre@0: /* Make sure there is a context */ andre@0: return (globalrng != NULL) ? SECSuccess : SECFailure; andre@0: } andre@0: andre@0: /* andre@0: ** Update the global random number generator with more seeding andre@0: ** material. andre@0: */ andre@0: SECStatus andre@0: RNG_RandomUpdate(const void *data, size_t bytes) andre@0: { andre@0: SECStatus rv; andre@0: andre@0: /* Make sure our assumption that size_t is unsigned is true */ andre@0: PR_STATIC_ASSERT(((size_t)-1) > (size_t)1); andre@0: andre@0: #if defined(NS_PTR_GT_32) || (defined(NSS_USE_64) && !defined(NS_PTR_LE_32)) andre@0: /* andre@0: * NIST 800-90 requires us to verify our inputs. This value can andre@0: * come from the application, so we need to make sure it's within the andre@0: * spec. The spec says it must be less than 2^32 bytes (2^35 bits). andre@0: * This can only happen if size_t is greater than 32 bits (i.e. on andre@0: * most 64 bit platforms). The 90% case (perhaps 100% case), size_t andre@0: * is less than or equal to 32 bits if the platform is not 64 bits, and andre@0: * greater than 32 bits if it is a 64 bit platform. The corner andre@0: * cases are handled with explicit defines NS_PTR_GT_32 and NS_PTR_LE_32. andre@0: * andre@0: * In general, neither NS_PTR_GT_32 nor NS_PTR_LE_32 will need to be andre@0: * defined. If you trip over the next two size ASSERTS at compile time, andre@0: * you will need to define them for your platform. andre@0: * andre@0: * if 'sizeof(size_t) > 4' is triggered it means that we were expecting andre@0: * sizeof(size_t) to be greater than 4, but it wasn't. Setting andre@0: * NS_PTR_LE_32 will correct that mistake. andre@0: * andre@0: * if 'sizeof(size_t) <= 4' is triggered, it means that we were expecting andre@0: * sizeof(size_t) to be less than or equal to 4, but it wasn't. Setting andre@0: * NS_PTR_GT_32 will correct that mistake. andre@0: */ andre@0: andre@0: PR_STATIC_ASSERT(sizeof(size_t) > 4); andre@0: andre@0: if (bytes > PRNG_MAX_ADDITIONAL_BYTES) { andre@0: bytes = PRNG_MAX_ADDITIONAL_BYTES; andre@0: } andre@0: #else andre@0: PR_STATIC_ASSERT(sizeof(size_t) <= 4); andre@0: #endif andre@0: andre@0: PZ_Lock(globalrng->lock); andre@0: /* if we're passed more than our additionalDataCache, simply andre@0: * call reseed with that data */ andre@0: if (bytes > sizeof (globalrng->additionalDataCache)) { andre@0: rv = prng_reseed_test(globalrng, NULL, 0, data, (unsigned int) bytes); andre@0: /* if we aren't going to fill or overflow the buffer, just cache it */ andre@0: } else if (bytes < ((sizeof globalrng->additionalDataCache) andre@0: - globalrng->additionalAvail)) { andre@0: PORT_Memcpy(globalrng->additionalDataCache+globalrng->additionalAvail, andre@0: data, bytes); andre@0: globalrng->additionalAvail += (PRUint32) bytes; andre@0: rv = SECSuccess; andre@0: } else { andre@0: /* we are going to fill or overflow the buffer. In this case we will andre@0: * fill the entropy buffer, reseed with it, start a new buffer with the andre@0: * remainder. We know the remainder will fit in the buffer because andre@0: * we already handled the case where bytes > the size of the buffer. andre@0: */ andre@0: size_t bufRemain = (sizeof globalrng->additionalDataCache) andre@0: - globalrng->additionalAvail; andre@0: /* fill the rest of the buffer */ andre@0: if (bufRemain) { andre@0: PORT_Memcpy(globalrng->additionalDataCache andre@0: +globalrng->additionalAvail, andre@0: data, bufRemain); andre@0: data = ((unsigned char *)data) + bufRemain; andre@0: bytes -= bufRemain; andre@0: } andre@0: /* reseed from buffer */ andre@0: rv = prng_reseed_test(globalrng, NULL, 0, andre@0: globalrng->additionalDataCache, andre@0: sizeof globalrng->additionalDataCache); andre@0: andre@0: /* copy the rest into the cache */ andre@0: PORT_Memcpy(globalrng->additionalDataCache, data, bytes); andre@0: globalrng->additionalAvail = (PRUint32) bytes; andre@0: } andre@0: andre@0: PZ_Unlock(globalrng->lock); andre@0: return rv; andre@0: } andre@0: andre@0: /* andre@0: ** Generate some random bytes, using the global random number generator andre@0: ** object. andre@0: */ andre@0: static SECStatus andre@0: prng_GenerateGlobalRandomBytes(RNGContext *rng, andre@0: void *dest, size_t len) andre@0: { andre@0: SECStatus rv = SECSuccess; andre@0: PRUint8 *output = dest; andre@0: /* check for a valid global RNG context */ andre@0: PORT_Assert(rng != NULL); andre@0: if (rng == NULL) { andre@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); andre@0: return SECFailure; andre@0: } andre@0: /* FIPS limits the amount of entropy available in a single request */ andre@0: if (len > PRNG_MAX_REQUEST_SIZE) { andre@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); andre@0: return SECFailure; andre@0: } andre@0: /* --- LOCKED --- */ andre@0: PZ_Lock(rng->lock); andre@0: /* Check the amount of seed data in the generator. If not enough, andre@0: * don't produce any data. andre@0: */ andre@0: if (rng->reseed_counter[0] >= RESEED_VALUE) { andre@0: rv = prng_reseed_test(rng, NULL, 0, NULL, 0); andre@0: PZ_Unlock(rng->lock); andre@0: if (rv != SECSuccess) { andre@0: return rv; andre@0: } andre@0: RNG_SystemInfoForRNG(); andre@0: PZ_Lock(rng->lock); andre@0: } andre@0: /* andre@0: * see if we have enough bytes to fulfill the request. andre@0: */ andre@0: if (len <= rng->dataAvail) { andre@0: memcpy(output, rng->data + ((sizeof rng->data) - rng->dataAvail), len); andre@0: memset(rng->data + ((sizeof rng->data) - rng->dataAvail), 0, len); andre@0: rng->dataAvail -= len; andre@0: rv = SECSuccess; andre@0: /* if we are asking for a small number of bytes, cache the rest of andre@0: * the bytes */ andre@0: } else if (len < sizeof rng->data) { andre@0: rv = prng_generateNewBytes(rng, rng->data, sizeof rng->data, andre@0: rng->additionalAvail ? rng->additionalDataCache : NULL, andre@0: rng->additionalAvail); andre@0: rng->additionalAvail = 0; andre@0: if (rv == SECSuccess) { andre@0: memcpy(output, rng->data, len); andre@0: memset(rng->data, 0, len); andre@0: rng->dataAvail = (sizeof rng->data) - len; andre@0: } andre@0: /* we are asking for lots of bytes, just ask the generator to pass them */ andre@0: } else { andre@0: rv = prng_generateNewBytes(rng, output, len, andre@0: rng->additionalAvail ? rng->additionalDataCache : NULL, andre@0: rng->additionalAvail); andre@0: rng->additionalAvail = 0; andre@0: } andre@0: PZ_Unlock(rng->lock); andre@0: /* --- UNLOCKED --- */ andre@0: return rv; andre@0: } andre@0: andre@0: /* andre@0: ** Generate some random bytes, using the global random number generator andre@0: ** object. andre@0: */ andre@0: SECStatus andre@0: RNG_GenerateGlobalRandomBytes(void *dest, size_t len) andre@0: { andre@0: return prng_GenerateGlobalRandomBytes(globalrng, dest, len); andre@0: } andre@0: andre@0: void andre@0: RNG_RNGShutdown(void) andre@0: { andre@0: /* check for a valid global RNG context */ andre@0: PORT_Assert(globalrng != NULL); andre@0: if (globalrng == NULL) { andre@0: /* Should set a "not initialized" error code. */ andre@0: PORT_SetError(SEC_ERROR_NO_MEMORY); andre@0: return; andre@0: } andre@0: /* clear */ andre@0: prng_freeRNGContext(globalrng); andre@0: globalrng = NULL; andre@0: /* reset the callonce struct to allow a new call to RNG_RNGInit() */ andre@0: coRNGInit = pristineCallOnce; andre@0: } andre@0: andre@0: /* andre@0: * Test case interface. used by fips testing and power on self test andre@0: */ andre@0: /* make sure the test context is separate from the global context, This andre@0: * allows us to test the internal random number generator without losing andre@0: * entropy we may have previously collected. */ andre@0: RNGContext testContext; andre@0: andre@0: /* andre@0: * Test vector API. Use NIST SP 800-90 general interface so one of the andre@0: * other NIST SP 800-90 algorithms may be used in the future. andre@0: */ andre@0: SECStatus andre@0: PRNGTEST_Instantiate(const PRUint8 *entropy, unsigned int entropy_len, andre@0: const PRUint8 *nonce, unsigned int nonce_len, andre@0: const PRUint8 *personal_string, unsigned int ps_len) andre@0: { andre@0: int bytes_len = entropy_len + nonce_len + ps_len; andre@0: PRUint8 *bytes = NULL; andre@0: SECStatus rv; andre@0: andre@0: if (entropy_len < 256/PR_BITS_PER_BYTE) { andre@0: PORT_SetError(SEC_ERROR_NEED_RANDOM); andre@0: return SECFailure; andre@0: } andre@0: andre@0: bytes = PORT_Alloc(bytes_len); andre@0: if (bytes == NULL) { andre@0: PORT_SetError(SEC_ERROR_NO_MEMORY); andre@0: return SECFailure; andre@0: } andre@0: /* concatenate the various inputs, internally NSS only instantiates with andre@0: * a single long string */ andre@0: PORT_Memcpy(bytes, entropy, entropy_len); andre@0: if (nonce) { andre@0: PORT_Memcpy(&bytes[entropy_len], nonce, nonce_len); andre@0: } else { andre@0: PORT_Assert(nonce_len == 0); andre@0: } andre@0: if (personal_string) { andre@0: PORT_Memcpy(&bytes[entropy_len+nonce_len], personal_string, ps_len); andre@0: } else { andre@0: PORT_Assert(ps_len == 0); andre@0: } andre@0: rv = prng_instantiate(&testContext, bytes, bytes_len); andre@0: PORT_ZFree(bytes, bytes_len); andre@0: if (rv == SECFailure) { andre@0: return SECFailure; andre@0: } andre@0: testContext.isValid = PR_TRUE; andre@0: return SECSuccess; andre@0: } andre@0: andre@0: SECStatus andre@0: PRNGTEST_Reseed(const PRUint8 *entropy, unsigned int entropy_len, andre@0: const PRUint8 *additional, unsigned int additional_len) andre@0: { andre@0: if (!testContext.isValid) { andre@0: PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); andre@0: return SECFailure; andre@0: } andre@0: /* This magic input tells us to set the reseed count to it's max count, andre@0: * so we can simulate PRNGTEST_Generate reaching max reseed count */ andre@0: if ((entropy == NULL) && (entropy_len == 0) && andre@0: (additional == NULL) && (additional_len == 0)) { andre@0: testContext.reseed_counter[0] = RESEED_VALUE; andre@0: return SECSuccess; andre@0: } andre@0: return prng_reseed(&testContext, entropy, entropy_len, additional, andre@0: additional_len); andre@0: andre@0: } andre@0: andre@0: SECStatus andre@0: PRNGTEST_Generate(PRUint8 *bytes, unsigned int bytes_len, andre@0: const PRUint8 *additional, unsigned int additional_len) andre@0: { andre@0: SECStatus rv; andre@0: if (!testContext.isValid) { andre@0: PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); andre@0: return SECFailure; andre@0: } andre@0: /* replicate reseed test from prng_GenerateGlobalRandomBytes */ andre@0: if (testContext.reseed_counter[0] >= RESEED_VALUE) { andre@0: rv = prng_reseed(&testContext, NULL, 0, NULL, 0); andre@0: if (rv != SECSuccess) { andre@0: return rv; andre@0: } andre@0: } andre@0: return prng_generateNewBytes(&testContext, bytes, bytes_len, andre@0: additional, additional_len); andre@0: andre@0: } andre@0: andre@0: SECStatus andre@0: PRNGTEST_Uninstantiate() andre@0: { andre@0: if (!testContext.isValid) { andre@0: PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); andre@0: return SECFailure; andre@0: } andre@0: PORT_Memset(&testContext, 0, sizeof testContext); andre@0: return SECSuccess; andre@0: } andre@0: andre@0: SECStatus andre@0: PRNGTEST_RunHealthTests() andre@0: { andre@0: static const PRUint8 entropy[] = { andre@0: 0x8e,0x9c,0x0d,0x25,0x75,0x22,0x04,0xf9, andre@0: 0xc5,0x79,0x10,0x8b,0x23,0x79,0x37,0x14, andre@0: 0x9f,0x2c,0xc7,0x0b,0x39,0xf8,0xee,0xef, andre@0: 0x95,0x0c,0x97,0x59,0xfc,0x0a,0x85,0x41, andre@0: 0x76,0x9d,0x6d,0x67,0x00,0x4e,0x19,0x12, andre@0: 0x02,0x16,0x53,0xea,0xf2,0x73,0xd7,0xd6, andre@0: 0x7f,0x7e,0xc8,0xae,0x9c,0x09,0x99,0x7d, andre@0: 0xbb,0x9e,0x48,0x7f,0xbb,0x96,0x46,0xb3, andre@0: 0x03,0x75,0xf8,0xc8,0x69,0x45,0x3f,0x97, andre@0: 0x5e,0x2e,0x48,0xe1,0x5d,0x58,0x97,0x4c }; andre@0: static const PRUint8 rng_known_result[] = { andre@0: 0x16,0xe1,0x8c,0x57,0x21,0xd8,0xf1,0x7e, andre@0: 0x5a,0xa0,0x16,0x0b,0x7e,0xa6,0x25,0xb4, andre@0: 0x24,0x19,0xdb,0x54,0xfa,0x35,0x13,0x66, andre@0: 0xbb,0xaa,0x2a,0x1b,0x22,0x33,0x2e,0x4a, andre@0: 0x14,0x07,0x9d,0x52,0xfc,0x73,0x61,0x48, andre@0: 0xac,0xc1,0x22,0xfc,0xa4,0xfc,0xac,0xa4, andre@0: 0xdb,0xda,0x5b,0x27,0x33,0xc4,0xb3 }; andre@0: static const PRUint8 reseed_entropy[] = { andre@0: 0xc6,0x0b,0x0a,0x30,0x67,0x07,0xf4,0xe2, andre@0: 0x24,0xa7,0x51,0x6f,0x5f,0x85,0x3e,0x5d, andre@0: 0x67,0x97,0xb8,0x3b,0x30,0x9c,0x7a,0xb1, andre@0: 0x52,0xc6,0x1b,0xc9,0x46,0xa8,0x62,0x79 }; andre@0: static const PRUint8 additional_input[] = { andre@0: 0x86,0x82,0x28,0x98,0xe7,0xcb,0x01,0x14, andre@0: 0xae,0x87,0x4b,0x1d,0x99,0x1b,0xc7,0x41, andre@0: 0x33,0xff,0x33,0x66,0x40,0x95,0x54,0xc6, andre@0: 0x67,0x4d,0x40,0x2a,0x1f,0xf9,0xeb,0x65 }; andre@0: static const PRUint8 rng_reseed_result[] = { andre@0: 0x02,0x0c,0xc6,0x17,0x86,0x49,0xba,0xc4, andre@0: 0x7b,0x71,0x35,0x05,0xf0,0xdb,0x4a,0xc2, andre@0: 0x2c,0x38,0xc1,0xa4,0x42,0xe5,0x46,0x4a, andre@0: 0x7d,0xf0,0xbe,0x47,0x88,0xb8,0x0e,0xc6, andre@0: 0x25,0x2b,0x1d,0x13,0xef,0xa6,0x87,0x96, andre@0: 0xa3,0x7d,0x5b,0x80,0xc2,0x38,0x76,0x61, andre@0: 0xc7,0x80,0x5d,0x0f,0x05,0x76,0x85 }; andre@0: static const PRUint8 rng_no_reseed_result[] = { andre@0: 0xc4,0x40,0x41,0x8c,0xbf,0x2f,0x70,0x23, andre@0: 0x88,0xf2,0x7b,0x30,0xc3,0xca,0x1e,0xf3, andre@0: 0xef,0x53,0x81,0x5d,0x30,0xed,0x4c,0xf1, andre@0: 0xff,0x89,0xa5,0xee,0x92,0xf8,0xc0,0x0f, andre@0: 0x88,0x53,0xdf,0xb6,0x76,0xf0,0xaa,0xd3, andre@0: 0x2e,0x1d,0x64,0x37,0x3e,0xe8,0x4a,0x02, andre@0: 0xff,0x0a,0x7f,0xe5,0xe9,0x2b,0x6d }; andre@0: andre@0: SECStatus rng_status = SECSuccess; andre@0: PR_STATIC_ASSERT(sizeof(rng_known_result) >= sizeof(rng_reseed_result)); andre@0: PRUint8 result[sizeof(rng_known_result)]; andre@0: andre@0: /********************************************/ andre@0: /* First test instantiate error path. */ andre@0: /* In this case we supply enough entropy, */ andre@0: /* but not enough seed. This will trigger */ andre@0: /* the code that checks for a entropy */ andre@0: /* source failure. */ andre@0: /********************************************/ andre@0: rng_status = PRNGTEST_Instantiate(entropy, 256/PR_BITS_PER_BYTE, andre@0: NULL, 0, NULL, 0); andre@0: if (rng_status == SECSuccess) { andre@0: PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); andre@0: return SECFailure; andre@0: } andre@0: if (PORT_GetError() != SEC_ERROR_NEED_RANDOM) { andre@0: PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); andre@0: return SECFailure; andre@0: } andre@0: /* we failed with the proper error code, we can continue */ andre@0: andre@0: /********************************************/ andre@0: /* Generate random bytes with a known seed. */ andre@0: /********************************************/ andre@0: rng_status = PRNGTEST_Instantiate(entropy, sizeof entropy, andre@0: NULL, 0, NULL, 0); andre@0: if (rng_status != SECSuccess) { andre@0: /* Error set by PRNGTEST_Instantiate */ andre@0: return SECFailure; andre@0: } andre@0: rng_status = PRNGTEST_Generate(result, sizeof rng_known_result, NULL, 0); andre@0: if ( ( rng_status != SECSuccess) || andre@0: ( PORT_Memcmp( result, rng_known_result, andre@0: sizeof rng_known_result ) != 0 ) ) { andre@0: PRNGTEST_Uninstantiate(); andre@0: PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); andre@0: return SECFailure; andre@0: } andre@0: rng_status = PRNGTEST_Reseed(reseed_entropy, sizeof reseed_entropy, andre@0: additional_input, sizeof additional_input); andre@0: if (rng_status != SECSuccess) { andre@0: /* Error set by PRNG_Reseed */ andre@0: PRNGTEST_Uninstantiate(); andre@0: return SECFailure; andre@0: } andre@0: rng_status = PRNGTEST_Generate(result, sizeof rng_reseed_result, NULL, 0); andre@0: if ( ( rng_status != SECSuccess) || andre@0: ( PORT_Memcmp( result, rng_reseed_result, andre@0: sizeof rng_reseed_result ) != 0 ) ) { andre@0: PRNGTEST_Uninstantiate(); andre@0: PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); andre@0: return SECFailure; andre@0: } andre@0: /* This magic forces the reseed count to it's max count, so we can see if andre@0: * PRNGTEST_Generate will actually when it reaches it's count */ andre@0: rng_status = PRNGTEST_Reseed(NULL, 0, NULL, 0); andre@0: if (rng_status != SECSuccess) { andre@0: PRNGTEST_Uninstantiate(); andre@0: /* Error set by PRNG_Reseed */ andre@0: return SECFailure; andre@0: } andre@0: /* This generate should now reseed */ andre@0: rng_status = PRNGTEST_Generate(result, sizeof rng_reseed_result, NULL, 0); andre@0: if ( ( rng_status != SECSuccess) || andre@0: /* NOTE we fail if the result is equal to the no_reseed_result. andre@0: * no_reseed_result is the value we would have gotten if we didn't andre@0: * do an automatic reseed in PRNGTEST_Generate */ andre@0: ( PORT_Memcmp( result, rng_no_reseed_result, andre@0: sizeof rng_no_reseed_result ) == 0 ) ) { andre@0: PRNGTEST_Uninstantiate(); andre@0: PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); andre@0: return SECFailure; andre@0: } andre@0: /* make sure reseed fails when we don't supply enough entropy */ andre@0: rng_status = PRNGTEST_Reseed(reseed_entropy, 4, NULL, 0); andre@0: if (rng_status == SECSuccess) { andre@0: PRNGTEST_Uninstantiate(); andre@0: PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); andre@0: return SECFailure; andre@0: } andre@0: if (PORT_GetError() != SEC_ERROR_NEED_RANDOM) { andre@0: PRNGTEST_Uninstantiate(); andre@0: PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); andre@0: return SECFailure; andre@0: } andre@0: rng_status = PRNGTEST_Uninstantiate(); andre@0: if (rng_status != SECSuccess) { andre@0: /* Error set by PRNG_Uninstantiate */ andre@0: return rng_status; andre@0: } andre@0: /* make sure uninstantiate fails if the contest is not initiated (also tests andre@0: * if the context was cleared in the previous Uninstantiate) */ andre@0: rng_status = PRNGTEST_Uninstantiate(); andre@0: if (rng_status == SECSuccess) { andre@0: PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); andre@0: return SECFailure; andre@0: } andre@0: if (PORT_GetError() != SEC_ERROR_LIBRARY_FAILURE) { andre@0: return rng_status; andre@0: } andre@0: andre@0: return SECSuccess; andre@0: }