diff options
Diffstat (limited to 'src/tpm2/crypto/openssl/CryptSym.c')
-rw-r--r-- | src/tpm2/crypto/openssl/CryptSym.c | 771 |
1 files changed, 771 insertions, 0 deletions
diff --git a/src/tpm2/crypto/openssl/CryptSym.c b/src/tpm2/crypto/openssl/CryptSym.c new file mode 100644 index 0000000..c8a0497 --- /dev/null +++ b/src/tpm2/crypto/openssl/CryptSym.c @@ -0,0 +1,771 @@ +/********************************************************************************/ +/* */ +/* Symmetric block cipher modes */ +/* Written by Ken Goldman */ +/* IBM Thomas J. Watson Research Center */ +/* $Id: CryptSym.c 1658 2021-01-22 23:14:01Z kgoldman $ */ +/* */ +/* Licenses and Notices */ +/* */ +/* 1. Copyright Licenses: */ +/* */ +/* - Trusted Computing Group (TCG) grants to the user of the source code in */ +/* this specification (the "Source Code") a worldwide, irrevocable, */ +/* nonexclusive, royalty free, copyright license to reproduce, create */ +/* derivative works, distribute, display and perform the Source Code and */ +/* derivative works thereof, and to grant others the rights granted herein. */ +/* */ +/* - The TCG grants to the user of the other parts of the specification */ +/* (other than the Source Code) the rights to reproduce, distribute, */ +/* display, and perform the specification solely for the purpose of */ +/* developing products based on such documents. */ +/* */ +/* 2. Source Code Distribution Conditions: */ +/* */ +/* - Redistributions of Source Code must retain the above copyright licenses, */ +/* this list of conditions and the following disclaimers. */ +/* */ +/* - Redistributions in binary form must reproduce the above copyright */ +/* licenses, this list of conditions and the following disclaimers in the */ +/* documentation and/or other materials provided with the distribution. */ +/* */ +/* 3. Disclaimers: */ +/* */ +/* - THE COPYRIGHT LICENSES SET FORTH ABOVE DO NOT REPRESENT ANY FORM OF */ +/* LICENSE OR WAIVER, EXPRESS OR IMPLIED, BY ESTOPPEL OR OTHERWISE, WITH */ +/* RESPECT TO PATENT RIGHTS HELD BY TCG MEMBERS (OR OTHER THIRD PARTIES) */ +/* THAT MAY BE NECESSARY TO IMPLEMENT THIS SPECIFICATION OR OTHERWISE. */ +/* Contact TCG Administration (admin@trustedcomputinggroup.org) for */ +/* information on specification licensing rights available through TCG */ +/* membership agreements. */ +/* */ +/* - THIS SPECIFICATION IS PROVIDED "AS IS" WITH NO EXPRESS OR IMPLIED */ +/* WARRANTIES WHATSOEVER, INCLUDING ANY WARRANTY OF MERCHANTABILITY OR */ +/* FITNESS FOR A PARTICULAR PURPOSE, ACCURACY, COMPLETENESS, OR */ +/* NONINFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS, OR ANY WARRANTY */ +/* OTHERWISE ARISING OUT OF ANY PROPOSAL, SPECIFICATION OR SAMPLE. */ +/* */ +/* - Without limitation, TCG and its members and licensors disclaim all */ +/* liability, including liability for infringement of any proprietary */ +/* rights, relating to use of information in this specification and to the */ +/* implementation of this specification, and TCG disclaims all liability for */ +/* cost of procurement of substitute goods or services, lost profits, loss */ +/* of use, loss of data or any incidental, consequential, direct, indirect, */ +/* or special damages, whether under contract, tort, warranty or otherwise, */ +/* arising in any way out of use or reliance upon this specification or any */ +/* information herein. */ +/* */ +/* (c) Copyright IBM Corp. and others, 2016 - 2021 */ +/* */ +/********************************************************************************/ + +/* 10.2.19 CryptSym.c */ +/* 10.2.19.1 Introduction */ +/* This file contains the implementation of the symmetric block cipher modes allowed for a + TPM. These functions only use the single block encryption functions of the selected symmetric + crypto library. */ + +/* 10.2.19.2 Includes, Defines, and Typedefs */ +#include "Tpm.h" +#include "CryptSym.h" +#include "Helpers_fp.h" // libtpms changed + + +#define KEY_BLOCK_SIZES(ALG, alg) \ + static const INT16 alg##KeyBlockSizes[] = { \ + ALG##_KEY_SIZES_BITS, -1, ALG##_BLOCK_SIZES }; + +FOR_EACH_SYM(KEY_BLOCK_SIZES) + +/* 10.2.19.3 Initialization and Data Access Functions */ +/* 10.2.19.3.1 CryptSymInit() */ +/* This function is called to do _TPM_Init() processing */ +BOOL +CryptSymInit( + void + ) +{ + return TRUE; +} +/* 10.2.19.3.2 CryptSymStartup() */ +/* This function is called to do TPM2_Startup() processing */ +BOOL +CryptSymStartup( + void + ) +{ + return TRUE; +} +/* 10.2.20.4 Data Access Functions */ +/* 10.2.20.4.1 CryptGetSymmetricBlockSize() */ +/* This function returns the block size of the algorithm. The table of bit sizes has an entry for + each allowed key size. The entry for a key size is 0 if the TPM does not implement that key + size. The key size table is delimited with a negative number (-1). After the delimiter is a list + of block sizes with each entry corresponding to the key bit size. For most symmetric algorithms, + the block size is the same regardless of the key size but this arrangement allows them to be + different. */ +/* Return Values Meaning */ +/* <= 0 cipher not supported */ +/* > 0 the cipher block size in bytes */ + +LIB_EXPORT INT16 +CryptGetSymmetricBlockSize( + TPM_ALG_ID symmetricAlg, // IN: the symmetric algorithm + UINT16 keySizeInBits // IN: the key size + ) +{ + const INT16 *sizes; + INT16 i; +#if 0 // libtpms added +#define ALG_CASE(SYM, sym) case TPM_ALG_##SYM: sizes = sym##KeyBlockSizes; break +#endif // libtpms added + switch(symmetricAlg) + { +#define GET_KEY_BLOCK_POINTER(SYM, sym) \ + case TPM_ALG_##SYM: \ + sizes = sym##KeyBlockSizes; \ + break; + // Get the pointer to the block size array + FOR_EACH_SYM(GET_KEY_BLOCK_POINTER); + + default: + return 0; + } + // Find the index of the indicated keySizeInBits + for(i = 0; *sizes >= 0; i++, sizes++) + { + if(*sizes == keySizeInBits) + break; + } + // If sizes is pointing at the end of the list of key sizes, then the desired + // key size was not found so set the block size to zero. + if(*sizes++ < 0) + return 0; + // Advance until the end of the list is found + while(*sizes++ >= 0); + // sizes is pointing to the first entry in the list of block sizes. Use the + // ith index to find the block size for the corresponding key size. + return sizes[i]; +} + +#if !USE_OPENSSL_FUNCTIONS_SYMMETRIC // libtpms added +/* 10.2.20.5 Symmetric Encryption */ +/* This function performs symmetric encryption based on the mode. */ +/* Error Returns Meaning */ +/* TPM_RC_SIZE dSize is not a multiple of the block size for an algorithm that requires it */ +/* TPM_RC_FAILURE Fatal error */ +LIB_EXPORT TPM_RC +CryptSymmetricEncrypt( + BYTE *dOut, // OUT: + TPM_ALG_ID algorithm, // IN: the symmetric algorithm + UINT16 keySizeInBits, // IN: key size in bits + const BYTE *key, // IN: key buffer. The size of this buffer + // in bytes is (keySizeInBits + 7) / 8 + TPM2B_IV *ivInOut, // IN/OUT: IV for decryption. + TPM_ALG_ID mode, // IN: Mode to use + INT32 dSize, // IN: data size (may need to be a + // multiple of the blockSize) + const BYTE *dIn // IN: data buffer + ) +{ + BYTE *pIv; + int i; + BYTE tmp[MAX_SYM_BLOCK_SIZE]; + BYTE *pT; + tpmCryptKeySchedule_t keySchedule; + INT16 blockSize; + TpmCryptSetSymKeyCall_t encrypt; + BYTE *iv; + BYTE defaultIv[MAX_SYM_BLOCK_SIZE] = {0}; + // + memset(&keySchedule, 0, sizeof(keySchedule)); // libtpms added; coverity + pAssert(dOut != NULL && key != NULL && dIn != NULL); + if(dSize == 0) + return TPM_RC_SUCCESS; + TEST(algorithm); + blockSize = CryptGetSymmetricBlockSize(algorithm, keySizeInBits); + if(blockSize == 0) + return TPM_RC_FAILURE; + // If the iv is provided, then it is expected to be block sized. In some cases, + // the caller is providing an array of 0's that is equal to [MAX_SYM_BLOCK_SIZE] + // with no knowledge of the actual block size. This function will set it. + if((ivInOut != NULL) && (mode != TPM_ALG_ECB)) + { + ivInOut->t.size = blockSize; + iv = ivInOut->t.buffer; + } + else + iv = defaultIv; + pIv = iv; + // Create encrypt key schedule and set the encryption function pointer. + switch (algorithm) + { + FOR_EACH_SYM(ENCRYPT_CASE) + + default: + return TPM_RC_SYMMETRIC; + } + switch(mode) + { +#if ALG_CTR + case TPM_ALG_CTR: + for(; dSize > 0; dSize -= blockSize) + { + // Encrypt the current value of the IV(counter) + ENCRYPT(&keySchedule, iv, tmp); + //increment the counter (counter is big-endian so start at end) + for(i = blockSize - 1; i >= 0; i--) + if((iv[i] += 1) != 0) + break; + // XOR the encrypted counter value with input and put into output + pT = tmp; + for(i = (dSize < blockSize) ? dSize : blockSize; i > 0; i--) + *dOut++ = *dIn++ ^ *pT++; + } + break; +#endif +#if ALG_OFB + case TPM_ALG_OFB: + // This is written so that dIn and dOut may be the same + for(; dSize > 0; dSize -= blockSize) + { + // Encrypt the current value of the "IV" + ENCRYPT(&keySchedule, iv, iv); + // XOR the encrypted IV into dIn to create the cipher text (dOut) + pIv = iv; + for(i = (dSize < blockSize) ? dSize : blockSize; i > 0; i--) + *dOut++ = (*pIv++ ^ *dIn++); + } + break; +#endif +#if ALG_CBC + case TPM_ALG_CBC: + // For CBC the data size must be an even multiple of the + // cipher block size + if((dSize % blockSize) != 0) + return TPM_RC_SIZE; + // XOR the data block into the IV, encrypt the IV into the IV + // and then copy the IV to the output + for(; dSize > 0; dSize -= blockSize) + { + pIv = iv; + for(i = blockSize; i > 0; i--) + *pIv++ ^= *dIn++; + ENCRYPT(&keySchedule, iv, iv); + pIv = iv; + for(i = blockSize; i > 0; i--) + *dOut++ = *pIv++; + } + break; +#endif + // CFB is not optional + case TPM_ALG_CFB: + // Encrypt the IV into the IV, XOR in the data, and copy to output + for(; dSize > 0; dSize -= blockSize) + { + // Encrypt the current value of the IV + ENCRYPT(&keySchedule, iv, iv); + pIv = iv; + for(i = (int)(dSize < blockSize) ? dSize : blockSize; i > 0; i--) + // XOR the data into the IV to create the cipher text + // and put into the output + *dOut++ = *pIv++ ^= *dIn++; + } + // If the inner loop (i loop) was smaller than blockSize, then dSize + // would have been smaller than blockSize and it is now negative. If + // it is negative, then it indicates how many bytes are needed to pad + // out the IV for the next round. + for(; dSize < 0; dSize++) + *pIv++ = 0; + break; +#if ALG_ECB + case TPM_ALG_ECB: + // For ECB the data size must be an even multiple of the + // cipher block size + if((dSize % blockSize) != 0) + return TPM_RC_SIZE; + // Encrypt the input block to the output block + for(; dSize > 0; dSize -= blockSize) + { + ENCRYPT(&keySchedule, dIn, dOut); + dIn = &dIn[blockSize]; + dOut = &dOut[blockSize]; + } + break; +#endif + default: + return TPM_RC_FAILURE; + } + return TPM_RC_SUCCESS; +} +/* 10.2.20.5.1 CryptSymmetricDecrypt() */ +/* This function performs symmetric decryption based on the mode. */ +/* Error Returns Meaning */ +/* TPM_RC_FAILURE A fatal error */ +/* TPM_RCS_SIZE dSize is not a multiple of the block size for an algorithm that requires it */ +LIB_EXPORT TPM_RC +CryptSymmetricDecrypt( + BYTE *dOut, // OUT: decrypted data + TPM_ALG_ID algorithm, // IN: the symmetric algorithm + UINT16 keySizeInBits, // IN: key size in bits + const BYTE *key, // IN: key buffer. The size of this buffer + // in bytes is (keySizeInBits + 7) / 8 + TPM2B_IV *ivInOut, // IN/OUT: IV for decryption. + TPM_ALG_ID mode, // IN: Mode to use + INT32 dSize, // IN: data size (may need to be a + // multiple of the blockSize) + const BYTE *dIn // IN: data buffer + ) +{ + BYTE *pIv; + int i; + BYTE tmp[MAX_SYM_BLOCK_SIZE]; + BYTE *pT; + tpmCryptKeySchedule_t keySchedule; + INT16 blockSize; + BYTE *iv; + TpmCryptSetSymKeyCall_t encrypt; + TpmCryptSetSymKeyCall_t decrypt; + BYTE defaultIv[MAX_SYM_BLOCK_SIZE] = {0}; + + memset(&keySchedule, 0, sizeof(keySchedule)); // libtpms added; coverity + // These are used but the compiler can't tell because they are initialized + // in case statements and it can't tell if they are always initialized + // when needed, so... Comment these out if the compiler can tell or doesn't + // care that these are initialized before use. + encrypt = NULL; + decrypt = NULL; + pAssert(dOut != NULL && key != NULL && dIn != NULL); + if(dSize == 0) + return TPM_RC_SUCCESS; + TEST(algorithm); + blockSize = CryptGetSymmetricBlockSize(algorithm, keySizeInBits); + if(blockSize == 0) + return TPM_RC_FAILURE; + // If the iv is provided, then it is expected to be block sized. In some cases, + // the caller is providing an array of 0's that is equal to [MAX_SYM_BLOCK_SIZE] + // with no knowledge of the actual block size. This function will set it. + if((ivInOut != NULL) && (mode != TPM_ALG_ECB)) + { + ivInOut->t.size = blockSize; + iv = ivInOut->t.buffer; + } + else + iv = defaultIv; + pIv = iv; + // Use the mode to select the key schedule to create. Encrypt always uses the + // encryption schedule. Depending on the mode, decryption might use either + // the decryption or encryption schedule. + switch(mode) + { +#if ALG_CBC || ALG_ECB + case TPM_ALG_CBC: // decrypt = decrypt + case TPM_ALG_ECB: + // For ECB and CBC, the data size must be an even multiple of the + // cipher block size + if((dSize % blockSize) != 0) + return TPM_RC_SIZE; + switch (algorithm) + { + FOR_EACH_SYM(DECRYPT_CASE) + default: + return TPM_RC_SYMMETRIC; + } + break; +#endif + default: + // For the remaining stream ciphers, use encryption to decrypt + switch (algorithm) + { + FOR_EACH_SYM(ENCRYPT_CASE) + default: + return TPM_RC_SYMMETRIC; + } + } + // Now do the mode-dependent decryption + switch(mode) + { +#if ALG_CBC + case TPM_ALG_CBC: + // Copy the input data to a temp buffer, decrypt the buffer into the + // output, XOR in the IV, and copy the temp buffer to the IV and repeat. + for(; dSize > 0; dSize -= blockSize) + { + pT = tmp; + for(i = blockSize; i > 0; i--) + *pT++ = *dIn++; + DECRYPT(&keySchedule, tmp, dOut); + pIv = iv; + pT = tmp; + for(i = blockSize; i > 0; i--) + { + *dOut++ ^= *pIv; + *pIv++ = *pT++; + } + } + break; +#endif + case TPM_ALG_CFB: + for(; dSize > 0; dSize -= blockSize) + { + // Encrypt the IV into the temp buffer + ENCRYPT(&keySchedule, iv, tmp); + pT = tmp; + pIv = iv; + for(i = (dSize < blockSize) ? dSize : blockSize; i > 0; i--) + // Copy the current cipher text to IV, XOR + // with the temp buffer and put into the output + *dOut++ = *pT++ ^ (*pIv++ = *dIn++); + } + // If the inner loop (i loop) was smaller than blockSize, then dSize + // would have been smaller than blockSize and it is now negative + // If it is negative, then it indicates how may fill bytes + // are needed to pad out the IV for the next round. + for(; dSize < 0; dSize++) + *pIv++ = 0; + break; +#if ALG_CTR + case TPM_ALG_CTR: + for(; dSize > 0; dSize -= blockSize) + { + // Encrypt the current value of the IV(counter) + ENCRYPT(&keySchedule, iv, tmp); + //increment the counter (counter is big-endian so start at end) + for(i = blockSize - 1; i >= 0; i--) + if((iv[i] += 1) != 0) + break; + // XOR the encrypted counter value with input and put into output + pT = tmp; + for(i = (dSize < blockSize) ? dSize : blockSize; i > 0; i--) + *dOut++ = *dIn++ ^ *pT++; + } + break; +#endif +#if ALG_ECB + case TPM_ALG_ECB: + for(; dSize > 0; dSize -= blockSize) + { + DECRYPT(&keySchedule, dIn, dOut); + dIn = &dIn[blockSize]; + dOut = &dOut[blockSize]; + } + break; +#endif +#if ALG_OFB + case TPM_ALG_OFB: + // This is written so that dIn and dOut may be the same + for(; dSize > 0; dSize -= blockSize) + { + // Encrypt the current value of the "IV" + ENCRYPT(&keySchedule, iv, iv); + // XOR the encrypted IV into dIn to create the cipher text (dOut) + pIv = iv; + for(i = (dSize < blockSize) ? dSize : blockSize; i > 0; i--) + *dOut++ = (*pIv++ ^ *dIn++); + } + break; +#endif + default: + return TPM_RC_FAILURE; + } + return TPM_RC_SUCCESS; +} + +#else // libtpms added begin + +#if ALG_TDES && ALG_CTR +// Emulated TDES Counter mode since OpenSSL does not implement it +static void TDES_CTR(const BYTE *key, // IN + INT32 keySizeInBits, // IN + INT32 dSize, // IN + const BYTE *dIn, // IN + BYTE *iv, // IN + BYTE *dOut, // OUT + INT16 blockSize // IN + ) +{ + tpmCryptKeySchedule_t keySchedule; + int i; + BYTE tmp[MAX_SYM_BLOCK_SIZE]; + BYTE *pT; + + TDES_set_encrypt_key(key, keySizeInBits, + (tpmKeyScheduleTDES *)&keySchedule.tdes); + + for(; dSize > 0; dSize -= blockSize) + { + // Encrypt the current value of the IV(counter) + TDES_encrypt(iv, tmp, (tpmKeyScheduleTDES *)&keySchedule.tdes); + //increment the counter (counter is big-endian so start at end) + for(i = blockSize - 1; i >= 0; i--) + if((iv[i] += 1) != 0) + break; + // XOR the encrypted counter value with input and put into output + pT = tmp; + for(i = (dSize < blockSize) ? dSize : blockSize; i > 0; i--) + *dOut++ = *dIn++ ^ *pT++; + } +} +#endif + +/* 10.2.20.5 Symmetric Encryption */ +/* This function performs symmetric encryption based on the mode. */ +/* Error Returns Meaning */ +/* TPM_RC_SIZE dSize is not a multiple of the block size for an algorithm that requires it */ +/* TPM_RC_FAILURE Fatal error */ +LIB_EXPORT TPM_RC +CryptSymmetricEncrypt( + BYTE *dOut, // OUT: + TPM_ALG_ID algorithm, // IN: the symmetric algorithm + UINT16 keySizeInBits, // IN: key size in bits + const BYTE *key, // IN: key buffer. The size of this buffer + // in bytes is (keySizeInBits + 7) / 8 + TPM2B_IV *ivInOut, // IN/OUT: IV for decryption. + TPM_ALG_ID mode, // IN: Mode to use + INT32 dSize, // IN: data size (may need to be a + // multiple of the blockSize) + const BYTE *dIn // IN: data buffer + ) +{ + INT16 blockSize; + BYTE *iv; + BYTE defaultIv[MAX_SYM_BLOCK_SIZE] = {0}; + evpfunc evpfn; + EVP_CIPHER_CTX *ctx = NULL; + int outlen1 = 0; + int outlen2 = 0; + BYTE *pOut = dOut; + BYTE *buffer = NULL; // for in-place encryption + UINT32 buffersize = 0; + BYTE keyToUse[MAX_SYM_KEY_BYTES]; + UINT16 keyToUseLen = (UINT16)sizeof(keyToUse); + TPM_RC retVal = TPM_RC_SUCCESS; + int ivLen; + + pAssert(dOut != NULL && key != NULL && dIn != NULL); + if(dSize == 0) + return TPM_RC_SUCCESS; + TEST(algorithm); + blockSize = CryptGetSymmetricBlockSize(algorithm, keySizeInBits); + if(blockSize == 0) + return TPM_RC_FAILURE; + // If the iv is provided, then it is expected to be block sized. In some cases, + // the caller is providing an array of 0's that is equal to [MAX_SYM_BLOCK_SIZE] + // with no knowledge of the actual block size. This function will set it. + if((ivInOut != NULL) && (mode != TPM_ALG_ECB)) + { + ivInOut->t.size = blockSize; + iv = ivInOut->t.buffer; + } + else + iv = defaultIv; + + switch (mode) + { + case TPM_ALG_ECB: + case TPM_ALG_CBC: + // For ECB & CBC the data size must be an even multiple of the + // cipher block size + if((dSize % blockSize) != 0) + return TPM_RC_SIZE; + } + + evpfn = GetEVPCipher(algorithm, keySizeInBits, mode, key, + keyToUse, &keyToUseLen); + if (evpfn == NULL) + return TPM_RC_FAILURE; + + if (dIn == dOut) { + // in-place encryption; we use a temp buffer + buffersize = TPM2_ROUNDUP(dSize, blockSize); + buffer = malloc(buffersize); + if (buffer == NULL) + ERROR_RETURN(TPM_RC_FAILURE); + + pOut = buffer; + } + +#if ALG_TDES && ALG_CTR + if (algorithm == TPM_ALG_TDES && mode == TPM_ALG_CTR) { + TDES_CTR(keyToUse, keyToUseLen * 8, dSize, dIn, iv, pOut, blockSize); + outlen1 = dSize; + ERROR_RETURN(TPM_RC_SUCCESS); + } +#endif + + ctx = EVP_CIPHER_CTX_new(); + if (!ctx || + EVP_EncryptInit_ex(ctx, evpfn(), NULL, keyToUse, iv) != 1 || + EVP_CIPHER_CTX_set_padding(ctx, 0) != 1 || + EVP_EncryptUpdate(ctx, pOut, &outlen1, dIn, dSize) != 1) + ERROR_RETURN(TPM_RC_FAILURE); + + pAssert(outlen1 <= dSize || dSize >= outlen1 + blockSize); + + if (EVP_EncryptFinal_ex(ctx, pOut + outlen1, &outlen2) != 1) + ERROR_RETURN(TPM_RC_FAILURE); + + if (ivInOut) { + ivLen = EVP_CIPHER_CTX_iv_length(ctx); + if (ivLen < 0 || (size_t)ivLen > sizeof(ivInOut->t.buffer)) + ERROR_RETURN(TPM_RC_FAILURE); + + ivInOut->t.size = ivLen; + memcpy(ivInOut->t.buffer, EVP_CIPHER_CTX_iv(ctx), ivInOut->t.size); + } + Exit: + if (retVal == TPM_RC_SUCCESS && pOut != dOut) + memcpy(dOut, pOut, outlen1 + outlen2); + + clear_and_free(buffer, buffersize); + EVP_CIPHER_CTX_free(ctx); + + return retVal; +} + +/* 10.2.20.5.1 CryptSymmetricDecrypt() */ +/* This function performs symmetric decryption based on the mode. */ +/* Error Returns Meaning */ +/* TPM_RC_FAILURE A fatal error */ +/* TPM_RCS_SIZE dSize is not a multiple of the block size for an algorithm that requires it */ +LIB_EXPORT TPM_RC +CryptSymmetricDecrypt( + BYTE *dOut, // OUT: decrypted data + TPM_ALG_ID algorithm, // IN: the symmetric algorithm + UINT16 keySizeInBits, // IN: key size in bits + const BYTE *key, // IN: key buffer. The size of this buffer + // in bytes is (keySizeInBits + 7) / 8 + TPM2B_IV *ivInOut, // IN/OUT: IV for decryption. + TPM_ALG_ID mode, // IN: Mode to use + INT32 dSize, // IN: data size (may need to be a + // multiple of the blockSize) + const BYTE *dIn // IN: data buffer + ) +{ + INT16 blockSize; + BYTE *iv; + BYTE defaultIv[MAX_SYM_BLOCK_SIZE] = {0}; + evpfunc evpfn; + EVP_CIPHER_CTX *ctx = NULL; + int outlen1 = 0; + int outlen2 = 0; + BYTE *buffer; + UINT32 buffersize = 0; + BYTE keyToUse[MAX_SYM_KEY_BYTES]; + UINT16 keyToUseLen = (UINT16)sizeof(keyToUse); + TPM_RC retVal = TPM_RC_SUCCESS; + int ivLen; + + // These are used but the compiler can't tell because they are initialized + // in case statements and it can't tell if they are always initialized + // when needed, so... Comment these out if the compiler can tell or doesn't + // care that these are initialized before use. + pAssert(dOut != NULL && key != NULL && dIn != NULL); + if(dSize == 0) + return TPM_RC_SUCCESS; + TEST(algorithm); + blockSize = CryptGetSymmetricBlockSize(algorithm, keySizeInBits); + if(blockSize == 0) + return TPM_RC_FAILURE; + // If the iv is provided, then it is expected to be block sized. In some cases, + // the caller is providing an array of 0's that is equal to [MAX_SYM_BLOCK_SIZE] + // with no knowledge of the actual block size. This function will set it. + if((ivInOut != NULL) && (mode != TPM_ALG_ECB)) + { + ivInOut->t.size = blockSize; + iv = ivInOut->t.buffer; + } + else + iv = defaultIv; + + switch(mode) + { +#if ALG_CBC || ALG_ECB + case TPM_ALG_CBC: + case TPM_ALG_ECB: + // For ECB and CBC, the data size must be an even multiple of the + // cipher block size + if((dSize % blockSize) != 0) + return TPM_RC_SIZE; + break; +#endif + default: + break; + } + + evpfn = GetEVPCipher(algorithm, keySizeInBits, mode, key, + keyToUse, &keyToUseLen); + if (evpfn == NULL) + return TPM_RC_FAILURE; + + /* a buffer with a 'safety margin' for EVP_DecryptUpdate */ + buffersize = TPM2_ROUNDUP(dSize + blockSize, blockSize); + buffer = malloc(buffersize); + if (buffer == NULL) + ERROR_RETURN(TPM_RC_FAILURE); + +#if ALG_TDES && ALG_CTR + if (algorithm == TPM_ALG_TDES && mode == TPM_ALG_CTR) { + TDES_CTR(keyToUse, keyToUseLen * 8, dSize, dIn, iv, buffer, blockSize); + outlen1 = dSize; + ERROR_RETURN(TPM_RC_SUCCESS); + } +#endif + + ctx = EVP_CIPHER_CTX_new(); + if (!ctx || + EVP_DecryptInit_ex(ctx, evpfn(), NULL, keyToUse, iv) != 1 || + EVP_CIPHER_CTX_set_padding(ctx, 0) != 1 || + EVP_DecryptUpdate(ctx, buffer, &outlen1, dIn, dSize) != 1) + ERROR_RETURN(TPM_RC_FAILURE); + + pAssert((int)buffersize >= outlen1); + + if ((int)buffersize <= outlen1 /* coverity */ || + EVP_DecryptFinal(ctx, &buffer[outlen1], &outlen2) != 1) + ERROR_RETURN(TPM_RC_FAILURE); + + pAssert((int)buffersize >= outlen1 + outlen2); + + if (ivInOut) { + ivLen = EVP_CIPHER_CTX_iv_length(ctx); + if (ivLen < 0 || (size_t)ivLen > sizeof(ivInOut->t.buffer)) + ERROR_RETURN(TPM_RC_FAILURE); + + ivInOut->t.size = ivLen; + memcpy(ivInOut->t.buffer, EVP_CIPHER_CTX_iv(ctx), ivInOut->t.size); + } + + Exit: + if (retVal == TPM_RC_SUCCESS) { + pAssert(dSize >= outlen1 + outlen2); + memcpy(dOut, buffer, outlen1 + outlen2); + } + + clear_and_free(buffer, buffersize); + EVP_CIPHER_CTX_free(ctx); + + return retVal; +} + +#endif // libtpms added end + +/* 10.2.20.5.2 CryptSymKeyValidate() */ +/* Validate that a provided symmetric key meets the requirements of the TPM */ +/* Error Returns Meaning */ +/* TPM_RC_KEY_SIZE Key size specifiers do not match */ +/* TPM_RC_KEY Key is not allowed */ +TPM_RC +CryptSymKeyValidate( + TPMT_SYM_DEF_OBJECT *symDef, + TPM2B_SYM_KEY *key + ) +{ + if(key->t.size != BITS_TO_BYTES(symDef->keyBits.sym)) + return TPM_RCS_KEY_SIZE; +#if ALG_TDES + if(symDef->algorithm == TPM_ALG_TDES && !CryptDesValidateKey(key)) + return TPM_RCS_KEY; +#endif // TPM_ALG_TDES + return TPM_RC_SUCCESS; +} |