// This file was extracted from the TCG Published
// Trusted Platform Module Library
// Part 4: Supporting Routines
// Family "2.0"
// Level 00 Revision 01.16
// October 30, 2014
#include <string.h>
#include "OsslCryptoEngine.h"
#include "CpriHashData.c"
#define OSSL_HASH_STATE_DATA_SIZE (MAX_HASH_STATE_SIZE - 8)
typedef struct {
union {
EVP_MD_CTX context;
BYTE data[OSSL_HASH_STATE_DATA_SIZE];
} u;
INT16 copySize;
} OSSL_HASH_STATE;
//
// Temporary aliasing of SM3 to SHA256 until SM3 is available
//
#define EVP_sm3_256 EVP_sha256
//
//
// Static Functions
//
// GetHashServer()
//
// This function returns the address of the hash server function
//
static EVP_MD *
GetHashServer(
TPM_ALG_ID hashAlg
)
{
switch (hashAlg)
{
#ifdef TPM_ALG_SHA1
case TPM_ALG_SHA1:
return (EVP_MD *)EVP_sha1();
break;
#endif
#ifdef TPM_ALG_SHA256
case TPM_ALG_SHA256:
return (EVP_MD *)EVP_sha256();
break;
#endif
#ifdef TPM_ALG_SHA384
case TPM_ALG_SHA384:
return (EVP_MD *)EVP_sha384();
break;
#endif
#ifdef TPM_ALG_SHA512
case TPM_ALG_SHA512:
return (EVP_MD *)EVP_sha512();
break;
#endif
#ifdef TPM_ALG_SM3_256
case TPM_ALG_SM3_256:
return (EVP_MD *)EVP_sm3_256();
break;
#endif
case TPM_ALG_NULL:
return NULL;
default:
FAIL(FATAL_ERROR_INTERNAL);
}
return NULL; // Never reached.
}
//
//
// MarshalHashState()
//
// This function copies an OpenSSL() hash context into a caller provided buffer.
//
// Return Value Meaning
//
// >0 the number of bytes of buf used.
//
static UINT16
MarshalHashState(
EVP_MD_CTX *ctxt, // IN: Context to marshal
BYTE *buf // OUT: The buffer that will receive the
// context. This buffer is at least
// MAX_HASH_STATE_SIZE byte
)
{
// make sure everything will fit
pAssert(ctxt->digest->ctx_size <= OSSL_HASH_STATE_DATA_SIZE);
// Copy the context data
memcpy(buf, (void*) ctxt->md_data, ctxt->digest->ctx_size);
return (UINT16)ctxt->digest->ctx_size;
}
//
//
// GetHashState()
//
// This function will unmarshal a caller provided buffer into an OpenSSL() hash context. The function returns
// the number of bytes copied (which may be zero).
//
static UINT16
GetHashState(
EVP_MD_CTX *ctxt, // OUT: The context structure to receive the
// result of unmarshaling.
TPM_ALG_ID algType, // IN: The hash algorithm selector
BYTE *buf // IN: Buffer containing marshaled hash data
)
{
EVP_MD *evpmdAlgorithm = NULL;
pAssert(ctxt != NULL);
EVP_MD_CTX_init(ctxt);
evpmdAlgorithm = GetHashServer(algType);
if(evpmdAlgorithm == NULL)
return 0;
// This also allocates the ctxt->md_data
if((EVP_DigestInit_ex(ctxt, evpmdAlgorithm, NULL)) != 1)
FAIL(FATAL_ERROR_INTERNAL);
pAssert(ctxt->digest->ctx_size < sizeof(ALIGNED_HASH_STATE));
memcpy(ctxt->md_data, buf, ctxt->digest->ctx_size);
//
return (UINT16)ctxt->digest->ctx_size;
}
//
//
// GetHashInfoPointer()
//
// This function returns a pointer to the hash info for the algorithm. If the algorithm is not supported, function
// returns a pointer to the data block associated with TPM_ALG_NULL.
//
static const HASH_INFO *
GetHashInfoPointer(
TPM_ALG_ID hashAlg
)
{
UINT32 i, tableSize;
// Get the table size of g_hashData
tableSize = sizeof(g_hashData) / sizeof(g_hashData[0]);
for(i = 0; i < tableSize - 1; i++)
{
if(g_hashData[i].alg == hashAlg)
return &g_hashData[i];
}
return &g_hashData[tableSize-1];
}
//
//
// Hash Functions
//
// _cpri__HashStartup()
//
// Function that is called to initialize the hash service. In this implementation, this function does nothing but
// it is called by the CryptUtilStartup() function and must be present.
//
LIB_EXPORT BOOL
_cpri__HashStartup(
void
)
{
// On startup, make sure that the structure sizes are compatible. It would
// be nice if this could be done at compile time but I couldn't figure it out.
CPRI_HASH_STATE *cpriState = NULL;
// NUMBYTES evpCtxSize = sizeof(EVP_MD_CTX);
NUMBYTES cpriStateSize = sizeof(cpriState->state);
// OSSL_HASH_STATE *osslState;
NUMBYTES osslStateSize = sizeof(OSSL_HASH_STATE);
// int dataSize = sizeof(osslState->u.data);
pAssert(cpriStateSize >= osslStateSize);
return TRUE;
}
//
//
// _cpri__GetHashAlgByIndex()
//
// This function is used to iterate through the hashes. TPM_ALG_NULL is returned for all indexes that are
// not valid hashes. If the TPM implements 3 hashes, then an index value of 0 will return the first
// implemented hash and and index of 2 will return the last. All other index values will return
// TPM_ALG_NULL.
//
//
//
//
// Return Value Meaning
//
// TPM_ALG_xxx() a hash algorithm
// TPM_ALG_NULL this can be used as a stop value
//
LIB_EXPORT TPM_ALG_ID
_cpri__GetHashAlgByIndex(
UINT32 index // IN: the index
)
{
if(index >= HASH_COUNT)
return TPM_ALG_NULL;
return g_hashData[index].alg;
}
//
//
// _cpri__GetHashBlockSize()
//
// Returns the size of the block used for the hash
//
// Return Value Meaning
//
// <0 the algorithm is not a supported hash
// >= the digest size (0 for TPM_ALG_NULL)
//
LIB_EXPORT UINT16
_cpri__GetHashBlockSize(
TPM_ALG_ID hashAlg // IN: hash algorithm to look up
)
{
return GetHashInfoPointer(hashAlg)->blockSize;
}
//
//
// _cpri__GetHashDER
//
// This function returns a pointer to the DER string for the algorithm and indicates its size.
//
LIB_EXPORT UINT16
_cpri__GetHashDER(
TPM_ALG_ID hashAlg, // IN: the algorithm to look up
const BYTE **p
)
{
const HASH_INFO *q;
q = GetHashInfoPointer(hashAlg);
*p = &q->der[0];
return q->derSize;
}
//
//
// _cpri__GetDigestSize()
//
// Gets the digest size of the algorithm. The algorithm is required to be supported.
//
// Return Value Meaning
//
// =0 the digest size for TPM_ALG_NULL
// >0 the digest size of a hash algorithm
//
LIB_EXPORT UINT16
_cpri__GetDigestSize(
TPM_ALG_ID hashAlg // IN: hash algorithm to look up
)
{
return GetHashInfoPointer(hashAlg)->digestSize;
}
//
//
// _cpri__GetContextAlg()
//
// This function returns the algorithm associated with a hash context
//
LIB_EXPORT TPM_ALG_ID
_cpri__GetContextAlg(
CPRI_HASH_STATE *hashState // IN: the hash context
)
{
return hashState->hashAlg;
}
//
//
// _cpri__CopyHashState
//
// This function is used to clone a CPRI_HASH_STATE. The return value is the size of the state.
//
LIB_EXPORT UINT16
_cpri__CopyHashState (
CPRI_HASH_STATE *out, // OUT: destination of the state
CPRI_HASH_STATE *in // IN: source of the state
)
{
OSSL_HASH_STATE *i = (OSSL_HASH_STATE *)&in->state;
OSSL_HASH_STATE *o = (OSSL_HASH_STATE *)&out->state;
pAssert(sizeof(i) <= sizeof(in->state));
EVP_MD_CTX_init(&o->u.context);
EVP_MD_CTX_copy_ex(&o->u.context, &i->u.context);
o->copySize = i->copySize;
out->hashAlg = in->hashAlg;
return sizeof(CPRI_HASH_STATE);
}
//
//
// _cpri__StartHash()
//
// Functions starts a hash stack Start a hash stack and returns the digest size. As a side effect, the value of
// stateSize in hashState is updated to indicate the number of bytes of state that were saved. This function
// calls GetHashServer() and that function will put the TPM into failure mode if the hash algorithm is not
// supported.
//
// Return Value Meaning
//
// 0 hash is TPM_ALG_NULL
// >0 digest size
//
LIB_EXPORT UINT16
_cpri__StartHash(
TPM_ALG_ID hashAlg, // IN: hash algorithm
BOOL sequence, // IN: TRUE if the state should be saved
CPRI_HASH_STATE *hashState // OUT: the state of hash stack.
)
{
EVP_MD_CTX localState;
OSSL_HASH_STATE *state = (OSSL_HASH_STATE *)&hashState->state;
BYTE *stateData = state->u.data;
EVP_MD_CTX *context;
EVP_MD *evpmdAlgorithm = NULL;
UINT16 retVal = 0;
if(sequence)
context = &localState;
else
context = &state->u.context;
hashState->hashAlg = hashAlg;
EVP_MD_CTX_init(context);
evpmdAlgorithm = GetHashServer(hashAlg);
if(evpmdAlgorithm == NULL)
goto Cleanup;
if(EVP_DigestInit_ex(context, evpmdAlgorithm, NULL) != 1)
FAIL(FATAL_ERROR_INTERNAL);
retVal = (CRYPT_RESULT)EVP_MD_CTX_size(context);
Cleanup:
if(retVal > 0)
{
if (sequence)
{
if((state->copySize = MarshalHashState(context, stateData)) == 0)
{
// If MarshalHashState returns a negative number, it is an error
// code and not a hash size so copy the error code to be the return
// from this function and set the actual stateSize to zero.
retVal = state->copySize;
state->copySize = 0;
}
// Do the cleanup
EVP_MD_CTX_cleanup(context);
}
else
state->copySize = -1;
}
else
state->copySize = 0;
return retVal;
}
//
//
// _cpri__UpdateHash()
//
// Add data to a hash or HMAC stack.
//
LIB_EXPORT void
_cpri__UpdateHash(
CPRI_HASH_STATE *hashState, // IN: the hash context information
UINT32 dataSize, // IN: the size of data to be added to the
// digest
BYTE *data // IN: data to be hashed
)
{
EVP_MD_CTX localContext;
OSSL_HASH_STATE *state = (OSSL_HASH_STATE *)&hashState->state;
BYTE *stateData = state->u.data;
EVP_MD_CTX *context;
CRYPT_RESULT retVal = CRYPT_SUCCESS;
//
// If there is no context, return
if(state->copySize == 0)
return;
if(state->copySize > 0)
{
context = &localContext;
if((retVal = GetHashState(context, hashState->hashAlg, stateData)) <= 0)
return;
}
else
context = &state->u.context;
if(EVP_DigestUpdate(context, data, dataSize) != 1)
FAIL(FATAL_ERROR_INTERNAL);
else if( state->copySize > 0
&& (retVal= MarshalHashState(context, stateData)) >= 0)
{
// retVal is the size of the marshaled data. Make sure that it is consistent
// by ensuring that we didn't get more than allowed
if(retVal < state->copySize)
FAIL(FATAL_ERROR_INTERNAL);
else
EVP_MD_CTX_cleanup(context);
}
return;
}
//
//
// _cpri__CompleteHash()
//
// Complete a hash or HMAC computation. This function will place the smaller of digestSize or the size of
// the digest in dOut. The number of bytes in the placed in the buffer is returned. If there is a failure, the
// returned value is <= 0.
//
// Return Value Meaning
//
// 0 no data returned
// >0 the number of bytes in the digest
//
LIB_EXPORT UINT16
_cpri__CompleteHash(
CPRI_HASH_STATE *hashState, // IN: the state of hash stack
UINT32 dOutSize, // IN: size of digest buffer
BYTE *dOut // OUT: hash digest
)
{
EVP_MD_CTX localState;
OSSL_HASH_STATE *state = (OSSL_HASH_STATE *)&hashState->state;
BYTE *stateData = state->u.data;
EVP_MD_CTX *context;
UINT16 retVal;
int hLen;
BYTE temp[MAX_DIGEST_SIZE];
BYTE *rBuffer = dOut;
if(state->copySize == 0)
return 0;
if(state->copySize > 0)
{
context = &localState;
if((retVal = GetHashState(context, hashState->hashAlg, stateData)) <= 0)
goto Cleanup;
}
else
context = &state->u.context;
hLen = EVP_MD_CTX_size(context);
if((unsigned)hLen > dOutSize)
rBuffer = temp;
if(EVP_DigestFinal_ex(context, rBuffer, NULL) == 1)
{
if(rBuffer != dOut)
{
if(dOut != NULL)
{
memcpy(dOut, temp, dOutSize);
}
retVal = (UINT16)dOutSize;
}
else
{
retVal = (UINT16)hLen;
}
state->copySize = 0;
}
else
{
retVal = 0; // Indicate that no data is returned
}
Cleanup:
EVP_MD_CTX_cleanup(context);
return retVal;
}
//
//
// _cpri__ImportExportHashState()
//
// This function is used to import or export the hash state. This function would be called to export state when
// a sequence object was being prepared for export
//
LIB_EXPORT void
_cpri__ImportExportHashState(
CPRI_HASH_STATE *osslFmt, // IN/OUT: the hash state formated for use
// by openSSL
EXPORT_HASH_STATE *externalFmt, // IN/OUT: the exported hash state
IMPORT_EXPORT direction //
)
{
UNREFERENCED_PARAMETER(direction);
UNREFERENCED_PARAMETER(externalFmt);
UNREFERENCED_PARAMETER(osslFmt);
return;
#if 0
if(direction == IMPORT_STATE)
{
// don't have the import export functions yet so just copy
_cpri__CopyHashState(osslFmt, (CPRI_HASH_STATE *)externalFmt);
}
else
{
_cpri__CopyHashState((CPRI_HASH_STATE *)externalFmt, osslFmt);
}
#endif
}
//
//
//
// _cpri__HashBlock()
//
// Start a hash, hash a single block, update digest and return the size of the results.
// The digestSize parameter can be smaller than the digest. If so, only the more significant bytes are
// returned.
//
// Return Value Meaning
//
// >= 0 number of bytes in digest (may be zero)
//
LIB_EXPORT UINT16
_cpri__HashBlock(
TPM_ALG_ID hashAlg, // IN: The hash algorithm
UINT32 dataSize, // IN: size of buffer to hash
BYTE *data, // IN: the buffer to hash
UINT32 digestSize, // IN: size of the digest buffer
BYTE *digest // OUT: hash digest
)
{
EVP_MD_CTX hashContext;
EVP_MD *hashServer = NULL;
UINT16 retVal = 0;
BYTE b[MAX_DIGEST_SIZE]; // temp buffer in case digestSize not
// a full digest
unsigned int dSize = _cpri__GetDigestSize(hashAlg);
// If there is no digest to compute return
if(dSize == 0)
return 0;
// After the call to EVP_MD_CTX_init(), will need to call EVP_MD_CTX_cleanup()
EVP_MD_CTX_init(&hashContext); // Initialize the local hash context
hashServer = GetHashServer(hashAlg); // Find the hash server
// It is an error if the digest size is non-zero but there is no server
if( (hashServer == NULL)
|| (EVP_DigestInit_ex(&hashContext, hashServer, NULL) != 1)
|| (EVP_DigestUpdate(&hashContext, data, dataSize) != 1))
FAIL(FATAL_ERROR_INTERNAL);
else
{
// If the size of the digest produced (dSize) is larger than the available
// buffer (digestSize), then put the digest in a temp buffer and only copy
// the most significant part into the available buffer.
if(dSize > digestSize)
{
if(EVP_DigestFinal_ex(&hashContext, b, &dSize) != 1)
FAIL(FATAL_ERROR_INTERNAL);
memcpy(digest, b, digestSize);
retVal = (UINT16)digestSize;
}
else
{
if((EVP_DigestFinal_ex(&hashContext, digest, &dSize)) != 1)
FAIL(FATAL_ERROR_INTERNAL);
retVal = (UINT16) dSize;
}
}
EVP_MD_CTX_cleanup(&hashContext);
return retVal;
}
//
//
//
// HMAC Functions
//
// _cpri__StartHMAC
//
// This function is used to start an HMAC using a temp hash context. The function does the initialization of
// the hash with the HMAC key XOR iPad and updates the HMAC key XOR oPad.
// The function returns the number of bytes in a digest produced by hashAlg.
//
// Return Value Meaning
//
// >= 0 number of bytes in digest produced by hashAlg (may be zero)
//
LIB_EXPORT UINT16
_cpri__StartHMAC(
TPM_ALG_ID hashAlg, // IN: the algorithm to use
BOOL sequence, // IN: indicates if the state should be
// saved
CPRI_HASH_STATE *state, // IN/OUT: the state buffer
UINT16 keySize, // IN: the size of the HMAC key
BYTE *key, // IN: the HMAC key
TPM2B *oPadKey // OUT: the key prepared for the oPad round
)
{
CPRI_HASH_STATE localState;
UINT16 blockSize = _cpri__GetHashBlockSize(hashAlg);
UINT16 digestSize;
BYTE *pb; // temp pointer
UINT32 i;
// If the key size is larger than the block size, then the hash of the key
// is used as the key
if(keySize > blockSize)
{
// large key so digest
if((digestSize = _cpri__StartHash(hashAlg, FALSE, &localState)) == 0)
return 0;
_cpri__UpdateHash(&localState, keySize, key);
_cpri__CompleteHash(&localState, digestSize, oPadKey->buffer);
oPadKey->size = digestSize;
}
else
{
// key size is ok
memcpy(oPadKey->buffer, key, keySize);
oPadKey->size = keySize;
}
// XOR the key with iPad (0x36)
pb = oPadKey->buffer;
for(i = oPadKey->size; i > 0; i--)
*pb++ ^= 0x36;
// if the keySize is smaller than a block, fill the rest with 0x36
for(i = blockSize - oPadKey->size; i > 0; i--)
*pb++ = 0x36;
// Increase the oPadSize to a full block
oPadKey->size = blockSize;
// Start a new hash with the HMAC key
// This will go in the caller's state structure and may be a sequence or not
if((digestSize = _cpri__StartHash(hashAlg, sequence, state)) > 0)
{
_cpri__UpdateHash(state, oPadKey->size, oPadKey->buffer);
// XOR the key block with 0x5c ^ 0x36
for(pb = oPadKey->buffer, i = blockSize; i > 0; i--)
*pb++ ^= (0x5c ^ 0x36);
}
return digestSize;
}
//
//
// _cpri_CompleteHMAC()
//
// This function is called to complete an HMAC. It will finish the current digest, and start a new digest. It will
// then add the oPadKey and the completed digest and return the results in dOut. It will not return more than
// dOutSize bytes.
//
// Return Value Meaning
//
// >= 0 number of bytes in dOut (may be zero)
//
LIB_EXPORT UINT16
_cpri__CompleteHMAC(
CPRI_HASH_STATE *hashState, // IN: the state of hash stack
TPM2B *oPadKey, // IN: the HMAC key in oPad format
UINT32 dOutSize, // IN: size of digest buffer
BYTE *dOut // OUT: hash digest
)
{
BYTE digest[MAX_DIGEST_SIZE];
CPRI_HASH_STATE *state = (CPRI_HASH_STATE *)hashState;
CPRI_HASH_STATE localState;
UINT16 digestSize = _cpri__GetDigestSize(state->hashAlg);
_cpri__CompleteHash(hashState, digestSize, digest);
// Using the local hash state, do a hash with the oPad
if(_cpri__StartHash(state->hashAlg, FALSE, &localState) != digestSize)
return 0;
_cpri__UpdateHash(&localState, oPadKey->size, oPadKey->buffer);
_cpri__UpdateHash(&localState, digestSize, digest);
return _cpri__CompleteHash(&localState, dOutSize, dOut);
}
//
//
// Mask and Key Generation Functions
//
// _crypi_MGF1()
//
// This function performs MGF1 using the selected hash. MGF1 is T(n) = T(n-1) || H(seed || counter). This
// function returns the length of the mask produced which could be zero if the digest algorithm is not
// supported
//
// Return Value Meaning
//
// 0 hash algorithm not supported
// >0 should be the same as mSize
//
LIB_EXPORT CRYPT_RESULT
_cpri__MGF1(
UINT32 mSize, // IN: length of the mask to be produced
BYTE *mask, // OUT: buffer to receive the mask
TPM_ALG_ID hashAlg, // IN: hash to use
UINT32 sSize, // IN: size of the seed
BYTE *seed // IN: seed size
)
{
EVP_MD_CTX hashContext;
EVP_MD *hashServer = NULL;
CRYPT_RESULT retVal = 0;
BYTE b[MAX_DIGEST_SIZE]; // temp buffer in case mask is not an
// even multiple of a full digest
CRYPT_RESULT dSize = _cpri__GetDigestSize(hashAlg);
unsigned int digestSize = (UINT32)dSize;
UINT32 remaining;
UINT32 counter;
BYTE swappedCounter[4];
// Parameter check
if(mSize > (1024*16)) // Semi-arbitrary maximum
FAIL(FATAL_ERROR_INTERNAL);
// If there is no digest to compute return
if(dSize <= 0)
return 0;
EVP_MD_CTX_init(&hashContext); // Initialize the local hash context
hashServer = GetHashServer(hashAlg); // Find the hash server
if(hashServer == NULL)
// If there is no server, then there is no digest
return 0;
for(counter = 0, remaining = mSize; remaining > 0; counter++)
{
// Because the system may be either Endian...
UINT32_TO_BYTE_ARRAY(counter, swappedCounter);
// Start the hash and include the seed and counter
if( (EVP_DigestInit_ex(&hashContext, hashServer, NULL) != 1)
|| (EVP_DigestUpdate(&hashContext, seed, sSize) != 1)
|| (EVP_DigestUpdate(&hashContext, swappedCounter, 4) != 1)
)
FAIL(FATAL_ERROR_INTERNAL);
// Handling the completion depends on how much space remains in the mask
// buffer. If it can hold the entire digest, put it there. If not
// put the digest in a temp buffer and only copy the amount that
// will fit into the mask buffer.
if(remaining < (unsigned)dSize)
{
if(EVP_DigestFinal_ex(&hashContext, b, &digestSize) != 1)
FAIL(FATAL_ERROR_INTERNAL);
memcpy(mask, b, remaining);
break;
}
else
{
if(EVP_DigestFinal_ex(&hashContext, mask, &digestSize) != 1)
FAIL(FATAL_ERROR_INTERNAL);
remaining -= dSize;
mask = &mask[dSize];
}
retVal = (CRYPT_RESULT)mSize;
}
EVP_MD_CTX_cleanup(&hashContext);
return retVal;
}
//
//
// _cpri_KDFa()
//
// This function performs the key generation according to Part 1 of the TPM specification.
// This function returns the number of bytes generated which may be zero.
// The key and keyStream pointers are not allowed to be NULL. The other pointer values may be NULL.
// The value of sizeInBits must be no larger than (2^18)-1 = 256K bits (32385 bytes).
// The once parameter is set to allow incremental generation of a large value. If this flag is TRUE,
// sizeInBits will be used in the HMAC computation but only one iteration of the KDF is performed. This
// would be used for XOR obfuscation so that the mask value can be generated in digest-sized chunks
// rather than having to be generated all at once in an arbitrarily large buffer and then XORed() into the
// result. If once is TRUE, then sizeInBits must be a multiple of 8.
// Any error in the processing of this command is considered fatal.
//
// Return Value Meaning
//
// 0 hash algorithm is not supported or is TPM_ALG_NULL
// >0 the number of bytes in the keyStream buffer
//
LIB_EXPORT UINT16
_cpri__KDFa(
TPM_ALG_ID hashAlg, // IN: hash algorithm used in HMAC
TPM2B *key, // IN: HMAC key
const char *label, // IN: a 0-byte terminated label used in KDF
TPM2B *contextU, // IN: context U
TPM2B *contextV, // IN: context V
UINT32 sizeInBits, // IN: size of generated key in bit
BYTE *keyStream, // OUT: key buffer
UINT32 *counterInOut, // IN/OUT: caller may provide the iteration
// counter for incremental operations to
// avoid large intermediate buffers.
BOOL once // IN: TRUE if only one iteration is performed
// FALSE if iteration count determined by
// "sizeInBits"
)
{
UINT32 counter = 0; // counter value
INT32 lLen = 0; // length of the label
INT16 hLen; // length of the hash
INT16 bytes; // number of bytes to produce
BYTE *stream = keyStream;
BYTE marshaledUint32[4];
CPRI_HASH_STATE hashState;
TPM2B_MAX_HASH_BLOCK hmacKey;
pAssert(key != NULL && keyStream != NULL);
pAssert(once == FALSE || (sizeInBits & 7) == 0);
if(counterInOut != NULL)
counter = *counterInOut;
// Prepare label buffer. Calculate its size and keep the last 0 byte
if(label != NULL)
for(lLen = 0; label[lLen++] != 0; );
// Get the hash size. If it is less than or 0, either the
// algorithm is not supported or the hash is TPM_ALG_NULL
//
// In either case the digest size is zero. This is the only return
// other than the one at the end. All other exits from this function
// are fatal errors. After we check that the algorithm is supported
// anything else that goes wrong is an implementation flaw.
if((hLen = (INT16) _cpri__GetDigestSize(hashAlg)) == 0)
return 0;
// If the size of the request is larger than the numbers will handle,
// it is a fatal error.
pAssert(((sizeInBits + 7)/ 8) <= INT16_MAX);
bytes = once ? hLen : (INT16)((sizeInBits + 7) / 8);
// Generate required bytes
for (; bytes > 0; stream = &stream[hLen], bytes = bytes - hLen)
{
if(bytes < hLen)
hLen = bytes;
counter++;
// Start HMAC
if(_cpri__StartHMAC(hashAlg,
FALSE,
&hashState,
key->size,
&key->buffer[0],
&hmacKey.b) <= 0)
FAIL(FATAL_ERROR_INTERNAL);
// Adding counter
UINT32_TO_BYTE_ARRAY(counter, marshaledUint32);
_cpri__UpdateHash(&hashState, sizeof(UINT32), marshaledUint32);
// Adding label
if(label != NULL)
_cpri__UpdateHash(&hashState, lLen, (BYTE *)label);
// Adding contextU
if(contextU != NULL)
_cpri__UpdateHash(&hashState, contextU->size, contextU->buffer);
// Adding contextV
if(contextV != NULL)
_cpri__UpdateHash(&hashState, contextV->size, contextV->buffer);
// Adding size in bits
UINT32_TO_BYTE_ARRAY(sizeInBits, marshaledUint32);
_cpri__UpdateHash(&hashState, sizeof(UINT32), marshaledUint32);
// Compute HMAC. At the start of each iteration, hLen is set
// to the smaller of hLen and bytes. This causes bytes to decrement
// exactly to zero to complete the loop
_cpri__CompleteHMAC(&hashState, &hmacKey.b, hLen, stream);
}
// Mask off bits if the required bits is not a multiple of byte size
if((sizeInBits % 8) != 0)
keyStream[0] &= ((1 << (sizeInBits % 8)) - 1);
if(counterInOut != NULL)
*counterInOut = counter;
return (CRYPT_RESULT)((sizeInBits + 7)/8);
}
//
//
//
// _cpri__KDFe()
//
// KDFe() as defined in TPM specification part 1.
// This function returns the number of bytes generated which may be zero.
// The Z and keyStream pointers are not allowed to be NULL. The other pointer values may be NULL. The
// value of sizeInBits must be no larger than (2^18)-1 = 256K bits (32385 bytes). Any error in the processing
// of this command is considered fatal.
//
// Return Value Meaning
//
// 0 hash algorithm is not supported or is TPM_ALG_NULL
// >0 the number of bytes in the keyStream buffer
//
LIB_EXPORT UINT16
_cpri__KDFe(
TPM_ALG_ID hashAlg, // IN: hash algorithm used in HMAC
TPM2B *Z, // IN: Z
const char *label, // IN: a 0 terminated label using in KDF
TPM2B *partyUInfo, // IN: PartyUInfo
TPM2B *partyVInfo, // IN: PartyVInfo
UINT32 sizeInBits, // IN: size of generated key in bit
BYTE *keyStream // OUT: key buffer
)
{
UINT32 counter = 0; // counter value
UINT32 lSize = 0;
BYTE *stream = keyStream;
CPRI_HASH_STATE hashState;
INT16 hLen = (INT16) _cpri__GetDigestSize(hashAlg);
INT16 bytes; // number of bytes to generate
BYTE marshaledUint32[4];
pAssert( keyStream != NULL
&& Z != NULL
&& ((sizeInBits + 7) / 8) < INT16_MAX);
if(hLen == 0)
return 0;
bytes = (INT16)((sizeInBits + 7) / 8);
// Prepare label buffer. Calculate its size and keep the last 0 byte
if(label != NULL)
for(lSize = 0; label[lSize++] != 0;);
// Generate required bytes
//The inner loop of that KDF uses:
// Hashi := H(counter | Z | OtherInfo) (5)
// Where:
// Hashi the hash generated on the i-th iteration of the loop.
// H() an approved hash function
// counter a 32-bit counter that is initialized to 1 and incremented
// on each iteration
// Z the X coordinate of the product of a public ECC key and a
// different private ECC key.
// OtherInfo a collection of qualifying data for the KDF defined below.
// In this specification, OtherInfo will be constructed by:
// OtherInfo := Use | PartyUInfo | PartyVInfo
for (; bytes > 0; stream = &stream[hLen], bytes = bytes - hLen)
{
if(bytes < hLen)
hLen = bytes;
//
counter++;
// Start hash
if(_cpri__StartHash(hashAlg, FALSE, &hashState) == 0)
return 0;
// Add counter
UINT32_TO_BYTE_ARRAY(counter, marshaledUint32);
_cpri__UpdateHash(&hashState, sizeof(UINT32), marshaledUint32);
// Add Z
if(Z != NULL)
_cpri__UpdateHash(&hashState, Z->size, Z->buffer);
// Add label
if(label != NULL)
_cpri__UpdateHash(&hashState, lSize, (BYTE *)label);
else
// The SP800-108 specification requires a zero between the label
// and the context.
_cpri__UpdateHash(&hashState, 1, (BYTE *)"");
// Add PartyUInfo
if(partyUInfo != NULL)
_cpri__UpdateHash(&hashState, partyUInfo->size, partyUInfo->buffer);
// Add PartyVInfo
if(partyVInfo != NULL)
_cpri__UpdateHash(&hashState, partyVInfo->size, partyVInfo->buffer);
// Compute Hash. hLen was changed to be the smaller of bytes or hLen
// at the start of each iteration.
_cpri__CompleteHash(&hashState, hLen, stream);
}
// Mask off bits if the required bits is not a multiple of byte size
if((sizeInBits % 8) != 0)
keyStream[0] &= ((1 << (sizeInBits % 8)) - 1);
return (CRYPT_RESULT)((sizeInBits + 7) / 8);
}