blob: d4d7065e61941dbae3db3ee6b20b7bb148a5f3c5 [file] [log] [blame]
/* Microsoft Reference Implementation for TPM 2.0
*
* The copyright in this software is being made available under the BSD License,
* included below. This software may be subject to other third party and
* contributor rights, including patent rights, and no such rights are granted
* under this license.
*
* Copyright (c) Microsoft Corporation
*
* All rights reserved.
*
* BSD License
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this list
* of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ""AS IS""
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
//** 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 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])
)
{
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;
}
//*** 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)
{
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;
}
//*** 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())
{
LOG_FAILURE(FATAL_ERROR_SELF_TEST);
return TPM_RC_FAILURE;
}
// 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.
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 FALSE;
}
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;
}