/********************************************************************************/
/*										*/
/*			     TSS Library Dependent Crypto Support		*/
/*			     Written by Ken Goldman				*/
/*		       IBM Thomas J. Watson Research Center			*/
/*		ECC Salt functions written by Bill Martin			*/
/*										*/
/* (c) Copyright IBM Corporation 2015 - 2019.					*/
/*										*/
/* 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.		*/
/********************************************************************************/

/* Interface to OpenSSL version 1.0 or 1.1 crypto library */

#include <string.h>
#include <stdio.h>

#ifdef TPM_POSIX
#include <netinet/in.h>
#endif
#ifdef TPM_WINDOWS
#include <winsock2.h>
#endif

#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
#include <openssl/aes.h>
#ifndef TPM_TSS_NORSA
#include <openssl/rsa.h>
#endif
#include <openssl/rand.h>
#include <openssl/engine.h>

#include <ibmtss/tssresponsecode.h>
#include <ibmtss/tssutils.h>
#include <ibmtss/tssprint.h>
#include <ibmtss/tsserror.h>

#include <ibmtss/tsscryptoh.h>
#include <ibmtss/tsscrypto.h>

extern int tssVverbose;
extern int tssVerbose;

/* openssl compatibility code */

#if OPENSSL_VERSION_NUMBER < 0x10101000
#define EC_POINT_set_affine_coordinates(a,b,c,d,e)  EC_POINT_set_affine_coordinates_GFp(a,b,c,d,e)
#define EC_POINT_get_affine_coordinates(a,b,c,d,e)  EC_POINT_get_affine_coordinates_GFp(a,b,c,d,e)
#endif

/* local prototypes */

static TPM_RC TSS_Hash_GetMd(const EVP_MD **md,
			     TPMI_ALG_HASH hashAlg);

#ifndef TPM_TSS_NOECC

/* ECC salt */

typedef struct
{
    EC_GROUP            *G;
    BN_CTX              *ctx;
} CURVE_DATA;

static TPM_RC TSS_ECC_GeneratePlatformEphemeralKey(CURVE_DATA *eCurveData,
						   EC_KEY *myecc,
						   TPMI_ECC_CURVE curveID);
static TPM_RC TSS_BN_new(BIGNUM **bn);
static TPM_RC TSS_BN_hex2bn(BIGNUM **bn, const char *str);
#endif	/* TPM_TSS_NOECC */

#ifndef TPM_TSS_NORSA
static TPM_RC TSS_bin2bn(BIGNUM **bn, const unsigned char *bin, unsigned int bytes);
#endif	/* TPM_TSS_NORSA */

/*
  Initialization
*/

TPM_RC TSS_Crypto_Init(void)
{
    TPM_RC		rc = 0;
#if 0
    int			irc;
#endif

    ERR_load_crypto_strings ();
    OpenSSL_add_all_algorithms();
#if 0
    irc = FIPS_mode_set(1);
    if (irc == 0) {
	if (tssVerbose) printf("TSS_Crypto_Init: Cannot set FIPS mode\n");
    }
#endif
    return rc;
}

/*
  Digests
*/

static TPM_RC TSS_Hash_GetMd(const EVP_MD **md,
			     TPMI_ALG_HASH hashAlg)
{
    TPM_RC		rc = 0;

    if (rc == 0) {
	switch (hashAlg) {
#ifdef TPM_ALG_SHA1
	  case TPM_ALG_SHA1:
	    *md = EVP_get_digestbyname("sha1");
	    break;
#endif
#ifdef TPM_ALG_SHA256	
	  case TPM_ALG_SHA256:
	    *md = EVP_get_digestbyname("sha256");
	    break;
#endif
#ifdef TPM_ALG_SHA384
	  case 	TPM_ALG_SHA384:
	    *md = EVP_get_digestbyname("sha384");
	    break;
#endif
#ifdef TPM_ALG_SHA512
	  case 	TPM_ALG_SHA512:
	    *md = EVP_get_digestbyname("sha512");
	    break;
#endif
	  default:
	    rc = TSS_RC_BAD_HASH_ALGORITHM;
	}
    }
    return rc;
}

/* On call, digest->hashAlg is the desired hash algorithm

   length 0 is ignored, buffer NULL terminates list.
*/

TPM_RC TSS_HMAC_Generate_valist(TPMT_HA *digest,		/* largest size of a digest */
				const TPM2B_KEY *hmacKey,
				va_list ap)
{
    TPM_RC		rc = 0;
    int 		irc = 0;
    int			done = FALSE;
    const EVP_MD 	*md;	/* message digest method */
#if OPENSSL_VERSION_NUMBER < 0x10100000
    HMAC_CTX 		ctx;
#else
    HMAC_CTX 		*ctx;
#endif
    int			length;
    uint8_t 		*buffer;
    
#if OPENSSL_VERSION_NUMBER < 0x10100000
    HMAC_CTX_init(&ctx);
#else
    ctx = HMAC_CTX_new();
#endif
    if (rc == 0) {
	rc = TSS_Hash_GetMd(&md, digest->hashAlg);
    }
    if (rc == 0) {
#if OPENSSL_VERSION_NUMBER < 0x10100000
	irc = HMAC_Init_ex(&ctx,
			   hmacKey->b.buffer, hmacKey->b.size,	/* HMAC key */
			   md,					/* message digest method */
			   NULL);
#else
	irc = HMAC_Init_ex(ctx,
			   hmacKey->b.buffer, hmacKey->b.size,	/* HMAC key */
			   md,					/* message digest method */
			   NULL);
#endif
	
	if (irc == 0) {
	    rc = TSS_RC_HMAC;
	}
    }
    while ((rc == 0) && !done) {
	length = va_arg(ap, int);		/* first vararg is the length */
	buffer = va_arg(ap, unsigned char *);	/* second vararg is the array */
	if (buffer != NULL) {			/* loop until a NULL buffer terminates */
	    if (length < 0) {
		if (tssVerbose) printf("TSS_HMAC_Generate: Length is negative\n");
		rc = TSS_RC_HMAC;
	    }
	    else {
#if OPENSSL_VERSION_NUMBER < 0x10100000
		irc = HMAC_Update(&ctx, buffer, length);
#else
		irc = HMAC_Update(ctx, buffer, length);
#endif
		if (irc == 0) {
		    if (tssVerbose) printf("TSS_HMAC_Generate: HMAC_Update failed\n");
		    rc = TSS_RC_HMAC;
		}
	    }
 	}
	else {
	    done = TRUE;
	}
    }

    if (rc == 0) {
#if OPENSSL_VERSION_NUMBER < 0x10100000
	irc = HMAC_Final(&ctx, (uint8_t *)&digest->digest, NULL);
#else
	irc = HMAC_Final(ctx, (uint8_t *)&digest->digest, NULL);
#endif
	if (irc == 0) {
	    rc = TSS_RC_HMAC;
	}
    }
#if OPENSSL_VERSION_NUMBER < 0x10100000
    HMAC_CTX_cleanup(&ctx);
#else
    HMAC_CTX_free(ctx);
#endif
    return rc;
}

/*
  valist is int length, unsigned char *buffer pairs
  
  length 0 is ignored, buffer NULL terminates list.
*/

