/********************************************************************************/
/*										*/
/*			TPM Identity Handling					*/
/*			     Written by Ken Goldman				*/
/*		       IBM Thomas J. Watson Research Center			*/
/*	      $Id: tpm_identity.c 4526 2011-03-24 21:14:42Z 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 "tpm_auth.h"
#include "tpm_crypto.h"
#include "tpm_cryptoh.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_pcr.h"
#include "tpm_process.h"
#include "tpm_secret.h"
#include "tpm_storage.h"
#include "tpm_ver.h"
#include "tpm_identity.h"

/*
  TPM_EK_BLOB
*/

/* TPM_EKBlob_Init()

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

void TPM_EKBlob_Init(TPM_EK_BLOB *tpm_ek_blob)
{
    printf(" TPM_EKBlob_Init:\n");
    tpm_ek_blob->ekType = 0;
    TPM_SizedBuffer_Init(&(tpm_ek_blob->blob));
    return;
}

/* TPM_EKBlob_Load()

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

TPM_RESULT TPM_EKBlob_Load(TPM_EK_BLOB *tpm_ek_blob,
			   unsigned char **stream,
			   uint32_t *stream_size)
{
    TPM_RESULT		rc = 0;

    printf(" TPM_EKBlob_Load:\n");
    /* check the tag */
    if (rc == 0) {
	rc = TPM_CheckTag(TPM_TAG_EK_BLOB, stream, stream_size);
    }
    /* load ekType */
    if (rc == 0) {
	rc = TPM_Load16(&(tpm_ek_blob->ekType), stream, stream_size);
    }
    /* load blob */
    if (rc == 0) {
	rc = TPM_SizedBuffer_Load(&(tpm_ek_blob->blob), stream, stream_size);
    }
    return rc;
}

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

TPM_RESULT TPM_EKBlob_Store(TPM_STORE_BUFFER *sbuffer,
			    const TPM_EK_BLOB *tpm_ek_blob)
{
    TPM_RESULT		rc = 0;

    printf(" TPM_EKBlob_Store:\n");
    if (rc == 0) {
	rc = TPM_Sbuffer_Append16(sbuffer, TPM_TAG_EK_BLOB); 
    }
    if (rc == 0) {
	rc = TPM_Sbuffer_Append16(sbuffer, tpm_ek_blob->ekType); 
    }
    if (rc == 0) {
	rc = TPM_SizedBuffer_Store(sbuffer, &(tpm_ek_blob->blob)); 
    }
    return rc;
}

/* TPM_EKBlob_Delete()

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

void TPM_EKBlob_Delete(TPM_EK_BLOB *tpm_ek_blob)
{
    printf(" TPM_EKBlob_Delete:\n");
    if (tpm_ek_blob != NULL) {
	TPM_SizedBuffer_Delete(&(tpm_ek_blob->blob));
	TPM_EKBlob_Init(tpm_ek_blob);
    }
    return;
}

/*
  TPM_EK_BLOB_ACTIVATE
*/

/* TPM_EKBlobActivate_Init()

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

void TPM_EKBlobActivate_Init(TPM_EK_BLOB_ACTIVATE *tpm_ek_blob_activate)
{
    printf(" TPM_EKBlobActivate_Init:\n");
    TPM_SymmetricKey_Init(&(tpm_ek_blob_activate->sessionKey));
    TPM_Digest_Init(tpm_ek_blob_activate->idDigest);
    TPM_PCRInfoShort_Init(&(tpm_ek_blob_activate->pcrInfo));
    return;
}

/* TPM_EKBlobActivate_Load()

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

TPM_RESULT TPM_EKBlobActivate_Load(TPM_EK_BLOB_ACTIVATE *tpm_ek_blob_activate,
				   unsigned char **stream,
				   uint32_t *stream_size)
{
    TPM_RESULT		rc = 0;

    printf(" TPM_EKBlobActivate_Load:\n");
    /* check tag */
    if (rc == 0) {
	rc = TPM_CheckTag(TPM_TAG_EK_BLOB_ACTIVATE, stream, stream_size);
    }
    /* load sessionKey */
    if (rc == 0) {
	rc = TPM_SymmetricKey_Load(&(tpm_ek_blob_activate->sessionKey), stream, stream_size);
    }
    /* load idDigest */
    if (rc == 0) {
	rc = TPM_Digest_Load(tpm_ek_blob_activate->idDigest, stream, stream_size);
    }
    /* load pcrInfo */
    if (rc == 0) {
	rc = TPM_PCRInfoShort_Load(&(tpm_ek_blob_activate->pcrInfo), stream, stream_size, FALSE);
    }
    return rc;
}

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

TPM_RESULT TPM_EKBlobActivate_Store(TPM_STORE_BUFFER *sbuffer,
				    const TPM_EK_BLOB_ACTIVATE *tpm_ek_blob_activate)
{
    TPM_RESULT		rc = 0;

    printf(" TPM_EKBlobActivate_Store:\n");
    /* store tag */
    if (rc == 0) {
	rc = TPM_Sbuffer_Append16(sbuffer, TPM_TAG_EK_BLOB_ACTIVATE); 
    }
    /* store sessionKey */
    if (rc == 0) {
	rc = TPM_SymmetricKey_Store(sbuffer, &(tpm_ek_blob_activate->sessionKey));
    }
    /* store idDigest */
    if (rc == 0) {
	rc = TPM_Digest_Store(sbuffer, tpm_ek_blob_activate->idDigest);
    }
    /* store pcrInfo */
    if (rc == 0) {
	rc = TPM_PCRInfoShort_Store(sbuffer, &(tpm_ek_blob_activate->pcrInfo), FALSE);
    }
    return rc;
}

