| /* 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; | |
| } | |