/********************************************************************************/
/*										*/
/*				Storage Functions				*/
/*			     Written by Ken Goldman				*/
/*		       IBM Thomas J. Watson Research Center			*/
/*	      $Id: tpm_storage.c 4442 2011-02-14 20:20:01Z 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.		*/
/********************************************************************************/

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

#include "tpm_auth.h"
#include "tpm_cryptoh.h"
#include "tpm_crypto.h"
#include "tpm_debug.h"
#include "tpm_digest.h"
#include "tpm_error.h"
#include "tpm_io.h"
#include "tpm_key.h"
#include "tpm_memory.h"
#include "tpm_nonce.h"
#include "tpm_pcr.h"
#include "tpm_process.h"
#include "tpm_secret.h"
#include "tpm_structures.h"
#include "tpm_ver.h"

#include "tpm_storage.h"

/* local function prototypes */

static TPM_RESULT TPM_SealCryptCommon(BYTE **o1,
				      TPM_ADIP_ENC_SCHEME adipEncScheme,
				      TPM_SIZED_BUFFER *inData,
				      TPM_AUTH_SESSION_DATA *auth_session_data,
				      TPM_NONCE nonceOdd);

static TPM_RESULT TPM_LoadKeyCommon(TPM_KEY_HANDLE	*inKeyHandle,
				    TPM_BOOL		*key_added,
				    TPM_SECRET		**hmacKey,
				    TPM_AUTH_SESSION_DATA	**auth_session_data,
				    tpm_state_t		*tpm_state,
				    TPM_TAG		tag,
				    TPM_COMMAND_CODE	ordinal,
				    TPM_KEY_HANDLE	parentHandle,
				    TPM_KEY		*inKey,
				    TPM_DIGEST		inParamDigest,
				    TPM_AUTHHANDLE	authHandle,
				    TPM_NONCE		nonceOdd,
				    TPM_BOOL		continueAuthSession,
				    TPM_AUTHDATA	parentAuth);

/*
  TPM_BOUND_DATA
*/

/* TPM_BoundData_Init()

   sets members to default values
   sets all pointers to NULL and sizes to 0
   always succeeds - no return code
*/

void TPM_BoundData_Init(TPM_BOUND_DATA *tpm_bound_data)
{
    printf(" TPM_BoundData_Init:\n");
    TPM_StructVer_Init(&(tpm_bound_data->ver));
    tpm_bound_data->payload = TPM_PT_BIND;
    tpm_bound_data->payloadDataSize = 0;
    tpm_bound_data->payloadData = NULL;
    return;
}

/* TPM_BoundData_Load()

   deserialize the structure from a 'stream'
   'stream_size' is checked for sufficient data
   returns 0 or error codes
   
   Before use, call TPM_BoundData_Init()
   After use, call TPM_BoundData_Delete() to free memory
*/

TPM_RESULT TPM_BoundData_Load(TPM_BOUND_DATA *tpm_bound_data,
			      unsigned char **stream,
			      uint32_t *stream_size)
{
    TPM_RESULT		rc = 0;

    printf(" TPM_BoundData_Load:\n");
    if (rc == 0) {
	rc = TPM_StructVer_Load(&(tpm_bound_data->ver), stream, stream_size);
    }
    /* check ver immediately to ease debugging */
    if (rc == 0) {
	rc = TPM_StructVer_CheckVer(&(tpm_bound_data->ver));
    }
    if (rc == 0) {
	rc = TPM_Load8(&(tpm_bound_data->payload), stream, stream_size);
    }
    if ((rc == 0) && (*stream_size > 0)){
	/* There is no payloadData size in the serialized data.	 Assume it consumes the rest of the
	   stream */
	tpm_bound_data->payloadDataSize = *stream_size;
	rc = TPM_Malloc(&(tpm_bound_data->payloadData), tpm_bound_data->payloadDataSize);
    }
    if ((rc == 0) && (*stream_size > 0)){
	memcpy(tpm_bound_data->payloadData, *stream, tpm_bound_data->payloadDataSize);
	*stream += tpm_bound_data->payloadDataSize;
	*stream_size -= tpm_bound_data->payloadDataSize;
    }
    return rc;
}

/* TPM_BoundData_Store()
   
   serialize the structure to a stream contained in 'sbuffer'
   returns 0 or error codes

   This structure serialization assumes that the payloadDataSize member indicates the size of
   payloadData.
*/

TPM_RESULT TPM_BoundData_Store(TPM_STORE_BUFFER *sbuffer,
			       const TPM_BOUND_DATA *tpm_bound_data)
{
    TPM_RESULT		rc = 0;

    printf(" TPM_BoundData_Store:\n");
    if (rc == 0) {
	rc = TPM_StructVer_Store(sbuffer, &(tpm_bound_data->ver));
    }
    if (rc == 0) {
	rc = TPM_Sbuffer_Append(sbuffer, &(tpm_bound_data->payload), sizeof(TPM_PAYLOAD_TYPE));
    }
    if (rc == 0) {
	rc = TPM_Sbuffer_Append(sbuffer, tpm_bound_data->payloadData,
				tpm_bound_data->payloadDataSize);
    }
    return rc;
}

/* TPM_BoundData_Delete()

   No-OP if the parameter is NULL, else:
   frees memory allocated for the bound_data
   sets pointers to NULL
   calls TPM_BoundData_Init to set members back to default values
   The bound_data itself is not freed
*/   

void TPM_BoundData_Delete(TPM_BOUND_DATA *tpm_bound_data)
{
    printf(" TPM_BoundData_Delete:\n");
    if (tpm_bound_data != NULL) {
	free(tpm_bound_data->payloadData);
	TPM_BoundData_Init(tpm_bound_data);
    }
    return;
}

/*
  TPM_SEALED_DATA
*/

/* TPM_SealedData_Init()

   sets members to default values
   sets all pointers to NULL and sizes to 0
   always succeeds - no return code
*/

void TPM_SealedData_Init(TPM_SEALED_DATA *tpm_sealed_data)
{
    printf(" TPM_SealedData_Init:\n");
    tpm_sealed_data->payload = TPM_PT_SEAL;
    TPM_Secret_Init(tpm_sealed_data->authData);
    TPM_Secret_Init(tpm_sealed_data->tpmProof);
    TPM_Digest_Init(tpm_sealed_data->storedDigest);
    TPM_SizedBuffer_Init(&(tpm_sealed_data->data));
    return;
}

/* TPM_SealedData_Load()

   deserialize the structure from a 'stream'
   'stream_size' is checked for sufficient data
   returns 0 or error codes
   
   Before use, call TPM_SealedData_Init()
   After use, call TPM_SealedData_Delete() to free memory
*/

TPM_RESULT TPM_SealedData_Load(TPM_SEALED_DATA *tpm_sealed_data,
			       unsigned char **stream,
			       uint32_t *stream_size)
{
    TPM_RESULT		rc = 0;

    printf(" TPM_SealedData_Load:\n");
    /* load payload */
    if (rc == 0) {
	rc = TPM_Load8(&(tpm_sealed_data->payload), stream, stream_size);
    }
    /* load authData */
    if (rc == 0) {
	rc = TPM_Secret_Load(tpm_sealed_data->authData, stream, stream_size);
    }
    /* load tpmProof */
    if (rc == 0) {
	rc = TPM_Secret_Load(tpm_sealed_data->tpmProof, stream, stream_size);
    }
    /* load storedDigest */
    if (rc == 0) {
	rc = TPM_Digest_Load(tpm_sealed_data->storedDigest, stream, stream_size);
    }
    /* load dataSize and data  */
    if (rc == 0) {
	rc = TPM_SizedBuffer_Load(&(tpm_sealed_data->data), stream, stream_size);
    }
    return rc;
}

/* TPM_SealedData_Store()
   
   serialize the structure to a stream contained in 'sbuffer'
   returns 0 or error codes
*/

TPM_RESULT TPM_SealedData_Store(TPM_STORE_BUFFER *sbuffer,
				const TPM_SEALED_DATA *tpm_sealed_data)
{
    TPM_RESULT		rc = 0;
    printf(" TPM_SealedData_Store:\n");
    /* store payload */
    if (rc == 0) {
	rc = TPM_Sbuffer_Append(sbuffer, &(tpm_sealed_data->payload), sizeof(TPM_PAYLOAD_TYPE));
    }
    /* store authData */
    if (rc == 0) {
	rc = TPM_Secret_Store(sbuffer, tpm_sealed_data->authData);
    }
    /* store tpmProof */
    if (rc == 0) {
	rc = TPM_Secret_Store(sbuffer, tpm_sealed_data->tpmProof);
    }
    /* store storedDigest */
    if (rc == 0) {
	rc = TPM_Digest_Store(sbuffer, tpm_sealed_data->storedDigest);
    }
    /* store dataSize and data	*/
    if (rc == 0) {
	rc = TPM_SizedBuffer_Store(sbuffer, &(tpm_sealed_data->data));
    }
    return rc;
}

/* TPM_SealedData_Delete()

   No-OP if the parameter is NULL, else:
   frees memory allocated for the object
   sets pointers to NULL
   calls TPM_SealedData_Init to set members back to default values
   The object itself is not freed
*/   

void TPM_SealedData_Delete(TPM_SEALED_DATA *tpm_sealed_data)
{
    printf(" TPM_SealedData_Delete:\n");
    if (tpm_sealed_data != NULL) {
	TPM_SizedBuffer_Delete(&(tpm_sealed_data->data));
	TPM_SealedData_Init(tpm_sealed_data);
    }
    return;
}

/* TPM_SealedData_GenerateEncData() generates an enc_data structure by serializing the
   TPM_SEALED_DATA structure and encrypting the result using the public key.
*/

TPM_RESULT TPM_SealedData_GenerateEncData(TPM_SIZED_BUFFER *enc_data,
					  const TPM_SEALED_DATA *tpm_sealed_data,
					  TPM_KEY *tpm_key)
{
    TPM_RESULT		rc = 0;
    TPM_STORE_BUFFER	sbuffer;		/* TPM_SEALED_DATA serialization */

    printf(" TPM_SealedData_GenerateEncData\n");
    TPM_Sbuffer_Init(&sbuffer);			/* freed @1 */
    /* serialize the TPM_SEALED_DATA */
    if (rc == 0) {
	rc = TPM_SealedData_Store(&sbuffer, tpm_sealed_data);
    }
    /* encrypt the TPM_SEALED_DATA serialization buffer with the public key, and place
       the result in the encData members */
    if (rc == 0) {
	rc = TPM_RSAPublicEncryptSbuffer_Key(enc_data, &sbuffer, tpm_key);
    }
    TPM_Sbuffer_Delete(&sbuffer);	/* @1 */
    return rc;
}

/* TPM_SealedData_DecryptEncData() decrypts the enc_data using the private key.	 The
   result is deserialized and stored in the TPM_SEALED_DATA structure.

*/

TPM_RESULT TPM_SealedData_DecryptEncData(TPM_SEALED_DATA *tpm_sealed_data,	/* result */
					 TPM_SIZED_BUFFER *enc_data,	/* encrypted input */
					 TPM_KEY *tpm_key)		/* key for decrypting */
{
    TPM_RESULT		rc = 0;
    unsigned char	*decryptData = NULL;	/* freed @1 */
    uint32_t		decryptDataLength = 0;	/* actual valid data */
    unsigned char	*stream;
    uint32_t		stream_size;
    
    printf(" TPM_SealedData_DecryptEncData:\n");
    /* allocate space for the decrypted data */
    if (rc == 0) {
	rc = TPM_RSAPrivateDecryptMalloc(&decryptData,		/* decrypted data */
					 &decryptDataLength,	/* actual size of decrypted data */
					 enc_data->buffer,	/* encrypted data */
					 enc_data->size,	/* encrypted data size */
					 tpm_key);
    }
    /* load the TPM_SEALED_DATA structure from the decrypted data stream */
    if (rc == 0) {
	/* use temporary variables, because TPM_SealedData_Load() moves the stream */
	stream = decryptData;
	stream_size = decryptDataLength;
	rc = TPM_SealedData_Load(tpm_sealed_data, &stream, &stream_size);
    }
    free(decryptData);		/* @1 */
    return rc;
}


/*
  TPM_STORED_DATA
*/

/* TPM_StoredData_Init()

   sets members to default values
   sets all pointers to NULL and sizes to 0
   always succeeds - no return code
*/

void TPM_StoredData_Init(TPM_STORED_DATA *tpm_stored_data,
			 unsigned int version)
{
    printf(" TPM_StoredData_Init: v%u\n", version);
    if (version == 1) {
	TPM_StructVer_Init(&(tpm_stored_data->ver));
    }
    else {
	((TPM_STORED_DATA12 *)tpm_stored_data)->tag = TPM_TAG_STORED_DATA12;
	((TPM_STORED_DATA12 *)tpm_stored_data)->et = 0x0000;
    }
    TPM_SizedBuffer_Init(&(tpm_stored_data->sealInfo));
    TPM_SizedBuffer_Init(&(tpm_stored_data->encData));
    tpm_stored_data->tpm_seal_info = NULL;
    return;
}

/* TPM_StoredData_Load()

   deserialize the structure from a 'stream'
   'stream_size' is checked for sufficient data
   returns 0 or error codes
   
   Before use, call TPM_StoredData_Init()
   After use, call TPM_StoredData_Delete() to free memory

   This function handles both TPM_STORED_DATA and TPM_STORED_DATA12 and returns the 'version'.
*/

TPM_RESULT TPM_StoredData_Load(TPM_STORED_DATA *tpm_stored_data,
			       unsigned int *version,
			       unsigned char **stream,
			       uint32_t *stream_size)
{
    TPM_RESULT		rc = 0;

    /* Peek at the first byte to guess the version number.  The data is verified later.
       TPM_STORED_DATA is 01,01,00,00 TPM_STORED_DATA12 is 00,16,00,00 */
    if ((rc == 0) && (*stream_size > 0)) {
	if (**stream == 0x01) {
	    *version = 1;
	}
	else {
	    *version = 2;
	}
	printf(" TPM_StoredData_Load: v%u\n", *version);
    }	 
    /* 1.1 load ver */
    if ((rc == 0) && (*version == 1)) {
	rc = TPM_StructVer_Load(&(tpm_stored_data->ver), stream, stream_size);
    }
    /* 1.2 load tag */
    if ((rc == 0) && (*version != 1)) {
	rc = TPM_Load16(&(((TPM_STORED_DATA12 *)tpm_stored_data)->tag), stream, stream_size);
    }
    /* 1.2 load et */
    if ((rc == 0) && (*version != 1)) {
	rc = TPM_Load16(&(((TPM_STORED_DATA12 *)tpm_stored_data)->et), stream, stream_size);
    }
    /* check the TPM_STORED_DATA structure version */
    if ((rc == 0) && (*version == 1)) {
	rc = TPM_StructVer_CheckVer(&(tpm_stored_data->ver));
    }
    /* check the TPM_STORED_DATA12 structure tag */
    if ((rc == 0) && (*version != 1)) {
	rc = TPM_StoredData_CheckTag((TPM_STORED_DATA12 *)tpm_stored_data);
    }
    /* load sealInfoSize and sealInfo */
    if (rc == 0) {
	rc = TPM_SizedBuffer_Load(&(tpm_stored_data->sealInfo), stream, stream_size);
    }
    /* load the TPM_PCR_INFO or TPM_PCR_INFO_LONG cache */
    if (rc == 0) {
	if (*version == 1) {
	    rc = TPM_PCRInfo_CreateFromBuffer(&(tpm_stored_data->tpm_seal_info),
					      &(tpm_stored_data->sealInfo));
	}
	else {
	    rc = TPM_PCRInfoLong_CreateFromBuffer
		 (&(((TPM_STORED_DATA12 *)tpm_stored_data)->tpm_seal_info_long),
		  &(tpm_stored_data->sealInfo));
	}
    }
    /* load encDataSize and encData */
    if (rc == 0) {
	rc = TPM_SizedBuffer_Load(&(tpm_stored_data->encData), stream, stream_size);
    }
    return rc;
}

/* TPM_StoredData_StoreClearData() serializes a TPM_STORED_DATA structure, excluding encData,
   appending results to 'sbuffer'.

   Before serializing, it serializes tpm_seal_info to sealInfoSize and sealInfo.

   This function handles both TPM_STORED_DATA and TPM_STORED_DATA12.
   
   serialize the structure to a stream contained in 'sbuffer'
   returns 0 or error codes
*/

TPM_RESULT TPM_StoredData_StoreClearData(TPM_STORE_BUFFER *sbuffer,
					 TPM_STORED_DATA *tpm_stored_data,
					 unsigned int version)
{
    TPM_RESULT		rc = 0;

    printf(" TPM_StoredData_StoreClearData: v%u\n", version);
    /* 1.1 store ver */
    if ((rc == 0) && (version == 1)) {
	rc = TPM_StructVer_Store(sbuffer, &(tpm_stored_data->ver));
    }
    /* 1.2 store tag */
    if ((rc == 0) && (version != 1)) {
	rc = TPM_Sbuffer_Append16(sbuffer, ((TPM_STORED_DATA12 *)tpm_stored_data)->tag);
    }
    /* 1.2 store et */
    if ((rc == 0) && (version != 1)) {
	rc = TPM_Sbuffer_Append16(sbuffer, ((TPM_STORED_DATA12 *)tpm_stored_data)->et);
    }
    /* store sealInfoSize and sealInfo */
    if (rc == 0) {
	/* copy cache to sealInfoSize and sealInfo */
	if (version == 1) {
	    rc = TPM_SizedBuffer_SetStructure(&(tpm_stored_data->sealInfo),
					      tpm_stored_data->tpm_seal_info,
					      (TPM_STORE_FUNCTION_T)TPM_PCRInfo_Store);
	}
	else {
	    rc = TPM_SizedBuffer_SetStructure(&(tpm_stored_data->sealInfo),
					      tpm_stored_data->tpm_seal_info,
					      (TPM_STORE_FUNCTION_T)TPM_PCRInfoLong_Store);
	}
    }
    /* copy sealInfoSize and sealInfo to sbuffer */
    if (rc == 0) {
	rc = TPM_SizedBuffer_Store(sbuffer, &(tpm_stored_data->sealInfo));
    }
    return rc;
}

/* TPM_StoredData_Store()
   
   Before serializing, it serializes tpm_seal_info to sealInfoSize and sealInfo.

   serialize the structure to a stream contained in 'sbuffer'
   returns 0 or error codes
*/

TPM_RESULT TPM_StoredData_Store(TPM_STORE_BUFFER *sbuffer,
				TPM_STORED_DATA *tpm_stored_data,
				unsigned int version)
{
    TPM_RESULT		rc = 0;

    printf(" TPM_StoredData_Store: v%u\n", version);
    if (rc == 0) {
	rc = TPM_StoredData_StoreClearData(sbuffer, tpm_stored_data, version);
    }
    /* store encDataSize and encData */
    if (rc == 0) {
	rc = TPM_SizedBuffer_Store(sbuffer, &(tpm_stored_data->encData));
    }
    return rc;
}

/* TPM_StoredData_Delete()

   No-OP if the parameter is NULL, else:
   frees memory allocated for the object
   sets pointers to NULL
   calls TPM_StoredData_Init to set members back to default values
   The object itself is not freed
*/   

