924 lines
33 KiB
C
924 lines
33 KiB
C
// 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);
|
|
}
|