diff options
Diffstat (limited to '')
-rw-r--r-- | src/tpm2/crypto/openssl/CryptHash.c | 871 |
1 files changed, 871 insertions, 0 deletions
diff --git a/src/tpm2/crypto/openssl/CryptHash.c b/src/tpm2/crypto/openssl/CryptHash.c new file mode 100644 index 0000000..cb5bd0f --- /dev/null +++ b/src/tpm2/crypto/openssl/CryptHash.c @@ -0,0 +1,871 @@ +/********************************************************************************/ +/* */ +/* Implementation of cryptographic functions for hashing. */ +/* Written by Ken Goldman */ +/* IBM Thomas J. Watson Research Center */ +/* $Id: CryptHash.c 1658 2021-01-22 23:14:01Z kgoldman $ */ +/* */ +/* Licenses and Notices */ +/* */ +/* 1. Copyright Licenses: */ +/* */ +/* - Trusted Computing Group (TCG) grants to the user of the source code in */ +/* this specification (the "Source Code") a worldwide, irrevocable, */ +/* nonexclusive, royalty free, copyright license to reproduce, create */ +/* derivative works, distribute, display and perform the Source Code and */ +/* derivative works thereof, and to grant others the rights granted herein. */ +/* */ +/* - The TCG grants to the user of the other parts of the specification */ +/* (other than the Source Code) the rights to reproduce, distribute, */ +/* display, and perform the specification solely for the purpose of */ +/* developing products based on such documents. */ +/* */ +/* 2. Source Code Distribution Conditions: */ +/* */ +/* - Redistributions of Source Code must retain the above copyright licenses, */ +/* this list of conditions and the following disclaimers. */ +/* */ +/* - Redistributions in binary form must reproduce the above copyright */ +/* licenses, this list of conditions and the following disclaimers in the */ +/* documentation and/or other materials provided with the distribution. */ +/* */ +/* 3. Disclaimers: */ +/* */ +/* - THE COPYRIGHT LICENSES SET FORTH ABOVE DO NOT REPRESENT ANY FORM OF */ +/* LICENSE OR WAIVER, EXPRESS OR IMPLIED, BY ESTOPPEL OR OTHERWISE, WITH */ +/* RESPECT TO PATENT RIGHTS HELD BY TCG MEMBERS (OR OTHER THIRD PARTIES) */ +/* THAT MAY BE NECESSARY TO IMPLEMENT THIS SPECIFICATION OR OTHERWISE. */ +/* Contact TCG Administration (admin@trustedcomputinggroup.org) for */ +/* information on specification licensing rights available through TCG */ +/* membership agreements. */ +/* */ +/* - THIS SPECIFICATION IS PROVIDED "AS IS" WITH NO EXPRESS OR IMPLIED */ +/* WARRANTIES WHATSOEVER, INCLUDING ANY WARRANTY OF MERCHANTABILITY OR */ +/* FITNESS FOR A PARTICULAR PURPOSE, ACCURACY, COMPLETENESS, OR */ +/* NONINFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS, OR ANY WARRANTY */ +/* OTHERWISE ARISING OUT OF ANY PROPOSAL, SPECIFICATION OR SAMPLE. */ +/* */ +/* - Without limitation, TCG and its members and licensors disclaim all */ +/* liability, including liability for infringement of any proprietary */ +/* rights, relating to use of information in this specification and to the */ +/* implementation of this specification, and TCG disclaims all liability for */ +/* cost of procurement of substitute goods or services, lost profits, loss */ +/* of use, loss of data or any incidental, consequential, direct, indirect, */ +/* or special damages, whether under contract, tort, warranty or otherwise, */ +/* arising in any way out of use or reliance upon this specification or any */ +/* information herein. */ +/* */ +/* (c) Copyright IBM Corp. and others, 2016 - 2021 */ +/* */ +/********************************************************************************/ + +/* 10.2.13 CryptHash.c */ +/* 10.2.13.1 Description */ +/* This file contains implementation of cryptographic functions for hashing. */ +/* 10.2.13.2 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 +}; + +/* 10.2.13.3 Obligatory Initialization Functions */ +/* 10.2.13.3.1 CryptHashInit() */ +/* This function is called by _TPM_Init() do perform the initialization operations for the + library. */ +BOOL +CryptHashInit( + void + ) +{ + LibHashInit(); + return TRUE; +} +/* 10.2.13.3.2 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); +} +/* 10.2.13.4 Hash Information Access Functions */ +/* 10.2.13.4.1 Introduction */ +/* These functions provide access to the hash algorithm description information. */ +/* 10.2.13.4.2 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 +} +/* 10.2.13.4.3 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 Value Meaning */ +/* 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; +} +/* 10.2.13.4.4 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 Value Meaning */ +/* 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; +} +/* 10.2.13.4.5 CryptHashGetDigestSize() */ +/* Returns the size of the digest produced by the hash. If hashAlg is not a hash algorithm, the TPM + will FAIL. */ +/* Return Value Meaning */ +/* 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; +} +/* 10.2.13.4.6 CryptHashGetBlockSize() */ +/* Returns the size of the block used by the hash. If hashAlg is not a hash algorithm, the TPM will + FAIL. */ +/* Return Value Meaning */ +/* 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; +} +/* 10.2.13.4.7 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. */ +#if 0 // libtpms added +LIB_EXPORT const BYTE * +CryptHashGetOid( + TPM_ALG_ID hashAlg + ) +{ + return CryptGetHashDef(hashAlg)->OID; +} +#endif // libtpms added +/* 10.2.13.4.8 CryptHashGetContextAlg() */ +/* This function returns the hash algorithm associated with a hash context. */ +#if 0 // libtpms added +TPM_ALG_ID +CryptHashGetContextAlg( + PHASH_STATE state // IN: the context to check + ) +{ + return state->hashAlg; +} +#endif // libtpms added +/* 10.2.13.5 State Import and Export */ +/* 10.2.13.5.1 CryptHashCopyState */ +/* This function is used to clone a HASH_STATE. */ +#if 0 // libtpms added +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; +} +#endif // libtpms added +#if 0 // libtpms added +/* 10.2.13.5.2 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); +} +/* 10.2.13.5.3 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)); + } + } +} +#endif // libtpms added +/* 10.2.13.6 State Modification Functions */ +/* 10.2.13.6.1 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; +} +/* 10.2.13.6.2 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 Value Meaning */ +/* 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; +} +/* 10.2.13.6.3 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; +} +/* 10.2.13.6.4 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 Value Meaning */ +/* 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); +} +/* 10.2.13.6.5 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 Value Meaning */ +/* >= 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); +} +/* 10.2.13.6.6 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; +} +/* 10.2.13.6.7 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 Value Meaning */ +/* >=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); +} +/* 10.2.13.6.8 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]); +} +/* 10.2.13.7 HMAC Functions */ +/* 10.2.13.7.1 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 Value Meaning */ +/* >= 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; +} +/* 10.2.13.7.2 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 Value Meaning */ +/* >= 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); +} +/* 10.2.13.7.3 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 Value Meaning */ +/* > 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); +} + /* 10.2.13.7.4 CryptHmacEnd2B() */ + /* This function is the same as CryptHmacEnd() but the HMAC result is returned in a TPM2B which is the most common use. */ + /* Return Value Meaning */ + /* >=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); +} +/* 10.2.13.8 Mask and Key Generation Functions */ +/* 10.2.13.8.1 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. */ +/* Return Value Meaning */ +/* 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; +} +/* 10.2.13.8.2 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 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 +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; +} +/* 10.2.13.8.3 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 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 +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); +} |