/* TPM_EKBlobActivate_Delete()

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

void TPM_EKBlobActivate_Delete(TPM_EK_BLOB_ACTIVATE *tpm_ek_blob_activate)
{
    printf(" TPM_EKBlobActivate_Delete:\n");
    if (tpm_ek_blob_activate != NULL) {
	TPM_SymmetricKey_Delete(&(tpm_ek_blob_activate->sessionKey));
	TPM_PCRInfoShort_Delete(&(tpm_ek_blob_activate->pcrInfo));
	TPM_EKBlobActivate_Init(tpm_ek_blob_activate);
    }
    return;
}

/*
  TPM_EK_BLOB_AUTH
*/

/* TPM_EKBlobAuth_Init()

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

void TPM_EKBlobAuth_Init(TPM_EK_BLOB_AUTH *tpm_ek_blob_auth)
{
    printf(" TPM_EKBlobAuth_Init:\n");
    TPM_Secret_Init(tpm_ek_blob_auth->authValue);
    return;
}

/* TPM_EKBlobAuth_Load()

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

TPM_RESULT TPM_EKBlobAuth_Load(TPM_EK_BLOB_AUTH *tpm_ek_blob_auth,
			       unsigned char **stream,
			       uint32_t *stream_size)
{
    TPM_RESULT		rc = 0;

    printf(" TPM_EKBlobAuth_Load:\n");
    /* check tag */
    if (rc == 0) {
	rc = TPM_CheckTag(TPM_TAG_EK_BLOB_AUTH, stream, stream_size);
    }
    /* load authValue */
    if (rc == 0) {
	rc = TPM_Secret_Load(tpm_ek_blob_auth->authValue, stream, stream_size);
    }
    return rc;
}

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

TPM_RESULT TPM_EKBlobAuth_Store(TPM_STORE_BUFFER *sbuffer,
				const TPM_EK_BLOB_AUTH *tpm_ek_blob_auth)
{
    TPM_RESULT		rc = 0;

    printf(" TPM_EKBlobAuth_Store:\n");
    if (rc == 0) {
	rc = TPM_Sbuffer_Append16(sbuffer, TPM_TAG_EK_BLOB_AUTH); 
    }
    if (rc == 0) {
	rc = TPM_Secret_Store(sbuffer, tpm_ek_blob_auth->authValue);
    }
    return rc;
}

/* TPM_EKBlobAuth_Delete()

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

void TPM_EKBlobAuth_Delete(TPM_EK_BLOB_AUTH *tpm_ek_blob_auth)
{
    printf(" TPM_EKBlobAuth_Delete:\n");
    if (tpm_ek_blob_auth != NULL) {
	TPM_EKBlobAuth_Init(tpm_ek_blob_auth);
    }
    return;
}

/*
  TPM_IDENTITY_CONTENTS
*/

/* TPM_IdentityContents_Init()

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

void TPM_IdentityContents_Init(TPM_IDENTITY_CONTENTS *tpm_identity_contents)
{
    printf(" TPM_IdentityContents_Init:\n");
    TPM_StructVer_Init(&(tpm_identity_contents->ver));
    tpm_identity_contents->ordinal = TPM_ORD_MakeIdentity;
    TPM_Digest_Init(tpm_identity_contents->labelPrivCADigest);
    TPM_Pubkey_Init(&(tpm_identity_contents->identityPubKey));
    return;
}

/* TPM_IdentityContents_Load()	

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

   NOTE: Never called.
*/

TPM_RESULT TPM_IdentityContents_Load(TPM_IDENTITY_CONTENTS *tpm_identity_contents,
				     unsigned char **stream,
				     uint32_t *stream_size)
{
    TPM_RESULT		rc = 0;

    printf(" TPM_IdentityContents_Load:\n");
    /* load ver */
    if (rc == 0) {
	rc = TPM_StructVer_Load(&(tpm_identity_contents->ver), stream, stream_size);
    }
    /* check ver immediately to ease debugging */
    if (rc == 0) {
	rc = TPM_StructVer_CheckVer(&(tpm_identity_contents->ver));
    }
    /* load ordinal */
    if (rc == 0) {
	rc = TPM_Load32(&(tpm_identity_contents->ordinal), stream, stream_size);
    }
    /* load labelPrivCADigest */
    if (rc == 0) {
	rc = TPM_Digest_Load(tpm_identity_contents->labelPrivCADigest, stream, stream_size);
    }
    /* load identityPubKey */
    if (rc == 0) {
	rc = TPM_Pubkey_Load(&(tpm_identity_contents->identityPubKey), stream, stream_size);
    }
    return rc;
}

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

TPM_RESULT TPM_IdentityContents_Store(TPM_STORE_BUFFER *sbuffer,
				      TPM_IDENTITY_CONTENTS *tpm_identity_contents)
{
    TPM_RESULT		rc = 0;

    printf(" TPM_IdentityContents_Store:\n");
    /* store ver */
    if (rc == 0) {
	rc = TPM_StructVer_Store(sbuffer, &(tpm_identity_contents->ver));
    }
    /* store ordinal */
    if (rc == 0) {
	rc = TPM_Sbuffer_Append32(sbuffer, tpm_identity_contents->ordinal);
    }
    /* store labelPrivCADigest */
    if (rc == 0) {
	rc = TPM_Digest_Store(sbuffer, tpm_identity_contents->labelPrivCADigest);
    }
    /* store identityPubKey */
    if (rc == 0) {
	rc = TPM_Pubkey_Store(sbuffer, &(tpm_identity_contents->identityPubKey));
    }
    return rc;
}

/* TPM_IdentityContents_Delete()

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

void TPM_IdentityContents_Delete(TPM_IDENTITY_CONTENTS *tpm_identity_contents)
{
    printf(" TPM_IdentityContents_Delete:\n");
    if (tpm_identity_contents != NULL) {
	TPM_Pubkey_Delete(&(tpm_identity_contents->identityPubKey));
	TPM_IdentityContents_Init(tpm_identity_contents);
    }
    return;
}

/*
  TPM_ASYM_CA_CONTENTS
*/

