Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
SUSE:SLE-12-SP2:GA
mozilla-nss.1743
nss-CC-drbg_continuous_selftests.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File nss-CC-drbg_continuous_selftests.patch of Package mozilla-nss.1743
# 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) {
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor