/********************************************************************************/ /* */ /* DRBG with a behavior according to SP800-90A */ /* Written by Ken Goldman */ /* IBM Thomas J. Watson Research Center */ /* */ /* Licenses and Notices */ /* */ /* 1. Copyright Licenses: */ /* */ /* - Trusted Computing Group (TCG) grants to the user of the source code in */ /* this specification (the "Source Code") a worldwide, irrevocable, */ /* nonexclusive, royalty free, copyright license to reproduce, create */ /* derivative works, distribute, display and perform the Source Code and */ /* derivative works thereof, and to grant others the rights granted herein. */ /* */ /* - The TCG grants to the user of the other parts of the specification */ /* (other than the Source Code) the rights to reproduce, distribute, */ /* display, and perform the specification solely for the purpose of */ /* developing products based on such documents. */ /* */ /* 2. Source Code Distribution Conditions: */ /* */ /* - Redistributions of Source Code must retain the above copyright licenses, */ /* this list of conditions and the following disclaimers. */ /* */ /* - Redistributions in binary form must reproduce the above copyright */ /* licenses, this list of conditions and the following disclaimers in the */ /* documentation and/or other materials provided with the distribution. */ /* */ /* 3. Disclaimers: */ /* */ /* - THE COPYRIGHT LICENSES SET FORTH ABOVE DO NOT REPRESENT ANY FORM OF */ /* LICENSE OR WAIVER, EXPRESS OR IMPLIED, BY ESTOPPEL OR OTHERWISE, WITH */ /* RESPECT TO PATENT RIGHTS HELD BY TCG MEMBERS (OR OTHER THIRD PARTIES) */ /* THAT MAY BE NECESSARY TO IMPLEMENT THIS SPECIFICATION OR OTHERWISE. */ /* Contact TCG Administration (admin@trustedcomputinggroup.org) for */ /* information on specification licensing rights available through TCG */ /* membership agreements. */ /* */ /* - THIS SPECIFICATION IS PROVIDED "AS IS" WITH NO EXPRESS OR IMPLIED */ /* WARRANTIES WHATSOEVER, INCLUDING ANY WARRANTY OF MERCHANTABILITY OR */ /* FITNESS FOR A PARTICULAR PURPOSE, ACCURACY, COMPLETENESS, OR */ /* NONINFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS, OR ANY WARRANTY */ /* OTHERWISE ARISING OUT OF ANY PROPOSAL, SPECIFICATION OR SAMPLE. */ /* */ /* - Without limitation, TCG and its members and licensors disclaim all */ /* liability, including liability for infringement of any proprietary */ /* rights, relating to use of information in this specification and to the */ /* implementation of this specification, and TCG disclaims all liability for */ /* cost of procurement of substitute goods or services, lost profits, loss */ /* of use, loss of data or any incidental, consequential, direct, indirect, */ /* or special damages, whether under contract, tort, warranty or otherwise, */ /* arising in any way out of use or reliance upon this specification or any */ /* information herein. */ /* */ /* (c) Copyright IBM Corp. and others, 2016 - 2023 */ /* */ /********************************************************************************/ //** Introduction // This file implements a DRBG with a behavior according to SP800-90A using // a block cypher. This is also compliant to ISO/IEC 18031:2011(E) C.3.2. // // A state structure is created for use by TPM.lib and functions // within the CryptoEngine my use their own state structures when they need to have // deterministic values. // // A debug mode is available that allows the random numbers generated for TPM.lib // to be repeated during runs of the simulator. The switch for it is in // TpmBuildSwitches.h. It is USE_DEBUG_RNG. // // // This is the implementation layer of CTR DRGB mechanism as defined in SP800-90A // and the functions are organized as closely as practical to the organization in // SP800-90A. It is intended to be compiled as a separate module that is linked // with a secure application so that both reside inside the same boundary // [SP 800-90A 8.5]. The secure application in particular manages the accesses // protected storage for the state of the DRBG instantiations, and supplies the // implementation functions here with a valid pointer to the working state of the // given instantiations (as a DRBG_STATE structure). // // This DRBG mechanism implementation does not support prediction resistance. Thus // 'prediction_resistance_flag' is omitted from Instantiate_function(), // Reseed_function(), Generate_function() argument lists [SP 800-90A 9.1, 9.2, // 9.3], as well as from the working state data structure DRBG_STATE [SP 800-90A // 9.1]. // // This DRBG mechanism implementation always uses the highest security strength of // available in the block ciphers. Thus 'requested_security_strength' parameter is // omitted from Instantiate_function() and Generate_function() argument lists // [SP 800-90A 9.1, 9.2, 9.3], as well as from the working state data structure // DRBG_STATE [SP 800-90A 9.1]. // // Internal functions (ones without Crypt prefix) expect validated arguments and // therefore use assertions instead of runtime parameter checks and mostly return // void instead of a status value. #include "Tpm.h" // Pull in the test vector definitions and define the space #include "PRNG_TestVectors.h" const BYTE DRBG_NistTestVector_Entropy[] = {DRBG_TEST_INITIATE_ENTROPY}; const BYTE DRBG_NistTestVector_GeneratedInterm[] = {DRBG_TEST_GENERATED_INTERM}; const BYTE DRBG_NistTestVector_EntropyReseed[] = {DRBG_TEST_RESEED_ENTROPY}; const BYTE DRBG_NistTestVector_Generated[] = {DRBG_TEST_GENERATED}; //** Derivation Functions //*** Description // The functions in this section are used to reduce the personalization input values // to make them usable as input for reseeding and instantiation. The overall // behavior is intended to produce the same results as described in SP800-90A, // section 10.4.2 "Derivation Function Using a Block Cipher Algorithm // (Block_Cipher_df)." The code is broken into several subroutines to deal with the // fact that the data used for personalization may come in several separate blocks // such as a Template hash and a proof value and a primary seed. //*** Derivation Function Defines and Structures #define DF_COUNT (DRBG_KEY_SIZE_WORDS / DRBG_IV_SIZE_WORDS + 1) #if DRBG_KEY_SIZE_BITS != 128 && DRBG_KEY_SIZE_BITS != 256 # error "CryptRand.c only written for AES with 128- or 256-bit keys." #endif typedef tpmKeyScheduleAES DRBG_KEY_SCHEDULE; typedef struct { DRBG_KEY_SCHEDULE keySchedule; DRBG_IV iv[DF_COUNT]; DRBG_IV out1; DRBG_IV buf; int contents; } DF_STATE, *PDF_STATE; //*** DfCompute() // This function does the incremental update of the derivation function state. It // encrypts the 'iv' value and XOR's the results into each of the blocks of the // output. This is equivalent to processing all of input data for each output block. static void DfCompute(PDF_STATE dfState) { int i; int iv; crypt_uword_t* pIv; crypt_uword_t temp[DRBG_IV_SIZE_WORDS] = {0}; // for(iv = 0; iv < DF_COUNT; iv++) { pIv = (crypt_uword_t*)&dfState->iv[iv].words[0]; for(i = 0; i < DRBG_IV_SIZE_WORDS; i++) { temp[i] ^= pIv[i] ^ dfState->buf.words[i]; } DRBG_ENCRYPT(&dfState->keySchedule, &temp, pIv); } for(i = 0; i < DRBG_IV_SIZE_WORDS; i++) dfState->buf.words[i] = 0; dfState->contents = 0; } //*** DfStart() // This initializes the output blocks with an encrypted counter value and // initializes the key schedule. static void DfStart(PDF_STATE dfState, uint32_t inputLength) { BYTE init[8]; int i; UINT32 drbgSeedSize = sizeof(DRBG_SEED); const BYTE dfKey[DRBG_KEY_SIZE_BYTES] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f #if DRBG_KEY_SIZE_BYTES > 16 , 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f #endif }; memset(dfState, 0, sizeof(DF_STATE)); DRBG_ENCRYPT_SETUP(&dfKey[0], DRBG_KEY_SIZE_BITS, &dfState->keySchedule); // Create the first chaining values for(i = 0; i < DF_COUNT; i++) ((BYTE*)&dfState->iv[i])[3] = (BYTE)i; DfCompute(dfState); // initialize the first 64 bits of the IV in a way that doesn't depend // on the size of the words used. UINT32_TO_BYTE_ARRAY(inputLength, init); UINT32_TO_BYTE_ARRAY(drbgSeedSize, &init[4]); memcpy(&dfState->iv[0], init, 8); dfState->contents = 4; } //*** DfUpdate() // This updates the state with the input data. A byte at a time is moved into the // state buffer until it is full and then that block is encrypted by DfCompute(). static void DfUpdate(PDF_STATE dfState, int size, const BYTE* data) { while(size > 0) { int toFill = DRBG_IV_SIZE_BYTES - dfState->contents; if(size < toFill) toFill = size; // Copy as many bytes as there are or until the state buffer is full memcpy(&dfState->buf.bytes[dfState->contents], data, toFill); // Reduce the size left by the amount copied size -= toFill; // Advance the data pointer by the amount copied data += toFill; // increase the buffer contents count by the amount copied dfState->contents += toFill; pAssert(dfState->contents <= DRBG_IV_SIZE_BYTES); // If we have a full buffer, do a computation pass. if(dfState->contents == DRBG_IV_SIZE_BYTES) DfCompute(dfState); } } //*** DfEnd() // This function is called to get the result of the derivation function computation. // If the buffer is not full, it is padded with zeros. The output buffer is // structured to be the same as a DRBG_SEED value so that the function can return // a pointer to the DRBG_SEED value in the DF_STATE structure. static DRBG_SEED* DfEnd(PDF_STATE dfState) { // Since DfCompute is always called when a buffer is full, there is always // space in the buffer for the terminator dfState->buf.bytes[dfState->contents++] = 0x80; // If the buffer is not full, pad with zeros while(dfState->contents < DRBG_IV_SIZE_BYTES) dfState->buf.bytes[dfState->contents++] = 0; // Do a final state update DfCompute(dfState); return (DRBG_SEED*)&dfState->iv; } //*** DfBuffer() // Function to take an input buffer and do the derivation function to produce a // DRBG_SEED value that can be used in DRBG_Reseed(); static DRBG_SEED* DfBuffer(DRBG_SEED* output, // OUT: receives the result int size, // IN: size of the buffer to add BYTE* buf // IN: address of the buffer ) { DF_STATE dfState; if(size == 0 || buf == NULL) return NULL; // Initialize the derivation function DfStart(&dfState, size); DfUpdate(&dfState, size, buf); DfEnd(&dfState); memcpy(output, &dfState.iv[0], sizeof(DRBG_SEED)); return output; } //*** DRBG_GetEntropy() // Even though this implementation never fails, it may get blocked // indefinitely long in the call to get entropy from the platform // (DRBG_GetEntropy32()). // This function is only used during instantiation of the DRBG for // manufacturing and on each start-up after an non-orderly shutdown. // // Return Type: BOOL // TRUE(1) requested entropy returned // FALSE(0) entropy Failure BOOL DRBG_GetEntropy(UINT32 requiredEntropy, // IN: requested number of bytes of full // entropy BYTE* entropy // OUT: buffer to return collected entropy ) { #if !USE_DEBUG_RNG UINT32 obtainedEntropy; INT32 returnedEntropy; // If in debug mode, always use the self-test values for initialization if(IsSelfTest()) { #endif // If doing simulated DRBG, then check to see if the // entropyFailure condition is being tested if(!IsEntropyBad()) { // In self-test, the caller should be asking for exactly the seed // size of entropy. pAssert(requiredEntropy == sizeof(DRBG_NistTestVector_Entropy)); memcpy(entropy, DRBG_NistTestVector_Entropy, sizeof(DRBG_NistTestVector_Entropy)); } #if !USE_DEBUG_RNG } else if(!IsEntropyBad()) { // Collect entropy // Note: In debug mode, the only "entropy" value ever returned // is the value of the self-test vector. for(returnedEntropy = 1, obtainedEntropy = 0; obtainedEntropy < requiredEntropy && !IsEntropyBad(); obtainedEntropy += returnedEntropy) { returnedEntropy = _plat__GetEntropy(&entropy[obtainedEntropy], requiredEntropy - obtainedEntropy); if(returnedEntropy <= 0) SetEntropyBad(); } } #endif return !IsEntropyBad(); } //*** IncrementIv() // This function increments the IV value by 1. It is used by EncryptDRBG(). void IncrementIv(DRBG_IV* iv) { BYTE* ivP = ((BYTE*)iv) + DRBG_IV_SIZE_BYTES; while((--ivP >= (BYTE*)iv) && ((*ivP = ((*ivP + 1) & 0xFF)) == 0)) ; } //*** EncryptDRBG() // This does the encryption operation for the DRBG. It will encrypt // the input state counter (IV) using the state key. Into the output // buffer for as many times as it takes to generate the required // number of bytes. static BOOL EncryptDRBG(BYTE* dOut, UINT32 dOutBytes, DRBG_KEY_SCHEDULE* keySchedule, DRBG_IV* iv, UINT32* lastValue // Points to the last output value ) { #if FIPS_COMPLIANT // For FIPS compliance, the DRBG has to do a continuous self-test to make sure that // no two consecutive values are the same. This overhead is not incurred if the TPM // is not required to be FIPS compliant // UINT32 temp[DRBG_IV_SIZE_BYTES / sizeof(UINT32)]; int i; BYTE* p; for(; dOutBytes > 0;) { // Increment the IV before each encryption (this is what makes this // different from normal counter-mode encryption IncrementIv(iv); DRBG_ENCRYPT(keySchedule, iv, temp); // Expect a 16 byte block # if DRBG_IV_SIZE_BITS != 128 # error "Unsuppored IV size in DRBG" # endif if((lastValue[0] == temp[0]) && (lastValue[1] == temp[1]) && (lastValue[2] == temp[2]) && (lastValue[3] == temp[3])) { FAIL_BOOL(FATAL_ERROR_ENTROPY); } lastValue[0] = temp[0]; lastValue[1] = temp[1]; lastValue[2] = temp[2]; lastValue[3] = temp[3]; i = MIN(dOutBytes, DRBG_IV_SIZE_BYTES); dOutBytes -= i; for(p = (BYTE*)temp; i > 0; i--) *dOut++ = *p++; } #else // version without continuous self-test NOT_REFERENCED(lastValue); for(; dOutBytes >= DRBG_IV_SIZE_BYTES; dOut = &dOut[DRBG_IV_SIZE_BYTES], dOutBytes -= DRBG_IV_SIZE_BYTES) { // Increment the IV IncrementIv(iv); DRBG_ENCRYPT(keySchedule, iv, dOut); } // If there is a partial, generate into a block-sized // temp buffer and copy to the output. if(dOutBytes != 0) { BYTE temp[DRBG_IV_SIZE_BYTES]; // Increment the IV IncrementIv(iv); DRBG_ENCRYPT(keySchedule, iv, temp); memcpy(dOut, temp, dOutBytes); } #endif return TRUE; } //*** DRBG_Update() // This function performs the state update function. // According to SP800-90A, a temp value is created by doing CTR mode // encryption of 'providedData' and replacing the key and IV with // these values. The one difference is that, with counter mode, the // IV is incremented after each block is encrypted and in this // operation, the counter is incremented before each block is // encrypted. This function implements an 'optimized' version // of the algorithm in that it does the update of the drbgState->seed // in place and then 'providedData' is XORed into drbgState->seed // to complete the encryption of 'providedData'. This works because // the IV is the last thing that gets encrypted. // static BOOL DRBG_Update( DRBG_STATE* drbgState, // IN:OUT state to update DRBG_KEY_SCHEDULE* keySchedule, // IN: the key schedule (optional) DRBG_SEED* providedData // IN: additional data ) { UINT32 i; BYTE* temp = (BYTE*)&drbgState->seed; DRBG_KEY* key = pDRBG_KEY(&drbgState->seed); DRBG_IV* iv = pDRBG_IV(&drbgState->seed); DRBG_KEY_SCHEDULE localKeySchedule; // pAssert(drbgState->magic == DRBG_MAGIC); // If an key schedule was not provided, make one if(keySchedule == NULL) { if(DRBG_ENCRYPT_SETUP((BYTE*)key, DRBG_KEY_SIZE_BITS, &localKeySchedule) != 0) { FAIL_BOOL(FATAL_ERROR_INTERNAL); } keySchedule = &localKeySchedule; } // Encrypt the temp value EncryptDRBG(temp, sizeof(DRBG_SEED), keySchedule, iv, drbgState->lastValue); if(providedData != NULL) { BYTE* pP = (BYTE*)providedData; for(i = DRBG_SEED_SIZE_BYTES; i != 0; i--) *temp++ ^= *pP++; } // Since temp points to the input key and IV, we are done and // don't need to copy the resulting 'temp' to drbgState->seed return TRUE; } //*** DRBG_Reseed() // This function is used when reseeding of the DRBG is required. If // entropy is provided, it is used in lieu of using hardware entropy. // Note: the provided entropy must be the required size. // // Return Type: BOOL // TRUE(1) reseed succeeded // FALSE(0) reseed failed, probably due to the entropy generation BOOL DRBG_Reseed(DRBG_STATE* drbgState, // IN: the state to update DRBG_SEED* providedEntropy, // IN: entropy DRBG_SEED* additionalData // IN: ) { DRBG_SEED seed; pAssert((drbgState != NULL) && (drbgState->magic == DRBG_MAGIC)); if(providedEntropy == NULL) { providedEntropy = &seed; if(!DRBG_GetEntropy(sizeof(DRBG_SEED), (BYTE*)providedEntropy)) return FALSE; } if(additionalData != NULL) { unsigned int i; // XOR the provided data into the provided entropy for(i = 0; i < sizeof(DRBG_SEED); i++) ((BYTE*)providedEntropy)[i] ^= ((BYTE*)additionalData)[i]; } DRBG_Update(drbgState, NULL, providedEntropy); drbgState->reseedCounter = 1; return TRUE; } //*** DRBG_SelfTest() // This is run when the DRBG is instantiated and at startup. // // Return Type: BOOL // TRUE(1) test OK // FALSE(0) test failed BOOL DRBG_SelfTest(void) { BYTE buf[sizeof(DRBG_NistTestVector_Generated)]; DRBG_SEED seed; UINT32 i; BYTE* p; DRBG_STATE testState; // pAssert(!IsSelfTest()); SetSelfTest(); SetDrbgTested(); // Do an instantiate if(!DRBG_Instantiate(&testState, 0, NULL)) return FALSE; #if DRBG_DEBUG_PRINT dbgDumpMemBlock( pDRBG_KEY(&testState), DRBG_KEY_SIZE_BYTES, "Key after Instantiate"); dbgDumpMemBlock( pDRBG_IV(&testState), DRBG_IV_SIZE_BYTES, "Value after Instantiate"); #endif if(DRBG_Generate((RAND_STATE*)&testState, buf, sizeof(buf)) == 0) return FALSE; #if DRBG_DEBUG_PRINT dbgDumpMemBlock( pDRBG_KEY(&testState.seed), DRBG_KEY_SIZE_BYTES, "Key after 1st Generate"); dbgDumpMemBlock( pDRBG_IV(&testState.seed), DRBG_IV_SIZE_BYTES, "Value after 1st Generate"); #endif if(memcmp(buf, DRBG_NistTestVector_GeneratedInterm, sizeof(buf)) != 0) return FALSE; memcpy(seed.bytes, DRBG_NistTestVector_EntropyReseed, sizeof(seed)); DRBG_Reseed(&testState, &seed, NULL); #if DRBG_DEBUG_PRINT dbgDumpMemBlock((BYTE*)pDRBG_KEY(&testState.seed), DRBG_KEY_SIZE_BYTES, "Key after 2nd Generate"); dbgDumpMemBlock((BYTE*)pDRBG_IV(&testState.seed), DRBG_IV_SIZE_BYTES, "Value after 2nd Generate"); dbgDumpMemBlock(buf, sizeof(buf), "2nd Generated"); #endif if(DRBG_Generate((RAND_STATE*)&testState, buf, sizeof(buf)) == 0) return FALSE; if(memcmp(buf, DRBG_NistTestVector_Generated, sizeof(buf)) != 0) return FALSE; ClearSelfTest(); DRBG_Uninstantiate(&testState); for(p = (BYTE*)&testState, i = 0; i < sizeof(DRBG_STATE); i++) { if(*p++) return FALSE; } // Simulate hardware failure to make sure that we get an error when // trying to instantiate SetEntropyBad(); if(DRBG_Instantiate(&testState, 0, NULL)) return FALSE; ClearEntropyBad(); return TRUE; } //** Public Interface //*** Description // The functions in this section are the interface to the RNG. These // are the functions that are used by TPM.lib. //*** CryptRandomStir() // This function is used to cause a reseed. A DRBG_SEED amount of entropy is // collected from the hardware and then additional data is added. // // Return Type: TPM_RC // TPM_RC_NO_RESULT failure of the entropy generator LIB_EXPORT TPM_RC CryptRandomStir(UINT16 additionalDataSize, BYTE* additionalData) { #if !USE_DEBUG_RNG DRBG_SEED tmpBuf; DRBG_SEED dfResult; // // All reseed with outside data starts with a buffer full of entropy if(!DRBG_GetEntropy(sizeof(tmpBuf), (BYTE*)&tmpBuf)) return TPM_RC_NO_RESULT; DRBG_Reseed(&drbgDefault, &tmpBuf, DfBuffer(&dfResult, additionalDataSize, additionalData)); drbgDefault.reseedCounter = 1; return TPM_RC_SUCCESS; #else // If doing debug, use the input data as the initial setting for the RNG state // so that the test can be reset at any time. // Note: If this is called with a data size of 0 or less, nothing happens. The // presumption is that, in a debug environment, the caller will have specific // values for initialization, so this check is just a simple way to prevent // inadvertent programming errors from screwing things up. This doesn't use an // pAssert() because the non-debug version of this function will accept these // parameters as meaning that there is no additionalData and only hardware // entropy is used. if((additionalDataSize > 0) && (additionalData != NULL)) { memset(drbgDefault.seed.bytes, 0, sizeof(drbgDefault.seed.bytes)); memcpy(drbgDefault.seed.bytes, additionalData, MIN(additionalDataSize, sizeof(drbgDefault.seed.bytes))); } drbgDefault.reseedCounter = 1; return TPM_RC_SUCCESS; #endif } //*** CryptRandomGenerate() // Generate a 'randomSize' number or random bytes. LIB_EXPORT UINT16 CryptRandomGenerate(UINT16 randomSize, BYTE* buffer) { return DRBG_Generate((RAND_STATE*)&drbgDefault, buffer, randomSize); } //*** DRBG_InstantiateSeededKdf() // This function is used to instantiate a KDF-based RNG. This is used for derivations. // This function always returns TRUE. LIB_EXPORT BOOL DRBG_InstantiateSeededKdf( KDF_STATE* state, // OUT: buffer to hold the state TPM_ALG_ID hashAlg, // IN: hash algorithm TPM_ALG_ID kdf, // IN: the KDF to use TPM2B* seed, // IN: the seed to use const TPM2B* label, // IN: a label for the generation process. TPM2B* context, // IN: the context value UINT32 limit // IN: Maximum number of bits from the KDF ) { state->magic = KDF_MAGIC; state->limit = limit; state->seed = seed; state->hash = hashAlg; state->kdf = kdf; state->label = label; state->context = context; state->digestSize = CryptHashGetDigestSize(hashAlg); state->counter = 0; state->residual.t.size = 0; return TRUE; } //*** DRBG_AdditionalData() // Function to reseed the DRBG with additional entropy. This is normally called // before computing the protection value of a primary key in the Endorsement // hierarchy. LIB_EXPORT void DRBG_AdditionalData(DRBG_STATE* drbgState, // IN:OUT state to update TPM2B* additionalData // IN: value to incorporate ) { DRBG_SEED dfResult; if(drbgState->magic == DRBG_MAGIC) { DfBuffer(&dfResult, additionalData->size, additionalData->buffer); DRBG_Reseed(drbgState, &dfResult, NULL); } } //*** DRBG_InstantiateSeeded() // This function is used to instantiate a random number generator from seed values. // The nominal use of this generator is to create sequences of pseudo-random // numbers from a seed value. // // Return Type: TPM_RC // TPM_RC_FAILURE DRBG self-test failure LIB_EXPORT TPM_RC DRBG_InstantiateSeeded( DRBG_STATE* drbgState, // IN/OUT: buffer to hold the state const TPM2B* seed, // IN: the seed to use const TPM2B* purpose, // IN: a label for the generation process. const TPM2B* name, // IN: name of the object const TPM2B* additional // IN: additional data ) { DF_STATE dfState; int totalInputSize; // DRBG should have been tested, but... if(!IsDrbgTested() && !DRBG_SelfTest()) { FAIL_RC(FATAL_ERROR_SELF_TEST); } // Initialize the DRBG state memset(drbgState, 0, sizeof(DRBG_STATE)); drbgState->magic = DRBG_MAGIC; // Size all of the values totalInputSize = (seed != NULL) ? seed->size : 0; totalInputSize += (purpose != NULL) ? purpose->size : 0; totalInputSize += (name != NULL) ? name->size : 0; totalInputSize += (additional != NULL) ? additional->size : 0; // Initialize the derivation DfStart(&dfState, totalInputSize); // Run all the input strings through the derivation function if(seed != NULL) DfUpdate(&dfState, seed->size, seed->buffer); if(purpose != NULL) DfUpdate(&dfState, purpose->size, purpose->buffer); if(name != NULL) DfUpdate(&dfState, name->size, name->buffer); if(additional != NULL) DfUpdate(&dfState, additional->size, additional->buffer); // Used the derivation function output as the "entropy" input. This is not // how it is described in SP800-90A but this is the equivalent function DRBG_Reseed(((DRBG_STATE*)drbgState), DfEnd(&dfState), NULL); return TPM_RC_SUCCESS; } //*** CryptRandStartup() // This function is called when TPM_Startup is executed. This function always returns // TRUE. LIB_EXPORT BOOL CryptRandStartup(void) { #if !_DRBG_STATE_SAVE // If not saved in NV, re-instantiate on each startup return DRBG_Instantiate(&drbgDefault, 0, NULL); #else // If the running state is saved in NV, NV has to be loaded before it can // be updated if(go.drbgState.magic == DRBG_MAGIC) return DRBG_Reseed(&go.drbgState, NULL, NULL); else return DRBG_Instantiate(&go.drbgState, 0, NULL); #endif } //*** CryptRandInit() // This function is called when _TPM_Init is being processed. // // Return Type: BOOL // TRUE(1) success // FALSE(0) failure LIB_EXPORT BOOL CryptRandInit(void) { #if !USE_DEBUG_RNG _plat__GetEntropy(NULL, 0); #endif return DRBG_SelfTest(); } //*** DRBG_Generate() // This function generates a random sequence according SP800-90A. // If 'random' is not NULL, then 'randomSize' bytes of random values are generated. // If 'random' is NULL or 'randomSize' is zero, then the function returns // zero without generating any bits or updating the reseed counter. // This function returns the number of bytes produced which could be less than the // number requested if the request is too large ("too large" is implementation // dependent.) LIB_EXPORT UINT16 DRBG_Generate( RAND_STATE* state, BYTE* random, // OUT: buffer to receive the random values UINT16 randomSize // IN: the number of bytes to generate ) { if(state == NULL) state = (RAND_STATE*)&drbgDefault; if(random == NULL) return 0; // If the caller used a KDF state, generate a sequence from the KDF not to // exceed the limit. if(state->kdf.magic == KDF_MAGIC) { KDF_STATE* kdf = (KDF_STATE*)state; UINT32 counter = (UINT32)kdf->counter; INT32 bytesLeft = randomSize; // // If the number of bytes to be returned would put the generator // over the limit, then return 0 if((((kdf->counter * kdf->digestSize) + randomSize) * 8) > kdf->limit) return 0; // Process partial and full blocks until all requested bytes provided while(bytesLeft > 0) { // If there is any residual data in the buffer, copy it to the output // buffer if(kdf->residual.t.size > 0) { INT32 size; // // Don't use more of the residual than will fit or more than are // available size = MIN(kdf->residual.t.size, bytesLeft); // Copy some or all of the residual to the output. The residual is // at the end of the buffer. The residual might be a full buffer. MemoryCopy( random, &kdf->residual.t.buffer[kdf->digestSize - kdf->residual.t.size], size); // Advance the buffer pointer random += size; // Reduce the number of bytes left to get bytesLeft -= size; // And reduce the residual size appropriately kdf->residual.t.size -= (UINT16)size; } else { UINT16 blocks = (UINT16)(bytesLeft / kdf->digestSize); // // Get the number of required full blocks if(blocks > 0) { UINT16 size = blocks * kdf->digestSize; // Get some number of full blocks and put them in the return buffer CryptKDFa(kdf->hash, kdf->seed, kdf->label, kdf->context, NULL, kdf->limit, random, &counter, blocks); // reduce the size remaining to be moved and advance the pointer bytesLeft -= size; random += size; } else { // Fill the residual buffer with a full block and then loop to // top to get part of it copied to the output. kdf->residual.t.size = CryptKDFa(kdf->hash, kdf->seed, kdf->label, kdf->context, NULL, kdf->limit, kdf->residual.t.buffer, &counter, 1); } } } kdf->counter = counter; return randomSize; } else if(state->drbg.magic == DRBG_MAGIC) { DRBG_STATE* drbgState = (DRBG_STATE*)state; DRBG_KEY_SCHEDULE keySchedule; DRBG_SEED* seed = &drbgState->seed; if(drbgState->reseedCounter >= CTR_DRBG_MAX_REQUESTS_PER_RESEED) { if(drbgState == &drbgDefault) { DRBG_Reseed(drbgState, NULL, NULL); if(IsEntropyBad() && !IsSelfTest()) return 0; } else { // If this is a PRNG then the only way to get // here is if the SW has run away. FAIL_IMMEDIATE(FATAL_ERROR_INTERNAL, 0); } } // if the allowed number of bytes in a request is larger than the // less than the number of bytes that can be requested, then check #if UINT16_MAX >= CTR_DRBG_MAX_BYTES_PER_REQUEST if(randomSize > CTR_DRBG_MAX_BYTES_PER_REQUEST) randomSize = CTR_DRBG_MAX_BYTES_PER_REQUEST; #endif // Create encryption schedule if(DRBG_ENCRYPT_SETUP( (BYTE*)pDRBG_KEY(seed), DRBG_KEY_SIZE_BITS, &keySchedule) != 0) { FAIL_IMMEDIATE(FATAL_ERROR_INTERNAL, 0); } // Generate the random data EncryptDRBG( random, randomSize, &keySchedule, pDRBG_IV(seed), drbgState->lastValue); // Do a key update DRBG_Update(drbgState, &keySchedule, NULL); // Increment the reseed counter drbgState->reseedCounter += 1; } else { // invalid DRBG state structure FAIL_IMMEDIATE(FATAL_ERROR_INTERNAL, 0); } return randomSize; } //*** DRBG_Instantiate() // This is CTR_DRBG_Instantiate_algorithm() from [SP 800-90A 10.2.1.3.1]. // This is called when a the TPM DRBG is to be instantiated. This is // called to instantiate a DRBG used by the TPM for normal // operations. // // Return Type: BOOL // TRUE(1) instantiation succeeded // FALSE(0) instantiation failed LIB_EXPORT BOOL DRBG_Instantiate( DRBG_STATE* drbgState, // OUT: the instantiated value UINT16 pSize, // IN: Size of personalization string BYTE* personalization // IN: The personalization string ) { DRBG_SEED seed; DRBG_SEED dfResult; // pAssert((pSize == 0) || (pSize <= sizeof(seed)) || (personalization != NULL)); // If the DRBG has not been tested, test when doing an instantiation. Since // Instantiation is called during self test, make sure we don't get stuck in a // loop. if(!IsDrbgTested() && !IsSelfTest() && !DRBG_SelfTest()) return FALSE; // If doing a self test, DRBG_GetEntropy will return the NIST // test vector value. if(!DRBG_GetEntropy(sizeof(seed), (BYTE*)&seed)) return FALSE; // set everything to zero memset(drbgState, 0, sizeof(DRBG_STATE)); drbgState->magic = DRBG_MAGIC; // Steps 1, 2, 3, 6, 7 of SP 800-90A 10.2.1.3.1 are exactly what // reseeding does. So, do a reduction on the personalization value (if any) // and do a reseed. DRBG_Reseed(drbgState, &seed, DfBuffer(&dfResult, pSize, personalization)); return TRUE; } //*** DRBG_Uninstantiate() // This is Uninstantiate_function() from [SP 800-90A 9.4]. // // Return Type: TPM_RC // TPM_RC_VALUE not a valid state LIB_EXPORT TPM_RC DRBG_Uninstantiate( DRBG_STATE* drbgState // IN/OUT: working state to erase ) { if((drbgState == NULL) || (drbgState->magic != DRBG_MAGIC)) return TPM_RC_VALUE; memset(drbgState, 0, sizeof(DRBG_STATE)); return TPM_RC_SUCCESS; }