void TPM_StoredData_Delete(TPM_STORED_DATA *tpm_stored_data,
			   unsigned int version)
{
    printf(" TPM_StoredData_Delete: v%u\n", version);
    if (tpm_stored_data != NULL) {
	TPM_SizedBuffer_Delete(&(tpm_stored_data->sealInfo));
	TPM_SizedBuffer_Delete(&(tpm_stored_data->encData));
	if (version == 1) {
	    TPM_PCRInfo_Delete(tpm_stored_data->tpm_seal_info);
	    free(tpm_stored_data->tpm_seal_info);
	}
	else {
	    TPM_PCRInfoLong_Delete((TPM_PCR_INFO_LONG *)tpm_stored_data->tpm_seal_info);
	    free(tpm_stored_data->tpm_seal_info);
	}
	TPM_StoredData_Init(tpm_stored_data, version);
    }
    return;
}

/* TPM_StoredData_CheckTag() verifies the tag and et members of a TPM_STORED_DATA12 structure

 */

TPM_RESULT TPM_StoredData_CheckTag(TPM_STORED_DATA12 *tpm_stored_data12)
{
    TPM_RESULT	rc = 0;

    printf(" TPM_StoredData_CheckTag:\n");
    if (rc == 0) {
	if (tpm_stored_data12->tag != TPM_TAG_STORED_DATA12) {
	    printf("TPM_StoredData_CheckTag: Error, tag expected %04x found %04hx\n",
		   TPM_TAG_STORED_DATA12, tpm_stored_data12->tag);
	    rc = TPM_BAD_VERSION;
	}
    }
    return rc;
}

/* TPM_StoredData_GenerateDigest() generates a TPM_DIGEST over the TPM_STORED_DATA structure
   excluding the encDataSize and encData members.
*/

TPM_RESULT TPM_StoredData_GenerateDigest(TPM_DIGEST tpm_digest,
					 TPM_STORED_DATA *tpm_stored_data,
					 unsigned int version)
{
    TPM_RESULT		rc = 0;
    TPM_STORE_BUFFER	sbuffer;	/* TPM_STORED_DATA serialization */
    
    printf(" TPM_StoredData_GenerateDigest:\n");
    TPM_Sbuffer_Init(&sbuffer);			/* freed @1 */
    /* serialize the TPM_STORED_DATA excluding the encData fields */
    if (rc == 0) {
	rc = TPM_StoredData_StoreClearData(&sbuffer, tpm_stored_data, version);
    }
    if (rc == 0) {
	rc = TPM_SHA1Sbuffer(tpm_digest, &sbuffer);
    }
    TPM_Sbuffer_Delete(&sbuffer);	/* @1 */
    return rc;
}

/*
  Processing Functions
*/

/* TPM_SealCryptCommon() rev 98

   Handles the encrypt/decrypt actions common to TPM_Sealx and TPM_Unseal

   'encrypt TRUE for encryption, FALSE for decryption

   The output o1 must be freed by the caller.
*/

static TPM_RESULT TPM_SealCryptCommon(BYTE **o1,	/* freed by caller */
				      TPM_ADIP_ENC_SCHEME adipEncScheme,
				      TPM_SIZED_BUFFER *inData,
				      TPM_AUTH_SESSION_DATA *auth_session_data,
				      TPM_NONCE nonceOdd)
{
    TPM_RESULT		rc = 0;
    BYTE		*x1;			/* XOR string, MGF1 output */
    TPM_DIGEST		ctr;			/* symmetric key algorithm CTR */

    printf(" TPM_SealCryptCommon:\n");
    x1 = NULL;					/* freed @1 */

    /* allocate for the output o1 */
    if (rc == TPM_SUCCESS) {
	rc = TPM_Malloc(o1, inData->size);	/* freed by caller */
    }
    if (rc == TPM_SUCCESS) {
	TPM_PrintFour("  TPM_SealCryptCommon: input data", inData->buffer);
    }
    switch (adipEncScheme) {
      case TPM_ET_XOR:
	printf("  TPM_SealCryptCommon: TPM_ET_XOR\n");
	if (rc == TPM_SUCCESS) {
	    /* i. Use MGF1 to create string X1 of length sealedDataSize. The inputs to MGF1 are;
	       authLastnonceEven, nonceOdd, "XOR", and authHandle -> sharedSecret. The four
	       concatenated values form the Z value that is the seed for MFG1. */
	    rc = TPM_MGF1_GenerateArray(&x1,		/* MGF1 array */
					inData->size,	/* MGF1 array length */
					
					TPM_NONCE_SIZE +
					TPM_NONCE_SIZE +
					sizeof("XOR") -1 +
					TPM_DIGEST_SIZE, /* seed length */
					
					TPM_NONCE_SIZE, auth_session_data->nonceEven,
					TPM_NONCE_SIZE, nonceOdd,
					sizeof("XOR") -1, "XOR",
					TPM_DIGEST_SIZE, auth_session_data->sharedSecret,
					0, NULL);
	}
	/* ii. Create o1 by XOR of d1 -> data and X1 */
	if (rc == TPM_SUCCESS) {
	    TPM_PrintFour("  TPM_SealCryptCommon: XOR key", x1);
	    TPM_XOR(*o1, inData->buffer, x1, inData->size);
	}
	break;
      case TPM_ET_AES128_CTR:
	printf("  TPM_SealCryptCommon: TPM_ET_AES128_CTR\n");
	/* i. Create o1 by encrypting d1 -> data using the algorithm indicated by inData ->
	   et */
	/* ii. Key is from authHandle -> sharedSecret */
	/* iii. IV is SHA-1 of (authLastNonceEven || nonceOdd) */
	if (rc == TPM_SUCCESS) {
	    rc = TPM_SHA1(ctr,
			  TPM_NONCE_SIZE, auth_session_data->nonceEven,
			  TPM_NONCE_SIZE, nonceOdd,
			  0, NULL);
	}
	if (rc == TPM_SUCCESS) {
	    TPM_PrintFour("  TPM_SealCryptCommon: AES key", auth_session_data->sharedSecret);
	    TPM_PrintFour("  TPM_SealCryptCommon: CTR", ctr);
	    rc = TPM_SymmetricKeyData_CtrCrypt(*o1,				/* output data */
					       inData->buffer,			/* input data */
					       inData->size,			/* data size */
					       auth_session_data->sharedSecret, /* key */
					       TPM_SECRET_SIZE,			/* key size */
					       ctr,				/* CTR */
					       TPM_DIGEST_SIZE);		/* CTR size */
	}
	break;
      default:
	printf("TPM_SealCryptCommon: Error, unsupported adipEncScheme %02x\n", adipEncScheme);
	rc = TPM_INAPPROPRIATE_ENC;
	break;
    }
    if (rc == 0) {
	TPM_PrintFour("  TPM_SealCryptCommon: output data", *o1);
	
    }
    free(x1);				/* @1 */
    return rc;
}

/* 10.1 TPM_Seal rev 110

   The SEAL operation allows software to explicitly state the future "trusted" configuration that
   the platform must be in for the secret to be revealed. The SEAL operation also implicitly
   includes the relevant platform configuration (PCR-values) when the SEAL operation was
   performed. The SEAL operation uses the tpmProof value to BIND the blob to an individual TPM.

   TPM_Seal is used to encrypt private objects that can only be decrypted using TPM_Unseal.
*/

TPM_RESULT TPM_Process_Seal(tpm_state_t *tpm_state,
			    TPM_STORE_BUFFER *response,
			    TPM_TAG tag,
			    uint32_t paramSize,
			    TPM_COMMAND_CODE ordinal,
			    unsigned char *command,
			    TPM_TRANSPORT_INTERNAL *transportInternal)
{
    TPM_RESULT	rcf = 0;			/* fatal error precluding response */
    TPM_RESULT	returnCode = TPM_SUCCESS;	/* command return code */

    /* input parameters */
    TPM_KEY_HANDLE	keyHandle;	/* Handle of a loaded key that can perform seal
					   operations. */
    TPM_ENCAUTH		encAuth;	/* The encrypted authorization data for the sealed data. */
    TPM_SIZED_BUFFER	pcrInfo;	/* The PCR selection information. The caller MAY use
					   TPM_PCR_INFO_LONG. */
    TPM_SIZED_BUFFER	inData;		/* The data to be sealed to the platform and any specified
					   PCRs */
    TPM_AUTHHANDLE	authHandle;	/* The authorization handle used for keyHandle
					   authorization. Must be an OS_AP session for this
					   command. */
    TPM_NONCE		nonceOdd;	/* Nonce generated by system associated with authHandle */
    TPM_BOOL		continueAuthSession = TRUE;	/* Ignored */
    TPM_AUTHDATA	pubAuth;	/* The authorization digest for inputs and keyHandle. HMAC
					   key: key.usageAuth. */

    /* processing */
    unsigned char *		inParamStart;		/* starting point of inParam's */
    unsigned char *		inParamEnd;		/* ending point of inParam's */
    TPM_DIGEST			inParamDigest;
    TPM_BOOL			auditStatus;		/* audit the ordinal */
    TPM_BOOL			transportEncrypt;	/* wrapped in encrypted transport session */
    TPM_BOOL			authHandleValid = FALSE;
    TPM_SECRET			*hmacKey;
    TPM_KEY			*key = NULL;		/* the key specified by keyHandle */
    TPM_SECRET			*keyUsageAuth;
    TPM_BOOL			parentPCRStatus;
    TPM_AUTH_SESSION_DATA	*auth_session_data = NULL;	/* session data for authHandle */
    unsigned int		v1PcrVersion = 1;		/* pcrInfo version */
    TPM_STORED_DATA12		*s1_12;
    TPM_PCR_INFO		tpm_pcr_info;		/* deserialized pcrInfo v1 */
    TPM_PCR_INFO_LONG		tpm_pcr_info_long;	/* deserialized pcrInfo v2 */
    unsigned char		*stream;
    uint32_t			stream_size;
    TPM_DIGEST			a1Auth;
    TPM_SEALED_DATA		s2SealedData;
    
    /* output parameters */
    uint32_t		outParamStart;	/* starting point of outParam's */
    uint32_t		outParamEnd;	/* ending point of outParam's */
    TPM_DIGEST		outParamDigest;
    TPM_STORED_DATA	s1StoredData;	/* Encrypted, integrity-protected data object that is the
					   result of the TPM_Seal operation. Returned as
					   SealedData */
    
    printf("TPM_Process_Seal: Ordinal Entry\n");
    TPM_SizedBuffer_Init(&pcrInfo);			/* freed @1 */
    TPM_SizedBuffer_Init(&inData);			/* freed @2 */
    TPM_StoredData_Init(&s1StoredData, v1PcrVersion);	/* freed @3, default is v1 */
    TPM_PCRInfo_Init(&tpm_pcr_info);			/* freed @4 */
    TPM_PCRInfoLong_Init(&tpm_pcr_info_long);		/* freed @5 */
    TPM_SealedData_Init(&s2SealedData);			/* freed @6 */
    s1_12 = (TPM_STORED_DATA12 *)&s1StoredData;		/* to avoid casts */
    /*
      get inputs
    */
    /* get keyHandle parameter */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_Load32(&keyHandle, &command, &paramSize);
    }
    /* save the starting point of inParam's for authorization and auditing */
    inParamStart = command;
    /* get encAuth parameter */
    if (returnCode == TPM_SUCCESS) {
	printf("TPM_Process_Seal: keyHandle %08x\n", keyHandle);
	returnCode = TPM_Authdata_Load(encAuth, &command, &paramSize);
    }
    /* get pcrInfo parameter */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_SizedBuffer_Load(&pcrInfo, &command, &paramSize);
    }	
    /* get inData parameter */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_SizedBuffer_Load(&inData, &command, &paramSize);
    }	
    if (returnCode == TPM_SUCCESS) {
	printf("TPM_Process_Seal: Sealing %u bytes\n", inData.size);
    }	
    /* save the ending point of inParam's for authorization and auditing */
    inParamEnd = command;
    /* digest the input parameters */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_GetInParamDigest(inParamDigest,	/* output */
					  &auditStatus,		/* output */
					  &transportEncrypt,	/* output */
					  tpm_state,
					  tag,
					  ordinal,
					  inParamStart,
					  inParamEnd,
					  transportInternal);
    }
    /* check state */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_CheckState(tpm_state, tag, TPM_CHECK_ALL);
    }
    /* check tag */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_CheckRequestTag1(tag);
    }
    /* get the 'below the line' authorization parameters */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_AuthParams_Get(&authHandle,
					&authHandleValid,
					nonceOdd,
					&continueAuthSession,
					pubAuth,
					&command, &paramSize);
    }
    if (returnCode == TPM_SUCCESS) {
	if (paramSize != 0) {
	    printf("TPM_Process_Seal: Error, command has %u extra bytes\n",
		   paramSize);
	    returnCode = TPM_BAD_PARAM_SIZE;
	}
    }
    /* do not terminate sessions if the command did not parse correctly */
    if (returnCode != TPM_SUCCESS) {
	authHandleValid = FALSE;
    }
    /*
      Processing
    */
    /* get the key corresponding to the keyHandle parameter */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_KeyHandleEntries_GetKey(&key, &parentPCRStatus, tpm_state, keyHandle,
						 FALSE,		/* not r/o, using to encrypt */
						 FALSE,		/* do not ignore PCRs */
						 FALSE);	/* cannot use EK */
    }
    /* get keyHandle -> usageAuth */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_Key_GetUsageAuth(&keyUsageAuth, key);
    }	 
    /* get the session data */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_AuthSessions_GetData(&auth_session_data,
					      &hmacKey,
					      tpm_state,
					      authHandle,
					      TPM_PID_OSAP,
					      TPM_ET_KEYHANDLE,
					      ordinal,
					      key,
					      NULL,			/* OIAP */
					      key->tpm_store_asymkey->pubDataDigest);	/* OSAP */
    }
    /* 1. Validate the authorization to use the key pointed to by keyHandle */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_Authdata_Check(tpm_state,
					*hmacKey,		/* HMAC key */
					inParamDigest,
					auth_session_data,	/* authorization session */
					nonceOdd,		/* Nonce generated by system
								   associated with authHandle */
					continueAuthSession,
					pubAuth);		/* Authorization digest for input */
    }
    /* 2. If the inDataSize is 0 the TPM returns TPM_BAD_PARAMETER */
    if (returnCode == TPM_SUCCESS) {
	if (inData.size == 0) {
	    printf("TPM_Process_Seal: Error, inDataSize is 0\n");
	    returnCode = TPM_BAD_PARAMETER;
	}
    }
    /* 3. If the keyUsage field of the key indicated by keyHandle does not have the value
       TPM_KEY_STORAGE, the TPM must return the error code TPM_INVALID_KEYUSAGE. */
    if (returnCode == TPM_SUCCESS) {
	if (key->keyUsage != TPM_KEY_STORAGE) {
	    printf("TPM_Process_Seal: Error, key keyUsage %04hx must be TPM_KEY_STORAGE\n",
		   key->keyUsage);
	    returnCode = TPM_INVALID_KEYUSAGE;
	}
    }
    /* 4. If the keyHandle points to a migratable key then the TPM MUST return the error code
       TPM_INVALID_KEY_USAGE. */
    if (returnCode == TPM_SUCCESS) {
	if (key->keyFlags & TPM_MIGRATABLE) {
	    printf("TPM_Process_Seal: Error, key keyFlags %08x indicates migratable\n",
		   key->keyFlags);
	    returnCode = TPM_INVALID_KEYUSAGE;
	}
    }
    /* 5. Determine the version of pcrInfo */
    if (returnCode == TPM_SUCCESS) {
	/* a. If pcrInfoSize is 0 */
	if (pcrInfo.size == 0) {
	    v1PcrVersion = 1;			/* i. set V1 to 1  */
	}
	else {				/* b. Else */
	    /* i. Point X1 as TPM_PCR_INFO_LONG structure to pcrInfo  */
	    /* ii. If X1 -> tag is TPM_TAG_PCR_INFO_LONG  */
	    if (htons(*(uint16_t *)(pcrInfo.buffer)) == TPM_TAG_PCR_INFO_LONG) {
		v1PcrVersion = 2;			/* (1) Set V1 to 2  */
	    }
	    else {			/* iii. Else */
		v1PcrVersion = 1;			/* (1) Set V1 to 1 */
	    }
	}
	/* 6. If V1 is 1 then */
	/* a. Create S1 a TPM_STORED_DATA structure  */
	/* 7. else  */
	/* a. Create S1 a TPM_STORED_DATA12 structure  */
	/* b. Set S1 -> et to 0 */
	/* 8. Set S1 -> encDataSize to 0 */
	/* 9. Set S1 -> encData to all zeros */
	printf("TPM_Process_Seal: V%u\n", v1PcrVersion);
	TPM_StoredData_Init(&s1StoredData, v1PcrVersion);
	/* 10. Set S1 -> sealInfoSize to pcrInfoSize */
	/* NOTE This step is unnecessary.  If pcrInfoSize is 0, sealInfoSize is already initialized
	   to 0.  If pcrInfoSize is non-zero, sealInfoSize is the result of serialization of the
	   tpm_seal_info member, which is either a TPM_PCR_INFO or a TPM_PCR_INFO_LONG */
    }
    /* 11. If pcrInfoSize is not 0 then */
    if ((returnCode == TPM_SUCCESS) && (pcrInfo.size != 0)) {
	printf("TPM_Process_Seal: Creating PCR digest\n");
	/* assign the stream, so pcrInfo is not altered */
	stream = pcrInfo.buffer;
	stream_size = pcrInfo.size;
	/* a. if V1 is 1 then */
	if (v1PcrVersion == 1) {
	    /* i. Validate pcrInfo as a valid TPM_PCR_INFO structure, return TPM_BADINDEX on
	       error */
	    if (returnCode == TPM_SUCCESS) {
		returnCode = TPM_PCRInfo_Load(&tpm_pcr_info, &stream, &stream_size);
		if (returnCode != 0) {
		    returnCode = TPM_BADINDEX;
		}
	    }
	    /* build the TPM_STORED_DATA S1 structure */
	    if (returnCode == TPM_SUCCESS) {
		/* ii. Set S1 -> sealInfo -> pcrSelection to pcrInfo -> pcrSelection */
		returnCode = TPM_PCRInfo_CreateFromBuffer(&(s1StoredData.tpm_seal_info), &pcrInfo);
	    }
	    /* iii. Create h1 the composite hash of the PCR selected by pcrInfo -> pcrSelection */
	    /* iv. Set S1 -> sealInfo -> digestAtCreation to h1 */
	    /* NOTE hash directly to destination.  */
	    if (returnCode == TPM_SUCCESS) {
		returnCode =
		    TPM_PCRSelection_GenerateDigest(s1StoredData.tpm_seal_info->digestAtCreation,
						    &(tpm_pcr_info.pcrSelection),
						    tpm_state->tpm_stclear_data.PCRS);
	    }
	    /* v. Set S1 -> sealInfo -> digestAtRelease to pcrInfo -> digestAtRelease */
	    /* NOTE digestAtRelease copied during TPM_PCRInfo_CreateFromBuffer() */
	}
	/* b. else (v1 is 2) */
	else {
	    /* i. Validate pcrInfo as a valid TPM_PCR_INFO_LONG structure, return TPM_BADINDEX
	       on error */
	    if (returnCode == TPM_SUCCESS) {
		returnCode = TPM_PCRInfoLong_Load(&tpm_pcr_info_long, &stream, &stream_size);
		if (returnCode != 0) {
		    returnCode = TPM_BADINDEX;
		}
	    }
	    /* build the TPM_STORED_DATA S1 structure */
	    if (returnCode == TPM_SUCCESS) {
		/* ii. Set S1 -> sealInfo -> creationPCRSelection to pcrInfo -> creationPCRSelection
		   */
		/* iii. Set S1 -> sealInfo -> releasePCRSelection to pcrInfo -> releasePCRSelection
		   */
		/* iv. Set S1 -> sealInfo -> digestAtRelease to pcrInfo -> digestAtRelease */
		/* v. Set S1 -> sealInfo -> localityAtRelease to pcrInfo -> localityAtRelease */
		/* NOTE copied during TPM_PCRInfoLong_CreateFromBuffer() */
		returnCode = TPM_PCRInfoLong_CreateFromBuffer(&(s1_12->tpm_seal_info_long),
							      &pcrInfo);
	    }
	    if (returnCode == TPM_SUCCESS) {
		/* vi. Create h2 the composite hash of the PCR selected by pcrInfo ->
		   creationPCRSelection */
		/* vii. Set S1 -> sealInfo -> digestAtCreation to h2 */
		/* NOTE hash directly to destination. */
		returnCode =
		    TPM_PCRSelection_GenerateDigest(s1_12->tpm_seal_info_long->digestAtCreation,
						    &(tpm_pcr_info_long.creationPCRSelection),
						    tpm_state->tpm_stclear_data.PCRS);
	    }
	    /* viii. Set S1 -> sealInfo -> localityAtCreation to TPM_STANY_FLAGS ->
	       localityModifier */
	    if (returnCode == TPM_SUCCESS) {
		returnCode = TPM_Locality_Set(&(s1_12->tpm_seal_info_long->localityAtCreation),
					      tpm_state->tpm_stany_flags.localityModifier);
	    }
	}
    }
    /* 12. Create a1 by decrypting encAuth according to the ADIP indicated by authHandle. */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_AuthSessionData_Decrypt(a1Auth,
						 NULL,
						 encAuth,
						 auth_session_data,
						 NULL,
						 NULL,
						 FALSE);	/* even and odd */
    }
    /* 13. The TPM provides NO validation of a1. Well-known values (like all zeros) are valid and
       possible. */
    /* 14. Create S2 a TPM_SEALED_DATA structure */
    if (returnCode == TPM_SUCCESS) {
	/* a. Set S2 -> payload to TPM_PT_SEAL */
	/* NOTE: Done at TPM_SealedData_Init() */
	/* b. Set S2 -> tpmProof to TPM_PERMANENT_DATA -> tpmProof */
	TPM_Secret_Copy(s2SealedData.tpmProof, tpm_state->tpm_permanent_data.tpmProof);
	/* c. Create h3 the SHA-1 of S1 */
	/* d. Set S2 -> storedDigest to h3 */
	returnCode = TPM_StoredData_GenerateDigest(s2SealedData.storedDigest,
						   &s1StoredData, v1PcrVersion);
    }
    if (returnCode == TPM_SUCCESS) {
	/* e. Set S2 -> authData to a1 */
	TPM_Secret_Copy(s2SealedData.authData, a1Auth);
	/* f. Set S2 -> dataSize to inDataSize */
	/* g. Set S2 -> data to inData */
	returnCode = TPM_SizedBuffer_Copy(&(s2SealedData.data), &inData);
    }
    /* 15. Validate that the size of S2 can be encrypted by the key pointed to by keyHandle, return
       TPM_BAD_DATASIZE on error */
    /* 16. Create s3 the encryption of S2 using the key pointed to by keyHandle */
    /* 17. Set continueAuthSession to FALSE */
    if (returnCode == TPM_SUCCESS) {
	continueAuthSession = FALSE;
    }
    /* 18. Set S1 -> encDataSize to the size of s3 */
    /* 19. Set S1 -> encData to s3 */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_SealedData_GenerateEncData(&(s1StoredData.encData), &s2SealedData, key);
    }
    /*
      response
    */
    /* standard response: tag, (dummy) paramSize, returnCode.  Failure is fatal. */
    if (rcf == 0) {
	printf("TPM_Process_Seal: Ordinal returnCode %08x %u\n",
	       returnCode, returnCode);
	rcf = TPM_Sbuffer_StoreInitialResponse(response, tag, returnCode);
    }
    /* success response, append the rest of the parameters.  */
    if (rcf == 0) {
	if (returnCode == TPM_SUCCESS) {
	    /* checkpoint the beginning of the outParam's */
	    outParamStart = response->buffer_current - response->buffer;
	    /* 20. Return S1 as sealedData */
	    returnCode = TPM_StoredData_Store(response, &s1StoredData, v1PcrVersion);
	    /* checkpoint the end of the outParam's */
	    outParamEnd = response->buffer_current - response->buffer;
	}
	/* digest the above the line output parameters */
	if (returnCode == TPM_SUCCESS) {
	    returnCode = TPM_GetOutParamDigest(outParamDigest,	/* output */
					       auditStatus,	/* input audit status */
					       transportEncrypt,
					       tag,			
					       returnCode,
					       ordinal,		/* command ordinal */
					       response->buffer + outParamStart,	/* start */
					       outParamEnd - outParamStart);		/* length */
	}
	/* calculate and set the below the line parameters */
	if (returnCode == TPM_SUCCESS) {
	    returnCode = TPM_AuthParams_Set(response,
					    *hmacKey,		/* owner HMAC key */
					    auth_session_data,
					    outParamDigest,
					    nonceOdd,
					    continueAuthSession);
	}
	/* audit if required */
	if ((returnCode == TPM_SUCCESS) && auditStatus) {
	    returnCode = TPM_ProcessAudit(tpm_state,
					  transportEncrypt,
					  inParamDigest,
					  outParamDigest,
					  ordinal);
	}
	/* adjust the initial response */
	rcf = TPM_Sbuffer_StoreFinalResponse(response, returnCode, tpm_state);
    }
    /* if there was an error, or continueAuthSession is FALSE, terminate the session */
    if (((rcf != 0) ||
	 ((returnCode != TPM_SUCCESS) && (returnCode != TPM_DEFEND_LOCK_RUNNING)) ||
	 !continueAuthSession) &&
	authHandleValid) {
	TPM_AuthSessions_TerminateHandle(tpm_state->tpm_stclear_data.authSessions, authHandle);
    }
    /*
      cleanup
    */
    TPM_SizedBuffer_Delete(&pcrInfo);			/* @1 */
    TPM_SizedBuffer_Delete(&inData);			/* @2 */
    TPM_StoredData_Delete(&s1StoredData, v1PcrVersion);	/* @3 */
    TPM_PCRInfo_Delete(&tpm_pcr_info);			/* @4 */
    TPM_PCRInfoLong_Delete(&tpm_pcr_info_long); 	/* @5 */
    TPM_SealedData_Delete(&s2SealedData);		/* @6 */
    return rcf;
}

