255 lines
11 KiB
C
255 lines
11 KiB
C
/* This file includes functions that were extracted from the TPM2
|
|
* source, but were present in files not included in compilation.
|
|
*/
|
|
#include "Global.h"
|
|
#include "CryptoEngine.h"
|
|
|
|
#include <string.h>
|
|
|
|
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;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
UINT16 _cpri__GenerateSeededRandom(
|
|
INT32 randomSize, // IN: the size of the request
|
|
BYTE * random, // OUT: receives the data
|
|
TPM_ALG_ID hashAlg, // IN: used by KDF version but not here
|
|
TPM2B * seed, // IN: the seed value
|
|
const char *label, // IN: a label string (optional)
|
|
TPM2B * partyU, // IN: other data (oprtional)
|
|
TPM2B * partyV // IN: still more (optional)
|
|
)
|
|
{
|
|
return (_cpri__KDFa(hashAlg, seed, label, partyU, partyV,
|
|
randomSize * 8, random, NULL, FALSE));
|
|
}
|