/* 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); | |
} |