/* 10.7 TPM_Sealx rev 110
     
   The TPM_Sealx command works exactly like the TPM_Seal command with the additional requirement of
   encryption for the inData parameter. This command also places in the sealed blob the information
   that the TPM_Unseal also requires encryption.

   TPM_Sealx requires the use of 1.2 data structures. The actions are the same as TPM_Seal without
   the checks for 1.1 data structure usage.
*/

TPM_RESULT TPM_Process_Sealx(tpm_state_t *tpm_state,
			     TPM_STORE_BUFFER *response,
			     TPM_TAG tag,
			     uint32_t paramSize,
			     TPM_COMMAND_CODE ordinal,
			     unsigned char *command,
			     TPM_TRANSPORT_INTERNAL *transportInternal)
{
    TPM_RESULT	rcf = 0;			/* fatal error precluding response */
    TPM_RESULT	returnCode = TPM_SUCCESS;	/* command return code */

    /* input parameters */
    TPM_KEY_HANDLE	keyHandle;	/* Handle of a loaded key that can perform seal
					   operations. */
    TPM_ENCAUTH		encAuth;	/* The encrypted authorization data for the sealed data */
    TPM_SIZED_BUFFER	pcrInfo;	/* If 0 there are no PCR registers in use.  pcrInfo MUST use
					   TPM_PCR_INFO_LONG */
    TPM_SIZED_BUFFER	inData;		/* The data to be sealed to the platform and any specified
					   PCRs */

    TPM_AUTHHANDLE	authHandle;	/* The authorization session handle used for keyHandle
					   authorization.  Must be an OSAP session for this command.
					   */
    TPM_NONCE		nonceOdd;	/* Nonce generated by system associated with authHandle */
    TPM_BOOL		continueAuthSession = TRUE;	/* Ignored */
    TPM_AUTHDATA	pubAuth;	/* The authorization digest for inputs and keyHandle. HMAC
					   key: key.usageAuth. */

    /* processing */
    unsigned char *		inParamStart;			/* starting point of inParam's */
    unsigned char *		inParamEnd;			/* ending point of inParam's */
    TPM_DIGEST			inParamDigest;
    TPM_BOOL			auditStatus;			/* audit the ordinal */
    TPM_BOOL			transportEncrypt;	/* wrapped in encrypted transport session */
    TPM_BOOL			authHandleValid = FALSE;
    TPM_SECRET			*hmacKey;
    TPM_KEY			*key = NULL;			/* the key specified by keyHandle */
    TPM_SECRET			*keyUsageAuth;
    TPM_BOOL			parentPCRStatus;
    TPM_AUTH_SESSION_DATA	*auth_session_data = NULL;	/* session data for authHandle */

    /* output parameters */
    uint32_t			outParamStart;	/* starting point of outParam's */
    uint32_t			outParamEnd;	/* ending point of outParam's */
    TPM_DIGEST			outParamDigest;
    TPM_STORED_DATA12		s1StoredData;	/* Encrypted, integrity-protected data object that
						   is the result of the TPM_Seal operation. Returned
						   as SealedData */
    TPM_STORED_DATA		*s1_11;		/* 1.1 version to avoid casts */
    TPM_SEALED_DATA		s2SealedData;
    TPM_DIGEST			a1Auth;
    BYTE			*o1DecryptedData;
    
    printf("TPM_Process_Sealx: Ordinal Entry\n");
    s1_11 = (TPM_STORED_DATA *)&s1StoredData;	/* 1.1 version to avoid casts */
    TPM_SizedBuffer_Init(&pcrInfo);		/* freed @1 */
    TPM_SizedBuffer_Init(&inData);		/* freed @2 */
    TPM_StoredData_Init(s1_11, 2);		/* freed @3 */
    TPM_SealedData_Init(&s2SealedData); 	/* freed @4 */
    o1DecryptedData = NULL;			/* freed @5 */
    /*
      get inputs
    */
    /*	get keyHandle parameter */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_Load32(&keyHandle, &command, &paramSize);
    }
    /* save the starting point of inParam's for authorization and auditing */
    inParamStart = command;
    /* get encAuth parameter */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_Authdata_Load(encAuth, &command, &paramSize);
    }
    /* get pcrInfo parameter */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_SizedBuffer_Load(&pcrInfo, &command, &paramSize);
    }	
    /* get inData parameter */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_SizedBuffer_Load(&inData, &command, &paramSize);
    }	
    if (returnCode == TPM_SUCCESS) {
	printf("TPM_Process_Sealx: Sealing %u bytes\n", inData.size);
	TPM_PrintFour("TPM_Process_Sealx: Sealing data", inData.buffer);
    }	
    /* save the ending point of inParam's for authorization and auditing */
    inParamEnd = command;
    /* digest the input parameters */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_GetInParamDigest(inParamDigest,	/* output */
					  &auditStatus,		/* output */
					  &transportEncrypt,	/* output */
					  tpm_state,
					  tag,
					  ordinal,
					  inParamStart,
					  inParamEnd,
					  transportInternal);
    }
    /* check state */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_CheckState(tpm_state, tag, TPM_CHECK_ALL);
    }
    /* check tag */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_CheckRequestTag1(tag);
    }
    /* get the 'below the line' authorization parameters */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_AuthParams_Get(&authHandle,
					&authHandleValid,
					nonceOdd,
					&continueAuthSession,
					pubAuth,
					&command, &paramSize);
    }
    if (returnCode == TPM_SUCCESS) {
	if (paramSize != 0) {
	    printf("TPM_Process_Sealx: Error, command has %u extra bytes\n",
		   paramSize);
	    returnCode = TPM_BAD_PARAM_SIZE;
	}
    }
    /* do not terminate sessions if the command did not parse correctly */
    if (returnCode != TPM_SUCCESS) {
	authHandleValid = FALSE;
    }
    /*
      Processing
    */
    /* get the key corresponding to the keyHandle parameter */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_KeyHandleEntries_GetKey(&key, &parentPCRStatus, tpm_state, keyHandle,
						 FALSE,		/* not r/o, using to encrypt */
						 FALSE,		/* do not ignore PCRs */
						 FALSE);	/* cannot use EK */
    }
    /* get keyHandle -> usageAuth */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_Key_GetUsageAuth(&keyUsageAuth, key);
    }	 
    /* get the session data */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_AuthSessions_GetData(&auth_session_data,
					      &hmacKey,
					      tpm_state,
					      authHandle,
					      TPM_PID_OSAP,
					      TPM_ET_KEYHANDLE,
					      ordinal,
					      key,
					      NULL,			/* OIAP */
					      key->tpm_store_asymkey->pubDataDigest);	/* OSAP */
    }
    /* 1. Validate the authorization to use the key pointed to by keyHandle */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_Authdata_Check(tpm_state,
					*hmacKey,		/* HMAC key */
					inParamDigest,
					auth_session_data,	/* authorization session */
					nonceOdd,		/* Nonce generated by system
								   associated with authHandle */
					continueAuthSession,
					pubAuth);		/* Authorization digest for input */
    }
    /* 2. If the inDataSize is 0 the TPM returns TPM_BAD_PARAMETER */
    if (returnCode == TPM_SUCCESS) {
	if (inData.size == 0) {
	    printf("TPM_Process_Sealx: Error, inDataSize is 0\n");
	    returnCode = TPM_BAD_PARAMETER;
	}
    }
    /* 3. If the keyUsage field of the key indicated by keyHandle does not have the value
       TPM_KEY_STORAGE, the TPM must return the error code TPM_INVALID_KEYUSAGE. */
    if (returnCode == TPM_SUCCESS) {
	if (key->keyUsage != TPM_KEY_STORAGE) {
	    printf("TPM_Process_Sealx: Error, key keyUsage %04hx must be TPM_KEY_STORAGE\n",
		   key->keyUsage);
	    returnCode = TPM_INVALID_KEYUSAGE;
	}
    }
    /* 4. If the keyHandle points to a migratable key then the TPM MUST return the error code
       TPM_INVALID_KEY_USAGE. */
    if (returnCode == TPM_SUCCESS) {
	if (key->keyFlags & TPM_MIGRATABLE) {
	    printf("TPM_Process_Sealx: Error, key keyFlags %08x indicates migratable\n",
		   key->keyFlags);
	    returnCode = TPM_INVALID_KEYUSAGE;
	}
    }
    /* 5. Create S1 a TPM_STORED_DATA12 structure */
    /* 6. Set S1 -> encDataSize to 0 */
    /* 7. Set S1 -> encData to all zeros */
    /* NOTE: Done by TPM_StoredData_Init() */
    /* 8. Set S1 -> sealInfoSize to pcrInfoSize */
    /* NOTE This step is unnecessary.  If pcrInfoSize is 0, sealInfoSize is already initialized
       to 0.  If pcrInfoSize is non-zero, sealInfoSize is the result of serialization of the
       tpm_seal_info member, which is a TPM_PCR_INFO_LONG */
    /* 9. If pcrInfoSize is not 0 then */
    if ((returnCode == TPM_SUCCESS) && (pcrInfo.size != 0)) {
	printf("TPM_Process_Sealx: Setting sealInfo to pcrInfo\n");
	/* initializing the s -> TPM_PCR_INFO_LONG cache to the contents of pcrInfo */
	/* a. Validate pcrInfo as a valid TPM_PCR_INFO_LONG structure, return TPM_BADINDEX on
	   error */
	if (returnCode == TPM_SUCCESS) {
	    returnCode = TPM_PCRInfoLong_CreateFromBuffer(&(s1StoredData.tpm_seal_info_long),
							  &pcrInfo);
	    if (returnCode != TPM_SUCCESS) {
		returnCode = TPM_BADINDEX;
	    }
	}
	/* b. Set S1 -> sealInfo -> creationPCRSelection to pcrInfo -> creationPCRSelection */
	/* c. Set S1 -> sealInfo -> releasePCRSelection to pcrInfo -> releasePCRSelection */
	/* d. Set S1 -> sealInfo -> digestAtRelease to pcrInfo -> digestAtRelease */
	/* e. Set S1 -> sealInfo -> localityAtRelease to pcrInfo -> localityAtRelease  */
	/* NOTE copied during TPM_PCRInfoLong_CreateFromBuffer() */
	/* f. Create h2 the composite hash of the PCR selected by pcrInfo -> creationPCRSelection */
	/* g. Set S1 -> sealInfo -> digestAtCreation to h2 */
	if (returnCode == TPM_SUCCESS) {
	    returnCode = TPM_PCRSelection_GenerateDigest
			 (s1StoredData.tpm_seal_info_long->digestAtCreation,
			  &(s1StoredData.tpm_seal_info_long->creationPCRSelection),
			  tpm_state->tpm_stclear_data.PCRS);
	}
	/* h. Set S1 -> sealInfo -> localityAtCreation to TPM_STANY_DATA -> localityModifier */
	if (returnCode == TPM_SUCCESS) {
	    returnCode = TPM_Locality_Set(&(s1StoredData.tpm_seal_info_long->localityAtCreation),
					  tpm_state->tpm_stany_flags.localityModifier);
	}
    }
    /* 10. Create S2 a TPM_SEALED_DATA structure */
    /* NOTE: Done at TPM_SealedData_Init() */
    /* 11.Create a1 by decrypting encAuth according to the ADIP indicated by authHandle. */
    if (returnCode == TPM_SUCCESS) {
	printf("TPM_Process_Sealx: Decrypting encAuth\n");
	returnCode = TPM_AuthSessionData_Decrypt(a1Auth,	/* a1 even */
						 NULL,		/* a1 odd (2nd encAuth) */
						 encAuth,	/* encAuthEven */
						 auth_session_data,
						 NULL,		/* nonceOdd */
						 NULL,		/* encAuthOdd */
						 FALSE);	/* even and odd */
    }
    if (returnCode == TPM_SUCCESS) {
	TPM_PrintFour("TPM_Process_Sealx: Decrypted Auth", a1Auth);
	/* a. If authHandle indicates XOR encryption for the AuthData secrets */
	if (auth_session_data->adipEncScheme == TPM_ET_XOR) {
	    /* i. Set S1 -> et to TPM_ET_XOR || TPM_ET_KEY */
	    /* (1) TPM_ET_KEY is added because TPM_Unseal uses zero as a special value indicating no
	       encryption. */
	    s1StoredData.et = TPM_ET_XOR | TPM_ET_KEY;
	}
	/* b. Else */
	else {
	    /* i. Set S1 -> et to algorithm indicated by authHandle */
	    s1StoredData.et = auth_session_data->adipEncScheme << 8;
	}
    }
    /* 12. The TPM provides NO validation of a1. Well-known values (like all zeros) are valid and
       possible. */
    /* 13. If authHandle indicates XOR encryption */
    /* a. Use MGF1 to create string X2 of length inDataSize. The inputs to MGF1 are;
       authLastNonceEven, nonceOdd, "XOR", and authHandle -> sharedSecret. The four concatenated
       values form the Z value that is the seed for MFG1. */
    /* b. Create o1 by XOR of inData and x2 */
    /* 14. Else */
    /* a. Create o1 by decrypting inData using the algorithm indicated by authHandle */
    /* b. Key is from authHandle -> sharedSecret */
    /* c. CTR is SHA-1 of (authLastNonceEven || nonceOdd) */
    if (returnCode == TPM_SUCCESS) {
	printf("TPM_Process_Sealx: decrypting inData\n");
	returnCode = TPM_SealCryptCommon(&o1DecryptedData,		/* freed by caller */
					 auth_session_data->adipEncScheme,
					 &inData,
					 auth_session_data,
					 nonceOdd);

   }
    /* 15. Create S2 a TPM_SEALED_DATA structure */
    if (returnCode == TPM_SUCCESS) {
	/* a. Set S2 -> payload to TPM_PT_SEAL */
	/* NOTE: Done at TPM_SealedData_Init() */
	/* b. Set S2 -> tpmProof to TPM_PERMANENT_DATA -> tpmProof */
	TPM_Secret_Copy(s2SealedData.tpmProof, tpm_state->tpm_permanent_data.tpmProof);
	/* c. Create h3 the SHA-1 of S1 */
	/* d. Set S2 -> storedDigest to h3 */
	returnCode = TPM_StoredData_GenerateDigest(s2SealedData.storedDigest, s1_11, 2);
    }
    /* e. Set S2 -> authData to a1 */
    if (returnCode == TPM_SUCCESS) {
	TPM_Secret_Copy(s2SealedData.authData, a1Auth);
	/* f. Set S2 -> dataSize to inDataSize */
	/* g. Set S2 -> data to o1 */
	returnCode = TPM_SizedBuffer_Set(&(s2SealedData.data), inData.size, o1DecryptedData);
    }
    /* 16. Validate that the size of S2 can be encrypted by the key pointed to by keyHandle, return
       */
    /* TPM_BAD_DATASIZE on error */
    /* 17. Create s3 the encryption of S2 using the key pointed to by keyHandle */
    /* 18. Set continueAuthSession to FALSE */
    if (returnCode == TPM_SUCCESS) {
	continueAuthSession = FALSE;
    }
    /* 19. Set S1 -> encDataSize to the size of s3 */
    /* 20. Set S1 -> encData to s3 */
    if (returnCode == TPM_SUCCESS) {
	printf("TPM_Process_Sealx: Encrypting sealed data\n");
	returnCode = TPM_SealedData_GenerateEncData(&(s1StoredData.encData), &s2SealedData, key);
    }
    /*
      response
    */
    /* standard response: tag, (dummy) paramSize, returnCode.  Failure is fatal. */
    if (rcf == 0) {
	printf("TPM_Process_Sealx: Ordinal returnCode %08x %u\n",
	       returnCode, returnCode);
	rcf = TPM_Sbuffer_StoreInitialResponse(response, tag, returnCode);
    }
    /* success response, append the rest of the parameters.  */
    if (rcf == 0) {
	if (returnCode == TPM_SUCCESS) {
	    /* checkpoint the beginning of the outParam's */
	    outParamStart = response->buffer_current - response->buffer;
	    /* 21. Return S1 as sealedData */
	    returnCode = TPM_StoredData_Store(response, s1_11, 2);
	    /* checkpoint the end of the outParam's */
	    outParamEnd = response->buffer_current - response->buffer;
	}
	/* digest the above the line output parameters */
	if (returnCode == TPM_SUCCESS) {
	    returnCode = TPM_GetOutParamDigest(outParamDigest,	/* output */
					       auditStatus,	/* input audit status */
					       transportEncrypt,
					       tag,			
					       returnCode,
					       ordinal,		/* command ordinal */
					       response->buffer + outParamStart,	/* start */
					       outParamEnd - outParamStart);	/* length */
	}
	/* calculate and set the below the line parameters */
	if (returnCode == TPM_SUCCESS) {
	    returnCode = TPM_AuthParams_Set(response,
					    *hmacKey,		/* owner HMAC key */
					    auth_session_data,
					    outParamDigest,
					    nonceOdd,
					    continueAuthSession);
	}
	/* audit if required */
	if ((returnCode == TPM_SUCCESS) && auditStatus) {
	    returnCode = TPM_ProcessAudit(tpm_state,
					  transportEncrypt,
					  inParamDigest,
					  outParamDigest,
					  ordinal);
	}
	/* adjust the initial response */
	rcf = TPM_Sbuffer_StoreFinalResponse(response, returnCode, tpm_state);
    }
    /* if there was an error, or continueAuthSession is FALSE, terminate the session */
    if (((rcf != 0) ||
	 ((returnCode != TPM_SUCCESS) && (returnCode != TPM_DEFEND_LOCK_RUNNING)) ||
	 !continueAuthSession) &&
	authHandleValid) {
	TPM_AuthSessions_TerminateHandle(tpm_state->tpm_stclear_data.authSessions, authHandle);
    }
    /*
      cleanup
    */
    TPM_SizedBuffer_Delete(&pcrInfo);		/* @1 */
    TPM_SizedBuffer_Delete(&inData);		/* @2 */
    TPM_StoredData_Delete(s1_11, 2);		/* @3 */
    TPM_SealedData_Delete(&s2SealedData);	/* @4 */
    free(o1DecryptedData);			/* @5 */
    return rcf;
}

