/********************************************************************************/
/*                                                                              */
/*                      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"


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

/* 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_PrintFour("  TPM_RSAPrivateDecrypt: Decrypt data", decrypt_data);
    }
    if (rsa_pri_key != NULL) {
        RSA_free(rsa_pri_key);          /* @1 */
    }
    free(padded_data);                  /* @2 */
    return rc;
}

/* TPM_RSAPublicEncrypt() pads 'decrypt_data' to 'encrypt_data_size' and encrypts using the public
   key 'n, e'.
*/

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

/* 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_PrintFour("  TPM_RSAPublicEncryptRaw: Decrypt data", decrypt_data);
        /* 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;
}

/* 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.
*/

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_PrintFour("  TPM_RSASignDER: Input message", message);
        /* 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 */
    }
    /* 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;
        }
    }
    /* 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;
        }
    }
    /* 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");
        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_PrintFour("  TPM_SymmetricKeyData_Crypt: Input", data_in);
        /* 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 */