/* TPM_AsymCaContents_Init()

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

void TPM_AsymCaContents_Init(TPM_ASYM_CA_CONTENTS *tpm_asym_ca_contents)
{
    printf(" TPM_AsymCaContents_Init:\n");
    TPM_SymmetricKey_Init(&(tpm_asym_ca_contents->sessionKey));
    TPM_Digest_Init(tpm_asym_ca_contents->idDigest);
    return;
}

/* TPM_AsymCaContents_Load()

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

TPM_RESULT TPM_AsymCaContents_Load(TPM_ASYM_CA_CONTENTS *tpm_asym_ca_contents,
				   unsigned char **stream,
				   uint32_t *stream_size)
{
    TPM_RESULT		rc = 0;

    printf(" TPM_AsymCaContents_Load:\n");
    if (rc == 0) {
	rc = TPM_SymmetricKey_Load(&(tpm_asym_ca_contents->sessionKey), stream, stream_size);
    }
    if (rc == 0) {
	rc = TPM_Digest_Load(tpm_asym_ca_contents->idDigest, stream, stream_size);
    }
    return rc;
}

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

TPM_RESULT TPM_AsymCaContents_Store(TPM_STORE_BUFFER *sbuffer,
				    const TPM_ASYM_CA_CONTENTS *tpm_asym_ca_contents)
{
    TPM_RESULT		rc = 0;

    printf(" TPM_AsymCaContents_Store:\n");
    if (rc == 0) {
	rc = TPM_SymmetricKey_Store(sbuffer, &(tpm_asym_ca_contents->sessionKey));
    }
    if (rc == 0) {
	rc = TPM_Digest_Store(sbuffer, tpm_asym_ca_contents->idDigest);
    }
    return rc;
}

/* TPM_AsymCaContents_Delete()

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

void TPM_AsymCaContents_Delete(TPM_ASYM_CA_CONTENTS *tpm_asym_ca_contents)
{
    printf(" TPM_AsymCaContents_Delete:\n");
    if (tpm_asym_ca_contents != NULL) {
	TPM_SymmetricKey_Delete(&(tpm_asym_ca_contents->sessionKey));
	TPM_AsymCaContents_Init(tpm_asym_ca_contents);
    }
    return;
}






/*
  Processing Functions
*/

/* 15.1 TPM_MakeIdentity rev 114

   Generate a new Attestation Identity Key (AIK)

   labelPrivCADigest identifies the privacy CA that the owner expects to be the target CA for the
   AIK.	 The selection is not enforced by the TPM.  It is advisory only.  It is included because the
   TSS cannot be trusted to send the AIK to the correct privacy CA.  The privacy CA can use this
   parameter to validate that it is the target privacy CA and label intended by the TPM owner at the
   time the key was created.  The label can be used to indicate an application purpose.

   The public key of the new TPM identity SHALL be identityPubKey. The private key of the new TPM
   identity SHALL be tpm_signature_key.

   Properties of the new identity

   TPM_PUBKEY identityPubKey This SHALL be the public key of a previously unused asymmetric key
   pair.

   TPM_STORE_ASYMKEY tpm_signature_key This SHALL be the private key that forms a pair with
   identityPubKey and SHALL be extant only in a TPM-shielded location.
   
   This capability also generates a TPM_KEY containing the tpm_signature_key.
   
   If identityPubKey is stored on a platform it SHALL exist only in storage to which access is
   controlled and is available to authorized entities.
*/

