diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 21:41:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 21:41:43 +0000 |
commit | 92cccad89d1c12b39165d5f0ed7ccd2d44965a1a (patch) | |
tree | f59a2764cd8c50959050a428bd8fc935138df750 /src/tpm2/crypto/openssl/CryptRand.c | |
parent | Initial commit. (diff) | |
download | libtpms-92cccad89d1c12b39165d5f0ed7ccd2d44965a1a.tar.xz libtpms-92cccad89d1c12b39165d5f0ed7ccd2d44965a1a.zip |
Adding upstream version 0.9.2.upstream/0.9.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | src/tpm2/crypto/openssl/CryptRand.c | 881 |
1 files changed, 881 insertions, 0 deletions
diff --git a/src/tpm2/crypto/openssl/CryptRand.c b/src/tpm2/crypto/openssl/CryptRand.c new file mode 100644 index 0000000..5bf0643 --- /dev/null +++ b/src/tpm2/crypto/openssl/CryptRand.c @@ -0,0 +1,881 @@ +/********************************************************************************/ +/* */ +/* DRBG with a behavior according to SP800-90A */ +/* Written by Ken Goldman */ +/* IBM Thomas J. Watson Research Center */ +/* $Id: CryptRand.c 1658 2021-01-22 23:14:01Z kgoldman $ */ +/* */ +/* 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 - 2021 */ +/* */ +/********************************************************************************/ + +#include "Tpm.h" +#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}; + +/* 10.2.16.2.2 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 struct +{ + DRBG_KEY_SCHEDULE keySchedule; + DRBG_IV iv[DF_COUNT]; + DRBG_IV out1; + DRBG_IV buf; + int contents; +} DF_STATE, *PDF_STATE; +/* 10.2.16.2.3 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; +} +/* 10.2.16.2.4 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; +} +/* 10.2.16.2.5 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); + } +} +/* 10.2.16.2.6 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; +} +/* 10.2.16.2.7 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; +} +/* 10.2.16.2.8 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 Values Meaning */ +/* TRUE Requested entropy returned */ +/* FALSE 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())/* This function increments the IV value by 1. It is used by EncryptDRBG(). */ + { + // 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(); +} + +void +IncrementIv( + DRBG_IV *iv + ) +{ + BYTE *ivP = ((BYTE *)iv) + DRBG_IV_SIZE_BYTES; + while((--ivP >= (BYTE *)iv) && ((*ivP = ((*ivP + 1) & 0xFF)) == 0)); +} +/* 10.2.16.2.10 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]) + ) + { + LOG_FAILURE(FATAL_ERROR_ENTROPY); + return FALSE; + } + 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; +} +/* 10.2.16.2.11 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; + memset(&localKeySchedule, 0, sizeof(localKeySchedule)); /* libtpms added: coverity */ + // + 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) + { + LOG_FAILURE(FATAL_ERROR_INTERNAL); + return FALSE; + } + 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; +} +/* 10.2.16.2.12 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 Values Meaning */ +/* TRUE reseed succeeded */ +/* FALSE 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; +} +/* 10.2.16.2.13 DRBG_SelfTest() */ +/* This is run when the DRBG is instantiated and at startup */ +/* Return Values Meaning */ +/* FALSE test failed */ +/* TRUE test OK */ +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; +} +/* 10.2.16.3 Public Interface */ + +/* 10.2.16.3.1 Description */ +/* The functions in this section are the interface to the RNG. These are the functions that are used + by TPM.lib. */ + +/* 10.2.16.3.2 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. */ +/* Error Returns Meaning */ +/* 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 +} +/* 10.2.16.3.3 CryptRandomGenerate() */ +/* Generate a randomSize number or random bytes. */ +LIB_EXPORT UINT16 +CryptRandomGenerate( + UINT16 randomSize, + BYTE *buffer + ) +{ + return DRBG_Generate((RAND_STATE *)&drbgDefault, buffer, randomSize); +} +/* 10.2.16.3.4 DRBG_InstantiateSeededKdf() */ +/* Function 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; +} +/* 10.2.16.3.5 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); + } +} +/* 10.2.16.3.6 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. */ +/* Returns + 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 + SEED_COMPAT_LEVEL seedCompatLevel // IN: compatibility level; libtpms added + ) +{ + DF_STATE dfState; + int totalInputSize; + // DRBG should have been tested, but... + if(!IsDrbgTested() && !DRBG_SelfTest()) + { + LOG_FAILURE(FATAL_ERROR_SELF_TEST); + return TPM_RC_FAILURE; + } + // Initialize the DRBG state + memset(drbgState, 0, sizeof(DRBG_STATE)); + drbgState->magic = DRBG_MAGIC; + drbgState->seedCompatLevel = seedCompatLevel; // libtpms added + // 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; +} +/* 10.2.16.3.7 CryptRandStartup() */ +/* This function is called when TPM_Startup() is executed. */ +/* TRUE instantiation succeeded */ /* kgold */ +/* FALSE instantiation failed */ +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 +} +/* 10.2.16.3.8 CryptRandInit() */ +/* This function is called when _TPM_Init() is being processed */ +LIB_EXPORT BOOL +CryptRandInit( + void + ) +{ +#if !USE_DEBUG_RNG + _plat__GetEntropy(NULL, 0); +#endif + return DRBG_SelfTest(); +} +// libtpms added begin +LIB_EXPORT SEED_COMPAT_LEVEL +DRBG_GetSeedCompatLevel( + RAND_STATE *state + ) +{ + if(state == NULL) + { + return SEED_COMPAT_LEVEL_LAST; + } + else if(state->drbg.magic == DRBG_MAGIC) + { + DRBG_STATE *drbgState = (DRBG_STATE *)state; + + return drbgState->seedCompatLevel; + } + else + { + return SEED_COMPAT_LEVEL_LAST; + } +} +// libtpms added end +/* 10.2.16.5 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 TRUE without generating any bits or updating the reseed counter. This + function returns 0 if a reseed is required. Otherwise, it 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; + memset(&keySchedule, 0, sizeof(keySchedule)); /* libtpms added: coverity */ + 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. + LOG_FAILURE(FATAL_ERROR_INTERNAL); + return 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) + { + LOG_FAILURE(FATAL_ERROR_INTERNAL); + return 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 + { + LOG_FAILURE(FATAL_ERROR_INTERNAL); + return 0; // libtpms changed from FALSE + } + return randomSize; +} +/* 10.2.16.6 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 Values Meaning */ +/* TRUE instantiation succeeded */ +/* FALSE 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; +} +/* 10.2.16.7 DRBG_Uninstantiate() */ +/* This is Uninstantiate_function() from [SP 800-90A 9.4]. */ +/* Error Returns Meaning */ +/* 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; +} + |