/* Microsoft Reference Implementation for TPM 2.0 | |
* | |
* The copyright in this software is being made available under the BSD License, | |
* included below. This software may be subject to other third party and | |
* contributor rights, including patent rights, and no such rights are granted | |
* under this license. | |
* | |
* Copyright (c) Microsoft Corporation | |
* | |
* All rights reserved. | |
* | |
* BSD License | |
* | |
* Redistribution and use in source and binary forms, with or without modification, | |
* are permitted provided that the following conditions are met: | |
* | |
* Redistributions of source code must retain the above copyright notice, this list | |
* of conditions and the following disclaimer. | |
* | |
* Redistributions in binary form must reproduce the above copyright notice, this | |
* list of conditions and the following disclaimer in the documentation and/or | |
* other materials provided with the distribution. | |
* | |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ""AS IS"" | |
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR | |
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | |
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
*/ | |
//** Introduction | |
// | |
// This module contains the interfaces to the CryptoEngine and provides | |
// miscellaneous cryptographic functions in support of the TPM. | |
// | |
//** Includes | |
#include "Tpm.h" | |
//****************************************************************************/ | |
//** Hash/HMAC Functions | |
//****************************************************************************/ | |
//*** CryptHmacSign() | |
// Sign a digest using an HMAC key. This an HMAC of a digest, not an HMAC of a | |
// message. | |
// Return Type: TPM_RC | |
// TPM_RC_HASH not a valid hash | |
static TPM_RC | |
CryptHmacSign( | |
TPMT_SIGNATURE *signature, // OUT: signature | |
OBJECT *signKey, // IN: HMAC key sign the hash | |
TPM2B_DIGEST *hashData // IN: hash to be signed | |
) | |
{ | |
HMAC_STATE hmacState; | |
UINT32 digestSize; | |
digestSize = CryptHmacStart2B(&hmacState, signature->signature.any.hashAlg, | |
&signKey->sensitive.sensitive.bits.b); | |
CryptDigestUpdate2B(&hmacState.hashState, &hashData->b); | |
CryptHmacEnd(&hmacState, digestSize, | |
(BYTE *)&signature->signature.hmac.digest); | |
return TPM_RC_SUCCESS; | |
} | |
//*** CryptHMACVerifySignature() | |
// This function will verify a signature signed by a HMAC key. | |
// Note that a caller needs to prepare 'signature' with the signature algorithm | |
// (TPM_ALG_HMAC) and the hash algorithm to use. This function then builds a | |
// signature of that type. | |
// Return Type: TPM_RC | |
// TPM_RC_SCHEME not the proper scheme for this key type | |
// 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 | |
) | |
{ | |
TPMT_SIGNATURE test; | |
TPMT_KEYEDHASH_SCHEME *keyScheme = | |
&signKey->publicArea.parameters.keyedHashDetail.scheme; | |
// | |
if((signature->sigAlg != TPM_ALG_HMAC) | |
|| (signature->signature.hmac.hashAlg == TPM_ALG_NULL)) | |
return TPM_RC_SCHEME; | |
// This check is not really needed for verification purposes. However, it does | |
// prevent someone from trying to validate a signature using a weaker hash | |
// algorithm than otherwise allowed by the key. That is, a key with a scheme | |
// other than TMP_ALG_NULL can only be used to validate signatures that have | |
// a matching scheme. | |
if((keyScheme->scheme != TPM_ALG_NULL) | |
&& ((keyScheme->scheme != signature->sigAlg) | |
|| (keyScheme->details.hmac.hashAlg | |
!= signature->signature.any.hashAlg))) | |
return TPM_RC_SIGNATURE; | |
test.sigAlg = signature->sigAlg; | |
test.signature.hmac.hashAlg = signature->signature.hmac.hashAlg; | |
CryptHmacSign(&test, signKey, hashData); | |
// Compare digest | |
if(!MemoryEqual(&test.signature.hmac.digest, | |
&signature->signature.hmac.digest, | |
CryptHashGetDigestSize(signature->signature.any.hashAlg))) | |
return TPM_RC_SIGNATURE; | |
return TPM_RC_SUCCESS; | |
} | |
//*** CryptGenerateKeyedHash() | |
// This function creates a keyedHash object. | |
// Return type: TPM_RC | |
// TPM_RC_NO_RESULT cannot get values from random number generator | |
// TPM_RC_SIZE sensitive data size is larger than allowed for | |
// the scheme | |
static TPM_RC | |
CryptGenerateKeyedHash( | |
TPMT_PUBLIC *publicArea, // IN/OUT: the public area template | |
// for the new key. | |
TPMT_SENSITIVE *sensitive, // OUT: sensitive area | |
TPMS_SENSITIVE_CREATE *sensitiveCreate, // IN: sensitive creation data | |
RAND_STATE *rand // IN: "entropy" source | |
) | |
{ | |
TPMT_KEYEDHASH_SCHEME *scheme; | |
TPM_ALG_ID hashAlg; | |
UINT16 digestSize; | |
scheme = &publicArea->parameters.keyedHashDetail.scheme; | |
if(publicArea->type != TPM_ALG_KEYEDHASH) | |
return TPM_RC_FAILURE; | |
// 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; | |
digestSize = CryptHashGetDigestSize(hashAlg); | |
// if this is a signing or a decryption key, 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. The lower limit is 1/2 the size of the digest | |
// | |
//If the user provided the key, check that it is a proper size | |
if(sensitiveCreate->data.t.size != 0) | |
{ | |
if(IS_ATTRIBUTE(publicArea->objectAttributes, TPMA_OBJECT, decrypt) | |
|| IS_ATTRIBUTE(publicArea->objectAttributes, TPMA_OBJECT, sign)) | |
{ | |
if(sensitiveCreate->data.t.size > CryptHashGetBlockSize(hashAlg)) | |
return TPM_RC_SIZE; | |
#if 0 // May make this a FIPS-mode requirement | |
if(sensitiveCreate->data.t.size < (digestSize / 2)) | |
return TPM_RC_SIZE; | |
#endif | |
} | |
// If this is a data blob, then anything that will get past the unmarshaling | |
// is OK | |
MemoryCopy2B(&sensitive->sensitive.bits.b, &sensitiveCreate->data.b, | |
sizeof(sensitive->sensitive.bits.t.buffer)); | |
} | |
else | |
{ | |
// The TPM is going to generate the data so set the size to be the | |
// size of the digest of the algorithm | |
sensitive->sensitive.bits.t.size = | |
DRBG_Generate(rand, sensitive->sensitive.bits.t.buffer, digestSize); | |
if(sensitive->sensitive.bits.t.size == 0) | |
return (g_inFailureMode) ? TPM_RC_FAILURE : TPM_RC_NO_RESULT; | |
} | |
return TPM_RC_SUCCESS; | |
} | |
//*** 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 | |
) | |
{ | |
return scheme == TPM_ALG_ECDAA; | |
} | |
//**** ************************************************************************ | |
//** Symmetric Functions | |
//**** ************************************************************************ | |
//*** ParmDecryptSym() | |
// This function performs parameter decryption using symmetric block cipher. | |
/*(See Part 1 specification) | |
// Symmetric parameter decryption | |
// When parameter decryption uses a symmetric block cipher, a decryption | |
// key and IV will be generated from: | |
// KDFa(hash, sessionAuth, "CFB", nonceNewer, nonceOlder, bits) (24) | |
// Where: | |
// hash the hash function associated with the session | |
// sessionAuth the sessionAuth associated with the session | |
// nonceNewer nonceCaller for a command | |
// nonceOlder nonceTPM for a command | |
// bits the number of bits required for the symmetric key | |
// plus an IV | |
*/ | |
void | |
ParmDecryptSym( | |
TPM_ALG_ID symAlg, // IN: the symmetric algorithm | |
TPM_ALG_ID hash, // IN: hash algorithm for KDFa | |
UINT16 keySizeInBits, // IN: the key size in bits | |
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_KEY, nonceCaller, nonceTpm, | |
keySizeInBits + (iv.t.size * 8), symParmString, NULL, FALSE); | |
MemoryCopy(iv.t.buffer, &symParmString[keySize], iv.t.size); | |
CryptSymmetricDecrypt(data, symAlg, keySizeInBits, symParmString, | |
&iv, TPM_ALG_CFB, dataSize, data); | |
} | |
return; | |
} | |
//*** ParmEncryptSym() | |
// This function performs parameter encryption using symmetric block cipher. | |
/*(See part 1 specification) | |
// When parameter decryption uses a symmetric block cipher, an encryption | |
// key and IV will be generated from: | |
// KDFa(hash, sessionAuth, "CFB", nonceNewer, nonceOlder, bits) (24) | |
// Where: | |
// hash the hash function associated with the session | |
// sessionAuth the sessionAuth associated with the session | |
// nonceNewer nonceTPM for a response | |
// nonceOlder nonceCaller for a response | |
// bits the number of bits required for the symmetric key | |
// plus an IV | |
*/ | |
void | |
ParmEncryptSym( | |
TPM_ALG_ID symAlg, // IN: symmetric algorithm | |
TPM_ALG_ID hash, // IN: hash algorithm for KDFa | |
UINT16 keySizeInBits, // IN: symmetric key size in bits | |
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_KEY, nonceTpm, nonceCaller, | |
keySizeInBits + (iv.t.size * 8), symParmString, NULL, FALSE); | |
MemoryCopy(iv.t.buffer, &symParmString[keySize], iv.t.size); | |
CryptSymmetricEncrypt(data, symAlg, keySizeInBits, symParmString, &iv, | |
TPM_ALG_CFB, dataSize, data); | |
} | |
return; | |
} | |
//*** CryptGenerateKeySymmetric() | |
// This function generates a symmetric cipher key. The derivation process is | |
// determined by the type of the provided 'rand' | |
// Return type: TPM_RC | |
// TPM_RC_NO_RESULT cannot get a random value | |
// TPM_RC_KEY_SIZE key size in the public area does not match the size | |
// in the sensitive creation area | |
// TPM_RC_KEY provided key value is not allowed | |
static TPM_RC | |
CryptGenerateKeySymmetric( | |
TPMT_PUBLIC *publicArea, // IN/OUT: The public area template | |
// for the new key. | |
TPMT_SENSITIVE *sensitive, // OUT: sensitive area | |
TPMS_SENSITIVE_CREATE *sensitiveCreate, // IN: sensitive creation data | |
RAND_STATE *rand // IN: the "entropy" source for | |
) | |
{ | |
UINT16 keyBits = publicArea->parameters.symDetail.sym.keyBits.sym; | |
TPM_RC result; | |
// | |
// only do multiples of RADIX_BITS | |
if((keyBits % RADIX_BITS) != 0) | |
return TPM_RC_KEY_SIZE; | |
// If this is not a new key, then the provided key data must be the right size | |
if(sensitiveCreate->data.t.size != 0) | |
{ | |
result = CryptSymKeyValidate(&publicArea->parameters.symDetail.sym, | |
(TPM2B_SYM_KEY *)&sensitiveCreate->data); | |
if(result == TPM_RC_SUCCESS) | |
MemoryCopy2B(&sensitive->sensitive.sym.b, &sensitiveCreate->data.b, | |
sizeof(sensitive->sensitive.sym.t.buffer)); | |
} | |
#if ALG_TDES | |
else if(publicArea->parameters.symDetail.sym.algorithm == TPM_ALG_TDES) | |
{ | |
result = CryptGenerateKeyDes(publicArea, sensitive, rand); | |
} | |
#endif | |
else | |
{ | |
sensitive->sensitive.sym.t.size = | |
DRBG_Generate(rand, sensitive->sensitive.sym.t.buffer, | |
BITS_TO_BYTES(keyBits)); | |
if(g_inFailureMode) | |
result = TPM_RC_FAILURE; | |
else if(sensitive->sensitive.sym.t.size == 0) | |
result = TPM_RC_NO_RESULT; | |
else | |
result = TPM_RC_SUCCESS; | |
} | |
return result; | |
} | |
//*** 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. | |
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 = CryptHashGetDigestSize(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 | |
CryptKDFa(hash, key, XOR_KEY, contextU, contextV, | |
requestSize, mask, &counter, TRUE); | |
// XOR next piece of the data | |
pm = mask; | |
for(i = hLen < remainBytes ? hLen : remainBytes; i > 0; i--) | |
*data++ ^= *pm++; | |
} | |
return; | |
} | |
//**************************************************************************** | |
//** Initialization and shut down | |
//**************************************************************************** | |
//*** CryptInit() | |
// This function is called when the TPM receives a _TPM_Init indication. | |
// | |
// 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. | |
// Return Type: BOOL | |
// TRUE(1) initializations succeeded | |
// FALSE(0) initialization failed and caller should place the TPM into | |
// Failure Mode | |
BOOL | |
CryptInit( | |
void | |
) | |
{ | |
BOOL ok; | |
// Initialize the vector of implemented algorithms | |
AlgorithmGetImplementedVector(&g_implementedAlgorithms); | |
// Indicate that all test are necessary | |
CryptInitializeToTest(); | |
// Do any library initializations that are necessary. If any fails, | |
// the caller should go into failure mode; | |
ok = SupportLibInit(); | |
ok = ok && CryptSymInit(); | |
ok = ok && CryptRandInit(); | |
ok = ok && CryptHashInit(); | |
#if ALG_RSA | |
ok = ok && CryptRsaInit(); | |
#endif // ALG_RSA | |
#if ALG_ECC | |
ok = ok && CryptEccInit(); | |
#endif // ALG_ECC | |
return ok; | |
} | |
//*** CryptStartup() | |
// This function is called by TPM2_Startup() to initialize the functions in | |
// this cryptographic library and in the provided CryptoLibrary. This function | |
// and CryptUtilInit() are both provided so that the implementation may move the | |
// initialization around to get the best interaction. | |
// Return Type: BOOL | |
// TRUE(1) startup succeeded | |
// FALSE(0) startup failed and caller should place the TPM into | |
// Failure Mode | |
BOOL | |
CryptStartup( | |
STARTUP_TYPE type // IN: the startup type | |
) | |
{ | |
BOOL OK; | |
NOT_REFERENCED(type); | |
OK = CryptSymStartup() && CryptRandStartup() && CryptHashStartup() | |
#if ALG_RSA | |
&& CryptRsaStartup() | |
#endif // ALG_RSA | |
#if ALG_ECC | |
&& CryptEccStartup() | |
#endif // ALG_ECC | |
; | |
#if ALG_ECC | |
// Don't directly check for SU_RESET because that is the default | |
if(OK && (type != SU_RESTART) && (type != SU_RESUME)) | |
{ | |
// If the shutdown was orderly, then the values recovered from NV will | |
// be OK to use. | |
// Get a new random commit nonce | |
gr.commitNonce.t.size = sizeof(gr.commitNonce.t.buffer); | |
CryptRandomGenerate(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 // ALG_ECC | |
return OK; | |
} | |
//**************************************************************************** | |
//** Algorithm-Independent Functions | |
//**************************************************************************** | |
//*** 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. | |
// | |
//*** CryptIsAsymAlgorithm() | |
// This function indicates if an algorithm is an asymmetric algorithm. | |
// Return Type: BOOL | |
// TRUE(1) if it is an asymmetric algorithm | |
// FALSE(0) if it is not an asymmetric algorithm | |
BOOL | |
CryptIsAsymAlgorithm( | |
TPM_ALG_ID algID // IN: algorithm ID | |
) | |
{ | |
switch(algID) | |
{ | |
#if ALG_RSA | |
case TPM_ALG_RSA: | |
#endif | |
#if ALG_ECC | |
case TPM_ALG_ECC: | |
#endif | |
return TRUE; | |
break; | |
default: | |
break; | |
} | |
return FALSE; | |
} | |
//*** 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(). | |
// Return Type: TPM_RC | |
// 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( | |
OBJECT *encryptKey, // IN: encryption key object | |
const TPM2B *label, // IN: a null-terminated string as L | |
TPM2B_DATA *data, // OUT: secret value | |
TPM2B_ENCRYPTED_SECRET *secret // OUT: secret structure | |
) | |
{ | |
TPMT_RSA_DECRYPT scheme; | |
TPM_RC result = TPM_RC_SUCCESS; | |
// | |
if(data == NULL || secret == NULL) | |
return TPM_RC_FAILURE; | |
// The output secret value has the size of the digest produced by the nameAlg. | |
data->t.size = CryptHashGetDigestSize(encryptKey->publicArea.nameAlg); | |
// The encryption scheme is OAEP using the nameAlg of the encrypt key. | |
scheme.scheme = TPM_ALG_OAEP; | |
scheme.details.anySig.hashAlg = encryptKey->publicArea.nameAlg; | |
if(!IS_ATTRIBUTE(encryptKey->publicArea.objectAttributes, TPMA_OBJECT, decrypt)) | |
return TPM_RC_ATTRIBUTES; | |
switch(encryptKey->publicArea.type) | |
{ | |
#if ALG_RSA | |
case TPM_ALG_RSA: | |
{ | |
// Create secret data from RNG | |
CryptRandomGenerate(data->t.size, data->t.buffer); | |
// Encrypt the data by RSA OAEP into encrypted secret | |
result = CryptRsaEncrypt((TPM2B_PUBLIC_KEY_RSA *)secret, &data->b, | |
encryptKey, &scheme, label, NULL); | |
} | |
break; | |
#endif // ALG_RSA | |
#if ALG_ECC | |
case TPM_ALG_ECC: | |
{ | |
TPMS_ECC_POINT eccPublic; | |
TPM2B_ECC_PARAMETER eccPrivate; | |
TPMS_ECC_POINT eccSecret; | |
BYTE *buffer = secret->t.secret; | |
// Need to make sure that the public point of the key is on the | |
// curve defined by the key. | |
if(!CryptEccIsPointOnCurve( | |
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. | |
CryptEccNewKeyPair(&eccPublic, &eccPrivate, | |
encryptKey->publicArea.parameters.eccDetail.curveID); | |
// 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, NULL); | |
// 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, | |
&encryptKey->publicArea.unique.ecc, &eccPrivate, | |
NULL, NULL) | |
!= TPM_RC_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 // ALG_ECC | |
default: | |
FAIL(FATAL_ERROR_INTERNAL); | |
break; | |
} | |
return result; | |
} | |
//*** 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 | |
// | |
// Return Type: TPM_RC | |
// 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( | |
OBJECT *decryptKey, // IN: decrypt key | |
TPM2B_NONCE *nonceCaller, // IN: nonceCaller. It is needed for | |
// symmetric decryption. For | |
// asymmetric decryption, this | |
// parameter is NULL | |
const TPM2B *label, // IN: a value for L | |
TPM2B_ENCRYPTED_SECRET *secret, // IN: input secret | |
TPM2B_DATA *data // OUT: decrypted secret value | |
) | |
{ | |
TPM_RC result = TPM_RC_SUCCESS; | |
// Decryption for secret | |
switch(decryptKey->publicArea.type) | |
{ | |
#if ALG_RSA | |
case TPM_ALG_RSA: | |
{ | |
TPMT_RSA_DECRYPT scheme; | |
TPMT_RSA_SCHEME *keyScheme | |
= &decryptKey->publicArea.parameters.rsaDetail.scheme; | |
UINT16 digestSize; | |
scheme = *(TPMT_RSA_DECRYPT *)keyScheme; | |
// If the key scheme is TPM_ALG_NULL, set the scheme to OAEP and | |
// set the algorithm to the name algorithm. | |
if(scheme.scheme == TPM_ALG_NULL) | |
{ | |
// Use OAEP scheme | |
scheme.scheme = TPM_ALG_OAEP; | |
scheme.details.oaep.hashAlg = decryptKey->publicArea.nameAlg; | |
} | |
// use the digestSize as an indicator of whether or not the scheme | |
// is using a supported hash algorithm. | |
// Note: depending on the scheme used for encryption, a hashAlg might | |
// not be needed. However, the return value has to have some upper | |
// limit on the size. In this case, it is the size of the digest of the | |
// hash algorithm. It is checked after the decryption is done but, there | |
// is no point in doing the decryption if the size is going to be | |
// 'wrong' anyway. | |
digestSize = CryptHashGetDigestSize(scheme.details.oaep.hashAlg); | |
if(scheme.scheme != TPM_ALG_OAEP || digestSize == 0) | |
return TPM_RC_SCHEME; | |
// Set the output buffer capacity | |
data->t.size = sizeof(data->t.buffer); | |
// Decrypt seed by RSA OAEP | |
result = CryptRsaDecrypt(&data->b, &secret->b, | |
decryptKey, &scheme, label); | |
if((result == TPM_RC_SUCCESS) && (data->t.size > digestSize)) | |
result = TPM_RC_VALUE; | |
} | |
break; | |
#endif // ALG_RSA | |
#if 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, | |
&eccPublic, &decryptKey->sensitive.sensitive.ecc, | |
NULL, NULL); | |
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 = | |
CryptHashGetDigestSize(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 // ALG_ECC | |
#if !ALG_KEYEDHASH | |
# error "KEYEDHASH support is required" | |
#endif | |
case TPM_ALG_KEYEDHASH: | |
// The seed size can not be bigger than the digest size of nameAlg | |
if(secret->t.size > | |
CryptHashGetDigestSize(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 that 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 = {{0}}; | |
TPMT_SYM_DEF_OBJECT *symDef; | |
// The seed size can not be bigger than the digest size of nameAlg | |
if(secret->t.size > | |
CryptHashGetDigestSize(decryptKey->publicArea.nameAlg)) | |
result = TPM_RC_VALUE; | |
else | |
{ | |
symDef = &decryptKey->publicArea.parameters.symDetail.sym; | |
iv.t.size = CryptGetSymmetricBlockSize(symDef->algorithm, | |
symDef->keyBits.sym); | |
if(iv.t.size == 0) | |
return TPM_RC_FAILURE; | |
if(nonceCaller->t.size >= iv.t.size) | |
{ | |
MemoryCopy(iv.t.buffer, nonceCaller->t.buffer, iv.t.size); | |
} | |
else | |
{ | |
if(nonceCaller->t.size > sizeof(iv.t.buffer)) | |
return TPM_RC_FAILURE; | |
MemoryCopy(iv.b.buffer, nonceCaller->t.buffer, | |
nonceCaller->t.size); | |
} | |
// make sure secret will fit | |
if(secret->t.size > data->t.size) | |
return TPM_RC_FAILURE; | |
data->t.size = secret->t.size; | |
// CFB decrypt, using nonceCaller as iv | |
CryptSymmetricDecrypt(data->t.buffer, symDef->algorithm, | |
symDef->keyBits.sym, | |
decryptKey->sensitive.sensitive.sym.t.buffer, | |
&iv, TPM_ALG_CFB, secret->t.size, | |
secret->t.secret); | |
} | |
} | |
break; | |
default: | |
FAIL(FATAL_ERROR_INTERNAL); | |
break; | |
} | |
return result; | |
} | |
//*** 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 | |
// bytes | |
TPM2B_AUTH *extraKey, // IN: additional key material other than | |
// sessionAuth | |
BYTE *buffer // IN/OUT: parameter buffer to be encrypted | |
) | |
{ | |
SESSION *session = SessionGet(handle); // encrypt session | |
TPM2B_TYPE(TEMP_KEY, (sizeof(extraKey->t.buffer) | |
+ sizeof(session->sessionKey.t.buffer))); | |
TPM2B_TEMP_KEY key; // encryption key | |
UINT32 cipherSize = 0; // size of cipher text | |
// | |
// 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 | |
{ | |
FAIL(FATAL_ERROR_INTERNAL); | |
} | |
// Compute encryption key by concatenating sessionKey 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; | |
} | |
//*** CryptParameterDecryption() | |
// This function does in-place decryption of a command parameter. | |
// Return Type: TPM_RC | |
// 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 | |
// | |
// 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 | |
{ | |
FAIL(FATAL_ERROR_INTERNAL); | |
} | |
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; | |
} | |
//*** CryptComputeSymmetricUnique() | |
// This function computes the unique field in public area for symmetric objects. | |
void | |
CryptComputeSymmetricUnique( | |
TPMT_PUBLIC *publicArea, // IN: the object's public area | |
TPMT_SENSITIVE *sensitive, // IN: the associated sensitive area | |
TPM2B_DIGEST *unique // OUT: unique buffer | |
) | |
{ | |
// For parents (symmetric and derivation), use an HMAC to compute | |
// the 'unique' field | |
if(IS_ATTRIBUTE(publicArea->objectAttributes, TPMA_OBJECT, restricted) | |
&& IS_ATTRIBUTE(publicArea->objectAttributes, TPMA_OBJECT, decrypt)) | |
{ | |
// Unique field is HMAC(sensitive->seedValue, sensitive->sensitive) | |
HMAC_STATE hmacState; | |
unique->b.size = CryptHmacStart2B(&hmacState, publicArea->nameAlg, | |
&sensitive->seedValue.b); | |
CryptDigestUpdate2B(&hmacState.hashState, | |
&sensitive->sensitive.any.b); | |
CryptHmacEnd2B(&hmacState, &unique->b); | |
} | |
else | |
{ | |
HASH_STATE hashState; | |
// Unique := Hash(sensitive->seedValue || sensitive->sensitive) | |
unique->t.size = CryptHashStart(&hashState, publicArea->nameAlg); | |
CryptDigestUpdate2B(&hashState, &sensitive->seedValue.b); | |
CryptDigestUpdate2B(&hashState, &sensitive->sensitive.any.b); | |
CryptHashEnd2B(&hashState, &unique->b); | |
} | |
return; | |
} | |
//*** CryptCreateObject() | |
// This function creates an object. | |
// For an asymmetric key, it will create a key pair and, for a parent key, a seed | |
// value for child protections. | |
// | |
// For an symmetric object, (TPM_ALG_SYMCIPHER or TPM_ALG_KEYEDHASH), it will | |
// create a secret key if the caller did not provide one. It will create a random | |
// secret seed value that is hashed with the secret value to create the public | |
// unique value. | |
// | |
// 'publicArea', 'sensitive', and 'sensitiveCreate' are the only required parameters | |
// and are the only ones that are used by TPM2_Create(). The other parameters | |
// are optional and are used when the generated Object needs to be deterministic. | |
// This is the case for both Primary Objects and Derived Objects. | |
// | |
// When a seed value is provided, a RAND_STATE will be populated and used for | |
// all operations in the object generation that require a random number. In the | |
// simplest case, TPM2_CreatePrimary() will use 'seed', 'label' and 'context' with | |
// context being the hash of the template. If the Primary Object is in | |
// the Endorsement hierarchy, it will also populate 'proof' with ehProof. | |
// | |
// For derived keys, 'seed' will be the secret value from the parent, 'label' and | |
// 'context' will be set according to the parameters of TPM2_CreateLoaded() and | |
// 'hashAlg' will be set which causes the RAND_STATE to be a KDF generator. | |
// | |
// Return Type: TPM_RC | |
// TPM_RC_KEY a provided key is not an allowed value | |
// 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_NO_RESULT unable to get random values (only in derivation) | |
// 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 | |
TPM_RC | |
CryptCreateObject( | |
OBJECT *object, // IN: new object structure pointer | |
TPMS_SENSITIVE_CREATE *sensitiveCreate, // IN: sensitive creation | |
RAND_STATE *rand // IN: the random number generator | |
// to use | |
) | |
{ | |
TPMT_PUBLIC *publicArea = &object->publicArea; | |
TPMT_SENSITIVE *sensitive = &object->sensitive; | |
TPM_RC result = TPM_RC_SUCCESS; | |
// | |
// Set the sensitive type for the object | |
sensitive->sensitiveType = publicArea->type; | |
// For all objects, copy the initial authorization data | |
sensitive->authValue = sensitiveCreate->userAuth; | |
// If the TPM is the source of the data, set the size of the provided data to | |
// zero so that there's no confusion about what to do. | |
if(IS_ATTRIBUTE(publicArea->objectAttributes, | |
TPMA_OBJECT, sensitiveDataOrigin)) | |
sensitiveCreate->data.t.size = 0; | |
// Generate the key and unique fields for the asymmetric keys and just the | |
// sensitive value for symmetric object | |
switch(publicArea->type) | |
{ | |
#if ALG_RSA | |
// Create RSA key | |
case TPM_ALG_RSA: | |
// RSA uses full object so that it has a place to put the private | |
// exponent | |
result = CryptRsaGenerateKey(publicArea, sensitive, rand); | |
break; | |
#endif // ALG_RSA | |
#if ALG_ECC | |
// Create ECC key | |
case TPM_ALG_ECC: | |
result = CryptEccGenerateKey(publicArea, sensitive, rand); | |
break; | |
#endif // ALG_ECC | |
case TPM_ALG_SYMCIPHER: | |
result = CryptGenerateKeySymmetric(publicArea, sensitive, | |
sensitiveCreate, rand); | |
break; | |
case TPM_ALG_KEYEDHASH: | |
result = CryptGenerateKeyedHash(publicArea, sensitive, | |
sensitiveCreate, rand); | |
break; | |
default: | |
FAIL(FATAL_ERROR_INTERNAL); | |
break; | |
} | |
if(result != TPM_RC_SUCCESS) | |
return result; | |
// Create the sensitive seed value | |
// If this is a primary key in the endorsement hierarchy, stir the DRBG state | |
// This implementation uses both shProof and ehProof to make sure that there | |
// is no leakage of either. | |
if(object->attributes.primary && object->attributes.epsHierarchy) | |
{ | |
DRBG_AdditionalData((DRBG_STATE *)rand, &gp.shProof.b); | |
DRBG_AdditionalData((DRBG_STATE *)rand, &gp.ehProof.b); | |
} | |
// Generate a seedValue that is the size of the digest produced by nameAlg | |
sensitive->seedValue.t.size = | |
DRBG_Generate(rand, sensitive->seedValue.t.buffer, | |
CryptHashGetDigestSize(publicArea->nameAlg)); | |
if(g_inFailureMode) | |
return TPM_RC_FAILURE; | |
else if(sensitive->seedValue.t.size == 0) | |
return TPM_RC_NO_RESULT; | |
// For symmetric objects, need to compute the unique value for the public area | |
if(publicArea->type == TPM_ALG_SYMCIPHER | |
|| publicArea->type == TPM_ALG_KEYEDHASH) | |
{ | |
CryptComputeSymmetricUnique(publicArea, sensitive, &publicArea->unique.sym); | |
} | |
else | |
{ | |
// if this is an asymmetric key and it isn't a parent, then | |
// get rid of the seed. | |
if(IS_ATTRIBUTE(publicArea->objectAttributes, TPMA_OBJECT, sign) | |
|| !IS_ATTRIBUTE(publicArea->objectAttributes, TPMA_OBJECT, restricted)) | |
memset(&sensitive->seedValue, 0, sizeof(sensitive->seedValue)); | |
} | |
// Compute the name | |
PublicMarshalAndComputeName(publicArea, &object->name); | |
return result; | |
} | |
//*** 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 | |
) | |
{ | |
if(auth->sigAlg == TPM_ALG_NULL) | |
FAIL(FATAL_ERROR_INTERNAL); | |
// Get authHash algorithm based on signing scheme | |
switch(auth->sigAlg) | |
{ | |
#if ALG_RSA | |
// If RSA is supported, both RSASSA and RSAPSS are required | |
# if !defined TPM_ALG_RSASSA || !defined TPM_ALG_RSAPSS | |
# error "RSASSA and RSAPSS are required for RSA" | |
# endif | |
case TPM_ALG_RSASSA: | |
return auth->signature.rsassa.hash; | |
case TPM_ALG_RSAPSS: | |
return auth->signature.rsapss.hash; | |
#endif // ALG_RSA | |
#if ALG_ECC | |
// If ECC is defined, ECDSA is mandatory | |
# if !ALG_ECDSA | |
# error "ECDSA is requried for ECC" | |
# endif | |
case TPM_ALG_ECDSA: | |
// SM2 and ECSCHNORR are optional | |
# if ALG_SM2 | |
case TPM_ALG_SM2: | |
# endif | |
# if ALG_ECSCHNORR | |
case TPM_ALG_ECSCHNORR: | |
# endif | |
//all ECC signatures look the same | |
return auth->signature.ecdsa.hash; | |
# if ALG_ECDAA | |
// Don't know how to verify an ECDAA signature | |
case TPM_ALG_ECDAA: | |
break; | |
# endif | |
#endif // ALG_ECC | |
case TPM_ALG_HMAC: | |
return auth->signature.hmac.hashAlg; | |
default: | |
break; | |
} | |
return TPM_ALG_NULL; | |
} | |
//*** 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 | |
) | |
{ | |
switch(scheme) | |
{ | |
# if ALG_ECDAA | |
case TPM_ALG_ECDAA: | |
return TRUE; | |
break; | |
# endif // ALG_ECDAA | |
default: | |
return FALSE; | |
break; | |
} | |
} | |
//*** CryptIsAsymSignScheme() | |
// This function indicates if a scheme algorithm is a sign algorithm. | |
BOOL | |
CryptIsAsymSignScheme( | |
TPMI_ALG_PUBLIC publicType, // IN: Type of the object | |
TPMI_ALG_ASYM_SCHEME scheme // IN: the scheme | |
) | |
{ | |
BOOL isSignScheme = TRUE; | |
switch(publicType) | |
{ | |
#if ALG_RSA | |
case TPM_ALG_RSA: | |
switch(scheme) | |
{ | |
# if !ALG_RSASSA || !ALG_RSAPSS | |
# error "RSASSA and PSAPSS required if RSA used." | |
# endif | |
case TPM_ALG_RSASSA: | |
case TPM_ALG_RSAPSS: | |
break; | |
default: | |
isSignScheme = FALSE; | |
break; | |
} | |
break; | |
#endif // ALG_RSA | |
#if ALG_ECC | |
// If ECC is implemented ECDSA is required | |
case TPM_ALG_ECC: | |
switch(scheme) | |
{ | |
// Support for ECDSA is required for ECC | |
case TPM_ALG_ECDSA: | |
#if ALG_ECDAA // ECDAA is optional | |
case TPM_ALG_ECDAA: | |
#endif | |
#if ALG_ECSCHNORR // Schnorr is also optional | |
case TPM_ALG_ECSCHNORR: | |
#endif | |
#if ALG_SM2 // SM2 is optional | |
case TPM_ALG_SM2: | |
#endif | |
break; | |
default: | |
isSignScheme = FALSE; | |
break; | |
} | |
break; | |
#endif // ALG_ECC | |
default: | |
isSignScheme = FALSE; | |
break; | |
} | |
return isSignScheme; | |
} | |
//*** CryptIsAsymDecryptScheme() | |
// This function indicate if a scheme algorithm is a decrypt algorithm. | |
BOOL | |
CryptIsAsymDecryptScheme( | |
TPMI_ALG_PUBLIC publicType, // IN: Type of the object | |
TPMI_ALG_ASYM_SCHEME scheme // IN: the scheme | |
) | |
{ | |
BOOL isDecryptScheme = TRUE; | |
switch(publicType) | |
{ | |
#if ALG_RSA | |
case TPM_ALG_RSA: | |
switch(scheme) | |
{ | |
case TPM_ALG_RSAES: | |
case TPM_ALG_OAEP: | |
break; | |
default: | |
isDecryptScheme = FALSE; | |
break; | |
} | |
break; | |
#endif // ALG_RSA | |
#if ALG_ECC | |
// If ECC is implemented ECDH is required | |
case TPM_ALG_ECC: | |
switch(scheme) | |
{ | |
#if !ALG_ECDH | |
# error "ECDH is required for ECC" | |
#endif | |
case TPM_ALG_ECDH: | |
#if ALG_SM2 | |
case TPM_ALG_SM2: | |
#endif | |
#if ALG_ECMQV | |
case TPM_ALG_ECMQV: | |
#endif | |
break; | |
default: | |
isDecryptScheme = FALSE; | |
break; | |
} | |
break; | |
#endif // ALG_ECC | |
default: | |
isDecryptScheme = FALSE; | |
break; | |
} | |
return isDecryptScheme; | |
} | |
//*** 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. | |
// | |
// This function should not be called if 'signObject->publicArea.type' == | |
// ALG_SYMCIPHER. | |
// | |
// Return Type: BOOL | |
// TRUE(1) scheme selected | |
// FALSE(0) 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' | |
BOOL | |
CryptSelectSignScheme( | |
OBJECT *signObject, // IN: signing key | |
TPMT_SIG_SCHEME *scheme // IN/OUT: signing scheme | |
) | |
{ | |
TPMT_SIG_SCHEME *objectScheme; | |
TPMT_PUBLIC *publicArea; | |
BOOL OK; | |
// If the signHandle is TPM_RH_NULL, then the NULL scheme is used, regardless | |
// of the setting of scheme | |
if(signObject == NULL) | |
{ | |
OK = TRUE; | |
scheme->scheme = TPM_ALG_NULL; | |
scheme->details.any.hashAlg = TPM_ALG_NULL; | |
} | |
else | |
{ | |
// assignment to save typing. | |
publicArea = &signObject->publicArea; | |
// A symmetric cipher can be used to encrypt and decrypt but it can't | |
// be used for signing | |
if(publicArea->type == TPM_ALG_SYMCIPHER) | |
return FALSE; | |
// Point to the scheme object | |
if(CryptIsAsymAlgorithm(publicArea->type)) | |
objectScheme = | |
(TPMT_SIG_SCHEME *)&publicArea->parameters.asymDetail.scheme; | |
else | |
objectScheme = | |
(TPMT_SIG_SCHEME *)&publicArea->parameters.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 | |
OK = (scheme->scheme != TPM_ALG_NULL); | |
// Assume that the scheme is compatible with the key. If not, | |
// an error will be generated 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 | |
OK = !CryptIsSplitSign(objectScheme->scheme); | |
if(OK) | |
{ | |
// The object has a scheme and the input is TPM_ALG_NULL so copy | |
// the object scheme as the final scheme. It is better to use a | |
// structure copy than a copy of the individual fields. | |
*scheme = *objectScheme; | |
} | |
} | |
else | |
{ | |
// Both input and object have scheme selectors | |
// If the scheme and the hash are not the same then... | |
// NOTE: the reason that there is no copy here is that the input | |
// might contain extra data for a split signing scheme and that | |
// data is not in the object so, it has to be preserved. | |
OK = (objectScheme->scheme == scheme->scheme) | |
&& (objectScheme->details.any.hashAlg | |
== scheme->details.any.hashAlg); | |
} | |
} | |
return OK; | |
} | |
//*** 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. | |
// | |
// Return Type: TPM_RC | |
// 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 in'signScheme' (for an RSA key); | |
// invalid commit status or failed to generate "r" value | |
// (for an ECC key) | |
TPM_RC | |
CryptSign( | |
OBJECT *signKey, // IN: signing key | |
TPMT_SIG_SCHEME *signScheme, // IN: sign scheme. | |
TPM2B_DIGEST *digest, // IN: The digest being signed | |
TPMT_SIGNATURE *signature // OUT: signature | |
) | |
{ | |
TPM_RC result = TPM_RC_SCHEME; | |
// Initialize signature scheme | |
signature->sigAlg = signScheme->scheme; | |
// If the signature algorithm is TPM_ALG_NULL or the signing key is NULL, | |
// then we are done | |
if((signature->sigAlg == TPM_ALG_NULL) || (signKey == NULL)) | |
return TPM_RC_SUCCESS; | |
// Initialize signature hash | |
// Note: need to do the check for TPM_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) | |
{ | |
#if ALG_RSA | |
case TPM_ALG_RSA: | |
result = CryptRsaSign(signature, signKey, digest, NULL); | |
break; | |
#endif // ALG_RSA | |
#if ALG_ECC | |
case TPM_ALG_ECC: | |
// The reason that signScheme is passed to CryptEccSign but not to the | |
// other signing methods is that the signing for ECC may be split and | |
// need the 'r' value that is in the scheme but not in the signature. | |
result = CryptEccSign(signature, signKey, digest, | |
(TPMT_ECC_SCHEME *)signScheme, NULL); | |
break; | |
#endif // ALG_ECC | |
case TPM_ALG_KEYEDHASH: | |
result = CryptHmacSign(signature, signKey, digest); | |
break; | |
default: | |
FAIL(FATAL_ERROR_INTERNAL); | |
break; | |
} | |
return result; | |
} | |
//*** CryptValidateSignature() | |
// 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. | |
// | |
// Return Type: TPM_RC | |
// 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 | |
CryptValidateSignature( | |
TPMI_DH_OBJECT keyHandle, // IN: The handle of sign key | |
TPM2B_DIGEST *digest, // IN: The digest being validated | |
TPMT_SIGNATURE *signature // IN: signature | |
) | |
{ | |
// NOTE: HandleToObject 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 HandleToObject() | |
// without checking it first. | |
OBJECT *signObject = HandleToObject(keyHandle); | |
TPMT_PUBLIC *publicArea = &signObject->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) | |
{ | |
#if ALG_RSA | |
case TPM_ALG_RSA: | |
{ | |
// | |
// Call RSA code to verify signature | |
result = CryptRsaValidateSignature(signature, signObject, digest); | |
break; | |
} | |
#endif // ALG_RSA | |
#if ALG_ECC | |
case TPM_ALG_ECC: | |
result = CryptEccValidateSignature(signature, signObject, digest); | |
break; | |
#endif // ALG_ECC | |
case TPM_ALG_KEYEDHASH: | |
if(signObject->attributes.publicOnly) | |
result = TPM_RCS_HANDLE; | |
else | |
result = CryptHMACVerifySignature(signObject, digest, signature); | |
break; | |
default: | |
break; | |
} | |
return result; | |
} | |
//*** 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; | |
} | |
//*** CryptValidateKeys() | |
// This function is used to verify that the key material of and object is valid. | |
// For a 'publicOnly' object, the key is verified for size and, if it is an ECC | |
// key, it is verified to be on the specified curve. For a key with a sensitive | |
// area, the binding between the public and private parts of the key are verified. | |
// If the nameAlg of the key is TPM_ALG_NULL, then the size of the sensitive area | |
// is verified but the public portion is not verified, unless the key is an RSA key. | |
// For an RSA key, the reason for loading the sensitive area is to use it. The | |
// only way to use a private RSA key is to compute the private exponent. To compute | |
// the private exponent, the public modulus is used. | |
// Return Type: TPM_RC | |
// TPM_RC_BINDING the public and private parts are not cryptographically | |
// bound | |
// TPM_RC_HASH cannot have a publicOnly key with nameAlg of TPM_ALG_NULL | |
// TPM_RC_KEY the public unique is not valid | |
// TPM_RC_KEY_SIZE the private area key is not valid | |
// TPM_RC_TYPE the types of the sensitive and private parts do not match | |
TPM_RC | |
CryptValidateKeys( | |
TPMT_PUBLIC *publicArea, | |
TPMT_SENSITIVE *sensitive, | |
TPM_RC blamePublic, | |
TPM_RC blameSensitive | |
) | |
{ | |
TPM_RC result; | |
UINT16 keySizeInBytes; | |
UINT16 digestSize = CryptHashGetDigestSize(publicArea->nameAlg); | |
TPMU_PUBLIC_PARMS *params = &publicArea->parameters; | |
TPMU_PUBLIC_ID *unique = &publicArea->unique; | |
if(sensitive != NULL) | |
{ | |
// Make sure that the types of the public and sensitive are compatible | |
if(publicArea->type != sensitive->sensitiveType) | |
return TPM_RCS_TYPE + blameSensitive; | |
// Make sure that the authValue is not bigger than allowed | |
// If there is no name algorithm, then the size just needs to be less than | |
// the maximum size of the buffer used for authorization. That size check | |
// was made during unmarshaling of the sensitive area | |
if((sensitive->authValue.t.size) > digestSize && (digestSize > 0)) | |
return TPM_RCS_SIZE + blameSensitive; | |
} | |
switch(publicArea->type) | |
{ | |
#if ALG_RSA | |
case TPM_ALG_RSA: | |
keySizeInBytes = BITS_TO_BYTES(params->rsaDetail.keyBits); | |
// Regardless of whether there is a sensitive area, the public modulus | |
// needs to have the correct size. Otherwise, it can't be used for | |
// any public key operation nor can it be used to compute the private | |
// exponent. | |
// NOTE: This implementation only supports key sizes that are multiples | |
// of 1024 bits which means that the MSb of the 0th byte will always be | |
// SET in any prime and in the public modulus. | |
if((unique->rsa.t.size != keySizeInBytes) | |
|| (unique->rsa.t.buffer[0] < 0x80)) | |
return TPM_RCS_KEY + blamePublic; | |
if(params->rsaDetail.exponent != 0 | |
&& params->rsaDetail.exponent < 7) | |
return TPM_RCS_VALUE + blamePublic; | |
if(sensitive != NULL) | |
{ | |
// If there is a sensitive area, it has to be the correct size | |
// including having the correct high order bit SET. | |
if(((sensitive->sensitive.rsa.t.size * 2) != keySizeInBytes) | |
|| (sensitive->sensitive.rsa.t.buffer[0] < 0x80)) | |
return TPM_RCS_KEY_SIZE + blameSensitive; | |
} | |
break; | |
#endif | |
#if ALG_ECC | |
case TPM_ALG_ECC: | |
{ | |
TPMI_ECC_CURVE curveId; | |
curveId = params->eccDetail.curveID; | |
keySizeInBytes = BITS_TO_BYTES(CryptEccGetKeySizeForCurve(curveId)); | |
if(sensitive == NULL) | |
{ | |
// Validate the public key size | |
if(unique->ecc.x.t.size != keySizeInBytes | |
|| unique->ecc.y.t.size != keySizeInBytes) | |
return TPM_RCS_KEY + blamePublic; | |
if(publicArea->nameAlg != TPM_ALG_NULL) | |
{ | |
if(!CryptEccIsPointOnCurve(curveId, &unique->ecc)) | |
return TPM_RCS_ECC_POINT + blamePublic; | |
} | |
} | |
else | |
{ | |
// If the nameAlg is TPM_ALG_NULL, then only verify that the | |
// private part of the key is OK. | |
if(!CryptEccIsValidPrivateKey(&sensitive->sensitive.ecc, | |
curveId)) | |
return TPM_RCS_KEY_SIZE; | |
if(publicArea->nameAlg != TPM_ALG_NULL) | |
{ | |
// Full key load, verify that the public point belongs to the | |
// private key. | |
TPMS_ECC_POINT toCompare; | |
result = CryptEccPointMultiply(&toCompare, curveId, NULL, | |
&sensitive->sensitive.ecc, | |
NULL, NULL); | |
if(result != TPM_RC_SUCCESS) | |
return TPM_RCS_BINDING; | |
else | |
{ | |
// Make sure that the private key generated the public key. | |
// The input values and the values produced by the point | |
// multiply may not be the same size so adjust the computed | |
// value to match the size of the input value by adding or | |
// removing zeros. | |
AdjustNumberB(&toCompare.x.b, unique->ecc.x.t.size); | |
AdjustNumberB(&toCompare.y.b, unique->ecc.y.t.size); | |
if(!MemoryEqual2B(&unique->ecc.x.b, &toCompare.x.b) | |
|| !MemoryEqual2B(&unique->ecc.y.b, &toCompare.y.b)) | |
return TPM_RCS_BINDING; | |
} | |
} | |
} | |
break; | |
} | |
#endif | |
default: | |
// Checks for SYMCIPHER and KEYEDHASH are largely the same | |
// If public area has a nameAlg, then validate the public area size | |
// and if there is also a sensitive area, validate the binding | |
// For consistency, if the object is public-only just make sure that | |
// the unique field is consistent with the name algorithm | |
if(sensitive == NULL) | |
{ | |
if(unique->sym.t.size != digestSize) | |
return TPM_RCS_KEY + blamePublic; | |
} | |
else | |
{ | |
// Make sure that the key size in the sensitive area is consistent. | |
if(publicArea->type == TPM_ALG_SYMCIPHER) | |
{ | |
result = CryptSymKeyValidate(¶ms->symDetail.sym, | |
&sensitive->sensitive.sym); | |
if(result != TPM_RC_SUCCESS) | |
return result + blameSensitive; | |
} | |
else | |
{ | |
// For a keyed hash object, the key has to be less than the | |
// smaller of the block size of the hash used in the scheme or | |
// 128 bytes. The worst case value is limited by the | |
// unmarshaling code so the only thing left to be checked is | |
// that it does not exceed the block size of the hash. | |
// by the hash algorithm of the scheme. | |
TPMT_KEYEDHASH_SCHEME *scheme; | |
UINT16 maxSize; | |
scheme = ¶ms->keyedHashDetail.scheme; | |
if(scheme->scheme == TPM_ALG_XOR) | |
{ | |
maxSize = CryptHashGetBlockSize(scheme->details.xor.hashAlg); | |
} | |
else if(scheme->scheme == TPM_ALG_HMAC) | |
{ | |
maxSize = CryptHashGetBlockSize(scheme->details.hmac.hashAlg); | |
} | |
else if(scheme->scheme == TPM_ALG_NULL) | |
{ | |
// Not signing or xor so must be a data block | |
maxSize = 128; | |
} | |
else | |
return TPM_RCS_SCHEME + blamePublic; | |
if(sensitive->sensitive.bits.t.size > maxSize) | |
return TPM_RCS_KEY_SIZE + blameSensitive; | |
} | |
// If there is a nameAlg, check the binding | |
if(publicArea->nameAlg != TPM_ALG_NULL) | |
{ | |
TPM2B_DIGEST compare; | |
if(sensitive->seedValue.t.size != digestSize) | |
return TPM_RCS_KEY_SIZE + blameSensitive; | |
CryptComputeSymmetricUnique(publicArea, sensitive, &compare); | |
if(!MemoryEqual2B(&unique->sym.b, &compare.b)) | |
return TPM_RC_BINDING; | |
} | |
} | |
break; | |
} | |
// For a parent, need to check that the seedValue is the correct size for | |
// protections. It should be at least half the size of the nameAlg | |
if(IS_ATTRIBUTE(publicArea->objectAttributes, TPMA_OBJECT, restricted) | |
&& IS_ATTRIBUTE(publicArea->objectAttributes, TPMA_OBJECT, decrypt) | |
&& sensitive != NULL | |
&& publicArea->nameAlg != TPM_ALG_NULL) | |
{ | |
if((sensitive->seedValue.t.size < (digestSize / 2)) | |
|| (sensitive->seedValue.t.size > digestSize)) | |
return TPM_RCS_SIZE + blameSensitive; | |
} | |
return TPM_RC_SUCCESS; | |
} | |
//*** CryptSelectMac() | |
// This function is used to set the MAC scheme based on the key parameters and | |
// the input scheme. | |
// Return Type: TPM_RC | |
// TPM_RC_SCHEME the scheme is not a valid mac scheme | |
// TPM_RC_TYPE the input key is not a type that supports a mac | |
// TPM_RC_VALUE the input scheme and the key scheme are not compatible | |
TPM_RC | |
CryptSelectMac( | |
TPMT_PUBLIC *publicArea, | |
TPMI_ALG_MAC_SCHEME *inMac | |
) | |
{ | |
TPM_ALG_ID macAlg = TPM_ALG_NULL; | |
switch(publicArea->type) | |
{ | |
case TPM_ALG_KEYEDHASH: | |
{ | |
// Local value to keep lines from getting too long | |
TPMT_KEYEDHASH_SCHEME *scheme; | |
scheme = &publicArea->parameters.keyedHashDetail.scheme; | |
// Expect that the scheme is either HMAC or NULL | |
if(scheme->scheme != TPM_ALG_NULL) | |
macAlg = scheme->details.hmac.hashAlg; | |
break; | |
} | |
case TPM_ALG_SYMCIPHER: | |
{ | |
TPMT_SYM_DEF_OBJECT *scheme; | |
scheme = &publicArea->parameters.symDetail.sym; | |
// Expect that the scheme is either valid symmetric cipher or NULL | |
if(scheme->algorithm != TPM_ALG_NULL) | |
macAlg = scheme->mode.sym; | |
break; | |
} | |
default: | |
return TPM_RCS_TYPE; | |
} | |
// If the input value is not TPM_ALG_NULL ... | |
if(*inMac != TPM_ALG_NULL) | |
{ | |
// ... then either the scheme in the key must be TPM_ALG_NULL or the input | |
// value must match | |
if((macAlg != TPM_ALG_NULL) && (*inMac != macAlg)) | |
return TPM_RCS_VALUE; | |
} | |
else | |
{ | |
// Since the input value is TPM_ALG_NULL, then the key value can't be | |
// TPM_ALG_NULL | |
if(macAlg == TPM_ALG_NULL) | |
return TPM_RCS_VALUE; | |
*inMac = macAlg; | |
} | |
if(!CryptMacIsValidForKey(publicArea->type, *inMac, FALSE)) | |
return TPM_RCS_SCHEME; | |
return TPM_RC_SUCCESS; | |
} | |
//*** CryptMacIsValidForKey() | |
// Check to see if the key type is compatible with the mac type | |
BOOL | |
CryptMacIsValidForKey( | |
TPM_ALG_ID keyType, | |
TPM_ALG_ID macAlg, | |
BOOL flag | |
) | |
{ | |
switch(keyType) | |
{ | |
case TPM_ALG_KEYEDHASH: | |
return CryptHashIsValidAlg(macAlg, flag); | |
break; | |
case TPM_ALG_SYMCIPHER: | |
return CryptSmacIsValidAlg(macAlg, flag); | |
break; | |
default: | |
break; | |
} | |
return FALSE; | |
} | |
//*** CryptSmacIsValidAlg() | |
// This function is used to test if an algorithm is a supported SMAC algorithm. It | |
// needs to be updated as new algorithms are added. | |
BOOL | |
CryptSmacIsValidAlg( | |
TPM_ALG_ID alg, | |
BOOL FLAG // IN: Indicates if TPM_ALG_NULL is valid | |
) | |
{ | |
switch (alg) | |
{ | |
#if ALG_CMAC | |
case TPM_ALG_CMAC: | |
return TRUE; | |
break; | |
#endif | |
case TPM_ALG_NULL: | |
return FLAG; | |
break; | |
default: | |
return FALSE; | |
} | |
} | |
//*** CryptSymModeIsValid() | |
// Function checks to see if an algorithm ID is a valid, symmetric block cipher | |
// mode for the TPM. If 'flag' is SET, them TPM_ALG_NULL is a valid mode. | |
// not include the modes used for SMAC | |
BOOL | |
CryptSymModeIsValid( | |
TPM_ALG_ID mode, | |
BOOL flag | |
) | |
{ | |
switch(mode) | |
{ | |
#if ALG_CTR | |
case TPM_ALG_CTR: | |
#endif // ALG_CTR | |
#if ALG_OFB | |
case TPM_ALG_OFB: | |
#endif // ALG_OFB | |
#if ALG_CBC | |
case TPM_ALG_CBC: | |
#endif // ALG_CBC | |
#if ALG_CFB | |
case TPM_ALG_CFB: | |
#endif // ALG_CFB | |
#if ALG_ECB | |
case TPM_ALG_ECB: | |
#endif // ALG_ECB | |
return TRUE; | |
case TPM_ALG_NULL: | |
return flag; | |
break; | |
default: | |
break; | |
} | |
return FALSE; | |
} | |