/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* PRNG_SEEDLEN defined in NIST SP 800-90 section 10.1 *forSHA-1,SHA-224,andSHA-256it's440bits.
* for SHA-384 and SHA-512 it's 888 bits */ #define PRNG_SEEDLEN (440 / PR_BITS_PER_BYTE) #define PRNG_MAX_ADDITIONAL_BYTES PR_INT64(0x100000000) /* 2^35 bits or 2^32 bytes */ #define PRNG_MAX_REQUEST_SIZE 0x10000 /* 2^19 bits or 2^16 bytes */ #define PRNG_ADDITONAL_DATA_CACHE_SIZE (8 * 1024) /* must be less than \ *PRNG_MAX_ADDITIONAL_BYTES\
*/ #define PRNG_ENTROPY_BLOCK_SIZE SHA256_LENGTH
/* RESEED_COUNT is how many calls to the prng before we need to reseed *undernormalNISTrules,youmustreturnanerror.IntheNSScase,we *self-reseedwithRNG_SystemRNG().Countcanbealargenumber.Forcode *simplicity,wespecifycountwith2components:RESEED_BYTE(whichis *thesameasLOG256(RESEED_COUNT))andRESEED_VALUE(whichisthesameas *RESEED_COUNT/(256^RESEED_BYTE)).Anotherwaytolookatthisis *RESEED_COUNT=RESEED_VALUE*(256^RESEED_BYTE).ForHashbasedDRBG *weusethemaximumcountvalue,2^48,orRESEED_BYTE=6andRESEED_VALUE=1
*/ #define RESEED_BYTE 6 #define RESEED_VALUE 1
/* *TheactualvaluesofthisenumarespecifiedinSP800-90,10.1.1.* *Thespecdoesnotnamethetypes,itonlyusesbarevalues
*/ typedefenum {
prngCGenerateType = 0, /* used when creating a new 'C' */
prngReseedType = 1, /* used in reseeding */
prngAdditionalDataType = 2, /* used in mixing additional data */
prngGenerateByteType = 3/* used when mixing internal state while
* generating bytes */
} prngVTypes;
/* *GlobalRNGcontext
*/ struct RNGContextStr {
PRLock *lock; /* Lock to serialize access to global rng */ /* *NOTE,anumberofstepsinthedrbgalgorithmneedtohash *V_type||V.Thecode,therefore,dependsontheVarrayfollowing *immediatelyafterV_typetoavoidextracopies.Toaccomplishthis *inawaythatcompilierscan'tperturb,wedeclareV_typeandV
* 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 */ /* If we get calls for the PRNG to return less than the length of our *hash,weextendtherequestforafullhash(sincewe'llbedoing *thefullhashanyway).Futurerequestsforrandomnumbersarefulfilled *fromtheremainderofthebyteswegenerated.Requestsforbyteslonger *thanthehashsizearefulfilleddirectlyfromtheHashGenfunction
* of the random number generator. */
PRUint8 reseed_counter[RESEED_BYTE + 1]; /* number of requests since the *lastreseed.Needonlybe *bigenoughtoholdthewhole
* reseed count */
PRUint8 data[SHA256_LENGTH]; /* when we request less than a block *savetherestoftherngoutputfor
* another partial block */
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 isKatTest; /* true if running NIST PRNG KAT tests */ /* for continuous entropy check */
PRUint8 previousEntropyHash[SHA256_LENGTH];
};
/* For FIPS 140-2 4.9.2 continuous random number generator test, *fetchtheinitialentropyfromthesystemRNGandkeepitfor
* later comparison. */
length = RNG_SystemRNG(block, sizeof(block)); if (length == 0) { return PR_FAILURE; /* error is already set */
}
PORT_Assert(length == sizeof(block));
/* Store the hash of the entropy block rather than the block
* itself for backward secrecy. */
SHA256_Begin(&ctx);
SHA256_Update(&ctx, block, sizeof(block));
SHA256_End(&ctx, globalrng->previousEntropyHash, NULL, sizeof(globalrng->previousEntropyHash));
PORT_SafeZero(block, sizeof(block));
SHA256_DestroyContext(&ctx, PR_FALSE); return PR_SUCCESS;
}
if (PR_CallOnce(&coRNGInitEntropy, prng_initEntropy) != PR_SUCCESS) {
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); return SECFailure;
}
/* For FIPS 140-2 4.9.2 continuous random generator test, *iterativelyfetchfixedsizedblocksfromthesystemand
* compare consecutive blocks. */ while (total < requestLength) {
size_t length = RNG_SystemRNG(block, sizeof(block)); if (length == 0) {
rv = SECFailure; /* error is already set */ goto out;
}
PORT_Assert(length == sizeof(block));
/* Store the hash of the entropy block rather than the block
* itself for backward secrecy. */
SHA256_Begin(&ctx);
SHA256_Update(&ctx, block, sizeof(block));
SHA256_End(&ctx, hash, NULL, sizeof(hash));
/* if entropy wasn't supplied, fetch it. (normal operation case) */ if (entropy == NULL) {
entropy_len = PRNG_SEEDLEN;
rv = prng_getEntropy(&noiseData[sizeof(rng->V_Data)], entropy_len); if (rv != SECSuccess) { return SECFailure; /* error is already set */
}
} else { /* NOTE: this code is only available for testing, not to applications */ /* if entropy was too big for the stack variable, get it from malloc */ if (entropy_len > PRNG_SEEDLEN) {
noise = PORT_Alloc(entropy_len + (sizeof(rng->V_Data))); if (noise == NULL) { return SECFailure;
}
}
PORT_Memcpy(&noise[sizeof(rng->V_Data)], entropy, entropy_len);
}
if (entropy_len < 256 / PR_BITS_PER_BYTE) { /* noise == &noiseData[0] at this point, so nothing to free */
PORT_SetError(SEC_ERROR_NEED_RANDOM); return SECFailure;
}
/* do health checks in FIPS mode */
rv = PRNGTEST_RunHealthTests(); if (rv != SECSuccess) { /* error set by PRNGTEST_RunHealTests() */
rng->isValid = PR_FALSE; return SECFailure;
} return prng_reseed(rng, entropy, entropy_len,
additional_input, additional_input_len);
}
SHA256_Begin(&ctx);
SHA256_Update(&ctx, data, sizeof(data));
SHA256_End(&ctx, thisHash, &len, SHA256_LENGTH); if (no_of_returned_bytes < SHA256_LENGTH) {
len = no_of_returned_bytes;
}
PORT_Memcpy(returned_bytes, thisHash, len);
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 */
carry = no_of_returned_bytes;
PRNG_ADD_CARRY_ONLY(data, (sizeof(data)) - 1, carry);
SHA256_DestroyContext(&ctx, PR_FALSE);
}
PORT_SafeZero(data, sizeof(data));
PORT_SafeZero(thisHash, sizeof(thisHash));
}
/* *Generatesnewrandombytesandadvancestheinternalprngstate. *additionalbytesareonlyusedinalgorithmtesting. * *ThisfunctionisspecifiedinNISTSP800-90section10.1.1.4
*/ static SECStatus
prng_generateNewBytes(RNGContext *rng,
PRUint8 *returned_bytes, unsignedint no_of_returned_bytes, const PRUint8 *additional_input, unsignedint additional_input_len)
{
PRUint8 H[SHA256_LENGTH]; /* both H and w since they
* aren't used concurrently */ unsignedint carry;
if (!rng->isValid) {
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); return SECFailure;
} /* This code only triggers during tests, normal
* prng operation does not use additional_input */ if (additional_input) {
SHA256Context ctx; /* NIST SP 800-90 defines two temporaries in their calculations, *wandH.Thesetemporariesarethesamelengths,andused *atdifferenttimes,soweusethefollowingmacrotocollapse *themtothesamevariable,butkeepingtheiruniquenamesfor
* easy comparison to the spec */ #define w H
rng->V_type = prngAdditionalDataType;
SHA256_Begin(&ctx);
SHA256_Update(&ctx, rng->V_Data, sizeof(rng->V_Data));
SHA256_Update(&ctx, additional_input, additional_input_len);
SHA256_End(&ctx, w, NULL, sizeof(w));
PRNG_ADD_BITS_AND_CARRY(V(rng), VSize(rng), w, sizeof(w), carry)
PORT_Memset(w, 0, sizeof(w));
SHA256_DestroyContext(&ctx, PR_FALSE); #undef w
}
/* if the prng failed, don't return any output, signal softoken */
PORT_SafeZero(H, sizeof(H)); if (!rng->isValid) {
PORT_Memset(returned_bytes, 0, no_of_returned_bytes);
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); return SECFailure;
} return SECSuccess;
}
/* Use NSPR to prevent RNG_RNGInit from being called from separate *threads,creatingaracecondition.
*/ staticconst PRCallOnceType pristineCallOnce; static PRCallOnceType coRNGInit; static PRStatus
rng_init(void)
{
PRUint8 bytes[PRNG_SEEDLEN * 2]; /* entropy + nonce */
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 = PR_NewLock(); 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 */
rv = prng_getEntropy(bytes, sizeof(bytes)); if (rv == SECSuccess) { /* if this is our first call, instantiate, otherwise reseed *prng_instantiategetsanewcleanstate,wewanttomix
* any previous entropy we may have collected */ if (V(globalrng)[0] == 0) {
rv = prng_instantiate(globalrng, bytes, sizeof(bytes));
} else {
rv = prng_reseed_test(globalrng, bytes, sizeof(bytes), NULL, 0);
}
memset(bytes, 0, sizeof(bytes));
} else {
PR_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;
}
PR_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, (unsignedint)bytes); /* if we aren't going to fill or overflow the buffer, just cache it */
} elseif (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 *filltheentropybuffer,reseedwithit,startanewbufferwiththe *remainder.Weknowtheremainderwillfitinthebufferbecause *wealreadyhandledthecasewherebytes>thesizeofthebuffer.
*/
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 = ((unsignedchar *)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;
}
PR_Unlock(globalrng->lock); return rv;
}
/* **Generatesomerandombytes,usingtheglobalrandomnumbergenerator **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 --- */
PR_Lock(rng->lock); /* Check the amount of seed data in the generator. If not enough, *don'tproduceanydata.
*/ if (rng->reseed_counter[0] >= RESEED_VALUE) {
rv = prng_reseed_test(rng, NULL, 0, NULL, 0);
PR_Unlock(rng->lock); if (rv != SECSuccess) { return rv;
}
RNG_SystemInfoForRNG();
PR_Lock(rng->lock);
} /* *seeifwehaveenoughbytestofulfilltherequest.
*/ 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 */
} elseif (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;
}
PR_Unlock(rng->lock); /* --- UNLOCKED --- */ return rv;
}
void
RNG_RNGShutdown(void)
{ /* check for a valid global RNG context */
PORT_Assert(globalrng != NULL); if (globalrng == NULL) { /* Should set a "not initialized" error code. */
PORT_SetError(SEC_ERROR_NO_MEMORY); return;
} /* clear */
prng_freeRNGContext(globalrng);
globalrng = NULL; /* reset the callonce struct to allow a new call to RNG_RNGInit() */
coRNGInit = pristineCallOnce;
}
/* *Testcaseinterface.usedbyfipstestingandpoweronselftest
*/ /* make sure the test context is separate from the global context, This *allowsustotesttheinternalrandomnumbergeneratorwithoutlosing
* entropy we may have previously collected. */
RNGContext testContext;
/********************************************/ /* First test instantiate error path. */ /* In this case we supply enough entropy, */ /* but not enough seed. This will trigger */ /* the code that checks for a entropy */ /* source failure. */ /********************************************/
rng_status = PRNGTEST_Instantiate(entropy, 256 / PR_BITS_PER_BYTE,
NULL, 0, NULL, 0); if (rng_status == SECSuccess) {
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); return SECFailure;
} if (PORT_GetError() != SEC_ERROR_NEED_RANDOM) {
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); return SECFailure;
} /* we failed with the proper error code, we can continue */
/********************************************/ /* Generate random bytes with a known seed. */ /********************************************/
rng_status = PRNGTEST_Instantiate(entropy, sizeof(entropy),
NULL, 0, NULL, 0); if (rng_status != SECSuccess) { /* Error set by PRNGTEST_Instantiate */ return SECFailure;
}
rng_status = PRNGTEST_Generate(result, sizeof(rng_known_result), NULL, 0); if ((rng_status != SECSuccess) ||
(PORT_Memcmp(result, rng_known_result, sizeof(rng_known_result)) != 0)) {
PRNGTEST_Uninstantiate();
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); return SECFailure;
}
rng_status = PRNGTEST_Reseed(reseed_entropy, sizeof(reseed_entropy),
additional_input, sizeof(additional_input)); if (rng_status != SECSuccess) { /* Error set by PRNG_Reseed */
PRNGTEST_Uninstantiate(); return SECFailure;
}
rng_status = PRNGTEST_Generate(result, sizeof(rng_reseed_result), NULL, 0); if ((rng_status != SECSuccess) ||
(PORT_Memcmp(result, rng_reseed_result, sizeof(rng_reseed_result)) != 0)) {
PRNGTEST_Uninstantiate();
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); return SECFailure;
} /* This magic forces the reseed count to it's max count, so we can see if
* PRNGTEST_Generate will actually when it reaches it's count */
rng_status = PRNGTEST_Reseed(NULL, 0, NULL, 0); if (rng_status != SECSuccess) {
PRNGTEST_Uninstantiate(); /* Error set by PRNG_Reseed */ return SECFailure;
} /* This generate should now reseed */
rng_status = PRNGTEST_Generate(result, sizeof(rng_reseed_result), NULL, 0); if ((rng_status != SECSuccess) || /* NOTE we fail if the result is equal to the no_reseed_result. *no_reseed_resultisthevaluewewouldhavegottenifwedidn't
* do an automatic reseed in PRNGTEST_Generate */
(PORT_Memcmp(result, rng_no_reseed_result, sizeof(rng_no_reseed_result)) == 0)) {
PRNGTEST_Uninstantiate();
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); return SECFailure;
} /* make sure reseed fails when we don't supply enough entropy */
rng_status = PRNGTEST_Reseed(reseed_entropy, 4, NULL, 0); if (rng_status == SECSuccess) {
PRNGTEST_Uninstantiate();
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); return SECFailure;
} if (PORT_GetError() != SEC_ERROR_NEED_RANDOM) {
PRNGTEST_Uninstantiate();
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); return SECFailure;
}
rng_status = PRNGTEST_Uninstantiate(); if (rng_status != SECSuccess) { /* Error set by PRNG_Uninstantiate */ return rng_status;
} /* make sure uninstantiate fails if the contest is not initiated (also tests
* if the context was cleared in the previous Uninstantiate) */
rng_status = PRNGTEST_Uninstantiate(); if (rng_status == SECSuccess) {
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); return SECFailure;
} if (PORT_GetError() != SEC_ERROR_LIBRARY_FAILURE) { return rng_status;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.