static PRStatus rng_init(void) { PRUint8 bytes[PRNG_SEEDLEN * 2]; /* entropy + nonce */ unsigned int numBytes; 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 */ 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) { rv = prng_instantiate(globalrng, bytes, numBytes); } else { rv = prng_reseed_test(globalrng, bytes, numBytes, NULL, 0); } memset(bytes, 0, numBytes); } 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; }
/* ** 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; }
/* ** 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_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; }