/* 10.2 TPM_Unseal rev 110

   The TPM_Unseal operation will reveal TPM_Sealed data only if it was encrypted on this platform
   and the current configuration (as defined by the named PCR contents) is the one named as
   qualified to decrypt it.  Internally, TPM_Unseal accepts a data blob generated by a TPM_Seal
   operation. TPM_Unseal decrypts the structure internally, checks the integrity of the resulting
   data, and checks that the PCR named has the value named during TPM_Seal.  Additionally, the
   caller must supply appropriate authorization data for blob and for the key that was used to seal
   that data.
   
   If the integrity, platform configuration and authorization checks succeed, the sealed data is
   returned to the caller; otherwise, an error is generated.
*/

TPM_RESULT TPM_Process_Unseal(tpm_state_t *tpm_state,
			      TPM_STORE_BUFFER *response,
			      TPM_TAG tag,
			      uint32_t paramSize,
			      TPM_COMMAND_CODE ordinal,
			      unsigned char *command,
			      TPM_TRANSPORT_INTERNAL *transportInternal)
{
    TPM_RESULT	rcf = 0;			/* fatal error precluding response */
    TPM_RESULT	returnCode = TPM_SUCCESS;	/* command return code */

    /* input parameters */
    TPM_KEY_HANDLE	parentHandle;	/* Handle of a loaded key that can unseal the data. */
    TPM_STORED_DATA	inData;		/* The encrypted data generated by TPM_Seal. */
    TPM_AUTHHANDLE	authHandle;	/* The authorization handle used for parentHandle. */
    TPM_NONCE		nonceOdd;	/* Nonce generated by system associated with authHandle */
    TPM_BOOL	continueAuthSession = TRUE;	/* The continue use flag for the authorization
						   handle */
    TPM_AUTHDATA	parentAuth;	/* The authorization digest for inputs and
					   parentHandle. HMAC key: parentKey.usageAuth. */
    TPM_AUTHHANDLE	dataAuthHandle; /* The authorization handle used to authorize inData. */
    TPM_NONCE		datanonceOdd;	/* Nonce generated by system associated with
					   entityAuthHandle */
    TPM_BOOL	continueDataSession = TRUE;	/* Continue usage flag for dataAuthHandle. */
    TPM_AUTHDATA	dataAuth;	/* The authorization digest for the encrypted entity. HMAC
					   key: entity.usageAuth.  */

    /* processing */
    unsigned char *		inParamStart;		/* starting point of inParam's */
    unsigned char *		inParamEnd;		/* ending point of inParam's */
    TPM_DIGEST			inParamDigest;
    TPM_BOOL			auditStatus;		/* audit the ordinal */
    TPM_BOOL			transportEncrypt;	/* wrapped in encrypted transport session */
    TPM_BOOL			authHandleValid = FALSE;
    TPM_BOOL			dataAuthHandleValid = FALSE;
    TPM_AUTH_SESSION_DATA	*auth_session_data = NULL;	/* session data for authHandle */
    TPM_AUTH_SESSION_DATA	*data_auth_session_data = NULL; /* session data for dataAuthHandle
								   */
    TPM_SECRET			*hmacKey;
    TPM_SECRET			*dataHmacKey;
    unsigned int		v1StoredDataVersion = 1;	/* version of TPM_STORED_DATA
								   inData */
    TPM_KEY			*parentKey;
    TPM_BOOL			parentPCRStatus;
    TPM_SECRET			*parentUsageAuth;
    TPM_SEALED_DATA		d1SealedData;
    TPM_DIGEST			h1StoredDataDigest;
    TPM_STORED_DATA12		*s2StoredData;
    BYTE			*o1Encrypted;			/* For ADIP encryption */
    TPM_ADIP_ENC_SCHEME		adipEncScheme;	 

    /* output parameters */
    uint32_t			outParamStart;	/* starting point of outParam's */
    uint32_t			outParamEnd;	/* ending point of outParam's */
    TPM_DIGEST			outParamDigest;
    uint32_t			secretSize = 0; /* Decrypted data that had been sealed */
    BYTE			*secret = NULL;

    printf("TPM_Process_Unseal: Ordinal Entry\n");
    TPM_StoredData_Init(&inData, v1StoredDataVersion);	/* freed @1, default is v1 */
    TPM_SealedData_Init(&d1SealedData); 		/* freed @2 */
    o1Encrypted = NULL;					/* freed @3 */
    s2StoredData = (TPM_STORED_DATA12 *)&inData;	/* inData when it's a TPM_STORED_DATA12
							   structure */
    /*
      get inputs
    */
    /* get parentHandle parameter */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_Load32(&parentHandle, &command, &paramSize);
    }
    /* save the starting point of inParam's for authorization and auditing */
    inParamStart = command;
    /* get inData parameter */
    if (returnCode == TPM_SUCCESS) {
	printf("TPM_Process_Unseal: parentHandle %08x\n", parentHandle);
	returnCode = TPM_StoredData_Load(&inData, &v1StoredDataVersion, &command, &paramSize);
    }
    if (returnCode == TPM_SUCCESS) {
	printf("TPM_Process_Unseal: inData is v%u\n", v1StoredDataVersion);
    }
    /* save the ending point of inParam's for authorization and auditing */
    inParamEnd = command;
    /* digest the input parameters */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_GetInParamDigest(inParamDigest,	/* output */
					  &auditStatus,		/* output */
					  &transportEncrypt,	/* output */
					  tpm_state,
					  tag,
					  ordinal,
					  inParamStart,
					  inParamEnd,
					  transportInternal);
    }
    /* check state */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_CheckState(tpm_state, tag, TPM_CHECK_ALL);
    }
    /* check tag */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_CheckRequestTag21(tag);
    }
    /* get the optional 'below the line' authorization parameters */
    if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH2_COMMAND)) {
	returnCode = TPM_AuthParams_Get(&authHandle,
					&authHandleValid,
					nonceOdd,
					&continueAuthSession,
					parentAuth,
					&command, &paramSize);
    }
    if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH2_COMMAND)) {
	printf("TPM_Process_Unseal: authHandle %08x\n", authHandle);
    }
    /* get the 'below the line' authorization parameters */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_AuthParams_Get(&dataAuthHandle,
					&dataAuthHandleValid,
					datanonceOdd,
					&continueDataSession,
					dataAuth,
					&command, &paramSize);
    }
    if (returnCode == TPM_SUCCESS) {
	printf("TPM_Process_Unseal: dataAuthHandle %08x\n", dataAuthHandle); 
    }
    if (returnCode == TPM_SUCCESS) {
	if (paramSize != 0) {
	    printf("TPM_Process_Unseal: Error, command has %u extra bytes\n",
		   paramSize);
	    returnCode = TPM_BAD_PARAM_SIZE;
	}
    }
    /* do not terminate sessions if the command did not parse correctly */
    if (returnCode != TPM_SUCCESS) {
	authHandleValid = FALSE;
	dataAuthHandleValid = FALSE;
    }
    /*
      Processing
    */
    /* Verify that parentHandle points to a valid key.	Get the TPM_KEY associated with parentHandle
     */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_KeyHandleEntries_GetKey(&parentKey, &parentPCRStatus,
						 tpm_state, parentHandle,
						 FALSE,		/* not r/o, using to decrypt */
						 FALSE,		/* do not ignore PCRs */
						 FALSE);	/* cannot use EK */
    }
    /* get parentHandle -> usageAuth */
    if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH2_COMMAND)) {
	returnCode = TPM_Key_GetUsageAuth(&parentUsageAuth, parentKey);
    }	 
    /* get the first session data */
    if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH2_COMMAND)) {
	returnCode = TPM_AuthSessions_GetData(&auth_session_data,
					      &hmacKey,
					      tpm_state,
					      authHandle,
					      TPM_PID_NONE,
					      TPM_ET_KEYHANDLE,
					      ordinal,
					      parentKey,
					      parentUsageAuth,			/* OIAP */
					      parentKey->tpm_store_asymkey->pubDataDigest); /*OSAP*/
    }
    /* 1. The TPM MUST validate that parentAuth authorizes the use of the key in parentHandle, on
       error return TPM_AUTHFAIL */
    if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH2_COMMAND)) {
	returnCode = TPM_Authdata_Check(tpm_state,
					*hmacKey,		/* HMAC key */
					inParamDigest,
					auth_session_data,	/* authorization session */
					nonceOdd,		/* Nonce generated by system
								   associated with authHandle */
					continueAuthSession,
					parentAuth);		/* Authorization digest for input */
    }
    /* if there are no parent auth parameters */
    if ((returnCode == TPM_SUCCESS) && (tag != TPM_TAG_RQU_AUTH2_COMMAND)) {
	if (parentKey->authDataUsage != TPM_AUTH_NEVER) {
	    printf("TPM_Process_Unseal: Error, parent key authorization required\n");
	    returnCode = TPM_AUTHFAIL;
	}
    }
    /* 2. If the keyUsage field of the key indicated by parentHandle does not have the value
       TPM_KEY_STORAGE, the TPM MUST return the error code TPM_INVALID_KEYUSAGE. */
    if (returnCode == TPM_SUCCESS) {
	if (parentKey->keyUsage != TPM_KEY_STORAGE) {
	    printf("TPM_Process_Unseal: Error, key keyUsage %04hx must be TPM_KEY_STORAGE\n",
		   parentKey->keyUsage);
	    returnCode = TPM_INVALID_KEYUSAGE;
	}
    }
    /* 3. The TPM MUST check that the TPM_KEY_FLAGS -> Migratable flag has the value FALSE in the
       key indicated by parentKeyHandle. If not, the TPM MUST return the error code
       TPM_INVALID_KEYUSAGE */
    if (returnCode == TPM_SUCCESS) {
	if (parentKey->keyFlags & TPM_MIGRATABLE) {
	    printf("TPM_Process_Unseal: Error, key keyFlags %08x indicates migratable\n",
		   parentKey->keyFlags);
	    returnCode = TPM_INVALID_KEYUSAGE;
	}
    }
    /* 4. Determine the version of inData */
    /* a. If inData -> tag = TPM_TAG_STORED_DATA12 */
    /* i. Set V1 to 2 */
    /* ii. Map S2 a TPM_STORED_DATA12 structure to inData */
    /* b. Else If inData -> ver = 1.1 */
    /* i. Set V1 to 1 */
    /* ii. Map S2 a TPM_STORED_DATA structure to inData */
    /* c. Else */
    /* i. Return TPM_BAD_VERSION */
    /* NOTE: Done during TPM_StoredData_Load() */
    /* The extra indent of error checking is required because the next steps all return
       TPM_NOTSEALED_BLOB on error */
    if (returnCode == TPM_SUCCESS) {
	/* 5. Create d1 by decrypting S2 -> encData using the key pointed to by parentHandle */
	printf("TPM_Process_Unseal: Decrypting encData\n");
	if (returnCode == TPM_SUCCESS) {
	    returnCode = TPM_SealedData_DecryptEncData(&d1SealedData,	/* TPM_SEALED_DATA */
						       &(inData.encData),
						       parentKey);	
	}
	/* 6. Validate d1 */
	/* a. d1 MUST be a TPM_SEALED_DATA structure */
	/* NOTE Done during TPM_SealedData_DecryptEncData() */
	/* b. d1 -> tpmProof MUST match TPM_PERMANENT_DATA -> tpmProof */
	if (returnCode == TPM_SUCCESS) {
	    printf("TPM_Process_Unseal: Sealed data size %u\n", d1SealedData.data.size);
	    TPM_PrintFour("TPM_Process_Unseal: Sealed data", d1SealedData.data.buffer);
	    printf("TPM_Process_Unseal: Checking tpmProof\n");
	    returnCode = TPM_Secret_Compare(d1SealedData.tpmProof,
					    tpm_state->tpm_permanent_data.tpmProof);
	}
	if (returnCode == TPM_SUCCESS) {
	    /* c. Set S2 -> encDataSize to 0 */
	    /* d. Set S2 -> encData to all zeros */
	    /* NOTE: This would be done at cleanup */
	    TPM_SizedBuffer_Delete(&(inData.encData));
	    /* e. Create h1 the SHA-1 of S2 */
	    returnCode = TPM_StoredData_GenerateDigest(h1StoredDataDigest,
						       &inData, v1StoredDataVersion);
	}
	/* f. d1 -> storedDigest MUST match h1 */
	if (returnCode == TPM_SUCCESS) {
	    printf("TPM_Process_Unseal: Checking storedDigest\n");
	    returnCode = TPM_Digest_Compare(d1SealedData.storedDigest, h1StoredDataDigest);
	}
	/* g. d1 -> payload MUST be TPM_PT_SEAL */
	if (returnCode == TPM_SUCCESS) {
	    if (d1SealedData.payload != TPM_PT_SEAL) {
		printf("TPM_Process_Unseal: Error, payload %02x not TPM_PT_SEAL\n",
		       d1SealedData.payload);
		returnCode = TPM_NOTSEALED_BLOB;
	    }
	}
	/* h. Any failure MUST return TPM_NOTSEALED_BLOB */
	if (returnCode != TPM_SUCCESS) {
	    returnCode = TPM_NOTSEALED_BLOB;
	}
    }
    /* 7. If S2 -> sealInfo is not 0 then */
    /* NOTE: Done by _CheckDigest() */
    if (returnCode == TPM_SUCCESS) {
	printf("TPM_Process_Unseal: Checking PCR digest\n");
	/* a. If V1 is 1 then */
	if (v1StoredDataVersion == 1) {
	    /* i. Validate that S2 -> pcrInfo is a valid TPM_PCR_INFO structure */
	    /* NOTE: Done during TPM_StoredData_Load() */
	    /* ii. Create h2 the composite hash of the PCR selected by S2 -> pcrInfo -> pcrSelection
	     */
	    /* c. Compare h2 with S2 -> pcrInfo -> digestAtRelease, on mismatch return
	       TPM_WRONGPCRVALUE */
	    returnCode = TPM_PCRInfo_CheckDigest(inData.tpm_seal_info,
						 tpm_state->tpm_stclear_data.PCRS); /* PCR array */
	}
	/* b. If V1 is 2 then */
	else {
	    /* i. Validate that S2 -> pcrInfo is a valid TPM_PCR_INFO_LONG structure */
	    /* NOTE: Done during TPM_StoredData_Load() */
	    /* ii. Create h2 the composite hash of the PCR selected by S2 -> pcrInfo ->
	       releasePCRSelection */
	    /* iii. Check that S2 -> pcrInfo -> localityAtRelease for TPM_STANY_DATA ->
	       localityModifier is TRUE */
	    /* (1) For example if TPM_STANY_DATA -> localityModifier was 2 then S2 -> pcrInfo ->
	       localityAtRelease -> TPM_LOC_TWO would have to be TRUE */
	    /* c. Compare h2 with S2 -> pcrInfo -> digestAtRelease, on mismatch return
	       TPM_WRONGPCRVALUE */
	    returnCode =
		TPM_PCRInfoLong_CheckDigest(s2StoredData->tpm_seal_info_long,
					    tpm_state->tpm_stclear_data.PCRS,	/* PCR array */
					    tpm_state->tpm_stany_flags.localityModifier);
	}
    }
    /* 8. The TPM MUST validate authorization to use d1 by checking that the HMAC calculation
       using d1 -> authData as the shared secret matches the dataAuth. Return TPM_AUTHFAIL on
       mismatch. */
    /* get the second session data */
    /* NOTE: While OSAP isn't specifically excluded, there is currently no way to set up an OSAP
       session using TPM_SEALED_DATA as the entity */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_AuthSessions_GetData(&data_auth_session_data,
					      &dataHmacKey,
					      tpm_state,
					      dataAuthHandle,
					      TPM_PID_OIAP,	/* currently require OIAP */
					      0,		/* OSAP entity type */
					      ordinal,
					      NULL,
					      &(d1SealedData.authData), /* OIAP */
					      NULL);			/* OSAP */
    }
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_Auth2data_Check(tpm_state,
					 *dataHmacKey,		/* HMAC key */
					 inParamDigest,
					 data_auth_session_data, /* authorization session */
					 datanonceOdd,		/* Nonce generated by system
								   associated with authHandle */
					 continueDataSession,
					 dataAuth);		/* Authorization digest for input */
    }
    if (returnCode == TPM_SUCCESS) {
	/* 9. If V1 is 2 and S2 -> et specifies encryption (i.e. is not all zeros) then */
	if ((v1StoredDataVersion == 2) && (s2StoredData->et != 0x0000)) {
	    /* a. If tag is not TPM_TAG_RQU_AUTH2_COMMAND, return TPM_AUTHFAIL */
	    if (returnCode == TPM_SUCCESS) {
		if (tag != TPM_TAG_RQU_AUTH2_COMMAND) {
		    printf("TPM_Process_Unseal: Error, sealed with encryption but auth-1\n");
		    returnCode = TPM_AUTHFAIL;
		}
	    }
	    /* b. Verify that the authHandle session type is TPM_PID_OSAP or TPM_PID_DSAP, return
	       TPM_BAD_MODE on error. */
	    if (returnCode == TPM_SUCCESS) {
		if ((auth_session_data->protocolID != TPM_PID_OSAP) &&
		    (auth_session_data->protocolID != TPM_PID_DSAP)) {
		    printf("TPM_Process_Unseal: Error, sealed with encryption but OIAP\n");
		    returnCode = TPM_BAD_MODE;
		}
	    }	    
	    /* c. If MSB of S2 -> et is TPM_ET_XOR */
	    /* i. Use MGF1 to create string X1 of length sealedDataSize. The inputs to MGF1 are;
	       authLastnonceEven, nonceOdd, "XOR", and authHandle -> sharedSecret. The four
	       concatenated values form the Z value that is the seed for MFG1. */
	    /* d. Else */
	    /* i. Create o1 by encrypting d1 -> data using the algorithm indicated by inData ->
	       et */
	    /* ii. Key is from authHandle -> sharedSecret */
	    /* iii. IV is SHA-1 of (authLastNonceEven || nonceOdd) */
	    if (returnCode == TPM_SUCCESS) {
		/* entity type MSB is ADIP encScheme */
		adipEncScheme = (s2StoredData->et >> 8) & 0x00ff;
		printf("TPM_Process_Unseal: Encrypting the output, encScheme %02x\n",
		       adipEncScheme);
		returnCode = TPM_SealCryptCommon(&o1Encrypted,
						 adipEncScheme,
						 &(d1SealedData.data),
						 auth_session_data,
						 nonceOdd);
		secretSize = d1SealedData.data.size;
		secret = o1Encrypted;
	    }
	    /* e. Set continueAuthSession to FALSE */
	    continueAuthSession = FALSE;
	}
	/* 10. else */
	else {
	    printf("TPM_Process_Unseal: No output encryption\n");
	    /* a. Set o1 to d1 -> data */
	    secretSize = d1SealedData.data.size;
	    secret = d1SealedData.data.buffer;
	}
    }
    /* 11. Set the return secret as o1 */
    /*
      response
    */
    /* standard response: tag, (dummy) paramSize, returnCode.  Failure is fatal. */
    if (rcf == 0) {
	printf("TPM_Process_Unseal: Ordinal returnCode %08x %u\n",
	       returnCode, returnCode);
	rcf = TPM_Sbuffer_StoreInitialResponse(response, tag, returnCode);
    }
    /* success response, append the rest of the parameters.  */
    if (rcf == 0) {
	if (returnCode == TPM_SUCCESS) {
	    /* checkpoint the beginning of the outParam's */
	    outParamStart = response->buffer_current - response->buffer;
	    /* return secretSize */
	    returnCode = TPM_Sbuffer_Append32(response, secretSize);
	}
	if (returnCode == TPM_SUCCESS) {
	    /* return secret */
	    returnCode = TPM_Sbuffer_Append(response, secret, secretSize);
	    /* checkpoint the end of the outParam's */
	    outParamEnd = response->buffer_current - response->buffer;
	}
	/* digest the above the line output parameters */
	if (returnCode == TPM_SUCCESS) {
	    returnCode = TPM_GetOutParamDigest(outParamDigest,	/* output */
					       auditStatus,	/* input audit status */
					       transportEncrypt,
					       tag,			
					       returnCode,
					       ordinal,		/* command ordinal */
					       response->buffer + outParamStart,	/* start */
					       outParamEnd - outParamStart);	/* length */
	}
	/* calculate and set the below the line parameters */
	if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH2_COMMAND)) {
	    returnCode = TPM_AuthParams_Set(response,
					    *hmacKey,		/* HMAC key */
					    auth_session_data,
					    outParamDigest,
					    nonceOdd,
					    continueAuthSession);
	}
	/* calculate and set the below the line parameters */
	if (returnCode == TPM_SUCCESS) {
	    returnCode = TPM_AuthParams_Set(response,
					    *dataHmacKey,	/* HMAC key */
					    data_auth_session_data,
					    outParamDigest,
					    datanonceOdd,
					    continueDataSession);
	}
	/* audit if required */
	if ((returnCode == TPM_SUCCESS) && auditStatus) {
	    returnCode = TPM_ProcessAudit(tpm_state,
					  transportEncrypt,
					  inParamDigest,
					  outParamDigest,
					  ordinal);
	}
	/* adjust the initial response */
	rcf = TPM_Sbuffer_StoreFinalResponse(response, returnCode, tpm_state);
    }
    /* if there was an error, or continueAuthSession is FALSE, terminate the session */
    if (((rcf != 0) ||
	 ((returnCode != TPM_SUCCESS) && (returnCode != TPM_DEFEND_LOCK_RUNNING)) ||
	 !continueAuthSession) &&
	authHandleValid) {
	TPM_AuthSessions_TerminateHandle(tpm_state->tpm_stclear_data.authSessions, authHandle);
    }
    if (((rcf != 0) ||
	 ((returnCode != TPM_SUCCESS) && (returnCode != TPM_DEFEND_LOCK_RUNNING)) ||
	 !continueDataSession) &&
	dataAuthHandleValid) {
	TPM_AuthSessions_TerminateHandle(tpm_state->tpm_stclear_data.authSessions, dataAuthHandle);
    }
    /*
      cleanup
    */
    TPM_StoredData_Delete(&inData, v1StoredDataVersion);	/* @1 */
    TPM_SealedData_Delete(&d1SealedData);			/* @2 */
    free(o1Encrypted);						/* @3 */
    return rcf;
}
	    