TPM_RESULT TPM_Process_MakeIdentity(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_ENCAUTH		identityAuth;	/* Encrypted usage authorization data for the new identity
					   */
    TPM_CHOSENID_HASH	labelPrivCADigest;	/* The digest of the identity label and privacy CA
						   chosen for the new TPM identity. */
    TPM_KEY		idKeyParams;	/* Structure containing all parameters of new identity
					   key. pubKey.keyLength & idKeyParams.encData are both 0
					   MAY be TPM_KEY12 */
    TPM_AUTHHANDLE	srkAuthHandle;	/* The authorization handle used for SRK authorization. */
    TPM_NONCE		srknonceOdd;	/* Nonce generated by system associated with srkAuthHandle
					   */
    TPM_BOOL	continueSrkSession = TRUE;	/* Ignored */
    TPM_AUTHDATA	srkAuth;	/* The authorization digest for the inputs and the SRK. HMAC
					   key: srk.usageAuth. */
    TPM_AUTHHANDLE	authHandle;	/* The authorization handle used for owner
					   authorization. Session type MUST be OSAP. */
    TPM_NONCE		nonceOdd;	/* Nonce generated by system associated with authHandle */
    TPM_BOOL	continueAuthSession = TRUE;	/* Ignored */
    TPM_AUTHDATA	ownerAuth;	/* The authorization digest for inputs and owner. HMAC key:
					   ownerAuth. */
    /* 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			srkAuthHandleValid = FALSE;
    TPM_BOOL			authHandleValid = FALSE;
    TPM_AUTH_SESSION_DATA	*srk_auth_session_data = NULL;	/* session data for authHandle */
    TPM_AUTH_SESSION_DATA	*auth_session_data = NULL;  /* session data for dataAuthHandle */
    TPM_SECRET			*srkHmacKey;
    TPM_SECRET			*hmacKey;
    TPM_SECRET			a1Auth;
    TPM_STORE_ASYMKEY		*idKeyStoreAsymkey;
    TPM_IDENTITY_CONTENTS	idContents;
    TPM_DIGEST			h1Digest;	/* digest of TPM_IDENTITY_CONTENTS structure */
    int				ver;

    /* output parameters */
    uint32_t			outParamStart;	/* starting point of outParam's */
    uint32_t			outParamEnd;	/* ending point of outParam's */
    TPM_DIGEST			outParamDigest;
    TPM_KEY			idKey;		/* The newly created identity key. MAY be TPM_KEY12
						   */
    TPM_SIZED_BUFFER		identityBinding;	/* Signature of TPM_IDENTITY_CONTENTS using
							   idKey.private. */
    printf("TPM_Process_MakeIdentity: Ordinal Entry\n");
    TPM_Key_Init(&idKeyParams);			/* freed @1 */
    TPM_Key_Init(&idKey);			/* freed @2 */
    TPM_SizedBuffer_Init(&identityBinding);	/* freed @3 */
    TPM_IdentityContents_Init(&idContents);	/* freed @4 */
    /*
      get inputs
    */
    /* save the starting point of inParam's for authorization and auditing */
    inParamStart = command;
    /* get identityAuth parameter */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_Authdata_Load(identityAuth, &command, &paramSize);
    }
    /* get labelPrivCADigest parameter */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_Digest_Load(labelPrivCADigest, &command, &paramSize);
    }
    /* get idKeyParams parameter */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_Key_Load(&idKeyParams, &command, &paramSize);
    }
    /* 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(&srkAuthHandle,
					&srkAuthHandleValid,
					srknonceOdd,
					&continueSrkSession,
					srkAuth,
					&command, &paramSize);
	printf("TPM_Process_MakeIdentity: srkAuthHandle %08x\n", srkAuthHandle);
    }
    /* get the 'below the line' authorization parameters */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_AuthParams_Get(&authHandle,
					&authHandleValid,
					nonceOdd,
					&continueAuthSession,
					ownerAuth,
					&command, &paramSize);
    }
    if (returnCode == TPM_SUCCESS) {
	printf("TPM_Process_MakeIdentity: authHandle %08x\n", authHandle); 
    }
    if (returnCode == TPM_SUCCESS) {
	if (paramSize != 0) {
	    printf("TPM_Process_MakeIdentity: 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) {
	srkAuthHandleValid = FALSE;
	authHandleValid = FALSE;
    }
    /*
      Processing
    */
    /* 1. Validate the idKeyParams parameters for the key description */
    /* a. If the algorithm type is RSA the key length MUST be a minimum of 2048 and MUST use the
       default exponent. For interoperability the key length SHOULD be 2048 */
    /* b. If the algorithm type is other than RSA the strength provided by the key MUST be
       comparable to RSA 2048 */
    /* c. If the TPM is not designed to create a key of the requested type, return the error code
       TPM_BAD_KEY_PROPERTY */
    /* d. If TPM_PERMANENT_FLAGS -> FIPS is TRUE then */
    /* i. If authDataUsage specifies TPM_AUTH_NEVER return TPM_NOTFIPS */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_Key_CheckProperties(&ver, &idKeyParams, 2048,
					     tpm_state->tpm_permanent_flags.FIPS);
	printf("TPM_Process_MakeIdentity: key parameters v = %d\n", ver);
    }
    /* 2. Use authHandle to verify that the Owner authorized all TPM_MakeIdentity input
       parameters. */
    /* get the session data */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_AuthSessions_GetData(&auth_session_data,
					      &hmacKey,
					      tpm_state,
					      authHandle,
					      TPM_PID_OSAP,
					      TPM_ET_OWNER,
					      ordinal,
					      NULL,
					      NULL,
					      tpm_state->tpm_permanent_data.ownerAuth);
    }
    /* Validate the authorization to use the key pointed to by keyHandle */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_Auth2data_Check(tpm_state,
					 *hmacKey,		/* HMAC key */
					 inParamDigest,
					 auth_session_data,	/* authorization session */
					 nonceOdd,		/* Nonce generated by system
								   associated with authHandle */
					 continueAuthSession,
					 ownerAuth);		/* Authorization digest for input */
    }
    /* 3. Use srkAuthHandle to verify that the SRK owner authorized all TPM_MakeIdentity input
       parameters. */
    /* get the session data */
    if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH2_COMMAND)) {
	returnCode = TPM_AuthSessions_GetData
		     (&srk_auth_session_data,
		      &srkHmacKey,
		      tpm_state,
		      srkAuthHandle,
		      TPM_PID_NONE,
		      TPM_ET_KEYHANDLE,
		      ordinal,
		      &(tpm_state->tpm_permanent_data.srk),
		      /* OIAP */
		      &(tpm_state->tpm_permanent_data.srk.tpm_store_asymkey->usageAuth),
		      /* OSAP */
		      tpm_state->tpm_permanent_data.srk.tpm_store_asymkey->pubDataDigest);
    }
    /* Validate the authorization to use the key pointed to by keyHandle */
    if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH2_COMMAND)) {
	returnCode = TPM_Authdata_Check(tpm_state,
					*srkHmacKey,		/* HMAC key */
					inParamDigest,
					srk_auth_session_data, /* authorization session */
					srknonceOdd,		/* Nonce generated by system
								   associated with authHandle */
					continueSrkSession,
					srkAuth);		/* Authorization digest for input */
    }
    /* if there is no SRK authorization, check that the SRK authDataUsage is TPM_AUTH_NEVER */
    if ((returnCode == TPM_SUCCESS) && (tag != TPM_TAG_RQU_AUTH2_COMMAND)) {
	if (tpm_state->tpm_permanent_data.srk.authDataUsage != TPM_AUTH_NEVER) {
	    printf("TPM_Process_MakeIdentity: Error, SRK authorization required\n");
	    returnCode = TPM_AUTHFAIL;
	}
    }
    /* 4. Verify that idKeyParams -> keyUsage is TPM_KEY_IDENTITY. If it is not, return
       TPM_INVALID_KEYUSAGE */
    /* NOTE: TPM_KEY_IDENTITY keys must use TPM_SS_RSASSAPKCS1v15_SHA1 */
    if (returnCode == TPM_SUCCESS) {
	printf("TPM_Process_MakeIdentity: Checking key parameters\n");
	if (idKeyParams.keyUsage != TPM_KEY_IDENTITY) {
	    printf("TPM_Process_MakeIdentity: Error, "
		   "idKeyParams keyUsage %08x should be TPM_KEY_IDENTITY\n",
		   idKeyParams.keyUsage);
	    returnCode = TPM_INVALID_KEYUSAGE;
	}
    }
    /* 5. Verify that idKeyParams -> keyFlags -> migratable is FALSE. If it is not, return
       TPM_INVALID_KEYUSAGE */
    if (returnCode == TPM_SUCCESS) {
	if (idKeyParams.keyFlags & TPM_MIGRATABLE) {
	    printf("TPM_Process_MakeIdentity: Error, "
		   "idKeyParams keyFlags %08x cannot be migratable\n",
		   idKeyParams.keyFlags);
	    returnCode = TPM_INVALID_KEYUSAGE;
	}
    }
    /* 6. Create a1 by decrypting identityAuth according to the ADIP indicated by authHandle. */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_AuthSessionData_Decrypt(a1Auth,
						 NULL,
						 identityAuth,
						 auth_session_data,
						 NULL,
						 NULL,
						 FALSE);	/* even and odd */
    }
    /* 7. Set continueAuthSession and continueSRKSession to FALSE. */
    if (returnCode == TPM_SUCCESS) {
	continueAuthSession = FALSE;
	continueSrkSession = FALSE;
	/* 8. Determine the structure version */
	/* a. If idKeyParams -> tag is TPM_TAG_KEY12 */
	/* i. Set V1 to 2 */
	/* ii. Create idKey a TPM_KEY12 structure using idKeyParams as the default values for the
	       structure */
	/* b. If idKeyParams -> ver is 1.1	*/
	/* i. Set V1 to 1 */
	/* ii. Create idKey a TPM_KEY structure using idKeyParams as the default values for the
	       structure */
	/* NOTE Done by TPM_Key_CheckProperties() */
	/* NOTE The creation determination is done by TPM_Key_GenerateRSA() */
    }
    /* 9. Set the digestAtCreation values for pcrInfo */
    /* NOTE Done as the key is generated */
    /* a. For PCR_INFO_LONG include the locality of the current command */
    /* 10. Create an asymmetric key pair (identityPubKey and tpm_signature_key) using a
       TPM-protected capability, in accordance with the algorithm specified in idKeyParams */
    if (returnCode == TPM_SUCCESS) {
	/* generate the key pair, create the tpm_store_asymkey cache, copy key parameters, create
	   tpm_pcr_info cache, copies pcr parameters, sets digestAtCreation, sets pubKey, serializes
	   pcrInfo
	   
	   does not set encData */
	printf("TPM_Process_MakeIdentity: Generating key\n");
	returnCode = TPM_Key_GenerateRSA(&idKey,
					 tpm_state,
					 &(tpm_state->tpm_permanent_data.srk),	/* parent key */
					 tpm_state->tpm_stclear_data.PCRS,	/* PCR array */
					 ver,
					 idKeyParams.keyUsage,
					 idKeyParams.keyFlags,
					 idKeyParams.authDataUsage,	/* TPM_AUTH_DATA_USAGE */
					 &(idKeyParams.algorithmParms), /* TPM_KEY_PARMS */
					 idKeyParams.tpm_pcr_info,	/* TPM_PCR_INFO */
					 idKeyParams.tpm_pcr_info_long);/* TPM_PCR_INFO_LONG */

    }
    /* 11. Ensure that the authorization information in A1 is properly stored in the idKey as
       usageAuth. */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_Key_GetStoreAsymkey(&idKeyStoreAsymkey,
					     &idKey);
    }
    if (returnCode == TPM_SUCCESS) {
	TPM_Secret_Copy(idKeyStoreAsymkey->usageAuth, a1Auth);
	/* 12. Attach identityPubKey and tpm_signature_key to idKey */
	/* Note: Done as the key is generated */
	/* 13. Set idKey -> migrationAuth to TPM_PERMANENT_DATA -> tpmProof */
	TPM_Secret_Copy(idKeyStoreAsymkey->migrationAuth, tpm_state->tpm_permanent_data.tpmProof);
	/* 14. Ensure that all TPM_PAYLOAD_TYPE structures identity this key as TPM_PT_ASYM */
	/* NOTE Done as the key is generated */
    }	 
    /* 15. Encrypt the private portion of idKey using the SRK as the parent key */
    if (returnCode == TPM_SUCCESS) {
	printf("TPM_Process_MakeIdentity: Encrypting key private part with SRK\n");
	returnCode = TPM_Key_GenerateEncData(&idKey, &(tpm_state->tpm_permanent_data.srk));
    }
    /* 16. Create a TPM_IDENTITY_CONTENTS structure named idContents using labelPrivCADigest and the
       information from idKey */
    if (returnCode == TPM_SUCCESS) {
	TPM_Digest_Copy(idContents.labelPrivCADigest, labelPrivCADigest);
	returnCode = TPM_Pubkey_Set(&(idContents.identityPubKey), &idKey);
    }
    /* 17. Sign idContents using tpm_signature_key and TPM_SS_RSASSAPKCS1v15_SHA1. Store the result
       in identityBinding. */
    /* NOTE: TPM_Key_CheckProperties() verified TPM_SS_RSASSAPKCS1v15_SHA1 */
    /* serialize tpm_identity_contents and hash the results*/
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_SHA1_GenerateStructure(h1Digest,
						&idContents,
						(TPM_STORE_FUNCTION_T)TPM_IdentityContents_Store);
    }
    /* sign the digest */
    if (returnCode == TPM_SUCCESS) {
	printf("TPM_Process_MakeIdentity: Signing digest of TPM_IDENTITY_CONTENTS\n");
	returnCode = TPM_RSASignToSizedBuffer(&identityBinding, h1Digest, TPM_DIGEST_SIZE, &idKey);
    }