TPM_RC TSS_Hash_Generate_valist(TPMT_HA *digest,		/* largest size of a digest */
				va_list ap)
{
    TPM_RC		rc = 0;
    int			irc = 0;
    int			done = FALSE;
    int			length;
    uint8_t 		*buffer;
    EVP_MD_CTX 		*mdctx;
    const EVP_MD 	*md;

    if (rc == 0) {
	mdctx = EVP_MD_CTX_create();
        if (mdctx == NULL) {
	    if (tssVerbose) printf("TSS_Hash_Generate: EVP_MD_CTX_create failed\n");
	    rc = TSS_RC_OUT_OF_MEMORY;
	}
    }
    if (rc == 0) {
	rc = TSS_Hash_GetMd(&md, digest->hashAlg);
    }
    if (rc == 0) {
	irc = EVP_DigestInit_ex(mdctx, md, NULL);
	if (irc != 1) {
	    rc = TSS_RC_HASH;
	}
    }
    while ((rc == 0) && !done) {
	length = va_arg(ap, int);		/* first vararg is the length */
	buffer = va_arg(ap, unsigned char *);	/* second vararg is the array */
	if (buffer != NULL) {			/* loop until a NULL buffer terminates */
	    if (length < 0) {
		if (tssVerbose) printf("TSS_Hash_Generate: Length is negative\n");
		rc = TSS_RC_HASH;
	    }
	    else {
		/* if (tssVverbose) TSS_PrintAll("TSS_Hash_Generate:", buffer, length); */
		if (length != 0) {
		    EVP_DigestUpdate(mdctx, buffer, length);
		}
	    }
	}
	else {
	    done = TRUE;
	}
    }
    if (rc == 0) {
	EVP_DigestFinal_ex(mdctx, (uint8_t *)&digest->digest, NULL);
    }
    EVP_MD_CTX_destroy(mdctx);
    return rc;
}

/* Random Numbers */

TPM_RC TSS_RandBytes(unsigned char *buffer, uint32_t size)
{
    TPM_RC 	rc = 0;
    int		irc = 0;

    irc = RAND_bytes(buffer, size);
    if (irc != 1) {
	if (tssVerbose) printf("TSS_RandBytes: Random number generation failed\n");
	rc = TSS_RC_RNG_FAILURE;
    }
    return rc;
}

/*
  RSA functions
*/

#ifndef TPM_TSS_NORSA

/* TSS_RsaNew() allocates an openssl RSA key token.

   This abstracts the crypto library specific allocation.

   For Openssl, rsaKey is an RSA structure.
*/

TPM_RC TSS_RsaNew(void **rsaKey)
{
    TPM_RC  	rc = 0;

    /* sanity check for the free */
    if (rc == 0) {
	if (*rsaKey != NULL) {
            if (tssVerbose)
		printf("TSS_RsaNew: Error (fatal), token %p should be NULL\n",
		       *rsaKey);
            rc = TSS_RC_ALLOC_INPUT;
	}
    }
    /* construct the OpenSSL private key object */
    if (rc == 0) {
        *rsaKey = RSA_new();                        	/* freed by caller */
        if (*rsaKey == NULL) {
            if (tssVerbose) printf("TSS_RsaNew: Error in RSA_new()\n");
            rc = TSS_RC_RSA_KEY_CONVERT;
        }
    }
    return rc;
}

/* TSS_RsaFree() frees an openssl RSA key token.

   This abstracts the crypto library specific free.
   
   For Openssl, rsaKey is an RSA structure.
*/

void TSS_RsaFree(void *rsaKey)
{
    if (rsaKey != NULL) {
        RSA_free(rsaKey); 
    }
    return;
}

/* TSS_RSAGeneratePublicToken() is deprecated for application use, since it is openssl library
   dependent.

   Use TSS_RSAGeneratePublicTokenI().
*/

TPM_RC TSS_RSAGeneratePublicToken(RSA **rsa_pub_key,		/* freed by caller */
				  const unsigned char *narr,    /* public modulus */
				  uint32_t nbytes,
				  const unsigned char *earr,    /* public exponent */
				  uint32_t ebytes)
{
    TPM_RC  	rc = 0;
    rc = TSS_RSAGeneratePublicTokenI((void **)rsa_pub_key,
				     narr, 
				     nbytes,
				     earr,
				     ebytes);
    return rc;
}

/* TSS_RSAGeneratePublicTokenI() generates an RSA key token from n and e

   Free rsa_pub_key using TSS_RsaFree();
 */

TPM_RC TSS_RSAGeneratePublicTokenI(void **rsa_pub_key,		/* freed by caller */
				   const unsigned char *narr,    /* public modulus */
				   uint32_t nbytes,
				   const unsigned char *earr,    /* public exponent */
				   uint32_t ebytes)
{
    TPM_RC  	rc = 0;
    BIGNUM *    n = NULL;
    BIGNUM *    e = NULL;
    RSA **	rsaPubKey = (RSA **)rsa_pub_key;	/* openssl specific structure */

    /* construct the OpenSSL private key object */
    if (rc == 0) {
	rc = TSS_RsaNew(rsa_pub_key);
    }
    if (rc == 0) {
        rc = TSS_bin2bn(&n, narr, nbytes);	/* freed by caller */
    }
    if (rc == 0) {
        rc = TSS_bin2bn(&e, earr, ebytes);	/* freed by caller */
    }
    if (rc == 0) {
#if OPENSSL_VERSION_NUMBER < 0x10100000
        (*rsaPubKey)->n = n;
        (*rsaPubKey)->e = e;
        (*rsaPubKey)->d = NULL;
#else
	int irc = RSA_set0_key(*rsaPubKey, n, e, NULL);
	if (irc != 1) {
            if (tssVerbose) printf("TSS_RSAGeneratePublicTokenI: Error in RSA_set0_key()\n");
            rc = TSS_RC_RSA_KEY_CONVERT;
	}
#endif
    }
    return rc;
}

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