/* 10.3 TPM_UnBind rev 87

   TPM_UnBind takes the data blob that is the result of a Tspi_Data_Bind command and decrypts it
   for export to the User. The caller must authorize the use of the key that will decrypt the
   incoming blob.

   UnBind operates on a block-by-block basis, and has no notion of any relation between one block
   and another.

   UnBind SHALL operate on a single block only. 
*/

TPM_RESULT TPM_Process_UnBind(tpm_state_t *tpm_state,
			      TPM_STORE_BUFFER *response,
			      TPM_TAG tag,
			      uint32_t paramSize,
			      TPM_COMMAND_CODE ordinal,
			      unsigned char *command,
			      TPM_TRANSPORT_INTERNAL *transportInternal)
{
    TPM_RESULT	rcf = 0;			/* fatal error precluding response */
    TPM_RESULT	returnCode = TPM_SUCCESS;	/* command return code */

    /* input parameters */
    TPM_KEY_HANDLE	keyHandle;	/* The keyHandle identifier of a loaded key that can perform
					   UnBind operations. */
    TPM_SIZED_BUFFER	inData;		/* Encrypted blob to be decrypted */
    TPM_AUTHHANDLE	authHandle;	/* The handle used for keyHandle authorization */
    TPM_NONCE		nonceOdd;	/* Nonce generated by system associated with authHandle */
    TPM_BOOL	continueAuthSession = TRUE;	/* The continue use flag for the authorization
						   handle */
    TPM_AUTHDATA	privAuth;	/* The authorization digest that authorizes the inputs and
					   use of keyHandle. HMAC key: key.usageAuth. */

    /* processing parameters */
    unsigned char *		inParamStart;			/* starting point of inParam's */
    unsigned char *		inParamEnd;			/* ending point of inParam's */
    TPM_DIGEST			inParamDigest;
    TPM_BOOL			auditStatus;		/* audit the ordinal */
    TPM_BOOL			transportEncrypt;	/* wrapped in encrypted transport session */
    TPM_BOOL			authHandleValid = FALSE;
    TPM_SECRET			*hmacKey;
    TPM_KEY			*key = NULL;			/* the key specified by keyHandle */
    TPM_SECRET			*keyUsageAuth;
    TPM_RSA_KEY_PARMS		*tpm_rsa_key_parms;		/* for key */
    TPM_BOOL			parentPCRStatus;
    TPM_AUTH_SESSION_DATA	*auth_session_data = NULL;	/* session data for authHandle */
    uint32_t			decrypt_data_size;		/* resulting decrypted data size */
    BYTE			*decrypt_data = NULL;		/* The resulting decrypted data. */
    unsigned char		*stream;
    uint32_t			stream_size;
    TPM_BOUND_DATA		tpm_bound_data;

    /* output parameters */
    uint32_t		outParamStart;		/* starting point of outParam's */
    uint32_t		outParamEnd;		/* ending point of outParam's */
    TPM_DIGEST		outParamDigest;
    size_t		outDataSize = 0;	/* The length of the returned decrypted data */
    BYTE		*outData = NULL;	/* The resulting decrypted data. */
    
    printf("TPM_Process_UnBind: Ordinal Entry\n");
    TPM_SizedBuffer_Init(&inData);		/* freed @1 */
    TPM_BoundData_Init(&tpm_bound_data);	/* freed @3 */
    /*
      get inputs
    */
    /*	get keyHandle parameter */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_Load32(&keyHandle, &command, &paramSize);
    }
    /* save the starting point of inParam's for authorization and auditing */
    inParamStart = command;
    /* get areaToSignSize and areaToSign parameters */
    if (returnCode == TPM_SUCCESS) {
	printf("TPM_Process_UnBind: keyHandle %08x\n", keyHandle);
	returnCode = TPM_SizedBuffer_Load(&inData, &command, &paramSize);
    }
    if (returnCode == TPM_SUCCESS) {
	printf("TPM_Process_UnBind: UnBinding %u bytes\n", inData.size);
    }
    /* save the ending point of inParam's for authorization and auditing */
    inParamEnd = command;
    /* digest the input parameters */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_GetInParamDigest(inParamDigest,	/* output */
					  &auditStatus,		/* output */
					  &transportEncrypt,	/* output */
					  tpm_state,
					  tag,
					  ordinal,
					  inParamStart,
					  inParamEnd,
					  transportInternal);
    }
    /* check state */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_CheckState(tpm_state, tag, TPM_CHECK_ALL);
    }
    /* check tag */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_CheckRequestTag10(tag);
    }
    /* get the optional 'below the line' authorization parameters */
    if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH1_COMMAND)) {
	returnCode = TPM_AuthParams_Get(&authHandle,
					&authHandleValid,
					nonceOdd,
					&continueAuthSession,
					privAuth,
					&command, &paramSize);
    }
    if (returnCode == TPM_SUCCESS) {
	if (paramSize != 0) {
	    printf("TPM_Process_UnBind: Error, command has %u extra bytes\n",
		   paramSize);
	    returnCode = TPM_BAD_PARAM_SIZE;
	}
    }
    /* do not terminate sessions if the command did not parse correctly */
    if (returnCode != TPM_SUCCESS) {
	authHandleValid = FALSE;
    }
    /*
      Processing
    */
    /* 1. If the inDataSize is 0 the TPM returns TPM_BAD_PARAMETER */
    if (returnCode == TPM_SUCCESS) {
	if (inData.size == 0) {
	    printf("TPM_Process_UnBind: Error, inDataSize is 0\n");
	    returnCode = TPM_BAD_PARAMETER;
	}
    }
    /* get the key corresponding to the keyHandle parameter */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_KeyHandleEntries_GetKey(&key, &parentPCRStatus, tpm_state, keyHandle,
						 FALSE,		/* not read-only */
						 FALSE,		/* do not ignore PCRs */
						 FALSE);	/* cannot use EK */
    }
    if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_COMMAND)){
	if (key->authDataUsage != TPM_AUTH_NEVER) {
	    printf("TPM_Process_UnBind: Error, authorization required\n");
	    returnCode = TPM_AUTHFAIL;
	}
    }
    /* get keyHandle -> usageAuth */
    if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH1_COMMAND)) {
	returnCode = TPM_Key_GetUsageAuth(&keyUsageAuth, key);
    }	 
    /* get the session data */
    if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH1_COMMAND)) {
	returnCode = TPM_AuthSessions_GetData(&auth_session_data,
					      &hmacKey,
					      tpm_state,
					      authHandle,
					      TPM_PID_NONE,
					      TPM_ET_KEYHANDLE,
					      ordinal,
					      key,
					      keyUsageAuth,		/* OIAP */
					      key->tpm_store_asymkey->pubDataDigest); /* OSAP */
    }
    /* 2. Validate the authorization to use the key pointed to by keyHandle */
    if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH1_COMMAND)) {
	returnCode = TPM_Authdata_Check(tpm_state,
					*hmacKey,		/* HMAC key */
					inParamDigest,
					auth_session_data,	/* authorization session */
					nonceOdd,		/* Nonce generated by system
								   associated with authHandle */
					continueAuthSession,
					privAuth);		/* Authorization digest for input */
    }
    /* 3. If the keyUsage field of the key referenced by keyHandle does not have the value
       TPM_KEY_BIND or TPM_KEY_LEGACY, the TPM must return the error code TPM_INVALID_KEYUSAGE */
    if (returnCode == TPM_SUCCESS) {
	if ((key->keyUsage != TPM_KEY_BIND) && (key->keyUsage != TPM_KEY_LEGACY)) {
	    printf("TPM_Process_UnBind: Error, invalid keyUsage %04hx\n", (key->keyUsage));
	    returnCode = TPM_INVALID_KEYUSAGE;
	}
    }
    /* Get the TPM_RSA_KEY_PARMS associated with key */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_KeyParms_GetRSAKeyParms(&tpm_rsa_key_parms, &(key->algorithmParms));
    }	     
    /* 4. Decrypt the inData using the key pointed to by keyHandle */
    if (returnCode == TPM_SUCCESS) {
	returnCode =
	    TPM_RSAPrivateDecryptMalloc(&decrypt_data,		/* decrypted data, freed @2 */
					&decrypt_data_size,	/* actual size of decrypted data
								   data */
					inData.buffer,
					inData.size,
					key);
    }
    if (returnCode == TPM_SUCCESS) {
	/* 5. if (keyHandle -> encScheme does not equal TPM_ES_RSAESOAEP_SHA1_MGF1) and (keyHandle
	   -> keyUsage equals TPM_KEY_LEGACY), */
	if ((key->algorithmParms.encScheme != TPM_ES_RSAESOAEP_SHA1_MGF1) &&
	    (key->keyUsage == TPM_KEY_LEGACY)) {
	    printf("TPM_Process_UnBind: Legacy key\n");
	    /* a. The payload does not have TPM specific markers to validate, so no consistency
	       check can be performed. */
	    /* b. Set the output parameter outData to the value of the decrypted value of
	       inData. (Padding associated with the encryption wrapping of inData SHALL NOT be
	       returned.) */
	    outData = decrypt_data;
	    /* c. Set the output parameter outDataSize to the size of outData, as deduced from the
	       decryption process. */
	    outDataSize = decrypt_data_size;
	}
	/* 6. else */
	else {
	    printf("TPM_Process_UnBind: Payload is TPM_BOUND_DATA structure\n");
	    /* a. Interpret the decrypted data under the assumption that it is a TPM_BOUND_DATA
	       structure, and validate that the payload type is TPM_PT_BIND */
	    if (returnCode == TPM_SUCCESS) {
		stream = decrypt_data;
		stream_size = decrypt_data_size;
		returnCode = TPM_BoundData_Load(&tpm_bound_data,
						&stream,
						&stream_size);
	    }
	    if (returnCode == TPM_SUCCESS) {
		if (tpm_bound_data.payload != TPM_PT_BIND) {
		    printf("TPM_Process_UnBind: Error, "
			   "TPM_BOUND_DATA->payload %02x not TPM_PT_BIND\n",
			   tpm_bound_data.payload);
		    returnCode = TPM_INVALID_STRUCTURE;
		}
	    }
	    /* b. Set the output parameter outData to the value of TPM_BOUND_DATA ->
	       payloadData. (Other parameters of TPM_BOUND_DATA SHALL NOT be returned. Padding
	       associated with the encryption wrapping of inData SHALL NOT be returned.) */
	    if (returnCode == TPM_SUCCESS) {
		outData = tpm_bound_data.payloadData;
		/* c. Set the output parameter outDataSize to the size of outData, as deduced from
		   the decryption process and the interpretation of TPM_BOUND_DATA. */
		outDataSize = tpm_bound_data.payloadDataSize;
	    }
	}
    }
    /*
      response
    */
    /* standard response: tag, (dummy) paramSize, returnCode.  Failure is fatal. */
    if (rcf == 0) {
	printf("TPM_Process_UnBind: Ordinal returnCode %08x %u\n",
	       returnCode, returnCode);
	rcf = TPM_Sbuffer_StoreInitialResponse(response, tag, returnCode);
    }
    /* success response, append the rest of the parameters.  */
    if (rcf == 0) {
	if (returnCode == TPM_SUCCESS) {
	    /* checkpoint the beginning of the outParam's */
	    outParamStart = response->buffer_current - response->buffer;
	    /* 10. Return the computed outData */
	    /* append outDataSize */
	    returnCode = TPM_Sbuffer_Append32(response, outDataSize);
	}
	if (returnCode == TPM_SUCCESS) {
	    /* append outData */
	    returnCode = TPM_Sbuffer_Append(response, outData, outDataSize);
	    /* checkpoint the end of the outParam's */
	    outParamEnd = response->buffer_current - response->buffer;
	}
	/* digest the above the line output parameters */
	if (returnCode == TPM_SUCCESS) {
	    returnCode = TPM_GetOutParamDigest(outParamDigest,	/* output */
					       auditStatus,	/* input audit status */
					       transportEncrypt,
					       tag,			
					       returnCode,
					       ordinal,		/* command ordinal */
					       response->buffer + outParamStart,	/* start */
					       outParamEnd - outParamStart);	/* length */
	}
	/* calculate and set the below the line parameters */
	if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH1_COMMAND)) {
	    returnCode = TPM_AuthParams_Set(response,
					    *hmacKey,		/* HMAC key */
					    auth_session_data,
					    outParamDigest,
					    nonceOdd,
					    continueAuthSession);
	}
	/* audit if required */
	if ((returnCode == TPM_SUCCESS) && auditStatus) {
	    returnCode = TPM_ProcessAudit(tpm_state,
					  transportEncrypt,
					  inParamDigest,
					  outParamDigest,
					  ordinal);
	}
	/* adjust the initial response */
	rcf = TPM_Sbuffer_StoreFinalResponse(response, returnCode, tpm_state);
    }
    /* if there was an error, or continueAuthSession is FALSE, terminate the session */
    if (((rcf != 0) ||
	 ((returnCode != TPM_SUCCESS) && (returnCode != TPM_DEFEND_LOCK_RUNNING)) ||
	 !continueAuthSession) &&
	authHandleValid) {
	TPM_AuthSessions_TerminateHandle(tpm_state->tpm_stclear_data.authSessions, authHandle);
    }
    /*
      cleanup
    */
    TPM_SizedBuffer_Delete(&inData);		/* @1 */
    free(decrypt_data);				/* @2 */
    TPM_BoundData_Delete(&tpm_bound_data);	/* @3 */
    return rcf;
}

