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) {
openSUSE Build Service is sponsored by