3712 lines
143 KiB
C
3712 lines
143 KiB
C
// 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 "TPM_Types.h"
|
||
#include "CryptoEngine.h" // types shared by CryptUtil and CryptoEngine.
|
||
// Includes the function prototypes for the
|
||
// CryptoEngine functions.
|
||
#include "Global.h"
|
||
#include "InternalRoutines.h"
|
||
#include "MemoryLib_fp.h"
|
||
//#include "CryptSelfTest_fp.h"
|
||
//
|
||
//
|
||
// 10.2.2 TranslateCryptErrors()
|
||
//
|
||
// This function converts errors from the cryptographic library into TPM_RC_VALUES.
|
||
//
|
||
// Error Returns Meaning
|
||
//
|
||
// TPM_RC_VALUE CRYPT_FAIL
|
||
// TPM_RC_NO_RESULT CRYPT_NO_RESULT
|
||
// TPM_RC_SCHEME CRYPT_SCHEME
|
||
// TPM_RC_VALUE CRYPT_PARAMETER
|
||
// TPM_RC_SIZE CRYPT_UNDERFLOW
|
||
// TPM_RC_ECC_POINT CRYPT_POINT
|
||
// TPM_RC_CANCELLED CRYPT_CANCEL
|
||
//
|
||
static TPM_RC
|
||
TranslateCryptErrors (
|
||
CRYPT_RESULT retVal // IN: crypt error to evaluate
|
||
)
|
||
{
|
||
switch (retVal)
|
||
{
|
||
case CRYPT_SUCCESS:
|
||
return TPM_RC_SUCCESS;
|
||
case CRYPT_FAIL:
|
||
return TPM_RC_VALUE;
|
||
case CRYPT_NO_RESULT:
|
||
return TPM_RC_NO_RESULT;
|
||
case CRYPT_SCHEME:
|
||
return TPM_RC_SCHEME;
|
||
case CRYPT_PARAMETER:
|
||
return TPM_RC_VALUE;
|
||
case CRYPT_UNDERFLOW:
|
||
return TPM_RC_SIZE;
|
||
case CRYPT_POINT:
|
||
return TPM_RC_ECC_POINT;
|
||
case CRYPT_CANCEL:
|
||
return TPM_RC_CANCELED;
|
||
default: // Other unknown warnings
|
||
return TPM_RC_FAILURE;
|
||
}
|
||
}
|
||
//
|
||
//
|
||
// 10.2.3 Random Number Generation Functions
|
||
//
|
||
#ifdef TPM_ALG_NULL //%
|
||
#ifdef _DRBG_STATE_SAVE //%
|
||
//
|
||
//
|
||
// 10.2.3.1 CryptDrbgGetPutState()
|
||
//
|
||
// Read or write the current state from the DRBG in the cryptoEngine.
|
||
//
|
||
void
|
||
CryptDrbgGetPutState(
|
||
GET_PUT direction // IN: Get from or put to DRBG
|
||
)
|
||
{
|
||
_cpri__DrbgGetPutState(direction,
|
||
sizeof(go.drbgState),
|
||
(BYTE *)&go.drbgState);
|
||
}
|
||
#else //% 00
|
||
//%#define CryptDrbgGetPutState(ignored) // If not doing state save, turn this
|
||
//% // into a null macro
|
||
#endif //%
|
||
//
|
||
//
|
||
// 10.2.3.2 CryptStirRandom()
|
||
//
|
||
// Stir random entropy
|
||
//
|
||
void
|
||
CryptStirRandom(
|
||
UINT32 entropySize, // IN: size of entropy buffer
|
||
BYTE *buffer // IN: entropy buffer
|
||
)
|
||
{
|
||
// RNG self testing code may be inserted here
|
||
// Call crypto engine random number stirring function
|
||
_cpri__StirRandom(entropySize, buffer);
|
||
return;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.3.3 CryptGenerateRandom()
|
||
//
|
||
// This is the interface to _cpri__GenerateRandom().
|
||
//
|
||
UINT16
|
||
CryptGenerateRandom(
|
||
UINT16 randomSize, // IN: size of random number
|
||
BYTE *buffer // OUT: buffer of random number
|
||
)
|
||
{
|
||
UINT16 result;
|
||
pAssert(randomSize <= MAX_RSA_KEY_BYTES || randomSize <= PRIMARY_SEED_SIZE);
|
||
if(randomSize == 0)
|
||
return 0;
|
||
// Call crypto engine random number generation
|
||
result = _cpri__GenerateRandom(randomSize, buffer);
|
||
if(result != randomSize)
|
||
FAIL(FATAL_ERROR_INTERNAL);
|
||
return result;
|
||
}
|
||
#endif //TPM_ALG_NULL //%
|
||
//
|
||
//
|
||
// 10.2.4 Hash/HMAC Functions
|
||
//
|
||
// 10.2.4.1 CryptGetContextAlg()
|
||
//
|
||
// This function returns the hash algorithm associated with a hash context.
|
||
//
|
||
#ifdef TPM_ALG_KEYEDHASH //% 1
|
||
TPM_ALG_ID
|
||
CryptGetContextAlg(
|
||
void *state // IN: the context to check
|
||
)
|
||
{
|
||
HASH_STATE *context = (HASH_STATE *)state;
|
||
return _cpri__GetContextAlg(&context->state);
|
||
}
|
||
//
|
||
//
|
||
// 10.2.4.2 CryptStartHash()
|
||
//
|
||
// This function starts a hash and return the size, in bytes, of the digest.
|
||
//
|
||
// Return Value Meaning
|
||
//
|
||
// >0 the digest size of the algorithm
|
||
// =0 the hashAlg was TPM_ALG_NULL
|
||
//
|
||
UINT16
|
||
CryptStartHash(
|
||
TPMI_ALG_HASH hashAlg, // IN: hash algorithm
|
||
HASH_STATE *hashState // OUT: the state of hash stack. It will be used
|
||
// in hash update and completion
|
||
)
|
||
{
|
||
CRYPT_RESULT retVal = 0;
|
||
pAssert(hashState != NULL);
|
||
TEST_HASH(hashAlg);
|
||
hashState->type = HASH_STATE_EMPTY;
|
||
// Call crypto engine start hash function
|
||
if((retVal = _cpri__StartHash(hashAlg, FALSE, &hashState->state)) > 0)
|
||
hashState->type = HASH_STATE_HASH;
|
||
return retVal;
|
||
}
|
||
//
|
||
//
|
||
//
|
||
// 10.2.4.3 CryptStartHashSequence()
|
||
//
|
||
// Start a hash stack for a sequence object and return the size, in bytes, of the digest. This call uses the
|
||
// form of the hash state that requires context save and restored.
|
||
//
|
||
// Return Value Meaning
|
||
//
|
||
// >0 the digest size of the algorithm
|
||
// =0 the hashAlg was TPM_ALG_NULL
|
||
//
|
||
UINT16
|
||
CryptStartHashSequence(
|
||
TPMI_ALG_HASH hashAlg, // IN: hash algorithm
|
||
HASH_STATE *hashState // OUT: the state of hash stack. It will be used
|
||
// in hash update and completion
|
||
)
|
||
{
|
||
CRYPT_RESULT retVal = 0;
|
||
pAssert(hashState != NULL);
|
||
TEST_HASH(hashAlg);
|
||
hashState->type = HASH_STATE_EMPTY;
|
||
// Call crypto engine start hash function
|
||
if((retVal = _cpri__StartHash(hashAlg, TRUE, &hashState->state)) > 0)
|
||
hashState->type = HASH_STATE_HASH;
|
||
return retVal;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.4.4 CryptStartHMAC()
|
||
//
|
||
// This function starts an HMAC sequence and returns the size of the digest that will be produced.
|
||
// 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
|
||
//
|
||
UINT16
|
||
CryptStartHMAC(
|
||
TPMI_ALG_HASH hashAlg, // IN: hash algorithm
|
||
UINT16 keySize, // IN: the size of HMAC key in byte
|
||
BYTE *key, // IN: HMAC key
|
||
HMAC_STATE *hmacState // OUT: the state of HMAC stack. It will be used
|
||
// in HMAC update and completion
|
||
)
|
||
{
|
||
HASH_STATE *hashState = (HASH_STATE *)hmacState;
|
||
CRYPT_RESULT retVal;
|
||
// This has to come before the pAssert in case we all calling this function
|
||
// during testing. If so, the first instance will have no arguments but the
|
||
// hash algorithm. The call from the test routine will have arguments. When
|
||
// the second call is done, then we return to the test dispatcher.
|
||
TEST_HASH(hashAlg);
|
||
pAssert(hashState != NULL);
|
||
hashState->type = HASH_STATE_EMPTY;
|
||
if((retVal = _cpri__StartHMAC(hashAlg, FALSE, &hashState->state, keySize, key,
|
||
&hmacState->hmacKey.b)) > 0)
|
||
hashState->type = HASH_STATE_HMAC;
|
||
return retVal;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.4.5 CryptStartHMACSequence()
|
||
//
|
||
// This function starts an HMAC sequence and returns the size of the digest that will be produced.
|
||
// 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.
|
||
// This call is used to start a sequence HMAC that spans multiple TPM commands.
|
||
//
|
||
// Return Value Meaning
|
||
//
|
||
// >0 the digest size of the algorithm
|
||
// =0 the hashAlg was TPM_ALG_NULL
|
||
//
|
||
UINT16
|
||
CryptStartHMACSequence(
|
||
TPMI_ALG_HASH hashAlg, // IN: hash algorithm
|
||
UINT16 keySize, // IN: the size of HMAC key in byte
|
||
BYTE *key, // IN: HMAC key
|
||
HMAC_STATE *hmacState // OUT: the state of HMAC stack. It will be used
|
||
// in HMAC update and completion
|
||
)
|
||
{
|
||
HASH_STATE *hashState = (HASH_STATE *)hmacState;
|
||
CRYPT_RESULT retVal;
|
||
TEST_HASH(hashAlg);
|
||
hashState->type = HASH_STATE_EMPTY;
|
||
if((retVal = _cpri__StartHMAC(hashAlg, TRUE, &hashState->state,
|
||
keySize, key, &hmacState->hmacKey.b)) > 0)
|
||
hashState->type = HASH_STATE_HMAC;
|
||
return retVal;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.4.6 CryptStartHMAC2B()
|
||
//
|
||
// 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
|
||
CryptStartHMAC2B(
|
||
TPMI_ALG_HASH hashAlg, // IN: hash algorithm
|
||
TPM2B *key, // IN: HMAC key
|
||
HMAC_STATE *hmacState // OUT: the state of HMAC stack. It will be used
|
||
// in HMAC update and completion
|
||
)
|
||
{
|
||
return CryptStartHMAC(hashAlg, key->size, key->buffer, hmacState);
|
||
}
|
||
//
|
||
//
|
||
// 10.2.4.7 CryptStartHMACSequence2B()
|
||
//
|
||
// This function starts an HMAC sequence 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
|
||
//
|
||
UINT16
|
||
CryptStartHMACSequence2B(
|
||
TPMI_ALG_HASH hashAlg, // IN: hash algorithm
|
||
TPM2B *key, // IN: HMAC key
|
||
HMAC_STATE *hmacState // OUT: the state of HMAC stack. It will be used
|
||
// in HMAC update and completion
|
||
)
|
||
{
|
||
return CryptStartHMACSequence(hashAlg, key->size, key->buffer, hmacState);
|
||
}
|
||
//
|
||
//
|
||
// 10.2.4.8 CryptUpdateDigest()
|
||
//
|
||
// This function updates a digest (hash or HMAC) with an array of octets.
|
||
// 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
|
||
CryptUpdateDigest(
|
||
void *digestState, // IN: the state of hash stack
|
||
UINT32 dataSize, // IN: the size of data
|
||
BYTE *data // IN: data to be hashed
|
||
)
|
||
{
|
||
HASH_STATE *hashState = (HASH_STATE *)digestState;
|
||
pAssert(digestState != NULL);
|
||
if(hashState->type != HASH_STATE_EMPTY && data != NULL && dataSize != 0)
|
||
{
|
||
// Call crypto engine update hash function
|
||
_cpri__UpdateHash(&hashState->state, dataSize, data);
|
||
}
|
||
return;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.4.9 CryptUpdateDigest2B()
|
||
//
|
||
// 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
|
||
CryptUpdateDigest2B(
|
||
void *digestState, // IN: the digest state
|
||
TPM2B *bIn // IN: 2B containing the data
|
||
)
|
||
{
|
||
// Only compute the digest if a pointer to the 2B is provided.
|
||
// In CryptUpdateDigest(), 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.
|
||
if(bIn != NULL)
|
||
CryptUpdateDigest(digestState, bIn->size, bIn->buffer);
|
||
return;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.4.10 CryptUpdateDigestInt()
|
||
//
|
||
// This function is used to include an integer value to a hash stack. The function marshals the integer into its
|
||
// canonical form before calling CryptUpdateHash().
|
||
//
|
||
LIB_EXPORT void
|
||
CryptUpdateDigestInt(
|
||
void *state, // IN: the state of hash stack
|
||
UINT32 intSize, // IN: the size of 'intValue' in byte
|
||
void *intValue // IN: integer value to be hashed
|
||
)
|
||
{
|
||
#if BIG_ENDIAN_TPM == YES
|
||
pAssert( intValue != NULL && (intSize == 1 || intSize == 2
|
||
|| intSize == 4 || intSize == 8));
|
||
CryptUpdateHash(state, inSize, (BYTE *)intValue);
|
||
#else
|
||
BYTE marshalBuffer[8];
|
||
// Point to the big end of an little-endian value
|
||
BYTE *p = &((BYTE *)intValue)[intSize - 1];
|
||
// Point to the big end of an big-endian value
|
||
BYTE *q = marshalBuffer;
|
||
pAssert(intValue != NULL);
|
||
switch (intSize)
|
||
{
|
||
case 8:
|
||
*q++ = *p--;
|
||
*q++ = *p--;
|
||
*q++ = *p--;
|
||
*q++ = *p--;
|
||
case 4:
|
||
*q++ = *p--;
|
||
*q++ = *p--;
|
||
case 2:
|
||
*q++ = *p--;
|
||
case 1:
|
||
*q = *p;
|
||
// Call update the hash
|
||
CryptUpdateDigest(state, intSize, marshalBuffer);
|
||
break;
|
||
default:
|
||
FAIL(0);
|
||
}
|
||
#endif
|
||
return;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.4.11 CryptCompleteHash()
|
||
//
|
||
// This function completes a hash sequence and returns the digest.
|
||
// This function can be called to complete either an HMAC or hash sequence. The state type determines if
|
||
// the context type is a hash or HMAC. If an HMAC, then the call is forwarded to CryptCompleteHash().
|
||
// If digestSize is smaller than the digest size of hash/HMAC algorithm, the most significant bytes of
|
||
// required size will be returned
|
||
//
|
||
// Return Value Meaning
|
||
//
|
||
// >=0 the number of bytes placed in digest
|
||
//
|
||
LIB_EXPORT UINT16
|
||
CryptCompleteHash(
|
||
void *state, // IN: the state of hash stack
|
||
UINT16 digestSize, // IN: size of digest buffer
|
||
BYTE *digest // OUT: hash digest
|
||
)
|
||
{
|
||
HASH_STATE *hashState = (HASH_STATE *)state; // local value
|
||
// If the session type is HMAC, then could forward this to
|
||
// the HMAC processing and not cause an error. However, if no
|
||
// function calls this routine to forward it, then we can't get
|
||
// test coverage. The decision is to assert if this is called with
|
||
// the type == HMAC and fix anything that makes the wrong call.
|
||
pAssert(hashState->type == HASH_STATE_HASH);
|
||
// Set the state to empty so that it doesn't get used again
|
||
hashState->type = HASH_STATE_EMPTY;
|
||
// Call crypto engine complete hash function
|
||
return _cpri__CompleteHash(&hashState->state, digestSize, digest);
|
||
}
|
||
//
|
||
//
|
||
// 10.2.4.12 CryptCompleteHash2B()
|
||
//
|
||
// This function is the same as CypteCompleteHash() 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
|
||
CryptCompleteHash2B(
|
||
void *state, // IN: the state of hash stack
|
||
TPM2B *digest // IN: the size of the buffer Out: requested
|
||
// number of byte
|
||
)
|
||
{
|
||
UINT16 retVal = 0;
|
||
if(digest != NULL)
|
||
retVal = CryptCompleteHash(state, digest->size, digest->buffer);
|
||
return retVal;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.4.13 CryptHashBlock()
|
||
//
|
||
// Hash a block of data and return the results. If the digest is larger than retSize, it is truncated and with the
|
||
// least significant octets dropped.
|
||
//
|
||
// Return Value Meaning
|
||
//
|
||
// >=0 the number of bytes placed in ret
|
||
//
|
||
LIB_EXPORT UINT16
|
||
CryptHashBlock(
|
||
TPM_ALG_ID algId, // IN: the hash algorithm to use
|
||
UINT16 blockSize, // IN: size of the data block
|
||
BYTE *block, // IN: address of the block to hash
|
||
UINT16 retSize, // IN: size of the return buffer
|
||
BYTE *ret // OUT: address of the buffer
|
||
)
|
||
{
|
||
TEST_HASH(algId);
|
||
return _cpri__HashBlock(algId, blockSize, block, retSize, ret);
|
||
}
|
||
//
|
||
//
|
||
// 10.2.4.14 CryptCompleteHMAC()
|
||
//
|
||
// This function completes a HMAC sequence and returns the digest. If digestSize is smaller than the digest
|
||
// size of the HMAC algorithm, the most significant bytes of required size will be returned.
|
||
//
|
||
// Return Value Meaning
|
||
//
|
||
// >=0 the number of bytes placed in digest
|
||
//
|
||
LIB_EXPORT UINT16
|
||
CryptCompleteHMAC(
|
||
HMAC_STATE *hmacState, // IN: the state of HMAC stack
|
||
UINT32 digestSize, // IN: size of digest buffer
|
||
BYTE *digest // OUT: HMAC digest
|
||
)
|
||
{
|
||
HASH_STATE *hashState;
|
||
pAssert(hmacState != NULL);
|
||
hashState = &hmacState->hashState;
|
||
pAssert(hashState->type == HASH_STATE_HMAC);
|
||
hashState->type = HASH_STATE_EMPTY;
|
||
return _cpri__CompleteHMAC(&hashState->state, &hmacState->hmacKey.b,
|
||
digestSize, digest);
|
||
}
|
||
//
|
||
//
|
||
// 10.2.4.15 CryptCompleteHMAC2B()
|
||
//
|
||
// This function is the same as CryptCompleteHMAC() 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
|
||
CryptCompleteHMAC2B(
|
||
HMAC_STATE *hmacState, // IN: the state of HMAC stack
|
||
TPM2B *digest // OUT: HMAC
|
||
)
|
||
{
|
||
UINT16 retVal = 0;
|
||
if(digest != NULL)
|
||
retVal = CryptCompleteHMAC(hmacState, digest->size, digest->buffer);
|
||
return retVal;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.4.16 CryptHashStateImportExport()
|
||
//
|
||
// This function is used to prepare a hash state context for LIB_EXPORT or to import it into the internal
|
||
// format. It is used by TPM2_ContextSave() and TPM2_ContextLoad() via SequenceDataImportExport().
|
||
// This is just a pass-through function to the crypto library.
|
||
//
|
||
void
|
||
CryptHashStateImportExport(
|
||
HASH_STATE *internalFmt, // IN: state to LIB_EXPORT
|
||
HASH_STATE *externalFmt, // OUT: exported state
|
||
IMPORT_EXPORT direction
|
||
)
|
||
{
|
||
_cpri__ImportExportHashState(&internalFmt->state,
|
||
(EXPORT_HASH_STATE *)&externalFmt->state,
|
||
direction);
|
||
}
|
||
//
|
||
//
|
||
// 10.2.4.17 CryptGetHashDigestSize()
|
||
//
|
||
// This function returns the digest size in bytes for a hash algorithm.
|
||
//
|
||
// Return Value Meaning
|
||
//
|
||
// 0 digest size for TPM_ALG_NULL
|
||
// >0 digest size
|
||
//
|
||
LIB_EXPORT UINT16
|
||
CryptGetHashDigestSize(
|
||
TPM_ALG_ID hashAlg // IN: hash algorithm
|
||
)
|
||
{
|
||
return _cpri__GetDigestSize(hashAlg);
|
||
}
|
||
//
|
||
//
|
||
// 10.2.4.18 CryptGetHashBlockSize()
|
||
//
|
||
// Get the digest size in byte of a hash algorithm.
|
||
//
|
||
// Return Value Meaning
|
||
//
|
||
// 0 block size for TPM_ALG_NULL
|
||
// >0 block size
|
||
//
|
||
LIB_EXPORT UINT16
|
||
CryptGetHashBlockSize(
|
||
TPM_ALG_ID hash // IN: hash algorithm to look up
|
||
)
|
||
{
|
||
return _cpri__GetHashBlockSize(hash);
|
||
}
|
||
//
|
||
//
|
||
// 10.2.4.19 CryptGetHashAlgByIndex()
|
||
//
|
||
// 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 value of 2 will return the last implemented hash. 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
|
||
CryptGetHashAlgByIndex(
|
||
UINT32 index // IN: the index
|
||
)
|
||
{
|
||
return _cpri__GetHashAlgByIndex(index);
|
||
}
|
||
//
|
||
//
|
||
// 10.2.4.20 CryptSignHMAC()
|
||
//
|
||
// Sign a digest using an HMAC key. This an HMAC of a digest, not an HMAC of a message.
|
||
//
|
||
// Error Returns Meaning
|
||
//
|
||
static TPM_RC
|
||
CryptSignHMAC(
|
||
OBJECT *signKey, // IN: HMAC key sign the hash
|
||
TPMT_SIG_SCHEME *scheme, // IN: signing scheme
|
||
TPM2B_DIGEST *hashData, // IN: hash to be signed
|
||
TPMT_SIGNATURE *signature // OUT: signature
|
||
)
|
||
{
|
||
//
|
||
HMAC_STATE hmacState;
|
||
UINT32 digestSize;
|
||
// HMAC algorithm self testing code may be inserted here
|
||
digestSize = CryptStartHMAC2B(scheme->details.hmac.hashAlg,
|
||
&signKey->sensitive.sensitive.bits.b,
|
||
&hmacState);
|
||
// The hash algorithm must be a valid one.
|
||
pAssert(digestSize > 0);
|
||
CryptUpdateDigest2B(&hmacState, &hashData->b);
|
||
CryptCompleteHMAC(&hmacState, digestSize,
|
||
(BYTE *) &signature->signature.hmac.digest);
|
||
// Set HMAC algorithm
|
||
signature->signature.hmac.hashAlg = scheme->details.hmac.hashAlg;
|
||
return TPM_RC_SUCCESS;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.4.21 CryptHMACVerifySignature()
|
||
//
|
||
// This function will verify a signature signed by a HMAC key.
|
||
//
|
||
// Error Returns Meaning
|
||
//
|
||
// TPM_RC_SIGNATURE if invalid input or signature is not genuine
|
||
//
|
||
static TPM_RC
|
||
CryptHMACVerifySignature(
|
||
OBJECT *signKey, // IN: HMAC key signed the hash
|
||
TPM2B_DIGEST *hashData, // IN: digest being verified
|
||
TPMT_SIGNATURE *signature // IN: signature to be verified
|
||
)
|
||
{
|
||
HMAC_STATE hmacState;
|
||
TPM2B_DIGEST digestToCompare;
|
||
digestToCompare.t.size = CryptStartHMAC2B(signature->signature.hmac.hashAlg,
|
||
&signKey->sensitive.sensitive.bits.b, &hmacState);
|
||
CryptUpdateDigest2B(&hmacState, &hashData->b);
|
||
CryptCompleteHMAC2B(&hmacState, &digestToCompare.b);
|
||
// Compare digest
|
||
if(MemoryEqual(digestToCompare.t.buffer,
|
||
(BYTE *) &signature->signature.hmac.digest,
|
||
digestToCompare.t.size))
|
||
return TPM_RC_SUCCESS;
|
||
else
|
||
return TPM_RC_SIGNATURE;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.4.22 CryptGenerateKeyedHash()
|
||
//
|
||
// This function creates a keyedHash object.
|
||
//
|
||
//
|
||
//
|
||
// Error Returns Meaning
|
||
//
|
||
// TPM_RC_SIZE sensitive data size is larger than allowed for the scheme
|
||
// TPM_RC_VALUE the publicArea nameAlg is invalid
|
||
//
|
||
static TPM_RC
|
||
CryptGenerateKeyedHash(
|
||
TPMT_PUBLIC *publicArea, // IN/OUT: the public area template
|
||
// for the new key.
|
||
TPMS_SENSITIVE_CREATE *sensitiveCreate, // IN: sensitive creation data
|
||
TPMT_SENSITIVE *sensitive, // OUT: sensitive area
|
||
TPM_ALG_ID kdfHashAlg, // IN: algorithm for the KDF
|
||
TPM2B_SEED *seed, // IN: the seed
|
||
TPM2B_NAME *name // IN: name of the object
|
||
)
|
||
{
|
||
TPMT_KEYEDHASH_SCHEME *scheme;
|
||
TPM_ALG_ID hashAlg;
|
||
UINT16 hashBlockSize;
|
||
// Check parameter values
|
||
if(publicArea->nameAlg == TPM_ALG_NULL)
|
||
{
|
||
return TPM_RC_VALUE;
|
||
}
|
||
scheme = &publicArea->parameters.keyedHashDetail.scheme;
|
||
pAssert(publicArea->type == TPM_ALG_KEYEDHASH);
|
||
// Pick the limiting hash algorithm
|
||
if(scheme->scheme == TPM_ALG_NULL)
|
||
hashAlg = publicArea->nameAlg;
|
||
else if(scheme->scheme == TPM_ALG_XOR)
|
||
hashAlg = scheme->details.xor_.hashAlg;
|
||
else
|
||
hashAlg = scheme->details.hmac.hashAlg;
|
||
hashBlockSize = CryptGetHashBlockSize(hashAlg);
|
||
// if this is a signing or a decryption key, then then the limit
|
||
// for the data size is the block size of the hash. This limit
|
||
// is set because larger values have lower entropy because of the
|
||
// HMAC function.
|
||
if(publicArea->objectAttributes.sensitiveDataOrigin == CLEAR)
|
||
{
|
||
if( ( publicArea->objectAttributes.decrypt
|
||
|| publicArea->objectAttributes.sign)
|
||
&& sensitiveCreate->data.t.size > hashBlockSize)
|
||
return TPM_RC_SIZE;
|
||
}
|
||
else
|
||
{
|
||
// If the TPM is going to generate the data, then set the size to be the
|
||
// size of the digest of the algorithm
|
||
sensitive->sensitive.sym.t.size = CryptGetHashDigestSize(hashAlg);
|
||
sensitiveCreate->data.t.size = 0;
|
||
}
|
||
// Fill in the sensitive area
|
||
CryptGenerateNewSymmetric(sensitiveCreate, sensitive, kdfHashAlg,
|
||
seed, name);
|
||
// Create unique area in public
|
||
CryptComputeSymmetricUnique(publicArea->nameAlg,
|
||
sensitive, &publicArea->unique.sym);
|
||
return TPM_RC_SUCCESS;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.4.25 KDFa()
|
||
//
|
||
// This function is used by functions outside of CryptUtil() to access _cpri_KDFa().
|
||
//
|
||
void
|
||
KDFa(
|
||
TPM_ALG_ID hash, // IN: hash algorithm used in HMAC
|
||
TPM2B *key, // IN: HMAC key
|
||
const char *label, // IN: a null-terminated label for 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.
|
||
)
|
||
{
|
||
CryptKDFa(hash, key, label, contextU, contextV, sizeInBits,
|
||
keyStream, counterInOut);
|
||
}
|
||
#endif //TPM_ALG_KEYEDHASH //% 1
|
||
//
|
||
//
|
||
// 10.2.5 RSA Functions
|
||
//
|
||
// 10.2.5.1 BuildRSA()
|
||
//
|
||
// Function to set the cryptographic elements of an RSA key into a structure to simplify the interface to
|
||
// _cpri__ RSA function. This can/should be eliminated by building this structure into the object structure.
|
||
//
|
||
#ifdef TPM_ALG_RSA //% 2
|
||
static void
|
||
BuildRSA(
|
||
OBJECT *rsaKey,
|
||
RSA_KEY *key
|
||
)
|
||
{
|
||
key->exponent = rsaKey->publicArea.parameters.rsaDetail.exponent;
|
||
if(key->exponent == 0)
|
||
key->exponent = RSA_DEFAULT_PUBLIC_EXPONENT;
|
||
key->publicKey = &rsaKey->publicArea.unique.rsa.b;
|
||
if(rsaKey->attributes.publicOnly || rsaKey->privateExponent.t.size == 0)
|
||
key->privateKey = NULL;
|
||
else
|
||
key->privateKey = &(rsaKey->privateExponent.b);
|
||
}
|
||
//
|
||
//
|
||
// 10.2.5.2 CryptTestKeyRSA()
|
||
//
|
||
// This function provides the interface to _cpri__TestKeyRSA(). If both p and q are provided, n will be set to
|
||
// p*q.
|
||
// If only p is provided, q is computed by q = n/p. If n mod p != 0, TPM_RC_BINDING is returned.
|
||
// The key is validated by checking that a d can be found such that e d mod ((p-1)*(q-1)) = 1. If d is found
|
||
// that satisfies this requirement, it will be placed in d.
|
||
// Page 286 TCG Published Family "2.0"
|
||
// October 30, 2014 Copyright © TCG 2006-2014 Level 00 Revision 01.16
|
||
// Part 4: Supporting Routines Trusted Platform Module Library
|
||
//
|
||
//
|
||
// Error Returns Meaning
|
||
//
|
||
// TPM_RC_BINDING the public and private portions of the key are not matched
|
||
//
|
||
TPM_RC
|
||
CryptTestKeyRSA(
|
||
TPM2B *d, // OUT: receives the private exponent
|
||
UINT32 e, // IN: public exponent
|
||
TPM2B *n, // IN/OUT: public modulu
|
||
TPM2B *p, // IN: a first prime
|
||
TPM2B *q // IN: an optional second prime
|
||
)
|
||
{
|
||
CRYPT_RESULT retVal;
|
||
TEST(ALG_NULL_VALUE);
|
||
pAssert(d != NULL && n != NULL && p != NULL);
|
||
// Set the exponent
|
||
if(e == 0)
|
||
e = RSA_DEFAULT_PUBLIC_EXPONENT;
|
||
// CRYPT_PARAMETER
|
||
retVal =_cpri__TestKeyRSA(d, e, n, p, q);
|
||
if(retVal == CRYPT_SUCCESS)
|
||
return TPM_RC_SUCCESS;
|
||
else
|
||
return TPM_RC_BINDING; // convert CRYPT_PARAMETER
|
||
}
|
||
//
|
||
//
|
||
// 10.2.5.3 CryptGenerateKeyRSA()
|
||
//
|
||
// This function is called to generate an RSA key from a provided seed. It calls _cpri__GenerateKeyRSA()
|
||
// to perform the computations. The implementation is vendor specific.
|
||
//
|
||
// Error Returns Meaning
|
||
//
|
||
// TPM_RC_RANGE the exponent value is not supported
|
||
// TPM_RC_CANCELLED key generation has been canceled
|
||
// TPM_RC_VALUE exponent is not prime or is less than 3; or could not find a prime using
|
||
// the provided parameters
|
||
//
|
||
static TPM_RC
|
||
CryptGenerateKeyRSA(
|
||
TPMT_PUBLIC *publicArea, // IN/OUT: The public area template for
|
||
// the new key. The public key
|
||
// area will be replaced by the
|
||
// product of two primes found by
|
||
// this function
|
||
TPMT_SENSITIVE *sensitive, // OUT: the sensitive area will be
|
||
// updated to contain the first
|
||
// prime and the symmetric
|
||
// encryption key
|
||
TPM_ALG_ID hashAlg, // IN: the hash algorithm for the KDF
|
||
TPM2B_SEED *seed, // IN: Seed for the creation
|
||
TPM2B_NAME *name, // IN: Object name
|
||
UINT32 *counter // OUT: last iteration of the counter
|
||
)
|
||
{
|
||
CRYPT_RESULT retVal;
|
||
UINT32 exponent = publicArea->parameters.rsaDetail.exponent;
|
||
TEST_HASH(hashAlg);
|
||
TEST(ALG_NULL_VALUE);
|
||
// In this implementation, only the default exponent is allowed
|
||
if(exponent != 0 && exponent != RSA_DEFAULT_PUBLIC_EXPONENT)
|
||
return TPM_RC_RANGE;
|
||
exponent = RSA_DEFAULT_PUBLIC_EXPONENT;
|
||
*counter = 0;
|
||
// _cpri_GenerateKeyRSA can return CRYPT_CANCEL or CRYPT_FAIL
|
||
retVal = _cpri__GenerateKeyRSA(&publicArea->unique.rsa.b,
|
||
&sensitive->sensitive.rsa.b,
|
||
publicArea->parameters.rsaDetail.keyBits,
|
||
exponent,
|
||
hashAlg,
|
||
&seed->b,
|
||
"RSA key by vendor",
|
||
&name->b,
|
||
counter);
|
||
// CRYPT_CANCEL -> TPM_RC_CANCELLED; CRYPT_FAIL -> TPM_RC_VALUE
|
||
return TranslateCryptErrors(retVal);
|
||
}
|
||
//
|
||
//
|
||
// 10.2.5.4 CryptLoadPrivateRSA()
|
||
//
|
||
// This function is called to generate the private exponent of an RSA key. It uses CryptTestKeyRSA().
|
||
//
|
||
// Error Returns Meaning
|
||
//
|
||
// TPM_RC_BINDING public and private parts of rsaKey are not matched
|
||
//
|
||
TPM_RC
|
||
CryptLoadPrivateRSA(
|
||
OBJECT *rsaKey // IN: the RSA key object
|
||
)
|
||
{
|
||
TPM_RC result;
|
||
TPMT_PUBLIC *publicArea = &rsaKey->publicArea;
|
||
TPMT_SENSITIVE *sensitive = &rsaKey->sensitive;
|
||
// Load key by computing the private exponent
|
||
// TPM_RC_BINDING
|
||
result = CryptTestKeyRSA(&(rsaKey->privateExponent.b),
|
||
publicArea->parameters.rsaDetail.exponent,
|
||
&(publicArea->unique.rsa.b),
|
||
&(sensitive->sensitive.rsa.b),
|
||
NULL);
|
||
if(result == TPM_RC_SUCCESS)
|
||
rsaKey->attributes.privateExp = SET;
|
||
return result;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.5.5 CryptSelectRSAScheme()
|
||
//
|
||
// This function is used by TPM2_RSA_Decrypt() and TPM2_RSA_Encrypt(). It sets up the rules to select a
|
||
// scheme between input and object default. This function assume the RSA object is loaded. If a default
|
||
// scheme is defined in object, the default scheme should be chosen, otherwise, the input scheme should
|
||
// be chosen. In the case that both the object and scheme are not TPM_ALG_NULL, then if the schemes
|
||
//
|
||
//
|
||
// are the same, the input scheme will be chosen. if the scheme are not compatible, a NULL pointer will be
|
||
// returned.
|
||
// The return pointer may point to a TPM_ALG_NULL scheme.
|
||
//
|
||
TPMT_RSA_DECRYPT*
|
||
CryptSelectRSAScheme(
|
||
TPMI_DH_OBJECT rsaHandle, // IN: handle of sign key
|
||
TPMT_RSA_DECRYPT *scheme // IN: a sign or decrypt scheme
|
||
)
|
||
{
|
||
OBJECT *rsaObject;
|
||
TPMT_ASYM_SCHEME *keyScheme;
|
||
TPMT_RSA_DECRYPT *retVal = NULL;
|
||
// Get sign object pointer
|
||
rsaObject = ObjectGet(rsaHandle);
|
||
keyScheme = &rsaObject->publicArea.parameters.asymDetail.scheme;
|
||
// if the default scheme of the object is TPM_ALG_NULL, then select the
|
||
// input scheme
|
||
if(keyScheme->scheme == TPM_ALG_NULL)
|
||
{
|
||
retVal = scheme;
|
||
}
|
||
// if the object scheme is not TPM_ALG_NULL and the input scheme is
|
||
// TPM_ALG_NULL, then select the default scheme of the object.
|
||
else if(scheme->scheme == TPM_ALG_NULL)
|
||
{
|
||
// if input scheme is NULL
|
||
retVal = (TPMT_RSA_DECRYPT *)keyScheme;
|
||
}
|
||
// get here if both the object scheme and the input scheme are
|
||
// not TPM_ALG_NULL. Need to insure that they are the same.
|
||
// The hash algorithm match has to be verified for OAEP.
|
||
// IMPLEMENTATION NOTE: This could cause problems if future versions have
|
||
// schemes that have more values than just a hash algorithm. A new function
|
||
// (IsSchemeSame()) might be needed then.
|
||
else if (keyScheme->scheme == scheme->scheme
|
||
&& ((keyScheme->scheme != TPM_ALG_OAEP) ||
|
||
(keyScheme->details.anySig.hashAlg == scheme->details.anySig.hashAlg)))
|
||
{
|
||
retVal = scheme;
|
||
}
|
||
// two different, incompatible schemes specified will return NULL
|
||
return retVal;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.5.6 CryptDecryptRSA()
|
||
//
|
||
// This function is the interface to _cpri__DecryptRSA(). It handles the return codes from that function and
|
||
// converts them from CRYPT_RESULT to TPM_RC values. The rsaKey parameter must reference an RSA
|
||
// decryption key
|
||
//
|
||
// Error Returns Meaning
|
||
//
|
||
// TPM_RC_BINDING Public and private parts of the key are not cryptographically bound.
|
||
// TPM_RC_SIZE Size of data to decrypt is not the same as the key size.
|
||
// TPM_RC_VALUE Numeric value of the encrypted data is greater than the public
|
||
// exponent, or output buffer is too small for the decrypted message.
|
||
//
|
||
TPM_RC
|
||
CryptDecryptRSA(
|
||
UINT16 *dataOutSize, // OUT: size of plain text in byte
|
||
BYTE *dataOut, // OUT: plain text
|
||
OBJECT *rsaKey, // IN: internal RSA key
|
||
TPMT_RSA_DECRYPT *scheme, // IN: selects the padding scheme
|
||
UINT16 cipherInSize, // IN: size of cipher text in byte
|
||
BYTE *cipherIn, // IN: cipher text
|
||
const char *label // IN: a label, when needed
|
||
)
|
||
{
|
||
RSA_KEY key;
|
||
CRYPT_RESULT retVal = CRYPT_SUCCESS;
|
||
UINT32 dSize; // Place to put temporary value for the
|
||
// returned data size
|
||
TPMI_ALG_HASH hashAlg = TPM_ALG_NULL; // hash algorithm in the selected
|
||
// padding scheme
|
||
TPM_RC result = TPM_RC_SUCCESS;
|
||
// pointer checks
|
||
pAssert( (dataOutSize != NULL) && (dataOut != NULL)
|
||
&& (rsaKey != NULL) && (cipherIn != NULL));
|
||
// The public type is a RSA decrypt key
|
||
pAssert( (rsaKey->publicArea.type == TPM_ALG_RSA
|
||
&& rsaKey->publicArea.objectAttributes.decrypt == SET));
|
||
// Must have the private portion loaded. This check is made before this
|
||
// function is called.
|
||
pAssert(rsaKey->attributes.publicOnly == CLEAR);
|
||
// decryption requires that the private modulus be present
|
||
if(rsaKey->attributes.privateExp == CLEAR)
|
||
{
|
||
// Load key by computing the private exponent
|
||
// CryptLoadPrivateRSA may return TPM_RC_BINDING
|
||
result = CryptLoadPrivateRSA(rsaKey);
|
||
}
|
||
// the input buffer must be the size of the key
|
||
if(result == TPM_RC_SUCCESS)
|
||
{
|
||
if(cipherInSize != rsaKey->publicArea.unique.rsa.t.size)
|
||
result = TPM_RC_SIZE;
|
||
else
|
||
{
|
||
BuildRSA(rsaKey, &key);
|
||
// Initialize the dOutSize parameter
|
||
dSize = *dataOutSize;
|
||
// For OAEP scheme, initialize the hash algorithm for padding
|
||
if(scheme->scheme == TPM_ALG_OAEP)
|
||
{
|
||
hashAlg = scheme->details.oaep.hashAlg;
|
||
TEST_HASH(hashAlg);
|
||
}
|
||
// See if the padding mode needs to be tested
|
||
TEST(scheme->scheme);
|
||
// _cpri__DecryptRSA may return CRYPT_PARAMETER CRYPT_FAIL CRYPT_SCHEME
|
||
retVal = _cpri__DecryptRSA(&dSize, dataOut, &key, scheme->scheme,
|
||
cipherInSize, cipherIn, hashAlg, label);
|
||
// Scheme must have been validated when the key was loaded/imported
|
||
pAssert(retVal != CRYPT_SCHEME);
|
||
// Set the return size
|
||
pAssert(dSize <= UINT16_MAX);
|
||
*dataOutSize = (UINT16)dSize;
|
||
// CRYPT_PARAMETER -> TPM_RC_VALUE, CRYPT_FAIL -> TPM_RC_VALUE
|
||
result = TranslateCryptErrors(retVal);
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.5.7 CryptEncryptRSA()
|
||
//
|
||
// This function provides the interface to _cpri__EncryptRSA(). The object referenced by rsaKey is required
|
||
// to be an RSA decryption key.
|
||
//
|
||
// Error Returns Meaning
|
||
//
|
||
// TPM_RC_SCHEME scheme is not supported
|
||
// TPM_RC_VALUE numeric value of dataIn is greater than the key modulus
|
||
//
|
||
TPM_RC
|
||
CryptEncryptRSA(
|
||
UINT16 *cipherOutSize, // OUT: size of cipher text in byte
|
||
BYTE *cipherOut, // OUT: cipher text
|
||
OBJECT *rsaKey, // IN: internal RSA key
|
||
TPMT_RSA_DECRYPT *scheme, // IN: selects the padding scheme
|
||
UINT16 dataInSize, // IN: size of plain text in byte
|
||
BYTE *dataIn, // IN: plain text
|
||
const char *label // IN: an optional label
|
||
)
|
||
{
|
||
RSA_KEY key;
|
||
CRYPT_RESULT retVal;
|
||
UINT32 cOutSize; // Conversion variable
|
||
TPMI_ALG_HASH hashAlg = TPM_ALG_NULL; // hash algorithm in selected
|
||
// padding scheme
|
||
// must have a pointer to a key and some data to encrypt
|
||
pAssert(rsaKey != NULL && dataIn != NULL);
|
||
// The public type is a RSA decryption key
|
||
pAssert( rsaKey->publicArea.type == TPM_ALG_RSA
|
||
&& rsaKey->publicArea.objectAttributes.decrypt == SET);
|
||
// If the cipher buffer must be provided and it must be large enough
|
||
// for the result
|
||
pAssert( cipherOut != NULL
|
||
&& cipherOutSize != NULL
|
||
&& *cipherOutSize >= rsaKey->publicArea.unique.rsa.t.size);
|
||
// Only need the public key and exponent for encryption
|
||
BuildRSA(rsaKey, &key);
|
||
// Copy the size to the conversion buffer
|
||
cOutSize = *cipherOutSize;
|
||
// For OAEP scheme, initialize the hash algorithm for padding
|
||
if(scheme->scheme == TPM_ALG_OAEP)
|
||
{
|
||
hashAlg = scheme->details.oaep.hashAlg;
|
||
TEST_HASH(hashAlg);
|
||
}
|
||
// This is a public key operation and does not require that the private key
|
||
// be loaded. To verify this, need to do the full algorithm
|
||
TEST(scheme->scheme);
|
||
// Encrypt the data with the public exponent
|
||
// _cpri__EncryptRSA may return CRYPT_PARAMETER or CRYPT_SCHEME
|
||
retVal = _cpri__EncryptRSA(&cOutSize,cipherOut, &key, scheme->scheme,
|
||
dataInSize, dataIn, hashAlg, label);
|
||
pAssert (cOutSize <= UINT16_MAX);
|
||
*cipherOutSize = (UINT16)cOutSize;
|
||
// CRYPT_PARAMETER -> TPM_RC_VALUE, CRYPT_SCHEME -> TPM_RC_SCHEME
|
||
return TranslateCryptErrors(retVal);
|
||
}
|
||
//
|
||
//
|
||
// 10.2.5.8 CryptSignRSA()
|
||
//
|
||
// This function is used to sign a digest with an RSA signing key.
|
||
//
|
||
// Error Returns Meaning
|
||
//
|
||
// TPM_RC_BINDING public and private part of signKey are not properly bound
|
||
// TPM_RC_SCHEME scheme is not supported
|
||
// TPM_RC_VALUE hashData is larger than the modulus of signKey, or the size of
|
||
// hashData does not match hash algorithm in scheme
|
||
//
|
||
static TPM_RC
|
||
CryptSignRSA(
|
||
OBJECT *signKey, // IN: RSA key signs the hash
|
||
TPMT_SIG_SCHEME *scheme, // IN: sign scheme
|
||
TPM2B_DIGEST *hashData, // IN: hash to be signed
|
||
TPMT_SIGNATURE *sig // OUT: signature
|
||
)
|
||
{
|
||
UINT32 signSize;
|
||
RSA_KEY key;
|
||
CRYPT_RESULT retVal;
|
||
TPM_RC result = TPM_RC_SUCCESS;
|
||
pAssert( (signKey != NULL) && (scheme != NULL)
|
||
&& (hashData != NULL) && (sig != NULL));
|
||
// assume that the key has private part loaded and that it is a signing key.
|
||
pAssert( (signKey->attributes.publicOnly == CLEAR)
|
||
&& (signKey->publicArea.objectAttributes.sign == SET));
|
||
// check if the private exponent has been computed
|
||
if(signKey->attributes.privateExp == CLEAR)
|
||
// May return TPM_RC_BINDING
|
||
result = CryptLoadPrivateRSA(signKey);
|
||
if(result == TPM_RC_SUCCESS)
|
||
{
|
||
BuildRSA(signKey, &key);
|
||
// Make sure that the hash is tested
|
||
TEST_HASH(sig->signature.any.hashAlg);
|
||
// Run a test of the RSA sign
|
||
TEST(scheme->scheme);
|
||
// _crypi__SignRSA can return CRYPT_SCHEME and CRYPT_PARAMETER
|
||
retVal = _cpri__SignRSA(&signSize,
|
||
sig->signature.rsassa.sig.t.buffer,
|
||
&key,
|
||
sig->sigAlg,
|
||
sig->signature.any.hashAlg,
|
||
hashData->t.size, hashData->t.buffer);
|
||
pAssert(signSize <= UINT16_MAX);
|
||
sig->signature.rsassa.sig.t.size = (UINT16)signSize;
|
||
// CRYPT_SCHEME -> TPM_RC_SCHEME; CRYPT_PARAMTER -> TPM_RC_VALUE
|
||
result = TranslateCryptErrors(retVal);
|
||
}
|
||
return result;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.5.9 CryptRSAVerifySignature()
|
||
//
|
||
// This function is used to verify signature signed by a RSA key.
|
||
//
|
||
// Error Returns Meaning
|
||
//
|
||
// TPM_RC_SIGNATURE if signature is not genuine
|
||
// TPM_RC_SCHEME signature scheme not supported
|
||
//
|
||
static TPM_RC
|
||
CryptRSAVerifySignature(
|
||
OBJECT *signKey, // IN: RSA key signed the hash
|
||
TPM2B_DIGEST *digestData, // IN: digest being signed
|
||
TPMT_SIGNATURE *sig // IN: signature to be verified
|
||
)
|
||
{
|
||
RSA_KEY key;
|
||
CRYPT_RESULT retVal;
|
||
TPM_RC result;
|
||
// Validate parameter assumptions
|
||
pAssert((signKey != NULL) && (digestData != NULL) && (sig != NULL));
|
||
TEST_HASH(sig->signature.any.hashAlg);
|
||
TEST(sig->sigAlg);
|
||
// This is a public-key-only operation
|
||
BuildRSA(signKey, &key);
|
||
// Call crypto engine to verify signature
|
||
// _cpri_ValidateSignaturRSA may return CRYPT_FAIL or CRYPT_SCHEME
|
||
retVal = _cpri__ValidateSignatureRSA(&key,
|
||
sig->sigAlg,
|
||
sig->signature.any.hashAlg,
|
||
digestData->t.size,
|
||
digestData->t.buffer,
|
||
sig->signature.rsassa.sig.t.size,
|
||
sig->signature.rsassa.sig.t.buffer,
|
||
0);
|
||
// _cpri__ValidateSignatureRSA can return CRYPT_SUCCESS, CRYPT_FAIL, or
|
||
// CRYPT_SCHEME. Translate CRYPT_FAIL to TPM_RC_SIGNATURE
|
||
if(retVal == CRYPT_FAIL)
|
||
result = TPM_RC_SIGNATURE;
|
||
else
|
||
// CRYPT_SCHEME -> TPM_RC_SCHEME
|
||
result = TranslateCryptErrors(retVal);
|
||
return result;
|
||
}
|
||
//
|
||
#endif //TPM_ALG_RSA //% 2
|
||
//
|
||
//
|
||
// 10.2.6 ECC Functions
|
||
//
|
||
// 10.2.6.1 CryptEccGetCurveDataPointer()
|
||
//
|
||
// This function returns a pointer to an ECC_CURVE_VALUES structure that contains the parameters for
|
||
// the key size and schemes for a given curve.
|
||
//
|
||
#ifdef TPM_ALG_ECC //% 3
|
||
static const ECC_CURVE *
|
||
CryptEccGetCurveDataPointer(
|
||
TPM_ECC_CURVE curveID // IN: id of the curve
|
||
)
|
||
{
|
||
return _cpri__EccGetParametersByCurveId(curveID);
|
||
}
|
||
//
|
||
//
|
||
// 10.2.6.2 CryptEccGetKeySizeInBits()
|
||
//
|
||
// This function returns the size in bits of the key associated with a curve.
|
||
//
|
||
UINT16
|
||
CryptEccGetKeySizeInBits(
|
||
TPM_ECC_CURVE curveID // IN: id of the curve
|
||
)
|
||
{
|
||
const ECC_CURVE *curve = CryptEccGetCurveDataPointer(curveID);
|
||
UINT16 keySizeInBits = 0;
|
||
if(curve != NULL)
|
||
keySizeInBits = curve->keySizeBits;
|
||
return keySizeInBits;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.6.4 CryptEccGetParameter()
|
||
//
|
||
// This function returns a pointer to an ECC curve parameter. The parameter is selected by a single
|
||
// character designator from the set of {pnabxyh}.
|
||
//
|
||
LIB_EXPORT const TPM2B *
|
||
CryptEccGetParameter(
|
||
char p, // IN: the parameter selector
|
||
TPM_ECC_CURVE curveId // IN: the curve id
|
||
)
|
||
{
|
||
const ECC_CURVE *curve = _cpri__EccGetParametersByCurveId(curveId);
|
||
const TPM2B *parameter = NULL;
|
||
if(curve != NULL)
|
||
{
|
||
switch (p)
|
||
{
|
||
case 'p':
|
||
parameter = curve->curveData->p;
|
||
break;
|
||
case 'n':
|
||
parameter = curve->curveData->n;
|
||
break;
|
||
case 'a':
|
||
parameter = curve->curveData->a;
|
||
break;
|
||
case 'b':
|
||
parameter = curve->curveData->b;
|
||
break;
|
||
case 'x':
|
||
parameter = curve->curveData->x;
|
||
break;
|
||
case 'y':
|
||
parameter = curve->curveData->y;
|
||
break;
|
||
case 'h':
|
||
parameter = curve->curveData->h;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
return parameter;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.6.5 CryptGetCurveSignScheme()
|
||
//
|
||
// This function will return a pointer to the scheme of the curve.
|
||
//
|
||
const TPMT_ECC_SCHEME *
|
||
CryptGetCurveSignScheme(
|
||
TPM_ECC_CURVE curveId // IN: The curve selector
|
||
)
|
||
{
|
||
const ECC_CURVE *curve = _cpri__EccGetParametersByCurveId(curveId);
|
||
const TPMT_ECC_SCHEME *scheme = NULL;
|
||
if(curve != NULL)
|
||
scheme = &(curve->sign);
|
||
return scheme;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.6.6 CryptEccIsPointOnCurve()
|
||
//
|
||
// This function will validate that an ECC point is on the curve of given curveID.
|
||
//
|
||
// Return Value Meaning
|
||
//
|
||
// TRUE if the point is on curve
|
||
// FALSE if the point is not on curve
|
||
//
|
||
BOOL
|
||
CryptEccIsPointOnCurve(
|
||
TPM_ECC_CURVE curveID, // IN: ECC curve ID
|
||
TPMS_ECC_POINT *Q // IN: ECC point
|
||
)
|
||
{
|
||
// Make sure that point multiply is working
|
||
TEST(TPM_ALG_ECC);
|
||
// Check point on curve logic by seeing if the test key is on the curve
|
||
// Call crypto engine function to check if a ECC public point is on the
|
||
// given curve
|
||
if(_cpri__EccIsPointOnCurve(curveID, Q))
|
||
return TRUE;
|
||
else
|
||
return FALSE;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.6.7 CryptNewEccKey()
|
||
//
|
||
// This function creates a random ECC key that is not derived from other parameters as is a Primary Key.
|
||
//
|
||
TPM_RC
|
||
CryptNewEccKey(
|
||
TPM_ECC_CURVE curveID, // IN: ECC curve
|
||
TPMS_ECC_POINT *publicPoint, // OUT: public point
|
||
TPM2B_ECC_PARAMETER *sensitive // OUT: private area
|
||
)
|
||
{
|
||
TPM_RC result = TPM_RC_SUCCESS;
|
||
// _cpri__GetEphemeralECC may return CRYPT_PARAMETER
|
||
if(_cpri__GetEphemeralEcc(publicPoint, sensitive, curveID) != CRYPT_SUCCESS)
|
||
// Something is wrong with the key.
|
||
result = TPM_RC_KEY;
|
||
return result;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.6.8 CryptEccPointMultiply()
|
||
//
|
||
// This function is used to perform a point multiply R = [d]Q. If Q is not provided, the multiplication is
|
||
// performed using the generator point of the curve.
|
||
//
|
||
// Error Returns Meaning
|
||
//
|
||
// TPM_RC_ECC_POINT invalid optional ECC point pIn
|
||
// TPM_RC_NO_RESULT multiplication resulted in a point at infinity
|
||
// TPM_RC_CANCELED if a self-test was done, it might have been aborted
|
||
//
|
||
TPM_RC
|
||
CryptEccPointMultiply(
|
||
TPMS_ECC_POINT *pOut, // OUT: output point
|
||
TPM_ECC_CURVE curveId, // IN: curve selector
|
||
TPM2B_ECC_PARAMETER *dIn, // IN: public scalar
|
||
TPMS_ECC_POINT *pIn // IN: optional point
|
||
)
|
||
{
|
||
TPM2B_ECC_PARAMETER *n = NULL;
|
||
CRYPT_RESULT retVal;
|
||
pAssert(pOut != NULL && dIn != NULL);
|
||
if(pIn != NULL)
|
||
{
|
||
n = dIn;
|
||
dIn = NULL;
|
||
}
|
||
// Do a test of point multiply
|
||
TEST(TPM_ALG_ECC);
|
||
// _cpri__EccPointMultiply may return CRYPT_POINT or CRYPT_NO_RESULT
|
||
retVal = _cpri__EccPointMultiply(pOut, curveId, dIn, pIn, n);
|
||
// CRYPT_POINT->TPM_RC_ECC_POINT and CRYPT_NO_RESULT->TPM_RC_NO_RESULT
|
||
return TranslateCryptErrors(retVal);
|
||
}
|
||
//
|
||
//
|
||
// 10.2.6.9 CryptGenerateKeyECC()
|
||
//
|
||
// This function generates an ECC key from a seed value.
|
||
// The method here may not work for objects that have an order (G) that with a different size than a private
|
||
// key.
|
||
//
|
||
// Error Returns Meaning
|
||
//
|
||
// TPM_RC_VALUE hash algorithm is not supported
|
||
//
|
||
static TPM_RC
|
||
CryptGenerateKeyECC(
|
||
TPMT_PUBLIC *publicArea, // IN/OUT: The public area template for the new
|
||
// key.
|
||
TPMT_SENSITIVE *sensitive, // IN/OUT: the sensitive area
|
||
TPM_ALG_ID hashAlg, // IN: algorithm for the KDF
|
||
TPM2B_SEED *seed, // IN: the seed value
|
||
TPM2B_NAME *name, // IN: the name of the object
|
||
UINT32 *counter // OUT: the iteration counter
|
||
)
|
||
{
|
||
CRYPT_RESULT retVal;
|
||
TEST_HASH(hashAlg);
|
||
TEST(ALG_ECDSA_VALUE); // ECDSA is used to verify each key
|
||
// The iteration counter has no meaning for ECC key generation. The parameter
|
||
// will be overloaded for those implementations that have a requirement for
|
||
// doing pair-wise consistency checks on signing keys. If the counter parameter
|
||
// is 0 or NULL, then no consistency check is done. If it is other than 0, then
|
||
// a consistency check is run. This modification allow this code to work with
|
||
// the existing versions of the CrytpoEngine and with FIPS-compliant versions
|
||
// as well.
|
||
*counter = (UINT32)(publicArea->objectAttributes.sign == SET);
|
||
// _cpri__GenerateKeyEcc only has one error return (CRYPT_PARAMETER) which means
|
||
// that the hash algorithm is not supported. This should not be possible
|
||
retVal = _cpri__GenerateKeyEcc(&publicArea->unique.ecc,
|
||
&sensitive->sensitive.ecc,
|
||
publicArea->parameters.eccDetail.curveID,
|
||
hashAlg, &seed->b, "ECC key by vendor",
|
||
&name->b, counter);
|
||
// This will only be useful if _cpri__GenerateKeyEcc return CRYPT_CANCEL
|
||
return TranslateCryptErrors(retVal);
|
||
}
|
||
//
|
||
//
|
||
// 10.2.6.10 CryptSignECC()
|
||
//
|
||
// This function is used for ECC signing operations. If the signing scheme is a split scheme, and the signing
|
||
// operation is successful, the commit value is retired.
|
||
//
|
||
//
|
||
// Error Returns Meaning
|
||
//
|
||
// TPM_RC_SCHEME unsupported scheme
|
||
// TPM_RC_VALUE invalid commit status (in case of a split scheme) or failed to generate
|
||
// r value.
|
||
//
|
||
static TPM_RC
|
||
CryptSignECC(
|
||
OBJECT *signKey, // IN: ECC key to sign the hash
|
||
TPMT_SIG_SCHEME *scheme, // IN: sign scheme
|
||
TPM2B_DIGEST *hashData, // IN: hash to be signed
|
||
TPMT_SIGNATURE *signature // OUT: signature
|
||
)
|
||
{
|
||
TPM2B_ECC_PARAMETER r;
|
||
TPM2B_ECC_PARAMETER *pr = NULL;
|
||
CRYPT_RESULT retVal;
|
||
// Run a test of the ECC sign and verify if it has not already been run
|
||
TEST_HASH(scheme->details.any.hashAlg);
|
||
TEST(scheme->scheme);
|
||
if(CryptIsSplitSign(scheme->scheme))
|
||
{
|
||
// When this code was written, the only split scheme was ECDAA
|
||
// (which can also be used for U-Prove).
|
||
if(!CryptGenerateR(&r,
|
||
&scheme->details.ecdaa.count,
|
||
signKey->publicArea.parameters.eccDetail.curveID,
|
||
&signKey->name))
|
||
return TPM_RC_VALUE;
|
||
pr = &r;
|
||
}
|
||
// Call crypto engine function to sign
|
||
// _cpri__SignEcc may return CRYPT_SCHEME
|
||
retVal = _cpri__SignEcc(&signature->signature.ecdsa.signatureR,
|
||
&signature->signature.ecdsa.signatureS,
|
||
scheme->scheme,
|
||
scheme->details.any.hashAlg,
|
||
signKey->publicArea.parameters.eccDetail.curveID,
|
||
&signKey->sensitive.sensitive.ecc,
|
||
&hashData->b,
|
||
pr
|
||
);
|
||
if(CryptIsSplitSign(scheme->scheme) && retVal == CRYPT_SUCCESS)
|
||
CryptEndCommit(scheme->details.ecdaa.count);
|
||
// CRYPT_SCHEME->TPM_RC_SCHEME
|
||
return TranslateCryptErrors(retVal);
|
||
}
|
||
//
|
||
//
|
||
// 10.2.6.11 CryptECCVerifySignature()
|
||
//
|
||
// This function is used to verify a signature created with an ECC key.
|
||
//
|
||
// Error Returns Meaning
|
||
//
|
||
// TPM_RC_SIGNATURE if signature is not valid
|
||
// TPM_RC_SCHEME the signing scheme or hashAlg is not supported
|
||
//
|
||
static TPM_RC
|
||
CryptECCVerifySignature(
|
||
OBJECT *signKey, // IN: ECC key signed the hash
|
||
TPM2B_DIGEST *digestData, // IN: digest being signed
|
||
TPMT_SIGNATURE *signature // IN: signature to be verified
|
||
)
|
||
{
|
||
CRYPT_RESULT retVal;
|
||
TEST_HASH(signature->signature.any.hashAlg);
|
||
TEST(signature->sigAlg);
|
||
// This implementation uses the fact that all the defined ECC signing
|
||
// schemes have the hash as the first parameter.
|
||
// _cpriValidateSignatureEcc may return CRYPT_FAIL or CRYP_SCHEME
|
||
retVal = _cpri__ValidateSignatureEcc(&signature->signature.ecdsa.signatureR,
|
||
&signature->signature.ecdsa.signatureS,
|
||
signature->sigAlg,
|
||
signature->signature.any.hashAlg,
|
||
signKey->publicArea.parameters.eccDetail.curveID,
|
||
&signKey->publicArea.unique.ecc,
|
||
&digestData->b);
|
||
if(retVal == CRYPT_FAIL)
|
||
return TPM_RC_SIGNATURE;
|
||
// CRYPT_SCHEME->TPM_RC_SCHEME
|
||
return TranslateCryptErrors(retVal);
|
||
}
|
||
//
|
||
//
|
||
// 10.2.6.12 CryptGenerateR()
|
||
//
|
||
// This function computes the commit random value for a split signing scheme.
|
||
// If c is NULL, it indicates that r is being generated for TPM2_Commit(). If c is not NULL, the TPM will
|
||
// validate that the gr.commitArray bit associated with the input value of c is SET. If not, the TPM returns
|
||
// FALSE and no r value is generated.
|
||
//
|
||
// Return Value Meaning
|
||
//
|
||
// TRUE r value computed
|
||
// FALSE no r value computed
|
||
//
|
||
BOOL
|
||
CryptGenerateR(
|
||
TPM2B_ECC_PARAMETER *r, // OUT: the generated random value
|
||
UINT16 *c, // IN/OUT: count value.
|
||
TPMI_ECC_CURVE curveID, // IN: the curve for the value
|
||
TPM2B_NAME *name // IN: optional name of a key to
|
||
// associate with 'r'
|
||
)
|
||
{
|
||
// This holds the marshaled g_commitCounter.
|
||
TPM2B_TYPE(8B, 8);
|
||
TPM2B_8B cntr = {.b.size = 8};
|
||
UINT32 iterations;
|
||
const TPM2B *n;
|
||
UINT64 currentCount = gr.commitCounter;
|
||
// This is just to suppress a compiler warning about a conditional expression
|
||
// being a constant. This is because of the macro expansion of ryptKDFa
|
||
TPMI_ALG_HASH hashAlg = CONTEXT_INTEGRITY_HASH_ALG;
|
||
n = CryptEccGetParameter('n', curveID);
|
||
pAssert(r != NULL && n != NULL);
|
||
// If this is the commit phase, use the current value of the commit counter
|
||
if(c != NULL)
|
||
//
|
||
{
|
||
UINT16 t1;
|
||
// if the array bit is not set, can't use the value.
|
||
if(!BitIsSet((*c & COMMIT_INDEX_MASK), gr.commitArray,
|
||
sizeof(gr.commitArray)))
|
||
return FALSE;
|
||
// If it is the sign phase, figure out what the counter value was
|
||
// when the commitment was made.
|
||
//
|
||
// When gr.commitArray has less than 64K bits, the extra
|
||
// bits of 'c' are used as a check to make sure that the
|
||
// signing operation is not using an out of range count value
|
||
t1 = (UINT16)currentCount;
|
||
// If the lower bits of c are greater or equal to the lower bits of t1
|
||
// then the upper bits of t1 must be one more than the upper bits
|
||
// of c
|
||
if((*c & COMMIT_INDEX_MASK) >= (t1 & COMMIT_INDEX_MASK))
|
||
// Since the counter is behind, reduce the current count
|
||
currentCount = currentCount - (COMMIT_INDEX_MASK + 1);
|
||
t1 = (UINT16)currentCount;
|
||
if((t1 & ~COMMIT_INDEX_MASK) != (*c & ~COMMIT_INDEX_MASK))
|
||
return FALSE;
|
||
// set the counter to the value that was
|
||
// present when the commitment was made
|
||
currentCount = (currentCount & 0xffffffffffff0000) | *c;
|
||
}
|
||
// Marshal the count value to a TPM2B buffer for the KDF
|
||
cntr.t.size = sizeof(currentCount);
|
||
UINT64_TO_BYTE_ARRAY(currentCount, cntr.t.buffer);
|
||
// Now can do the KDF to create the random value for the signing operation
|
||
// During the creation process, we may generate an r that does not meet the
|
||
// requirements of the random value.
|
||
// want to generate a new r.
|
||
r->t.size = n->size;
|
||
// Arbitrary upper limit on the number of times that we can look for
|
||
// a suitable random value. The normally number of tries will be 1.
|
||
for(iterations = 1; iterations < 1000000;)
|
||
{
|
||
BYTE *pr = &r->b.buffer[0];
|
||
int i;
|
||
CryptKDFa(hashAlg, &gr.commitNonce.b, "ECDAA Commit",
|
||
name, &cntr.b, n->size * 8, r->t.buffer, &iterations);
|
||
// random value must be less than the prime
|
||
if(CryptCompare(r->b.size, r->b.buffer, n->size, n->buffer) >= 0)
|
||
continue;
|
||
// in this implementation it is required that at least bit
|
||
// in the upper half of the number be set
|
||
for(i = n->size/2; i > 0; i--)
|
||
if(*pr++ != 0)
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
//
|
||
//
|
||
//
|
||
// 10.2.6.13 CryptCommit()
|
||
//
|
||
// This function is called when the count value is committed. The gr.commitArray value associated with the
|
||
// current count value is SET and g_commitCounter is incremented. The low-order 16 bits of old value of the
|
||
// counter is returned.
|
||
//
|
||
UINT16
|
||
CryptCommit(
|
||
void
|
||
)
|
||
{
|
||
UINT16 oldCount = (UINT16)gr.commitCounter;
|
||
gr.commitCounter++;
|
||
BitSet(oldCount & COMMIT_INDEX_MASK, gr.commitArray, sizeof(gr.commitArray));
|
||
return oldCount;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.6.14 CryptEndCommit()
|
||
//
|
||
// This function is called when the signing operation using the committed value is completed. It clears the
|
||
// gr.commitArray bit associated with the count value so that it can't be used again.
|
||
//
|
||
void
|
||
CryptEndCommit(
|
||
UINT16 c // IN: the counter value of the commitment
|
||
)
|
||
{
|
||
BitClear((c & COMMIT_INDEX_MASK), gr.commitArray, sizeof(gr.commitArray));
|
||
}
|
||
//
|
||
//
|
||
// 10.2.6.15 CryptCommitCompute()
|
||
//
|
||
// This function performs the computations for the TPM2_Commit() command. This could be a macro.
|
||
//
|
||
// Error Returns Meaning
|
||
//
|
||
// TPM_RC_NO_RESULT K, L, or E is the point at infinity
|
||
// TPM_RC_CANCELLED command was canceled
|
||
//
|
||
TPM_RC
|
||
CryptCommitCompute(
|
||
TPMS_ECC_POINT *K, // OUT: [d]B
|
||
TPMS_ECC_POINT *L, // OUT: [r]B
|
||
TPMS_ECC_POINT *E, // OUT: [r]M
|
||
TPM_ECC_CURVE curveID, // IN: The curve for the computation
|
||
TPMS_ECC_POINT *M, // IN: M (P1)
|
||
TPMS_ECC_POINT *B, // IN: B (x2, y2)
|
||
TPM2B_ECC_PARAMETER *d, // IN: the private scalar
|
||
TPM2B_ECC_PARAMETER *r // IN: the computed r value
|
||
)
|
||
{
|
||
TEST(ALG_ECDH_VALUE);
|
||
// CRYPT_NO_RESULT->TPM_RC_NO_RESULT CRYPT_CANCEL->TPM_RC_CANCELLED
|
||
return TranslateCryptErrors(
|
||
_cpri__EccCommitCompute(K, L , E, curveID, M, B, d, r));
|
||
}
|
||
//
|
||
//
|
||
//
|
||
// 10.2.6.16 CryptEccGetParameters()
|
||
//
|
||
// This function returns the ECC parameter details of the given curve
|
||
//
|
||
// Return Value Meaning
|
||
//
|
||
// TRUE Get parameters success
|
||
// FALSE Unsupported ECC curve ID
|
||
//
|
||
BOOL
|
||
CryptEccGetParameters(
|
||
TPM_ECC_CURVE curveId, // IN: ECC curve ID
|
||
TPMS_ALGORITHM_DETAIL_ECC *parameters // OUT: ECC parameter
|
||
)
|
||
{
|
||
const ECC_CURVE *curve = _cpri__EccGetParametersByCurveId(curveId);
|
||
const ECC_CURVE_DATA *data;
|
||
BOOL found = curve != NULL;
|
||
if(found)
|
||
{
|
||
data = curve->curveData;
|
||
parameters->curveID = curve->curveId;
|
||
// Key size in bit
|
||
parameters->keySize = curve->keySizeBits;
|
||
// KDF
|
||
parameters->kdf = curve->kdf;
|
||
// Sign
|
||
parameters->sign = curve->sign;
|
||
// Copy p value
|
||
MemoryCopy2B(¶meters->p.b, data->p, sizeof(parameters->p.t.buffer));
|
||
// Copy a value
|
||
MemoryCopy2B(¶meters->a.b, data->a, sizeof(parameters->a.t.buffer));
|
||
// Copy b value
|
||
MemoryCopy2B(¶meters->b.b, data->b, sizeof(parameters->b.t.buffer));
|
||
// Copy Gx value
|
||
MemoryCopy2B(¶meters->gX.b, data->x, sizeof(parameters->gX.t.buffer));
|
||
// Copy Gy value
|
||
MemoryCopy2B(¶meters->gY.b, data->y, sizeof(parameters->gY.t.buffer));
|
||
// Copy n value
|
||
MemoryCopy2B(¶meters->n.b, data->n, sizeof(parameters->n.t.buffer));
|
||
// Copy h value
|
||
MemoryCopy2B(¶meters->h.b, data->h, sizeof(parameters->h.t.buffer));
|
||
}
|
||
return found;
|
||
}
|
||
#if CC_ZGen_2Phase == YES
|
||
//
|
||
// CryptEcc2PhaseKeyExchange() This is the interface to the key exchange function.
|
||
//
|
||
TPM_RC
|
||
CryptEcc2PhaseKeyExchange(
|
||
TPMS_ECC_POINT *outZ1, // OUT: the computed point
|
||
TPMS_ECC_POINT *outZ2, // OUT: optional second point
|
||
TPM_ALG_ID scheme, // IN: the key exchange scheme
|
||
TPM_ECC_CURVE curveId, // IN: the curve for the computation
|
||
TPM2B_ECC_PARAMETER *dsA, // IN: static private TPM key
|
||
TPM2B_ECC_PARAMETER *deA, // IN: ephemeral private TPM key
|
||
TPMS_ECC_POINT *QsB, // IN: static public party B key
|
||
TPMS_ECC_POINT *QeB // IN: ephemeral public party B key
|
||
)
|
||
{
|
||
return (TranslateCryptErrors(_cpri__C_2_2_KeyExchange(outZ1,
|
||
outZ2,
|
||
scheme,
|
||
curveId,
|
||
dsA,
|
||
deA,
|
||
QsB,
|
||
QeB)));
|
||
}
|
||
#endif // CC_ZGen_2Phase
|
||
#endif //TPM_ALG_ECC //% 3
|
||
//
|
||
//
|
||
// 10.2.6.17 CryptIsSchemeAnonymous()
|
||
//
|
||
// This function is used to test a scheme to see if it is an anonymous scheme The only anonymous scheme
|
||
// is ECDAA. ECDAA can be used to do things like U-Prove.
|
||
//
|
||
BOOL
|
||
CryptIsSchemeAnonymous(
|
||
TPM_ALG_ID scheme // IN: the scheme algorithm to test
|
||
)
|
||
{
|
||
#ifdef TPM_ALG_ECDAA
|
||
return (scheme == TPM_ALG_ECDAA);
|
||
#else
|
||
UNREFERENCED(scheme);
|
||
return 0;
|
||
#endif
|
||
}
|
||
//
|
||
//
|
||
// 10.2.7 Symmetric Functions
|
||
//
|
||
// 10.2.7.1 ParmDecryptSym()
|
||
//
|
||
// This function performs parameter decryption using symmetric block cipher.
|
||
//
|
||
void
|
||
ParmDecryptSym(
|
||
TPM_ALG_ID symAlg, // IN: the symmetric algorithm
|
||
TPM_ALG_ID hash, // IN: hash algorithm for KDFa
|
||
UINT16 keySizeInBits, // IN: key key size in bit
|
||
TPM2B *key, // IN: KDF HMAC key
|
||
TPM2B *nonceCaller, // IN: nonce caller
|
||
TPM2B *nonceTpm, // IN: nonce TPM
|
||
UINT32 dataSize, // IN: size of parameter buffer
|
||
BYTE *data // OUT: buffer to be decrypted
|
||
)
|
||
{
|
||
// KDF output buffer
|
||
// It contains parameters for the CFB encryption
|
||
// From MSB to LSB, they are the key and iv
|
||
BYTE symParmString[MAX_SYM_KEY_BYTES + MAX_SYM_BLOCK_SIZE];
|
||
// Symmetric key size in byte
|
||
UINT16 keySize = (keySizeInBits + 7) / 8;
|
||
TPM2B_IV iv;
|
||
iv.t.size = CryptGetSymmetricBlockSize(symAlg, keySizeInBits);
|
||
// If there is decryption to do...
|
||
if(iv.t.size > 0)
|
||
{
|
||
// Generate key and iv
|
||
CryptKDFa(hash, key, "CFB", nonceCaller, nonceTpm,
|
||
keySizeInBits + (iv.t.size * 8), symParmString, NULL);
|
||
MemoryCopy(iv.t.buffer, &symParmString[keySize], iv.t.size,
|
||
sizeof(iv.t.buffer));
|
||
CryptSymmetricDecrypt(data, symAlg, keySizeInBits, TPM_ALG_CFB,
|
||
symParmString, &iv, dataSize, data);
|
||
}
|
||
return;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.7.2 ParmEncryptSym()
|
||
//
|
||
// This function performs parameter encryption using symmetric block cipher.
|
||
//
|
||
void
|
||
ParmEncryptSym(
|
||
TPM_ALG_ID symAlg, // IN: symmetric algorithm
|
||
TPM_ALG_ID hash, // IN: hash algorithm for KDFa
|
||
UINT16 keySizeInBits, // IN: AES key size in bit
|
||
TPM2B *key, // IN: KDF HMAC key
|
||
TPM2B *nonceCaller, // IN: nonce caller
|
||
TPM2B *nonceTpm, // IN: nonce TPM
|
||
UINT32 dataSize, // IN: size of parameter buffer
|
||
BYTE *data // OUT: buffer to be encrypted
|
||
)
|
||
{
|
||
// KDF output buffer
|
||
// It contains parameters for the CFB encryption
|
||
BYTE symParmString[MAX_SYM_KEY_BYTES + MAX_SYM_BLOCK_SIZE];
|
||
// Symmetric key size in bytes
|
||
UINT16 keySize = (keySizeInBits + 7) / 8;
|
||
TPM2B_IV iv;
|
||
iv.t.size = CryptGetSymmetricBlockSize(symAlg, keySizeInBits);
|
||
// See if there is any encryption to do
|
||
if(iv.t.size > 0)
|
||
{
|
||
// Generate key and iv
|
||
CryptKDFa(hash, key, "CFB", nonceTpm, nonceCaller,
|
||
keySizeInBits + (iv.t.size * 8), symParmString, NULL);
|
||
MemoryCopy(iv.t.buffer, &symParmString[keySize], iv.t.size,
|
||
sizeof(iv.t.buffer));
|
||
CryptSymmetricEncrypt(data, symAlg, keySizeInBits, TPM_ALG_CFB,
|
||
symParmString, &iv, dataSize, data);
|
||
}
|
||
return;
|
||
}
|
||
//
|
||
//
|
||
//
|
||
// 10.2.7.3 CryptGenerateNewSymmetric()
|
||
//
|
||
// This function creates the sensitive symmetric values for an HMAC or symmetric key. If the sensitive area
|
||
// is zero, then the sensitive creation key data is copied. If it is not zero, then the TPM will generate a
|
||
// random value of the selected size.
|
||
//
|
||
void
|
||
CryptGenerateNewSymmetric(
|
||
TPMS_SENSITIVE_CREATE *sensitiveCreate, // IN: sensitive creation data
|
||
TPMT_SENSITIVE *sensitive, // OUT: sensitive area
|
||
TPM_ALG_ID hashAlg, // IN: hash algorithm for the KDF
|
||
TPM2B_SEED *seed, // IN: seed used in creation
|
||
TPM2B_NAME *name // IN: name of the object
|
||
)
|
||
{
|
||
// This function is called to create a key and obfuscation value for a
|
||
// symmetric key that can either be a block cipher or an XOR key. The buffer
|
||
// in sensitive->sensitive will hold either. When we call the function
|
||
// to copy the input value or generated value to the sensitive->sensitive
|
||
// buffer we will need to have a size for the output buffer. This define
|
||
// computes the maximum that it might need to be and uses that. It will always
|
||
// be smaller than the largest value that will fit.
|
||
#define MAX_SENSITIVE_SIZE \
|
||
(MAX(sizeof(sensitive->sensitive.bits.t.buffer), \
|
||
sizeof(sensitive->sensitive.sym.t.buffer)))
|
||
// set the size of the obfuscation value
|
||
sensitive->seedValue.t.size = CryptGetHashDigestSize(hashAlg);
|
||
// If the input sensitive size is zero, then create both the sensitive data
|
||
// and the obfuscation value
|
||
if(sensitiveCreate->data.t.size == 0)
|
||
{
|
||
BYTE symValues[MAX(MAX_DIGEST_SIZE, MAX_SYM_KEY_BYTES)
|
||
+ MAX_DIGEST_SIZE];
|
||
UINT16 requestSize;
|
||
// Set the size of the request to be the size of the key and the
|
||
// obfuscation value
|
||
requestSize = sensitive->sensitive.sym.t.size
|
||
+ sensitive->seedValue.t.size;
|
||
pAssert(requestSize <= sizeof(symValues));
|
||
requestSize = _cpri__GenerateSeededRandom(requestSize, symValues, hashAlg,
|
||
&seed->b,
|
||
"symmetric sensitive", &name->b,
|
||
NULL);
|
||
pAssert(requestSize != 0);
|
||
// Copy the new key
|
||
MemoryCopy(sensitive->sensitive.sym.t.buffer,
|
||
symValues, sensitive->sensitive.sym.t.size,
|
||
MAX_SENSITIVE_SIZE);
|
||
// copy the obfuscation value
|
||
MemoryCopy(sensitive->seedValue.t.buffer,
|
||
&symValues[sensitive->sensitive.sym.t.size],
|
||
sensitive->seedValue.t.size,
|
||
sizeof(sensitive->seedValue.t.buffer));
|
||
}
|
||
else
|
||
{
|
||
// Copy input symmetric key to sensitive area as long as it will fit
|
||
MemoryCopy2B(&sensitive->sensitive.sym.b, &sensitiveCreate->data.b,
|
||
MAX_SENSITIVE_SIZE);
|
||
// Create the obfuscation value
|
||
_cpri__GenerateSeededRandom(sensitive->seedValue.t.size,
|
||
sensitive->seedValue.t.buffer,
|
||
hashAlg, &seed->b,
|
||
"symmetric obfuscation", &name->b, NULL);
|
||
}
|
||
return;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.7.4 CryptGenerateKeySymmetric()
|
||
//
|
||
// This function derives a symmetric cipher key from the provided seed.
|
||
//
|
||
// Error Returns Meaning
|
||
//
|
||
// TPM_RC_KEY_SIZE key size in the public area does not match the size in the sensitive
|
||
// creation area
|
||
// TPM_RC_VALUE the publicArea nameAlg is invalid
|
||
//
|
||
static TPM_RC
|
||
CryptGenerateKeySymmetric(
|
||
TPMT_PUBLIC *publicArea, // IN/OUT: The public area template
|
||
// for the new key.
|
||
TPMS_SENSITIVE_CREATE *sensitiveCreate, // IN: sensitive creation data
|
||
TPMT_SENSITIVE *sensitive, // OUT: sensitive area
|
||
TPM_ALG_ID hashAlg, // IN: hash algorithm for the KDF
|
||
TPM2B_SEED *seed, // IN: seed used in creation
|
||
TPM2B_NAME *name // IN: name of the object
|
||
)
|
||
{
|
||
// Check parameter values
|
||
if(publicArea->nameAlg == TPM_ALG_NULL)
|
||
{
|
||
return TPM_RC_VALUE;
|
||
}
|
||
// If this is not a new key, then the provided key data must be the right size
|
||
if(publicArea->objectAttributes.sensitiveDataOrigin == CLEAR)
|
||
{
|
||
if( (sensitiveCreate->data.t.size * 8)
|
||
!= publicArea->parameters.symDetail.sym.keyBits.sym)
|
||
return TPM_RC_KEY_SIZE;
|
||
// Make sure that the key size is OK.
|
||
// This implementation only supports symmetric key sizes that are
|
||
// multiples of 8
|
||
if(publicArea->parameters.symDetail.sym.keyBits.sym % 8 != 0)
|
||
return TPM_RC_KEY_SIZE;
|
||
}
|
||
else
|
||
{
|
||
// TPM is going to generate the key so set the size
|
||
sensitive->sensitive.sym.t.size
|
||
= publicArea->parameters.symDetail.sym.keyBits.sym / 8;
|
||
sensitiveCreate->data.t.size = 0;
|
||
}
|
||
// Fill in the sensitive area
|
||
CryptGenerateNewSymmetric(sensitiveCreate, sensitive, hashAlg,
|
||
seed, name);
|
||
// Create unique area in public
|
||
CryptComputeSymmetricUnique(publicArea->nameAlg,
|
||
sensitive, &publicArea->unique.sym);
|
||
return TPM_RC_SUCCESS;
|
||
}
|
||
//
|
||
//
|
||
//
|
||
// 10.2.7.5 CryptXORObfuscation()
|
||
//
|
||
// This function implements XOR obfuscation. It should not be called if the hash algorithm is not
|
||
// implemented. The only return value from this function is TPM_RC_SUCCESS.
|
||
//
|
||
#ifdef TPM_ALG_KEYEDHASH //% 5
|
||
void
|
||
CryptXORObfuscation(
|
||
TPM_ALG_ID hash, // IN: hash algorithm for KDF
|
||
TPM2B *key, // IN: KDF key
|
||
TPM2B *contextU, // IN: contextU
|
||
TPM2B *contextV, // IN: contextV
|
||
UINT32 dataSize, // IN: size of data buffer
|
||
BYTE *data // IN/OUT: data to be XORed in place
|
||
)
|
||
{
|
||
BYTE mask[MAX_DIGEST_SIZE]; // Allocate a digest sized buffer
|
||
BYTE *pm;
|
||
UINT32 i;
|
||
UINT32 counter = 0;
|
||
UINT16 hLen = CryptGetHashDigestSize(hash);
|
||
UINT32 requestSize = dataSize * 8;
|
||
INT32 remainBytes = (INT32) dataSize;
|
||
pAssert((key != NULL) && (data != NULL) && (hLen != 0));
|
||
// Call KDFa to generate XOR mask
|
||
for(; remainBytes > 0; remainBytes -= hLen)
|
||
{
|
||
// Make a call to KDFa to get next iteration
|
||
CryptKDFaOnce(hash, key, "XOR", contextU, contextV,
|
||
requestSize, mask, &counter);
|
||
// XOR next piece of the data
|
||
pm = mask;
|
||
for(i = hLen < remainBytes ? hLen : remainBytes; i > 0; i--)
|
||
*data++ ^= *pm++;
|
||
}
|
||
return;
|
||
}
|
||
#endif //TPM_ALG_KEYED_HASH //%5
|
||
//
|
||
//
|
||
// 10.2.8 Initialization and shut down
|
||
//
|
||
// 10.2.8.1 CryptInitUnits()
|
||
//
|
||
// This function is called when the TPM receives a _TPM_Init() indication. After function returns, the hash
|
||
// algorithms should be available.
|
||
//
|
||
// NOTE: The hash algorithms do not have to be tested, they just need to be available. They have to be tested before the
|
||
// TPM can accept HMAC authorization or return any result that relies on a hash algorithm.
|
||
//
|
||
void
|
||
CryptInitUnits(
|
||
void
|
||
)
|
||
{
|
||
// Initialize the vector of implemented algorithms
|
||
AlgorithmGetImplementedVector(&g_implementedAlgorithms);
|
||
// Indicate that all test are necessary
|
||
CryptInitializeToTest();
|
||
//
|
||
// Call crypto engine unit initialization
|
||
// It is assumed that crypt engine initialization should always succeed.
|
||
// Otherwise, TPM should go to failure mode.
|
||
if(_cpri__InitCryptoUnits(&TpmFail) != CRYPT_SUCCESS)
|
||
FAIL(FATAL_ERROR_INTERNAL);
|
||
return;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.8.2 CryptStopUnits()
|
||
//
|
||
// This function is only used in a simulated environment. There should be no reason to shut down the
|
||
// cryptography on an actual TPM other than loss of power. After receiving TPM2_Startup(), the TPM should
|
||
// be able to accept commands until it loses power and, unless the TPM is in Failure Mode, the
|
||
// cryptographic algorithms should be available.
|
||
//
|
||
void
|
||
CryptStopUnits(
|
||
void
|
||
)
|
||
{
|
||
// Call crypto engine unit stopping
|
||
_cpri__StopCryptoUnits();
|
||
return;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.8.3 CryptUtilStartup()
|
||
//
|
||
// This function is called by TPM2_Startup() to initialize the functions in this crypto library and in the
|
||
// provided CryptoEngine(). In this implementation, the only initialization required in this library is
|
||
// initialization of the Commit nonce on TPM Reset.
|
||
// This function returns false if some problem prevents the functions from starting correctly. The TPM should
|
||
// go into failure mode.
|
||
//
|
||
BOOL
|
||
CryptUtilStartup(
|
||
STARTUP_TYPE type // IN: the startup type
|
||
)
|
||
{
|
||
// Make sure that the crypto library functions are ready.
|
||
// NOTE: need to initialize the crypto before loading
|
||
// the RND state may trigger a self-test which
|
||
// uses the
|
||
if( !_cpri__Startup())
|
||
return FALSE;
|
||
// Initialize the state of the RNG.
|
||
CryptDrbgGetPutState(PUT_STATE);
|
||
if(type == SU_RESET)
|
||
{
|
||
#ifdef TPM_ALG_ECC
|
||
// Get a new random commit nonce
|
||
gr.commitNonce.t.size = sizeof(gr.commitNonce.t.buffer);
|
||
_cpri__GenerateRandom(gr.commitNonce.t.size, gr.commitNonce.t.buffer);
|
||
// Reset the counter and commit array
|
||
gr.commitCounter = 0;
|
||
MemorySet(gr.commitArray, 0, sizeof(gr.commitArray));
|
||
#endif // TPM_ALG_ECC
|
||
}
|
||
// If the shutdown was orderly, then the values recovered from NV will
|
||
// be OK to use. If the shutdown was not orderly, then a TPM Reset was required
|
||
// and we would have initialized in the code above.
|
||
return TRUE;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.9 Algorithm-Independent Functions
|
||
//
|
||
// 10.2.9.1 Introduction
|
||
//
|
||
// These functions are used generically when a function of a general type (e.g., symmetric encryption) is
|
||
// required. The functions will modify the parameters as required to interface to the indicated algorithms.
|
||
//
|
||
// 10.2.9.2 CryptIsAsymAlgorithm()
|
||
//
|
||
// This function indicates if an algorithm is an asymmetric algorithm.
|
||
//
|
||
// Return Value Meaning
|
||
//
|
||
// TRUE if it is an asymmetric algorithm
|
||
// FALSE if it is not an asymmetric algorithm
|
||
//
|
||
BOOL
|
||
CryptIsAsymAlgorithm(
|
||
TPM_ALG_ID algID // IN: algorithm ID
|
||
)
|
||
{
|
||
return (
|
||
#ifdef TPM_ALG_RSA
|
||
algID == TPM_ALG_RSA
|
||
#endif
|
||
#if defined TPM_ALG_RSA && defined TPM_ALG_ECC
|
||
||
|
||
#endif
|
||
#ifdef TPM_ALG_ECC
|
||
algID == TPM_ALG_ECC
|
||
#endif
|
||
);
|
||
}
|
||
//
|
||
//
|
||
// 10.2.9.3 CryptGetSymmetricBlockSize()
|
||
//
|
||
// This function returns the size in octets of the symmetric encryption block used by an algorithm and key
|
||
// size combination.
|
||
//
|
||
INT16
|
||
CryptGetSymmetricBlockSize(
|
||
TPMI_ALG_SYM algorithm, // IN: symmetric algorithm
|
||
UINT16 keySize // IN: key size in bit
|
||
)
|
||
{
|
||
return _cpri__GetSymmetricBlockSize(algorithm, keySize);
|
||
}
|
||
//
|
||
//
|
||
//
|
||
// 10.2.9.4 CryptSymmetricEncrypt()
|
||
//
|
||
// This function does in-place encryption of a buffer using the indicated symmetric algorithm, key, IV, and
|
||
// mode. If the symmetric algorithm and mode are not defined, the TPM will fail.
|
||
//
|
||
void
|
||
CryptSymmetricEncrypt(
|
||
BYTE *encrypted, // OUT: the encrypted data
|
||
TPM_ALG_ID algorithm, // IN: algorithm for encryption
|
||
UINT16 keySizeInBits, // IN: key size in bit
|
||
TPMI_ALG_SYM_MODE mode, // IN: symmetric encryption mode
|
||
BYTE *key, // IN: encryption key
|
||
TPM2B_IV *ivIn, // IN/OUT: Input IV and output chaining
|
||
// value for the next block
|
||
UINT32 dataSize, // IN: data size in byte
|
||
BYTE *data // IN/OUT: data buffer
|
||
)
|
||
{
|
||
TPM2B_IV defaultIv = {};
|
||
TPM2B_IV *iv = (ivIn != NULL) ? ivIn : &defaultIv;
|
||
TEST(algorithm);
|
||
pAssert(encrypted != NULL && key != NULL);
|
||
// this check can pass but the case below can fail. ALG_xx_VALUE values are
|
||
// defined for all algorithms but the TPM_ALG_xx might not be.
|
||
if(algorithm == ALG_AES_VALUE || algorithm == ALG_SM4_VALUE)
|
||
{
|
||
if(mode != TPM_ALG_ECB)
|
||
defaultIv.t.size = 16;
|
||
// A provided IV has to be the right size
|
||
pAssert(mode == TPM_ALG_ECB || iv->t.size == 16);
|
||
}
|
||
switch(algorithm)
|
||
{
|
||
#ifdef TPM_ALG_AES
|
||
case TPM_ALG_AES:
|
||
{
|
||
switch (mode)
|
||
{
|
||
case TPM_ALG_CTR:
|
||
_cpri__AESEncryptCTR(encrypted, keySizeInBits, key,
|
||
iv->t.buffer, dataSize, data);
|
||
break;
|
||
case TPM_ALG_OFB:
|
||
_cpri__AESEncryptOFB(encrypted, keySizeInBits, key,
|
||
iv->t.buffer, dataSize, data);
|
||
break;
|
||
case TPM_ALG_CBC:
|
||
_cpri__AESEncryptCBC(encrypted, keySizeInBits, key,
|
||
iv->t.buffer, dataSize, data);
|
||
break;
|
||
case TPM_ALG_CFB:
|
||
_cpri__AESEncryptCFB(encrypted, keySizeInBits, key,
|
||
iv->t.buffer, dataSize, data);
|
||
break;
|
||
case TPM_ALG_ECB:
|
||
_cpri__AESEncryptECB(encrypted, keySizeInBits, key,
|
||
dataSize, data);
|
||
break;
|
||
default:
|
||
pAssert(0);
|
||
}
|
||
}
|
||
break;
|
||
#endif
|
||
#ifdef TPM_ALG_SM4
|
||
case TPM_ALG_SM4:
|
||
{
|
||
switch (mode)
|
||
{
|
||
case TPM_ALG_CTR:
|
||
_cpri__SM4EncryptCTR(encrypted, keySizeInBits, key,
|
||
iv->t.buffer, dataSize, data);
|
||
break;
|
||
case TPM_ALG_OFB:
|
||
_cpri__SM4EncryptOFB(encrypted, keySizeInBits, key,
|
||
iv->t.buffer, dataSize, data);
|
||
break;
|
||
case TPM_ALG_CBC:
|
||
_cpri__SM4EncryptCBC(encrypted, keySizeInBits, key,
|
||
iv->t.buffer, dataSize, data);
|
||
break;
|
||
case TPM_ALG_CFB:
|
||
_cpri__SM4EncryptCFB(encrypted, keySizeInBits, key,
|
||
iv->t.buffer, dataSize, data);
|
||
break;
|
||
case TPM_ALG_ECB:
|
||
_cpri__SM4EncryptECB(encrypted, keySizeInBits, key,
|
||
dataSize, data);
|
||
break;
|
||
default:
|
||
pAssert(0);
|
||
}
|
||
}
|
||
break;
|
||
#endif
|
||
default:
|
||
pAssert(FALSE);
|
||
break;
|
||
}
|
||
return;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.9.5 CryptSymmetricDecrypt()
|
||
//
|
||
// This function does in-place decryption of a buffer using the indicated symmetric algorithm, key, IV, and
|
||
// mode. If the symmetric algorithm and mode are not defined, the TPM will fail.
|
||
//
|
||
void
|
||
CryptSymmetricDecrypt(
|
||
BYTE *decrypted,
|
||
TPM_ALG_ID algorithm, // IN: algorithm for encryption
|
||
UINT16 keySizeInBits, // IN: key size in bit
|
||
TPMI_ALG_SYM_MODE mode, // IN: symmetric encryption mode
|
||
BYTE *key, // IN: encryption key
|
||
TPM2B_IV *ivIn, // IN/OUT: IV for next block
|
||
UINT32 dataSize, // IN: data size in byte
|
||
BYTE *data // IN/OUT: data buffer
|
||
)
|
||
{
|
||
BYTE *iv = NULL;
|
||
BYTE defaultIV[sizeof(TPMT_HA)];
|
||
TEST(algorithm);
|
||
if(
|
||
#ifdef TPM_ALG_AES
|
||
algorithm == TPM_ALG_AES
|
||
#endif
|
||
#if defined TPM_ALG_AES && defined TPM_ALG_SM4
|
||
||
|
||
#endif
|
||
#ifdef TPM_ALG_SM4
|
||
algorithm == TPM_ALG_SM4
|
||
#endif
|
||
)
|
||
{
|
||
// Both SM4 and AES have block size of 128 bits
|
||
// If the iv is not provided, create a default of 0
|
||
if(ivIn == NULL)
|
||
{
|
||
// Initialize the default IV
|
||
iv = defaultIV;
|
||
MemorySet(defaultIV, 0, 16);
|
||
}
|
||
else
|
||
{
|
||
// A provided IV has to be the right size
|
||
pAssert(mode == TPM_ALG_ECB || ivIn->t.size == 16);
|
||
iv = &(ivIn->t.buffer[0]);
|
||
}
|
||
}
|
||
switch(algorithm)
|
||
{
|
||
#ifdef TPM_ALG_AES
|
||
case TPM_ALG_AES:
|
||
{
|
||
switch (mode)
|
||
{
|
||
case TPM_ALG_CTR:
|
||
_cpri__AESDecryptCTR(decrypted, keySizeInBits, key, iv,
|
||
dataSize, data);
|
||
break;
|
||
case TPM_ALG_OFB:
|
||
_cpri__AESDecryptOFB(decrypted, keySizeInBits, key, iv,
|
||
dataSize, data);
|
||
break;
|
||
case TPM_ALG_CBC:
|
||
_cpri__AESDecryptCBC(decrypted, keySizeInBits, key, iv,
|
||
dataSize, data);
|
||
break;
|
||
case TPM_ALG_CFB:
|
||
_cpri__AESDecryptCFB(decrypted, keySizeInBits, key, iv,
|
||
dataSize, data);
|
||
break;
|
||
case TPM_ALG_ECB:
|
||
_cpri__AESDecryptECB(decrypted, keySizeInBits, key,
|
||
dataSize, data);
|
||
break;
|
||
default:
|
||
pAssert(0);
|
||
}
|
||
break;
|
||
}
|
||
#endif //TPM_ALG_AES
|
||
#ifdef TPM_ALG_SM4
|
||
case TPM_ALG_SM4 :
|
||
switch (mode)
|
||
{
|
||
case TPM_ALG_CTR:
|
||
_cpri__SM4DecryptCTR(decrypted, keySizeInBits, key, iv,
|
||
dataSize, data);
|
||
break;
|
||
case TPM_ALG_OFB:
|
||
_cpri__SM4DecryptOFB(decrypted, keySizeInBits, key, iv,
|
||
dataSize, data);
|
||
break;
|
||
case TPM_ALG_CBC:
|
||
_cpri__SM4DecryptCBC(decrypted, keySizeInBits, key, iv,
|
||
dataSize, data);
|
||
break;
|
||
case TPM_ALG_CFB:
|
||
_cpri__SM4DecryptCFB(decrypted, keySizeInBits, key, iv,
|
||
dataSize, data);
|
||
break;
|
||
case TPM_ALG_ECB:
|
||
_cpri__SM4DecryptECB(decrypted, keySizeInBits, key,
|
||
dataSize, data);
|
||
break;
|
||
default:
|
||
pAssert(0);
|
||
}
|
||
break;
|
||
#endif //TPM_ALG_SM4
|
||
default:
|
||
pAssert(FALSE);
|
||
break;
|
||
}
|
||
return;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.9.6 CryptSecretEncrypt()
|
||
//
|
||
// This function creates a secret value and its associated secret structure using an asymmetric algorithm.
|
||
// This function is used by TPM2_Rewrap() TPM2_MakeCredential(), and TPM2_Duplicate().
|
||
//
|
||
// Error Returns Meaning
|
||
//
|
||
// TPM_RC_ATTRIBUTES keyHandle does not reference a valid decryption key
|
||
// TPM_RC_KEY invalid ECC key (public point is not on the curve)
|
||
// TPM_RC_SCHEME RSA key with an unsupported padding scheme
|
||
// TPM_RC_VALUE numeric value of the data to be decrypted is greater than the RSA
|
||
// key modulus
|
||
//
|
||
TPM_RC
|
||
CryptSecretEncrypt(
|
||
TPMI_DH_OBJECT keyHandle, // IN: encryption key handle
|
||
const char *label, // IN: a null-terminated string as L
|
||
TPM2B_DATA *data, // OUT: secret value
|
||
TPM2B_ENCRYPTED_SECRET *secret // OUT: secret structure
|
||
)
|
||
{
|
||
TPM_RC result = TPM_RC_SUCCESS;
|
||
OBJECT *encryptKey = ObjectGet(keyHandle); // TPM key used for encrypt
|
||
pAssert(data != NULL && secret != NULL);
|
||
// The output secret value has the size of the digest produced by the nameAlg.
|
||
data->t.size = CryptGetHashDigestSize(encryptKey->publicArea.nameAlg);
|
||
pAssert(encryptKey->publicArea.objectAttributes.decrypt == SET);
|
||
switch(encryptKey->publicArea.type)
|
||
{
|
||
#ifdef TPM_ALG_RSA
|
||
case TPM_ALG_RSA:
|
||
{
|
||
TPMT_RSA_DECRYPT scheme;
|
||
// Use OAEP scheme
|
||
scheme.scheme = TPM_ALG_OAEP;
|
||
scheme.details.oaep.hashAlg = encryptKey->publicArea.nameAlg;
|
||
// Create secret data from RNG
|
||
CryptGenerateRandom(data->t.size, data->t.buffer);
|
||
// Encrypt the data by RSA OAEP into encrypted secret
|
||
result = CryptEncryptRSA(&secret->t.size, secret->t.secret,
|
||
encryptKey, &scheme,
|
||
data->t.size, data->t.buffer, label);
|
||
}
|
||
break;
|
||
#endif //TPM_ALG_RSA
|
||
#ifdef TPM_ALG_ECC
|
||
case TPM_ALG_ECC:
|
||
{
|
||
TPMS_ECC_POINT eccPublic;
|
||
TPM2B_ECC_PARAMETER eccPrivate;
|
||
TPMS_ECC_POINT eccSecret;
|
||
BYTE *buffer = secret->t.secret;
|
||
INT32 bufferSize = sizeof(TPMS_ECC_POINT);
|
||
// Need to make sure that the public point of the key is on the
|
||
// curve defined by the key.
|
||
if(!_cpri__EccIsPointOnCurve(
|
||
encryptKey->publicArea.parameters.eccDetail.curveID,
|
||
&encryptKey->publicArea.unique.ecc))
|
||
result = TPM_RC_KEY;
|
||
else
|
||
{
|
||
// Call crypto engine to create an auxiliary ECC key
|
||
// We assume crypt engine initialization should always success.
|
||
// Otherwise, TPM should go to failure mode.
|
||
CryptNewEccKey(encryptKey->publicArea.parameters.eccDetail.curveID,
|
||
&eccPublic, &eccPrivate);
|
||
// Marshal ECC public to secret structure. This will be used by the
|
||
// recipient to decrypt the secret with their private key.
|
||
secret->t.size = TPMS_ECC_POINT_Marshal(&eccPublic, &buffer, &bufferSize);
|
||
// Compute ECDH shared secret which is R = [d]Q where d is the
|
||
// private part of the ephemeral key and Q is the public part of a
|
||
// TPM key. TPM_RC_KEY error return from CryptComputeECDHSecret
|
||
// because the auxiliary ECC key is just created according to the
|
||
// parameters of input ECC encrypt key.
|
||
if( CryptEccPointMultiply(&eccSecret,
|
||
encryptKey->publicArea.parameters.eccDetail.curveID,
|
||
&eccPrivate,
|
||
&encryptKey->publicArea.unique.ecc)
|
||
!= CRYPT_SUCCESS)
|
||
result = TPM_RC_KEY;
|
||
else
|
||
// The secret value is computed from Z using KDFe as:
|
||
// secret := KDFe(HashID, Z, Use, PartyUInfo, PartyVInfo, bits)
|
||
// Where:
|
||
// HashID the nameAlg of the decrypt key
|
||
// Z the x coordinate (Px) of the product (P) of the point
|
||
// (Q) of the secret and the private x coordinate (de,V)
|
||
// of the decryption key
|
||
// Use a null-terminated string containing "SECRET"
|
||
// PartyUInfo the x coordinate of the point in the secret
|
||
// (Qe,U )
|
||
// PartyVInfo the x coordinate of the public key (Qs,V )
|
||
// bits the number of bits in the digest of HashID
|
||
// Retrieve seed from KDFe
|
||
CryptKDFe(encryptKey->publicArea.nameAlg, &eccSecret.x.b,
|
||
label, &eccPublic.x.b,
|
||
&encryptKey->publicArea.unique.ecc.x.b,
|
||
data->t.size * 8, data->t.buffer);
|
||
}
|
||
}
|
||
break;
|
||
#endif //TPM_ALG_ECC
|
||
default:
|
||
FAIL(FATAL_ERROR_INTERNAL);
|
||
break;
|
||
}
|
||
return result;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.9.7 CryptSecretDecrypt()
|
||
//
|
||
// Decrypt a secret value by asymmetric (or symmetric) algorithm This function is used for
|
||
// ActivateCredential() and Import for asymmetric decryption, and StartAuthSession() for both asymmetric
|
||
// and symmetric decryption process
|
||
//
|
||
// Error Returns Meaning
|
||
//
|
||
// TPM_RC_ATTRIBUTES RSA key is not a decryption key
|
||
// TPM_RC_BINDING Invalid RSA key (public and private parts are not cryptographically
|
||
// bound.
|
||
// TPM_RC_ECC_POINT ECC point in the secret is not on the curve
|
||
// TPM_RC_INSUFFICIENT failed to retrieve ECC point from the secret
|
||
// TPM_RC_NO_RESULT multiplication resulted in ECC point at infinity
|
||
// TPM_RC_SIZE data to decrypt is not of the same size as RSA key
|
||
// TPM_RC_VALUE For RSA key, numeric value of the encrypted data is greater than the
|
||
// modulus, or the recovered data is larger than the output buffer. For
|
||
// keyedHash or symmetric key, the secret is larger than the size of the
|
||
// digest produced by the name algorithm.
|
||
// TPM_RC_FAILURE internal error
|
||
//
|
||
TPM_RC
|
||
CryptSecretDecrypt(
|
||
TPM_HANDLE tpmKey, // IN: decrypt key
|
||
TPM2B_NONCE *nonceCaller, // IN: nonceCaller. It is needed for
|
||
// symmetric decryption. For
|
||
// asymmetric decryption, this
|
||
// parameter is NULL
|
||
const char *label, // IN: a null-terminated string as L
|
||
TPM2B_ENCRYPTED_SECRET *secret, // IN: input secret
|
||
TPM2B_DATA *data // OUT: decrypted secret value
|
||
)
|
||
{
|
||
TPM_RC result = TPM_RC_SUCCESS;
|
||
OBJECT *decryptKey = ObjectGet(tpmKey); //TPM key used for decrypting
|
||
// Decryption for secret
|
||
switch(decryptKey->publicArea.type)
|
||
{
|
||
#ifdef TPM_ALG_RSA
|
||
case TPM_ALG_RSA:
|
||
{
|
||
TPMT_RSA_DECRYPT scheme;
|
||
// Use OAEP scheme
|
||
scheme.scheme = TPM_ALG_OAEP;
|
||
scheme.details.oaep.hashAlg = decryptKey->publicArea.nameAlg;
|
||
// Set the output buffer capacity
|
||
data->t.size = sizeof(data->t.buffer);
|
||
// Decrypt seed by RSA OAEP
|
||
result = CryptDecryptRSA(&data->t.size, data->t.buffer, decryptKey,
|
||
&scheme,
|
||
secret->t.size, secret->t.secret,label);
|
||
if( (result == TPM_RC_SUCCESS)
|
||
&& (data->t.size
|
||
> CryptGetHashDigestSize(decryptKey->publicArea.nameAlg)))
|
||
result = TPM_RC_VALUE;
|
||
}
|
||
break;
|
||
#endif //TPM_ALG_RSA
|
||
#ifdef TPM_ALG_ECC
|
||
case TPM_ALG_ECC:
|
||
{
|
||
TPMS_ECC_POINT eccPublic;
|
||
TPMS_ECC_POINT eccSecret;
|
||
BYTE *buffer = secret->t.secret;
|
||
INT32 size = secret->t.size;
|
||
// Retrieve ECC point from secret buffer
|
||
result = TPMS_ECC_POINT_Unmarshal(&eccPublic, &buffer, &size);
|
||
if(result == TPM_RC_SUCCESS)
|
||
{
|
||
result = CryptEccPointMultiply(&eccSecret,
|
||
decryptKey->publicArea.parameters.eccDetail.curveID,
|
||
&decryptKey->sensitive.sensitive.ecc,
|
||
&eccPublic);
|
||
if(result == TPM_RC_SUCCESS)
|
||
{
|
||
// Set the size of the "recovered" secret value to be the size
|
||
// of the digest produced by the nameAlg.
|
||
data->t.size =
|
||
CryptGetHashDigestSize(decryptKey->publicArea.nameAlg);
|
||
// The secret value is computed from Z using KDFe as:
|
||
// secret := KDFe(HashID, Z, Use, PartyUInfo, PartyVInfo, bits)
|
||
// Where:
|
||
// HashID -- the nameAlg of the decrypt key
|
||
// Z -- the x coordinate (Px) of the product (P) of the point
|
||
// (Q) of the secret and the private x coordinate (de,V)
|
||
// of the decryption key
|
||
// Use -- a null-terminated string containing "SECRET"
|
||
// PartyUInfo -- the x coordinate of the point in the secret
|
||
// (Qe,U )
|
||
// PartyVInfo -- the x coordinate of the public key (Qs,V )
|
||
// bits -- the number of bits in the digest of HashID
|
||
// Retrieve seed from KDFe
|
||
CryptKDFe(decryptKey->publicArea.nameAlg, &eccSecret.x.b, label,
|
||
&eccPublic.x.b,
|
||
&decryptKey->publicArea.unique.ecc.x.b,
|
||
data->t.size * 8, data->t.buffer);
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
#endif //TPM_ALG_ECC
|
||
case TPM_ALG_KEYEDHASH:
|
||
// The seed size can not be bigger than the digest size of nameAlg
|
||
if(secret->t.size >
|
||
CryptGetHashDigestSize(decryptKey->publicArea.nameAlg))
|
||
result = TPM_RC_VALUE;
|
||
else
|
||
{
|
||
// Retrieve seed by XOR Obfuscation:
|
||
// seed = XOR(secret, hash, key, nonceCaller, nullNonce)
|
||
// where:
|
||
// secret the secret parameter from the TPM2_StartAuthHMAC
|
||
// command
|
||
// which contains the seed value
|
||
// hash nameAlg of tpmKey
|
||
// key the key or data value in the object referenced by
|
||
// entityHandle in the TPM2_StartAuthHMAC command
|
||
// nonceCaller the parameter from the TPM2_StartAuthHMAC command
|
||
// nullNonce a zero-length nonce
|
||
// XOR Obfuscation in place
|
||
CryptXORObfuscation(decryptKey->publicArea.nameAlg,
|
||
&decryptKey->sensitive.sensitive.bits.b,
|
||
&nonceCaller->b, NULL,
|
||
secret->t.size, secret->t.secret);
|
||
// Copy decrypted seed
|
||
MemoryCopy2B(&data->b, &secret->b, sizeof(data->t.buffer));
|
||
}
|
||
break;
|
||
case TPM_ALG_SYMCIPHER:
|
||
{
|
||
TPM2B_IV iv = {};
|
||
TPMT_SYM_DEF_OBJECT *symDef;
|
||
// The seed size can not be bigger than the digest size of nameAlg
|
||
if(secret->t.size >
|
||
CryptGetHashDigestSize(decryptKey->publicArea.nameAlg))
|
||
result = TPM_RC_VALUE;
|
||
else
|
||
{
|
||
symDef = &decryptKey->publicArea.parameters.symDetail.sym;
|
||
iv.t.size = CryptGetSymmetricBlockSize(symDef->algorithm,
|
||
symDef->keyBits.sym);
|
||
pAssert(iv.t.size != 0);
|
||
if(nonceCaller->t.size >= iv.t.size)
|
||
MemoryCopy(iv.t.buffer, nonceCaller->t.buffer, iv.t.size,
|
||
sizeof(iv.t.buffer));
|
||
else
|
||
MemoryCopy(iv.b.buffer, nonceCaller->t.buffer,
|
||
nonceCaller->t.size, sizeof(iv.t.buffer));
|
||
// CFB decrypt in place, using nonceCaller as iv
|
||
CryptSymmetricDecrypt(secret->t.secret, symDef->algorithm,
|
||
symDef->keyBits.sym, TPM_ALG_CFB,
|
||
decryptKey->sensitive.sensitive.sym.t.buffer,
|
||
&iv, secret->t.size, secret->t.secret);
|
||
// Copy decrypted seed
|
||
MemoryCopy2B(&data->b, &secret->b, sizeof(data->t.buffer));
|
||
}
|
||
}
|
||
break;
|
||
default:
|
||
pAssert(0);
|
||
break;
|
||
}
|
||
return result;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.9.8 CryptParameterEncryption()
|
||
//
|
||
// This function does in-place encryption of a response parameter.
|
||
//
|
||
void
|
||
CryptParameterEncryption(
|
||
TPM_HANDLE handle, // IN: encrypt session handle
|
||
TPM2B *nonceCaller, // IN: nonce caller
|
||
UINT16 leadingSizeInByte, // IN: the size of the leading size field in
|
||
// byte
|
||
TPM2B_AUTH *extraKey, // IN: additional key material other than
|
||
// session auth
|
||
BYTE *buffer // IN/OUT: parameter buffer to be encrypted
|
||
)
|
||
{
|
||
SESSION *session = SessionGet(handle); // encrypt session
|
||
TPM2B_TYPE(SYM_KEY, ( sizeof(extraKey->t.buffer)
|
||
+ sizeof(session->sessionKey.t.buffer)));
|
||
TPM2B_SYM_KEY key; // encryption key
|
||
UINT32 cipherSize = 0; // size of cipher text
|
||
pAssert(session->sessionKey.t.size + extraKey->t.size <= sizeof(key.t.buffer));
|
||
// Retrieve encrypted data size.
|
||
if(leadingSizeInByte == 2)
|
||
{
|
||
// Extract the first two bytes as the size field as the data size
|
||
// encrypt
|
||
cipherSize = (UINT32)BYTE_ARRAY_TO_UINT16(buffer);
|
||
// advance the buffer
|
||
buffer = &buffer[2];
|
||
}
|
||
#ifdef TPM4B
|
||
else if(leadingSizeInByte == 4)
|
||
{
|
||
// use the first four bytes to indicate the number of bytes to encrypt
|
||
cipherSize = BYTE_ARRAY_TO_UINT32(buffer);
|
||
//advance pointer
|
||
buffer = &buffer[4];
|
||
}
|
||
#endif
|
||
else
|
||
{
|
||
pAssert(FALSE);
|
||
}
|
||
//
|
||
// Compute encryption key by concatenating sessionAuth with extra key
|
||
MemoryCopy2B(&key.b, &session->sessionKey.b, sizeof(key.t.buffer));
|
||
MemoryConcat2B(&key.b, &extraKey->b, sizeof(key.t.buffer));
|
||
if (session->symmetric.algorithm == TPM_ALG_XOR)
|
||
// XOR parameter encryption formulation:
|
||
// XOR(parameter, hash, sessionAuth, nonceNewer, nonceOlder)
|
||
CryptXORObfuscation(session->authHashAlg, &(key.b),
|
||
&(session->nonceTPM.b),
|
||
nonceCaller, cipherSize, buffer);
|
||
else
|
||
ParmEncryptSym(session->symmetric.algorithm, session->authHashAlg,
|
||
session->symmetric.keyBits.aes, &(key.b),
|
||
nonceCaller, &(session->nonceTPM.b),
|
||
cipherSize, buffer);
|
||
return;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.9.9 CryptParameterDecryption()
|
||
//
|
||
// This function does in-place decryption of a command parameter.
|
||
//
|
||
// Error Returns Meaning
|
||
//
|
||
// TPM_RC_SIZE The number of bytes in the input buffer is less than the number of
|
||
// bytes to be decrypted.
|
||
//
|
||
TPM_RC
|
||
CryptParameterDecryption(
|
||
TPM_HANDLE handle, // IN: encrypted session handle
|
||
TPM2B *nonceCaller, // IN: nonce caller
|
||
UINT32 bufferSize, // IN: size of parameter buffer
|
||
UINT16 leadingSizeInByte, // IN: the size of the leading size field in
|
||
// byte
|
||
TPM2B_AUTH *extraKey, // IN: the authValue
|
||
BYTE *buffer // IN/OUT: parameter buffer to be decrypted
|
||
)
|
||
{
|
||
SESSION *session = SessionGet(handle); // encrypt session
|
||
// The HMAC key is going to be the concatenation of the session key and any
|
||
// additional key material (like the authValue). The size of both of these
|
||
// is the size of the buffer which can contain a TPMT_HA.
|
||
TPM2B_TYPE(HMAC_KEY, ( sizeof(extraKey->t.buffer)
|
||
+ sizeof(session->sessionKey.t.buffer)));
|
||
TPM2B_HMAC_KEY key; // decryption key
|
||
UINT32 cipherSize = 0; // size of cipher text
|
||
pAssert(session->sessionKey.t.size + extraKey->t.size <= sizeof(key.t.buffer));
|
||
// Retrieve encrypted data size.
|
||
if(leadingSizeInByte == 2)
|
||
{
|
||
// The first two bytes of the buffer are the size of the
|
||
// data to be decrypted
|
||
cipherSize = (UINT32)BYTE_ARRAY_TO_UINT16(buffer);
|
||
buffer = &buffer[2]; // advance the buffer
|
||
}
|
||
#ifdef TPM4B
|
||
else if(leadingSizeInByte == 4)
|
||
{
|
||
// the leading size is four bytes so get the four byte size field
|
||
cipherSize = BYTE_ARRAY_TO_UINT32(buffer);
|
||
buffer = &buffer[4]; //advance pointer
|
||
}
|
||
#endif
|
||
else
|
||
{
|
||
pAssert(FALSE);
|
||
}
|
||
if(cipherSize > bufferSize)
|
||
return TPM_RC_SIZE;
|
||
// Compute decryption key by concatenating sessionAuth with extra input key
|
||
MemoryCopy2B(&key.b, &session->sessionKey.b, sizeof(key.t.buffer));
|
||
MemoryConcat2B(&key.b, &extraKey->b, sizeof(key.t.buffer));
|
||
if(session->symmetric.algorithm == TPM_ALG_XOR)
|
||
// XOR parameter decryption formulation:
|
||
// XOR(parameter, hash, sessionAuth, nonceNewer, nonceOlder)
|
||
// Call XOR obfuscation function
|
||
CryptXORObfuscation(session->authHashAlg, &key.b, nonceCaller,
|
||
&(session->nonceTPM.b), cipherSize, buffer);
|
||
else
|
||
// Assume that it is one of the symmetric block ciphers.
|
||
ParmDecryptSym(session->symmetric.algorithm, session->authHashAlg,
|
||
session->symmetric.keyBits.sym,
|
||
&key.b, nonceCaller, &session->nonceTPM.b,
|
||
cipherSize, buffer);
|
||
return TPM_RC_SUCCESS;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.9.10 CryptComputeSymmetricUnique()
|
||
//
|
||
// This function computes the unique field in public area for symmetric objects.
|
||
//
|
||
void
|
||
CryptComputeSymmetricUnique(
|
||
TPMI_ALG_HASH nameAlg, // IN: object name algorithm
|
||
TPMT_SENSITIVE *sensitive, // IN: sensitive area
|
||
TPM2B_DIGEST *unique // OUT: unique buffer
|
||
)
|
||
{
|
||
HASH_STATE hashState;
|
||
pAssert(sensitive != NULL && unique != NULL);
|
||
// Compute the public value as the hash of sensitive.symkey || unique.buffer
|
||
unique->t.size = CryptGetHashDigestSize(nameAlg);
|
||
CryptStartHash(nameAlg, &hashState);
|
||
// Add obfuscation value
|
||
CryptUpdateDigest2B(&hashState, &sensitive->seedValue.b);
|
||
// Add sensitive value
|
||
CryptUpdateDigest2B(&hashState, &sensitive->sensitive.any.b);
|
||
CryptCompleteHash2B(&hashState, &unique->b);
|
||
return;
|
||
}
|
||
#if 0 //%
|
||
//
|
||
//
|
||
//
|
||
// 10.2.9.11 CryptComputeSymValue()
|
||
//
|
||
// This function computes the seedValue field in asymmetric sensitive areas.
|
||
//
|
||
void
|
||
CryptComputeSymValue(
|
||
TPM_HANDLE parentHandle, // IN: parent handle of the object to be created
|
||
TPMT_PUBLIC *publicArea, // IN/OUT: the public area template
|
||
TPMT_SENSITIVE *sensitive, // IN: sensitive area
|
||
TPM2B_SEED *seed, // IN: the seed
|
||
TPMI_ALG_HASH hashAlg, // IN: hash algorithm for KDFa
|
||
TPM2B_NAME *name // IN: object name
|
||
)
|
||
{
|
||
TPM2B_AUTH *proof = NULL;
|
||
if(CryptIsAsymAlgorithm(publicArea->type))
|
||
{
|
||
// Generate seedValue only when an asymmetric key is a storage key
|
||
if(publicArea->objectAttributes.decrypt == SET
|
||
&& publicArea->objectAttributes.restricted == SET)
|
||
{
|
||
// If this is a primary object in the endorsement hierarchy, use
|
||
// ehProof in the creation of the symmetric seed so that child
|
||
// objects in the endorsement hierarchy are voided on TPM2_Clear()
|
||
// or TPM2_ChangeEPS()
|
||
if( parentHandle == TPM_RH_ENDORSEMENT
|
||
&& publicArea->objectAttributes.fixedTPM == SET)
|
||
proof = &gp.ehProof;
|
||
}
|
||
else
|
||
{
|
||
sensitive->seedValue.t.size = 0;
|
||
return;
|
||
}
|
||
}
|
||
// For all object types, the size of seedValue is the digest size of nameAlg
|
||
sensitive->seedValue.t.size = CryptGetHashDigestSize(publicArea->nameAlg);
|
||
// Compute seedValue using implementation-dependent method
|
||
_cpri__GenerateSeededRandom(sensitive->seedValue.t.size,
|
||
sensitive->seedValue.t.buffer,
|
||
hashAlg,
|
||
&seed->b,
|
||
"seedValue",
|
||
&name->b,
|
||
(TPM2B *)proof);
|
||
return;
|
||
}
|
||
#endif //%
|
||
//
|
||
//
|
||
// 10.2.9.12 CryptCreateObject()
|
||
//
|
||
// This function creates an object. It:
|
||
// a) fills in the created key in public and sensitive area;
|
||
// b) creates a random number in sensitive area for symmetric keys; and
|
||
// c) compute the unique id in public area for symmetric keys.
|
||
//
|
||
//
|
||
//
|
||
//
|
||
// Error Returns Meaning
|
||
//
|
||
// TPM_RC_KEY_SIZE key size in the public area does not match the size in the sensitive
|
||
// creation area for a symmetric key
|
||
// TPM_RC_RANGE for an RSA key, the exponent is not supported
|
||
// TPM_RC_SIZE sensitive data size is larger than allowed for the scheme for a keyed
|
||
// hash object
|
||
// TPM_RC_VALUE exponent is not prime or could not find a prime using the provided
|
||
// parameters for an RSA key; unsupported name algorithm for an ECC
|
||
// key; unsupported name algorithm for symmetric algorithms
|
||
//
|
||
TPM_RC
|
||
CryptCreateObject(
|
||
TPM_HANDLE parentHandle, // IN/OUT: indication of the seed
|
||
// source
|
||
TPMT_PUBLIC *publicArea, // IN/OUT: public area
|
||
TPMS_SENSITIVE_CREATE *sensitiveCreate, // IN: sensitive creation
|
||
TPMT_SENSITIVE *sensitive // OUT: sensitive area
|
||
)
|
||
{
|
||
// Next value is a placeholder for a random seed that is used in
|
||
// key creation when the parent is not a primary seed. It has the same
|
||
// size as the primary seed.
|
||
TPM2B_SEED localSeed; // data to seed key creation if this
|
||
// is not a primary seed
|
||
TPM2B_SEED *seed = NULL;
|
||
TPM_RC result = TPM_RC_SUCCESS;
|
||
TPM2B_NAME name;
|
||
TPM_ALG_ID hashAlg = CONTEXT_INTEGRITY_HASH_ALG;
|
||
OBJECT *parent;
|
||
UINT32 counter;
|
||
// Set the sensitive type for the object
|
||
sensitive->sensitiveType = publicArea->type;
|
||
ObjectComputeName(publicArea, &name);
|
||
// For all objects, copy the initial auth data
|
||
sensitive->authValue = sensitiveCreate->userAuth;
|
||
// If this is a permanent handle assume that it is a hierarchy
|
||
if(HandleGetType(parentHandle) == TPM_HT_PERMANENT)
|
||
{
|
||
seed = HierarchyGetPrimarySeed(parentHandle);
|
||
}
|
||
else
|
||
{
|
||
// If not hierarchy handle, get parent
|
||
parent = ObjectGet(parentHandle);
|
||
hashAlg = parent->publicArea.nameAlg;
|
||
// Use random value as seed for non-primary objects
|
||
localSeed.t.size = PRIMARY_SEED_SIZE;
|
||
CryptGenerateRandom(PRIMARY_SEED_SIZE, localSeed.t.buffer);
|
||
seed = &localSeed;
|
||
}
|
||
switch(publicArea->type)
|
||
{
|
||
#ifdef TPM_ALG_RSA
|
||
// Create RSA key
|
||
case TPM_ALG_RSA:
|
||
result = CryptGenerateKeyRSA(publicArea, sensitive,
|
||
hashAlg, seed, &name, &counter);
|
||
break;
|
||
#endif // TPM_ALG_RSA
|
||
#ifdef TPM_ALG_ECC
|
||
// Create ECC key
|
||
case TPM_ALG_ECC:
|
||
result = CryptGenerateKeyECC(publicArea, sensitive,
|
||
hashAlg, seed, &name, &counter);
|
||
break;
|
||
#endif // TPM_ALG_ECC
|
||
// Collect symmetric key information
|
||
case TPM_ALG_SYMCIPHER:
|
||
return CryptGenerateKeySymmetric(publicArea, sensitiveCreate,
|
||
sensitive, hashAlg, seed, &name);
|
||
break;
|
||
case TPM_ALG_KEYEDHASH:
|
||
return CryptGenerateKeyedHash(publicArea, sensitiveCreate,
|
||
sensitive, hashAlg, seed, &name);
|
||
break;
|
||
default:
|
||
pAssert(0);
|
||
break;
|
||
}
|
||
if(result == TPM_RC_SUCCESS)
|
||
{
|
||
TPM2B_AUTH *proof = NULL;
|
||
if(publicArea->objectAttributes.decrypt == SET
|
||
&& publicArea->objectAttributes.restricted == SET)
|
||
{
|
||
// If this is a primary object in the endorsement hierarchy, use
|
||
// ehProof in the creation of the symmetric seed so that child
|
||
// objects in the endorsement hierarchy are voided on TPM2_Clear()
|
||
// or TPM2_ChangeEPS()
|
||
if( parentHandle == TPM_RH_ENDORSEMENT
|
||
&& publicArea->objectAttributes.fixedTPM == SET)
|
||
proof = &gp.ehProof;
|
||
// For all object types, the size of seedValue is the digest size
|
||
// of its nameAlg
|
||
sensitive->seedValue.t.size
|
||
= CryptGetHashDigestSize(publicArea->nameAlg);
|
||
// Compute seedValue using implementation-dependent method
|
||
_cpri__GenerateSeededRandom(sensitive->seedValue.t.size,
|
||
sensitive->seedValue.t.buffer,
|
||
hashAlg,
|
||
&seed->b,
|
||
"seedValuea",
|
||
&name.b,
|
||
(TPM2B *)proof);
|
||
}
|
||
else
|
||
{
|
||
sensitive->seedValue.t.size = 0;
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
//
|
||
// 10.2.9.13 CryptObjectIsPublicConsistent()
|
||
//
|
||
// This function checks that the key sizes in the public area are consistent. For an asymmetric key, the size
|
||
// of the public key must match the size indicated by the public->parameters.
|
||
// Checks for the algorithm types matching the key type are handled by the unmarshaling operation.
|
||
//
|
||
// Return Value Meaning
|
||
//
|
||
// TRUE sizes are consistent
|
||
// FALSE sizes are not consistent
|
||
//
|
||
BOOL
|
||
CryptObjectIsPublicConsistent(
|
||
TPMT_PUBLIC *publicArea // IN: public area
|
||
)
|
||
{
|
||
BOOL OK = TRUE;
|
||
switch (publicArea->type)
|
||
{
|
||
#ifdef TPM_ALG_RSA
|
||
case TPM_ALG_RSA:
|
||
OK = CryptAreKeySizesConsistent(publicArea);
|
||
break;
|
||
#endif //TPM_ALG_RSA
|
||
#ifdef TPM_ALG_ECC
|
||
case TPM_ALG_ECC:
|
||
{
|
||
const ECC_CURVE *curveValue;
|
||
// Check that the public point is on the indicated curve.
|
||
OK = CryptEccIsPointOnCurve(
|
||
publicArea->parameters.eccDetail.curveID,
|
||
&publicArea->unique.ecc);
|
||
if(OK)
|
||
{
|
||
curveValue = CryptEccGetCurveDataPointer(
|
||
publicArea->parameters.eccDetail.curveID);
|
||
pAssert(curveValue != NULL);
|
||
// The input ECC curve must be a supported curve
|
||
// IF a scheme is defined for the curve, then that scheme must
|
||
// be used.
|
||
OK = (curveValue->sign.scheme == TPM_ALG_NULL
|
||
|| ( publicArea->parameters.eccDetail.scheme.scheme
|
||
== curveValue->sign.scheme));
|
||
OK = OK && CryptAreKeySizesConsistent(publicArea);
|
||
}
|
||
}
|
||
break;
|
||
#endif //TPM_ALG_ECC
|
||
default:
|
||
// Symmetric object common checks
|
||
// There is noting to check with a symmetric key that is public only.
|
||
// Also not sure that there is anything useful to be done with it
|
||
// either.
|
||
break;
|
||
}
|
||
return OK;
|
||
}
|
||
//
|
||
//
|
||
//
|
||
// 10.2.9.14 CryptObjectPublicPrivateMatch()
|
||
//
|
||
// This function checks the cryptographic binding between the public and sensitive areas.
|
||
//
|
||
// Error Returns Meaning
|
||
//
|
||
// TPM_RC_TYPE the type of the public and private areas are not the same
|
||
// TPM_RC_FAILURE crypto error
|
||
// TPM_RC_BINDING the public and private areas are not cryptographically matched.
|
||
//
|
||
TPM_RC
|
||
CryptObjectPublicPrivateMatch(
|
||
OBJECT *object // IN: the object to check
|
||
)
|
||
{
|
||
TPMT_PUBLIC *publicArea;
|
||
TPMT_SENSITIVE *sensitive;
|
||
TPM_RC result = TPM_RC_SUCCESS;
|
||
BOOL isAsymmetric = FALSE;
|
||
pAssert(object != NULL);
|
||
publicArea = &object->publicArea;
|
||
sensitive = &object->sensitive;
|
||
if(publicArea->type != sensitive->sensitiveType)
|
||
return TPM_RC_TYPE;
|
||
switch(publicArea->type)
|
||
{
|
||
#ifdef TPM_ALG_RSA
|
||
case TPM_ALG_RSA:
|
||
isAsymmetric = TRUE;
|
||
// The public and private key sizes need to be consistent
|
||
if(sensitive->sensitive.rsa.t.size != publicArea->unique.rsa.t.size/2)
|
||
result = TPM_RC_BINDING;
|
||
else
|
||
// Load key by computing the private exponent
|
||
result = CryptLoadPrivateRSA(object);
|
||
break;
|
||
#endif
|
||
#ifdef TPM_ALG_ECC
|
||
// This function is called from ObjectLoad() which has already checked to
|
||
// see that the public point is on the curve so no need to repeat that
|
||
// check.
|
||
case TPM_ALG_ECC:
|
||
isAsymmetric = TRUE;
|
||
if( publicArea->unique.ecc.x.t.size
|
||
!= sensitive->sensitive.ecc.t.size)
|
||
result = TPM_RC_BINDING;
|
||
else if(publicArea->nameAlg != TPM_ALG_NULL)
|
||
{
|
||
TPMS_ECC_POINT publicToCompare;
|
||
// Compute ECC public key
|
||
CryptEccPointMultiply(&publicToCompare,
|
||
publicArea->parameters.eccDetail.curveID,
|
||
&sensitive->sensitive.ecc, NULL);
|
||
// Compare ECC public key
|
||
if( (!Memory2BEqual(&publicArea->unique.ecc.x.b,
|
||
&publicToCompare.x.b))
|
||
|| (!Memory2BEqual(&publicArea->unique.ecc.y.b,
|
||
&publicToCompare.y.b)))
|
||
result = TPM_RC_BINDING;
|
||
}
|
||
break;
|
||
//
|
||
#endif
|
||
case TPM_ALG_KEYEDHASH:
|
||
break;
|
||
case TPM_ALG_SYMCIPHER:
|
||
if( (publicArea->parameters.symDetail.sym.keyBits.sym + 7)/8
|
||
!= sensitive->sensitive.sym.t.size)
|
||
result = TPM_RC_BINDING;
|
||
break;
|
||
default:
|
||
// The choice here is an assert or a return of a bad type for the object
|
||
pAssert(0);
|
||
break;
|
||
}
|
||
// For asymmetric keys, the algorithm for validating the linkage between
|
||
// the public and private areas is algorithm dependent. For symmetric keys
|
||
// the linkage is based on hashing the symKey and obfuscation values.
|
||
if( result == TPM_RC_SUCCESS && !isAsymmetric
|
||
&& publicArea->nameAlg != TPM_ALG_NULL)
|
||
{
|
||
TPM2B_DIGEST uniqueToCompare;
|
||
// Compute unique for symmetric key
|
||
CryptComputeSymmetricUnique(publicArea->nameAlg, sensitive,
|
||
&uniqueToCompare);
|
||
// Compare unique
|
||
if(!Memory2BEqual(&publicArea->unique.sym.b,
|
||
&uniqueToCompare.b))
|
||
result = TPM_RC_BINDING;
|
||
}
|
||
return result;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.9.15 CryptGetSignHashAlg()
|
||
//
|
||
// Get the hash algorithm of signature from a TPMT_SIGNATURE structure. It assumes the signature is not
|
||
// NULL This is a function for easy access
|
||
//
|
||
TPMI_ALG_HASH
|
||
CryptGetSignHashAlg(
|
||
TPMT_SIGNATURE *auth // IN: signature
|
||
)
|
||
{
|
||
pAssert(auth->sigAlg != TPM_ALG_NULL);
|
||
// Get authHash algorithm based on signing scheme
|
||
switch(auth->sigAlg)
|
||
{
|
||
#ifdef TPM_ALG_RSA
|
||
case TPM_ALG_RSASSA:
|
||
return auth->signature.rsassa.hash;
|
||
case TPM_ALG_RSAPSS:
|
||
return auth->signature.rsapss.hash;
|
||
#endif //TPM_ALG_RSA
|
||
#ifdef TPM_ALG_ECC
|
||
case TPM_ALG_ECDSA:
|
||
return auth->signature.ecdsa.hash;
|
||
#endif //TPM_ALG_ECC
|
||
case TPM_ALG_HMAC:
|
||
return auth->signature.hmac.hashAlg;
|
||
default:
|
||
return TPM_ALG_NULL;
|
||
}
|
||
}
|
||
//
|
||
//
|
||
// 10.2.9.16 CryptIsSplitSign()
|
||
//
|
||
// This function us used to determine if the signing operation is a split signing operation that required a
|
||
// TPM2_Commit().
|
||
//
|
||
BOOL
|
||
CryptIsSplitSign(
|
||
TPM_ALG_ID scheme // IN: the algorithm selector
|
||
)
|
||
{
|
||
if( 0
|
||
# ifdef TPM_ALG_ECDAA
|
||
|| scheme == TPM_ALG_ECDAA
|
||
# endif // TPM_ALG_ECDAA
|
||
)
|
||
return TRUE;
|
||
return FALSE;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.9.17 CryptIsSignScheme()
|
||
//
|
||
// This function indicates if a scheme algorithm is a sign algorithm.
|
||
//
|
||
BOOL
|
||
CryptIsSignScheme(
|
||
TPMI_ALG_ASYM_SCHEME scheme
|
||
)
|
||
{
|
||
BOOL isSignScheme = FALSE;
|
||
switch(scheme)
|
||
{
|
||
#ifdef TPM_ALG_RSA
|
||
// If RSA is implemented, then both signing schemes are required
|
||
case TPM_ALG_RSASSA:
|
||
case TPM_ALG_RSAPSS:
|
||
isSignScheme = TRUE;
|
||
break;
|
||
#endif //TPM_ALG_RSA
|
||
#ifdef TPM_ALG_ECC
|
||
// If ECC is implemented ECDSA is required
|
||
case TPM_ALG_ECDSA:
|
||
#ifdef TPM_ALG_ECDAA
|
||
// ECDAA is optional
|
||
case TPM_ALG_ECDAA:
|
||
#endif
|
||
#ifdef TPM_ALG_ECSCHNORR
|
||
// Schnorr is also optional
|
||
case TPM_ALG_ECSCHNORR:
|
||
#endif
|
||
#ifdef TPM_ALG_SM2
|
||
case TPM_ALG_SM2:
|
||
#endif
|
||
isSignScheme = TRUE;
|
||
break;
|
||
#endif //TPM_ALG_ECC
|
||
default:
|
||
break;
|
||
}
|
||
return isSignScheme;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.9.18 CryptIsDecryptScheme()
|
||
//
|
||
// This function indicate if a scheme algorithm is a decrypt algorithm.
|
||
//
|
||
BOOL
|
||
CryptIsDecryptScheme(
|
||
TPMI_ALG_ASYM_SCHEME scheme
|
||
)
|
||
{
|
||
BOOL isDecryptScheme = FALSE;
|
||
switch(scheme)
|
||
{
|
||
#ifdef TPM_ALG_RSA
|
||
// If RSA is implemented, then both decrypt schemes are required
|
||
case TPM_ALG_RSAES:
|
||
case TPM_ALG_OAEP:
|
||
isDecryptScheme = TRUE;
|
||
break;
|
||
#endif //TPM_ALG_RSA
|
||
#ifdef TPM_ALG_ECC
|
||
// If ECC is implemented ECDH is required
|
||
case TPM_ALG_ECDH:
|
||
#ifdef TPM_ALG_SM2
|
||
case TPM_ALG_SM2:
|
||
#endif
|
||
#ifdef TPM_ALG_ECMQV
|
||
case TPM_ALG_ECMQV:
|
||
#endif
|
||
isDecryptScheme = TRUE;
|
||
break;
|
||
#endif //TPM_ALG_ECC
|
||
default:
|
||
break;
|
||
}
|
||
return isDecryptScheme;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.9.19 CryptSelectSignScheme()
|
||
//
|
||
// This function is used by the attestation and signing commands. It implements the rules for selecting the
|
||
// signature scheme to use in signing. This function requires that the signing key either be TPM_RH_NULL
|
||
// or be loaded.
|
||
// If a default scheme is defined in object, the default scheme should be chosen, otherwise, the input
|
||
// scheme should be chosen. In the case that both object and input scheme has a non-NULL scheme
|
||
// algorithm, if the schemes are compatible, the input scheme will be chosen.
|
||
//
|
||
//
|
||
//
|
||
//
|
||
// Error Returns Meaning
|
||
//
|
||
// TPM_RC_KEY key referenced by signHandle is not a signing key
|
||
// TPM_RC_SCHEME both scheme and key's default scheme are empty; or scheme is
|
||
// empty while key's default scheme requires explicit input scheme (split
|
||
// signing); or non-empty default key scheme differs from scheme
|
||
//
|
||
TPM_RC
|
||
CryptSelectSignScheme(
|
||
TPMI_DH_OBJECT signHandle, // IN: handle of signing key
|
||
TPMT_SIG_SCHEME *scheme // IN/OUT: signing scheme
|
||
)
|
||
{
|
||
OBJECT *signObject;
|
||
TPMT_SIG_SCHEME *objectScheme;
|
||
TPMT_PUBLIC *publicArea;
|
||
TPM_RC result = TPM_RC_SUCCESS;
|
||
// If the signHandle is TPM_RH_NULL, then the NULL scheme is used, regardless
|
||
// of the setting of scheme
|
||
if(signHandle == TPM_RH_NULL)
|
||
{
|
||
scheme->scheme = TPM_ALG_NULL;
|
||
scheme->details.any.hashAlg = TPM_ALG_NULL;
|
||
}
|
||
else
|
||
{
|
||
// sign handle is not NULL so...
|
||
// Get sign object pointer
|
||
signObject = ObjectGet(signHandle);
|
||
publicArea = &signObject->publicArea;
|
||
// is this a signing key?
|
||
if(!publicArea->objectAttributes.sign)
|
||
result = TPM_RC_KEY;
|
||
else
|
||
{
|
||
// "parms" defined to avoid long code lines.
|
||
TPMU_PUBLIC_PARMS *parms = &publicArea->parameters;
|
||
if(CryptIsAsymAlgorithm(publicArea->type))
|
||
objectScheme = (TPMT_SIG_SCHEME *)&parms->asymDetail.scheme;
|
||
else
|
||
objectScheme = (TPMT_SIG_SCHEME *)&parms->keyedHashDetail.scheme;
|
||
// If the object doesn't have a default scheme, then use the
|
||
// input scheme.
|
||
if(objectScheme->scheme == TPM_ALG_NULL)
|
||
{
|
||
// Input and default can't both be NULL
|
||
if(scheme->scheme == TPM_ALG_NULL)
|
||
result = TPM_RC_SCHEME;
|
||
// Assume that the scheme is compatible with the key. If not,
|
||
// we will generate an error in the signing operation.
|
||
}
|
||
else if(scheme->scheme == TPM_ALG_NULL)
|
||
{
|
||
// input scheme is NULL so use default
|
||
// First, check to see if the default requires that the caller
|
||
// provided scheme data
|
||
if(CryptIsSplitSign(objectScheme->scheme))
|
||
result = TPM_RC_SCHEME;
|
||
else
|
||
{
|
||
scheme->scheme = objectScheme->scheme;
|
||
scheme->details.any.hashAlg
|
||
= objectScheme->details.any.hashAlg;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Both input and object have scheme selectors
|
||
// If the scheme and the hash are not the same then...
|
||
if( objectScheme->scheme != scheme->scheme
|
||
|| ( objectScheme->details.any.hashAlg
|
||
!= scheme->details.any.hashAlg))
|
||
result = TPM_RC_SCHEME;
|
||
}
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.9.20 CryptSign()
|
||
//
|
||
// Sign a digest with asymmetric key or HMAC. This function is called by attestation commands and the
|
||
// generic TPM2_Sign() command. This function checks the key scheme and digest size. It does not check
|
||
// if the sign operation is allowed for restricted key. It should be checked before the function is called. The
|
||
// function will assert if the key is not a signing key.
|
||
//
|
||
// Error Returns Meaning
|
||
//
|
||
// TPM_RC_SCHEME signScheme is not compatible with the signing key type
|
||
// TPM_RC_VALUE digest value is greater than the modulus of signHandle or size of
|
||
// hashData does not match hash algorithm insignScheme (for an RSA
|
||
// key); invalid commit status or failed to generate r value (for an ECC
|
||
// key)
|
||
//
|
||
TPM_RC
|
||
CryptSign(
|
||
TPMI_DH_OBJECT signHandle, // IN: The handle of sign key
|
||
TPMT_SIG_SCHEME *signScheme, // IN: sign scheme.
|
||
TPM2B_DIGEST *digest, // IN: The digest being signed
|
||
TPMT_SIGNATURE *signature // OUT: signature
|
||
)
|
||
{
|
||
OBJECT *signKey = ObjectGet(signHandle);
|
||
TPM_RC result = TPM_RC_SCHEME;
|
||
// check if input handle is a sign key
|
||
pAssert(signKey->publicArea.objectAttributes.sign == SET);
|
||
// Must have the private portion loaded. This check is made during
|
||
// authorization.
|
||
pAssert(signKey->attributes.publicOnly == CLEAR);
|
||
// Initialize signature scheme
|
||
signature->sigAlg = signScheme->scheme;
|
||
// If the signature algorithm is TPM_ALG_NULL, then we are done
|
||
if(signature->sigAlg == TPM_ALG_NULL)
|
||
return TPM_RC_SUCCESS;
|
||
// All the schemes other than TPM_ALG_NULL have a hash algorithm
|
||
TEST_HASH(signScheme->details.any.hashAlg);
|
||
// Initialize signature hash
|
||
// Note: need to do the check for alg null first because the null scheme
|
||
// doesn't have a hashAlg member.
|
||
signature->signature.any.hashAlg = signScheme->details.any.hashAlg;
|
||
// perform sign operation based on different key type
|
||
switch (signKey->publicArea.type)
|
||
{
|
||
#ifdef TPM_ALG_RSA
|
||
case TPM_ALG_RSA:
|
||
result = CryptSignRSA(signKey, signScheme, digest, signature);
|
||
break;
|
||
#endif //TPM_ALG_RSA
|
||
#ifdef TPM_ALG_ECC
|
||
case TPM_ALG_ECC:
|
||
result = CryptSignECC(signKey, signScheme, digest, signature);
|
||
break;
|
||
#endif //TPM_ALG_ECC
|
||
case TPM_ALG_KEYEDHASH:
|
||
result = CryptSignHMAC(signKey, signScheme, digest, signature);
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
return result;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.9.21 CryptVerifySignature()
|
||
//
|
||
// This function is used to verify a signature. It is called by TPM2_VerifySignature() and
|
||
// TPM2_PolicySigned().
|
||
// Since this operation only requires use of a public key, no consistency checks are necessary for the key to
|
||
// signature type because a caller can load any public key that they like with any scheme that they like. This
|
||
// routine simply makes sure that the signature is correct, whatever the type.
|
||
// This function requires that auth is not a NULL pointer.
|
||
//
|
||
// Error Returns Meaning
|
||
//
|
||
// TPM_RC_SIGNATURE the signature is not genuine
|
||
// TPM_RC_SCHEME the scheme is not supported
|
||
// TPM_RC_HANDLE an HMAC key was selected but the private part of the key is not
|
||
// loaded
|
||
//
|
||
TPM_RC
|
||
CryptVerifySignature(
|
||
TPMI_DH_OBJECT keyHandle, // IN: The handle of sign key
|
||
TPM2B_DIGEST *digest, // IN: The digest being validated
|
||
TPMT_SIGNATURE *signature // IN: signature
|
||
)
|
||
{
|
||
// NOTE: ObjectGet will either return a pointer to a loaded object or
|
||
// will assert. It will never return a non-valid value. This makes it save
|
||
// to initialize 'publicArea' with the return value from ObjectGet() without
|
||
// checking it first.
|
||
OBJECT *authObject = ObjectGet(keyHandle);
|
||
TPMT_PUBLIC *publicArea = &authObject->publicArea;
|
||
TPM_RC result = TPM_RC_SCHEME;
|
||
// The input unmarshaling should prevent any input signature from being
|
||
// a NULL signature, but just in case
|
||
if(signature->sigAlg == TPM_ALG_NULL)
|
||
return TPM_RC_SIGNATURE;
|
||
switch (publicArea->type)
|
||
{
|
||
#ifdef TPM_ALG_RSA
|
||
case TPM_ALG_RSA:
|
||
result = CryptRSAVerifySignature(authObject, digest, signature);
|
||
break;
|
||
#endif //TPM_ALG_RSA
|
||
#ifdef TPM_ALG_ECC
|
||
case TPM_ALG_ECC:
|
||
result = CryptECCVerifySignature(authObject, digest, signature);
|
||
break;
|
||
#endif // TPM_ALG_ECC
|
||
case TPM_ALG_KEYEDHASH:
|
||
if(authObject->attributes.publicOnly)
|
||
result = TPM_RC_HANDLE;
|
||
else
|
||
result = CryptHMACVerifySignature(authObject, digest, signature);
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
return result;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.10 Math functions
|
||
//
|
||
// 10.2.10.1 CryptDivide()
|
||
//
|
||
// This function interfaces to the math library for large number divide.
|
||
//
|
||
// Error Returns Meaning
|
||
//
|
||
// TPM_RC_SIZE quotient or remainder is too small to receive the result
|
||
//
|
||
TPM_RC
|
||
CryptDivide(
|
||
TPM2B *numerator, // IN: numerator
|
||
TPM2B *denominator, // IN: denominator
|
||
TPM2B *quotient, // OUT: quotient = numerator / denominator.
|
||
TPM2B *remainder // OUT: numerator mod denominator.
|
||
)
|
||
{
|
||
pAssert( numerator != NULL && denominator!= NULL
|
||
&& (quotient != NULL || remainder != NULL)
|
||
);
|
||
// assume denominator is not 0
|
||
pAssert(denominator->size != 0);
|
||
return TranslateCryptErrors(_math__Div(numerator,
|
||
denominator,
|
||
quotient,
|
||
remainder)
|
||
);
|
||
}
|
||
//
|
||
//
|
||
// 10.2.10.2 CryptCompare()
|
||
//
|
||
// This function interfaces to the math library for large number, unsigned compare.
|
||
//
|
||
// Return Value Meaning
|
||
//
|
||
// 1 if a > b
|
||
// 0 if a = b
|
||
// -1 if a < b
|
||
//
|
||
LIB_EXPORT int
|
||
CryptCompare(
|
||
const UINT32 aSize, // IN: size of a
|
||
const BYTE *a, // IN: a buffer
|
||
const UINT32 bSize, // IN: size of b
|
||
const BYTE *b // IN: b buffer
|
||
)
|
||
{
|
||
return _math__uComp(aSize, a, bSize, b);
|
||
}
|
||
//
|
||
//
|
||
// 10.2.10.3 CryptCompareSigned()
|
||
//
|
||
// This function interfaces to the math library for large number, signed compare.
|
||
//
|
||
// Return Value Meaning
|
||
//
|
||
// 1 if a > b
|
||
// 0 if a = b
|
||
// -1 if a < b
|
||
//
|
||
int
|
||
CryptCompareSigned(
|
||
UINT32 aSize, // IN: size of a
|
||
BYTE *a, // IN: a buffer
|
||
UINT32 bSize, // IN: size of b
|
||
BYTE *b // IN: b buffer
|
||
)
|
||
{
|
||
return _math__Comp(aSize, a, bSize, b);
|
||
}
|
||
//
|
||
//
|
||
// 10.2.10.4 CryptGetTestResult
|
||
//
|
||
// This function returns the results of a self-test function.
|
||
//
|
||
// NOTE: the behavior in this function is NOT the correct behavior for a real TPM implementation. An artificial behavior is
|
||
// placed here due to the limitation of a software simulation environment. For the correct behavior, consult the
|
||
// part 3 specification for TPM2_GetTestResult().
|
||
//
|
||
TPM_RC
|
||
CryptGetTestResult(
|
||
TPM2B_MAX_BUFFER *outData // OUT: test result data
|
||
)
|
||
{
|
||
outData->t.size = 0;
|
||
return TPM_RC_SUCCESS;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.11 Capability Support
|
||
//
|
||
// 10.2.11.1 CryptCapGetECCCurve()
|
||
//
|
||
// This function returns the list of implemented ECC curves.
|
||
//
|
||
// Return Value Meaning
|
||
//
|
||
// YES if no more ECC curve is available
|
||
// NO if there are more ECC curves not reported
|
||
//
|
||
#ifdef TPM_ALG_ECC //% 5
|
||
TPMI_YES_NO
|
||
CryptCapGetECCCurve(
|
||
TPM_ECC_CURVE curveID, // IN: the starting ECC curve
|
||
UINT32 maxCount, // IN: count of returned curve
|
||
TPML_ECC_CURVE *curveList // OUT: ECC curve list
|
||
)
|
||
{
|
||
TPMI_YES_NO more = NO;
|
||
UINT16 i;
|
||
UINT32 count = _cpri__EccGetCurveCount();
|
||
TPM_ECC_CURVE curve;
|
||
// Initialize output property list
|
||
curveList->count = 0;
|
||
// The maximum count of curves we may return is MAX_ECC_CURVES
|
||
if(maxCount > MAX_ECC_CURVES) maxCount = MAX_ECC_CURVES;
|
||
// Scan the eccCurveValues array
|
||
for(i = 0; i < count; i++)
|
||
{
|
||
curve = _cpri__GetCurveIdByIndex(i);
|
||
// If curveID is less than the starting curveID, skip it
|
||
if(curve < curveID)
|
||
continue;
|
||
if(curveList->count < maxCount)
|
||
{
|
||
// If we have not filled up the return list, add more curves to
|
||
// it
|
||
curveList->eccCurves[curveList->count] = curve;
|
||
curveList->count++;
|
||
}
|
||
else
|
||
{
|
||
// If the return list is full but we still have curves
|
||
// available, report this and stop iterating
|
||
more = YES;
|
||
break;
|
||
}
|
||
}
|
||
return more;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.11.2 CryptCapGetEccCurveNumber()
|
||
//
|
||
// This function returns the number of ECC curves supported by the TPM.
|
||
//
|
||
UINT32
|
||
CryptCapGetEccCurveNumber(
|
||
void
|
||
)
|
||
{
|
||
// There is an array that holds the curve data. Its size divided by the
|
||
// size of an entry is the number of values in the table.
|
||
return _cpri__EccGetCurveCount();
|
||
}
|
||
#endif //TPM_ALG_ECC //% 5
|
||
//
|
||
//
|
||
// 10.2.11.3 CryptAreKeySizesConsistent()
|
||
//
|
||
// This function validates that the public key size values are consistent for an asymmetric key.
|
||
//
|
||
// NOTE: This is not a comprehensive test of the public key.
|
||
//
|
||
//
|
||
// Return Value Meaning
|
||
//
|
||
// TRUE sizes are consistent
|
||
// FALSE sizes are not consistent
|
||
//
|
||
BOOL
|
||
CryptAreKeySizesConsistent(
|
||
TPMT_PUBLIC *publicArea // IN: the public area to check
|
||
)
|
||
{
|
||
BOOL consistent = FALSE;
|
||
switch (publicArea->type)
|
||
{
|
||
#ifdef TPM_ALG_RSA
|
||
case TPM_ALG_RSA:
|
||
// The key size in bits is filtered by the unmarshaling
|
||
consistent = ( ((publicArea->parameters.rsaDetail.keyBits+7)/8)
|
||
== publicArea->unique.rsa.t.size);
|
||
break;
|
||
#endif //TPM_ALG_RSA
|
||
#ifdef TPM_ALG_ECC
|
||
case TPM_ALG_ECC:
|
||
{
|
||
UINT16 keySizeInBytes;
|
||
TPM_ECC_CURVE curveId = publicArea->parameters.eccDetail.curveID;
|
||
keySizeInBytes = CryptEccGetKeySizeInBytes(curveId);
|
||
consistent = keySizeInBytes > 0
|
||
&& publicArea->unique.ecc.x.t.size <= keySizeInBytes
|
||
&& publicArea->unique.ecc.y.t.size <= keySizeInBytes;
|
||
}
|
||
break;
|
||
#endif //TPM_ALG_ECC
|
||
default:
|
||
break;
|
||
}
|
||
return consistent;
|
||
}
|
||
//
|
||
//
|
||
// 10.2.11.4 CryptAlgSetImplemented()
|
||
//
|
||
// This function initializes the bit vector with one bit for each implemented algorithm. This function is called
|
||
// from _TPM_Init(). The vector of implemented algorithms should be generated by the part 2 parser so that
|
||
// the g_implementedAlgorithms vector can be a const. That's not how it is now
|
||
//
|
||
void
|
||
CryptAlgsSetImplemented(
|
||
void
|
||
)
|
||
{
|
||
AlgorithmGetImplementedVector(&g_implementedAlgorithms);
|
||
}
|