/* 10.4 TPM_CreateWrapKey rev 114

   The TPM_CreateWrapKey command both generates and creates a secure storage bundle for asymmetric
   keys.

   The newly created key can be locked to a specific PCR value by specifying a set of PCR registers.
*/

TPM_RESULT TPM_Process_CreateWrapKey(tpm_state_t *tpm_state,
				     TPM_STORE_BUFFER *response,
				     TPM_TAG tag,
				     uint32_t paramSize,
				     TPM_COMMAND_CODE ordinal,
				     unsigned char *command,
				     TPM_TRANSPORT_INTERNAL *transportInternal)
{
    TPM_RESULT	rcf = 0;			/* fatal error precluding response */
    TPM_RESULT	returnCode = TPM_SUCCESS;	/* command return code */

    /* input parameters */
    TPM_KEY_HANDLE	parentHandle;	/* Handle of a loaded key that can perform key wrapping. */
    TPM_ENCAUTH		dataUsageAuth;	/* Encrypted usage authorization data for the key. */
    TPM_ENCAUTH		dataMigrationAuth;	/* Encrypted migration authorization data for the
						   key.*/
    TPM_KEY		keyInfo;	/* Information about key to be created, pubkey.keyLength and
					   keyInfo.encData elements are 0. MAY be TPM_KEY12 */
    TPM_AUTHHANDLE	authHandle;	/* The authorization handle used for parent key
					   authorization. Must be an OSAP session.  */
    TPM_NONCE	nonceOdd;		/* Nonce generated by system associated with authHandle */
    TPM_BOOL	continueAuthSession = TRUE;	/* Ignored */
    TPM_AUTHDATA pubAuth;		/* The authorization digest that authorizes the use of the
					   public key in parentHandle. HMAC key:
					   parentKey.usageAuth. */

    /* processing parameters */
    unsigned char *		inParamStart;			/* starting point of inParam's */
    unsigned char *		inParamEnd;			/* ending point of inParam's */
    TPM_DIGEST			inParamDigest;
    TPM_BOOL			auditStatus;		/* audit the ordinal */
    TPM_BOOL			transportEncrypt;	/* wrapped in encrypted transport session */
    TPM_BOOL			authHandleValid = FALSE;
    TPM_AUTH_SESSION_DATA	*auth_session_data = NULL;	/* session data for authHandle */
    TPM_SECRET			*hmacKey;
    TPM_KEY			*parentKey = NULL;	/* the key specified by parentHandle */
    TPM_BOOL			parentPCRStatus;
    TPM_RSA_KEY_PARMS		*keyInfoRSAParms = NULL;	/* substructure of keyInfo */
    TPM_SECRET			du1UsageAuth;
    TPM_SECRET			dm1MigrationAuth;
    TPM_STORE_ASYMKEY		*wrappedStoreAsymkey;		/* substructure of wrappedKey */
    TPM_PCR_INFO		wrappedPCRInfo;
    int				ver;				/* TPM_KEY or TPM_KEY12 */

    /* output parameters */
    TPM_KEY		wrappedKey;	/* The TPM_KEY structure which includes the public and
					   encrypted private key. MAY be TPM_KEY12 */
    uint32_t		outParamStart;	/* starting point of outParam's */
    uint32_t		outParamEnd;	/* ending point of outParam's */
    TPM_DIGEST		outParamDigest;
    
    printf("TPM_Process_CreateWrapKey: Ordinal Entry\n");
    TPM_Key_Init(&keyInfo);
    TPM_Key_Init(&wrappedKey);
    TPM_PCRInfo_Init(&wrappedPCRInfo);
    /*
      get inputs
    */
    /* get parentHandle parameter */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_Load32(&parentHandle, &command, &paramSize);
    }
    /* save the starting point of inParam's for authorization and auditing */
    inParamStart = command;
    /* get dataUsageAuth parameter */
    if (returnCode == TPM_SUCCESS) {
	printf("TPM_Process_CreateWrapKey: parentHandle %08x\n", parentHandle);
	returnCode = TPM_Authdata_Load(dataUsageAuth, &command, &paramSize);
    }
    /* get dataMigrationAuth parameter */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_Authdata_Load(dataMigrationAuth, &command, &paramSize);
    }
    /* get keyInfo parameter */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_Key_Load(&keyInfo, &command, &paramSize);	/* freed @1 */
    }
    /* save the ending point of inParam's for authorization and auditing */
    inParamEnd = command;
    /* digest the input parameters */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_GetInParamDigest(inParamDigest,	/* output */
					  &auditStatus,		/* output */
					  &transportEncrypt,	/* output */
					  tpm_state,
					  tag,
					  ordinal,
					  inParamStart,
					  inParamEnd,
					  transportInternal);
    }
    /* check state */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_CheckState(tpm_state, tag, TPM_CHECK_ALL);
    }
    /* check tag */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_CheckRequestTag1(tag);
    }
    /* get the 'below the line' authorization parameters */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_AuthParams_Get(&authHandle,
					&authHandleValid,
					nonceOdd,
					&continueAuthSession,
					pubAuth,
					&command, &paramSize);
    }
    if (returnCode == TPM_SUCCESS) {
	if (paramSize != 0) {
	    printf("TPM_Process_CreateWrapKey: Error, command has %u extra bytes\n",
		   paramSize);
	    returnCode = TPM_BAD_PARAM_SIZE;
	}
    }
    /* do not terminate sessions if the command did not parse correctly */
    if (returnCode != TPM_SUCCESS) {
	authHandleValid = FALSE;
    }
    /*
      Processing
    */
    /* get the key corresponding to the parentHandle parameter */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_KeyHandleEntries_GetKey(&parentKey, &parentPCRStatus, tpm_state,
						 parentHandle,
						 FALSE, /* not r/o, using to encrypt w/public key */
						 FALSE,		/* do not ignore PCRs */
						 FALSE);	/* cannot use EK */
    }
    /* get the session data */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_AuthSessions_GetData(&auth_session_data,
					      &hmacKey,
					      tpm_state,
					      authHandle,
					      TPM_PID_OSAP,
					      TPM_ET_KEYHANDLE,
					      ordinal,
					      parentKey,
					      NULL,			/* OIAP */
					      parentKey->tpm_store_asymkey->pubDataDigest); /*OSAP*/
    }
    /* 1. Validate the authorization to use the key pointed to by parentHandle. Return TPM_AUTHFAIL
       on any error. */
    /* 2. Validate the session type for parentHandle is OSAP. */
    if (returnCode == TPM_SUCCESS) {
	TPM_PrintFour("TPM_Process_CreateWrapKey: sharedSecret", auth_session_data->sharedSecret);
	returnCode = TPM_Authdata_Check(tpm_state,
					*hmacKey,		/* HMAC key */
					inParamDigest,
					auth_session_data,	/* authorization session */
					nonceOdd,		/* Nonce generated by system
								   associated with authHandle
								*/
					continueAuthSession,
					pubAuth);	/* Authorization digest for input */
    }
    /* 3. If the TPM is not designed to create a key of the type requested in keyInfo, return the
       error code TPM_BAD_KEY_PROPERTY */
    if (returnCode == TPM_SUCCESS) {
	printf("TPM_Process_CreateWrapKey: Checking key properties\n");
	returnCode = TPM_Key_CheckProperties(&ver, &keyInfo, 0,
					     tpm_state->tpm_permanent_flags.FIPS);
    }	     
    /* Get the TPM_RSA_KEY_PARMS associated with keyInfo */
    if (returnCode == TPM_SUCCESS) {
	printf("TPM_Process_CreateWrapKey: key parameters v = %d\n", ver);
	returnCode = TPM_KeyParms_GetRSAKeyParms(&keyInfoRSAParms, &(keyInfo.algorithmParms));
    }	     
    /* 4. Verify that parentHandle->keyUsage equals TPM_KEY_STORAGE */
    if (returnCode == TPM_SUCCESS) {
	if (parentKey->keyUsage != TPM_KEY_STORAGE) {
	    printf("TPM_Process_CreateWrapKey: Error, parent keyUsage not TPM_KEY_STORAGE\n");
	    returnCode = TPM_INVALID_KEYUSAGE;
	}
    }	 
    /* 5. If parentHandle -> keyFlags -> migratable is TRUE and keyInfo -> keyFlags -> migratable is
       FALSE then return TPM_INVALID_KEYUSAGE */
    if (returnCode == TPM_SUCCESS) {
	if ((parentKey->keyFlags & TPM_MIGRATABLE) && !(keyInfo.keyFlags & TPM_MIGRATABLE)) {
	    printf("TPM_Process_CreateWrapKey: Error, parent not migratable\n");
	    returnCode = TPM_INVALID_KEYUSAGE;
	}
    }	 
    /* 6. Validate key parameters */
    /* a. keyInfo -> keyUsage MUST NOT be TPM_KEY_IDENTITY or TPM_KEY_AUTHCHANGE. If it is, return
       TPM_INVALID_KEYUSAGE */
    if (returnCode == TPM_SUCCESS) {
	if ((keyInfo.keyUsage == TPM_KEY_IDENTITY) ||
	    (keyInfo.keyUsage == TPM_KEY_AUTHCHANGE)) {
	    printf("TPM_Process_CreateWrapKey: Error, Invalid key usage %04x\n",
		   keyInfo.keyUsage);
	    returnCode = TPM_INVALID_KEYUSAGE;
	}
    }	 
    /* b. If keyInfo -> keyFlags -> migrateAuthority is TRUE then return TPM_INVALID_KEYUSAGE */
    if (returnCode == TPM_SUCCESS) {
	if (keyInfo.keyFlags & TPM_MIGRATEAUTHORITY) {
	    printf("TPM_Process_CreateWrapKey: Error, Invalid key flags %08x\n",
		   keyInfo.keyFlags);
	    returnCode = TPM_INVALID_KEYUSAGE;
	}
    }	 
    /* 7.  If TPM_PERMANENT_FLAGS -> FIPS is TRUE then
       a.  If keyInfo -> keySize is less than 1024 return TPM_NOTFIPS
       b.  If keyInfo -> authDataUsage specifies TPM_AUTH_NEVER return TPM_NOTFIPS
       c.  If keyInfo -> keyUsage specifies TPM_KEY_LEGACY return TPM_NOTFIPS
       NOTE Done in step 3 TPM_Key_CheckProperties()
    */
    /* 8. If keyInfo -> keyUsage equals TPM_KEY_STORAGE	 or TPM_KEY_MIGRATE
       i. algorithmID MUST be TPM_ALG_RSA
       ii. encScheme MUST be TPM_ES_RSAESOAEP_SHA1_MGF1
       iii. sigScheme MUST be TPM_SS_NONE
       iv. key size MUST be 2048
       v. exponentSize MUST be 0
       NOTE Done in step 3 TPM_Key_CheckProperties()
    */
    /* 9. Determine the version of key
       a.If keyInfo -> ver is 1.1
       i. Set V1 to 1
       ii. Map wrappedKey to a TPM_KEY structure
       iii. Validate all remaining TPM_KEY structures
       b. Else if keyInfo -> tag is TPM_TAG_KEY12
       i. Set V1 to 2
       ii. Map wrappedKey to a TPM_KEY12 structure
       iii. Validate all remaining TPM_KEY12 structures
       NOTE Check done by TPM_Key_CheckProperties()
       NOTE Map done by TPM_Key_GenerateRSA()
    */
    /* 10..Create DU1 by decrypting dataUsageAuth according to the ADIP indicated by authHandle */
    /* 11. Create DM1 by decrypting dataMigrationAuth according to the ADIP indicated by
	   authHandle */
    if (returnCode == TPM_SUCCESS) {
	TPM_AuthSessionData_Decrypt(du1UsageAuth, 
				    dm1MigrationAuth,
				    dataUsageAuth,	/* even encAuth */
				    auth_session_data,
				    nonceOdd,
				    dataMigrationAuth,	/* odd encAuth */
				    TRUE);
    }
    /* 12. Set continueAuthSession to FALSE */
    if (returnCode == TPM_SUCCESS) {
	continueAuthSession = FALSE;
    }
    /* 13. Generate asymmetric key according to algorithm information in keyInfo */
    /* 14. Fill in the wrappedKey structure with information from the newly generated key. */
    if (returnCode == TPM_SUCCESS) {
	printf("TPM_Process_CreateWrapKey: Generating key\n");
	returnCode = TPM_Key_GenerateRSA(&wrappedKey,
					 tpm_state,
					 parentKey,
					 tpm_state->tpm_stclear_data.PCRS,	/* PCR array */
					 ver,
					 keyInfo.keyUsage,
					 keyInfo.keyFlags,
					 keyInfo.authDataUsage,		/* TPM_AUTH_DATA_USAGE */
					 &(keyInfo.algorithmParms),	/* TPM_KEY_PARMS */
					 keyInfo.tpm_pcr_info,		/* TPM_PCR_INFO */
					 keyInfo.tpm_pcr_info_long);	/* TPM_PCR_INFO_LONG */
    }	 
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_Key_GetStoreAsymkey(&wrappedStoreAsymkey,
					     &wrappedKey);
    }	 
    if (returnCode == TPM_SUCCESS) {
	/* a. Set wrappedKey -> encData -> usageAuth to DU1 */
	TPM_Secret_Copy(wrappedStoreAsymkey->usageAuth, du1UsageAuth);
	/* b. If the KeyFlags -> migratable bit is set to 1, the wrappedKey -> encData ->
	   migrationAuth SHALL contain the decrypted value from dataMigrationAuth. */
	if (wrappedKey.keyFlags & TPM_MIGRATABLE) {
	    TPM_Secret_Copy(wrappedStoreAsymkey->migrationAuth, dm1MigrationAuth);
	}
	/* c. If the KeyFlags -> migratable bit is set to 0, the wrappedKey -> encData ->
	   migrationAuth SHALL be set to the value tpmProof */
	else {
	    TPM_Secret_Copy(wrappedStoreAsymkey->migrationAuth,
			    tpm_state->tpm_permanent_data.tpmProof);
	}
	printf("TPM_Process_CreateWrapKey: wrappedKey.PCRInfoSize %d\n", wrappedKey.pcrInfo.size);
    }	 
    /* 15. If keyInfo->PCRInfoSize is non-zero. */
    /* a. If V1 is 1 */
    /* i. Set wrappedKey -> pcrInfo to a TPM_PCR_INFO structure using the pcrSelection to
       indicate the PCR's in use */
    /* b. Else */
    /* i. Set wrappedKey -> pcrInfo to a TPM_PCR_INFO_LONG structure */
    /* c. Set wrappedKey -> pcrInfo to keyInfo -> pcrInfo */
    /* d. Set wrappedKey -> digestAtCreation to the TPM_COMPOSITE_HASH indicated by
       creationPCRSelection */
    /* e. If V1 is 2 set wrappedKey -> localityAtCreation to TPM_STANY_DATA -> locality */
    /* NOTE Done by TPM_Key_GenerateRSA() */
    /* 16. Encrypt the private portions of the wrappedKey structure using the key in parentHandle */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_Key_GenerateEncData(&wrappedKey, parentKey);
    }	 
    /*
      response
    */
    /* standard response: tag, (dummy) paramSize, returnCode.  Failure is fatal. */
    if (rcf == 0) {
	printf("TPM_Process_CreateWrapKey: Ordinal returnCode %08x %u\n",
	       returnCode, returnCode);
	rcf = TPM_Sbuffer_StoreInitialResponse(response, tag, returnCode);
    }
    /* success response, append the rest of the parameters.  */
    if (rcf == 0) {
	if (returnCode == TPM_SUCCESS) {
	    /* checkpoint the beginning of the outParam's */
	    outParamStart = response->buffer_current - response->buffer;
	    /* 17. Return the newly generated key in the wrappedKey parameter */
	    returnCode = TPM_Key_Store(response, &wrappedKey);
	    /* checkpoint the end of the outParam's */
	    outParamEnd = response->buffer_current - response->buffer;
	}
	/* digest the above the line output parameters */
	if (returnCode == TPM_SUCCESS) {
	    returnCode = TPM_GetOutParamDigest(outParamDigest,	/* output */
					       auditStatus,	/* input audit status */
					       transportEncrypt,
					       tag,			
					       returnCode,
					       ordinal,		/* command ordinal */
					       response->buffer + outParamStart,	/* start */
					       outParamEnd - outParamStart);	/* length */
	}
	/* calculate and set the below the line parameters */
	if (returnCode == TPM_SUCCESS) {
	    returnCode = TPM_AuthParams_Set(response,
					    *hmacKey,		/* HMAC key */
					    auth_session_data,
					    outParamDigest,
					    nonceOdd,
					    continueAuthSession);
	}
	/* audit if required */
	if ((returnCode == TPM_SUCCESS) && auditStatus) {
	    returnCode = TPM_ProcessAudit(tpm_state,
					  transportEncrypt,
					  inParamDigest,
					  outParamDigest,
					  ordinal);
	}
	/* adjust the initial response */
	rcf = TPM_Sbuffer_StoreFinalResponse(response, returnCode, tpm_state);
    }
    /* if there was an error, or continueAuthSession is FALSE, terminate the session */
    if (((rcf != 0) ||
	 ((returnCode != TPM_SUCCESS) && (returnCode != TPM_DEFEND_LOCK_RUNNING)) ||
	 !continueAuthSession) &&
	authHandleValid) {
	TPM_AuthSessions_TerminateHandle(tpm_state->tpm_stclear_data.authSessions, authHandle);
    }
    /* cleanup */
    TPM_Key_Delete(&keyInfo);			/* @1 */
    TPM_Key_Delete(&wrappedKey);		/* @2 */
    TPM_PCRInfo_Delete(&wrappedPCRInfo);	/* @3 */
    return rcf;
}

/* 27.8 TPM_LoadKey rev 114

   Version 1.2 deprecates LoadKey due to the HMAC of the new keyhandle on return. The wrapping makes
   use of the handle difficult in an environment where the TSS, or other management entity, is
   changing the TPM handle to a virtual handle.
   
   Software using loadKey on a 1.2 TPM can have a collision with the returned handle as the 1.2 TPM
   uses random values in the lower three bytes of the handle. All new software must use LoadKey2 to
   allow management software the ability to manage the key handle.
   
   Before the TPM can use a key to either wrap, unwrap, bind, unbind, seal, unseal, sign or perform
   any other action, it needs to be present in the TPM.	 The TPM_LoadKey function loads the key into
   the TPM for further use.
*/

