// This file was extracted from the TCG Published // Trusted Platform Module Library // Part 4: Supporting Routines // Family "2.0" // Level 00 Revision 01.16 // October 30, 2014 #include #include "OsslCryptoEngine.h" #include "CpriHashData.c" #define OSSL_HASH_STATE_DATA_SIZE (MAX_HASH_STATE_SIZE - 8) typedef struct { union { EVP_MD_CTX context; BYTE data[OSSL_HASH_STATE_DATA_SIZE]; } u; INT16 copySize; } OSSL_HASH_STATE; // // Temporary aliasing of SM3 to SHA256 until SM3 is available // #define EVP_sm3_256 EVP_sha256 // // // Static Functions // // GetHashServer() // // This function returns the address of the hash server function // static EVP_MD * GetHashServer( TPM_ALG_ID hashAlg ) { switch (hashAlg) { #ifdef TPM_ALG_SHA1 case TPM_ALG_SHA1: return (EVP_MD *)EVP_sha1(); break; #endif #ifdef TPM_ALG_SHA256 case TPM_ALG_SHA256: return (EVP_MD *)EVP_sha256(); break; #endif #ifdef TPM_ALG_SHA384 case TPM_ALG_SHA384: return (EVP_MD *)EVP_sha384(); break; #endif #ifdef TPM_ALG_SHA512 case TPM_ALG_SHA512: return (EVP_MD *)EVP_sha512(); break; #endif #ifdef TPM_ALG_SM3_256 case TPM_ALG_SM3_256: return (EVP_MD *)EVP_sm3_256(); break; #endif case TPM_ALG_NULL: return NULL; default: FAIL(FATAL_ERROR_INTERNAL); } return NULL; // Never reached. } // // // MarshalHashState() // // This function copies an OpenSSL() hash context into a caller provided buffer. // // Return Value Meaning // // >0 the number of bytes of buf used. // static UINT16 MarshalHashState( EVP_MD_CTX *ctxt, // IN: Context to marshal BYTE *buf // OUT: The buffer that will receive the // context. This buffer is at least // MAX_HASH_STATE_SIZE byte ) { // make sure everything will fit pAssert(ctxt->digest->ctx_size <= OSSL_HASH_STATE_DATA_SIZE); // Copy the context data memcpy(buf, (void*) ctxt->md_data, ctxt->digest->ctx_size); return (UINT16)ctxt->digest->ctx_size; } // // // GetHashState() // // This function will unmarshal a caller provided buffer into an OpenSSL() hash context. The function returns // the number of bytes copied (which may be zero). // static UINT16 GetHashState( EVP_MD_CTX *ctxt, // OUT: The context structure to receive the // result of unmarshaling. TPM_ALG_ID algType, // IN: The hash algorithm selector BYTE *buf // IN: Buffer containing marshaled hash data ) { EVP_MD *evpmdAlgorithm = NULL; pAssert(ctxt != NULL); EVP_MD_CTX_init(ctxt); evpmdAlgorithm = GetHashServer(algType); if(evpmdAlgorithm == NULL) return 0; // This also allocates the ctxt->md_data if((EVP_DigestInit_ex(ctxt, evpmdAlgorithm, NULL)) != 1) FAIL(FATAL_ERROR_INTERNAL); pAssert(ctxt->digest->ctx_size < sizeof(ALIGNED_HASH_STATE)); memcpy(ctxt->md_data, buf, ctxt->digest->ctx_size); // return (UINT16)ctxt->digest->ctx_size; } // // // GetHashInfoPointer() // // This function returns a pointer to the hash info for the algorithm. If the algorithm is not supported, function // returns a pointer to the data block associated with TPM_ALG_NULL. // static const HASH_INFO * GetHashInfoPointer( TPM_ALG_ID hashAlg ) { UINT32 i, tableSize; // Get the table size of g_hashData tableSize = sizeof(g_hashData) / sizeof(g_hashData[0]); for(i = 0; i < tableSize - 1; i++) { if(g_hashData[i].alg == hashAlg) return &g_hashData[i]; } return &g_hashData[tableSize-1]; } // // // Hash Functions // // _cpri__HashStartup() // // Function that is called to initialize the hash service. In this implementation, this function does nothing but // it is called by the CryptUtilStartup() function and must be present. // LIB_EXPORT BOOL _cpri__HashStartup( void ) { // On startup, make sure that the structure sizes are compatible. It would // be nice if this could be done at compile time but I couldn't figure it out. CPRI_HASH_STATE *cpriState = NULL; // NUMBYTES evpCtxSize = sizeof(EVP_MD_CTX); NUMBYTES cpriStateSize = sizeof(cpriState->state); // OSSL_HASH_STATE *osslState; NUMBYTES osslStateSize = sizeof(OSSL_HASH_STATE); // int dataSize = sizeof(osslState->u.data); pAssert(cpriStateSize >= osslStateSize); return TRUE; } // // // _cpri__GetHashAlgByIndex() // // This function is used to iterate through the hashes. TPM_ALG_NULL is returned for all indexes that are // not valid hashes. If the TPM implements 3 hashes, then an index value of 0 will return the first // implemented hash and and index of 2 will return the last. All other index values will return // TPM_ALG_NULL. // // // // // Return Value Meaning // // TPM_ALG_xxx() a hash algorithm // TPM_ALG_NULL this can be used as a stop value // LIB_EXPORT TPM_ALG_ID _cpri__GetHashAlgByIndex( UINT32 index // IN: the index ) { if(index >= HASH_COUNT) return TPM_ALG_NULL; return g_hashData[index].alg; } // // // _cpri__GetHashBlockSize() // // Returns the size of the block used for the hash // // Return Value Meaning // // <0 the algorithm is not a supported hash // >= the digest size (0 for TPM_ALG_NULL) // LIB_EXPORT UINT16 _cpri__GetHashBlockSize( TPM_ALG_ID hashAlg // IN: hash algorithm to look up ) { return GetHashInfoPointer(hashAlg)->blockSize; } // // // _cpri__GetHashDER // // This function returns a pointer to the DER string for the algorithm and indicates its size. // LIB_EXPORT UINT16 _cpri__GetHashDER( TPM_ALG_ID hashAlg, // IN: the algorithm to look up const BYTE **p ) { const HASH_INFO *q; q = GetHashInfoPointer(hashAlg); *p = &q->der[0]; return q->derSize; } // // // _cpri__GetDigestSize() // // Gets the digest size of the algorithm. The algorithm is required to be supported. // // Return Value Meaning // // =0 the digest size for TPM_ALG_NULL // >0 the digest size of a hash algorithm // LIB_EXPORT UINT16 _cpri__GetDigestSize( TPM_ALG_ID hashAlg // IN: hash algorithm to look up ) { return GetHashInfoPointer(hashAlg)->digestSize; } // // // _cpri__GetContextAlg() // // This function returns the algorithm associated with a hash context // LIB_EXPORT TPM_ALG_ID _cpri__GetContextAlg( CPRI_HASH_STATE *hashState // IN: the hash context ) { return hashState->hashAlg; } // // // _cpri__CopyHashState // // This function is used to clone a CPRI_HASH_STATE. The return value is the size of the state. // LIB_EXPORT UINT16 _cpri__CopyHashState ( CPRI_HASH_STATE *out, // OUT: destination of the state CPRI_HASH_STATE *in // IN: source of the state ) { OSSL_HASH_STATE *i = (OSSL_HASH_STATE *)&in->state; OSSL_HASH_STATE *o = (OSSL_HASH_STATE *)&out->state; pAssert(sizeof(i) <= sizeof(in->state)); EVP_MD_CTX_init(&o->u.context); EVP_MD_CTX_copy_ex(&o->u.context, &i->u.context); o->copySize = i->copySize; out->hashAlg = in->hashAlg; return sizeof(CPRI_HASH_STATE); } // // // _cpri__StartHash() // // Functions starts a hash stack Start a hash stack and returns the digest size. As a side effect, the value of // stateSize in hashState is updated to indicate the number of bytes of state that were saved. This function // calls GetHashServer() and that function will put the TPM into failure mode if the hash algorithm is not // supported. // // Return Value Meaning // // 0 hash is TPM_ALG_NULL // >0 digest size // LIB_EXPORT UINT16 _cpri__StartHash( TPM_ALG_ID hashAlg, // IN: hash algorithm BOOL sequence, // IN: TRUE if the state should be saved CPRI_HASH_STATE *hashState // OUT: the state of hash stack. ) { EVP_MD_CTX localState; OSSL_HASH_STATE *state = (OSSL_HASH_STATE *)&hashState->state; BYTE *stateData = state->u.data; EVP_MD_CTX *context; EVP_MD *evpmdAlgorithm = NULL; UINT16 retVal = 0; if(sequence) context = &localState; else context = &state->u.context; hashState->hashAlg = hashAlg; EVP_MD_CTX_init(context); evpmdAlgorithm = GetHashServer(hashAlg); if(evpmdAlgorithm == NULL) goto Cleanup; if(EVP_DigestInit_ex(context, evpmdAlgorithm, NULL) != 1) FAIL(FATAL_ERROR_INTERNAL); retVal = (CRYPT_RESULT)EVP_MD_CTX_size(context); Cleanup: if(retVal > 0) { if (sequence) { if((state->copySize = MarshalHashState(context, stateData)) == 0) { // If MarshalHashState returns a negative number, it is an error // code and not a hash size so copy the error code to be the return // from this function and set the actual stateSize to zero. retVal = state->copySize; state->copySize = 0; } // Do the cleanup EVP_MD_CTX_cleanup(context); } else state->copySize = -1; } else state->copySize = 0; return retVal; } // // // _cpri__UpdateHash() // // Add data to a hash or HMAC stack. // LIB_EXPORT void _cpri__UpdateHash( CPRI_HASH_STATE *hashState, // IN: the hash context information UINT32 dataSize, // IN: the size of data to be added to the // digest BYTE *data // IN: data to be hashed ) { EVP_MD_CTX localContext; OSSL_HASH_STATE *state = (OSSL_HASH_STATE *)&hashState->state; BYTE *stateData = state->u.data; EVP_MD_CTX *context; CRYPT_RESULT retVal = CRYPT_SUCCESS; // // If there is no context, return if(state->copySize == 0) return; if(state->copySize > 0) { context = &localContext; if((retVal = GetHashState(context, hashState->hashAlg, stateData)) <= 0) return; } else context = &state->u.context; if(EVP_DigestUpdate(context, data, dataSize) != 1) FAIL(FATAL_ERROR_INTERNAL); else if( state->copySize > 0 && (retVal= MarshalHashState(context, stateData)) >= 0) { // retVal is the size of the marshaled data. Make sure that it is consistent // by ensuring that we didn't get more than allowed if(retVal < state->copySize) FAIL(FATAL_ERROR_INTERNAL); else EVP_MD_CTX_cleanup(context); } return; } // // // _cpri__CompleteHash() // // Complete a hash or HMAC computation. This function will place the smaller of digestSize or the size of // the digest in dOut. The number of bytes in the placed in the buffer is returned. If there is a failure, the // returned value is <= 0. // // Return Value Meaning // // 0 no data returned // >0 the number of bytes in the digest // LIB_EXPORT UINT16 _cpri__CompleteHash( CPRI_HASH_STATE *hashState, // IN: the state of hash stack UINT32 dOutSize, // IN: size of digest buffer BYTE *dOut // OUT: hash digest ) { EVP_MD_CTX localState; OSSL_HASH_STATE *state = (OSSL_HASH_STATE *)&hashState->state; BYTE *stateData = state->u.data; EVP_MD_CTX *context; UINT16 retVal; int hLen; BYTE temp[MAX_DIGEST_SIZE]; BYTE *rBuffer = dOut; if(state->copySize == 0) return 0; if(state->copySize > 0) { context = &localState; if((retVal = GetHashState(context, hashState->hashAlg, stateData)) <= 0) goto Cleanup; } else context = &state->u.context; hLen = EVP_MD_CTX_size(context); if((unsigned)hLen > dOutSize) rBuffer = temp; if(EVP_DigestFinal_ex(context, rBuffer, NULL) == 1) { if(rBuffer != dOut) { if(dOut != NULL) { memcpy(dOut, temp, dOutSize); } retVal = (UINT16)dOutSize; } else { retVal = (UINT16)hLen; } state->copySize = 0; } else { retVal = 0; // Indicate that no data is returned } Cleanup: EVP_MD_CTX_cleanup(context); return retVal; } // // // _cpri__ImportExportHashState() // // This function is used to import or export the hash state. This function would be called to export state when // a sequence object was being prepared for export // LIB_EXPORT void _cpri__ImportExportHashState( CPRI_HASH_STATE *osslFmt, // IN/OUT: the hash state formated for use // by openSSL EXPORT_HASH_STATE *externalFmt, // IN/OUT: the exported hash state IMPORT_EXPORT direction // ) { UNREFERENCED_PARAMETER(direction); UNREFERENCED_PARAMETER(externalFmt); UNREFERENCED_PARAMETER(osslFmt); return; #if 0 if(direction == IMPORT_STATE) { // don't have the import export functions yet so just copy _cpri__CopyHashState(osslFmt, (CPRI_HASH_STATE *)externalFmt); } else { _cpri__CopyHashState((CPRI_HASH_STATE *)externalFmt, osslFmt); } #endif } // // // // _cpri__HashBlock() // // Start a hash, hash a single block, update digest and return the size of the results. // The digestSize parameter can be smaller than the digest. If so, only the more significant bytes are // returned. // // Return Value Meaning // // >= 0 number of bytes in digest (may be zero) // LIB_EXPORT UINT16 _cpri__HashBlock( TPM_ALG_ID hashAlg, // IN: The hash algorithm UINT32 dataSize, // IN: size of buffer to hash BYTE *data, // IN: the buffer to hash UINT32 digestSize, // IN: size of the digest buffer BYTE *digest // OUT: hash digest ) { EVP_MD_CTX hashContext; EVP_MD *hashServer = NULL; UINT16 retVal = 0; BYTE b[MAX_DIGEST_SIZE]; // temp buffer in case digestSize not // a full digest unsigned int dSize = _cpri__GetDigestSize(hashAlg); // If there is no digest to compute return if(dSize == 0) return 0; // After the call to EVP_MD_CTX_init(), will need to call EVP_MD_CTX_cleanup() EVP_MD_CTX_init(&hashContext); // Initialize the local hash context hashServer = GetHashServer(hashAlg); // Find the hash server // It is an error if the digest size is non-zero but there is no server if( (hashServer == NULL) || (EVP_DigestInit_ex(&hashContext, hashServer, NULL) != 1) || (EVP_DigestUpdate(&hashContext, data, dataSize) != 1)) FAIL(FATAL_ERROR_INTERNAL); else { // If the size of the digest produced (dSize) is larger than the available // buffer (digestSize), then put the digest in a temp buffer and only copy // the most significant part into the available buffer. if(dSize > digestSize) { if(EVP_DigestFinal_ex(&hashContext, b, &dSize) != 1) FAIL(FATAL_ERROR_INTERNAL); memcpy(digest, b, digestSize); retVal = (UINT16)digestSize; } else { if((EVP_DigestFinal_ex(&hashContext, digest, &dSize)) != 1) FAIL(FATAL_ERROR_INTERNAL); retVal = (UINT16) dSize; } } EVP_MD_CTX_cleanup(&hashContext); return retVal; } // // // // HMAC Functions // // _cpri__StartHMAC // // This function is used to start an HMAC using a temp hash context. The function does the initialization of // the hash with the HMAC key XOR iPad and updates the HMAC key XOR oPad. // The function returns the number of bytes in a digest produced by hashAlg. // // Return Value Meaning // // >= 0 number of bytes in digest produced by hashAlg (may be zero) // LIB_EXPORT UINT16 _cpri__StartHMAC( TPM_ALG_ID hashAlg, // IN: the algorithm to use BOOL sequence, // IN: indicates if the state should be // saved CPRI_HASH_STATE *state, // IN/OUT: the state buffer UINT16 keySize, // IN: the size of the HMAC key BYTE *key, // IN: the HMAC key TPM2B *oPadKey // OUT: the key prepared for the oPad round ) { CPRI_HASH_STATE localState; UINT16 blockSize = _cpri__GetHashBlockSize(hashAlg); UINT16 digestSize; BYTE *pb; // temp pointer UINT32 i; // If the key size is larger than the block size, then the hash of the key // is used as the key if(keySize > blockSize) { // large key so digest if((digestSize = _cpri__StartHash(hashAlg, FALSE, &localState)) == 0) return 0; _cpri__UpdateHash(&localState, keySize, key); _cpri__CompleteHash(&localState, digestSize, oPadKey->buffer); oPadKey->size = digestSize; } else { // key size is ok memcpy(oPadKey->buffer, key, keySize); oPadKey->size = keySize; } // XOR the key with iPad (0x36) pb = oPadKey->buffer; for(i = oPadKey->size; i > 0; i--) *pb++ ^= 0x36; // if the keySize is smaller than a block, fill the rest with 0x36 for(i = blockSize - oPadKey->size; i > 0; i--) *pb++ = 0x36; // Increase the oPadSize to a full block oPadKey->size = blockSize; // Start a new hash with the HMAC key // This will go in the caller's state structure and may be a sequence or not if((digestSize = _cpri__StartHash(hashAlg, sequence, state)) > 0) { _cpri__UpdateHash(state, oPadKey->size, oPadKey->buffer); // XOR the key block with 0x5c ^ 0x36 for(pb = oPadKey->buffer, i = blockSize; i > 0; i--) *pb++ ^= (0x5c ^ 0x36); } return digestSize; } // // // _cpri_CompleteHMAC() // // This function is called to complete an HMAC. It will finish the current digest, and start a new digest. It will // then add the oPadKey and the completed digest and return the results in dOut. It will not return more than // dOutSize bytes. // // Return Value Meaning // // >= 0 number of bytes in dOut (may be zero) // LIB_EXPORT UINT16 _cpri__CompleteHMAC( CPRI_HASH_STATE *hashState, // IN: the state of hash stack TPM2B *oPadKey, // IN: the HMAC key in oPad format UINT32 dOutSize, // IN: size of digest buffer BYTE *dOut // OUT: hash digest ) { BYTE digest[MAX_DIGEST_SIZE]; CPRI_HASH_STATE *state = (CPRI_HASH_STATE *)hashState; CPRI_HASH_STATE localState; UINT16 digestSize = _cpri__GetDigestSize(state->hashAlg); _cpri__CompleteHash(hashState, digestSize, digest); // Using the local hash state, do a hash with the oPad if(_cpri__StartHash(state->hashAlg, FALSE, &localState) != digestSize) return 0; _cpri__UpdateHash(&localState, oPadKey->size, oPadKey->buffer); _cpri__UpdateHash(&localState, digestSize, digest); return _cpri__CompleteHash(&localState, dOutSize, dOut); } // // // Mask and Key Generation Functions // // _crypi_MGF1() // // This function performs MGF1 using the selected hash. MGF1 is T(n) = T(n-1) || H(seed || counter). This // function returns the length of the mask produced which could be zero if the digest algorithm is not // supported // // Return Value Meaning // // 0 hash algorithm not supported // >0 should be the same as mSize // LIB_EXPORT CRYPT_RESULT _cpri__MGF1( UINT32 mSize, // IN: length of the mask to be produced BYTE *mask, // OUT: buffer to receive the mask TPM_ALG_ID hashAlg, // IN: hash to use UINT32 sSize, // IN: size of the seed BYTE *seed // IN: seed size ) { EVP_MD_CTX hashContext; EVP_MD *hashServer = NULL; CRYPT_RESULT retVal = 0; BYTE b[MAX_DIGEST_SIZE]; // temp buffer in case mask is not an // even multiple of a full digest CRYPT_RESULT dSize = _cpri__GetDigestSize(hashAlg); unsigned int digestSize = (UINT32)dSize; UINT32 remaining; UINT32 counter; BYTE swappedCounter[4]; // Parameter check if(mSize > (1024*16)) // Semi-arbitrary maximum FAIL(FATAL_ERROR_INTERNAL); // If there is no digest to compute return if(dSize <= 0) return 0; EVP_MD_CTX_init(&hashContext); // Initialize the local hash context hashServer = GetHashServer(hashAlg); // Find the hash server if(hashServer == NULL) // If there is no server, then there is no digest return 0; for(counter = 0, remaining = mSize; remaining > 0; counter++) { // Because the system may be either Endian... UINT32_TO_BYTE_ARRAY(counter, swappedCounter); // Start the hash and include the seed and counter if( (EVP_DigestInit_ex(&hashContext, hashServer, NULL) != 1) || (EVP_DigestUpdate(&hashContext, seed, sSize) != 1) || (EVP_DigestUpdate(&hashContext, swappedCounter, 4) != 1) ) FAIL(FATAL_ERROR_INTERNAL); // Handling the completion depends on how much space remains in the mask // buffer. If it can hold the entire digest, put it there. If not // put the digest in a temp buffer and only copy the amount that // will fit into the mask buffer. if(remaining < (unsigned)dSize) { if(EVP_DigestFinal_ex(&hashContext, b, &digestSize) != 1) FAIL(FATAL_ERROR_INTERNAL); memcpy(mask, b, remaining); break; } else { if(EVP_DigestFinal_ex(&hashContext, mask, &digestSize) != 1) FAIL(FATAL_ERROR_INTERNAL); remaining -= dSize; mask = &mask[dSize]; } retVal = (CRYPT_RESULT)mSize; } EVP_MD_CTX_cleanup(&hashContext); return retVal; } // // // _cpri_KDFa() // // This function performs the key generation according to Part 1 of the TPM specification. // This function returns the number of bytes generated which may be zero. // The key and keyStream pointers are not allowed to be NULL. The other pointer values may be NULL. // The value of sizeInBits must be no larger than (2^18)-1 = 256K bits (32385 bytes). // The once parameter is set to allow incremental generation of a large value. If this flag is TRUE, // sizeInBits will be used in the HMAC computation but only one iteration of the KDF is performed. This // would be used for XOR obfuscation so that the mask value can be generated in digest-sized chunks // rather than having to be generated all at once in an arbitrarily large buffer and then XORed() into the // result. If once is TRUE, then sizeInBits must be a multiple of 8. // Any error in the processing of this command is considered fatal. // // Return Value Meaning // // 0 hash algorithm is not supported or is TPM_ALG_NULL // >0 the number of bytes in the keyStream buffer // LIB_EXPORT UINT16 _cpri__KDFa( TPM_ALG_ID hashAlg, // IN: hash algorithm used in HMAC TPM2B *key, // IN: HMAC key const char *label, // IN: a 0-byte terminated label used in KDF TPM2B *contextU, // IN: context U TPM2B *contextV, // IN: context V UINT32 sizeInBits, // IN: size of generated key in bit BYTE *keyStream, // OUT: key buffer UINT32 *counterInOut, // IN/OUT: caller may provide the iteration // counter for incremental operations to // avoid large intermediate buffers. BOOL once // IN: TRUE if only one iteration is performed // FALSE if iteration count determined by // "sizeInBits" ) { UINT32 counter = 0; // counter value INT32 lLen = 0; // length of the label INT16 hLen; // length of the hash INT16 bytes; // number of bytes to produce BYTE *stream = keyStream; BYTE marshaledUint32[4]; CPRI_HASH_STATE hashState; TPM2B_MAX_HASH_BLOCK hmacKey; pAssert(key != NULL && keyStream != NULL); pAssert(once == FALSE || (sizeInBits & 7) == 0); if(counterInOut != NULL) counter = *counterInOut; // Prepare label buffer. Calculate its size and keep the last 0 byte if(label != NULL) for(lLen = 0; label[lLen++] != 0; ); // Get the hash size. If it is less than or 0, either the // algorithm is not supported or the hash is TPM_ALG_NULL // // In either case the digest size is zero. This is the only return // other than the one at the end. All other exits from this function // are fatal errors. After we check that the algorithm is supported // anything else that goes wrong is an implementation flaw. if((hLen = (INT16) _cpri__GetDigestSize(hashAlg)) == 0) return 0; // If the size of the request is larger than the numbers will handle, // it is a fatal error. pAssert(((sizeInBits + 7)/ 8) <= INT16_MAX); bytes = once ? hLen : (INT16)((sizeInBits + 7) / 8); // Generate required bytes for (; bytes > 0; stream = &stream[hLen], bytes = bytes - hLen) { if(bytes < hLen) hLen = bytes; counter++; // Start HMAC if(_cpri__StartHMAC(hashAlg, FALSE, &hashState, key->size, &key->buffer[0], &hmacKey.b) <= 0) FAIL(FATAL_ERROR_INTERNAL); // Adding counter UINT32_TO_BYTE_ARRAY(counter, marshaledUint32); _cpri__UpdateHash(&hashState, sizeof(UINT32), marshaledUint32); // Adding label if(label != NULL) _cpri__UpdateHash(&hashState, lLen, (BYTE *)label); // Adding contextU if(contextU != NULL) _cpri__UpdateHash(&hashState, contextU->size, contextU->buffer); // Adding contextV if(contextV != NULL) _cpri__UpdateHash(&hashState, contextV->size, contextV->buffer); // Adding size in bits UINT32_TO_BYTE_ARRAY(sizeInBits, marshaledUint32); _cpri__UpdateHash(&hashState, sizeof(UINT32), marshaledUint32); // Compute HMAC. At the start of each iteration, hLen is set // to the smaller of hLen and bytes. This causes bytes to decrement // exactly to zero to complete the loop _cpri__CompleteHMAC(&hashState, &hmacKey.b, hLen, stream); } // Mask off bits if the required bits is not a multiple of byte size if((sizeInBits % 8) != 0) keyStream[0] &= ((1 << (sizeInBits % 8)) - 1); if(counterInOut != NULL) *counterInOut = counter; return (CRYPT_RESULT)((sizeInBits + 7)/8); } // // // // _cpri__KDFe() // // KDFe() as defined in TPM specification part 1. // This function returns the number of bytes generated which may be zero. // The Z and keyStream pointers are not allowed to be NULL. The other pointer values may be NULL. The // value of sizeInBits must be no larger than (2^18)-1 = 256K bits (32385 bytes). Any error in the processing // of this command is considered fatal. // // Return Value Meaning // // 0 hash algorithm is not supported or is TPM_ALG_NULL // >0 the number of bytes in the keyStream buffer // LIB_EXPORT UINT16 _cpri__KDFe( TPM_ALG_ID hashAlg, // IN: hash algorithm used in HMAC TPM2B *Z, // IN: Z const char *label, // IN: a 0 terminated label using in KDF TPM2B *partyUInfo, // IN: PartyUInfo TPM2B *partyVInfo, // IN: PartyVInfo UINT32 sizeInBits, // IN: size of generated key in bit BYTE *keyStream // OUT: key buffer ) { UINT32 counter = 0; // counter value UINT32 lSize = 0; BYTE *stream = keyStream; CPRI_HASH_STATE hashState; INT16 hLen = (INT16) _cpri__GetDigestSize(hashAlg); INT16 bytes; // number of bytes to generate BYTE marshaledUint32[4]; pAssert( keyStream != NULL && Z != NULL && ((sizeInBits + 7) / 8) < INT16_MAX); if(hLen == 0) return 0; bytes = (INT16)((sizeInBits + 7) / 8); // Prepare label buffer. Calculate its size and keep the last 0 byte if(label != NULL) for(lSize = 0; label[lSize++] != 0;); // Generate required bytes //The inner loop of that KDF uses: // Hashi := H(counter | Z | OtherInfo) (5) // Where: // Hashi the hash generated on the i-th iteration of the loop. // H() an approved hash function // counter a 32-bit counter that is initialized to 1 and incremented // on each iteration // Z the X coordinate of the product of a public ECC key and a // different private ECC key. // OtherInfo a collection of qualifying data for the KDF defined below. // In this specification, OtherInfo will be constructed by: // OtherInfo := Use | PartyUInfo | PartyVInfo for (; bytes > 0; stream = &stream[hLen], bytes = bytes - hLen) { if(bytes < hLen) hLen = bytes; // counter++; // Start hash if(_cpri__StartHash(hashAlg, FALSE, &hashState) == 0) return 0; // Add counter UINT32_TO_BYTE_ARRAY(counter, marshaledUint32); _cpri__UpdateHash(&hashState, sizeof(UINT32), marshaledUint32); // Add Z if(Z != NULL) _cpri__UpdateHash(&hashState, Z->size, Z->buffer); // Add label if(label != NULL) _cpri__UpdateHash(&hashState, lSize, (BYTE *)label); else // The SP800-108 specification requires a zero between the label // and the context. _cpri__UpdateHash(&hashState, 1, (BYTE *)""); // Add PartyUInfo if(partyUInfo != NULL) _cpri__UpdateHash(&hashState, partyUInfo->size, partyUInfo->buffer); // Add PartyVInfo if(partyVInfo != NULL) _cpri__UpdateHash(&hashState, partyVInfo->size, partyVInfo->buffer); // Compute Hash. hLen was changed to be the smaller of bytes or hLen // at the start of each iteration. _cpri__CompleteHash(&hashState, hLen, stream); } // Mask off bits if the required bits is not a multiple of byte size if((sizeInBits % 8) != 0) keyStream[0] &= ((1 << (sizeInBits % 8)) - 1); return (CRYPT_RESULT)((sizeInBits + 7) / 8); }