diff options
Diffstat (limited to 'src/tpm12/tpm_crypto.c')
-rw-r--r-- | src/tpm12/tpm_crypto.c | 3088 |
1 files changed, 3088 insertions, 0 deletions
diff --git a/src/tpm12/tpm_crypto.c b/src/tpm12/tpm_crypto.c new file mode 100644 index 0000000..9fb4fce --- /dev/null +++ b/src/tpm12/tpm_crypto.c @@ -0,0 +1,3088 @@ +/********************************************************************************/ +/* */ +/* Platform Dependent Crypto */ +/* Written by Ken Goldman */ +/* IBM Thomas J. Watson Research Center */ +/* $Id: tpm_crypto.c 4767 2017-07-27 23:06:32Z kgoldman $ */ +/* */ +/* (c) Copyright IBM Corporation 2006, 2010. */ +/* */ +/* All rights reserved. */ +/* */ +/* 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. */ +/* */ +/* Neither the names of the IBM Corporation nor the names of its */ +/* contributors may be used to endorse or promote products derived from */ +/* this software without specific prior written permission. */ +/* */ +/* 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. */ +/********************************************************************************/ + +/* This is the openSSL implementation */ + +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> + +#include <openssl/crypto.h> +#include <openssl/rand.h> +#include <openssl/sha.h> +#include <openssl/engine.h> + +#include "tpm_cryptoh.h" +#include "tpm_debug.h" +#include "tpm_error.h" +#include "tpm_key.h" +#include "tpm_io.h" +#include "tpm_load.h" +#include "tpm_memory.h" +#include "tpm_process.h" +#include "tpm_types.h" + +#include "tpm_crypto.h" + +#include "tpm_openssl_helpers.h" // libtpms added + +/* The TPM OAEP encoding parameter */ +static const unsigned char tpm_oaep_pad_str[] = { 'T', 'C', 'P', 'A' }; + + +/* local prototypes */ + +static void TPM_OpenSSL_PrintError(void); + +static TPM_RESULT TPM_RSAGeneratePublicToken(RSA **rsa_pub_key, + unsigned char *narr, + uint32_t nbytes, + unsigned char *earr, + uint32_t ebytes); +static TPM_RESULT TPM_RSAGeneratePrivateToken(RSA **rsa_pri_key, + unsigned char *narr, + uint32_t nbytes, + unsigned char *earr, + uint32_t ebytes, + unsigned char *darr, + uint32_t dbytes); +static TPM_RESULT TPM_RSASignSHA1(unsigned char *signature, + unsigned int *signature_length, + const unsigned char *message, + size_t message_size, + RSA *rsa_pri_key); +static TPM_RESULT TPM_RSASignDER(unsigned char *signature, + unsigned int *signature_length, + const unsigned char *message, + size_t message_size, + RSA *rsa_pri_key); + +static TPM_RESULT TPM_BN_CTX_new(BN_CTX **ctx); + + + +/* TPM_SYMMETRIC_KEY_DATA is a crypto library platform dependent symmetric key structure + */ +#ifdef TPM_DES + +/* local prototype and structure for DES */ + +#include <openssl/des.h> + +/* DES requires data lengths that are a multiple of the block size */ +#define TPM_DES_BLOCK_SIZE 8 + +typedef struct tdTPM_SYMMETRIC_KEY_DATA { + TPM_TAG tag; + TPM_BOOL valid; + BYTE fill; + DES_cblock des_cblock1; + DES_cblock des_cblock2; + DES_cblock des_cblock3; +} TPM_SYMMETRIC_KEY_DATA; + +static TPM_RESULT TPM_SymmetricKeyData_Crypt(unsigned char *data_out, + const unsigned char *data_in, + uint32_t length, + TPM_SYMMETRIC_KEY_DATA *tpm_symmetric_key_data, + int enc, + TPM_RESULT error); + +#endif + +#ifdef TPM_AES + +/* local prototype and structure for AES */ + +#include <openssl/aes.h> + +#if defined(__OpenBSD__) + # define OPENSSL_OLD_API +#else + #if OPENSSL_VERSION_NUMBER < 0x10100000 + #define OPENSSL_OLD_API + #endif +#endif + +/* AES requires data lengths that are a multiple of the block size */ +#define TPM_AES_BITS 128 +/* The AES block size is always 16 bytes */ +#define TPM_AES_BLOCK_SIZE 16 + +/* Since the AES key is often derived by truncating the session shared secret, test that it's not + too large +*/ + +#if (TPM_AES_BLOCK_SIZE > TPM_SECRET_SIZE) +#error TPM_AES_BLOCK_SIZE larger than TPM_SECRET_SIZE +#endif + +/* The AES initial CTR value is derived from a nonce. */ + +#if (TPM_AES_BLOCK_SIZE > TPM_NONCE_SIZE) +#error TPM_AES_BLOCK_SIZE larger than TPM_NONCE_SIZE +#endif + +typedef struct tdTPM_SYMMETRIC_KEY_DATA { + TPM_TAG tag; + TPM_BOOL valid; + TPM_BOOL fill; + unsigned char userKey[TPM_AES_BLOCK_SIZE]; + /* For performance, generate these once from userKey */ + AES_KEY aes_enc_key; + AES_KEY aes_dec_key; +} TPM_SYMMETRIC_KEY_DATA; + +static TPM_RESULT TPM_SymmetricKeyData_SetKeys(TPM_SYMMETRIC_KEY_DATA *tpm_symmetric_key_data); +static TPM_RESULT TPM_SymmetricKeyData_SetKey(TPM_SYMMETRIC_KEY_DATA *tpm_symmetric_key_data, + const unsigned char *key_data, + uint32_t key_data_size); +static TPM_RESULT TPM_AES_ctr128_encrypt(unsigned char *data_out, + const unsigned char *data_in, + uint32_t data_size, + const AES_KEY *aes_enc_key, + unsigned char ctr[TPM_AES_BLOCK_SIZE]); + +#endif + +/* + Initialization function +*/ + +TPM_RESULT TPM_Crypto_Init() +{ + TPM_RESULT rc = 0; + + printf("TPM_Crypto_Init: OpenSSL library %08lx\n", (unsigned long)OPENSSL_VERSION_NUMBER); + /* sanity check that the SHA1 context handling remains portable */ + if (rc == 0) { + if ((sizeof(SHA_LONG) != sizeof(uint32_t)) || + (sizeof(unsigned int) != sizeof(uint32_t)) || + (sizeof(SHA_CTX) != (sizeof(uint32_t) * (8 + SHA_LBLOCK)))) { + printf("TPM_Crypto_Init: Error(fatal), SHA_CTX has unexpected structure\n"); + rc = TPM_FAIL; + } + } + return rc; +} + +/* TPM_Crypto_TestSpecific() performs any library specific tests + + For OpenSSL + */ + +TPM_RESULT TPM_Crypto_TestSpecific() +{ + TPM_RESULT rc = 0; + + /* Saving the SHA-1 context is fragile code, so test at startup */ + void *context1; + void *context2; + unsigned char buffer1[] = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"; + unsigned char expect1[] = {0x84,0x98,0x3E,0x44,0x1C, + 0x3B,0xD2,0x6E,0xBA,0xAE, + 0x4A,0xA1,0xF9,0x51,0x29, + 0xE5,0xE5,0x46,0x70,0xF1}; + TPM_DIGEST actual; + int not_equal; + TPM_STORE_BUFFER sbuffer; + const unsigned char *stream; + uint32_t stream_size; + + printf(" TPM_Crypto_TestSpecific: Test 1 - SHA1 two parts\n"); + context1 = NULL; /* freed @1 */ + context2 = NULL; /* freed @2 */ + TPM_Sbuffer_Init(&sbuffer); /* freed @3 */ + + if (rc== 0) { + rc = TPM_Malloc((unsigned char **)&context1, sizeof(SHA_CTX)); /* freed @1 */ + } + /* digest the first part of the array */ + if (rc== 0) { + SHA1_Init(context1); + SHA1_Update(context1, buffer1, 16); + } + /* store the SHA1 context */ + if (rc== 0) { + rc = TPM_Sha1Context_Store(&sbuffer, context1); + } + /* load the SHA1 context */ + if (rc== 0) { + TPM_Sbuffer_Get(&sbuffer, &stream, &stream_size); + rc = TPM_Sha1Context_Load + (&context2, (unsigned char **)&stream, &stream_size); /* freed @2 */ + } + /* digest the rest of the array */ + if (rc== 0) { + SHA1_Update(context2, buffer1 + 16, sizeof(buffer1) - 17); + SHA1_Final(actual, context2); + } + if (rc == 0) { + not_equal = memcmp(expect1, actual, TPM_DIGEST_SIZE); + if (not_equal) { + printf("TPM_Crypto_TestSpecific: Error in test 1\n"); + TPM_PrintFour("\texpect", expect1); + TPM_PrintFour("\tactual", actual); + rc = TPM_FAILEDSELFTEST; + } + } + free(context1); /* @1 */ + free(context2); /* @2 */ + TPM_Sbuffer_Delete(&sbuffer); /* @3 */ + return rc; +} + + + +/* + Random Number Functions +*/ + +/* TPM_Random() fills 'buffer' with 'bytes' bytes. + */ + +TPM_RESULT TPM_Random(BYTE *buffer, size_t bytes) +{ + TPM_RESULT rc = 0; + + printf(" TPM_Random: Requesting %lu bytes\n", (unsigned long)bytes); + + if (rc == 0) { + /* openSSL call */ + rc = RAND_bytes(buffer, bytes); + if (rc == 1) { /* OSSL success */ + rc = 0; + } + else { /* OSSL failure */ + printf("TPM_Random: Error (fatal) calling RAND_bytes()\n"); + rc = TPM_FAIL; + } + } + return rc; +} + +TPM_RESULT TPM_StirRandomCmd(TPM_SIZED_BUFFER *inData) +{ + TPM_RESULT rc = 0; + + printf(" TPM_StirRandomCmd:\n"); + if (rc == 0) { + /* NOTE: The TPM command does not give an entropy estimate. This assumes the best case */ + /* openSSL call */ + RAND_add(inData->buffer, /* buf mixed into PRNG state*/ + inData->size, /* number of bytes */ + inData->size); /* entropy, the lower bound of an estimate of how much randomness is + contained in buf */ + } + return rc; +} + +/* + RSA Functions +*/ + +/* Generate an RSA key pair. + + 'n', 'p', 'q', 'd' must be freed by the caller +*/ + +TPM_RESULT TPM_RSAGenerateKeyPair(unsigned char **n, /* public key - modulus */ + unsigned char **p, /* private key prime */ + unsigned char **q, /* private key prime */ + unsigned char **d, /* private key (private exponent) */ + int num_bits, /* key size in bits */ + const unsigned char *earr, /* public exponent as an array */ + uint32_t e_size) +{ + TPM_RESULT rc = 0; + RSA *rsa = NULL; + const BIGNUM *bnn = NULL; + BIGNUM *bne = NULL; + const BIGNUM *bnp = NULL; + const BIGNUM *bnq = NULL; + const BIGNUM *bnd = NULL; + uint32_t nbytes; + uint32_t pbytes; + uint32_t qbytes; + uint32_t dbytes; + + unsigned long e; + + /* initialize in case of error */ + printf(" TPM_RSAGenerateKeyPair:\n"); + *n = NULL; + *p = NULL; + *q = NULL; + *d = NULL; + + /* check that num_bits is a multiple of 16. If not, the primes p and q will not be a multiple of + 8 and will not fit well in a byte */ + if (rc == 0) { + if ((num_bits % 16) != 0) { + printf("TPM_RSAGenerateKeyPair: Error, num_bits %d is not a multiple of 16\n", + num_bits); + rc = TPM_BAD_KEY_PROPERTY; + } + } + /* convert the e array to an unsigned long */ + if (rc == 0) { + rc = TPM_LoadLong(&e, earr, e_size); + } + /* validate the public exponent against a list of legal values. Some values (e.g. even numbers) + will hang the key generator. */ + if (rc == 0) { + rc = TPM_RSA_exponent_verify(e); + } + if (rc == 0) { + rsa = RSA_new(); /* freed @1 */ + if (rsa == NULL) { + printf("TPM_RSAGenerateKeyPair: Error in RSA_new()\n"); + rc = TPM_SIZE; + } + } + if (rc == 0) { + rc = TPM_bin2bn((TPM_BIGNUM *)&bne, earr, e_size); /* freed @2 */ + } + if (rc == 0) { + printf(" TPM_RSAGenerateKeyPair: num_bits %d exponent %08lx\n", num_bits, e); + int irc = RSA_generate_key_ex(rsa, num_bits, bne, NULL); + if (irc != 1) { + printf("TPM_RSAGenerateKeyPair: Error calling RSA_generate_key_ex()\n"); + rc = TPM_BAD_KEY_PROPERTY; + } + } + if (rc == 0) { +#if defined OPENSSL_OLD_API + bnn = rsa->n; + bnp = rsa->p; + bnq = rsa->q; + bnd = rsa->d; +#else + /* currently, this function accepts NULL inputs, but it's not guaranteed by the + documentation */ + const BIGNUM *bnetmp = NULL; /* not needed */ + RSA_get0_key(rsa, &bnn, &bnetmp, &bnd); + RSA_get0_factors(rsa, &bnp, &bnq); +#endif + } + /* load n */ + if (rc == 0) { + rc = TPM_bn2binMalloc(n, &nbytes, (TPM_BIGNUM)bnn, num_bits/8); /* freed by caller */ + } + /* load p */ + if (rc == 0) { + rc = TPM_bn2binMalloc(p, &pbytes, (TPM_BIGNUM)bnp, num_bits/16); /* freed by caller */ + } + /* load q */ + if (rc == 0) { + rc = TPM_bn2binMalloc(q, &qbytes, (TPM_BIGNUM)bnq, num_bits/16); /* freed by caller */ + } + /* load d */ + if (rc == 0) { + rc = TPM_bn2binMalloc(d, &dbytes, (TPM_BIGNUM)bnd, num_bits/8); /* freed by caller */ + } + if (rc == 0) { + printf(" TPM_RSAGenerateKeyPair: length of n,p,q,d = %d / %d / %d / %d\n", + nbytes, pbytes, qbytes, dbytes); + } + if (rc != 0) { + free(*n); + free(*p); + free(*q); + free(*d); + *n = NULL; + *p = NULL; + *q = NULL; + *d = NULL; + } + if (rsa != NULL) { + RSA_free(rsa); /* @1 */ + } + if (bne != NULL) { + BN_free(bne); /* @2 */ + } + return rc; +} + +/* TPM_RSAGeneratePublicToken() generates an RSA key token from n and e + */ + +static TPM_RESULT TPM_RSAGeneratePublicToken(RSA **rsa_pub_key, /* freed by caller */ + unsigned char *narr, /* public modulus */ + uint32_t nbytes, + unsigned char *earr, /* public exponent */ + uint32_t ebytes) +{ + TPM_RESULT rc = 0; + BIGNUM * n = NULL; + BIGNUM * e = NULL; + + /* sanity check for the free */ + if (rc == 0) { + if (*rsa_pub_key != NULL) { + printf("TPM_RSAGeneratePublicToken: Error (fatal), token %p should be NULL\n", + *rsa_pub_key ); + rc = TPM_FAIL; + + } + } + /* construct the OpenSSL private key object */ + if (rc == 0) { + *rsa_pub_key = RSA_new(); /* freed by caller */ + if (*rsa_pub_key == NULL) { + printf("TPM_RSAGeneratePublicToken: Error in RSA_new()\n"); + rc = TPM_SIZE; + } + } + if (rc == 0) { + rc = TPM_bin2bn((TPM_BIGNUM *)&n, narr, nbytes); /* freed by caller */ + } + if (rc == 0) { + rc = TPM_bin2bn((TPM_BIGNUM *)&e, earr, ebytes); /* freed by caller */ + } + if (rc == 0) { +#if defined OPENSSL_OLD_API + (*rsa_pub_key)->n = n; + (*rsa_pub_key)->e = e; + (*rsa_pub_key)->d = NULL; +#else + int irc = RSA_set0_key(*rsa_pub_key, n, e, NULL); + if (irc != 1) { + printf("TPM_RSAGeneratePublicToken: Error in RSA_set0_key()\n"); + rc = TPM_SIZE; + } +#endif + } + return rc; +} + +/* TPM_RSAGeneratePrivateToken() generates an RSA key token from n,e,d + */ + +static TPM_RESULT TPM_RSAGeneratePrivateToken(RSA **rsa_pri_key, /* freed by caller */ + unsigned char *narr, /* public modulus */ + uint32_t nbytes, + unsigned char *earr, /* public exponent */ + uint32_t ebytes, + unsigned char *darr, /* private exponent */ + uint32_t dbytes) +{ + TPM_RESULT rc = 0; + BIGNUM * n = NULL; + BIGNUM * e = NULL; + BIGNUM * d = NULL; + + /* sanity check for the free */ + if (rc == 0) { + if (*rsa_pri_key != NULL) { + printf("TPM_RSAGeneratePrivateToken: Error (fatal), token %p should be NULL\n", + *rsa_pri_key ); + rc = TPM_FAIL; + + } + } + /* construct the OpenSSL private key object */ + if (rc == 0) { + *rsa_pri_key = RSA_new(); /* freed by caller */ + if (*rsa_pri_key == NULL) { + printf("TPM_RSAGeneratePrivateToken: Error in RSA_new()\n"); + rc = TPM_SIZE; + } + } + if (rc == 0) { + rc = TPM_bin2bn((TPM_BIGNUM *)&n, narr, nbytes); /* freed by caller */ + } + if (rc == 0) { + rc = TPM_bin2bn((TPM_BIGNUM *)&e, earr, ebytes); /* freed by caller */ + } + if (rc == 0) { + rc = TPM_bin2bn((TPM_BIGNUM *)&d, darr, dbytes); /* freed by caller */ + } + if (rc == 0) { +#if defined OPENSSL_OLD_API + (*rsa_pri_key)->n = n; + (*rsa_pri_key)->e = e; + (*rsa_pri_key)->d = d; + BN_set_flags(d, BN_FLG_CONSTTIME); // d is private +#else + int irc = RSA_set0_key(*rsa_pri_key, n, e, d); + if (irc != 1) { + printf("TPM_RSAGeneratePrivateToken: Error in RSA_set0_key()\n"); + rc = TPM_SIZE; + } +#endif + } + return rc; +} + +#if !USE_OPENSSL_FUNCTIONS_RSA // libtpms added +/* TPM_RSAPrivateDecrypt() decrypts 'encrypt_data' using the private key 'n, e, d'. The OAEP + padding is removed and 'decrypt_data_length' bytes are moved to 'decrypt_data'. + + 'decrypt_data_length' is at most 'decrypt_data_size'. +*/ + +TPM_RESULT TPM_RSAPrivateDecrypt(unsigned char *decrypt_data, /* decrypted data */ + uint32_t *decrypt_data_length, /* length of data put into + decrypt_data */ + size_t decrypt_data_size, /* size of decrypt_data buffer */ + TPM_ENC_SCHEME encScheme, /* encryption scheme */ + unsigned char *encrypt_data, /* encrypted data */ + uint32_t encrypt_data_size, + unsigned char *narr, /* public modulus */ + uint32_t nbytes, + unsigned char *earr, /* public exponent */ + uint32_t ebytes, + unsigned char *darr, /* private exponent */ + uint32_t dbytes) +{ + TPM_RESULT rc = 0; + int irc; + RSA * rsa_pri_key = NULL; /* freed @1 */ + + unsigned char *padded_data = NULL; + int padded_data_size = 0; + + printf(" TPM_RSAPrivateDecrypt:\n"); + /* construct the OpenSSL private key object */ + if (rc == 0) { + rc = TPM_RSAGeneratePrivateToken(&rsa_pri_key, /* freed @1 */ + narr, /* public modulus */ + nbytes, + earr, /* public exponent */ + ebytes, + darr, /* private exponent */ + dbytes); + } + /* intermediate buffer for the decrypted but still padded data */ + if (rc == 0) { + /* the size of the decrypted data is guaranteed to be less than this */ + padded_data_size = RSA_size(rsa_pri_key); + rc = TPM_Malloc(&padded_data, padded_data_size); + } + if (rc == 0) { + /* decrypt with private key. Must decrypt first and then remove padding because the decrypt + call cannot specify an encoding parameter */ + /* returns the size of the encrypted data. On error, -1 is returned */ + irc = RSA_private_decrypt(encrypt_data_size, /* length */ + encrypt_data, /* from - the encrypted data */ + padded_data, /* to - the decrypted but padded data */ + rsa_pri_key, /* key */ + RSA_NO_PADDING); /* padding */ + if (irc < 0) { + printf("TPM_RSAPrivateDecrypt: Error in RSA_private_decrypt()\n"); + rc = TPM_DECRYPT_ERROR; + } + } + if (rc == 0) { + printf(" TPM_RSAPrivateDecrypt: RSA_private_decrypt() success\n"); + printf(" TPM_RSAPrivateDecrypt: Padded data size %u\n", padded_data_size); + TPM_PrintFour(" TPM_RSAPrivateDecrypt: Decrypt padded data", padded_data); + if (encScheme == TPM_ES_RSAESOAEP_SHA1_MGF1) { + /* openSSL expects the padded data to skip the first 0x00 byte, since it expects the + padded data to come from a bignum via bn2bin. */ + irc = RSA_padding_check_PKCS1_OAEP(decrypt_data, /* to */ + decrypt_data_size, /* to length */ + padded_data + 1, /* from */ + padded_data_size - 1, /* from length */ + encrypt_data_size, /* rsa_len */ + tpm_oaep_pad_str, /* encoding parameter */ + sizeof(tpm_oaep_pad_str) /* encoding parameter length + */ + ); + if (irc < 0) { + printf("TPM_RSAPrivateDecrypt: Error in RSA_padding_check_PKCS1_OAEP()\n"); + rc = TPM_DECRYPT_ERROR; + } + } + else if (encScheme == TPM_ES_RSAESPKCSv15) { + irc = RSA_padding_check_PKCS1_type_2(decrypt_data, /* to */ + decrypt_data_size, /* to length */ + padded_data + 1, /* from */ + padded_data_size - 1, /* from length */ + encrypt_data_size /* rsa_len */ + ); + if (irc < 0) { + printf("TPM_RSAPrivateDecrypt: Error in RSA_padding_check_PKCS1_type_2()\n"); + rc = TPM_DECRYPT_ERROR; + } + } + else { + printf("TPM_RSAPrivateDecrypt: Error, unknown encryption scheme %04x\n", encScheme); + rc = TPM_INAPPROPRIATE_ENC; + } + } + if (rc == 0) { + *decrypt_data_length = irc; + printf(" TPM_RSAPrivateDecrypt: RSA_padding_check_PKCS1_OAEP() recovered %d bytes\n", irc); + TPM_PrintFourLimit(" TPM_RSAPrivateDecrypt: Decrypt data", decrypt_data, *decrypt_data_length); + } + if (rsa_pri_key != NULL) { + RSA_free(rsa_pri_key); /* @1 */ + } + free(padded_data); /* @2 */ + return rc; +} + +#else // libtpms added begin + +TPM_RESULT TPM_RSAPrivateDecrypt(unsigned char *decrypt_data, /* decrypted data */ + uint32_t *decrypt_data_length, /* length of data put into + decrypt_data */ + size_t decrypt_data_size, /* size of decrypt_data buffer */ + TPM_ENC_SCHEME encScheme, /* encryption scheme */ + unsigned char *encrypt_data, /* encrypted data */ + uint32_t encrypt_data_size, + unsigned char *narr, /* public modulus */ + uint32_t nbytes, + unsigned char *earr, /* public exponent */ + uint32_t ebytes, + unsigned char *darr, /* private exponent */ + uint32_t dbytes) +{ + TPM_RESULT rc = 0; + EVP_PKEY *pkey = NULL; + EVP_PKEY_CTX *ctx = NULL; + const EVP_MD *md = NULL; + unsigned char *label = NULL; + size_t outlen; + unsigned char buffer[(TPM_RSA_KEY_LENGTH_MAX + 7) / 8]; + + printf(" TPM_RSAPrivateDecrypt:\n"); + /* construct the OpenSSL private key object */ + if (rc == 0) { + rc = TPM_RSAGenerateEVP_PKEY(&pkey, /* freed @1 */ + narr, /* public modulus */ + nbytes, + earr, /* public exponent */ + ebytes, + darr, /* private exponent */ + dbytes); + } + + if (rc == 0) { + ctx = EVP_PKEY_CTX_new(pkey, NULL); + if (ctx == 0) { + printf("TPM_RSAPrivateDecrypt: Error in EVP_PKEY_CTX_new()\n"); + rc = TPM_FAIL; + } + } + if (rc == 0) { + if (EVP_PKEY_decrypt_init(ctx) <= 0) { + printf("TPM_RSAPrivateDecrypt: Error in EVP_PKEY_decrypt_init()\n"); + rc = TPM_FAIL; + } + } + + if (rc == 0) { + switch (encScheme) { + case TPM_ES_RSAESOAEP_SHA1_MGF1: + if (rc == 0) { + md = EVP_get_digestbyname("sha1"); + if (md == NULL || + EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0 || + EVP_PKEY_CTX_set_rsa_oaep_md(ctx, md) <= 0) { + printf("TPM_RSAPrivateDecrypt: Error in setting up decrypt context for TPM_ES_RSAESOAEP_SHA1_MGF\n"); + rc = TPM_FAIL; + } + } + if (rc == 0) { + rc = TPM_Malloc(&label, sizeof(tpm_oaep_pad_str)); + if (rc) { + printf("TPM_RSAPrivateDecrypt: TPM_Malloc failed\n"); + } + } + if (rc == 0) { + memcpy(label, tpm_oaep_pad_str, sizeof(tpm_oaep_pad_str)); + if (EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, label, sizeof(tpm_oaep_pad_str)) <= 0) { + printf("TPM_RSAPrivateDecrypt: EVP_PKEY_CTX_set0_rsa_oaep_label() failed\n"); + rc = TPM_FAIL; + } + if (rc == 0) { + label = NULL; + } + } + break; + case TPM_ES_RSAESPKCSv15: + if (rc == 0) { + if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0) { + printf("TPM_RSAPrivateDecrypt: Error in setting up decrypt context for TPM_ES_RSAESPKCSv15\n"); + rc = TPM_FAIL; + } + } + break; + default: + if (rc == 0) { + printf("TPM_RSAPrivateDecrypt: Error, unknown encryption scheme %04x\n", encScheme); + rc = TPM_INAPPROPRIATE_ENC; + } + } + } + + if (rc == 0) { + outlen = sizeof(buffer); + if (EVP_PKEY_decrypt(ctx, buffer, &outlen, + encrypt_data, encrypt_data_size) <= 0) { + printf("TPM_RSAPrivateDecrypt: EVP_PKEY_decrypt failed\n"); + rc = TPM_DECRYPT_ERROR; + } + if (rc == 0) { + if (outlen > decrypt_data_size) { + printf("TPM_RSAPrivateDecrypt: Error, decrypt_data_size %u too small for message size %u\n", + decrypt_data_size, outlen); + rc = TPM_DECRYPT_ERROR; + } + } + if (rc == 0) { + *decrypt_data_length = (uint32_t)outlen; + memcpy(decrypt_data, buffer, outlen); + TPM_PrintFourLimit(" TPM_RSAPrivateDecrypt: Decrypt data", decrypt_data, *decrypt_data_length); + } + } + + EVP_PKEY_free(pkey); + EVP_PKEY_CTX_free(ctx); + TPM_Free(label); + + return rc; +} + +#endif // libtpms added end + +/* TPM_RSAPublicEncrypt() pads 'decrypt_data' to 'encrypt_data_size' and encrypts using the public + key 'n, e'. +*/ + +#if !USE_OPENSSL_FUNCTIONS_RSA // libtpms added + +TPM_RESULT TPM_RSAPublicEncrypt(unsigned char* encrypt_data, /* encrypted data */ + size_t encrypt_data_size, /* size of encrypted data buffer */ + TPM_ENC_SCHEME encScheme, + const unsigned char *decrypt_data, /* decrypted data */ + size_t decrypt_data_size, + unsigned char *narr, /* public modulus */ + uint32_t nbytes, + unsigned char *earr, /* public exponent */ + uint32_t ebytes) +{ + TPM_RESULT rc = 0; + int irc; + RSA *rsa_pub_key = NULL; + unsigned char *padded_data = NULL; + + printf(" TPM_RSAPublicEncrypt: Input data size %lu\n", (unsigned long)decrypt_data_size); + /* intermediate buffer for the decrypted but still padded data */ + if (rc == 0) { + rc = TPM_Malloc(&padded_data, encrypt_data_size); /* freed @2 */ + } + /* construct the OpenSSL public key object */ + if (rc == 0) { + rc = TPM_RSAGeneratePublicToken(&rsa_pub_key, /* freed @1 */ + narr, /* public modulus */ + nbytes, + earr, /* public exponent */ + ebytes); + } + if (rc == 0) { + if (encScheme == TPM_ES_RSAESOAEP_SHA1_MGF1) { + irc = RSA_padding_add_PKCS1_OAEP(padded_data, /* to */ + encrypt_data_size, /* to length */ + decrypt_data, /* from */ + decrypt_data_size, /* from length */ + tpm_oaep_pad_str, /* encoding parameter */ + sizeof(tpm_oaep_pad_str) /* encoding parameter length + */ + ); + if (irc != 1) { + printf("TPM_RSAPublicEncrypt: Error in RSA_padding_add_PKCS1_OAEP()\n"); + rc = TPM_ENCRYPT_ERROR; + } + else { + printf(" TPM_RSAPublicEncrypt: RSA_padding_add_PKCS1_OAEP() success\n"); + } + } + else if (encScheme == TPM_ES_RSAESPKCSv15) { + irc = RSA_padding_add_PKCS1_type_2(padded_data, /* to */ + encrypt_data_size, /* to length */ + decrypt_data, /* from */ + decrypt_data_size); /* from length */ + if (irc != 1) { + printf("TPM_RSAPublicEncrypt: Error in RSA_padding_add_PKCS1_type_2()\n"); + rc = TPM_ENCRYPT_ERROR; + } + else { + printf(" TPM_RSAPublicEncrypt: RSA_padding_add_PKCS1_type_2() success\n"); + } + } + else { + printf("TPM_RSAPublicEncrypt: Error, unknown encryption scheme %04x\n", encScheme); + rc = TPM_INAPPROPRIATE_ENC; + } + } + if (rc == 0) { + printf(" TPM_RSAPublicEncrypt: Padded data size %lu\n", (unsigned long)encrypt_data_size); + TPM_PrintFour(" TPM_RSAPublicEncrypt: Padded data", padded_data); + /* encrypt with public key. Must pad first and then encrypt because the encrypt + call cannot specify an encoding parameter */ + /* returns the size of the encrypted data. On error, -1 is returned */ + irc = RSA_public_encrypt(encrypt_data_size, /* from length */ + padded_data, /* from - the clear text data */ + encrypt_data, /* the padded and encrypted data */ + rsa_pub_key, /* key */ + RSA_NO_PADDING); /* padding */ + if (irc < 0) { + printf("TPM_RSAPublicEncrypt: Error in RSA_public_encrypt()\n"); + rc = TPM_ENCRYPT_ERROR; + } + } + if (rc == 0) { + printf(" TPM_RSAPublicEncrypt: RSA_public_encrypt() success\n"); + } + if (rsa_pub_key != NULL) { + RSA_free(rsa_pub_key); /* @1 */ + } + free(padded_data); /* @2 */ + return rc; +} + +#else // libtpms added begin + +TPM_RESULT TPM_RSAPublicEncrypt(unsigned char* encrypt_data, /* encrypted data */ + size_t encrypt_data_size, /* size of encrypted data buffer */ + TPM_ENC_SCHEME encScheme, + const unsigned char *decrypt_data, /* decrypted data */ + size_t decrypt_data_size, + unsigned char *narr, /* public modulus */ + uint32_t nbytes, + unsigned char *earr, /* public exponent */ + uint32_t ebytes) +{ + TPM_RESULT rc = 0; + EVP_PKEY *pkey = NULL; + EVP_PKEY_CTX *ctx = NULL; + const EVP_MD *md = NULL; + unsigned char *label = NULL; + size_t outlen; + + printf(" TPM_RSAPublicEncrypt: Input data size %lu\n", (unsigned long)decrypt_data_size); + + /* construct the OpenSSL private key object */ + if (rc == 0) { + rc = TPM_RSAGenerateEVP_PKEY(&pkey, /* freed @1 */ + narr, /* public modulus */ + nbytes, + earr, /* public exponent */ + ebytes, + NULL, /* private exponent */ + 0); + } + + if (rc == 0) { + ctx = EVP_PKEY_CTX_new(pkey, NULL); + if (ctx == 0) { + printf("TPM_RSAqPrivateDecrypt: Error in EVP_PKEY_CTX_new()\n"); + rc = TPM_FAIL; + } + } + if (rc == 0) { + if (EVP_PKEY_encrypt_init(ctx) <= 0) { + printf("TPM_RSAPrivateDecrypt: Error in EVP_PKEY_decrypt_init()\n"); + rc = TPM_FAIL; + } + } + + if (rc == 0) { + switch (encScheme) { + case TPM_ES_RSAESOAEP_SHA1_MGF1: + if (rc == 0) { + md = EVP_get_digestbyname("sha1"); + if (md == NULL || + EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0 || + EVP_PKEY_CTX_set_rsa_oaep_md(ctx, md) <= 0) { + printf("TPM_RSAPublicEncrypt: Error in setting up encrypt context for TPM_ES_RSAESOAEP_SHA1_MGF\n"); + rc = TPM_FAIL; + } + } + if (rc == 0) { + rc = TPM_Malloc(&label, sizeof(tpm_oaep_pad_str)); + if (rc) { + printf("TPM_RSAPublicEncrypt: TPM_Malloc failed\n"); + } + } + if (rc == 0) { + memcpy(label, tpm_oaep_pad_str, sizeof(tpm_oaep_pad_str)); + if (EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, label, sizeof(tpm_oaep_pad_str)) <= 0) { + printf("TPM_RSAPublicEncrypt: EVP_PKEY_CTX_set0_rsa_oaep_label() failed\n"); + rc = TPM_FAIL; + } + if (rc == 0) { + label = NULL; + } + } + break; + case TPM_ES_RSAESPKCSv15: + if (rc == 0) { + if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0) { + printf("TPM_RSAPublicEncrypt: Error in setting up encrypt context for TPM_ES_RSAESPKCSv15\n"); + rc = TPM_FAIL; + } + } + break; + default: + if (rc == 0) { + printf("TPM_RSAPublicEncrypt: Error, unknown encryption scheme %04x\n", encScheme); + rc = TPM_INAPPROPRIATE_ENC; + } + } + } + + if (rc == 0) { + outlen = encrypt_data_size; + if (EVP_PKEY_encrypt(ctx, encrypt_data, &outlen, + decrypt_data, decrypt_data_size) <= 0) { + printf("TPM_RSAPublicEncrypt: EVP_PKEY_encrypt failed\n"); + rc = TPM_ENCRYPT_ERROR; + } + } + + EVP_PKEY_free(pkey); + EVP_PKEY_CTX_free(ctx); + TPM_Free(label); + + return rc; +} + +#endif // libtpms added end + +#if USE_FREEBL_CRYPTO_LIBRARY +/* TPM_RSAPublicEncryptRaw() does a raw public key operation without any padding. + +*/ + +TPM_RESULT TPM_RSAPublicEncryptRaw(unsigned char *encrypt_data, /* output */ + uint32_t encrypt_data_size, /* input, size of message buffer */ + unsigned char *decrypt_data, /* input */ + uint32_t decrypt_data_size, /* input, size of sig buffer */ + unsigned char *narr, /* public modulus */ + uint32_t nbytes, + unsigned char *earr, /* public exponent */ + uint32_t ebytes) +{ + TPM_RESULT rc = 0; + int irc; + RSA *rsa_pub_key = NULL; + + printf(" TPM_RSAPublicEncryptRaw:\n"); + /* the input data size must equal the public key size */ + if (rc == 0) { + if (decrypt_data_size != nbytes) { + printf("TPM_RSAPublicEncryptRaw: Error, decrypt data size is %u not %u\n", + decrypt_data_size, nbytes); + rc = TPM_ENCRYPT_ERROR; + } + } + /* the output data size must equal the public key size */ + if (rc == 0) { + if (encrypt_data_size != nbytes) { + printf("TPM_RSAPublicEncryptRaw: Error, Encrypted data size is %u not %u\n", + encrypt_data_size, nbytes); + rc = TPM_ENCRYPT_ERROR; + } + } + /* construct the OpenSSL public key object */ + if (rc == 0) { + rc = TPM_RSAGeneratePublicToken(&rsa_pub_key, /* freed @1 */ + narr, /* public modulus */ + nbytes, + earr, /* public exponent */ + ebytes); + } + if (rc == 0) { + TPM_PrintFour(" TPM_RSAPublicEncryptRaw: Public modulus", narr); + TPM_PrintAll(" TPM_RSAPublicEncryptRaw: Public exponent", earr, ebytes); + TPM_PrintFourLimit(" TPM_RSAPublicEncryptRaw: Decrypt data", decrypt_data, decrypt_data_size); + /* encrypt the decrypt_data */ + irc = RSA_public_encrypt(decrypt_data_size, /* from length */ + decrypt_data, /* from - the clear text data */ + encrypt_data, /* to - the padded and encrypted data */ + rsa_pub_key, /* key */ + RSA_NO_PADDING); /* padding */ + if (irc < 0) { + printf("TPM_RSAPublicEncryptRaw: Error in RSA_public_encrypt()\n"); + rc = TPM_ENCRYPT_ERROR; + } + } + if (rc == 0) { + TPM_PrintFour(" TPM_RSAPublicEncryptRaw: Encrypt data", encrypt_data); +#if 0 /* NOTE: Uncomment as a debug aid for signature verification */ + TPM_PrintAll(" TPM_RSAPublicEncryptRaw: Padded signed data", + encrypt_data, encrypt_data_size); +#endif + } + if (rsa_pub_key != NULL) { + RSA_free(rsa_pub_key); /* @1 */ + } + return rc; +} +#endif + +/* TPM_RSASign() signs 'message' of size 'message_size' using the private key n,e,d and the + signature scheme 'sigScheme' as specified in PKCS #1 v2.0. + + 'signature_length' bytes are moved to 'signature'. 'signature_length' is at most + 'signature_size'. signature must point to RSA_size(rsa) bytes of memory. +*/ +/* Note regarding conversion to EVP_PKEY_sign for the purpose of constant-timeness: + + - TPM_SS_RSASSAPKCS1v15_SHA1: + EVP_PKEY_sign() will call pkey_rsa_sign() which in turn will call RSA_sign() for + RSA_PKCS1_PADDING. This is the same as we do here. + - TPM_SS_RSASSAPKCS1v15_DER: + EVP_PKEY_sign() must not have a message digest since none of the padding choices calls + RSA_padding_add_PKCS1_type_1(), so we would have to do the padding again ourselves. +*/ + +TPM_RESULT TPM_RSASign(unsigned char *signature, /* output */ + unsigned int *signature_length, /* output, size of signature */ + unsigned int signature_size, /* input, size of signature buffer */ + TPM_SIG_SCHEME sigScheme, /* input, type of signature */ + const unsigned char *message, /* input */ + size_t message_size, /* input */ + unsigned char *narr, /* public modulus */ + uint32_t nbytes, + unsigned char *earr, /* public exponent */ + uint32_t ebytes, + unsigned char *darr, /* private exponent */ + uint32_t dbytes) +{ + TPM_RESULT rc = 0; + RSA * rsa_pri_key = NULL; /* freed @1 */ + unsigned int key_size; + + printf(" TPM_RSASign:\n"); + /* construct the OpenSSL private key object */ + if (rc == 0) { + rc = TPM_RSAGeneratePrivateToken(&rsa_pri_key, /* freed @1 */ + narr, /* public modulus */ + nbytes, + earr, /* public exponent */ + ebytes, + darr, /* private exponent */ + dbytes); + } + /* check the size of the output signature buffer */ + if (rc == 0) { + key_size = (unsigned int)RSA_size(rsa_pri_key); /* openSSL returns an int, but never + negative */ + if (signature_size < key_size) { + printf("TPM_RSASign: Error (fatal), buffer %u too small for signature %u\n", + signature_size, key_size); + rc = TPM_FAIL; /* internal error, should never occur */ + } + } + /* determine the signature scheme for the key */ + if (rc == 0) { + switch(sigScheme) { + case TPM_SS_NONE: + printf("TPM_RSASign: Error, sigScheme TPM_SS_NONE\n"); + rc = TPM_INVALID_KEYUSAGE; + break; + case TPM_SS_RSASSAPKCS1v15_SHA1: + case TPM_SS_RSASSAPKCS1v15_INFO: + rc = TPM_RSASignSHA1(signature, + signature_length, + message, + message_size, + rsa_pri_key); + break; + case TPM_SS_RSASSAPKCS1v15_DER: + rc = TPM_RSASignDER(signature, + signature_length, + message, + message_size, + rsa_pri_key); + break; + default: + printf("TPM_RSASign: Error, sigScheme %04hx unknown\n", sigScheme); + rc = TPM_INVALID_KEYUSAGE; + break; + } + } + if (rsa_pri_key != NULL) { + RSA_free(rsa_pri_key); /* @1 */ + } + return rc; +} + +/* TPM_RSASignSHA1() performs the following: + prepend a DER encoded algorithm ID + prepend a type 1 pad + encrypt with the private key +*/ + +static TPM_RESULT TPM_RSASignSHA1(unsigned char *signature, /* output */ + unsigned int *signature_length, /* output, size of signature */ + const unsigned char *message, /* input */ + size_t message_size, /* input */ + RSA *rsa_pri_key) /* signing private key */ +{ + TPM_RESULT rc = 0; + int irc; + + printf(" TPM_RSASignSHA1:\n"); + /* sanity check, SHA1 messages must be 20 bytes */ + if (rc == 0) { + if (message_size != TPM_DIGEST_SIZE) { + printf("TPM_RSASignSHA1: Error, message size %lu not TPM_DIGEST_SIZE\n", + (unsigned long)message_size ); + rc = TPM_DECRYPT_ERROR; + } + } + if (rc == 0) { + /* type NID_sha1, adds the algorithm identifier and type 1 pad */ + irc = RSA_sign(NID_sha1, /* type */ + message, message_size, + signature, signature_length, + rsa_pri_key); + /* RSA_sign() returns 1 on success, 0 otherwise. */ + if (irc != 1) { + printf("TPM_RSASignSHA1: Error in RSA_sign()\n"); + rc = TPM_DECRYPT_ERROR; + } + } + return rc; +} + +/* TPM_RSASignDER() performs the following: + + prepend a type 1 pad + encrypt with the private key + + The caller must check that the signature buffer is >= the key size. +*/ + +static TPM_RESULT TPM_RSASignDER(unsigned char *signature, /* output */ + unsigned int *signature_length, /* output, size of signature */ + const unsigned char *message, /* input */ + size_t message_size, /* input */ + RSA *rsa_pri_key) /* signing private key */ +{ + TPM_RESULT rc = 0; + int irc; + int key_size; + unsigned char *message_pad; + int int_sig_len; /* openSSL overloads RSA_private_decrypt return code */ + + printf(" TPM_RSASignDER:\n"); + message_pad = NULL; /* freed @1 */ + /* the padded message size is the same as the key size */ + if (rc == 0) { + key_size = RSA_size(rsa_pri_key); + if (key_size < 0) { + printf(" TPM_RSASignDER: Error (fatal), negative key size %d\n", key_size); + rc = TPM_FAIL; /* should never occur */ + } + } + /* allocate memory for the padded message */ + if (rc == 0) { + printf(" TPM_RSASignDER: key size %d\n", key_size); + rc = TPM_Malloc(&message_pad, key_size); /* freed @1 */ + } + /* PKCS1 type 1 pad the message */ + if (rc == 0) { + printf(" TPM_RSASignDER: Applying PKCS1 type 1 padding, size from %lu to %u\n", + (unsigned long)message_size, key_size); + TPM_PrintFourLimit(" TPM_RSASignDER: Input message", message, message_size); + /* This call checks that the message will fit with the padding */ + irc = RSA_padding_add_PKCS1_type_1(message_pad, /* to */ + key_size, + message, /* from */ + message_size); + if (irc != 1) { + printf("TPM_RSASignDER: Error padding message, size %lu key size %u\n", + (unsigned long)message_size, key_size); + rc = TPM_DECRYPT_ERROR; + } + } + /* raw sign with private key */ + if (rc == 0) { + printf(" TPM_RSASignDER: Encrypting with private key, message size %d\n", key_size); + TPM_PrintFour(" TPM_RSASignDER: Padded message", message_pad); + /* returns the size of the encrypted data. On error, -1 is returned */ + int_sig_len = RSA_private_encrypt(key_size, /* int flen */ + message_pad, /* unsigned char *from, */ + signature, /* unsigned char *to, */ + rsa_pri_key, /* RSA *rsa, */ + RSA_NO_PADDING); /* int padding); */ + if (int_sig_len >= 0) { + *signature_length = (unsigned int)int_sig_len; + } + else { + printf("TPM_RSASignDER: Error in RSA_private_encrypt()\n"); + rc = TPM_DECRYPT_ERROR; + } + } + if (rc == 0) { + TPM_PrintFour(" TPM_RSASignDER: signature", signature); + } + free(message_pad); /* @1 */ + return rc; +} + +/* TPM_RSAVerifySHA1() performs the following: + decrypt the signature + verify and remove type 1 pad + verify and remove DER encoded algorithm ID + verify the signature on the message +*/ + +TPM_RESULT TPM_RSAVerifySHA1(unsigned char *signature, /* input */ + unsigned int signature_size, /* input, size of signature + buffer */ + const unsigned char *message, /* input */ + uint32_t message_size, /* input */ + unsigned char *narr, /* public modulus */ + uint32_t nbytes, + unsigned char *earr, /* public exponent */ + uint32_t ebytes) +{ + TPM_RESULT rc = 0; + TPM_BOOL valid; + RSA * rsa_pub_key = NULL; + + printf(" TPM_RSAVerifySHA1:\n"); + /* construct the openSSL public key object from n and e */ + if (rc == 0) { + rc = TPM_RSAGeneratePublicToken(&rsa_pub_key, /* freed @1 */ + narr, /* public modulus */ + nbytes, + earr, /* public exponent */ + ebytes); + } + if (rc == 0) { + /* RSA_verify() returns 1 on successful verification, 0 otherwise. */ + valid = RSA_verify(NID_sha1, + message, message_size, + signature, signature_size, rsa_pub_key); + if (valid != 1) { + printf("TPM_RSAVerifySHA1: Error, bad signature\n"); + rc = TPM_BAD_SIGNATURE; + } + } + if (rsa_pub_key != NULL) { + RSA_free(rsa_pub_key); /* @1 */ + } + return rc; +} + +/* TPM_RSAGetPrivateKey recalculates q (2nd prime factor) and d (private key) from n (public key), e + (public exponent), and p (1st prime factor) + + The private key is validated by dividing the RSA product n by the RSA prime p and verifying that + the remainder is 0. + + 'qarr', darr' must be freed by the caller. +*/ + +TPM_RESULT TPM_RSAGetPrivateKey(uint32_t *qbytes, unsigned char **qarr, + uint32_t *dbytes, unsigned char **darr, + uint32_t nbytes, unsigned char *narr, + uint32_t ebytes, unsigned char *earr, + uint32_t pbytes, unsigned char *parr) +{ + TPM_RESULT rc = 0; /* TPM return code */ + int irc; /* openSSL return code */ + BIGNUM *brc; /* BIGNUM return code */ + + BIGNUM *n = NULL; /* public modulus */ + BIGNUM *e = NULL; /* public exponent */ + BIGNUM *d = NULL; /* private exponent */ + BIGNUM *p = NULL; /* secret prime factor */ + BIGNUM *q = NULL; /* secret prime factor */ + /* temporary variables */ + BN_CTX *ctx = NULL; /* freed @5, @6 */ + BIGNUM *r0 = NULL; /* n/p remainder */ + BIGNUM *r1 = NULL; + BIGNUM *r2 = NULL; + + /* set to NULL so caller can free after failure */ + printf(" TPM_RSAGetPrivateKey:\n"); + *qarr = NULL; + *darr = NULL; + /* check input parameters */ + if (rc == 0) { + if ((narr == NULL) || (nbytes == 0)) { + printf("TPM_RSAGetPrivateKey: Error, missing n\n"); + rc = TPM_BAD_PARAMETER; + } + } + /* check input parameters */ + if (rc == 0) { + if ((earr == NULL) || (ebytes == 0)) { + printf("TPM_RSAGetPrivateKey: Error, missing e\n"); + rc = TPM_BAD_PARAMETER; + } + } + /* check input parameters */ + if (rc == 0) { + if ((parr == NULL) || (pbytes == 0)) { + printf("TPM_RSAGetPrivateKey: Error, missing p\n"); + rc = TPM_BAD_PARAMETER; + } + } + /* get some temporary BIGNUM's for use in the calculations */ + if (rc == 0) { + rc = TPM_BN_CTX_new(&ctx); + } + if (rc == 0) { + BN_CTX_start(ctx); /* no return code */ + r0 = BN_CTX_get(ctx); /* sufficient to test return of last 'get' call */ + r1 = BN_CTX_get(ctx); + r2 = BN_CTX_get(ctx); + if (r2 == 0) { + printf("TPM_RSAGetPrivateKey: Error in BN_CTX_get()\n"); + TPM_OpenSSL_PrintError(); + rc = TPM_SIZE; + } + } + /* allocate BIGNUM's for q, d */ + if (rc == 0) { + rc = TPM_BN_new((TPM_BIGNUM *)&q); + } + if (rc == 0) { + rc = TPM_BN_new((TPM_BIGNUM *)&d); + } + /* convert n, e, p to BIGNUM's */ + if (rc == 0) { + rc = TPM_bin2bn((TPM_BIGNUM *)&n, narr, nbytes); /* freed @1 */ + } + if (rc == 0) { + rc = TPM_bin2bn((TPM_BIGNUM *)&e, earr, ebytes); /* freed @2 */ + } + if (rc == 0) { + rc = TPM_bin2bn((TPM_BIGNUM *)&p, parr, pbytes); /* freed @3 */ + if (p) + BN_set_flags(p, BN_FLG_CONSTTIME); // p is private + } + /* calculate q = n/p */ + if (rc == 0) { + irc = BN_div(q, r0, n, p, ctx); /* q = n/p freed @4 */ + if (irc != 1) { /* 1 is success */ + printf("TPM_RSAGetPrivateKey: Error in BN_div()\n"); + TPM_OpenSSL_PrintError(); + rc = TPM_BAD_PARAMETER; + } else + BN_set_flags(q, BN_FLG_CONSTTIME); // q is private + } + /* remainder should be zero */ + if (rc == 0) { + irc = BN_is_zero(r0); + if (irc != 1) { /* 1 is success */ + printf("TPM_RSAGetPrivateKey: Error in BN_is_zero()\n"); + rc = TPM_BAD_PARAMETER; + } + } + /* calculate r0 = p-1 */ + if (rc == 0) { + irc = BN_sub(r0, p, BN_value_one()); /* r0 = p-1 freed @6 */ + if (irc != 1) { /* 1 is success */ + printf("TPM_RSAGetPrivateKey: Error in BN_sub()\n"); + TPM_OpenSSL_PrintError(); + rc = TPM_BAD_PARAMETER; + } + } + /* calculate r1 = q-1 */ + if (rc == 0) { + irc = BN_sub(r1, q, BN_value_one()); /* freed @6 */ + if (irc != 1) { /* 1 is success */ + printf("TPM_RSAGetPrivateKey: Error in BN_sub()\n"); + TPM_OpenSSL_PrintError(); + rc = TPM_BAD_PARAMETER; + } + } + /* calculate r2 = (p-1)(q-1) */ + if (rc == 0) { + irc = BN_mul(r2, r0, r1, ctx); /* freed @6 */ + if (irc != 1) { /* 1 is success */ + printf("TPM_RSAGetPrivateKey: Error in BN_mul()\n"); + TPM_OpenSSL_PrintError(); + rc = TPM_BAD_PARAMETER; + } else + BN_set_flags(r2, BN_FLG_CONSTTIME); // r2 is private + } + /* calculate d = multiplicative inverse e mod r0 */ + if (rc == 0) { + brc = BN_mod_inverse(d, e, r2, ctx); /* feed @5 */ + if (brc == NULL) { + printf("TPM_RSAGetPrivateKey: Error in BN_mod_inverse()\n"); + TPM_OpenSSL_PrintError(); + rc = TPM_BAD_PARAMETER; + } + } + /* get q as an array */ + if (rc == 0) { + rc = TPM_bn2binMalloc(qarr, qbytes, (TPM_BIGNUM)q, pbytes); /* freed by caller */ + } + /* get d as an array */ + if (rc == 0) { + TPM_PrintFour(" TPM_RSAGetPrivateKey: Calculated q", *qarr); + rc = TPM_bn2binMalloc(darr, dbytes, (TPM_BIGNUM)d, nbytes); /* freed by caller */ + } + if (rc == 0) { + TPM_PrintFour(" TPM_RSAGetPrivateKey: Calculated d", *darr); + printf(" TPM_RSAGetPrivateKey: length of n,p,q,d = %u / %u / %u / %u\n", + nbytes, pbytes, *qbytes, *dbytes); + } + BN_free(n); /* @1 */ + BN_free(e); /* @2 */ + BN_free(p); /* @3 */ + BN_free(q); /* @4 */ + BN_free(d); /* @3 */ + BN_CTX_end(ctx); /* @5 */ + BN_CTX_free(ctx); /* @6 */ + return rc; +} + +/* + openSSL wrappers do error logging and transformation of openSSL errors to TPM type errors +*/ + +/* TPM_OpenSSL_PrintError() prints a detailed openSSL error trace. + +*/ + +static void TPM_OpenSSL_PrintError() +{ + /* openssl error printing */ + unsigned long error; + const char *file; + int line; + const char *data; + int flags; + + error = ERR_get_error_line_data(&file, &line, &data, &flags); + printf("\terror %08lx file %s line %d data %s flags %08x\n", + error, file, line, data, flags); + return; +} + +/* TPM_BN_num_bytes() wraps the openSSL function in a TPM error handler + + Returns number of bytes in the input +*/ + +TPM_RESULT TPM_BN_num_bytes(unsigned int *numBytes, TPM_BIGNUM bn_in) +{ + TPM_RESULT rc = 0; + int i; + BIGNUM *bn = (BIGNUM *)bn_in; + + i = BN_num_bytes(bn); + if (i >= 0) { + *numBytes = (unsigned int)i; + } + else { + printf("TPM_BN_num_bytes: Error (fatal), bytes in BIGNUM is negative\n"); + TPM_OpenSSL_PrintError(); + rc = TPM_FAIL; + } + return rc; +} + +/* TPM_BN_is_one() wraps the openSSL function in a TPM error handler + + Returns success if input is 1 + */ + +TPM_RESULT TPM_BN_is_one(TPM_BIGNUM bn_in) +{ + TPM_RESULT rc = 0; + int irc; + BIGNUM *bn = (BIGNUM *)bn_in; + + /* int BN_is_one(BIGNUM *a); + BN_is_one() tests if a equals 0, 1, + BN_is_one() returns 1 if the condition is true, 0 otherwise. */ + irc = BN_is_one(bn); + if (irc != 1) { + printf("TPM_BN_is_one: Error, result is not 1\n"); + rc = TPM_DAA_WRONG_W; + } + return rc; +} + +/* TPM_BN_mod() wraps the openSSL function in a TPM error handler + + r = a mod m + */ + +TPM_RESULT TPM_BN_mod(TPM_BIGNUM rem_in, + const TPM_BIGNUM a_in, + const TPM_BIGNUM m_in) +{ + TPM_RESULT rc = 0; + int irc; + BIGNUM *rem = (BIGNUM *)rem_in; + BIGNUM *a = (BIGNUM *)a_in; + BIGNUM *m = (BIGNUM *)m_in; + BN_CTX *ctx = NULL; /* freed @1 */ + + if (rc == 0) { + rc = TPM_BN_CTX_new(&ctx); /* freed @1 */ + } + /*int BN_mod(BIGNUM *rem, const BIGNUM *a, const BIGNUM *m, BN_CTX *ctx); + BN_mod() corresponds to BN_div() with dv set to NULL. + + int BN_div(BIGNUM *dv, BIGNUM *rem, const BIGNUM *a, const BIGNUM *d, BN_CTX *ctx); + + BN_div() divides a by d and places the result in dv and the remainder in rem (dv=a/d, + rem=a%d). Either of dv and rem may be NULL, in which case the respective value is not + returned. The result is rounded towards zero; thus if a is negative, the remainder will be + zero or negative. For division by powers of 2, use BN_rshift(3). + + For all functions, 1 is returned for success, 0 on error. The return value should always be + checked + */ + irc = BN_mod(rem, a, m, ctx); + if (irc != 1) { + printf("TPM_BN_mod: Error performing BN_mod()\n"); + TPM_OpenSSL_PrintError(); + rc = TPM_DAA_WRONG_W; + } + BN_CTX_free(ctx); /* @1 */ + return rc; +} + +/* TPM_BN_mask_bits() wraps the openSSL function in a TPM error handler + + erase all but the lowest n bits of bn + bn = bn mod 2^^n +*/ + +TPM_RESULT TPM_BN_mask_bits(TPM_BIGNUM bn_in, unsigned int n) +{ + TPM_RESULT rc = 0; + int irc; + unsigned int numBytes; + BIGNUM *bn = (BIGNUM *)bn_in; + + if (rc == 0) { + rc = TPM_BN_num_bytes(&numBytes, bn_in); + } + /* if the BIGNUM is already the correct number of bytes, no need to mask, and BN_mask_bits() + will fail. */ + if (rc == 0) { + if (numBytes > (n / 8)) { + /* BN_mask_bits() truncates a to an n bit number (a&=~((~0)>>;n)). An error occurs if a + already is shorter than n bits. + + int BN_mask_bits(BIGNUM *a, int n); + return 1 for success, 0 on error. + */ + irc = BN_mask_bits(bn, n); + if (irc != 1) { + printf("TPM_BN_mask_bits: Error performing BN_mask_bits()\n"); + TPM_OpenSSL_PrintError(); + rc = TPM_DAA_WRONG_W; + } + } + } + return rc; +} + +/* TPM_BN_rshift() wraps the openSSL function in a TPM error handler + + Shift a right by n bits (discard the lowest n bits) and label the result r +*/ + +TPM_RESULT TPM_BN_rshift(TPM_BIGNUM *rBignum_in, /* freed by caller */ + TPM_BIGNUM aBignum_in, + int n) +{ + TPM_RESULT rc = 0; + int irc; + BIGNUM **rBignum = (BIGNUM **)rBignum_in; + BIGNUM *aBignum = (BIGNUM *)aBignum_in; + + printf(" TPM_BN_rshift: n %d\n", n); + if (rc == 0) { + rc = TPM_BN_new(rBignum_in); + } + if (rc == 0) { + /* BN_rshift() shifts a right by n bits and places the result in r (r=a/2^n). + int BN_rshift(BIGNUM *r, BIGNUM *a, int n); + return 1 for success, 0 on error. + */ + irc = BN_rshift(*rBignum, aBignum, n); + if (irc != 1) { + printf("TPM_BN_rshift: Error performing BN_rshift()\n"); + TPM_OpenSSL_PrintError(); + rc = TPM_DAA_WRONG_W; + } + } + return rc; +} + +/* TPM_BN_lshift() wraps the openSSL function in a TPM error handler + + Shift a left by n bits and label the result r +*/ + +TPM_RESULT TPM_BN_lshift(TPM_BIGNUM *rBignum_in, /* freed by caller */ + TPM_BIGNUM aBignum_in, + int n) +{ + TPM_RESULT rc = 0; + int irc; + BIGNUM **rBignum = (BIGNUM **)rBignum_in; + BIGNUM *aBignum = (BIGNUM *)aBignum_in; + + printf(" TPM_BN_lshift: n %d\n", n); + if (rc == 0) { + rc = TPM_BN_new(rBignum_in); + } + if (rc == 0) { + /* BN_lshift() shifts a left by n bits and places the result in r (r=a*2^n). + int BN_lshift(BIGNUM *r, const BIGNUM *a, int n); + return 1 for success, 0 on error. + */ + irc = BN_lshift(*rBignum, aBignum, n); + if (irc != 1) { + printf("TPM_lshift: Error performing BN_lshift()\n"); + TPM_OpenSSL_PrintError(); + rc = TPM_DAA_WRONG_W; + } + } + return rc; +} + +/* TPM_BN_add() wraps the openSSL function in a TPM error handler + + Performs R = A + B + + R may be the same as A or B +*/ + +TPM_RESULT TPM_BN_add(TPM_BIGNUM rBignum_in, + TPM_BIGNUM aBignum_in, + TPM_BIGNUM bBignum_in) +{ + TPM_RESULT rc = 0; + int irc; + BIGNUM *rBignum = (BIGNUM *)rBignum_in; + BIGNUM *aBignum = (BIGNUM *)aBignum_in; + BIGNUM *bBignum = (BIGNUM *)bBignum_in; + + printf(" TPM_BN_add:\n"); + /* int BN_add(BIGNUM *r, const BIGNUM *a, const BIGNUM *b); + BN_add() adds a and b and places the result in r (r=a+b). r may be the same BIGNUM as a or b. + 1 is returned for success, 0 on error. + */ + irc = BN_add(rBignum, aBignum, bBignum); + if (irc != 1) { + printf("TPM_BN_add: Error performing BN_add()\n"); + TPM_OpenSSL_PrintError(); + rc = TPM_DAA_WRONG_W; + } + return rc; +} + +/* TPM_BN_mul() wraps the openSSL function in a TPM error handler + + r = a * b +*/ + +TPM_RESULT TPM_BN_mul(TPM_BIGNUM rBignum_in, + TPM_BIGNUM aBignum_in, + TPM_BIGNUM bBignum_in) +{ + TPM_RESULT rc = 0; + int irc; + BN_CTX *ctx; + BIGNUM *rBignum = (BIGNUM *)rBignum_in; + BIGNUM *aBignum = (BIGNUM *)aBignum_in; + BIGNUM *bBignum = (BIGNUM *)bBignum_in; + + printf(" TPM_BN_mul:\n"); + ctx = NULL; /* freed @1 */ + if (rc == 0) { + rc = TPM_BN_CTX_new(&ctx); /* freed @1 */ + } + /* int BN_mul(BIGNUM *r, BIGNUM *a, BIGNUM *b, BN_CTX *ctx); + BN_mul() multiplies a and b and places the result in r (r=a*b). r may be the same BIGNUM as a + or b. + 1 is returned for success, 0 on error. + */ + if (rc == 0) { + irc = BN_mul(rBignum, aBignum, bBignum, ctx); + if (irc != 1) { + printf("TPM_BN_add: Error performing BN_mul()\n"); + TPM_OpenSSL_PrintError(); + rc = TPM_DAA_WRONG_W; + } + } + BN_CTX_free(ctx); /* @1 */ + return rc; +} + +/* TPM_BN_mod_exp() wraps the openSSL function in a TPM error handler + + computes a to the p-th power modulo m (r=a^p % n) +*/ + +TPM_RESULT TPM_BN_mod_exp(TPM_BIGNUM rBignum_in, + TPM_BIGNUM aBignum_in, + TPM_BIGNUM pBignum_in, + TPM_BIGNUM nBignum_in) +{ + TPM_RESULT rc = 0; + int irc; + BN_CTX *ctx; + BIGNUM *rBignum = (BIGNUM *)rBignum_in; + BIGNUM *aBignum = (BIGNUM *)aBignum_in; + BIGNUM *pBignum = (BIGNUM *)pBignum_in; + BIGNUM *nBignum = (BIGNUM *)nBignum_in; + + printf(" TPM_BN_mod_exp:\n"); + ctx = NULL; /* freed @1 */ + if (rc == 0) { + rc = TPM_BN_CTX_new(&ctx); + } + /* BIGNUM calculation */ + /* int BN_mod_exp(BIGNUM *r, BIGNUM *a, const BIGNUM *p, const BIGNUM *m, BN_CTX *ctx); + + BN_mod_exp() computes a to the p-th power modulo m (r=a^p % m). This function uses less time + and space than BN_exp(). + + 1 is returned for success, 0 on error. + */ + if (rc == 0) { + printf(" TPM_BN_mod_exp: Calculate mod_exp\n"); + BN_set_flags(pBignum, BN_FLG_CONSTTIME); // p may be private + irc = BN_mod_exp(rBignum, aBignum, pBignum, nBignum, ctx); + if (irc != 1) { + printf("TPM_BN_mod_exp: Error performing BN_mod_exp()\n"); + TPM_OpenSSL_PrintError(); + rc = TPM_DAA_WRONG_W; + } + } + BN_CTX_free(ctx); /* @1 */ + return rc; +} + +/* TPM_BN_Mod_add() wraps the openSSL function in a TPM error handler + + adds a to b modulo m +*/ + +TPM_RESULT TPM_BN_mod_add(TPM_BIGNUM rBignum_in, + TPM_BIGNUM aBignum_in, + TPM_BIGNUM bBignum_in, + TPM_BIGNUM mBignum_in) +{ + TPM_RESULT rc = 0; + int irc; + BN_CTX *ctx; + BIGNUM *rBignum = (BIGNUM *)rBignum_in; + BIGNUM *aBignum = (BIGNUM *)aBignum_in; + BIGNUM *bBignum = (BIGNUM *)bBignum_in; + BIGNUM *mBignum = (BIGNUM *)mBignum_in; + + printf(" TPM_BN_mod_add:\n"); + ctx = NULL; /* freed @1 */ + if (rc == 0) { + rc = TPM_BN_CTX_new(&ctx); + } + /* int BN_mod_add(BIGNUM *r, BIGNUM *a, BIGNUM *b, const BIGNUM *m, BN_CTX *ctx); + BN_mod_add() adds a to b modulo m and places the non-negative result in r. + 1 is returned for success, 0 on error. + */ + if (rc == 0) { + irc = BN_mod_add(rBignum, aBignum, bBignum, mBignum, ctx); + if (irc != 1) { + printf("TPM_BN_mod_add: Error performing BN_mod_add()\n"); + TPM_OpenSSL_PrintError(); + rc = TPM_DAA_WRONG_W; + } + } + + BN_CTX_free(ctx); /* @1 */ + return rc; +} + +/* TPM_BN_mod_mul() wraps the openSSL function in a TPM error handler + + r = (a * b) mod m + */ + +TPM_RESULT TPM_BN_mod_mul(TPM_BIGNUM rBignum_in, + TPM_BIGNUM aBignum_in, + TPM_BIGNUM bBignum_in, + TPM_BIGNUM mBignum_in) +{ + TPM_RESULT rc = 0; + int irc; + BN_CTX *ctx; + BIGNUM *rBignum = (BIGNUM *)rBignum_in; + BIGNUM *aBignum = (BIGNUM *)aBignum_in; + BIGNUM *bBignum = (BIGNUM *)bBignum_in; + BIGNUM *mBignum = (BIGNUM *)mBignum_in; + + printf(" TPM_BN_mod_mul:\n"); + ctx = NULL; /* freed @1 */ + if (rc == 0) { + rc = TPM_BN_CTX_new(&ctx); + } + /* int BN_mod_mul(BIGNUM *r, BIGNUM *a, BIGNUM *b, const BIGNUM *m, BN_CTX *ctx); + BN_mod_mul() multiplies a by b and finds the non-negative remainder respective to modulus m + (r=(a*b) mod m). r may be the same BIGNUM as a or b. + 1 is returned for success, 0 on error. + */ + if (rc == 0) { + irc = BN_mod_mul(rBignum, aBignum, bBignum, mBignum, ctx); + if (irc != 1) { + printf("TPM_BN_mod_mul: Error performing BN_mod_mul()\n"); + TPM_OpenSSL_PrintError(); + rc = TPM_DAA_WRONG_W; + } + } + BN_CTX_free(ctx); /* @1 */ + return rc; +} + +/* TPM_BN_CTX_new() wraps the openSSL function in a TPM error handler */ + +static TPM_RESULT TPM_BN_CTX_new(BN_CTX **ctx) +{ + TPM_RESULT rc = 0; + + if (rc == 0) { + if (*ctx != NULL) { + printf("TPM_BN_CTX_new: Error (fatal), *ctx %p should be NULL before BN_CTX_new \n", + *ctx); + rc = TPM_FAIL; + } + } + if (rc == 0) { + *ctx = BN_CTX_new(); + if (*ctx == NULL) { + printf("TPM_BN_CTX_new: Error, context is NULL\n"); + TPM_OpenSSL_PrintError(); + rc = TPM_SIZE; + + } + } + return rc; +} + +/* TPM_BN_new() wraps the openSSL function in a TPM error handler + + Allocates a new bignum +*/ + +TPM_RESULT TPM_BN_new(TPM_BIGNUM *bn_in) +{ + TPM_RESULT rc = 0; + BIGNUM **bn = (BIGNUM **)bn_in; + + *bn = BN_new(); + if (*bn == NULL) { + printf("TPM_BN_new: Error, bn is NULL\n"); + TPM_OpenSSL_PrintError(); + rc = TPM_SIZE; + } + return rc; +} + +/* TPM_BN_free() wraps the openSSL function + + Frees the bignum +*/ + +void TPM_BN_free(TPM_BIGNUM bn_in) +{ + BIGNUM *bn = (BIGNUM *)bn_in; + + BN_free(bn); + return; +} + +/* TPM_bn2bin wraps the openSSL function in a TPM error handler. + + Converts a bignum to char array + + 'bin' must already be checked for sufficient size. + + int BN_bn2bin(const BIGNUM *a, unsigned char *to); + BN_bn2bin() returns the length of the big-endian number placed at to +*/ + +TPM_RESULT TPM_bn2bin(unsigned char *bin, + TPM_BIGNUM bn_in) +{ + TPM_RESULT rc = 0; + BN_bn2bin((BIGNUM *)bn_in, bin); + return rc; +} + + +/* TPM_bin2bn() wraps the openSSL function in a TPM error handler + + Converts a char array to bignum + + bn must be freed by the caller. +*/ + +TPM_RESULT TPM_bin2bn(TPM_BIGNUM *bn_in, const unsigned char *bin, unsigned int bytes) +{ + TPM_RESULT rc = 0; + BIGNUM **bn = (BIGNUM **)bn_in; + + /* BIGNUM *BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret); + + BN_bin2bn() converts the positive integer in big-endian form of length len at s into a BIGNUM + and places it in ret. If ret is NULL, a new BIGNUM is created. + + BN_bin2bn() returns the BIGNUM, NULL on error. + */ + if (rc == 0) { + *bn = BN_bin2bn(bin, bytes, *bn); + if (*bn == NULL) { + printf("TPM_bin2bn: Error in BN_bin2bn\n"); + TPM_OpenSSL_PrintError(); + rc = TPM_SIZE; + } + } + return rc; +} + +/* + Hash Functions +*/ + +/* for the openSSL version, TPM_SHA1Context is a SHA_CTX structure */ + +/* TPM_SHA1InitCmd() initializes a platform dependent TPM_SHA1Context structure. + + The structure must be freed using TPM_SHA1Delete() +*/ + +TPM_RESULT TPM_SHA1InitCmd(void **context) +{ + TPM_RESULT rc = 0; + + printf(" TPM_SHA1InitCmd:\n"); + if (rc== 0) { + rc = TPM_Malloc((unsigned char **)context, sizeof(SHA_CTX)); + } + if (rc== 0) { + SHA1_Init(*context); + } + return rc; +} + +/* TPM_SHA1UpdateCmd() adds 'data' of 'length' to the SHA-1 context + */ + +TPM_RESULT TPM_SHA1UpdateCmd(void *context, const unsigned char *data, uint32_t length) +{ + TPM_RESULT rc = 0; + + printf(" TPM_SHA1Update: length %u\n", length); + if (context != NULL) { + SHA1_Update(context, data, length); + } + else { + printf("TPM_SHA1Update: Error, no existing SHA1 thread\n"); + rc = TPM_SHA_THREAD; + } + return rc; +} + +/* TPM_SHA1FinalCmd() extracts the SHA-1 digest 'md' from the context + */ + +TPM_RESULT TPM_SHA1FinalCmd(unsigned char *md, void *context) +{ + TPM_RESULT rc = 0; + + printf(" TPM_SHA1FinalCmd:\n"); + if (context != NULL) { + SHA1_Final(md, context); + } + else { + printf("TPM_SHA1FinalCmd: Error, no existing SHA1 thread\n"); + rc = TPM_SHA_THREAD; + } + return rc; +} + +/* TPM_SHA1Delete() zeros and frees the SHA1 context */ + +void TPM_SHA1Delete(void **context) +{ + if (*context != NULL) { + printf(" TPM_SHA1Delete:\n"); + /* zero because the SHA1 context might have data left from an HMAC */ + memset(*context, 0, sizeof(SHA_CTX)); + free(*context); + *context = NULL; + } + return; +} + +/* TPM_Sha1Context_Load() is non-portable code to deserialize the OpenSSL SHA1 context. + + If the contextPresent prepended by TPM_Sha1Context_Store() is FALSE, context remains NULL. If + TRUE, context is allocated and loaded. +*/ + +TPM_RESULT TPM_Sha1Context_Load(void **context, + unsigned char **stream, + uint32_t *stream_size) +{ + TPM_RESULT rc = 0; + size_t i; + SHA_CTX *sha_ctx = NULL; /* initialize to silence hopefully bogus gcc 4.4.4 + warning */ + TPM_BOOL contextPresent; /* is there a context to be loaded */ + + printf(" TPM_Sha1Context_Load: OpenSSL\n"); + /* TPM_Sha1Context_Store() stored a flag to indicate whether a context should be stored */ + if (rc== 0) { + rc = TPM_LoadBool(&contextPresent, stream, stream_size); + printf(" TPM_Sha1Context_Load: contextPresent %u\n", contextPresent); + } + /* check format tag */ + /* In the future, if multiple formats are supported, this check will be replaced by a 'switch' + on the tag */ + if ((rc== 0) && contextPresent) { + rc = TPM_CheckTag(TPM_TAG_SHA1CONTEXT_OSSL_V1, stream, stream_size); + } + if ((rc== 0) && contextPresent) { + rc = TPM_Malloc((unsigned char **)context, sizeof(SHA_CTX)); + sha_ctx = (SHA_CTX *)*context; + } + /* load h0 */ + if ((rc== 0) && contextPresent) { + rc = TPM_Load32(&(sha_ctx->h0), stream, stream_size); + } + /* load h1 */ + if ((rc== 0) && contextPresent) { + rc = TPM_Load32(&(sha_ctx->h1), stream, stream_size); + } + /* load h2 */ + if ((rc== 0) && contextPresent) { + rc = TPM_Load32(&(sha_ctx->h2), stream, stream_size); + } + /* load h3 */ + if ((rc== 0) && contextPresent) { + rc = TPM_Load32(&(sha_ctx->h3), stream, stream_size); + } + /* load h4 */ + if ((rc== 0) && contextPresent) { + rc = TPM_Load32(&(sha_ctx->h4), stream, stream_size); + } + /* load Nl */ + if ((rc== 0) && contextPresent) { + rc = TPM_Load32(&(sha_ctx->Nl), stream, stream_size); + } + /* load Nh */ + if ((rc== 0) && contextPresent) { + rc = TPM_Load32(&(sha_ctx->Nh), stream, stream_size); + } + /* load data */ + for (i = 0 ; (rc == 0) && contextPresent && (i < SHA_LBLOCK) ; i++) { + rc = TPM_Load32(&(sha_ctx->data[i]), stream, stream_size); + } + /* load num */ + if ((rc== 0) && contextPresent) { + rc = TPM_Load32(&(sha_ctx->num), stream, stream_size); + } + return rc; +} + +/* TPM_Sha1Context_Store() is non-portable code to serialize the OpenSSL SHA1 context. context is + not altered. + + It prepends a contextPresent flag to the stream, FALSE if context is NULL, TRUE if not. +*/ + +TPM_RESULT TPM_Sha1Context_Store(TPM_STORE_BUFFER *sbuffer, + void *context) +{ + TPM_RESULT rc = 0; + size_t i; + SHA_CTX *sha_ctx = (SHA_CTX *)context; + TPM_BOOL contextPresent; /* is there a context to be stored */ + + printf(" TPM_Sha1Context_Store: OpenSSL\n"); + /* store contextPresent */ + if (rc == 0) { + if (sha_ctx != NULL) { + printf(" TPM_Sha1Context_Store: Storing context\n"); + contextPresent = TRUE; + } + else { + printf(" TPM_Sha1Context_Store: No context to store\n"); + contextPresent = FALSE; + } + rc = TPM_Sbuffer_Append(sbuffer, &contextPresent, sizeof(TPM_BOOL)); + } + /* overall format tag */ + if ((rc== 0) && contextPresent) { + rc = TPM_Sbuffer_Append16(sbuffer, TPM_TAG_SHA1CONTEXT_OSSL_V1); + } + if ((rc== 0) && contextPresent) { + rc = TPM_Sbuffer_Append32(sbuffer, sha_ctx->h0); + } + if ((rc== 0) && contextPresent) { + rc = TPM_Sbuffer_Append32(sbuffer, sha_ctx->h1); + } + if ((rc== 0) && contextPresent) { + rc = TPM_Sbuffer_Append32(sbuffer, sha_ctx->h2); + } + if ((rc== 0) && contextPresent) { + rc = TPM_Sbuffer_Append32(sbuffer, sha_ctx->h3); + } + if ((rc== 0) && contextPresent) { + rc = TPM_Sbuffer_Append32(sbuffer, sha_ctx->h4); + } + if ((rc== 0) && contextPresent) { + rc = TPM_Sbuffer_Append32(sbuffer, sha_ctx->Nl); + } + if ((rc== 0) && contextPresent) { + rc = TPM_Sbuffer_Append32(sbuffer, sha_ctx->Nh); + } + for (i = 0 ; (rc == 0) && contextPresent && (i < SHA_LBLOCK) ; i++) { + rc = TPM_Sbuffer_Append32(sbuffer, sha_ctx->data[i]); + } + if ((rc== 0) && contextPresent) { + rc = TPM_Sbuffer_Append32(sbuffer, sha_ctx->num); + } + return rc; +} + +/* + TPM_SYMMETRIC_KEY_DATA +*/ + +/* TPM_SymmetricKeyData_New() allocates memory for and initializes a TPM_SYMMETRIC_KEY_DATA token. + */ + +TPM_RESULT TPM_SymmetricKeyData_New(TPM_SYMMETRIC_KEY_TOKEN *tpm_symmetric_key_data) +{ + TPM_RESULT rc = 0; + + printf(" TPM_SymmetricKeyData_New:\n"); + if (rc == 0) { + rc = TPM_Malloc(tpm_symmetric_key_data, sizeof(TPM_SYMMETRIC_KEY_DATA)); + } + if (rc == 0) { + TPM_SymmetricKeyData_Init(*tpm_symmetric_key_data); + } + return rc; +} + +/* TPM_SymmetricKeyData_Free() initializes the key token to wipe secrets. It then frees the + TPM_SYMMETRIC_KEY_DATA token and sets it to NULL. +*/ + +void TPM_SymmetricKeyData_Free(TPM_SYMMETRIC_KEY_TOKEN *tpm_symmetric_key_data) +{ + printf(" TPM_SymmetricKeyData_Free:\n"); + if (*tpm_symmetric_key_data != NULL) { + TPM_SymmetricKeyData_Init(*tpm_symmetric_key_data); + free(*tpm_symmetric_key_data); + *tpm_symmetric_key_data = NULL; + } + return; +} + +#ifdef TPM_DES + +/* TPM_SymmetricKeyData_Init() is DES non-portable code to initialize the TPM_SYMMETRIC_KEY_DATA + + It depends on the TPM_SYMMETRIC_KEY_DATA declaration. +*/ + +void TPM_SymmetricKeyData_Init(TPM_SYMMETRIC_KEY_TOKEN tpm_symmetric_key_token) +{ + TPM_SYMMETRIC_KEY_DATA *tpm_symmetric_key_data = + (TPM_SYMMETRIC_KEY_DATA *)tpm_symmetric_key_token; + + printf(" TPM_SymmetricKeyData_Init:\n"); + tpm_symmetric_key_data->tag = TPM_TAG_KEY; + tpm_symmetric_key_data->valid = FALSE; + tpm_symmetric_key_data->fill = 0; + memset(tpm_symmetric_key_data->des_cblock1, 0, sizeof(DES_cblock)); + memset(tpm_symmetric_key_data->des_cblock2, 0, sizeof(DES_cblock)); + memset(tpm_symmetric_key_data->des_cblock3, 0, sizeof(DES_cblock)); + return; +} + +/* TPM_SymmetricKeyData_Load() is DES non-portable code to deserialize the TPM_SYMMETRIC_KEY_DATA + + It depends on the TPM_SYMMETRIC_KEY_DATA declaration. +*/ + +TPM_RESULT TPM_SymmetricKeyData_Load(TPM_SYMMETRIC_KEY_TOKEN tpm_symmetric_key_token, + unsigned char **stream, + uint32_t *stream_size) +{ + TPM_RESULT rc = 0; + TPM_SYMMETRIC_KEY_DATA *tpm_symmetric_key_data = + (TPM_SYMMETRIC_KEY_DATA *)tpm_symmetric_key_token; + + printf(" TPM_SymmetricKeyData_Load:\n"); + /* check tag */ + if (rc == 0) { + rc = TPM_CheckTag(TPM_TAG_KEY, stream, stream_size); + } + /* load valid */ + if (rc == 0) { + rc = TPM_LoadBool(&(tpm_symmetric_key_data->valid), stream, stream_size); + } + /* load fill */ + if (rc == 0) { + rc = TPM_Load8(&(tpm_symmetric_key_data->fill), stream, stream_size); + } + /* this assumes that DES_cblock is a consistently packed structure. It is in fact an array of 8 + bytes for openSSL. */ + if (rc == 0) { + rc = TPM_Loadn(tpm_symmetric_key_data->des_cblock1, sizeof(DES_cblock), + stream, stream_size); + } + if (rc == 0) { + rc = TPM_Loadn(tpm_symmetric_key_data->des_cblock2, sizeof(DES_cblock), + stream, stream_size); + } + if (rc == 0) { + rc = TPM_Loadn(tpm_symmetric_key_data->des_cblock3, sizeof(DES_cblock), + stream, stream_size); + } + if (rc == 0) { + TPM_PrintFour(" TPM_SymmetricKeyData_Load: des1", tpm_symmetric_key_data->des_cblock1); + TPM_PrintFour(" TPM_SymmetricKeyData_Load: des2", tpm_symmetric_key_data->des_cblock2); + TPM_PrintFour(" TPM_SymmetricKeyData_Load: des3", tpm_symmetric_key_data->des_cblock3); + } + return rc; +} + +/* TPM_SymmetricKeyData_Store() DES is non-portable code to serialize the TPM_SYMMETRIC_KEY_DATA + + It depends on the TPM_SYMMETRIC_KEY_DATA declaration. +*/ + +TPM_RESULT TPM_SymmetricKeyData_Store(TPM_STORE_BUFFER *sbuffer, + const TPM_SYMMETRIC_KEY_TOKEN tpm_symmetric_key_token) +{ + TPM_RESULT rc = 0; + TPM_SYMMETRIC_KEY_DATA *tpm_symmetric_key_data = + (TPM_SYMMETRIC_KEY_DATA *)tpm_symmetric_key_token; + + printf(" TPM_SymmetricKeyData_Store:\n"); + if (rc == 0) { + TPM_PrintFour(" TPM_SymmetricKeyData_Store: des1", tpm_symmetric_key_data->des_cblock1); + TPM_PrintFour(" TPM_SymmetricKeyData_Store: des2", tpm_symmetric_key_data->des_cblock2); + TPM_PrintFour(" TPM_SymmetricKeyData_Store: des3", tpm_symmetric_key_data->des_cblock3); + } + /* store tag */ + if (rc == 0) { + rc = TPM_Sbuffer_Append16(sbuffer, tpm_symmetric_key_data->tag); + } + /* store valid */s + if (rc == 0) { + rc = TPM_Sbuffer_Append(sbuffer, &(tpm_symmetric_key_data->valid), sizeof(TPM_BOOL)); + } + /* store fill */ + if (rc == 0) { + rc = TPM_Sbuffer_Append(sbuffer, &(tpm_symmetric_key_data->fill), sizeof(TPM_BOOL)); + } + /* store DES key */ + if (rc == 0) { + rc = TPM_Sbuffer_Append(sbuffer, tpm_symmetric_key_data->des_cblock1, sizeof(DES_cblock)); + } + if (rc == 0) { + rc = TPM_Sbuffer_Append(sbuffer, tpm_symmetric_key_data->des_cblock2, sizeof(DES_cblock)); + } + if (rc == 0) { + rc = TPM_Sbuffer_Append(sbuffer, tpm_symmetric_key_data->des_cblock3, sizeof(DES_cblock)); + } + return rc; +} + +/* TPM_SymmetricKeyData_GenerateKey() is DES non-portable code to generate a symmetric key + + vsymmetric_key must be freed by the caller +*/ + +TPM_RESULT TPM_SymmetricKeyData_GenerateKey(TPM_SYMMETRIC_KEY_TOKEN tpm_symmetric_key_token) +{ + TPM_RESULT rc = 0; + TPM_SYMMETRIC_KEY_DATA *tpm_symmetric_key_data = + (TPM_SYMMETRIC_KEY_DATA *)tpm_symmetric_key_token; + + printf(" TPM_SymmetricKeyData_GenerateKey:\n"); + /* generate a random key */ + if (rc == 0) { + DES_random_key(&(tpm_symmetric_key_data->des_cblock1)); + DES_random_key(&(tpm_symmetric_key_data->des_cblock2)); + DES_random_key(&(tpm_symmetric_key_data->des_cblock3)); + /* sets the parity of the passed key to odd. */ + DES_set_odd_parity(&(tpm_symmetric_key_data->des_cblock1)); + DES_set_odd_parity(&(tpm_symmetric_key_data->des_cblock2)); + DES_set_odd_parity(&(tpm_symmetric_key_data->des_cblock3)); + TPM_PrintFour(" TPM_SymmetricKeyData_GenerateKey: des1", + tpm_symmetric_key_data->des_cblock1); + TPM_PrintFour(" TPM_SymmetricKeyData_GenerateKey: des2", + tpm_symmetric_key_data->des_cblock2); + TPM_PrintFour(" TPM_SymmetricKeyData_GenerateKey: des3", + tpm_symmetric_key_data->des_cblock3); + tpm_symmetric_key_data->valid = TRUE; + } + return rc; +} + +/* TPM_SymmetricKeyData_Encrypt() is DES non-portable code to encrypt 'decrypt_data' to + 'encrypt_data' + + The stream is padded as per PKCS#7 / RFC2630 + + 'encrypt_data' must be free by the caller +*/ + +TPM_RESULT TPM_SymmetricKeyData_Encrypt(unsigned char **encrypt_data, /* output, caller frees */ + uint32_t *encrypt_length, /* output */ + const unsigned char *decrypt_data, /* input */ + uint32_t decrypt_length, /* input */ + const TPM_SYMMETRIC_KEY_TOKEN + tpm_symmetric_key_token) /* input */ +{ + TPM_RESULT rc = 0; + uint32_t pad_length; + unsigned char *decrypt_data_pad; + TPM_SYMMETRIC_KEY_DATA *tpm_symmetric_key_data = + (TPM_SYMMETRIC_KEY_DATA *)tpm_symmetric_key_token; + + printf(" TPM_SymmetricKeyData_Encrypt: Length %u\n", decrypt_length); + decrypt_data_pad = NULL; /* freed @1 */ + if (rc == 0) { + /* calculate the pad length and padded data length */ + pad_length = TPM_DES_BLOCK_SIZE - (decrypt_length % TPM_DES_BLOCK_SIZE); + *encrypt_length = decrypt_length + pad_length; + printf(" TPM_SymmetricKeyData_Encrypt: Padded length %u pad length %u\n", + *encrypt_length, pad_length); + /* allocate memory for the encrypted response */ + rc = TPM_Malloc(encrypt_data, *encrypt_length); + } + /* allocate memory for the padded decrypted data */ + if (rc == 0) { + rc = TPM_Malloc(&decrypt_data_pad, *encrypt_length); + } + /* pad the decrypted clear text data */ + if (rc == 0) { + /* unpadded original data */ + memcpy(decrypt_data_pad, decrypt_data, decrypt_length); + /* last gets pad = pad length */ + memset(decrypt_data_pad + decrypt_length, pad_length, pad_length); + /* encrypt the padded input to the output */ + rc = TPM_SymmetricKeyData_Crypt(*encrypt_data, + decrypt_data_pad, + *encrypt_length, + tpm_symmetric_key_data, + DES_ENCRYPT, + TPM_ENCRYPT_ERROR); + } + free(decrypt_data_pad); /* @1 */ + return rc; +} + +/* TPM_SymmetricKeyData_Decrypt() is DES non-portable code to decrypt 'encrypt_data' to + 'decrypt_data' + + The stream must be padded as per PKCS#7 / RFC2630 + + decrypt_data must be free by the caller +*/ + +TPM_RESULT TPM_SymmetricKeyData_Decrypt(unsigned char **decrypt_data, /* output, caller frees */ + uint32_t *decrypt_length, /* output */ + const unsigned char *encrypt_data, /* input */ + uint32_t encrypt_length, /* input */ + const TPM_SYMMETRIC_KEY_TOKEN + tpm_symmetric_key_data) /* input */ +{ + TPM_RESULT rc = 0; + uint32_t pad_length; + uint32_t i; + unsigned char *pad_data; + + printf(" TPM_SymmetricKeyData_Decrypt: Length %u\n", encrypt_length); + /* sanity check encrypted length */ + if (rc == 0) { + if (encrypt_length < TPM_DES_BLOCK_SIZE) { + printf("TPM_SymmetricKeyData_Decrypt: Error, bad length\n"); + rc = TPM_DECRYPT_ERROR; + } + } + /* allocate memory for the padded decrypted data */ + if (rc == 0) { + rc = TPM_Malloc(decrypt_data, encrypt_length); + } + /* decrypt the input to the padded output */ + if (rc == 0) { + rc = TPM_SymmetricKeyData_Crypt(*decrypt_data, + encrypt_data, + encrypt_length, + tpm_symmetric_key_data, + DES_DECRYPT, + TPM_DECRYPT_ERROR); + } + /* get the pad length */ + if (rc == 0) { + /* get the pad length from the last byte */ + pad_length = (uint32_t)*(*decrypt_data + encrypt_length - 1); + /* sanity check the pad length */ + printf(" TPM_SymmetricKeyData_Decrypt: Pad length %u\n", pad_length); + if ((pad_length == 0) || + (pad_length > TPM_DES_BLOCK_SIZE)) { + printf("TPM_SymmetricKeyData_Decrypt: Error, illegal pad length\n"); + rc = TPM_DECRYPT_ERROR; + } + } + if (rc == 0) { + /* get the unpadded length */ + *decrypt_length = encrypt_length - pad_length; + /* pad starting point */ + pad_data = *decrypt_data + *decrypt_length; + /* sanity check the pad */ + for (i = 0 ; i < pad_length ; i++, pad_data++) { + if (*pad_data != pad_length) { + printf("TPM_SymmetricKeyData_Decrypt: Error, bad pad %02x at index %u\n", + *pad_data, i); + rc = TPM_DECRYPT_ERROR; + } + } + } + return rc; +} + +/* TPM_SymmetricKeyData_Crypt() is DES common code for openSSL, since encrypt and decrypt use the + same function with an 'enc' flag. + + 'data_in' and 'data_out' must be preallocated arrays of 'length' bytes. 'length' must be a + multiple of TPM_DES_BLOCK_SIZE. + + Returns 'error' on error. +*/ + +/* openSSL prototype + + void DES_ede3_cbc_encrypt(const unsigned char *input, + unsigned char *output, long length, DES_key_schedule *ks1, + DES_key_schedule *ks2, DES_key_schedule *ks3, DES_cblock *ivec, + int enc); +*/ + +static TPM_RESULT TPM_SymmetricKeyData_Crypt(unsigned char *data_out, /* output */ + const unsigned char *data_in, /* input */ + uint32_t length, /* input */ + TPM_SYMMETRIC_KEY_DATA *tpm_symmetric_key_data, /*in*/ + int enc, /* input */ + TPM_RESULT error) /* input */ +{ + TPM_RESULT rc = 0; + int irc; + DES_key_schedule des_key_schedule1; + DES_key_schedule des_key_schedule2; + DES_key_schedule des_key_schedule3; + DES_cblock ivec; /* initial chaining vector */ + + if (rc == 0) { + if ((length % TPM_DES_BLOCK_SIZE) != 0) { + printf("TPM_SymmetricKeyData_Crypt: Error, illegal length %u\n", length); + rc = error; /* should never occur */ + } + } + if (rc == 0) { + TPM_PrintFour(" TPM_SymmetricKeyData_Crypt: des1", tpm_symmetric_key_data->des_cblock1); + TPM_PrintFour(" TPM_SymmetricKeyData_Crypt: des2", tpm_symmetric_key_data->des_cblock2); + TPM_PrintFour(" TPM_SymmetricKeyData_Crypt: des3", tpm_symmetric_key_data->des_cblock3); + } + /* Before a DES key can be used, it must be converted into the architecture dependent + DES_key_schedule via the DES_set_key_checked() or DES_set_key_unchecked() function. */ + if (rc == 0) { + irc = DES_set_key_checked(&(tpm_symmetric_key_data->des_cblock1), &des_key_schedule1); + if (irc != 0) { + printf("TPM_SymmetricKeyData_Crypt: Error, DES_set_key_checked rc %d\n", irc); + rc = error; + } + } + if (rc == 0) { + irc = DES_set_key_checked(&(tpm_symmetric_key_data->des_cblock2), &des_key_schedule2); + if (irc != 0) { + printf("TPM_SymmetricKeyData_Crypt: Error, DES_set_key_checked rc %d\n", irc); + rc = error; + } + } + if (rc == 0) { + irc = DES_set_key_checked(&(tpm_symmetric_key_data->des_cblock3), &des_key_schedule3); + if (irc != 0) { + printf("TPM_SymmetricKeyData_Crypt: Error, DES_set_key_checked rc %d\n", irc); + rc = error; + } + } + /* initialize initial chaining vector */ + if (rc == 0) { + TPM_PrintFourLimit(" TPM_SymmetricKeyData_Crypt: Input", data_in, length); + /* encrypt operation */ + memset(&ivec, 0, sizeof(DES_cblock)); + DES_ede3_cbc_encrypt(data_in, + data_out, + length, + &des_key_schedule1, + &des_key_schedule2, + &des_key_schedule3, + &ivec, + enc); + TPM_PrintFour(" TPM_SymmetricKeyData_Crypt: Output", data_out); + } + return rc; +} + +#endif + +#ifdef TPM_AES + +/* TPM_SymmetricKeyData_Init() is AES non-portable code to initialize the TPM_SYMMETRIC_KEY_DATA + + It depends on the TPM_SYMMETRIC_KEY_DATA declaration. +*/ + +void TPM_SymmetricKeyData_Init(TPM_SYMMETRIC_KEY_TOKEN tpm_symmetric_key_token) +{ + TPM_SYMMETRIC_KEY_DATA *tpm_symmetric_key_data = + (TPM_SYMMETRIC_KEY_DATA *)tpm_symmetric_key_token; + + printf(" TPM_SymmetricKeyData_Init:\n"); + tpm_symmetric_key_data->tag = TPM_TAG_KEY; + tpm_symmetric_key_data->valid = FALSE; + tpm_symmetric_key_data->fill = 0; + memset(tpm_symmetric_key_data->userKey, 0, sizeof(tpm_symmetric_key_data->userKey)); + memset(&(tpm_symmetric_key_data->aes_enc_key), 0, sizeof(tpm_symmetric_key_data->aes_enc_key)); + memset(&(tpm_symmetric_key_data->aes_dec_key), 0, sizeof(tpm_symmetric_key_data->aes_dec_key)); + return; +} + +/* TPM_SymmetricKeyData_Load() is AES non-portable code to deserialize the TPM_SYMMETRIC_KEY_DATA + + It depends on the above TPM_SYMMETRIC_KEY_DATA declaration. +*/ + +TPM_RESULT TPM_SymmetricKeyData_Load(TPM_SYMMETRIC_KEY_TOKEN tpm_symmetric_key_token, + unsigned char **stream, + uint32_t *stream_size) +{ + TPM_RESULT rc = 0; + TPM_SYMMETRIC_KEY_DATA *tpm_symmetric_key_data = + (TPM_SYMMETRIC_KEY_DATA *)tpm_symmetric_key_token; + + printf(" TPM_SymmetricKeyData_Load:\n"); + /* check tag */ + if (rc == 0) { + rc = TPM_CheckTag(TPM_TAG_KEY, stream, stream_size); + } + /* load valid */ + if (rc == 0) { + rc = TPM_LoadBool(&(tpm_symmetric_key_data->valid), stream, stream_size); + } + /* load fill */ + if (rc == 0) { + rc = TPM_Load8(&(tpm_symmetric_key_data->fill), stream, stream_size); + } + /* The AES key is a simple array. */ + if (rc == 0) { + rc = TPM_Loadn(tpm_symmetric_key_data->userKey, sizeof(tpm_symmetric_key_data->userKey), + stream, stream_size); + } + /* reconstruct the internal AES keys */ + if (rc == 0) { + rc = TPM_SymmetricKeyData_SetKeys(tpm_symmetric_key_data); + } + return rc; +} + +/* TPM_SymmetricKeyData_Store() is AES non-portable code to serialize the TPM_SYMMETRIC_KEY_DATA + + It depends on the above TPM_SYMMETRIC_KEY_DATA declaration. +*/ + +TPM_RESULT TPM_SymmetricKeyData_Store(TPM_STORE_BUFFER *sbuffer, + const TPM_SYMMETRIC_KEY_TOKEN tpm_symmetric_key_token) +{ + TPM_RESULT rc = 0; + TPM_SYMMETRIC_KEY_DATA *tpm_symmetric_key_data = + (TPM_SYMMETRIC_KEY_DATA *)tpm_symmetric_key_token; + + printf(" TPM_SymmetricKeyData_Store:\n"); + /* store tag */ + if (rc == 0) { + rc = TPM_Sbuffer_Append16(sbuffer, tpm_symmetric_key_data->tag); + } + /* store valid */ + if (rc == 0) { + rc = TPM_Sbuffer_Append(sbuffer, &(tpm_symmetric_key_data->valid), sizeof(TPM_BOOL)); + } + /* store fill */ + if (rc == 0) { + rc = TPM_Sbuffer_Append(sbuffer, &(tpm_symmetric_key_data->fill), sizeof(TPM_BOOL)); + } + /* store AES key */ + if (rc == 0) { + rc = TPM_Sbuffer_Append(sbuffer, + tpm_symmetric_key_data->userKey, + sizeof(tpm_symmetric_key_data->userKey)); + } + /* No need to store the internal AES keys. They are reconstructed on load */ + return rc; +} + +/* TPM_SymmetricKeyData_GenerateKey() is AES non-portable code to generate a random symmetric key + + tpm_symmetric_key_data should be initialized before and after use +*/ + +TPM_RESULT TPM_SymmetricKeyData_GenerateKey(TPM_SYMMETRIC_KEY_TOKEN tpm_symmetric_key_token) +{ + TPM_RESULT rc = 0; + TPM_SYMMETRIC_KEY_DATA *tpm_symmetric_key_data = + (TPM_SYMMETRIC_KEY_DATA *)tpm_symmetric_key_token; + + printf(" TPM_SymmetricKeyData_GenerateKey:\n"); + /* generate a random key */ + if (rc == 0) { + rc = TPM_Random(tpm_symmetric_key_data->userKey, sizeof(tpm_symmetric_key_data->userKey)); + } + /* construct the internal AES keys */ + if (rc == 0) { + rc = TPM_SymmetricKeyData_SetKeys(tpm_symmetric_key_data); + } + if (rc == 0) { + tpm_symmetric_key_data->valid = TRUE; + } + return rc; +} + +/* TPM_SymmetricKeyData_SetKey() is AES non-portable code to set a symmetric key from input data + + tpm_symmetric_key_data should be initialized before and after use +*/ + +TPM_RESULT TPM_SymmetricKeyData_SetKey(TPM_SYMMETRIC_KEY_DATA *tpm_symmetric_key_data, + const unsigned char *key_data, + uint32_t key_data_size) +{ + TPM_RESULT rc = 0; + + printf(" TPM_SymmetricKeyData_SetKey:\n"); + /* check the input data size, it can be truncated, but cannot be smaller than the AES key */ + if (rc == 0) { + if (sizeof(tpm_symmetric_key_data->userKey) > key_data_size) { + printf("TPM_SymmetricKeyData_SetKey: Error (fatal), need %lu bytes, received %u\n", + (unsigned long)sizeof(tpm_symmetric_key_data->userKey), key_data_size); + rc = TPM_FAIL; /* should never occur */ + } + } + if (rc == 0) { + /* copy the input data into the AES key structure */ + memcpy(tpm_symmetric_key_data->userKey, key_data, sizeof(tpm_symmetric_key_data->userKey)); + /* construct the internal AES keys */ + rc = TPM_SymmetricKeyData_SetKeys(tpm_symmetric_key_data); + } + if (rc == 0) { + tpm_symmetric_key_data->valid = TRUE; + } + return rc; +} + +/* TPM_SymmetricKeyData_SetKeys() is AES non-portable code to construct the internal AES keys from + the userKey + + tpm_symmetric_key_data should be initialized before and after use +*/ + +static TPM_RESULT TPM_SymmetricKeyData_SetKeys(TPM_SYMMETRIC_KEY_DATA *tpm_symmetric_key_data) +{ + TPM_RESULT rc = 0; + int irc; + + printf(" TPM_SymmetricKeyData_SetKeys:\n"); + if (rc == 0) { + TPM_PrintFour(" TPM_SymmetricKeyData_SetKeys: userKey", tpm_symmetric_key_data->userKey); + irc = AES_set_encrypt_key(tpm_symmetric_key_data->userKey, + TPM_AES_BITS, + &(tpm_symmetric_key_data->aes_enc_key)); + if (irc != 0) { + printf("TPM_SymmetricKeyData_SetKeys: Error (fatal) generating enc key\n"); + TPM_OpenSSL_PrintError(); + rc = TPM_FAIL; /* should never occur, null pointers or bad bit size */ + } + } + if (rc == 0) { + irc = AES_set_decrypt_key(tpm_symmetric_key_data->userKey, + TPM_AES_BITS, + &(tpm_symmetric_key_data->aes_dec_key)); + if (irc != 0) { + printf("TPM_SymmetricKeyData_SetKeys: Error (fatal) generating dec key\n"); + TPM_OpenSSL_PrintError(); + rc = TPM_FAIL; /* should never occur, null pointers or bad bit size */ + } + } + return rc; +} + +/* TPM_SymmetricKeyData_Encrypt() is AES non-portable code to encrypt 'decrypt_data' to + 'encrypt_data' + + The stream is padded as per PKCS#7 / RFC2630 + + 'encrypt_data' must be free by the caller +*/ + +TPM_RESULT TPM_SymmetricKeyData_Encrypt(unsigned char **encrypt_data, /* output, caller frees */ + uint32_t *encrypt_length, /* output */ + const unsigned char *decrypt_data, /* input */ + uint32_t decrypt_length, /* input */ + const TPM_SYMMETRIC_KEY_TOKEN + tpm_symmetric_key_token) /* input */ +{ + TPM_RESULT rc = 0; + uint32_t pad_length; + unsigned char *decrypt_data_pad; + unsigned char ivec[TPM_AES_BLOCK_SIZE]; /* initial chaining vector */ + TPM_SYMMETRIC_KEY_DATA *tpm_symmetric_key_data = + (TPM_SYMMETRIC_KEY_DATA *)tpm_symmetric_key_token; + + printf(" TPM_SymmetricKeyData_Encrypt: Length %u\n", decrypt_length); + decrypt_data_pad = NULL; /* freed @1 */ + if (rc == 0) { + /* calculate the pad length and padded data length */ + pad_length = TPM_AES_BLOCK_SIZE - (decrypt_length % TPM_AES_BLOCK_SIZE); + *encrypt_length = decrypt_length + pad_length; + printf(" TPM_SymmetricKeyData_Encrypt: Padded length %u pad length %u\n", + *encrypt_length, pad_length); + /* allocate memory for the encrypted response */ + rc = TPM_Malloc(encrypt_data, *encrypt_length); + } + /* allocate memory for the padded decrypted data */ + if (rc == 0) { + rc = TPM_Malloc(&decrypt_data_pad, *encrypt_length); + } + /* pad the decrypted clear text data */ + if (rc == 0) { + /* unpadded original data */ + memcpy(decrypt_data_pad, decrypt_data, decrypt_length); + /* last gets pad = pad length */ + memset(decrypt_data_pad + decrypt_length, pad_length, pad_length); + /* set the IV */ + memset(ivec, 0, sizeof(ivec)); + /* encrypt the padded input to the output */ + TPM_PrintFour(" TPM_SymmetricKeyData_Encrypt: Input", decrypt_data_pad); + AES_cbc_encrypt(decrypt_data_pad, + *encrypt_data, + *encrypt_length, + &(tpm_symmetric_key_data->aes_enc_key), + ivec, + AES_ENCRYPT); + TPM_PrintFour(" TPM_SymmetricKeyData_Encrypt: Output", *encrypt_data); + } + free(decrypt_data_pad); /* @1 */ + return rc; +} + +/* TPM_SymmetricKeyData_Decrypt() is AES non-portable code to decrypt 'encrypt_data' to + 'decrypt_data' + + The stream must be padded as per PKCS#7 / RFC2630 + + decrypt_data must be free by the caller +*/ + +TPM_RESULT TPM_SymmetricKeyData_Decrypt(unsigned char **decrypt_data, /* output, caller frees */ + uint32_t *decrypt_length, /* output */ + const unsigned char *encrypt_data, /* input */ + uint32_t encrypt_length, /* input */ + const TPM_SYMMETRIC_KEY_TOKEN + tpm_symmetric_key_token) /* input */ +{ + TPM_RESULT rc = 0; + uint32_t pad_length; + uint32_t i; + unsigned char *pad_data; + unsigned char ivec[TPM_AES_BLOCK_SIZE]; /* initial chaining vector */ + TPM_SYMMETRIC_KEY_DATA *tpm_symmetric_key_data = + (TPM_SYMMETRIC_KEY_DATA *)tpm_symmetric_key_token; + + printf(" TPM_SymmetricKeyData_Decrypt: Length %u\n", encrypt_length); + /* sanity check encrypted length */ + if (rc == 0) { + if (encrypt_length < TPM_AES_BLOCK_SIZE) { + printf("TPM_SymmetricKeyData_Decrypt: Error, bad length\n"); + rc = TPM_DECRYPT_ERROR; + } + } + /* allocate memory for the padded decrypted data */ + if (rc == 0) { + rc = TPM_Malloc(decrypt_data, encrypt_length); + } + /* decrypt the input to the padded output */ + if (rc == 0) { + /* set the IV */ + memset(ivec, 0, sizeof(ivec)); + /* decrypt the padded input to the output */ + TPM_PrintFour(" TPM_SymmetricKeyData_Decrypt: Input", encrypt_data); + AES_cbc_encrypt(encrypt_data, + *decrypt_data, + encrypt_length, + &(tpm_symmetric_key_data->aes_dec_key), + ivec, + AES_DECRYPT); + TPM_PrintFour(" TPM_SymmetricKeyData_Decrypt: Output", *decrypt_data); + } + /* get the pad length */ + if (rc == 0) { + /* get the pad length from the last byte */ + pad_length = (uint32_t)*(*decrypt_data + encrypt_length - 1); + /* sanity check the pad length */ + printf(" TPM_SymmetricKeyData_Decrypt: Pad length %u\n", pad_length); + if ((pad_length == 0) || + (pad_length > TPM_AES_BLOCK_SIZE)) { + printf("TPM_SymmetricKeyData_Decrypt: Error, illegal pad length\n"); + rc = TPM_DECRYPT_ERROR; + } + } + if (rc == 0) { + /* get the unpadded length */ + *decrypt_length = encrypt_length - pad_length; + /* pad starting point */ + pad_data = *decrypt_data + *decrypt_length; + /* sanity check the pad */ + for (i = 0 ; i < pad_length ; i++, pad_data++) { + if (*pad_data != pad_length) { + printf("TPM_SymmetricKeyData_Decrypt: Error, bad pad %02x at index %u\n", + *pad_data, i); + rc = TPM_DECRYPT_ERROR; + } + } + } + return rc; +} + +/* TPM_SymmetricKeyData_CtrCrypt() does an encrypt or decrypt (they are the same XOR operation with + a CTR mode pad) of 'data_in' to 'data_out'. + + NOTE: This function looks general, but is currently hard coded to AES128. + + 'symmetric key' is the raw key, not converted to a non-portable form + 'ctr_in' is the initial CTR value before possible truncation +*/ + +TPM_RESULT TPM_SymmetricKeyData_CtrCrypt(unsigned char *data_out, /* output */ + const unsigned char *data_in, /* input */ + uint32_t data_size, /* input */ + const unsigned char *symmetric_key, /* input */ + uint32_t symmetric_key_size, /* input */ + const unsigned char *ctr_in, /* input */ + uint32_t ctr_in_size) /* input */ +{ + TPM_RESULT rc = 0; + TPM_SYMMETRIC_KEY_DATA *tpm_symmetric_key_data = NULL; /* freed @1 */ + unsigned char ctr[TPM_AES_BLOCK_SIZE]; + + printf(" TPM_SymmetricKeyData_CtrCrypt: data_size %u\n", data_size); + /* allocate memory for the key token. The token is opaque in the API, but at this low level, + the code understands the TPM_SYMMETRIC_KEY_DATA structure */ + if (rc == 0) { + rc = TPM_SymmetricKeyData_New((TPM_SYMMETRIC_KEY_TOKEN *)&tpm_symmetric_key_data); + } + /* convert the raw key to the AES key, truncating as required */ + if (rc == 0) { + rc = TPM_SymmetricKeyData_SetKey(tpm_symmetric_key_data, + symmetric_key, + symmetric_key_size); + } + /* check the input CTR size, it can be truncated, but cannot be smaller than the AES key */ + if (rc == 0) { + if (ctr_in_size < sizeof(ctr)) { + printf(" TPM_SymmetricKeyData_CtrCrypt: Error (fatal)" + ", CTR size %u too small for AES key\n", ctr_in_size); + rc = TPM_FAIL; /* should never occur */ + } + } + if (rc == 0) { + /* make a truncated copy of CTR, since AES_ctr128_encrypt alters the value */ + memcpy(ctr, ctr_in, sizeof(ctr)); + printf(" TPM_SymmetricKeyData_CtrCrypt: Calling AES in CTR mode\n"); + TPM_PrintFour(" TPM_SymmetricKeyData_CtrCrypt: CTR", ctr); + rc = TPM_AES_ctr128_encrypt(data_out, + data_in, + data_size, + &(tpm_symmetric_key_data->aes_enc_key), + ctr); + } + TPM_SymmetricKeyData_Free((TPM_SYMMETRIC_KEY_TOKEN *)&tpm_symmetric_key_data); /* @1 */ + return rc; +} + +/* TPM_AES_ctr128_encrypt() is a TPM variant of the openSSL AES_ctr128_encrypt() function that + increments only the low 4 bytes of the counter. + + openSSL increments the entire CTR array. The TPM does not follow that convention. +*/ + +static TPM_RESULT TPM_AES_ctr128_encrypt(unsigned char *data_out, + const unsigned char *data_in, + uint32_t data_size, + const AES_KEY *aes_enc_key, + unsigned char ctr[TPM_AES_BLOCK_SIZE]) +{ + TPM_RESULT rc = 0; + uint32_t cint; + unsigned char pad_buffer[TPM_AES_BLOCK_SIZE]; /* the XOR pad */ + + printf(" TPM_AES_Ctr128_encrypt:\n"); + while (data_size != 0) { + printf(" TPM_AES_Ctr128_encrypt: data_size %lu\n", (unsigned long)data_size); + /* get an XOR pad array by encrypting the CTR with the AES key */ + AES_encrypt(ctr, pad_buffer, aes_enc_key); + /* partial or full last data block */ + if (data_size <= TPM_AES_BLOCK_SIZE) { + TPM_XOR(data_out, data_in, pad_buffer, data_size); + data_size = 0; + } + /* full block, not the last block */ + else { + TPM_XOR(data_out, data_in, pad_buffer, TPM_AES_BLOCK_SIZE); + data_in += TPM_AES_BLOCK_SIZE; + data_out += TPM_AES_BLOCK_SIZE; + data_size -= TPM_AES_BLOCK_SIZE; + } + /* if not the last block, increment CTR, only the low 4 bytes */ + if (data_size != 0) { + /* CTR is a big endian array, so the low 4 bytes are 12-15 */ + cint = LOAD32(ctr, 12); /* byte array to uint32_t */ + cint++; /* increment */ + STORE32(ctr, 12, cint); /* uint32_t to byte array */ + } + } + return rc; +} + +/* TPM_SymmetricKeyData_OfbCrypt() does an encrypt or decrypt (they are the same XOR operation with + a OFB mode pad) of 'data_in' to 'data_out' + + NOTE: This function looks general, but is currently hard coded to AES128. + + 'symmetric key' is the raw key, not converted to a non-portable form + 'ivec_in' is the initial IV value before possible truncation +*/ + +/* openSSL prototype + + void AES_ofb128_encrypt(const unsigned char *in, + unsigned char *out, + const unsigned long length, + const AES_KEY *key, + unsigned char *ivec, + int *num); +*/ + +TPM_RESULT TPM_SymmetricKeyData_OfbCrypt(unsigned char *data_out, /* output */ + const unsigned char *data_in, /* input */ + uint32_t data_size, /* input */ + const unsigned char *symmetric_key, /* in */ + uint32_t symmetric_key_size, /* in */ + unsigned char *ivec_in, /* input */ + uint32_t ivec_in_size) /* input */ +{ + TPM_RESULT rc = 0; + TPM_SYMMETRIC_KEY_DATA *tpm_symmetric_key_data = NULL; /* freed @1 */ + unsigned char ivec[TPM_AES_BLOCK_SIZE]; + int num; + + printf(" TPM_SymmetricKeyData_OfbCrypt: data_size %u\n", data_size); + /* allocate memory for the key token. The token is opaque in the API, but at this low level, + the code understands the TPM_SYMMETRIC_KEY_DATA structure */ + if (rc == 0) { + rc = TPM_SymmetricKeyData_New((TPM_SYMMETRIC_KEY_TOKEN *)&tpm_symmetric_key_data); + } + /* convert the raw key to the AES key, truncating as required */ + if (rc == 0) { + rc = TPM_SymmetricKeyData_SetKey(tpm_symmetric_key_data, + symmetric_key, + symmetric_key_size); + } + /* check the input OFB size, it can be truncated, but cannot be smaller than the AES key */ + if (rc == 0) { + if (ivec_in_size < sizeof(ivec)) { + printf(" TPM_SymmetricKeyData_OfbCrypt: Error (fatal)," + "IV size %u too small for AES key\n", ivec_in_size); + rc = TPM_FAIL; /* should never occur */ + } + } + if (rc == 0) { + /* make a truncated copy of IV, since AES_ofb128_encrypt alters the value */ + memcpy(ivec, ivec_in, sizeof(ivec)); + num = 0; + printf(" TPM_SymmetricKeyData_OfbCrypt: Calling AES in OFB mode\n"); + TPM_PrintFour(" TPM_SymmetricKeyData_OfbCrypt: IV", ivec); + AES_ofb128_encrypt(data_in, + data_out, + data_size, + &(tpm_symmetric_key_data->aes_enc_key), + ivec, + &num); + } + TPM_SymmetricKeyData_Free((TPM_SYMMETRIC_KEY_TOKEN *)&tpm_symmetric_key_data); + return rc; +} + +#endif /* TPM_AES */ |