File nss-CC-drbg_continuous_selftests.patch of Package mozilla-nss.972
# HG changeset patch
# Parent 7067ca1e149e91d4254229728c761749061346ab
# Parent 4decb47aec1db5d34b42ea94f41c91957c2ab71a
CC/FIPS changes to the DRBG - testing of previous vs. current (to be returned)
pseudorandom data
diff --git a/lib/freebl/drbg.c b/lib/freebl/drbg.c
--- a/lib/freebl/drbg.c
+++ b/lib/freebl/drbg.c
@@ -71,37 +71,40 @@ struct RNGContextStr {
* 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 prev_blk[SHA256_LENGTH]; /* for continuous rng checking */
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 isReady; /* false if prev_blk has not yet been set to the
+ * first generated random block, which is used
+ * solely for continuous testing */
};
typedef struct RNGContextStr RNGContext;
static RNGContext *globalrng = NULL;
static RNGContext theGlobalRng;
/*
@@ -158,16 +161,17 @@ static SECStatus
prng_instantiate(RNGContext *rng, const PRUint8 *bytes, unsigned int len)
{
if (len < PRNG_SEEDLEN) {
/* if the seedlen is to small, it's probably because we failed to get
* enough random data */
PORT_SetError(SEC_ERROR_NEED_RANDOM);
return SECFailure;
}
+ rng->isReady = PR_FALSE;
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;
}
@@ -275,50 +279,93 @@ prng_reseed_test(RNGContext *rng, const
*
* 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 buf[SHA256_LENGTH];
+ PRUint8 *random_bytes;
+ PRUint8 *previous_round;
+
+ /* PRNG has to be initialized first - this is done by requesting
+ * SHA256_LENGTH random bytes from prng_generateNewBytes and throwing
+ * away the random bits returned as is done in rng_init() */
+ if (!rng->isReady) {
+ rng->isValid = PR_FALSE;
+ return;
+ }
PORT_Memcpy(data, V(rng), VSize(rng));
+ previous_round = rng->prev_blk;
while (no_of_returned_bytes) {
SHA256Context ctx;
unsigned int len;
unsigned int carry;
int k1;
+ if (no_of_returned_bytes < SHA256_LENGTH) {
+ /* output might be too small to hold whole prng block which has to be
+ * generated for the continuous test */
+ random_bytes = buf;
+ } else {
+ random_bytes = returned_bytes;
+ }
+
SHA256_Begin(&ctx);
SHA256_Update(&ctx, data, sizeof data);
- SHA256_End(&ctx, returned_bytes, &len, no_of_returned_bytes);
+ SHA256_End(&ctx, random_bytes, &len, SHA256_LENGTH);
+
+ /* FIPS continuous PRNG check */
+ if (memcmp(random_bytes, previous_round, SHA256_LENGTH) == 0) {
+ rng->isValid = PR_FALSE;
+ goto cleanup;
+ }
+
+ if (no_of_returned_bytes < SHA256_LENGTH) {
+ /* random_bytes is now actually buf - copy what fits into output;
+ * getting here also means that we reached the last iteration of
+ * the loop thus we won't need to do any more updates to the hash
+ * context. */
+ PORT_Memcpy(returned_bytes, random_bytes, no_of_returned_bytes);
+ break;
+ }
+
+ previous_round = random_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);
}
+ /* save last block for later comparisons */
+ PORT_Memcpy(rng->prev_blk, random_bytes, SHA256_LENGTH);
+
+cleanup:
PORT_Memset(data, 0, sizeof data);
+ PORT_Memset(buf, 0, sizeof buf);
}
/*
* 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 */
+ SECStatus rv = SECSuccess;
unsigned int carry;
int k1, k2;
if (!rng->isValid) {
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
return SECFailure;
}
/* This code only triggers during tests, normal
@@ -339,36 +386,49 @@ prng_generateNewBytes(RNGContext *rng,
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) );
+ /* FIPS continuous PRNG check */
+ if (memcmp(returned_bytes, rng->prev_blk, sizeof rng->prev_blk) == 0) {
+ /* The check is irrelevant until prev_blk is populated which
+ * happens in rng_init by running through this branch.
+ * Note that we are here relying on initialisation being done
+ * properly by some wrapper function! */
+ rng->isValid = (rng->isReady) ? PR_FALSE : PR_TRUE;
+ }
+ PORT_Memcpy(rng->prev_blk, returned_bytes, sizeof rng->prev_blk);
} else {
+ /* prng_Hashgen implements the continuous check on its own */
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;
+ /* continuous rng check result handling */
+ if (!rng->isValid) {
+ /* zero the whole DRBG - some recent crypto data might be based on
+ * previous values and there's no reason to leave it hanging around
+ */
+ PORT_Memset(rng, 0, sizeof(*rng));
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
+ rv = SECFailure;
}
- PORT_Memcpy(rng->oldV, V(rng), sizeof rng->oldV);
- return SECSuccess;
+ PORT_Memset(H, 0, sizeof(H));
+ return rv;
}
/* 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)
@@ -416,16 +476,17 @@ static PRStatus rng_init(void)
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);
+ globalrng->isReady = PR_TRUE;
/* Fetch more entropy into the PRNG */
RNG_SystemInfoForRNG();
}
return PR_SUCCESS;
}
/*
@@ -698,16 +759,21 @@ PRNGTEST_Instantiate(const PRUint8 *entr
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;
+ /* test PRNG is not initialised in the same way as globalrng, so mark it
+ * as ready to prevent problems when PRNGTEST_RunHealthTests() is called
+ * as part of globalrng update and the validity/readiness tests intended
+ * for globalrng fail on the test one */
+ testContext.isReady = PR_TRUE;
return SECSuccess;
}
SECStatus
PRNGTEST_Reseed(const PRUint8 *entropy, unsigned int entropy_len,
const PRUint8 *additional, unsigned int additional_len)
{
if (!testContext.isValid) {