/* This file includes functions that were extracted from the TPM2 * source, but were present in files not included in compilation. */ #include "Global.h" #include "CryptoEngine.h" #include <string.h> UINT16 _cpri__StartHMAC( TPM_ALG_ID hashAlg, // IN: the algorithm to use BOOL sequence, // IN: indicates if the state should be saved CPRI_HASH_STATE * state, // IN/OUT: the state buffer UINT16 keySize, // IN: the size of the HMAC key BYTE * key, // IN: the HMAC key TPM2B * oPadKey // OUT: the key prepared for the oPad round ) { CPRI_HASH_STATE localState; UINT16 blockSize = _cpri__GetHashBlockSize(hashAlg); UINT16 digestSize; BYTE *pb; // temp pointer UINT32 i; // If the key size is larger than the block size, then the hash of the key // is used as the key if(keySize > blockSize) { // large key so digest if((digestSize = _cpri__StartHash(hashAlg, FALSE, &localState)) == 0) return 0; _cpri__UpdateHash(&localState, keySize, key); _cpri__CompleteHash(&localState, digestSize, oPadKey->buffer); oPadKey->size = digestSize; } else { // key size is ok memcpy(oPadKey->buffer, key, keySize); oPadKey->size = keySize; } // XOR the key with iPad (0x36) pb = oPadKey->buffer; for(i = oPadKey->size; i > 0; i--) *pb++ ^= 0x36; // if the keySize is smaller than a block, fill the rest with 0x36 for(i = blockSize - oPadKey->size; i > 0; i--) *pb++ = 0x36; // Increase the oPadSize to a full block oPadKey->size = blockSize; // Start a new hash with the HMAC key // This will go in the caller's state structure and may be a sequence or not if((digestSize = _cpri__StartHash(hashAlg, sequence, state)) > 0) { _cpri__UpdateHash(state, oPadKey->size, oPadKey->buffer); // XOR the key block with 0x5c ^ 0x36 for(pb = oPadKey->buffer, i = blockSize; i > 0; i--) *pb++ ^= (0x5c ^ 0x36); } return digestSize; } UINT16 _cpri__CompleteHMAC( CPRI_HASH_STATE * hashState, // IN: the state of hash stack TPM2B * oPadKey, // IN: the HMAC key in oPad format UINT32 dOutSize, // IN: size of digest buffer BYTE * dOut // OUT: hash digest ) { BYTE digest[MAX_DIGEST_SIZE]; CPRI_HASH_STATE *state = (CPRI_HASH_STATE *)hashState; CPRI_HASH_STATE localState; UINT16 digestSize = _cpri__GetDigestSize(state->hashAlg); _cpri__CompleteHash(hashState, digestSize, digest); // Using the local hash state, do a hash with the oPad if(_cpri__StartHash(state->hashAlg, FALSE, &localState) != digestSize) return 0; _cpri__UpdateHash(&localState, oPadKey->size, oPadKey->buffer); _cpri__UpdateHash(&localState, digestSize, digest); return _cpri__CompleteHash(&localState, dOutSize, dOut); } UINT16 _cpri__KDFa( TPM_ALG_ID hashAlg, // IN: hash algorithm used in HMAC TPM2B * key, // IN: HMAC key const char *label, // IN: a 0-byte terminated label used in KDF TPM2B * contextU, // IN: context U TPM2B * contextV, // IN: context V UINT32 sizeInBits, // IN: size of generated key in bit BYTE * keyStream, // OUT: key buffer UINT32 * counterInOut, // IN/OUT: caller may provide the iteration // counter for incremental operations to // avoid large intermediate buffers. BOOL once // IN: TRUE if only one iteration is // performed FALSE if iteration count determined by "sizeInBits" ) { UINT32 counter = 0; // counter value INT32 lLen = 0; // length of the label INT16 hLen; // length of the hash INT16 bytes; // number of bytes to produce BYTE *stream = keyStream; BYTE marshaledUint32[4]; CPRI_HASH_STATE hashState; TPM2B_MAX_HASH_BLOCK hmacKey; pAssert(key != NULL && keyStream != NULL); pAssert(once == FALSE || (sizeInBits & 7) == 0); if(counterInOut != NULL) counter = *counterInOut; // Prepare label buffer. Calculate its size and keep the last 0 byte if(label != NULL) for(lLen = 0; label[lLen++] != 0; ); // Get the hash size. If it is less than or 0, either the // algorithm is not supported or the hash is TPM_ALG_NULL // // In either case the digest size is zero. This is the only return // other than the one at the end. All other exits from this function // are fatal errors. After we check that the algorithm is supported // anything else that goes wrong is an implementation flaw. if((hLen = (INT16) _cpri__GetDigestSize(hashAlg)) == 0) return 0; // If the size of the request is larger than the numbers will handle, // it is a fatal error. pAssert(((sizeInBits + 7)/ 8) <= INT16_MAX); bytes = once ? hLen : (INT16)((sizeInBits + 7) / 8); // Generate required bytes for (; bytes > 0; stream = &stream[hLen], bytes = bytes - hLen) { if(bytes < hLen) hLen = bytes; counter++; // Start HMAC if(_cpri__StartHMAC(hashAlg, FALSE, &hashState, key->size, &key->buffer[0], &hmacKey.b) <= 0) FAIL(FATAL_ERROR_INTERNAL); // Adding counter UINT32_TO_BYTE_ARRAY(counter, marshaledUint32); _cpri__UpdateHash(&hashState, sizeof(UINT32), marshaledUint32); // Adding label if(label != NULL) _cpri__UpdateHash(&hashState, lLen, (BYTE *)label); // Adding contextU if(contextU != NULL) _cpri__UpdateHash(&hashState, contextU->size, contextU->buffer); // Adding contextV if(contextV != NULL) _cpri__UpdateHash(&hashState, contextV->size, contextV->buffer); // Adding size in bits UINT32_TO_BYTE_ARRAY(sizeInBits, marshaledUint32); _cpri__UpdateHash(&hashState, sizeof(UINT32), marshaledUint32); // Compute HMAC. At the start of each iteration, hLen is set // to the smaller of hLen and bytes. This causes bytes to decrement // exactly to zero to complete the loop _cpri__CompleteHMAC(&hashState, &hmacKey.b, hLen, stream); } // Mask off bits if the required bits is not a multiple of byte size if((sizeInBits % 8) != 0) keyStream[0] &= ((1 << (sizeInBits % 8)) - 1); if(counterInOut != NULL) *counterInOut = counter; return (CRYPT_RESULT)((sizeInBits + 7)/8); } UINT16 _cpri__KDFe( TPM_ALG_ID hashAlg, // IN: hash algorithm used in HMAC TPM2B * Z, // IN: Z const char *label, // IN: a 0 terminated label using in KDF TPM2B * partyUInfo, // IN: PartyUInfo TPM2B * partyVInfo, // IN: PartyVInfo UINT32 sizeInBits, // IN: size of generated key in bit BYTE * keyStream // OUT: key buffer ) { UINT32 counter = 0; // counter value UINT32 lSize = 0; BYTE *stream = keyStream; CPRI_HASH_STATE hashState; INT16 hLen = (INT16) _cpri__GetDigestSize(hashAlg); INT16 bytes; // number of bytes to generate BYTE marshaledUint32[4]; pAssert( keyStream != NULL && Z != NULL && ((sizeInBits + 7) / 8) < INT16_MAX); if(hLen == 0) return 0; bytes = (INT16)((sizeInBits + 7) / 8); // Prepare label buffer. Calculate its size and keep the last 0 byte if(label != NULL) for(lSize = 0; label[lSize++] != 0;); // Generate required bytes //The inner loop of that KDF uses: // Hashi := H(counter | Z | OtherInfo) (5) // Where: // Hashi the hash generated on the i-th iteration of the loop. // H() an approved hash function // counter a 32-bit counter that is initialized to 1 and incremented // on each iteration // Z the X coordinate of the product of a public ECC key and a // different private ECC key. // OtherInfo a collection of qualifying data for the KDF defined below. // In this specification, OtherInfo will be constructed by: // OtherInfo := Use | PartyUInfo | PartyVInfo for (; bytes > 0; stream = &stream[hLen], bytes = bytes - hLen) { if(bytes < hLen) hLen = bytes; // counter++; // Start hash if(_cpri__StartHash(hashAlg, FALSE, &hashState) == 0) return 0; // Add counter UINT32_TO_BYTE_ARRAY(counter, marshaledUint32); _cpri__UpdateHash(&hashState, sizeof(UINT32), marshaledUint32); // Add Z if(Z != NULL) _cpri__UpdateHash(&hashState, Z->size, Z->buffer); // Add label if(label != NULL) _cpri__UpdateHash(&hashState, lSize, (BYTE *)label); else // The SP800-108 specification requires a zero between the label // and the context. _cpri__UpdateHash(&hashState, 1, (BYTE *)""); // Add PartyUInfo if(partyUInfo != NULL) _cpri__UpdateHash(&hashState, partyUInfo->size, partyUInfo->buffer); // Add PartyVInfo if(partyVInfo != NULL) _cpri__UpdateHash(&hashState, partyVInfo->size, partyVInfo->buffer); // Compute Hash. hLen was changed to be the smaller of bytes or hLen // at the start of each iteration. _cpri__CompleteHash(&hashState, hLen, stream); } // Mask off bits if the required bits is not a multiple of byte size if((sizeInBits % 8) != 0) keyStream[0] &= ((1 << (sizeInBits % 8)) - 1); return (CRYPT_RESULT)((sizeInBits + 7) / 8); } UINT16 _cpri__GenerateSeededRandom( INT32 randomSize, // IN: the size of the request BYTE * random, // OUT: receives the data TPM_ALG_ID hashAlg, // IN: used by KDF version but not here TPM2B * seed, // IN: the seed value const char *label, // IN: a label string (optional) TPM2B * partyU, // IN: other data (oprtional) TPM2B * partyV // IN: still more (optional) ) { return (_cpri__KDFa(hashAlg, seed, label, partyU, partyV, randomSize * 8, random, NULL, FALSE)); }