blob: ae2e4c47588f026fb3493e4e67d371611f0d2c23 [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.
*/
//** Description
//
// This file contains implementation of cryptographic functions for hashing.
//
//** Includes, Defines, and Types
#define _CRYPT_HASH_C_
#include "Tpm.h"
#include "CryptHash_fp.h"
#include "CryptHash.h"
#include "OIDs.h"
// Instance each of the hash descriptors based on the implemented algorithms
FOR_EACH_HASH(HASH_DEF_TEMPLATE)
// Instance a 'null' def.
HASH_DEF NULL_Def = {{0}};
// Create a table of pointers to the defined hash definitions
#define HASH_DEF_ENTRY(HASH, Hash) &Hash##_Def,
PHASH_DEF HashDefArray[] = {
// for each implemented HASH, expands to: &HASH_Def,
FOR_EACH_HASH(HASH_DEF_ENTRY)
&NULL_Def
};
#undef HASH_DEF_ENTRY
//** Obligatory Initialization Functions
//*** CryptHashInit()
// This function is called by _TPM_Init do perform the initialization operations for
// the library.
BOOL
CryptHashInit(
void
)
{
LibHashInit();
return TRUE;
}
//*** CryptHashStartup()
// This function is called by TPM2_Startup(). It checks that the size of the
// HashDefArray is consistent with the HASH_COUNT.
BOOL
CryptHashStartup(
void
)
{
int i = sizeof(HashDefArray) / sizeof(PHASH_DEF) - 1;
return (i == HASH_COUNT);
}
//** Hash Information Access Functions
//*** Introduction
// These functions provide access to the hash algorithm description information.
//*** CryptGetHashDef()
// This function accesses the hash descriptor associated with a hash a
// algorithm. The function returns a pointer to a 'null' descriptor if hashAlg is
// TPM_ALG_NULL or not a defined algorithm.
PHASH_DEF
CryptGetHashDef(
TPM_ALG_ID hashAlg
)
{
#define GET_DEF(HASH, Hash) case ALG_##HASH##_VALUE: return &Hash##_Def;
switch(hashAlg)
{
FOR_EACH_HASH(GET_DEF)
default:
return &NULL_Def;
}
#undef GET_DEF
}
//*** CryptHashIsValidAlg()
// This function tests to see if an algorithm ID is a valid hash algorithm. If
// flag is true, then TPM_ALG_NULL is a valid hash.
// Return Type: BOOL
// TRUE(1) hashAlg is a valid, implemented hash on this TPM
// FALSE(0) hashAlg is not valid for this TPM
BOOL
CryptHashIsValidAlg(
TPM_ALG_ID hashAlg, // IN: the algorithm to check
BOOL flag // IN: TRUE if TPM_ALG_NULL is to be treated
// as a valid hash
)
{
if(hashAlg == TPM_ALG_NULL)
return flag;
return CryptGetHashDef(hashAlg) != &NULL_Def;
}
//*** CryptHashGetAlgByIndex()
// 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 an 'index' of 2 will return the
// last. All other index values will return TPM_ALG_NULL.
//
// Return Type: TPM_ALG_ID
// TPM_ALG_xxx a hash algorithm
// TPM_ALG_NULL this can be used as a stop value
LIB_EXPORT TPM_ALG_ID
CryptHashGetAlgByIndex(
UINT32 index // IN: the index
)
{
TPM_ALG_ID hashAlg;
if(index >= HASH_COUNT)
hashAlg = TPM_ALG_NULL;
else
hashAlg = HashDefArray[index]->hashAlg;
return hashAlg;
}
//*** CryptHashGetDigestSize()
// Returns the size of the digest produced by the hash. If 'hashAlg' is not a hash
// algorithm, the TPM will FAIL.
// Return Type: UINT16
// 0 TPM_ALG_NULL
// > 0 the digest size
//
LIB_EXPORT UINT16
CryptHashGetDigestSize(
TPM_ALG_ID hashAlg // IN: hash algorithm to look up
)
{
return CryptGetHashDef(hashAlg)->digestSize;
}
//*** CryptHashGetBlockSize()
// Returns the size of the block used by the hash. If 'hashAlg' is not a hash
// algorithm, the TPM will FAIL.
// Return Type: UINT16
// 0 TPM_ALG_NULL
// > 0 the digest size
//
LIB_EXPORT UINT16
CryptHashGetBlockSize(
TPM_ALG_ID hashAlg // IN: hash algorithm to look up
)
{
return CryptGetHashDef(hashAlg)->blockSize;
}
//*** CryptHashGetOid()
// This function returns a pointer to DER=encoded OID for a hash algorithm. All OIDs
// are full OID values including the Tag (0x06) and length byte.
LIB_EXPORT const BYTE *
CryptHashGetOid(
TPM_ALG_ID hashAlg
)
{
return CryptGetHashDef(hashAlg)->OID;
}
//*** CryptHashGetContextAlg()
// This function returns the hash algorithm associated with a hash context.
TPM_ALG_ID
CryptHashGetContextAlg(
PHASH_STATE state // IN: the context to check
)
{
return state->hashAlg;
}
//** State Import and Export
//*** CryptHashCopyState
// This function is used to clone a HASH_STATE.
LIB_EXPORT void
CryptHashCopyState(
HASH_STATE *out, // OUT: destination of the state
const HASH_STATE *in // IN: source of the state
)
{
pAssert(out->type == in->type);
out->hashAlg = in->hashAlg;
out->def = in->def;
if(in->hashAlg != TPM_ALG_NULL)
{
HASH_STATE_COPY(out, in);
}
if(in->type == HASH_STATE_HMAC)
{
const HMAC_STATE *hIn = (HMAC_STATE *)in;
HMAC_STATE *hOut = (HMAC_STATE *)out;
hOut->hmacKey = hIn->hmacKey;
}
return;
}
//*** CryptHashExportState()
// This function is used to export a hash or HMAC hash state. This function
// would be called when preparing to context save a sequence object.
void
CryptHashExportState(
PCHASH_STATE internalFmt, // IN: the hash state formatted for use by
// library
PEXPORT_HASH_STATE externalFmt // OUT: the exported hash state
)
{
BYTE *outBuf = (BYTE *)externalFmt;
//
cAssert(sizeof(HASH_STATE) <= sizeof(EXPORT_HASH_STATE));
// the following #define is used to move data from an aligned internal data
// structure to a byte buffer (external format data.
#define CopyToOffset(value) \
memcpy(&outBuf[offsetof(HASH_STATE,value)], &internalFmt->value, \
sizeof(internalFmt->value))
// Copy the hashAlg
CopyToOffset(hashAlg);
CopyToOffset(type);
#ifdef HASH_STATE_SMAC
if(internalFmt->type == HASH_STATE_SMAC)
{
memcpy(outBuf, internalFmt, sizeof(HASH_STATE));
return;
}
#endif
if(internalFmt->type == HASH_STATE_HMAC)
{
HMAC_STATE *from = (HMAC_STATE *)internalFmt;
memcpy(&outBuf[offsetof(HMAC_STATE, hmacKey)], &from->hmacKey,
sizeof(from->hmacKey));
}
if(internalFmt->hashAlg != TPM_ALG_NULL)
HASH_STATE_EXPORT(externalFmt, internalFmt);
}
//*** CryptHashImportState()
// This function is used to import the hash state. This function
// would be called to import a hash state when the context of a sequence object
// was being loaded.
void
CryptHashImportState(
PHASH_STATE internalFmt, // OUT: the hash state formatted for use by
// the library
PCEXPORT_HASH_STATE externalFmt // IN: the exported hash state
)
{
BYTE *inBuf = (BYTE *)externalFmt;
//
#define CopyFromOffset(value) \
memcpy(&internalFmt->value, &inBuf[offsetof(HASH_STATE,value)], \
sizeof(internalFmt->value))
// Copy the hashAlg of the byte-aligned input structure to the structure-aligned
// internal structure.
CopyFromOffset(hashAlg);
CopyFromOffset(type);
if(internalFmt->hashAlg != TPM_ALG_NULL)
{
#ifdef HASH_STATE_SMAC
if(internalFmt->type == HASH_STATE_SMAC)
{
memcpy(internalFmt, inBuf, sizeof(HASH_STATE));
return;
}
#endif
internalFmt->def = CryptGetHashDef(internalFmt->hashAlg);
HASH_STATE_IMPORT(internalFmt, inBuf);
if(internalFmt->type == HASH_STATE_HMAC)
{
HMAC_STATE *to = (HMAC_STATE *)internalFmt;
memcpy(&to->hmacKey, &inBuf[offsetof(HMAC_STATE, hmacKey)],
sizeof(to->hmacKey));
}
}
}
//** State Modification Functions
//***HashEnd()
// Local function to complete a hash that uses the hashDef instead of an algorithm
// ID. This function is used to complete the hash and only return a partial digest.
// The return value is the size of the data copied.
static UINT16
HashEnd(
PHASH_STATE hashState, // IN: the hash state
UINT32 dOutSize, // IN: the size of receive buffer
PBYTE dOut // OUT: the receive buffer
)
{
BYTE temp[MAX_DIGEST_SIZE];
if((hashState->hashAlg == TPM_ALG_NULL)
|| (hashState->type != HASH_STATE_HASH))
dOutSize = 0;
if(dOutSize > 0)
{
hashState->def = CryptGetHashDef(hashState->hashAlg);
// Set the final size
dOutSize = MIN(dOutSize, hashState->def->digestSize);
// Complete into the temp buffer and then copy
HASH_END(hashState, temp);
// Don't want any other functions calling the HASH_END method
// directly.
#undef HASH_END
memcpy(dOut, &temp, dOutSize);
}
hashState->type = HASH_STATE_EMPTY;
return (UINT16)dOutSize;
}
//*** CryptHashStart()
// 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.
//
// This function does not use the sequence parameter. If it is necessary to import
// or export context, this will start the sequence in a local state
// and export the state to the input buffer. Will need to add a flag to the state
// structure to indicate that it needs to be imported before it can be used.
// (BLEH).
// Return Type: UINT16
// 0 hash is TPM_ALG_NULL
// >0 digest size
LIB_EXPORT UINT16
CryptHashStart(
PHASH_STATE hashState, // OUT: the running hash state
TPM_ALG_ID hashAlg // IN: hash algorithm
)
{
UINT16 retVal;
TEST(hashAlg);
hashState->hashAlg = hashAlg;
if(hashAlg == TPM_ALG_NULL)
{
retVal = 0;
}
else
{
hashState->def = CryptGetHashDef(hashAlg);
HASH_START(hashState);
retVal = hashState->def->digestSize;
}
#undef HASH_START
hashState->type = HASH_STATE_HASH;
return retVal;
}
//*** CryptDigestUpdate()
// Add data to a hash or HMAC, SMAC stack.
//
void
CryptDigestUpdate(
PHASH_STATE hashState, // IN: the hash context information
UINT32 dataSize, // IN: the size of data to be added
const BYTE *data // IN: data to be hashed
)
{
if(hashState->hashAlg != TPM_ALG_NULL)
{
if((hashState->type == HASH_STATE_HASH)
|| (hashState->type == HASH_STATE_HMAC))
HASH_DATA(hashState, dataSize, (BYTE *)data);
#if SMAC_IMPLEMENTED
else if(hashState->type == HASH_STATE_SMAC)
(hashState->state.smac.smacMethods.data)(&hashState->state.smac.state,
dataSize, data);
#endif // SMAC_IMPLEMENTED
else
FAIL(FATAL_ERROR_INTERNAL);
}
return;
}
//*** CryptHashEnd()
// 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 Type: UINT16
// 0 no data returned
// > 0 the number of bytes in the digest or dOutSize, whichever is smaller
LIB_EXPORT UINT16
CryptHashEnd(
PHASH_STATE hashState, // IN: the state of hash stack
UINT32 dOutSize, // IN: size of digest buffer
BYTE *dOut // OUT: hash digest
)
{
pAssert(hashState->type == HASH_STATE_HASH);
return HashEnd(hashState, dOutSize, dOut);
}
//*** CryptHashBlock()
// 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 Type: UINT16
// >= 0 number of bytes placed in 'dOut'
LIB_EXPORT UINT16
CryptHashBlock(
TPM_ALG_ID hashAlg, // IN: The hash algorithm
UINT32 dataSize, // IN: size of buffer to hash
const BYTE *data, // IN: the buffer to hash
UINT32 dOutSize, // IN: size of the digest buffer
BYTE *dOut // OUT: digest buffer
)
{
HASH_STATE state;
CryptHashStart(&state, hashAlg);
CryptDigestUpdate(&state, dataSize, data);
return HashEnd(&state, dOutSize, dOut);
}
//*** CryptDigestUpdate2B()
// This function updates a digest (hash or HMAC) with a TPM2B.
//
// This function can be used for both HMAC and hash functions so the
// 'digestState' is void so that either state type can be passed.
LIB_EXPORT void
CryptDigestUpdate2B(
PHASH_STATE state, // IN: the digest state
const TPM2B *bIn // IN: 2B containing the data
)
{
// Only compute the digest if a pointer to the 2B is provided.
// In CryptDigestUpdate(), if size is zero or buffer is NULL, then no change
// to the digest occurs. This function should not provide a buffer if bIn is
// not provided.
pAssert(bIn != NULL);
CryptDigestUpdate(state, bIn->size, bIn->buffer);
return;
}
//*** CryptHashEnd2B()
// This function is the same as CryptCompleteHash() but the digest is
// placed in a TPM2B. This is the most common use and this is provided
// for specification clarity. 'digest.size' should be set to indicate the number of
// bytes to place in the buffer
// Return Type: UINT16
// >=0 the number of bytes placed in 'digest.buffer'
LIB_EXPORT UINT16
CryptHashEnd2B(
PHASH_STATE state, // IN: the hash state
P2B digest // IN: the size of the buffer Out: requested
// number of bytes
)
{
return CryptHashEnd(state, digest->size, digest->buffer);
}
//*** CryptDigestUpdateInt()
// This function is used to include an integer value to a hash stack. The function
// marshals the integer into its canonical form before calling CryptDigestUpdate().
LIB_EXPORT void
CryptDigestUpdateInt(
void *state, // IN: the state of hash stack
UINT32 intSize, // IN: the size of 'intValue' in bytes
UINT64 intValue // IN: integer value to be hashed
)
{
#if LITTLE_ENDIAN_TPM
intValue = REVERSE_ENDIAN_64(intValue);
#endif
CryptDigestUpdate(state, intSize, &((BYTE *)&intValue)[8 - intSize]);
}
//** HMAC Functions
//*** CryptHmacStart()
// 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 Type: UINT16
// >= 0 number of bytes in digest produced by 'hashAlg' (may be zero)
//
LIB_EXPORT UINT16
CryptHmacStart(
PHMAC_STATE state, // IN/OUT: the state buffer
TPM_ALG_ID hashAlg, // IN: the algorithm to use
UINT16 keySize, // IN: the size of the HMAC key
const BYTE *key // IN: the HMAC key
)
{
PHASH_DEF hashDef;
BYTE * pb;
UINT32 i;
//
hashDef = CryptGetHashDef(hashAlg);
if(hashDef->digestSize != 0)
{
// If the HMAC key is larger than the hash block size, it has to be reduced
// to fit. The reduction is a digest of the hashKey.
if(keySize > hashDef->blockSize)
{
// if the key is too big, reduce it to a digest of itself
state->hmacKey.t.size = CryptHashBlock(hashAlg, keySize, key,
hashDef->digestSize,
state->hmacKey.t.buffer);
}
else
{
memcpy(state->hmacKey.t.buffer, key, keySize);
state->hmacKey.t.size = keySize;
}
// XOR the key with iPad (0x36)
pb = state->hmacKey.t.buffer;
for(i = state->hmacKey.t.size; i > 0; i--)
*pb++ ^= 0x36;
// if the keySize is smaller than a block, fill the rest with 0x36
for(i = hashDef->blockSize - state->hmacKey.t.size; i > 0; i--)
*pb++ = 0x36;
// Increase the oPadSize to a full block
state->hmacKey.t.size = hashDef->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
CryptHashStart((PHASH_STATE)state, hashAlg);
CryptDigestUpdate((PHASH_STATE)state, state->hmacKey.t.size,
state->hmacKey.t.buffer);
// XOR the key block with 0x5c ^ 0x36
for(pb = state->hmacKey.t.buffer, i = hashDef->blockSize; i > 0; i--)
*pb++ ^= (0x5c ^ 0x36);
}
// Set the hash algorithm
state->hashState.hashAlg = hashAlg;
// Set the hash state type
state->hashState.type = HASH_STATE_HMAC;
return hashDef->digestSize;
}
//*** CryptHmacEnd()
// 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 Type: UINT16
// >= 0 number of bytes in 'dOut' (may be zero)
LIB_EXPORT UINT16
CryptHmacEnd(
PHMAC_STATE state, // IN: the hash state buffer
UINT32 dOutSize, // IN: size of digest buffer
BYTE *dOut // OUT: hash digest
)
{
BYTE temp[MAX_DIGEST_SIZE];
PHASH_STATE hState = (PHASH_STATE)&state->hashState;
#if SMAC_IMPLEMENTED
if(hState->type == HASH_STATE_SMAC)
return (state->hashState.state.smac.smacMethods.end)
(&state->hashState.state.smac.state,
dOutSize,
dOut);
#endif
pAssert(hState->type == HASH_STATE_HMAC);
hState->def = CryptGetHashDef(hState->hashAlg);
// Change the state type for completion processing
hState->type = HASH_STATE_HASH;
if(hState->hashAlg == TPM_ALG_NULL)
dOutSize = 0;
else
{
// Complete the current hash
HashEnd(hState, hState->def->digestSize, temp);
// Do another hash starting with the oPad
CryptHashStart(hState, hState->hashAlg);
CryptDigestUpdate(hState, state->hmacKey.t.size, state->hmacKey.t.buffer);
CryptDigestUpdate(hState, hState->def->digestSize, temp);
}
return HashEnd(hState, dOutSize, dOut);
}
//*** CryptHmacStart2B()
// This function starts an HMAC and returns the size of the digest
// that will be produced.
//
// This function is provided to support the most common use of starting an HMAC
// with a TPM2B key.
//
// The caller must provide a block of memory in which the hash sequence state
// is kept. The caller should not alter the contents of this buffer until the
// hash sequence is completed or abandoned.
//
// Return Type: UINT16
// > 0 the digest size of the algorithm
// = 0 the hashAlg was TPM_ALG_NULL
LIB_EXPORT UINT16
CryptHmacStart2B(
PHMAC_STATE hmacState, // OUT: the state of HMAC stack. It will be used
// in HMAC update and completion
TPMI_ALG_HASH hashAlg, // IN: hash algorithm
P2B key // IN: HMAC key
)
{
return CryptHmacStart(hmacState, hashAlg, key->size, key->buffer);
}
//*** CryptHmacEnd2B()
// This function is the same as CryptHmacEnd() but the HMAC result
// is returned in a TPM2B which is the most common use.
// Return Type: UINT16
// >=0 the number of bytes placed in 'digest'
LIB_EXPORT UINT16
CryptHmacEnd2B(
PHMAC_STATE hmacState, // IN: the state of HMAC stack
P2B digest // OUT: HMAC
)
{
return CryptHmacEnd(hmacState, digest->size, digest->buffer);
}
//** Mask and Key Generation Functions
//*** CryptMGF_KDF()
// This function performs MGF1/KDF1 or KDF2 using the selected hash. KDF1 and KDF2 are
// T('n') = T('n'-1) || H('seed' || 'counter') with the difference being that, with
// KDF1, 'counter' starts at 0 but with KDF2, 'counter' starts at 1. The caller
// determines which version by setting the initial value of counter to either 0 or 1.
// Note: Any value that is not 0 is considered to be 1.
//
// This function returns the length of the mask produced which
// could be zero if the digest algorithm is not supported
// Return Type: UINT16
// 0 hash algorithm was TPM_ALG_NULL
// > 0 should be the same as 'mSize'
LIB_EXPORT UINT16
CryptMGF_KDF(
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 seedSize, // IN: size of the seed
BYTE *seed, // IN: seed size
UINT32 counter // IN: counter initial value
)
{
HASH_STATE hashState;
PHASH_DEF hDef = CryptGetHashDef(hashAlg);
UINT32 hLen;
UINT32 bytes;
//
// If there is no digest to compute return
if((hDef->digestSize == 0) || (mSize == 0))
return 0;
if(counter != 0)
counter = 1;
hLen = hDef->digestSize;
for(bytes = 0; bytes < mSize; bytes += hLen)
{
// Start the hash and include the seed and counter
CryptHashStart(&hashState, hashAlg);
CryptDigestUpdate(&hashState, seedSize, seed);
CryptDigestUpdateInt(&hashState, 4, counter);
// Get as much as will fit.
CryptHashEnd(&hashState, MIN((mSize - bytes), hLen),
&mask[bytes]);
counter++;
}
return (UINT16)mSize;
}
//*** CryptKDFa()
// 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 Type: UINT16
// 0 hash algorithm is not supported or is TPM_ALG_NULL
// > 0 the number of bytes in the 'keyStream' buffer
LIB_EXPORT UINT16
CryptKDFa(
TPM_ALG_ID hashAlg, // IN: hash algorithm used in HMAC
const TPM2B *key, // IN: HMAC key
const TPM2B *label, // IN: a label for the KDF
const TPM2B *contextU, // IN: context U
const TPM2B *contextV, // IN: context V
UINT32 sizeInBits, // IN: size of generated key in bits
BYTE *keyStream, // OUT: key buffer
UINT32 *counterInOut, // IN/OUT: caller may provide the iteration
// counter for incremental operations to
// avoid large intermediate buffers.
UINT16 blocks // IN: If non-zero, this is the maximum number
// of blocks to be returned, regardless
// of sizeInBits
)
{
UINT32 counter = 0; // counter value
INT16 bytes; // number of bytes to produce
UINT16 generated; // number of bytes generated
BYTE *stream = keyStream;
HMAC_STATE hState;
UINT16 digestSize = CryptHashGetDigestSize(hashAlg);
pAssert(key != NULL && keyStream != NULL);
TEST(TPM_ALG_KDF1_SP800_108);
if(digestSize == 0)
return 0;
if(counterInOut != NULL)
counter = *counterInOut;
// If the size of the request is larger than the numbers will handle,
// it is a fatal error.
pAssert(((sizeInBits + 7) / 8) <= INT16_MAX);
// The number of bytes to be generated is the smaller of the sizeInBits bytes or
// the number of requested blocks. The number of blocks is the smaller of the
// number requested or the number allowed by sizeInBits. A partial block is
// a full block.
bytes = (blocks > 0) ? blocks * digestSize : (UINT16)BITS_TO_BYTES(sizeInBits);
generated = bytes;
// Generate required bytes
for(; bytes > 0; bytes -= digestSize)
{
counter++;
// Start HMAC
if(CryptHmacStart(&hState, hashAlg, key->size, key->buffer) == 0)
return 0;
// Adding counter
CryptDigestUpdateInt(&hState.hashState, 4, counter);
// Adding label
if(label != NULL)
HASH_DATA(&hState.hashState, label->size, (BYTE *)label->buffer);
// Add a null. SP108 is not very clear about when the 0 is needed but to
// make this like the previous version that did not add an 0x00 after
// a null-terminated string, this version will only add a null byte
// if the label parameter did not end in a null byte, or if no label
// is present.
if((label == NULL)
|| (label->size == 0)
|| (label->buffer[label->size - 1] != 0))
CryptDigestUpdateInt(&hState.hashState, 1, 0);
// Adding contextU
if(contextU != NULL)
HASH_DATA(&hState.hashState, contextU->size, contextU->buffer);
// Adding contextV
if(contextV != NULL)
HASH_DATA(&hState.hashState, contextV->size, contextV->buffer);
// Adding size in bits
CryptDigestUpdateInt(&hState.hashState, 4, sizeInBits);
// Complete and put the data in the buffer
CryptHmacEnd(&hState, bytes, stream);
stream = &stream[digestSize];
}
// Masking in the KDF is disabled. If the calling function wants something
// less than even number of bytes, then the caller should do the masking
// because there is no universal way to do it here
if(counterInOut != NULL)
*counterInOut = counter;
return generated;
}
//*** CryptKDFe()
// This function implements 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 Type: UINT16
// 0 hash algorithm is not supported or is TPM_ALG_NULL
// > 0 the number of bytes in the 'keyStream' buffer
//
LIB_EXPORT UINT16
CryptKDFe(
TPM_ALG_ID hashAlg, // IN: hash algorithm used in HMAC
TPM2B *Z, // IN: Z
const TPM2B *label, // IN: a label value for the KDF
TPM2B *partyUInfo, // IN: PartyUInfo
TPM2B *partyVInfo, // IN: PartyVInfo
UINT32 sizeInBits, // IN: size of generated key in bits
BYTE *keyStream // OUT: key buffer
)
{
HASH_STATE hashState;
PHASH_DEF hashDef = CryptGetHashDef(hashAlg);
UINT32 counter = 0; // counter value
UINT16 hLen;
BYTE *stream = keyStream;
INT16 bytes; // number of bytes to generate
pAssert(keyStream != NULL && Z != NULL && ((sizeInBits + 7) / 8) < INT16_MAX);
//
hLen = hashDef->digestSize;
bytes = (INT16)((sizeInBits + 7) / 8);
if(hashAlg == TPM_ALG_NULL || bytes == 0)
return 0;
// Generate required bytes
//The inner loop of that KDF uses:
// Hash[i] := H(counter | Z | OtherInfo) (5)
// Where:
// Hash[i] 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++;
// Do the hash
CryptHashStart(&hashState, hashAlg);
// Add counter
CryptDigestUpdateInt(&hashState, 4, counter);
// Add Z
if(Z != NULL)
CryptDigestUpdate2B(&hashState, Z);
// Add label
if(label != NULL)
CryptDigestUpdate2B(&hashState, label);
// Add a null. SP108 is not very clear about when the 0 is needed but to
// make this like the previous version that did not add an 0x00 after
// a null-terminated string, this version will only add a null byte
// if the label parameter did not end in a null byte, or if no label
// is present.
if((label == NULL)
|| (label->size == 0)
|| (label->buffer[label->size - 1] != 0))
CryptDigestUpdateInt(&hashState, 1, 0);
// Add PartyUInfo
if(partyUInfo != NULL)
CryptDigestUpdate2B(&hashState, partyUInfo);
// Add PartyVInfo
if(partyVInfo != NULL)
CryptDigestUpdate2B(&hashState, partyVInfo);
// Compute Hash. hLen was changed to be the smaller of bytes or hLen
// at the start of each iteration.
CryptHashEnd(&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 (UINT16)((sizeInBits + 7) / 8);
}