TPM_RESULT TPM_Process_LoadKey(tpm_state_t *tpm_state,
			       TPM_STORE_BUFFER *response,
			       TPM_TAG tag,
			       uint32_t paramSize,
			       TPM_COMMAND_CODE ordinal,
			       unsigned char *command,
			       TPM_TRANSPORT_INTERNAL *transportInternal)
{
    TPM_RESULT	rcf = 0;			/* fatal error precluding response */
    TPM_RESULT	returnCode = TPM_SUCCESS;	/* command return code */

    /* input parameters */
    TPM_KEY_HANDLE	parentHandle;	/* TPM handle of parent key. */
    TPM_KEY		*inKey;		/* Incoming key structure, both encrypted private and clear
					   public portions.  MAY be TPM_KEY12 */
    TPM_AUTHHANDLE	authHandle;	/* The authorization handle used for parentHandle
					   authorization. */
    TPM_NONCE		nonceOdd;	/* Nonce generated by system associated with authHandle */
    TPM_BOOL	continueAuthSession = FALSE;	/* The continue use flag for the authorization
						   handle */
    TPM_AUTHDATA	parentAuth;	/* The authorization digest for inputs and
					   parentHandle. HMAC key: parentKey.usageAuth. */
    /* processing parameters */
    unsigned char *		inParamStart;			/* starting point of inParam's */
    unsigned char *		inParamEnd;			/* ending point of inParam's */
    TPM_DIGEST			inParamDigest;
    TPM_BOOL			auditStatus;		/* audit the ordinal */
    TPM_BOOL			transportEncrypt;	/* wrapped in encrypted transport session */
    TPM_BOOL			authHandleValid = FALSE;
    TPM_SECRET			*hmacKey;
    TPM_BOOL			key_added = FALSE;	/* key has been added to handle list */
    TPM_AUTH_SESSION_DATA	*auth_session_data = NULL;	/* session data for authHandle */
    
    /* output parameters */
    uint32_t		outParamStart;	/* starting point of outParam's */
    uint32_t		outParamEnd;	/* ending point of outParam's */
    TPM_DIGEST		outParamDigest;
    TPM_KEY_HANDLE	inKeyHandle;	/* Internal TPM handle where decrypted key was loaded. */

    printf("TPM_Process_LoadKey: Ordinal Entry\n");
    inKey = NULL;			/* freed @1 */
    /*
      get inputs
    */
    /*	get parentHandle parameter */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_Load32(&parentHandle, &command, &paramSize);
    }
    /* save the starting point of inParam's for authorization and auditing */
    inParamStart = command;
    /* Allocate space for inKey.  The key cannot be a local variable, since it persists in key
       storage after the command completes. */
    if (returnCode == TPM_SUCCESS) {
	printf("TPM_Process_LoadKey: parentHandle %08x\n", parentHandle);
	returnCode = TPM_Malloc((unsigned char **)&inKey, sizeof(TPM_KEY));	/* freed @1 */
    }
    /* get inKey parameter */
    if (returnCode == TPM_SUCCESS) {
	TPM_Key_Init(inKey);					/* freed @2 */
	returnCode = TPM_Key_Load(inKey, &command, &paramSize); /* freed @2 */
    }
    if (returnCode == TPM_SUCCESS) {
	TPM_PrintFour("TPM_Process_LoadKey: inKey n", inKey->pubKey.buffer);
    }
    /* save the ending point of inParam's for authorization and auditing */
    inParamEnd = command;
    /* digest the input parameters */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_GetInParamDigest(inParamDigest,	/* output */
					  &auditStatus,		/* output */
					  &transportEncrypt,	/* output */
					  tpm_state,
					  tag,
					  ordinal,
					  inParamStart,
					  inParamEnd,
					  transportInternal);
    }
    /* check state */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_CheckState(tpm_state, tag, TPM_CHECK_ALL);
    }
    /* check tag */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_CheckRequestTag10(tag);
    }
    /* get the optional 'below the line' authorization parameters */
    if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH1_COMMAND)) {
	returnCode = TPM_AuthParams_Get(&authHandle,
					&authHandleValid,
					nonceOdd,
					&continueAuthSession,
					parentAuth,
					&command, &paramSize);
    }
    if (returnCode == TPM_SUCCESS) {
	if (paramSize != 0) {
	    printf("TPM_Process_LoadKey: Error, command has %u extra bytes\n",
		   paramSize);
	    returnCode = TPM_BAD_PARAM_SIZE;
	}
    }
    /* do not terminate sessions if the command did not parse correctly */
    if (returnCode != TPM_SUCCESS) {
	authHandleValid = FALSE;
    }
    /*
      Processing
    */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_LoadKeyCommon(&inKeyHandle,	/* output */
				       &key_added,	/* output */
				       &hmacKey,	/* output */
				       &auth_session_data, /* output */
				       tpm_state,
				       tag,
				       ordinal,
				       parentHandle,
				       inKey,
				       inParamDigest,
				       authHandle,	/*uninitialized*/
				       nonceOdd,
				       continueAuthSession,
				       parentAuth);
    }
    /*
      response
    */
    /* standard response: tag, (dummy) paramSize, returnCode.  Failure is fatal. */
    if (rcf == 0) {
	printf("TPM_Process_LoadKey: Ordinal returnCode %08x %u\n",
	       returnCode, returnCode);
	rcf = TPM_Sbuffer_StoreInitialResponse(response, tag, returnCode);
    }
    /* success response, append the rest of the parameters.  */
    if (rcf == 0) {
	if (returnCode == TPM_SUCCESS) {
	    /* checkpoint the beginning of the outParam's */
	    outParamStart = response->buffer_current - response->buffer;
	    /* return the key handle */
	    returnCode = TPM_Sbuffer_Append32(response, inKeyHandle);
	    /* checkpoint the end of the outParam's */
	    outParamEnd = response->buffer_current - response->buffer;
	}
	/* digest the above the line output parameters */
	if (returnCode == TPM_SUCCESS) {
	    returnCode = TPM_GetOutParamDigest(outParamDigest,	/* output */
					       auditStatus,	/* input audit status */
					       transportEncrypt,
					       tag,			
					       returnCode,
					       ordinal,		/* command ordinal */
					       response->buffer + outParamStart,	/* start */
					       outParamEnd - outParamStart);	/* length */
	}
	/* calculate and set the below the line parameters */
	if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH1_COMMAND)) {
	    returnCode = TPM_AuthParams_Set(response,
					    *hmacKey,		/* HMAC key */
					    auth_session_data,
					    outParamDigest,
					    nonceOdd,
					    continueAuthSession);
	}
	/* audit if required */
	if ((returnCode == TPM_SUCCESS) && auditStatus) {
	    returnCode = TPM_ProcessAudit(tpm_state,
					  transportEncrypt,
					  inParamDigest,
					  outParamDigest,
					  ordinal);
	}
	/* adjust the initial response */
	rcf = TPM_Sbuffer_StoreFinalResponse(response, returnCode, tpm_state);
    }
    /* if there was an error, or continueAuthSession is FALSE, terminate the session */
    if (((rcf != 0) ||
	 ((returnCode != TPM_SUCCESS) && (returnCode != TPM_DEFEND_LOCK_RUNNING)) ||
	 !continueAuthSession) &&
	authHandleValid) {
	TPM_AuthSessions_TerminateHandle(tpm_state->tpm_stclear_data.authSessions, authHandle);
    }
    /*
      cleanup
    */
    /* if there was a failure, delete inKey */
    if ((rcf != 0) || (returnCode != TPM_SUCCESS)) {
	TPM_Key_Delete(inKey);	/* @2 */
	free(inKey);		/* @1 */
	if (key_added) {
	    /* if there was a failure and inKey was stored in the handle list, free the handle.
	       Ignore errors, since only one error code can be returned. */
	    TPM_KeyHandleEntries_DeleteHandle(tpm_state->tpm_key_handle_entries, inKeyHandle);
	}	
    }
    return rcf;
}
	    
/* 10.5 TPM_LoadKey2 rev 107

   Before the TPM can use a key to either wrap, unwrap, unbind, seal, unseal, sign or perform any
   other action, it needs to be present in the TPM.  The TPM_LoadKey2 function loads the key into
   the TPM for further use.

   The TPM assigns the key handle. The TPM always locates a loaded key by use of the handle. The
   assumption is that the handle may change due to key management operations. It is the
   responsibility of upper level software to maintain the mapping between handle and any label used
   by external software.

   This command has the responsibility of enforcing restrictions on the use of keys. For example,
   when attempting to load a STORAGE key it will be checked for the restrictions on a storage key
   (2048 size etc.).

   The load command must maintain a record of whether any previous key in the key hierarchy was
   bound to a PCR using parentPCRStatus.
   
   The flag parentPCRStatus enables the possibility of checking that a platform passed through some
   particular state or states before finishing in the current state. A grandparent key could be
   linked to state-1, a parent key could linked to state-2, and a child key could be linked to
   state-3, for example. The use of the child key then indicates that the platform passed through
   states 1 and 2 and is currently in state 3, in this example.	 TPM_Startup with stType ==
   TPM_ST_CLEAR indicates that the platform has been reset, so the platform has not passed through
   the previous states. Hence keys with parentPCRStatus==TRUE must be unloaded if TPM_Startup is
   issued with stType == TPM_ST_CLEAR.
   
   If a TPM_KEY structure has been decrypted AND the integrity test using "pubDataDigest" has passed
   AND the key is non-migratory, the key must have been created by the TPM. So there is every reason
   to believe that the key poses no security threat to the TPM. While there is no known attack from
   a rogue migratory key, there is a desire to verify that a loaded migratory key is a real key,
   arising from a general sense of unease about execution of arbitrary data as a key. Ideally a
   consistency check would consist of an encrypt/decrypt cycle, but this may be expensive. For RSA
   keys, it is therefore suggested that the consistency test consists of dividing the supposed RSA
   product by the supposed RSA prime, and checking that there is no remainder.
*/

TPM_RESULT TPM_Process_LoadKey2(tpm_state_t *tpm_state,
				TPM_STORE_BUFFER *response,
				TPM_TAG tag,
				uint32_t paramSize,
				TPM_COMMAND_CODE ordinal,
				unsigned char *command,
				TPM_TRANSPORT_INTERNAL *transportInternal)
{
    TPM_RESULT	rcf = 0;			/* fatal error precluding response */
    TPM_RESULT	returnCode = TPM_SUCCESS;	/* command return code */

    /* input parameters */
    TPM_KEY_HANDLE	parentHandle;	/* TPM handle of parent key. */
    TPM_KEY		*inKey;		/* Incoming key structure, both encrypted private and clear
					   public portions.  MAY be TPM_KEY12 */
    TPM_AUTHHANDLE	authHandle;	/* The authorization handle used for parentHandle
					   authorization. */
    TPM_NONCE		nonceOdd;	/* Nonce generated by system associated with authHandle */
    TPM_BOOL	continueAuthSession = FALSE;	/* The continue use flag for the authorization
						   handle */
    TPM_AUTHDATA	parentAuth;	/* The authorization digest for inputs and
					   parentHandle. HMAC key: parentKey.usageAuth. */
    /* processing parameters */
    unsigned char *		inParamStart;			/* starting point of inParam's */
    unsigned char *		inParamEnd;			/* ending point of inParam's */
    TPM_DIGEST			inParamDigest;
    TPM_BOOL			auditStatus;		/* audit the ordinal */
    TPM_BOOL			transportEncrypt;	/* wrapped in encrypted transport session */
    TPM_BOOL			authHandleValid = FALSE;
    TPM_SECRET			*hmacKey;
    TPM_BOOL			key_added = FALSE;	/* key has been added to handle list */
    TPM_AUTH_SESSION_DATA	*auth_session_data = NULL;	/* session data for authHandle */
    
    /* output parameters */
    uint32_t		outParamStart;	/* starting point of outParam's */
    uint32_t		outParamEnd;	/* ending point of outParam's */
    TPM_DIGEST		outParamDigest;
    TPM_KEY_HANDLE	inKeyHandle;	/* Internal TPM handle where decrypted key was loaded. */

    printf("TPM_Process_LoadKey2: Ordinal Entry\n");
    inKey = NULL;			/* freed @1 */
    /*
      get inputs
    */
    /*	get parentHandle parameter */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_Load32(&parentHandle, &command, &paramSize);
    }
    /* save the starting point of inParam's for authorization and auditing */
    inParamStart = command;
    /* Allocate space for inKey.  The key cannot be a local variable, since it persists in key
       storage after the command completes. */
    if (returnCode == TPM_SUCCESS) {
	printf("TPM_Process_LoadKey2: parentHandle %08x\n", parentHandle);
	returnCode = TPM_Malloc((unsigned char **)&inKey, sizeof(TPM_KEY));	/* freed @1 */
    }
    /* get inKey parameter */
    if (returnCode == TPM_SUCCESS) {
	TPM_Key_Init(inKey);					/* freed @2 */
	returnCode = TPM_Key_Load(inKey, &command, &paramSize); /* freed @2 */
    }
    if (returnCode == TPM_SUCCESS) {
	TPM_PrintFour("TPM_Process_LoadKey2: inKey n", inKey->pubKey.buffer);
    }
    /* save the ending point of inParam's for authorization and auditing */
    inParamEnd = command;
    /* digest the input parameters */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_GetInParamDigest(inParamDigest,	/* output */
					  &auditStatus,		/* output */
					  &transportEncrypt,	/* output */
					  tpm_state,
					  tag,
					  ordinal,
					  inParamStart,
					  inParamEnd,
					  transportInternal);
    }
    /* check state */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_CheckState(tpm_state, tag, TPM_CHECK_ALL);
    }
    /* check tag */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_CheckRequestTag10(tag);
    }
    /* get the optional 'below the line' authorization parameters */
    if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH1_COMMAND)) {
	returnCode = TPM_AuthParams_Get(&authHandle,
					&authHandleValid,
					nonceOdd,
					&continueAuthSession,
					parentAuth,
					&command, &paramSize);
    }
    if (returnCode == TPM_SUCCESS) {
	if (paramSize != 0) {
	    printf("TPM_Process_LoadKey2: Error, command has %u extra bytes\n",
		   paramSize);
	    returnCode = TPM_BAD_PARAM_SIZE;
	}
    }
    /* do not terminate sessions if the command did not parse correctly */
    if (returnCode != TPM_SUCCESS) {
	authHandleValid = FALSE;
    }
    /*
      Processing
    */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_LoadKeyCommon(&inKeyHandle,		/* output */
				       &key_added,		/* output */
				       &hmacKey,		/* output */
				       &auth_session_data,	/* output */
				       tpm_state,
				       tag,
				       ordinal,
				       parentHandle,
				       inKey,
				       inParamDigest,
				       authHandle,		/* uninitialized */
				       nonceOdd,
				       continueAuthSession,
				       parentAuth);
    }
    /*
      response
    */
    /* standard response: tag, (dummy) paramSize, returnCode.  Failure is fatal. */
    if (rcf == 0) {
	printf("TPM_Process_LoadKey2: Ordinal returnCode %08x %u\n",
	       returnCode, returnCode);
	rcf = TPM_Sbuffer_StoreInitialResponse(response, tag, returnCode);
    }
    /* success response, append the rest of the parameters.  */
    if (rcf == 0) {
	if (returnCode == TPM_SUCCESS) {
	    /* checkpoint the beginning of the outParam's */
	    outParamStart = response->buffer_current - response->buffer;
	    /* checkpoint the end of the outParam's */
	    outParamEnd = response->buffer_current - response->buffer;
	    /* In TPM_LoadKey2, the inKeyHandle is not part of the output HMAC */
	    /* return the key handle */
	    returnCode = TPM_Sbuffer_Append32(response, inKeyHandle);
	}
	/* digest the above the line output parameters */
	if (returnCode == TPM_SUCCESS) {
	    returnCode = TPM_GetOutParamDigest(outParamDigest,	/* output */
					       auditStatus,	/* input audit status */
					       transportEncrypt,
					       tag,			
					       returnCode,
					       ordinal,		/* command ordinal */
					       response->buffer + outParamStart,	/* start */
					       outParamEnd - outParamStart);	/* length */
	}
	/* calculate and set the below the line parameters */
	if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH1_COMMAND)) {
	    returnCode = TPM_AuthParams_Set(response,
					    *hmacKey,		/* HMAC key */
					    auth_session_data,
					    outParamDigest,
					    nonceOdd,
					    continueAuthSession);
	}
	/* audit if required */
	if ((returnCode == TPM_SUCCESS) && auditStatus) {
	    returnCode = TPM_ProcessAudit(tpm_state,
					  transportEncrypt,
					  inParamDigest,
					  outParamDigest,
					  ordinal);
	}
	/* adjust the initial response */
	rcf = TPM_Sbuffer_StoreFinalResponse(response, returnCode, tpm_state);
    }
    /* if there was an error, or continueAuthSession is FALSE, terminate the session */
    if (((rcf != 0) ||
	 ((returnCode != TPM_SUCCESS) && (returnCode != TPM_DEFEND_LOCK_RUNNING)) ||
	 !continueAuthSession) &&
	authHandleValid) {
	TPM_AuthSessions_TerminateHandle(tpm_state->tpm_stclear_data.authSessions, authHandle);
    }
    /*
      cleanup
    */
    /* if there was a failure, delete inKey */
    if ((rcf != 0) || (returnCode != TPM_SUCCESS)) {
	TPM_Key_Delete(inKey);	/* @2 */
	free(inKey);		/* @1 */
	if (key_added) {
	    /* if there was a failure and inKey was stored in the handle list, free the handle.
	       Ignore errors, since only one error code can be returned. */
	    TPM_KeyHandleEntries_DeleteHandle(tpm_state->tpm_key_handle_entries, inKeyHandle);
	}	
    }
    return rcf;
}

/* TPM_LoadKeyCommon rev 114

   Code common to TPM_LoadKey and TPM_LoadKey2.	 They differ only in whether the key handle is
   included in the response HMAC calculation.
*/