TPM_RC TSS_RSAPublicEncrypt(unsigned char *encrypt_data,    /* encrypted data */
			    size_t encrypt_data_size,       /* size of encrypted data buffer */
			    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,
			    unsigned char *p,		/* encoding parameter */
			    int pl,
			    TPMI_ALG_HASH halg)		/* OAEP hash algorithm */
{
    TPM_RC  	rc = 0;
    int         irc;
    RSA         *rsa_pub_key = NULL;
    unsigned char *padded_data = NULL;
 
    if (tssVverbose) printf(" TSS_RSAPublicEncrypt: Input data size %lu\n",
			    (unsigned long)decrypt_data_size);
    /* intermediate buffer for the decrypted but still padded data */
    if (rc == 0) {
        rc = TSS_Malloc(&padded_data, (uint32_t)encrypt_data_size);               /* freed @2 */
    }
    /* construct the OpenSSL public key object */
    if (rc == 0) {
	rc = TSS_RSAGeneratePublicTokenI((void **)&rsa_pub_key,	/* freed @1 */
					 narr,      	/* public modulus */
					 nbytes,
					 earr,      	/* public exponent */
					 ebytes);
    }
    if (rc == 0) {
	padded_data[0] = 0x00;
	rc = TSS_RSA_padding_add_PKCS1_OAEP(padded_data,		    /* to */
					    (uint32_t)encrypt_data_size,    /* to length */
					    decrypt_data,		    /* from */
					    (uint32_t)decrypt_data_size,    /* from length */
					    p,		/* encoding parameter */
					    pl,		/* encoding parameter length */
					    halg);	/* OAEP hash algorithm */
    }
    if (rc == 0) {
        if (tssVverbose)
	    printf("  TSS_RSAPublicEncrypt: Padded data size %lu\n",
		   (unsigned long)encrypt_data_size);
        if (tssVverbose) TSS_PrintAll("  TPM_RSAPublicEncrypt: Padded data", padded_data,
				      (uint32_t)encrypt_data_size);
        /* 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((int)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) {
	    if (tssVerbose) printf("TSS_RSAPublicEncrypt: Error in RSA_public_encrypt()\n");
	    rc = TSS_RC_RSA_ENCRYPT;
	}
    }
    if (rc == 0) {
        if (tssVverbose) printf("  TSS_RSAPublicEncrypt: RSA_public_encrypt() success\n");
    }
    TSS_RsaFree(rsa_pub_key);          /* @1 */
    free(padded_data);                  /* @2 */
    return rc;
}

#endif /* TPM_TSS_NORSA */

#ifndef TPM_TSS_NOECC

/* TSS_GeneratePlatformEphemeralKey sets the EC parameters to curveID for generating the ephemeral
   key. Some OpenSSL versions do not come with NIST p256.

   Supports NIST p256 and NIST p384.  curveID must be correct from the caller.
   
   On success, eCurveData->G must be freed by the caller.
*/

static TPM_RC TSS_ECC_GeneratePlatformEphemeralKey(CURVE_DATA *eCurveData,
						   EC_KEY *myecc,
						   TPMI_ECC_CURVE curveID)
{
    TPM_RC      rc = 0;
    BIGNUM 	*p = NULL;
    BIGNUM 	*a = NULL;
    BIGNUM 	*b = NULL;
    BIGNUM 	*x = NULL;
    BIGNUM 	*y = NULL;
    BIGNUM 	*z = NULL;
    EC_POINT    *G = NULL; 	/* generator */

    /* Set the EC parameters to NISTp256 or NISTp384. Openssl versions might not have NISTP256 as a
     possible parameter so we make it possible by setting the curve ourselves.*/

    /*  NIST P256  from FIPS 186-3 */
    if (curveID == TPM_ECC_NIST_P256) {
	if (rc == 0) {
	    if (tssVverbose) printf("TSS_ECC_GeneratePlatformEphemeralKey: Converting p\n");
	    rc = TSS_BN_hex2bn(&p,		/* freed @1 */
			       "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF");
	}
	if (rc == 0) {
	    if (tssVverbose) printf("TSS_ECC_GeneratePlatformEphemeralKey: Converting a (p-3)\n");
	    rc = TSS_BN_hex2bn(&a,		/* freed @2 */
			       "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC");
	}
	if (rc == 0) {
	    if (tssVverbose) printf("TSS_ECC_GeneratePlatformEphemeralKey: Converting b\n");
	    rc = TSS_BN_hex2bn(&b,		/* freed @3 */
			       "5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B");
	}
    }
    /*  NIST P384 */
    else if (curveID == TPM_ECC_NIST_P384) {
	if (rc == 0) {
	    if (tssVverbose) printf("TSS_ECC_GeneratePlatformEphemeralKey: Converting p\n");
	    rc = TSS_BN_hex2bn(&p,		/* freed @1 */
			       "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
			       "FFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF");
	}
	if (rc == 0) {
	    if (tssVverbose) printf("TSS_ECC_GeneratePlatformEphemeralKey: Converting a (p-3)\n");
	    rc = TSS_BN_hex2bn(&a,		/* freed @2 */
			       "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
			       "FFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC");
	}
	if (rc == 0) {
	    if (tssVverbose) printf("TSS_ECC_GeneratePlatformEphemeralKey: Converting b\n");
	    rc = TSS_BN_hex2bn(&b,		/* freed @3 */
			       "B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE814112"
			       "0314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF");
	}
    }
    if (rc == 0) {
	if (tssVverbose) printf("TSS_ECC_GeneratePlatformEphemeralKey: New group\n");
	eCurveData->G = EC_GROUP_new(EC_GFp_mont_method());	/* freed @4 */
	if (eCurveData->G == NULL) {
	    if (tssVerbose) printf("TSS_ECC_GeneratePlatformEphemeralKey: "
				   "Error creating new group\n");
	    rc = TSS_RC_EC_EPHEMERAL_FAILURE;
	}
    }
    if (rc == 0) {
	if (tssVverbose) printf("TSS_ECC_GeneratePlatformEphemeralKey: Set the curve prime\n");
	if (EC_GROUP_set_curve_GFp(eCurveData->G, p, a, b, eCurveData->ctx) == 0) {
	    if (tssVerbose) printf("TSS_ECC_GeneratePlatformEphemeralKey: "
				   "Error seting curve prime\n");
	    rc = TSS_RC_EC_EPHEMERAL_FAILURE;
	}
    }
    if (rc == 0) {
	G = EC_POINT_new(eCurveData->G);			/* freed @5 */
	if (G == NULL ){
	    if (tssVerbose) printf("TSS_ECC_GeneratePlatformEphemeralKey: EC_POINT_new failed\n");
	    rc = TSS_RC_OUT_OF_MEMORY;
	}
    }
    if (curveID == TPM_ECC_NIST_P256) {
	if (rc == 0) {			/* G.x */
	    rc = TSS_BN_hex2bn(&x,					/* freed @6 */
			       "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296");
	}
	if (rc == 0) {			/* G.y */
	    rc = TSS_BN_hex2bn(&y,					/* freed @7 */
			       "4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5");
	}
    }
    else if (curveID == TPM_ECC_NIST_P384) {
	if (rc == 0) {		/* G.x */
	    rc = TSS_BN_hex2bn(&x,					/* freed @6 */
			       "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B98"
			       "59F741E082542A385502F25DBF55296C3A545E3872760AB7");
	}
	if (rc == 0) {		/* G.y */
	    rc = TSS_BN_hex2bn(&y,					/* freed @7 */
			       "3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147c"
			       "e9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f");
	}
    }
    if (rc == 0) {
	if (EC_POINT_set_affine_coordinates(eCurveData->G, G, x, y, eCurveData->ctx) == 0) {
	    if (tssVerbose) printf("TSS_ECC_GeneratePlatformEphemeralKey: Error, "
				   "Cannot create TPM public point from coordinates\n");
	    rc = TSS_RC_EC_EPHEMERAL_FAILURE;
	}
    }
    /* sanity check to see if point is on the curve */
    if (rc == 0) {
	if (EC_POINT_is_on_curve(eCurveData->G, G, eCurveData->ctx) == 0) {
	    if (tssVerbose) printf("TSS_ECC_GeneratePlatformEphemeralKey: Error, "
				   "Point not on curve\n");
	    rc = TSS_RC_EC_EPHEMERAL_FAILURE;
	}
    }
    if (curveID == TPM_ECC_NIST_P256) {
	if (rc == 0) {		/* q */
	    rc = TSS_BN_hex2bn(&z,					/* freed @8 */
			       "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551");
	}
    }
    else if (curveID == TPM_ECC_NIST_P384) {
	if (rc == 0) {		/* q */
	    rc = TSS_BN_hex2bn(&z,					/* freed @8 */
			       "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
			       "C7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973");
	}
    }
    if (rc == 0) {
	if (EC_GROUP_set_generator(eCurveData->G, G, z, BN_value_one()) == 0) {
	    if (tssVerbose) printf("TSS_ECC_GeneratePlatformEphemeralKey: Error, "
				   "EC_GROUP_set_generator()\n");
	    rc = TSS_RC_EC_EPHEMERAL_FAILURE;
        }
    }
    if (rc == 0) {
	if (EC_GROUP_check(eCurveData->G, eCurveData->ctx) == 0) {
	    if (tssVerbose) printf("TSS_ECC_GeneratePlatformEphemeralKey: Error, "
				   "EC_GROUP_check()\n");
	    rc = TSS_RC_EC_EPHEMERAL_FAILURE;
        }
    }
    if (rc == 0) {
	if (EC_KEY_set_group(myecc, eCurveData->G) == 0) {
	    if (tssVerbose) printf("TSS_ECC_GeneratePlatformEphemeralKey: Error, "
				   "EC_KEY_set_group()\n");
	    rc = TSS_RC_EC_EPHEMERAL_FAILURE;
        }
    }
    if (rc == 0) {
#if 0
	if (tssVverbose) printf("TSS_ECC_GeneratePlatformEphemeralKey: "
				"Address of eCurveData->G is %p\n", eCurveData->G);
	if (tssVverbose) printf("TSS_ECC_GeneratePlatformEphemeralKey: "
				"Address of eCurveData->CTX is %p\n", eCurveData->ctx);
#endif
	if (tssVverbose) printf("TSS_ECC_GeneratePlatformEphemeralKey: "
				"Set group for key\n");
    }
    /* Create the public/private EC key pair here */
    if (rc == 0) {
	if (EC_KEY_generate_key(myecc) == 0) 	{
	    if (tssVerbose) printf("TSS_ECC_GeneratePlatformEphemeralKey: "
				   "Error generating the ECC key.\n");
	    rc = TSS_RC_EC_EPHEMERAL_FAILURE;
	}
    }
    if (rc == 0) {
	if (!EC_KEY_check_key(myecc)) {
	    if (tssVerbose) printf("TSS_ECC_GeneratePlatformEphemeralKey: "
				   "Error on EC_KEY_check_key()\n");
	    rc = TSS_RC_EC_EPHEMERAL_FAILURE;
	}
    }
    if (p != NULL)	BN_clear_free(p);	/* @1 */
    if (a != NULL)	BN_clear_free(a);	/* @2 */
    if (b != NULL) 	BN_clear_free(b);	/* @3 */
    if (rc != 0) {				/* else freed by caller */
	EC_GROUP_free(eCurveData->G);	/* @4 */	
	/* EC_POINT_free(G);		/\* @5  *\/ */
    }
    EC_POINT_free(G);		/* @5  */
    if (x != NULL)	BN_clear_free(x);	/* @6 */
    if (y != NULL)	BN_clear_free(y);	/* @7 */
    if (z != NULL)	BN_clear_free(z);	/* @8 */

    /* don't free the key info.  This curve was constructed out of parameters, not of the openssl
       library */
    /* EC_KEY_free(myecc) */
    /* EC_POINT_free(G); */
    return rc;
}

/* TSS_ECC_Salt() returns both the plaintext and excrypted salt, based on the salt key bPublic.

*/

TPM_RC TSS_ECC_Salt(TPM2B_DIGEST 		*salt,
		    TPM2B_ENCRYPTED_SECRET	*encryptedSalt,
		    TPMT_PUBLIC			*publicArea)
{
    TPM_RC		rc = 0;
    EC_KEY		*myecc = NULL;		/* ephemeral key */
    const BIGNUM	*d_caller; 		/* ephemeral private key */
    const EC_POINT	*callerPointPub; 	/* ephemeral public key */
    EC_POINT		*tpmPointPub = NULL;
    BIGNUM		*p_tpmX = NULL;
    BIGNUM		*bigY = NULL;
    BIGNUM 		*zBn = NULL;
    EC_POINT 		*rPoint = NULL;
    BIGNUM 		*thepoint = NULL;
    BIGNUM		*sharedX = NULL;
    BIGNUM		*yBn = NULL;
    uint32_t		sizeInBytes;
    uint32_t		sizeInBits;
    uint8_t             *sharedXBin = NULL;
    unsigned int	lengthSharedXBin;
    BIGNUM		*p_caller_Xbn = NULL;
    BIGNUM		*p_caller_Ybn = NULL; 
    uint8_t		*p_caller_Xbin = NULL;
    uint8_t		*p_caller_Ybin = NULL;
    uint8_t		*p_tpmXbin = NULL;
    unsigned int 	length_p_caller_Xbin;
    unsigned int 	length_p_caller_Ybin;
    unsigned int	length_p_tpmXbin;
    TPM2B_ECC_PARAMETER	sharedX_For_KDFE;
    TPM2B_ECC_PARAMETER	p_caller_X_For_KDFE;
    TPM2B_ECC_PARAMETER	p_tpmX_For_KDFE;
    CURVE_DATA 		eCurveData;
    uint16_t		pointInBytes = 0;

    eCurveData.ctx = NULL;	/* for free */
    eCurveData.G = NULL;	/* this is initialized in TSS_ECC_GeneratePlatformEphemeralKey() at
				   EC_GROUP_new() but gcc -O3 emits a warning that it's
				   uninitialized. */
    /* only NIST P256 and NIST P384 are currently supported */
    if (rc == 0) {
	if (((publicArea->parameters.eccDetail.curveID != TPM_ECC_NIST_P256)) &&
	    ((publicArea->parameters.eccDetail.curveID != TPM_ECC_NIST_P384))) {
	    if (tssVerbose)
		printf("TSS_ECC_Salt: ECC curve ID %04x not supported\n",
		       publicArea->parameters.eccDetail.curveID);
	    rc = TSS_RC_BAD_SALT_KEY;
	}
    }
    if (rc == 0) {
	myecc = EC_KEY_new();		/* freed @1 */
	if (myecc == NULL) {
	    if (tssVerbose) printf("TSS_ECC_Salt: EC_KEY_new failed\n");
	    rc = TSS_RC_OUT_OF_MEMORY;
	}
    }
    if (rc == 0) {
	eCurveData.ctx = BN_CTX_new();	/* freed @16 */
	if (eCurveData.ctx == NULL) {
	    if (tssVerbose) printf("TSS_ECC_Salt: BN_CTX_new failed\n");
	    rc = TSS_RC_OUT_OF_MEMORY;
	}
    }
    /* Generate the TSS EC ephemeral key pair outside the TPM for the salt. The public part of this
       key is actually the 'encrypted' salt. */
    if (rc == 0) {
	if (tssVverbose) printf("TSS_ECC_Salt: "
				"Calling TSS_ECC_GeneratePlatformEphemeralKey\n");
	/* eCurveData->G freed @17 */
	rc = TSS_ECC_GeneratePlatformEphemeralKey(&eCurveData, myecc,
						  publicArea->parameters.eccDetail.curveID);
    }
    if (rc == 0) {
	d_caller = EC_KEY_get0_private_key(myecc);		/* ephemeral private key */
	callerPointPub = EC_KEY_get0_public_key(myecc); 	/* ephemeral public key */
    } 
    /* validate that the public point is on the curve */
    if (rc == 0) 		{
	if (EC_POINT_is_on_curve(eCurveData.G, callerPointPub, eCurveData.ctx) == 0) {
	    if (tssVerbose) printf("TSS_ECC_Salt: "
				   "Generated point not on curve\n"); 
	    rc = TSS_RC_EC_EPHEMERAL_FAILURE;
	}
    }
    if (rc == 0) { 
	/* let d_caller be private scalar and P_caller be public point */
	/* p_tpm is public point. p_tpmX is to be X-coordinate and p_tpmY the
	   Y-coordinate */

	/* Allocate the space for P_tpm */
	tpmPointPub = EC_POINT_new(eCurveData.G); 			/* freed @2 */
	if (tpmPointPub == NULL) {
	    if (tssVerbose) printf("TSS_ECC_Salt: EC_POINT_new failed\n");
	    rc = TSS_RC_OUT_OF_MEMORY;
	}
    }
    /* grab the public point x and y using the parameters passed in */
    if (rc == 0) {
	if (tssVverbose) printf("TSS_ECC_Salt: "
				"Salt key sizes are X: %d and Y: %d\n",
				publicArea->unique.ecc.x.t.size,
				publicArea->unique.ecc.y.t.size);
	p_tpmX = BN_bin2bn((const unsigned char *)&publicArea->unique.ecc.x.t.buffer,
			   publicArea->unique.ecc.x.t.size, NULL);	/* freed @3 */
	if (p_tpmX == NULL) {
	    if (tssVerbose) printf("TSS_ECC_Salt: BN_bin2bn p_tpmX failed\n");
	    rc = TSS_RC_OUT_OF_MEMORY;
	}
    }
    if (rc == 0) {
	bigY = BN_bin2bn((const unsigned char*)&publicArea->unique.ecc.y.t.buffer,
			 publicArea->unique.ecc.y.t.size, bigY);	/* freed @15 */
	if (bigY == NULL) {
	    if (tssVerbose) printf("TSS_ECC_Salt: BN_bin2bn bigY failed\n");
	    rc = TSS_RC_OUT_OF_MEMORY;
	}
    }
    if (rc == 0) {
	if (tssVverbose) printf("TSS_ECC_Salt: "
				"Salt public key X %s\n", BN_bn2hex(p_tpmX));
	if (tssVverbose) printf("TSS_ECC_Salt: "
				"Salt public key Y %s\n", BN_bn2hex(bigY));
    }
    /* Create the openssl form of the TPM salt public key as EC_POINT using coordinates */
    if (rc == 0) {
	if (EC_POINT_set_affine_coordinates
	    (eCurveData.G, tpmPointPub, p_tpmX, bigY, eCurveData.ctx) == 0) {
	    if (tssVerbose) printf("TSS_ECC_Salt: "
				   "Cannot create TPM public point from coordinates\n");
	    rc = TSS_RC_EC_EPHEMERAL_FAILURE;
	}
    }
    /* RFC 2440 Named curves */
    if (rc == 0) {
	if (publicArea->parameters.eccDetail.curveID == TPM_ECC_NIST_P256) {
	    pointInBytes = 256/8;
	    rc = TSS_BN_hex2bn(&zBn,			/* freed @4 */
			       "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551");
	}
	else if (publicArea->parameters.eccDetail.curveID == TPM_ECC_NIST_P384) {
	    pointInBytes = 384/8;
	    rc = TSS_BN_hex2bn(&zBn,			/* freed @4 */
			       "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
			       "C7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973");
	}
	else {
	    if (tssVerbose)
		printf("TSS_ECC_Salt: ECC curve ID %04x not supported\n",
		       publicArea->parameters.eccDetail.curveID);
	    rc = TSS_RC_BAD_SALT_KEY;
	}
	
    }    
    /* add the generator z to the group we are constructing */
    if (rc == 0) {
	if (EC_GROUP_set_generator(eCurveData.G, tpmPointPub, zBn, BN_value_one()) == 0) { 
	    if(tssVerbose) printf ("TSS_ECC_Salt: "
				   "Error EC_GROUP_set_generator()\n");
	    rc = TSS_RC_EC_EPHEMERAL_FAILURE; 
	}
    } 
    /* Check for validity of our group  */
    if (rc == 0) { 
	if (EC_GROUP_check(eCurveData.G, eCurveData.ctx) == 0) { 
	    if (tssVerbose) printf("TSS_ECC_Salt: "
				   "ec_group_check() failed\n"); 
	    rc = TSS_RC_EC_EPHEMERAL_FAILURE;
	}
    }
    /* Check to see if what we think is the TPM point is on the curve */
    if (rc == 0) {
	if (EC_POINT_is_on_curve(eCurveData.G, tpmPointPub, eCurveData.ctx) == 0) { 
	    if (tssVerbose) printf("TSS_ECC_Salt: Error, "
				   "Point not on curve\n");
	    rc = TSS_RC_EC_EPHEMERAL_FAILURE;
	}
	else {
	    if (tssVverbose) printf("TSS_ECC_Salt: "
				    "Validated that TPM EC point is on curve\n");
	}
    }
    if (rc == 0) {
	rPoint = EC_POINT_new(eCurveData.G);
	if (rPoint == NULL) {
	    if (tssVerbose) printf("TSS_ECC_Salt: "
				   "Cannot create rPoint\n");
	    rc = TSS_RC_EC_EPHEMERAL_FAILURE;
	}
    }
    /* Point multiply the TPM public point by the ephemeral scalar. This will produce the
       point from which we get the shared X coordinate, which we keep for use in KDFE. The
       TPM will calculate the same X. */
    if (rc == 0) {
	if (EC_POINT_mul(eCurveData.G, rPoint, NULL, tpmPointPub,
			 d_caller, eCurveData.ctx) == 0) { 
	    if (tssVerbose) printf("TSS_ECC_Salt: "
				   "EC_POINT_mul failed\n") ;
	    rc = TSS_RC_EC_EPHEMERAL_FAILURE; 
	}
	else {
	    if (tssVverbose) printf("TSS_ECC_Salt: "
				    "EC_POINT_mul() succeeded\n");
	}
    }
    /* Check to see if calculated point is on the curve, just for extra sanity */
    if (rc == 0) {  
	if (EC_POINT_is_on_curve(eCurveData.G, rPoint, eCurveData.ctx) == 0) { 
	    if (tssVerbose) printf("TSS_ECC_Salt: Error,"
				   "Point r is not on curve\n");
	    rc = TSS_RC_EC_EPHEMERAL_FAILURE;
	}
	else {
	    if (tssVverbose) printf("TSS_ECC_Salt: "
				    "Point calculated by EC_POINT_mul() is on the curve\n");
	}
    }
    if (rc == 0) {
	thepoint = EC_POINT_point2bn(eCurveData.G, rPoint, POINT_CONVERSION_UNCOMPRESSED,
				     NULL, eCurveData.ctx);	/* freed @6 */
	if (thepoint == NULL) {
	    if (tssVerbose) printf("TSS_ECC_Salt: "
				   "EC_POINT_point2bn thepoint failed\n");
	    rc = TSS_RC_OUT_OF_MEMORY;
	}
    }
    /* get sharedX */
    if (rc == 0) {
	rc = TSS_BN_new(&sharedX);		/* freed @7 */
    }
    if (rc == 0) {
	rc = TSS_BN_new(&yBn);			/* freed @8 */
    }
    if (rc == 0) {
	if (EC_POINT_get_affine_coordinates(eCurveData.G, rPoint,
						sharedX, yBn, eCurveData.ctx) == 0) {
	    if (tssVerbose) printf("TSS_ECC_Salt: "
				   "EC_POINT_get_affine_coordinates() failed\n");
	    rc = TSS_RC_EC_EPHEMERAL_FAILURE;
	}
    }
    if (rc == 0) {
	sizeInBytes = TSS_GetDigestSize(publicArea->nameAlg);
	sizeInBits =  sizeInBytes * 8;
	rc = TSS_Malloc(&sharedXBin, BN_num_bytes(sharedX));		/* freed @9 */
    }
    if (rc == 0) {
	lengthSharedXBin = (unsigned int)BN_bn2bin(sharedX, sharedXBin);
	if (tssVverbose) TSS_PrintAll("TSS_ECC_Salt: sharedXBin",
				      sharedXBin,
				      lengthSharedXBin);
    }
    /* encrypted salt is just the ephemeral public key */
    if (rc == 0) {
	rc = TSS_BN_new(&p_caller_Xbn);			/* freed 10 */
    }
    if (rc == 0) {
	rc = TSS_BN_new(&p_caller_Ybn);			/* freed @11 */
    }
    if (rc == 0) {
	if (tssVverbose) printf("TSS_ECC_Salt: "
				"Allocated space for ephemeral BIGNUM X, Y\n");
    }
    /* Get the X-coordinate and Y-Coordinate */
    if (rc == 0) {
	if (EC_POINT_get_affine_coordinates(eCurveData.G, callerPointPub,
						p_caller_Xbn, p_caller_Ybn,
						eCurveData.ctx) == 0) { 
	    if (tssVerbose) printf("TSS_ECC_Salt: "
				   "EC_POINT_get_affine_coordinates() failed\n");
	    rc = TSS_RC_EC_EPHEMERAL_FAILURE;
	}
	else {
	    if (tssVverbose) printf("TSS_ECC_Salt: "
				    "Retrieved X and Y coordinates from ephemeral public\n");
	}
    }
    if (rc == 0) {
	rc = TSS_Malloc(&p_caller_Xbin, BN_num_bytes(p_caller_Xbn));	/* freed @12 */
    }
    if (rc == 0) {    
	rc = TSS_Malloc(&p_caller_Ybin , BN_num_bytes(p_caller_Ybn));	/* freed @13 */
    }
    if (rc == 0) {    
	if (tssVverbose) printf("TSS_ECC_Salt: "
				"Allocated space for ephemeral binary X and y\n");
    }
    if (rc == 0) {
	rc = TSS_Malloc(&p_tpmXbin, BN_num_bytes(p_tpmX));		/* freed @14 */
    }
    if (rc == 0) {
	length_p_tpmXbin = (unsigned int)BN_bn2bin(p_tpmX, p_tpmXbin);
	if (tssVverbose) TSS_PrintAll("TSS_ECC_Salt: p_tpmXbin ",
				      p_tpmXbin,
				      length_p_tpmXbin);
	length_p_caller_Xbin = (unsigned int)BN_bn2bin(p_caller_Xbn, p_caller_Xbin);
	if (tssVverbose) TSS_PrintAll("TSS_ECC_Salt: p_caller_Xbin",
				      p_caller_Xbin,
				      length_p_caller_Xbin);
	length_p_caller_Ybin = (unsigned int)BN_bn2bin(p_caller_Ybn, p_caller_Ybin);
	if (tssVverbose) TSS_PrintAll("TSS_ECC_Salt: p_caller_Ybin",
				      p_caller_Ybin,
				      length_p_caller_Ybin);
    }
    /* in->encryptedSalt TPM2B_ENCRYPTED_SECRET is a size and TPMU_ENCRYPTED_SECRET secret.
       TPMU_ENCRYPTED_SECRET is a TPMS_ECC_POINT
       TPMS_ECC_POINT has two TPMB_ECC_PARAMETER, x and y
    */
    if (rc == 0) {
	/* TPMS_ECC_POINT size is pointInBytes */
	uint8_t *secret = encryptedSalt->t.secret;	/* TPMU_ENCRYPTED_SECRET pointer for
							   clarity */
	/* TPM2B_ENCRYPTED_SECRET size */
	encryptedSalt->t.size = sizeof(uint16_t) + pointInBytes + sizeof(uint16_t) + pointInBytes;
	/* leading zeros, because some points may be less than 32 bytes */
	memset(secret, 0, sizeof(TPMU_ENCRYPTED_SECRET));
	/* TPMB_ECC_PARAMETER X point */
	*(uint16_t *)(secret) = htons(pointInBytes);
	memcpy(secret +
	       sizeof(uint16_t) + pointInBytes - length_p_caller_Xbin,
	       p_caller_Xbin, length_p_caller_Xbin);
	/* TPMB_ECC_PARAMETER Y point */
	*(uint16_t *)(secret + sizeof(uint16_t) + pointInBytes) = htons(pointInBytes);
	memcpy(secret +
	       sizeof(uint16_t) + pointInBytes +
	       sizeof(uint16_t) + pointInBytes - length_p_caller_Ybin,
	       p_caller_Ybin, length_p_caller_Ybin);
    }
    if (rc == 0) {
	if (tssVverbose) TSS_PrintAll("TSS_ECC_Salt: ECC encrypted salt",
				      encryptedSalt->t.secret,
				      encryptedSalt->t.size);
    }
    /* TPM2B_ECC_PARAMETER sharedX_For_KDFE */
    if (rc == 0) {
	if (lengthSharedXBin > pointInBytes) {
	    if (tssVerbose) printf("TSS_ECC_Salt: "
				   "lengthSharedXBin %u too large\n",
				   lengthSharedXBin);
	    rc = TSS_RC_EC_EPHEMERAL_FAILURE;
	}
    }
    if (rc == 0) {
	sharedX_For_KDFE.t.size = pointInBytes;
	memset(sharedX_For_KDFE.t.buffer, 0, sizeof(sharedX_For_KDFE.t.buffer));
	memcpy(sharedX_For_KDFE.t.buffer + pointInBytes - lengthSharedXBin,
	       sharedXBin, lengthSharedXBin);
	if (tssVverbose) TSS_PrintAll("TSS_ECC_Salt: sharedX_For_KDFE",
				      sharedX_For_KDFE.t.buffer,
				      sharedX_For_KDFE.t.size);
    }
    /* TPM2B_ECC_PARAMETER p_caller_X_For_KDFE */
    if (rc == 0) {
	if (length_p_caller_Xbin > pointInBytes) {
	    if (tssVerbose) printf("TSS_ECC_Salt: "
				   "length_p_caller_Xbin %u too large\n",
				   length_p_caller_Xbin);
	    rc = TSS_RC_EC_EPHEMERAL_FAILURE;
	}
    }
    if (rc == 0) {
	p_caller_X_For_KDFE.t.size = pointInBytes;
	memset(p_caller_X_For_KDFE.t.buffer, 0, sizeof(p_caller_X_For_KDFE.t.buffer));
	memcpy(p_caller_X_For_KDFE.t.buffer + pointInBytes - length_p_caller_Xbin,
	       p_caller_Xbin, length_p_caller_Xbin);
	if (tssVverbose) TSS_PrintAll("TSS_ECC_Salt: p_caller_X_For_KDFE",
				      p_caller_X_For_KDFE.t.buffer,
				      p_caller_X_For_KDFE.t.size);
    }
    /* p_tpmX_For_KDFE */
    if (rc == 0) {
	if (length_p_tpmXbin > pointInBytes) {
	    if (tssVerbose) printf("TSS_ECC_Salt: "
				   "length_p_tpmXbin %u too large\n",
				   length_p_tpmXbin);
	    rc = TSS_RC_EC_EPHEMERAL_FAILURE;
	}
    }
    if (rc == 0) {
	p_tpmX_For_KDFE .t.size = pointInBytes;
	memset(p_tpmX_For_KDFE.t.buffer, 0, sizeof(p_tpmX_For_KDFE.t.buffer));
	memcpy(p_tpmX_For_KDFE.t.buffer + pointInBytes - length_p_tpmXbin,
	       p_tpmXbin, length_p_tpmXbin);
	if (tssVverbose) TSS_PrintAll("TSS_ECC_Salt: p_tpmX_For_KDFE",
				      p_tpmX_For_KDFE.t.buffer,
				      p_tpmX_For_KDFE.t.size);
    }
    if (rc == 0) {
	if (tssVverbose) printf("TSS_ECC_Salt: "
				"Calling TSS_KDFE\n");
	/* TPM2B_DIGEST salt size is the largest supported digest algorithm.
	   This has already been validated when unmarshaling the Name hash algorithm.
	*/
	/* salt = KDFe(tpmKey_NameAlg, sharedX, "SECRET", P_caller, P_tpm,
	   tpmKey_NameAlgSizeBits) */
	salt->t.size = sizeInBytes;
	rc = TSS_KDFE((uint8_t *)&salt->t.buffer, 	/* KDFe output */
		      publicArea->nameAlg,		/* hash algorithm */
		      &sharedX_For_KDFE.b,		/* Z (key) */
		      "SECRET",				/* KDFe label */
		      &p_caller_X_For_KDFE.b,		/* context U */
		      &p_tpmX_For_KDFE.b,		/* context V */
		      sizeInBits);			/* required size of key in bits */
    }
    if (rc == 0) { 
	if (tssVverbose) TSS_PrintAll("TSS_ECC_Salt: salt",
				      (uint8_t *)&salt->t.buffer,
				      salt->t.size);
    }
    /* cleanup */
    if (myecc != NULL) 		EC_KEY_free(myecc);		/* @1 */
    if (tpmPointPub != NULL)    EC_POINT_free(tpmPointPub);	/* @2 */
    if (p_tpmX != NULL)		BN_clear_free(p_tpmX);		/* @3 */
    if (zBn != NULL)            BN_clear_free(zBn);		/* @4 */
    if (rPoint != NULL)		EC_POINT_free(rPoint);		/* @5 */
    if (thepoint != NULL)       BN_clear_free(thepoint);	/* @6 */
    if (sharedX != NULL)        BN_clear_free(sharedX);		/* @7 */
    if (yBn != NULL)		BN_clear_free(yBn);		/* @8 */
    free(sharedXBin);						/* @9 */
    if (p_caller_Xbn != NULL)   BN_clear_free(p_caller_Xbn);	/* @10 */
    if (p_caller_Ybn != NULL)   BN_clear_free(p_caller_Ybn);	/* @11 */
    free(p_caller_Xbin);					/* @12 */
    free(p_caller_Ybin);					/* @13 */
    free(p_tpmXbin);						/* @14 */
    if (bigY != NULL)           BN_clear_free(bigY);		/* @15 */
    EC_GROUP_free(eCurveData.G);				/* @17 */	
    if (eCurveData.ctx != NULL)	BN_CTX_free(eCurveData.ctx);	/* @16 */

    return rc;
}

/* TSS_BN_new() wraps the openSSL function in a TPM error handler
 */

static TPM_RC TSS_BN_new(BIGNUM **bn)		/* freed by caller */
{
    TPM_RC	rc = 0;

    if (rc == 0) {
	if (*bn != NULL) {
	    if (tssVerbose)
		printf("TSS_BN_new: Error (fatal), *bn %p should be NULL before BN_new()\n", *bn);
	    rc = TSS_RC_ALLOC_INPUT;
	}	    
    }
    if (rc == 0) {
	*bn = BN_new();
	if (*bn == NULL) {
	    if (tssVerbose) printf("TSS_BN_new: BN_new() failed\n");
	    rc = TSS_RC_OUT_OF_MEMORY;
	}
    }
    return rc;
}

/* TSS_BN_hex2bn() wraps the openSSL function in a TPM error handler
 */

static TPM_RC TSS_BN_hex2bn(BIGNUM **bn, const char *str)	/* freed by caller */
{
    TPM_RC	rc = 0;

    if (rc == 0) {
	if (*bn != NULL) {
	    if (tssVerbose)
		printf("TSS_BN_hex2bn: Error (fatal), *bn %p should be NULL before BN_new()\n", *bn);
	    rc = TSS_RC_ALLOC_INPUT;
	}	    
    }
    if (rc == 0) {
	int irc;
	irc = BN_hex2bn(bn, str);
	if (irc == 0) {
	    if (tssVerbose) printf("TSS_BN_hex2bn: BN_hex2bn() failed\n"); 
	    rc = TSS_RC_EC_EPHEMERAL_FAILURE;
	}
    }    
    return rc;
}

#endif	/* TPM_TSS_NOECC */

#ifndef TPM_TSS_NORSA

/* TSS_bin2bn() wraps the openSSL function in a TPM error handler

   Converts a char array to bignum

   bn must be freed by the caller.
*/

static TPM_RC TSS_bin2bn(BIGNUM **bn, const unsigned char *bin, unsigned int bytes)
{
    TPM_RC	rc = 0;

    /* 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) {
            if (tssVerbose) printf("TSS_bin2bn: Error in BN_bin2bn\n");
            rc = TSS_RC_BIGNUM;
        }
    }
    return rc;
}

#endif /* TPM_TSS_NORSA */

/*
  AES
*/

TPM_RC TSS_AES_GetEncKeySize(size_t *tssSessionEncKeySize)
{
    *tssSessionEncKeySize = sizeof(AES_KEY);
    return 0;
}
TPM_RC TSS_AES_GetDecKeySize(size_t *tssSessionDecKeySize)
{
    *tssSessionDecKeySize = sizeof(AES_KEY);
    return 0;
}

#define TSS_AES_KEY_BITS 128

#ifndef TPM_TSS_NOFILE

TPM_RC TSS_AES_KeyGenerate(void *tssSessionEncKey,
			   void *tssSessionDecKey)
{
    TPM_RC		rc = 0;
    int 		irc;
    unsigned char 	userKey[AES_128_BLOCK_SIZE_BYTES];
    const char 		*envKeyString = NULL;
    unsigned char 	*envKeyBin = NULL;
    size_t 		envKeyBinLen;

    if (rc == 0) {
	envKeyString = getenv("TPM_SESSION_ENCKEY");
    }
    if (envKeyString == NULL) {
	/* If the env variable TPM_SESSION_ENCKEY is not set, generate a random key for this
	   TSS_CONTEXT */
	if (rc == 0) {
	    /* initialize userKey to silence valgrind false positive */
	    memset(userKey, 0, sizeof(userKey));
	    rc = TSS_RandBytes(userKey, AES_128_BLOCK_SIZE_BYTES);
	}
    }
    /* The env variable TPM_SESSION_ENCKEY can set a (typically constant) encryption key.  This is
       useful for scripting, where the env variable is set to a random seed at the beginning of the
       script. */
    else {
	/* hexascii to binary */
	if (rc == 0) {
	    rc = TSS_Array_Scan(&envKeyBin,			/* freed @1 */
				&envKeyBinLen, envKeyString);
	}
	/* range check */
	if (rc == 0) {
	    if (envKeyBinLen != AES_128_BLOCK_SIZE_BYTES) {
		if (tssVerbose)
		    printf("TSS_AES_KeyGenerate: Error, env variable length %lu not %lu\n",
			   (unsigned long)envKeyBinLen, (unsigned long)sizeof(userKey));
		rc = TSS_RC_BAD_PROPERTY_VALUE;
	    }
	}
	/* copy the binary to the common userKey for use below */
	if (rc == 0) {
	    memcpy(userKey, envKeyBin, envKeyBinLen);  
	}
    }
    /* translate to an openssl key token */
    if (rc == 0) {
        irc = AES_set_encrypt_key(userKey,
                                  TSS_AES_KEY_BITS,
                                  tssSessionEncKey);
	/* should never occur, null pointers or bad bit size */
	if (irc != 0) {
            if (tssVerbose)
		printf("TSS_AES_KeyGenerate: Error setting openssl AES encryption key\n");
	    rc = TSS_RC_AES_KEYGEN_FAILURE; 
	}
    }
    if (rc == 0) {
	irc = AES_set_decrypt_key(userKey,
				  TSS_AES_KEY_BITS,
				  tssSessionDecKey);
	/* should never occur, null pointers or bad bit size */
	if (irc != 0) {
            if (tssVerbose)
		printf("TSS_AES_KeyGenerate: Error setting openssl AES decryption key\n");
	    rc = TSS_RC_AES_KEYGEN_FAILURE; 
	}
    }
    free(envKeyBin);	/* @1 */
    return rc;
}

#endif

/* TSS_AES_Encrypt() is AES non-portable code to encrypt 'decrypt_data' to 'encrypt_data' using CBC.
   This function uses the session encryption key for encrypting session state.

   The stream is padded as per PKCS#7 / RFC2630

   'encrypt_data' must be free by the caller
*/
   
TPM_RC TSS_AES_Encrypt(void *tssSessionEncKey,
		       unsigned char **encrypt_data,   		/* output, caller frees */
		       uint32_t *encrypt_length,		/* output */
		       const unsigned char *decrypt_data,	/* input */
		       uint32_t decrypt_length)			/* input */
{
    TPM_RC		rc = 0;
    uint32_t		pad_length;
    unsigned char	*decrypt_data_pad;
    unsigned char	ivec[AES_128_BLOCK_SIZE_BYTES];       /* initial chaining vector */

    decrypt_data_pad = NULL;    /* freed @1 */
    if (rc == 0) {
        /* calculate the pad length and padded data length */
        pad_length = AES_128_BLOCK_SIZE_BYTES - (decrypt_length % AES_128_BLOCK_SIZE_BYTES);
        *encrypt_length = decrypt_length + pad_length;
         /* allocate memory for the encrypted response */
        rc = TSS_Malloc(encrypt_data, *encrypt_length);
    }
    /* allocate memory for the padded decrypted data */
    if (rc == 0) {
        rc = TSS_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 */
        AES_cbc_encrypt(decrypt_data_pad,
                        *encrypt_data,
                        *encrypt_length,
                        tssSessionEncKey,
                        ivec,
                        AES_ENCRYPT);
    }
    free(decrypt_data_pad);     /* @1 */
    return rc;
}

/* TSS_AES_Decrypt() is AES non-portable code to decrypt 'encrypt_data' to 'decrypt_data' using CBC.
   This function uses the session encryption key for decrypting session state.

   The stream must be padded as per PKCS#7 / RFC2630

   decrypt_data must be free by the caller
*/

TPM_RC TSS_AES_Decrypt(void *tssSessionDecKey,
		       unsigned char **decrypt_data,   		/* output, caller frees */
		       uint32_t *decrypt_length,		/* output */
		       const unsigned char *encrypt_data,	/* input */
		       uint32_t encrypt_length)			/* input */
{
    TPM_RC          	rc = 0;
    uint32_t		pad_length;
    uint32_t		i;
    unsigned char       *pad_data;
    unsigned char       ivec[AES_128_BLOCK_SIZE_BYTES];       /* initial chaining vector */
    
    /* sanity check encrypted length */
    if (rc == 0) {
        if (encrypt_length < AES_128_BLOCK_SIZE_BYTES) {
            if (tssVerbose) printf("TSS_AES_Decrypt: Error, bad length %u\n",
				   encrypt_length);
            rc = TSS_RC_AES_DECRYPT_FAILURE;
        }
    }
    /* allocate memory for the padded decrypted data */
    if (rc == 0) {
        rc = TSS_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 */
        AES_cbc_encrypt(encrypt_data,
                        *decrypt_data,
                        encrypt_length,
                        tssSessionDecKey,
                        ivec,
                        AES_DECRYPT);
    }
    /* 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 */
        if ((pad_length == 0) ||
            (pad_length > AES_128_BLOCK_SIZE_BYTES)) {
            if (tssVerbose) printf("TSS_AES_Decrypt: Error, illegal pad length\n");
            rc = TSS_RC_AES_DECRYPT_FAILURE;
        }
    }
    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 ; (rc == 0) && (i < pad_length) ; i++, pad_data++) {
            if (*pad_data != pad_length) {
                if (tssVerbose) printf("TSS_AES_Decrypt: Error, bad pad %02x at index %u\n",
				       *pad_data, i);
                rc = TSS_RC_AES_DECRYPT_FAILURE;
            }
        }
    }
    return rc;
}