#if 0	/* NOTE Debug code to reverse the signature */
    if (returnCode == TPM_SUCCESS) {
	unsigned char 	*message = NULL;
	unsigned char 	*narr = NULL;
	uint32_t 	nbytes;
	unsigned char 	*earr = NULL;
	uint32_t 	ebytes;
	if (returnCode == 0) {
	    returnCode = TPM_Malloc(&message, identityBinding.size); /* freed @10 */
	}
	if (returnCode == 0) {
	    returnCode = TPM_Key_GetPublicKey(&nbytes, &narr, &idKey);
	}
	if (returnCode == 0) {
	    returnCode = TPM_Key_GetExponent(&ebytes, &earr, &idKey);
	}
	if (returnCode == 0) {
	    returnCode = TPM_RSAPublicEncryptRaw(message,			/* output */
						 identityBinding.size,	
						 identityBinding.buffer,	/* input */
						 identityBinding.size,	
						 narr,			/* public modulus */
						 nbytes,
						 earr,			/* public exponent */
						 ebytes);
	}
	free(message);	/* @10 */
    }
#endif
    /*
      response
    */
    /* standard response: tag, (dummy) paramSize, returnCode.  Failure is fatal. */
    if (rcf == 0) {
	printf("TPM_Process_MakeIdentity: 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 idKey */
	    returnCode = TPM_Key_Store(response, &idKey);
	}
	if (returnCode == TPM_SUCCESS) {
	    /* return identityBinding */
	    returnCode = TPM_SizedBuffer_Store(response, &identityBinding);
	    /* 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,
					    *srkHmacKey,	/* owner HMAC key */
					    srk_auth_session_data,
					    outParamDigest,
					    srknonceOdd,
					    continueSrkSession);
	}
	/* 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 continueSrkSession is FALSE, terminate the session */
    if (((rcf != 0) ||
	 ((returnCode != TPM_SUCCESS) && (returnCode != TPM_DEFEND_LOCK_RUNNING)) ||
	 !continueSrkSession) &&
	srkAuthHandleValid) {
	TPM_AuthSessions_TerminateHandle(tpm_state->tpm_stclear_data.authSessions, srkAuthHandle);
    }
    /* 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(&idKeyParams);		/* freed @1 */
    TPM_Key_Delete(&idKey);			/* freed @2 */
    TPM_SizedBuffer_Delete(&identityBinding);	/* freed @3 */
    TPM_IdentityContents_Delete(&idContents);	/* freed @4 */
    return rcf;
}