static TPM_RESULT TPM_LoadKeyCommon(TPM_KEY_HANDLE	*inKeyHandle,	/* output */
				    TPM_BOOL		*key_added,	/* output */
				    TPM_SECRET		**hmacKey,	/* output */
				    TPM_AUTH_SESSION_DATA	**auth_session_data, /* output */
				    tpm_state_t		*tpm_state,
				    TPM_TAG		tag,
				    TPM_COMMAND_CODE	ordinal,
				    TPM_KEY_HANDLE	parentHandle,
				    TPM_KEY		*inKey,
				    TPM_DIGEST		inParamDigest,
				    TPM_AUTHHANDLE	authHandle,
				    TPM_NONCE		nonceOdd,
				    TPM_BOOL		continueAuthSession,
				    TPM_AUTHDATA	parentAuth)
{
    TPM_RESULT			rc = 0;
    TPM_KEY			*parentKey;		/* the key specified by parentHandle */
    TPM_SECRET			*parentUsageAuth;
    TPM_BOOL			parentPCRStatus;
    TPM_BOOL			parentPCRUsage;
    int				ver;

    printf("TPM_LoadKeyCommon:\n");
    *key_added = FALSE; /* key has been added to handle list */
    /* Verify that parentHandle points to a valid key.	Get the TPM_KEY associated with parentHandle
     */
    if (rc == TPM_SUCCESS) {
	rc = TPM_KeyHandleEntries_GetKey(&parentKey, &parentPCRStatus,
					 tpm_state, parentHandle,
					 FALSE,		/* not r/o, using to decrypt */
					 FALSE,		/* do not ignore PCRs */
					 FALSE);	/* cannot use EK */
    }
    /* check TPM_AUTH_DATA_USAGE authDataUsage */
    if ((rc == TPM_SUCCESS) && (tag == TPM_TAG_RQU_COMMAND)) {
	if (parentKey->authDataUsage != TPM_AUTH_NEVER) {
	    printf("TPM_LoadKeyCommon: Error, authorization required\n");
	    rc = TPM_AUTHFAIL;
	}
    }
    /* get parentHandle -> usageAuth */
    if ((rc == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH1_COMMAND)) {
	rc = TPM_Key_GetUsageAuth(&parentUsageAuth, parentKey);
    }	 
    /* get the session data */
    if ((rc == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH1_COMMAND)) {
	rc = TPM_AuthSessions_GetData(auth_session_data,
				      hmacKey,
				      tpm_state,
				      authHandle,
				      TPM_PID_NONE,
				      TPM_ET_KEYHANDLE,
				      ordinal,
				      parentKey,
				      parentUsageAuth,			/* OIAP */
				      parentKey->tpm_store_asymkey->pubDataDigest);	/* OSAP */
    }
    /* 1. Validate the command and the parameters using parentAuth and parentHandle -> usageAuth */
    if ((rc == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH1_COMMAND)) {
	rc = TPM_Authdata_Check(tpm_state,
				**hmacKey,		/* HMAC key */
				inParamDigest,
				*auth_session_data,	/* authorization session */
				nonceOdd,		/* Nonce generated by system
							   associated with authHandle */
				continueAuthSession,
				parentAuth);		/* Authorization digest for input */
    }
    /* 2. If parentHandle -> keyUsage is NOT TPM_KEY_STORAGE return TPM_INVALID_KEYUSAGE */
    if (rc == TPM_SUCCESS) {
	if (parentKey->keyUsage != TPM_KEY_STORAGE) {
	    printf("TPM_LoadKeyCommon: Error, "
		   "parentHandle -> keyUsage should be TPM_KEY_STORAGE, is %04x\n",
		   parentKey->keyUsage);
	    rc = TPM_INVALID_KEYUSAGE;
	}
    }
    /* 3. If the TPM is not designed to operate on a key of the type specified by inKey, return the
       error code TPM_BAD_KEY_PROPERTY.	 */
    if (rc == TPM_SUCCESS) {
	rc = TPM_Key_CheckProperties(&ver, inKey, 0, tpm_state->tpm_permanent_flags.FIPS);
	printf("TPM_LoadKeyCommon: key parameters v = %d\n", ver);
    }
    /* 4. The TPM MUST handle both TPM_KEY and TPM_KEY12 structures.
       This step is done at TPM_Key_Load()
    */
    /* 5. Decrypt the inKey -> privkey to obtain TPM_STORE_ASYMKEY structure using the key in
       parentHandle.
    */
    if (rc == TPM_SUCCESS) {
	rc = TPM_Key_DecryptEncData(inKey, parentKey);
    }
    /* 6. Validate the integrity of inKey and decrypted TPM_STORE_ASYMKEY
       a. Reproduce inKey -> TPM_STORE_ASYMKEY -> pubDataDigest using the fields of inKey, and check
       that the reproduced value is the same as pubDataDigest
    */
    if (rc == TPM_SUCCESS) {
	rc = TPM_Key_CheckPubDataDigest(inKey);
    }
    /* 7. Validate the consistency of the key and it's key usage. */
    /* a. If inKey -> keyFlags -> migratable is TRUE, the TPM SHALL verify consistency of the public
       and private components of the asymmetric key pair. If inKey -> keyFlags -> migratable is
       FALSE, the TPM MAY verify consistency of the public and private components of the asymmetric
       key pair. The consistency of an RSA key pair MAY be verified by dividing the supposed (P*Q)
       product by a supposed prime and checking that there is no remainder.

       This step is done at TPM_Key_Load()
    */
    /* b.  If inKey -> keyUsage is TPM_KEY_IDENTITY, verify that inKey->keyFlags->migratable is
       FALSE. If it is not, return TPM_INVALID_KEYUSAGE
    */
    if (rc == TPM_SUCCESS) {
	if ((inKey->keyUsage == TPM_KEY_IDENTITY) &&
	    (inKey->keyFlags & TPM_MIGRATABLE)) {
	    printf("TPM_LoadKeyCommon: Error, identity key is migratable\n");
	    rc = TPM_INVALID_KEYUSAGE;
	}
    }
    /* c.  If inKey -> keyUsage is TPM_KEY_AUTHCHANGE, return TPM_INVALID_KEYUSAGE */
    if (rc == TPM_SUCCESS) {
	if (inKey->keyUsage == TPM_KEY_AUTHCHANGE) {
	    printf("TPM_LoadKeyCommon: Error, keyUsage is TPM_KEY_AUTHCHANGE\n");
	    rc = TPM_INVALID_KEYUSAGE;
	}
    }
    /* d.  If inKey -> keyFlags -> migratable equals 0 then verify that TPM_STORE_ASYMKEY ->
       migrationAuth equals TPM_PERMANENT_DATA -> tpmProof */
    if (rc == TPM_SUCCESS) {
	if (!(inKey->keyFlags & TPM_MIGRATABLE)) {
	    rc = TPM_Secret_Compare(tpm_state->tpm_permanent_data.tpmProof,
				   inKey->tpm_store_asymkey->migrationAuth);
	    if (rc != 0) {
		printf("TPM_LoadKeyCommon: Error, tpmProof mismatch\n");
		rc = TPM_INVALID_KEYUSAGE;
	    }
	}
    }
    /*	 e.   Validate the mix of encryption and signature schemes
	 f.   If TPM_PERMANENT_FLAGS -> FIPS is TRUE then
	 i.   If keyInfo -> keySize is less than 1024 return TPM_NOTFIPS
	 ii.  If keyInfo -> authDataUsage specifies TPM_AUTH_NEVER return
	 TPM_NOTFIPS
	 iii.  If  keyInfo  ->	keyUsage specifies TPM_KEY_LEGACY  return
	 TPM_NOTFIPS
	 g.   If inKey -> keyUsage is TPM_KEY_STORAGE or TPM_KEY_MIGRATE
	 i.   algorithmID MUST be TPM_ALG_RSA
	 ii.  Key size MUST be 2048
	 iii. exponentSize MUST be 0
	 iv. sigScheme MUST be TPM_SS_NONE
	 h.   If inKey -> keyUsage is TPM_KEY_IDENTITY
	 i.   algorithmID MUST be TPM_ALG_RSA
	 ii.  Key size MUST be 2048
	 iv. exponentSize MUST be 0
	 iii. encScheme MUST be TPM_ES_NONE
	 NOTE Done in step 3.  
    */
    if (rc == TPM_SUCCESS) {
	/* i. If the decrypted inKey -> pcrInfo is NULL, */
	/* i. The TPM MUST set the internal indicator to indicate that the key is not using any PCR
	   registers. */
	/* j.  Else */
	/* i. The TPM MUST store pcrInfo in a manner that allows the TPM to calculate a composite
	   hash whenever the key will be in use */
	/* ii. The TPM MUST handle both version 1.1 TPM_PCR_INFO and 1.2 TPM_PCR_INFO_LONG
	   structures according to the type of TPM_KEY structure */
	/* (1) The TPM MUST validate the TPM_PCR_INFO or TPM_PCR_INFO_LONG structures for legal
	       values.	However, the digestAtRelease and localityAtRelease are not validated for
	       authorization until use time.*/
	/* NOTE TPM_Key_Load() loads the TPM_PCR_INFO or TPM_PCR_INFO_LONG cache */
    }
    /* 8.  Perform any processing necessary to make TPM_STORE_ASYMKEY key available for
       operations. */
    /* NOTE Done at TPM_Key_Load() */
    /* 9. Load key and key information into internal memory of the TPM. If insufficient memory
       exists return error TPM_NOSPACE. */
    /* 10. Assign inKeyHandle according to internal TPM rules. */
    /* 11. Set InKeyHandle -> parentPCRStatus to parentHandle -> parentPCRStatus. */
    if (rc == TPM_SUCCESS) {
	*inKeyHandle = 0;	/* no preferred value */
	rc = TPM_KeyHandleEntries_AddKeyEntry(inKeyHandle,			/* output */
					      tpm_state->tpm_key_handle_entries, /* input */
					      inKey,				/* input */
					      parentPCRStatus,
					      0);			/* keyControl */
    }
    if (rc == TPM_SUCCESS) {
	printf(" TPM_LoadKeyCommon: Loaded key handle %08x\n", *inKeyHandle);
	/* remember that the handle has been added to handle list, so it can be deleted on error */
	*key_added = TRUE;
	
    }
    /* 12. If parentHandle indicates it is using PCR registers then set inKeyHandle ->
       parentPCRStatus to TRUE. */
    if (rc == TPM_SUCCESS) {
	rc = TPM_Key_GetPCRUsage(&parentPCRUsage, parentKey, 0);
    }
    if (rc == TPM_SUCCESS) {
	if (parentPCRUsage) {
	    rc = TPM_KeyHandleEntries_SetParentPCRStatus(tpm_state->tpm_key_handle_entries,
							 *inKeyHandle, TRUE);
	}
    }	
    return rc;
}

/* 10.6 TPM_GetPubKey rev 102

   The owner of a key may wish to obtain the public key value from a loaded key. This information
   may have privacy concerns so the command must have authorization from the key owner.
*/

TPM_RESULT TPM_Process_GetPubKey(tpm_state_t *tpm_state,
				 TPM_STORE_BUFFER *response,
				 TPM_TAG tag,
				 uint32_t paramSize,
				 TPM_COMMAND_CODE ordinal,
				 unsigned char *command,
				 TPM_TRANSPORT_INTERNAL *transportInternal)
{
    TPM_RESULT	rcf = 0;			/* fatal error precluding response */
    TPM_RESULT	returnCode = TPM_SUCCESS;	/* command return code */

    /* input parameters */
    TPM_KEY_HANDLE	keyHandle;	/* TPM handle of key. */
    TPM_AUTHHANDLE	authHandle;	/* The authorization handle used for keyHandle
					   authorization. */
    TPM_NONCE		nonceOdd;	/* Nonce generated by system associated with authHandle */
    TPM_BOOL	continueAuthSession = TRUE;	/*The continue use flag for the authorization
						  handle */
    TPM_AUTHDATA	keyAuth;	/* Authorization HMAC key: key.usageAuth. */
    
    /* processing parameters */
    unsigned char *		inParamStart;		/* starting point of inParam's */
    unsigned char *		inParamEnd;		/* ending point of inParam's */
    TPM_DIGEST			inParamDigest;
    TPM_BOOL			auditStatus;		/* audit the ordinal */
    TPM_BOOL			transportEncrypt;	/* wrapped in encrypted transport session */
    TPM_BOOL			authHandleValid = FALSE;
    TPM_SECRET			*hmacKey;
    TPM_KEY			*key = NULL;		/* the key specified by keyHandle */
    TPM_BOOL			parentPCRStatus;
    TPM_AUTH_SESSION_DATA	*auth_session_data = NULL;	/* session data for authHandle */
    TPM_SECRET			*keyUsageAuth;
    TPM_STORE_BUFFER		pubkeyStream;

    /* output parameters */
    uint32_t			outParamStart;		/* starting point of outParam's */
    uint32_t			outParamEnd;		/* ending point of outParam's */
    TPM_DIGEST			outParamDigest;
    const unsigned char		*pubkeyStreamBuffer;	/* output */
    uint32_t			pubkeyStreamLength;

    printf("TPM_Process_GetPubKey: Ordinal Entry\n");
    TPM_Sbuffer_Init(&pubkeyStream);		/* freed @1 */
    /*
      get inputs
    */
    /* get keyHandle parameter */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_Load32(&keyHandle, &command, &paramSize);
    }
    /* save the starting point of inParam's for authorization and auditing */
    inParamStart = command;
    if (returnCode == TPM_SUCCESS) {
	printf("TPM_Process_GetPubKey: keyHandle %08x\n", keyHandle);
    }
    /* save the ending point of inParam's for authorization and auditing */
    inParamEnd = command;
    /* digest the input parameters */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_GetInParamDigest(inParamDigest,	/* output */
					  &auditStatus,		/* output */
					  &transportEncrypt,	/* output */
					  tpm_state,
					  tag,
					  ordinal,
					  inParamStart,
					  inParamEnd,
					  transportInternal);
    }
    /* check state */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_CheckState(tpm_state, tag, TPM_CHECK_ALL);
    }
    /* check tag */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_CheckRequestTag10(tag);
    }
    /* get the optional 'below the line' authorization parameters */
    if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH1_COMMAND)) {
	returnCode = TPM_AuthParams_Get(&authHandle,
					&authHandleValid,
					nonceOdd,
					&continueAuthSession,
					keyAuth,
					&command, &paramSize);
    }
    if (returnCode == TPM_SUCCESS) {
	if (paramSize != 0) {
	    printf("TPM_Process_GetPubKey: Error, command has %u extra bytes\n",
		   paramSize);
	    returnCode = TPM_BAD_PARAM_SIZE;
	}
    }
    /* do not terminate sessions if the command did not parse correctly */
    if (returnCode != TPM_SUCCESS) {
	authHandleValid = FALSE;
    }
    /*
      Processing
    */
    /* get the key corresponding to the keyHandle parameter */
    if (returnCode == TPM_SUCCESS) {
	printf("TPM_Process_GetPubKey: Key handle %08x\n", keyHandle);
	returnCode = TPM_KeyHandleEntries_GetKey(&key, &parentPCRStatus, tpm_state, keyHandle,
						 TRUE,		/* read-only */
						 FALSE,		/* do not ignore PCRs */
						 FALSE);	/* cannot use EK */
    }
    /* 1. If tag = TPM_TAG_RQU_AUTH1_COMMAND then */
    /* get keyHandle -> usageAuth */
    if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH1_COMMAND)) {
	returnCode = TPM_Key_GetUsageAuth(&keyUsageAuth, key);
    }	 
    /* get the session data */
    if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH1_COMMAND)) {
	returnCode = TPM_AuthSessions_GetData(&auth_session_data,
					      &hmacKey,
					      tpm_state,
					      authHandle,
					      TPM_PID_NONE,
					      TPM_ET_KEYHANDLE,
					      ordinal,
					      key,
					      keyUsageAuth,		/* OIAP */
					      key->tpm_store_asymkey->pubDataDigest);	/* OSAP */
    }


    /* a. Validate the command parameters using keyHandle -> usageAuth, on error return
       TPM_AUTHFAIL */
    if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH1_COMMAND)) {
	returnCode = TPM_Authdata_Check(tpm_state,
					*hmacKey,		/* HMAC key */
					inParamDigest,
					auth_session_data,	/* authorization session */
					nonceOdd,		/* Nonce generated by system
								   associated with authHandle */
					continueAuthSession,
					keyAuth);		/* Authorization digest for input */
    }
    /* 2. Else	*/
    if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_COMMAND)){
	/* a. Verify that keyHandle -> authDataUsage is TPM_NO_READ_PUBKEY_AUTH or TPM_AUTH_NEVER,
	   on error return TPM_AUTHFAIL */
#ifdef TPM_V12
	if ((key->authDataUsage != TPM_NO_READ_PUBKEY_AUTH) &&
	    (key->authDataUsage != TPM_AUTH_NEVER)) {
	    printf("TPM_Process_GetPubKey: Error, authorization required\n");
	    returnCode = TPM_AUTHFAIL;
	}
#else	/* TPM 1.1 does not have TPM_NO_READ_PUBKEY_AUTH */
	if (key->authDataUsage != TPM_AUTH_NEVER) {
	    printf("TPM_Process_GetPubKey: Error, authorization required\n");
	    returnCode = TPM_AUTHFAIL;
	}
#endif
    }
#ifdef TPM_V12	/* TPM 1.1 does not have readSRKPub  */
    if (returnCode == TPM_SUCCESS) {
	/* 3. If keyHandle == TPM_KH_SRK then  */
	if ((keyHandle == TPM_KH_SRK) &&
	    /* a. If TPM_PERMANENT_FLAGS -> readSRKPub is FALSE then return TPM_INVALID_KEYHANDLE */
	    !tpm_state->tpm_permanent_flags.readSRKPub) {
	    printf("TPM_Process_GetPubKey: "
		   "Error, keyHandle is TPM_KH_SRK and readSRKPub is FALSE\n");
	    returnCode = TPM_INVALID_KEYHANDLE;
	}
    }
#endif
    /* 4. If keyHandle -> pcrInfoSize is not 0 */
    /* a. If keyHandle -> keyFlags has pcrIgnoredOnRead set to FALSE */
    /* i. Create a digestAtRelease according to the specified PCR registers and compare
       to keyHandle -> digestAtRelease and if a mismatch return TPM_WRONGPCRVAL */
    /* ii. If specified validate any locality requests */
    /* NOTE: Done at TPM_KeyHandleEntries_GetKey() */
    /* 5. Create a TPM_PUBKEY structure and return */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_Key_StorePubkey(&pubkeyStream,		/* output */
					 &pubkeyStreamBuffer,	/* output */
					 &pubkeyStreamLength,	/* output */
					 key);			/* input */
    }
    /*
      response
    */
    /* standard response: tag, (dummy) paramSize, returnCode.  Failure is fatal. */
    if (rcf == 0) {
	printf("TPM_Process_GetPubKey: Ordinal returnCode %08x %u\n",
	       returnCode, returnCode);
	rcf = TPM_Sbuffer_StoreInitialResponse(response, tag, returnCode);
    }
    /* success response, append the rest of the parameters.  */
    if (rcf == 0) {
	if (returnCode == TPM_SUCCESS) {
	    /* checkpoint the beginning of the outParam's */
	    outParamStart = response->buffer_current - response->buffer;
	    /* TPM_PUBKEY structure  */
	    returnCode = TPM_Sbuffer_Append(response, pubkeyStreamBuffer, pubkeyStreamLength);
	    /* checkpoint the end of the outParam's */
	    outParamEnd = response->buffer_current - response->buffer;
	}
	/* digest the above the line output parameters */
	if (returnCode == TPM_SUCCESS) {
	    returnCode = TPM_GetOutParamDigest(outParamDigest,	/* output */
					       auditStatus,	/* input audit status */
					       transportEncrypt,
					       tag,			
					       returnCode,
					       ordinal,		/* command ordinal */
					       response->buffer + outParamStart,	/* start */
					       outParamEnd - outParamStart);	/* length */
	}
	/* calculate and set the below the line parameters */
	if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH1_COMMAND)) {
	    returnCode = TPM_AuthParams_Set(response,
					    *hmacKey,		/* owner HMAC key */
					    auth_session_data,
					    outParamDigest,
					    nonceOdd,
					    continueAuthSession);
	}
	/* audit if required */
	if ((returnCode == TPM_SUCCESS) && auditStatus) {
	    returnCode = TPM_ProcessAudit(tpm_state,
					  transportEncrypt,
					  inParamDigest,
					  outParamDigest,
					  ordinal);
	}
	/* adjust the initial response */
	rcf = TPM_Sbuffer_StoreFinalResponse(response, returnCode, tpm_state);
    }
    /* if there was an error, or continueAuthSession is FALSE, terminate the session */
    if (((rcf != 0) ||
	 ((returnCode != TPM_SUCCESS) && (returnCode != TPM_DEFEND_LOCK_RUNNING)) ||
	 !continueAuthSession) &&
	authHandleValid) {
	TPM_AuthSessions_TerminateHandle(tpm_state->tpm_stclear_data.authSessions, authHandle);
    }
    /*
      cleanup
    */
    TPM_Sbuffer_Delete(&pubkeyStream);		/* @1 */
    return rcf;
}