TPM_RC TSS_AES_EncryptCFB(uint8_t	*dOut,		/* OUT: the encrypted data */
			  uint32_t	keySizeInBits,	/* IN: key size in bits */
			  uint8_t 	*key,           /* IN: key buffer */
			  uint8_t 	*iv,		/* IN/OUT: IV for decryption */
			  uint32_t	dInSize,       	/* IN: data size */
			  uint8_t 	*dIn)		/* IN: data buffer */
{
    TPM_RC	rc = 0;
    int 	irc;
    int		blockSize;
    AES_KEY	aeskey;
    int32_t	dSize;         /* signed version of dInSize */
    
    /* Create AES encryption key token */
    if (rc == 0) {
	irc = AES_set_encrypt_key(key, keySizeInBits, &aeskey);
	if (irc != 0) {
            if (tssVerbose) printf("TSS_AES_EncryptCFB: Error setting openssl AES encryption key\n");
	    rc = TSS_RC_AES_KEYGEN_FAILURE;  /* should never occur, null pointers or bad bit size */
	}
    }
    if (rc == 0) {
	/* Encrypt the current IV into the new IV, XOR in the data, and copy to output */
	for(dSize = (int32_t)dInSize ; dSize > 0 ; dSize -= 16, dOut += 16, dIn += 16) {
	    /* Encrypt the current value of the IV to the intermediate value.  Store in old iv,
	       since it's not needed anymore. */
	    AES_encrypt(iv, iv, &aeskey);
	    blockSize = (dSize < 16) ? dSize : 16;	/* last block can be < 16 */	
	    TSS_XOR(dOut, dIn, iv, blockSize);
	    memcpy(iv, dOut, blockSize);
	}
    }
    return rc;
}