/* 15.2 TPM_ActivateIdentity rev 107

  The purpose of TPM_ActivateIdentity is to twofold. The first purpose is to obtain assurance that
  the credential in the TPM_SYM_CA_ATTESTATION is for this TPM. The second purpose is to obtain the
  session key used to encrypt the TPM_IDENTITY_CREDENTIAL.

  The command TPM_ActivateIdentity activates a TPM identity created using the command
  TPM_MakeIdentity.
  
  The command assumes the availability of the private key associated with the identity. The command
  will verify the association between the keys during the process.
  
  The command will decrypt the input blob and extract the session key and verify the connection
  between the public and private keys. The input blob can be in 1.1 or 1.2 format.
*/

TPM_RESULT TPM_Process_ActivateIdentity(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	idKeyHandle;	/* handle of identity key to be activated */
    TPM_SIZED_BUFFER	blob;		/* The encrypted ASYM_CA_CONTENTS or TPM_EK_BLOB */
    TPM_AUTHHANDLE	idKeyAuthHandle;	/* The authorization handle used for ID key
						   authorization. */
    TPM_NONCE		idKeynonceOdd;	/* Nonce generated by system associated with idKeyAuthHandle
					   */
    TPM_BOOL	continueIdKeySession = TRUE;	/* Continue usage flag for idKeyAuthHandle. */
    TPM_AUTHDATA	idKeyAuth;	/* The authorization digest for the inputs and ID key. HMAC
					   key: idKey.usageAuth. */
    TPM_AUTHHANDLE	authHandle;	/* The authorization handle used for owner 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		ownerAuth;	/* The authorization digest for inputs and
						   owner. HMAC key: ownerAuth. */

    /* 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			idKeyAuthHandleValid = FALSE;
    TPM_BOOL			authHandleValid = FALSE;
    TPM_AUTH_SESSION_DATA	*id_key_auth_session_data = NULL; /* session data for authHandle */
    TPM_AUTH_SESSION_DATA	*auth_session_data = NULL; /* session data for dataAuthHandle */
    TPM_SECRET			*idKeyHmacKey;
    TPM_SECRET			*hmacKey;
    TPM_KEY			*idKey;			/* Identity key to be activated */
    TPM_SECRET			*idKeyUsageAuth;
    TPM_BOOL			idPCRStatus;
    TPM_DIGEST			h1Digest;		/* digest of public key in idKey */
    unsigned char		*b1Blob = NULL;		/* decrypted blob */
    uint32_t			b1BlobLength = 0;	/* actual valid data */
    TPM_STRUCTURE_TAG		hTag;			/* b1 tag in host byte order */
    int				vers = 0;		/* version of blob */
    unsigned char		*stream;
    uint32_t			stream_size;
    TPM_EK_BLOB			b1EkBlob;
    TPM_ASYM_CA_CONTENTS	b1AsymCaContents;
    TPM_SYMMETRIC_KEY		*k1 = NULL;
    TPM_EK_BLOB_ACTIVATE	a1;

    /* output parameters */
    uint32_t			outParamStart;	/* starting point of outParam's */
    uint32_t			outParamEnd;	/* ending point of outParam's */
    TPM_DIGEST			outParamDigest;
    TPM_SYMMETRIC_KEY		symmetricKey;	/* The decrypted symmetric key. */

    printf("TPM_Process_ActivateIdentity: Ordinal Entry\n");
    TPM_SizedBuffer_Init(&blob);		/* freed @1 */
    TPM_SymmetricKey_Init(&symmetricKey);	/* freed @2 */
    TPM_AsymCaContents_Init(&b1AsymCaContents); /* freed @4 */
    TPM_EKBlob_Init(&b1EkBlob);			/* freed @5 */
    TPM_EKBlobActivate_Init(&a1);		/* freed @6 */
    /*
      get inputs
    */
    /* get idKey parameter */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_Load32(&idKeyHandle, &command, &paramSize);
    }
    /* save the starting point of inParam's for authorization and auditing */
    inParamStart = command;
    /* get blob parameter */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_SizedBuffer_Load(&blob, &command, &paramSize);
    }
    /* 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(&idKeyAuthHandle,
					&idKeyAuthHandleValid,
					idKeynonceOdd,
					&continueIdKeySession,
					idKeyAuth,
					&command, &paramSize);
	printf("TPM_Process_ActivateIdentity: idKeyAuthHandle %08x\n", idKeyAuthHandle);
    }
    /* get the 'below the line' authorization parameters */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_AuthParams_Get(&authHandle,
					&authHandleValid,
					nonceOdd,
					&continueAuthSession,
					ownerAuth,
					&command, &paramSize);
    }
    if (returnCode == TPM_SUCCESS) {
	printf("TPM_Process_ActivateIdentity: authHandle %08x\n", authHandle); 
    }
    if (returnCode == TPM_SUCCESS) {
	if (paramSize != 0) {
	    printf("TPM_Process_ActivateIdentity: 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) {
	idKeyAuthHandleValid = FALSE;
	authHandleValid = FALSE;
    }
    /*
      Processing
    */
    /* 1. Using the authHandle field, validate the owner's authorization to execute the command and
       all of the incoming parameters. */
    /* get the session data */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_AuthSessions_GetData(&auth_session_data,
					      &hmacKey,
					      tpm_state,
					      authHandle,
					      TPM_PID_NONE,
					      TPM_ET_OWNER,
					      ordinal,
					      NULL,
					      &(tpm_state->tpm_permanent_data.ownerAuth), /* OIAP */
					      tpm_state->tpm_permanent_data.ownerAuth);	  /* OSAP */
    }
    /* 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,
					ownerAuth);		/* Authorization digest for input */
    }
    /* 2. Using the idKeyAuthHandle, validate the authorization to execute command and all of the
       incoming parameters */
    /* get the idKey */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_KeyHandleEntries_GetKey(&idKey, &idPCRStatus, tpm_state, idKeyHandle,
						 FALSE,		/* not r/o, using to authenticate */
						 FALSE,		/* do not ignore PCRs */
						 FALSE);	/* cannot use EK */
    }
    /* get keyHandle -> usageAuth */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_Key_GetUsageAuth(&idKeyUsageAuth, idKey);
    }	 
    /* get the session data */
    if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH2_COMMAND)) {
	returnCode = TPM_AuthSessions_GetData(&id_key_auth_session_data,
					      &idKeyHmacKey,
					      tpm_state,
					      idKeyAuthHandle,
					      TPM_PID_NONE,
					      TPM_ET_KEYHANDLE,
					      ordinal,
					      idKey,
					      idKeyUsageAuth,		/* OIAP */
					      idKey->tpm_store_asymkey->pubDataDigest); /* OSAP */
    }
    /* Validate the authorization to use the key pointed to by keyHandle */
    if ((returnCode == TPM_SUCCESS) && (tag == TPM_TAG_RQU_AUTH2_COMMAND)) {
	returnCode = TPM_Auth2data_Check(tpm_state,
					 *idKeyHmacKey,		/* HMAC key */
					 inParamDigest,
					 id_key_auth_session_data,	/* authorization session */
					 idKeynonceOdd,		/* Nonce generated by system
								   associated with authHandle */
					 continueIdKeySession,
					 idKeyAuth);		/* Authorization digest for input */
    }
    /* if there is no idKey authorization, check that the idKey -> authDataUsage is TPM_AUTH_NEVER
       */
    if ((returnCode == TPM_SUCCESS) && (tag != TPM_TAG_RQU_AUTH2_COMMAND)) {
	if (idKey->authDataUsage != TPM_AUTH_NEVER) {
	    printf("TPM_Process_ActivateIdentity: Error, ID key authorization required\n");
	    returnCode = TPM_AUTHFAIL;
	}
    }
    /* 3. Validate that the idKey is the public key of a valid TPM identity by checking that
       idKeyHandle -> keyUsage is TPM_KEY_IDENTITY. Return TPM_BAD_PARAMETER on mismatch */
    if (returnCode == TPM_SUCCESS) {
	printf("TPM_Process_ActivateIdentity: Checking for identity key\n");
	if (idKey->keyUsage != TPM_KEY_IDENTITY) {
	    printf("TPM_Process_ActivateIdentity: Error, keyUsage %04hx must be TPM_KEY_IDENTITY\n",
		   idKey->keyUsage);
	    returnCode = TPM_BAD_PARAMETER;
	}
    }
    /* 4. Create H1 the digest of a TPM_PUBKEY derived from idKey */
    if (returnCode == TPM_SUCCESS) {
	returnCode = TPM_Key_GeneratePubkeyDigest(h1Digest, idKey);
    }
    /* 5. Decrypt blob creating B1 using PRIVEK as the decryption key */
    if (returnCode == TPM_SUCCESS) {
	printf("TPM_Process_ActivateIdentity: Decrypting blob with EK\n");
	returnCode = TPM_RSAPrivateDecryptMalloc(&b1Blob,	/* decrypted data */
						 &b1BlobLength,	/* actual size of b1 data */
						 blob.buffer,
						 blob.size,
						 &(tpm_state->tpm_permanent_data.endorsementKey));
    }
    /* 6. Determine the type and version of B1 */
    if (returnCode == TPM_SUCCESS) {
	stream = b1Blob;		/* b1 must be preserved for the free */
	stream_size = b1BlobLength;
	/* convert possible tag to uint16_t */
	hTag = ntohs(*(TPM_STRUCTURE_TAG *)b1Blob);
	/* a. If B1 -> tag is TPM_TAG_EK_BLOB then */
	if (hTag == TPM_TAG_EK_BLOB) {
	    /* i. B1 is a TPM_EK_BLOB */
	    printf("TPM_Process_ActivateIdentity: b1 is TPM_EK_BLOB\n");
	    vers = 2;
	    returnCode = TPM_EKBlob_Load(&b1EkBlob, &stream, &stream_size);
	}
	/* b. Else */
	else {
	    /* i. B1 is a TPM_ASYM_CA_CONTENTS. As there is no tag for this structure it is possible
	       for the TPM to make a mistake here but other sections of the structure undergo
	       validation */
	    printf("TPM_Process_ActivateIdentity: b1 is TPM_ASYM_CA_CONTENTS\n");
	    vers = 1;
	    returnCode = TPM_AsymCaContents_Load(&b1AsymCaContents, &stream, &stream_size);
	}
    }
    /* 7. If B1 is a version 1.1 TPM_ASYM_CA_CONTENTS then */
    if ((returnCode == TPM_SUCCESS) && (vers == 1)) {
	/* a. Compare H1 to B1 -> idDigest on mismatch return TPM_BAD_PARAMETER */
	if (returnCode == TPM_SUCCESS) {
	    returnCode = TPM_Digest_Compare(h1Digest, b1AsymCaContents.idDigest);
	    if (returnCode != 0) {
		printf("TPM_Process_ActivateIdentity: Error "
		       "comparing TPM_ASYM_CA_CONTENTS idDigest\n");
		returnCode = TPM_BAD_PARAMETER;
	    }
	}
	/* b. Set K1 to B1 -> sessionKey */
	if (returnCode == TPM_SUCCESS) {
	    k1 = &(b1AsymCaContents.sessionKey);
	}
    }
    /* 8. If B1 is a TPM_EK_BLOB then */
    if ((returnCode == TPM_SUCCESS) && (vers == 2)) {
	/* a. Validate that B1 -> ekType is TPM_EK_TYPE_ACTIVATE, return TPM_BAD_TYPE if not. */
	if (returnCode == TPM_SUCCESS) {
	    if (b1EkBlob.ekType != TPM_EK_TYPE_ACTIVATE) {
		printf("TPM_Process_ActivateIdentity: Error, "
		       "TPM_EK_BLOB not ekType TPM_EK_TYPE_ACTIVATE\n");
		returnCode = TPM_BAD_TYPE;
	    }
	}
	/* b. Assign A1 as a TPM_EK_BLOB_ACTIVATE structure from B1 -> blob */
	if (returnCode == TPM_SUCCESS) {
	    stream = b1EkBlob.blob.buffer;
	    stream_size =  b1EkBlob.blob.size;
	    returnCode = TPM_EKBlobActivate_Load(&a1, &stream, &stream_size);
	}
	/* c. Compare H1 to A1 -> idDigest on mismatch return TPM_BAD_PARAMETER */
	if (returnCode == TPM_SUCCESS) {
	    returnCode = TPM_Digest_Compare(h1Digest, a1.idDigest);
	    if (returnCode != 0) {
		printf("TPM_Process_ActivateIdentity: Error "
		       "comparing TPM_EK_TYPE_ACTIVATE idDigest\n");
		returnCode = TPM_BAD_PARAMETER;
	    }
	}
	/* d. If A1 -> pcrSelection is not NULL */
	/* i. Compute a composite hash C1 using the PCR selection A1 -> pcrSelection */
	/* ii. Compare C1 to A1 -> pcrInfo -> digestAtRelease and return TPM_WRONGPCRVAL on a
	   mismatch */
	/* e. If A1 -> pcrInfo specifies a locality ensure that the appropriate locality has been
	   asserted, return TPM_BAD_LOCALITY on error */
	if (returnCode == TPM_SUCCESS) {
	    if (returnCode == TPM_SUCCESS) {
		returnCode =
		    TPM_PCRInfoShort_CheckDigest(&(a1.pcrInfo),
						 tpm_state->tpm_stclear_data.PCRS, /* PCR array */
						 tpm_state->tpm_stany_flags.localityModifier);
	    }
	}
	/* f. Set K1 to A1 -> symmetricKey */
	if (returnCode == TPM_SUCCESS) {
	    k1 = &(a1.sessionKey);
	}
    }
    /*
      response
    */
    /* standard response: tag, (dummy) paramSize, returnCode.  Failure is fatal. */
    if (rcf == 0) {
	printf("TPM_Process_ActivateIdentity: 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;
	    /* 9. Return K1 */
	    returnCode = TPM_SymmetricKey_Store(response, k1);
	    /* 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,
					    *idKeyHmacKey,	/* owner HMAC key */
					    id_key_auth_session_data,
					    outParamDigest,
					    idKeynonceOdd,
					    continueIdKeySession);
	}
	/* 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)) ||
	 !continueIdKeySession) &&
	idKeyAuthHandleValid) {
	TPM_AuthSessions_TerminateHandle(tpm_state->tpm_stclear_data.authSessions, idKeyAuthHandle);
    }
    /* 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(&blob);			/* @1 */
    TPM_SymmetricKey_Delete(&symmetricKey);		/* @2 */
    free(b1Blob);					/* @3 */
    TPM_AsymCaContents_Delete(&b1AsymCaContents);	/* @4 */
    TPM_EKBlob_Delete(&b1EkBlob);			/* @5 */
    TPM_EKBlobActivate_Delete(&a1);			/* @6 */
    return rcf;
}