TPM_RC TSS_AES_DecryptCFB(uint8_t *dOut,          	/* OUT: the decrypted data */
			  uint32_t keySizeInBits, 	/* IN: key size in bits */
			  uint8_t *key,           	/* IN: key buffer */
			  uint8_t *iv,            	/* IN/OUT: IV for decryption. */
			  uint32_t dInSize,       	/* IN: data size */
			  uint8_t *dIn)			/* IN: data buffer */
{
    TPM_RC	rc = 0;
    int 	irc;
    uint8_t	tmp[16];
    int		blockSize;
    AES_KEY	aesKey;
    int32_t	dSize;
    
    /* Create AES encryption key token */
    if (rc == 0) {
	irc = AES_set_encrypt_key(key, keySizeInBits, &aesKey);
	if (irc != 0) {
            if (tssVerbose) printf("TSS_AES_DecryptCFB: Error setting openssl AES encryption key\n");
	    rc = TSS_RC_AES_KEYGEN_FAILURE;  /* should never occur, null pointers or bad bit size */
	}
    }
    if (rc == 0) {
	for (dSize = (int32_t)dInSize ; dSize > 0; dSize -= 16, dOut += 16, dIn += 16) {
	    /* Encrypt the IV into the temp buffer */
	    AES_encrypt(iv, tmp, &aesKey);
	    blockSize = (dSize < 16) ? dSize : 16;	/* last block can be < 16 */	
	    TSS_XOR(dOut, dIn, tmp, blockSize);
	    memcpy(iv, dIn, blockSize);
	}
    }
    return rc;
}

