/********************************************************************************/
/*										*/
/*		Process the Authorization Sessions     				*/
/*			     Written by Ken Goldman				*/
/*		       IBM Thomas J. Watson Research Center			*/
/*										*/
/*  Licenses and Notices							*/
/*										*/
/*  1. Copyright Licenses:							*/
/*										*/
/*  - Trusted Computing Group (TCG) grants to the user of the source code in	*/
/*    this specification (the "Source Code") a worldwide, irrevocable, 		*/
/*    nonexclusive, royalty free, copyright license to reproduce, create 	*/
/*    derivative works, distribute, display and perform the Source Code and	*/
/*    derivative works thereof, and to grant others the rights granted herein.	*/
/*										*/
/*  - The TCG grants to the user of the other parts of the specification 	*/
/*    (other than the Source Code) the rights to reproduce, distribute, 	*/
/*    display, and perform the specification solely for the purpose of 		*/
/*    developing products based on such documents.				*/
/*										*/
/*  2. Source Code Distribution Conditions:					*/
/*										*/
/*  - Redistributions of Source Code must retain the above copyright licenses, 	*/
/*    this list of conditions and the following disclaimers.			*/
/*										*/
/*  - Redistributions in binary form must reproduce the above copyright 	*/
/*    licenses, this list of conditions	and the following disclaimers in the 	*/
/*    documentation and/or other materials provided with the distribution.	*/
/*										*/
/*  3. Disclaimers:								*/
/*										*/
/*  - THE COPYRIGHT LICENSES SET FORTH ABOVE DO NOT REPRESENT ANY FORM OF	*/
/*  LICENSE OR WAIVER, EXPRESS OR IMPLIED, BY ESTOPPEL OR OTHERWISE, WITH	*/
/*  RESPECT TO PATENT RIGHTS HELD BY TCG MEMBERS (OR OTHER THIRD PARTIES)	*/
/*  THAT MAY BE NECESSARY TO IMPLEMENT THIS SPECIFICATION OR OTHERWISE.		*/
/*  Contact TCG Administration (admin@trustedcomputinggroup.org) for 		*/
/*  information on specification licensing rights available through TCG 	*/
/*  membership agreements.							*/
/*										*/
/*  - THIS SPECIFICATION IS PROVIDED "AS IS" WITH NO EXPRESS OR IMPLIED 	*/
/*    WARRANTIES WHATSOEVER, INCLUDING ANY WARRANTY OF MERCHANTABILITY OR 	*/
/*    FITNESS FOR A PARTICULAR PURPOSE, ACCURACY, COMPLETENESS, OR 		*/
/*    NONINFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS, OR ANY WARRANTY 		*/
/*    OTHERWISE ARISING OUT OF ANY PROPOSAL, SPECIFICATION OR SAMPLE.		*/
/*										*/
/*  - Without limitation, TCG and its members and licensors disclaim all 	*/
/*    liability, including liability for infringement of any proprietary 	*/
/*    rights, relating to use of information in this specification and to the	*/
/*    implementation of this specification, and TCG disclaims all liability for	*/
/*    cost of procurement of substitute goods or services, lost profits, loss 	*/
/*    of use, loss of data or any incidental, consequential, direct, indirect, 	*/
/*    or special damages, whether under contract, tort, warranty or otherwise, 	*/
/*    arising in any way out of use or reliance upon this specification or any 	*/
/*    information herein.							*/
/*										*/
/*  (c) Copyright IBM Corp. and others, 2016 - 2023				*/
/*										*/
/********************************************************************************/

//**  Introduction
// This file contains the subsystem that process the authorization sessions
// including implementation of the Dictionary Attack logic. ExecCommand() uses
// ParseSessionBuffer() to process the authorization session area of a command and
// BuildResponseSession() to create the authorization session area of a response.

//**  Includes and Data Definitions

#define SESSION_PROCESS_C

#include "Tpm.h"
#include "ACT.h"
#include "Marshal.h"

//
//**  Authorization Support Functions
//

//*** IsDAExempted()
// This function indicates if a handle is exempted from DA logic.
// A handle is exempted if it is:
//  a) a primary seed handle;
//  b) an object with noDA bit SET;
//  c) an NV Index with TPMA_NV_NO_DA bit SET; or
//  d) a PCR handle.
//
//  Return Type: BOOL
//      TRUE(1)         handle is exempted from DA logic
//      FALSE(0)        handle is not exempted from DA logic
BOOL IsDAExempted(TPM_HANDLE handle  // IN: entity handle
		  )
{
    BOOL result = FALSE;
    //
    switch(HandleGetType(handle))
	{
	  case TPM_HT_PERMANENT:
	    // All permanent handles, other than TPM_RH_LOCKOUT, are exempt from
	    // DA protection.
	    result = (handle != TPM_RH_LOCKOUT);
	    break;
	    // When this function is called, a persistent object will have been loaded
	    // into an object slot and assigned a transient handle.
	  case TPM_HT_TRANSIENT:
	      {
		  TPMA_OBJECT attributes = ObjectGetPublicAttributes(handle);
		  result                 = IS_ATTRIBUTE(attributes, TPMA_OBJECT, noDA);
		  break;
	      }
	  case TPM_HT_NV_INDEX:
	      {
		  NV_INDEX* nvIndex = NvGetIndexInfo(handle, NULL);
		  result = IS_ATTRIBUTE(nvIndex->publicArea.attributes, TPMA_NV, NO_DA);
		  break;
	      }
	  case TPM_HT_PCR:
	    // PCRs are always exempted from DA.
	    result = TRUE;
	    break;
	  default:
	    break;
	}
    return result;
}

//*** IncrementLockout()
// This function is called after an authorization failure that involves use of
// an authValue. If the entity referenced by the handle is not exempt from DA
// protection, then the failedTries counter will be incremented.
//
//  Return Type: TPM_RC
//      TPM_RC_AUTH_FAIL    authorization failure that caused DA lockout to increment
//      TPM_RC_BAD_AUTH     authorization failure did not cause DA lockout to
//                          increment
static TPM_RC IncrementLockout(UINT32 sessionIndex)
{
    TPM_HANDLE handle        = s_associatedHandles[sessionIndex];
    TPM_HANDLE sessionHandle = s_sessionHandles[sessionIndex];
    SESSION*   session       = NULL;
    //
    // Don't increment lockout unless the handle associated with the session
    // is DA protected or the session is bound to a DA protected entity.
    if(sessionHandle == TPM_RS_PW)
	{
	    if(IsDAExempted(handle))
		return TPM_RC_BAD_AUTH;
	}
    else
	{
	    session = SessionGet(sessionHandle);
	    // If the session is bound to lockout, then use that as the relevant
	    // handle. This means that an authorization failure with a bound session
	    // bound to lockoutAuth will take precedence over any other
	    // lockout check
	    if(session->attributes.isLockoutBound == SET)
		handle = TPM_RH_LOCKOUT;
	    if(session->attributes.isDaBound == CLEAR
	       && (IsDAExempted(handle) || session->attributes.includeAuth == CLEAR))
		// If the handle was changed to TPM_RH_LOCKOUT, this will not return
		// TPM_RC_BAD_AUTH
		return TPM_RC_BAD_AUTH;
	}
    if(handle == TPM_RH_LOCKOUT)
	{
	    pAssert(gp.lockOutAuthEnabled == TRUE);

	    // lockout is no longer enabled
	    gp.lockOutAuthEnabled = FALSE;

	    // For TPM_RH_LOCKOUT, if lockoutRecovery is 0, no need to update NV since
	    // the lockout authorization will be reset at startup.
	    if(gp.lockoutRecovery != 0)
		{
		    if(NV_IS_AVAILABLE)
			// Update NV.
			NV_SYNC_PERSISTENT(lockOutAuthEnabled);
		    else
			// No NV access for now. Put the TPM in pending mode.
			s_DAPendingOnNV = TRUE;
		}
	}
    else
	{
	    if(gp.recoveryTime != 0)
		{
		    gp.failedTries++;
		    if(NV_IS_AVAILABLE)
			// Record changes to NV. NvWrite will SET g_updateNV
			NV_SYNC_PERSISTENT(failedTries);
		    else
			// No NV access for now.  Put the TPM in pending mode.
			s_DAPendingOnNV = TRUE;
		}
	}
    // Register a DA failure and reset the timers.
    DARegisterFailure(handle);

    return TPM_RC_AUTH_FAIL;
}

//*** IsSessionBindEntity()
// This function indicates if the entity associated with the handle is the entity,
// to which this session is bound. The binding would occur by making the "bind"
// parameter in TPM2_StartAuthSession() not equal to TPM_RH_NULL. The binding only
// occurs if the session is an HMAC session. The bind value is a combination of
// the Name and the authValue of the entity.
//
//  Return Type: BOOL
//      TRUE(1)         handle points to the session start entity
//      FALSE(0)        handle does not point to the session start entity
static BOOL IsSessionBindEntity(
				TPM_HANDLE associatedHandle,  // IN: handle to be authorized
				SESSION*   session            // IN: associated session
				)
{
    TPM2B_NAME entity;  // The bind value for the entity
    //
    // If the session is not bound, return FALSE.
    if(session->attributes.isBound)
	{
	    // Compute the bind value for the entity.
	    SessionComputeBoundEntity(associatedHandle, &entity);

	    // Compare to the bind value in the session.
	    return MemoryEqual2B(&entity.b, &session->u1.boundEntity.b);
	}
    return FALSE;
}

//*** IsPolicySessionRequired()
// Checks if a policy session is required for a command. If a command requires
// DUP or ADMIN role authorization, then the handle that requires that role is the
// first handle in the command. This simplifies this checking. If a new command
// is created that requires multiple ADMIN role authorizations, then it will
// have to be special-cased in this function.
// A policy session is required if:
// a) the command requires the DUP role;
// b) the command requires the ADMIN role and the authorized entity
//    is an object and its adminWithPolicy bit is SET;
// c) the command requires the ADMIN role and the authorized entity
//    is a permanent handle or an NV Index; or
// d) the authorized entity is a PCR belonging to a policy group, and
//    has its policy initialized
//  Return Type: BOOL
//      TRUE(1)         policy session is required
//      FALSE(0)        policy session is not required
static BOOL IsPolicySessionRequired(COMMAND_INDEX commandIndex,  // IN: command index
				    UINT32        sessionIndex   // IN: session index
				    )
{
    AUTH_ROLE role = CommandAuthRole(commandIndex, sessionIndex);
    TPM_HT    type = HandleGetType(s_associatedHandles[sessionIndex]);
    //
    if(role == AUTH_DUP)
	return TRUE;
    if(role == AUTH_ADMIN)
	{
	    // We allow an exception for ADMIN role in a transient object. If the object
	    // allows ADMIN role actions with authorization, then policy is not
	    // required. For all other cases, there is no way to override the command
	    // requirement that a policy be used
	    if(type == TPM_HT_TRANSIENT)
		{
		    OBJECT* object = HandleToObject(s_associatedHandles[sessionIndex]);

		    if(!IS_ATTRIBUTE(object->publicArea.objectAttributes, TPMA_OBJECT, adminWithPolicy))
			return FALSE;
		}
	    return TRUE;
	}

    if(type == TPM_HT_PCR)
	{
	    if(PCRPolicyIsAvailable(s_associatedHandles[sessionIndex]))
		{
		    TPM2B_DIGEST  policy;
		    TPMI_ALG_HASH policyAlg;
		    policyAlg = PCRGetAuthPolicy(s_associatedHandles[sessionIndex], &policy);
		    if(policyAlg != TPM_ALG_NULL)
			return TRUE;
		}
	}
    return FALSE;
}

//*** IsAuthValueAvailable()
// This function indicates if authValue is available and allowed for USER role
// authorization of an entity.
//
// This function is similar to IsAuthPolicyAvailable() except that it does not
// check the size of the authValue as IsAuthPolicyAvailable() does (a null
// authValue is a valid authorization, but a null policy is not a valid policy).
//
// This function does not check that the handle reference is valid or if the entity
// is in an enabled hierarchy. Those checks are assumed to have been performed
// during the handle unmarshaling.
//
//  Return Type: BOOL
//      TRUE(1)         authValue is available
//      FALSE(0)        authValue is not available
static BOOL IsAuthValueAvailable(TPM_HANDLE    handle,        // IN: handle of entity
				 COMMAND_INDEX commandIndex,  // IN: command index
				 UINT32        sessionIndex   // IN: session index
				 )
{
    BOOL result = FALSE;
    //
    switch(HandleGetType(handle))
	{
	  case TPM_HT_PERMANENT:
	    switch(handle)
		{
		    // At this point hierarchy availability has already been
		    // checked so primary seed handles are always available here
		  case TPM_RH_OWNER:
		  case TPM_RH_ENDORSEMENT:
		  case TPM_RH_PLATFORM:
#if VENDOR_PERMANENT_AUTH_ENABLED == YES
		    // This vendor defined handle associated with the
		    // manufacturer's shared secret
		  case VENDOR_PERMANENT_AUTH_HANDLE:
#endif
		    // The DA checking has been performed on LockoutAuth but we
		    // bypass the DA logic if we are using lockout policy. The
		    // policy would allow execution to continue an lockoutAuth
		    // could be used, even if direct use of lockoutAuth is disabled
		  case TPM_RH_LOCKOUT:
		    // NullAuth is always available.
		  case TPM_RH_NULL:
		    result = TRUE;
		    break;

#ifndef __ACT_DISABLED	// libtpms changed
		    FOR_EACH_ACT(CASE_ACT_HANDLE)
			{
			    // The ACT auth value is not available if the platform is disabled
			    result = g_phEnable == SET;
			    break;
			}
#endif  // ACT_SUPPORT

		  default:
		    // Otherwise authValue is not available.
		    break;
		}
	    break;
	  case TPM_HT_TRANSIENT:
	    // A persistent object has already been loaded and the internal
	    // handle changed.
	      {
		  OBJECT*     object;
		  TPMA_OBJECT attributes;
		  //
		  object     = HandleToObject(handle);
		  attributes = object->publicArea.objectAttributes;

		  // authValue is always available for a sequence object.
		  // An alternative for this is to
		  // SET_ATTRIBUTE(object->publicArea, TPMA_OBJECT, userWithAuth) when the
		  // sequence is started.
		  if(ObjectIsSequence(object))
		      {
			  result = TRUE;
			  break;
		      }
		  // authValue is available for an object if it has its sensitive
		  // portion loaded and
		  //  a) userWithAuth bit is SET, or
		  //  b) ADMIN role is required
		  if(object->attributes.publicOnly == CLEAR
		     && (IS_ATTRIBUTE(attributes, TPMA_OBJECT, userWithAuth)
			 || (CommandAuthRole(commandIndex, sessionIndex) == AUTH_ADMIN
			     && !IS_ATTRIBUTE(
					      attributes, TPMA_OBJECT, adminWithPolicy))))
		      result = TRUE;
	      }
	      break;
	  case TPM_HT_NV_INDEX:
	    // NV Index.
	      {
		  NV_REF    locator;
		  NV_INDEX* nvIndex = NvGetIndexInfo(handle, &locator);
		  TPMA_NV   nvAttributes;
		  //
		  pAssert(nvIndex != 0);

		  nvAttributes = nvIndex->publicArea.attributes;

		  if(IsWriteOperation(commandIndex))
		      {
			  // AuthWrite can't be set for a PIN index
			  if(IS_ATTRIBUTE(nvAttributes, TPMA_NV, AUTHWRITE))
			      result = TRUE;
		      }
		  else
		      {
			  // A "read" operation
			  // For a PIN Index, the authValue is available as long as the
			  // Index has been written and the pinCount is less than pinLimit
			  if(IsNvPinFailIndex(nvAttributes)
			     || IsNvPinPassIndex(nvAttributes))
			      {
				  NV_PIN pin;
				  if(!IS_ATTRIBUTE(nvAttributes, TPMA_NV, WRITTEN))
				      break;  // return false
				  // get the index values
				  pin.intVal = NvGetUINT64Data(nvIndex, locator);
				  if(pin.pin.pinCount < pin.pin.pinLimit)
				      result = TRUE;
			      }
			  // For non-PIN Indexes, need to allow use of the authValue
			  else if(IS_ATTRIBUTE(nvAttributes, TPMA_NV, AUTHREAD))
			      result = TRUE;
		      }
	      }
	      break;
	  case TPM_HT_PCR:
	    // PCR handle.
	    // authValue is always allowed for PCR
	    result = TRUE;
	    break;
	  default:
	    // Otherwise, authValue is not available
	    break;
	}
    return result;
}

//*** IsAuthPolicyAvailable()
// This function indicates if an authPolicy is available and allowed.
//
// This function does not check that the handle reference is valid or if the entity
// is in an enabled hierarchy. Those checks are assumed to have been performed
// during the handle unmarshaling.
//
//  Return Type: BOOL
//      TRUE(1)         authPolicy is available
//      FALSE(0)        authPolicy is not available
static BOOL IsAuthPolicyAvailable(TPM_HANDLE    handle,        // IN: handle of entity
				  COMMAND_INDEX commandIndex,  // IN: command index
				  UINT32        sessionIndex   // IN: session index
				  )
{
    BOOL result = FALSE;
    //
    switch(HandleGetType(handle))
	{
	  case TPM_HT_PERMANENT:
	    switch(handle)
		{
		    // At this point hierarchy availability has already been checked.
		  case TPM_RH_OWNER:
		    if(gp.ownerPolicy.t.size != 0)
			result = TRUE;
		    break;
		  case TPM_RH_ENDORSEMENT:
		    if(gp.endorsementPolicy.t.size != 0)
			result = TRUE;
		    break;
		  case TPM_RH_PLATFORM:
		    if(gc.platformPolicy.t.size != 0)
			result = TRUE;
		    break;
#if ACT_SUPPORT || 1	// libtpms changed

#  define ACT_GET_POLICY(N)						\
		    case TPM_RH_ACT_##N:				\
		      if(go.ACT_##N.authPolicy.t.size != 0)		\
			  result = TRUE;				\
		      break;

		    FOR_EACH_ACT(ACT_GET_POLICY)
#endif  // ACT_SUPPORT

		  case TPM_RH_LOCKOUT:
		    if(gp.lockoutPolicy.t.size != 0)
			result = TRUE;
		    break;
		  default:
		    break;
		}
	    break;
	  case TPM_HT_TRANSIENT:
	      {
		  // Object handle.
		  // An evict object would already have been loaded and given a
		  // transient object handle by this point.
		  OBJECT* object = HandleToObject(handle);
		  // Policy authorization is not available for an object with only
		  // public portion loaded.
		  if(object->attributes.publicOnly == CLEAR)
		      {
			  // Policy authorization is always available for an object but
			  // is never available for a sequence.
			  if(!ObjectIsSequence(object))
			      result = TRUE;
		      }
		  break;
	      }
	  case TPM_HT_NV_INDEX:
	    // An NV Index.
	      {
		  NV_INDEX* nvIndex      = NvGetIndexInfo(handle, NULL);
		  TPMA_NV   nvAttributes = nvIndex->publicArea.attributes;
		  //
		  // If the policy size is not zero, check if policy can be used.
		  if(nvIndex->publicArea.authPolicy.t.size != 0)
		      {
			  // If policy session is required for this handle, always
			  // uses policy regardless of the attributes bit setting
			  if(IsPolicySessionRequired(commandIndex, sessionIndex))
			      result = TRUE;
			  // Otherwise, the presence of the policy depends on the NV
			  // attributes.
			  else if(IsWriteOperation(commandIndex))
			      {
				  if(IS_ATTRIBUTE(nvAttributes, TPMA_NV, POLICYWRITE))
				      result = TRUE;
			      }
			  else
			      {
				  if(IS_ATTRIBUTE(nvAttributes, TPMA_NV, POLICYREAD))
				      result = TRUE;
			      }
		      }
	      }
	      break;
	  case TPM_HT_PCR:
	    // PCR handle.
	    if(PCRPolicyIsAvailable(handle))
		result = TRUE;
	    break;
	  default:
	    break;
	}
    return result;
}

//**  Session Parsing Functions

//*** ClearCpRpHashes()
void ClearCpRpHashes(COMMAND* command)
{
    // The macros expand according to the implemented hash algorithms. An IDE may
    // complain that COMMAND does not contain SHA1CpHash or SHA1RpHash because of the
    // complexity of the macro expansion where the data space is defined; but, if SHA1
    // is implemented, it actually does  and the compiler is happy.
#define CLEAR_CP_HASH(HASH, Hash) command->Hash##CpHash.b.size = 0;
    FOR_EACH_HASH(CLEAR_CP_HASH)
#define CLEAR_RP_HASH(HASH, Hash) command->Hash##RpHash.b.size = 0;
	FOR_EACH_HASH(CLEAR_RP_HASH)
	}

//*** GetCpHashPointer()
// Function to get a pointer to the cpHash of the command
static TPM2B_DIGEST* GetCpHashPointer(COMMAND* command, TPMI_ALG_HASH hashAlg)
{
    TPM2B_DIGEST* retVal;
    //
    // Define the macro that will expand for each implemented algorithm in the switch
    // statement below.
#define GET_CP_HASH_POINTER(HASH, Hash)				    \
    case ALG_##HASH##_VALUE:					    \
      retVal = (TPM2B_DIGEST*)&command->Hash##CpHash;		    \
      break;

    switch(hashAlg)
	{
	    // For each implemented hash, this will expand as defined above
	    // by GET_CP_HASH_POINTER. Your IDE may complain that
	    // 'struct "COMMAND" has no field "SHA1CpHash"' but the compiler says
	    // it does, so...
	    FOR_EACH_HASH(GET_CP_HASH_POINTER)
	  default:
	    retVal = NULL;
	    break;
	}
    return retVal;
}

//*** GetRpHashPointer()
// Function to get a pointer to the RpHash of the command
static TPM2B_DIGEST* GetRpHashPointer(COMMAND* command, TPMI_ALG_HASH hashAlg)
{
    TPM2B_DIGEST* retVal;
    //
    // Define the macro that will expand for each implemented algorithm in the switch
    // statement below.
#define GET_RP_HASH_POINTER(HASH, Hash)				    \
    case ALG_##HASH##_VALUE:					    \
      retVal = (TPM2B_DIGEST*)&command->Hash##RpHash;		    \
      break;

    switch(hashAlg)
	{
	    // For each implemented hash, this will expand as defined above
	    // by GET_RP_HASH_POINTER. Your IDE may complain that
	    // 'struct "COMMAND" has no field 'SHA1RpHash'" but the compiler says
	    // it does, so...
	    FOR_EACH_HASH(GET_RP_HASH_POINTER)
	  default:
	    retVal = NULL;
	    break;
	}
    return retVal;
}

//*** ComputeCpHash()
// This function computes the cpHash as defined in Part 2 and described in Part 1.
static TPM2B_DIGEST* ComputeCpHash(COMMAND* command,  // IN: command parsing structure
				   TPMI_ALG_HASH hashAlg  // IN: hash algorithm
				   )
{
    UINT32        i;
    HASH_STATE    hashState;
    TPM2B_NAME    name;
    TPM2B_DIGEST* cpHash;
    //
    // cpHash = hash(commandCode [ || authName1
    //                           [ || authName2
    //                           [ || authName 3 ]]]
    //                           [ || parameters])
    // A cpHash can contain just a commandCode only if the lone session is
    // an audit session.
    // Get pointer to the hash value
    cpHash = GetCpHashPointer(command, hashAlg);
    if(cpHash->t.size == 0)
	{
	    cpHash->t.size = CryptHashStart(&hashState, hashAlg);
	    //  Add commandCode.
	    CryptDigestUpdateInt(&hashState, sizeof(TPM_CC), command->code);
	    //  Add authNames for each of the handles.
	    for(i = 0; i < command->handleNum; i++)
		CryptDigestUpdate2B(&hashState,
				    &EntityGetName(command->handles[i], &name)->b);
	    //  Add the parameters.
	    CryptDigestUpdate(
			      &hashState, command->parameterSize, command->parameterBuffer);
	    //  Complete the hash.
	    CryptHashEnd2B(&hashState, &cpHash->b);
	}
    return cpHash;
}

//*** GetCpHash()
// This function is used to access a precomputed cpHash.
static TPM2B_DIGEST* GetCpHash(COMMAND* command, TPMI_ALG_HASH hashAlg)
{
    TPM2B_DIGEST* cpHash = GetCpHashPointer(command, hashAlg);
    //
    pAssert(cpHash->t.size != 0);
    return cpHash;
}

//*** CompareTemplateHash()
// This function computes the template hash and compares it to the session
// templateHash. It is the hash of the second parameter
// assuming that the command is TPM2_Create(), TPM2_CreatePrimary(), or
// TPM2_CreateLoaded()
//  Return Type: BOOL
//      TRUE(1)         template hash equal to session->templateHash
//      FALSE(0)        template hash not equal to session->templateHash
static BOOL CompareTemplateHash(COMMAND* command,  // IN: parsing structure
				SESSION* session   // IN: session data
				)
{
    BYTE*        pBuffer = command->parameterBuffer;
    INT32        pSize   = command->parameterSize;
    TPM2B_DIGEST tHash;
    UINT16       size;
    //
    // Only try this for the three commands for which it is intended
    if(command->code != TPM_CC_Create && command->code != TPM_CC_CreatePrimary
#if CC_CreateLoaded
       && command->code != TPM_CC_CreateLoaded
#endif
       )
	return FALSE;
    // Assume that the first parameter is a TPM2B and unmarshal the size field
    // Note: this will not affect the parameter buffer and size in the calling
    // function.
    if(UINT16_Unmarshal(&size, &pBuffer, &pSize) != TPM_RC_SUCCESS)
	return FALSE;
    // reduce the space in the buffer.
    // NOTE: this could make pSize go negative if the parameters are not correct but
    // the unmarshaling code does not try to unmarshal if the remaining size is
    // negative.
    pSize -= size;

    // Advance the pointer
    pBuffer += size;

    // Get the size of what should be the template
    if(UINT16_Unmarshal(&size, &pBuffer, &pSize) != TPM_RC_SUCCESS)
	return FALSE;
    // See if this is reasonable
    if(size > pSize)
	return FALSE;
    // Hash the template data
    tHash.t.size = CryptHashBlock(
				  session->authHashAlg, size, pBuffer, sizeof(tHash.t.buffer), tHash.t.buffer);
    return (MemoryEqual2B(&session->u1.templateHash.b, &tHash.b));
}

//*** CompareNameHash()
// This function computes the name hash and compares it to the nameHash in the
// session data, returning true if they are equal.
BOOL CompareNameHash(COMMAND* command,  // IN: main parsing structure
		     SESSION* session   // IN: session structure with nameHash
		     )
{
    HASH_STATE   hashState;
    TPM2B_DIGEST nameHash;
    UINT32       i;
    TPM2B_NAME   name;
    //
    nameHash.t.size = CryptHashStart(&hashState, session->authHashAlg);
    //  Add names.
    for(i = 0; i < command->handleNum; i++)
	CryptDigestUpdate2B(&hashState,
			    &EntityGetName(command->handles[i], &name)->b);
    //  Complete hash.
    CryptHashEnd2B(&hashState, &nameHash.b);
    // and compare
    return MemoryEqual(
		       session->u1.nameHash.t.buffer, nameHash.t.buffer, nameHash.t.size);
}

//*** CompareParametersHash()
// This function computes the parameters hash and compares it to the pHash in
// the session data, returning true if they are equal.
BOOL CompareParametersHash(COMMAND* command,  // IN: main parsing structure
			   SESSION* session   // IN: session structure with pHash
			   )
{
    HASH_STATE   hashState;
    TPM2B_DIGEST pHash;
    //
    pHash.t.size = CryptHashStart(&hashState, session->authHashAlg);
    //  Add commandCode.
    CryptDigestUpdateInt(&hashState, sizeof(TPM_CC), command->code);
    //  Add the parameters.
    CryptDigestUpdate(&hashState, command->parameterSize, command->parameterBuffer);
    //  Complete hash.
    CryptHashEnd2B(&hashState, &pHash.b);
    // and compare
    return MemoryEqual2B(&session->u1.pHash.b, &pHash.b);
}

//*** CheckPWAuthSession()
// This function validates the authorization provided in a PWAP session. It
// compares the input value to authValue of the authorized entity. Argument
// sessionIndex is used to get handles handle of the referenced entities from
// s_inputAuthValues[] and s_associatedHandles[].
//
//  Return Type: TPM_RC
//        TPM_RC_AUTH_FAIL          authorization fails and increments DA failure
//                                  count
//        TPM_RC_BAD_AUTH           authorization fails but DA does not apply
//
static TPM_RC CheckPWAuthSession(
				 UINT32 sessionIndex  // IN: index of session to be processed
				 )
{
    TPM2B_AUTH authValue;
    TPM_HANDLE associatedHandle = s_associatedHandles[sessionIndex];
    //
    // Strip trailing zeros from the password.
    MemoryRemoveTrailingZeros(&s_inputAuthValues[sessionIndex]);

    // Get the authValue with trailing zeros removed
    EntityGetAuthValue(associatedHandle, &authValue);

    // Success if the values are identical.
    if(MemoryEqual2B(&s_inputAuthValues[sessionIndex].b, &authValue.b))
	{
	    return TPM_RC_SUCCESS;
	}
    else  // if the digests are not identical
	{
	    // Invoke DA protection if applicable.
	    return IncrementLockout(sessionIndex);
	}
}

//*** ComputeCommandHMAC()
// This function computes the HMAC for an authorization session in a command.
/*(See part 1 specification -- this tag keeps this comment from showing up in
// merged document which is probably good because this comment doesn't look right.
//      The sessionAuth value
//      authHMAC := HMACsHash((sessionKey | authValue),
//                  (pHash | nonceNewer | nonceOlder  | nonceTPMencrypt-only
//                   | nonceTPMaudit   | sessionAttributes))
// Where:
//      HMACsHash()     The HMAC algorithm using the hash algorithm specified
//                      when the session was started.
//
//      sessionKey      A value that is computed in a protocol-dependent way,
//                      using KDFa. When used in an HMAC or KDF, the size field
//                      for this value is not included.
//
//      authValue       A value that is found in the sensitive area of an entity.
//                      When used in an HMAC or KDF, the size field for this
//                      value is not included.
//
//      pHash           Hash of the command (cpHash) using the session hash.
//                      When using a pHash in an HMAC computation, only the
//                      digest is used.
//
//      nonceNewer      A value that is generated by the entity using the
//                      session. A new nonce is generated on each use of the
//                      session. For a command, this will be nonceCaller.
//                      When used in an HMAC or KDF, the size field is not used.
//
//      nonceOlder      A TPM2B_NONCE that was received the previous time the
//                      session was used. For a command, this is nonceTPM.
//                      When used in an HMAC or KDF, the size field is not used.
//
//      nonceTPMdecrypt     The nonceTPM of the decrypt session is included in
//                          the HMAC, but only in the command.
//
//      nonceTPMencrypt     The nonceTPM of the encrypt session is included in
//                          the HMAC but only in the command.
//
//      sessionAttributes   A byte indicating the attributes associated with the
//                          particular use of the session.
*/
static TPM2B_DIGEST* ComputeCommandHMAC(
					COMMAND*      command,       // IN: primary control structure
					UINT32        sessionIndex,  // IN: index of session to be processed
					TPM2B_DIGEST* hmac           // OUT: authorization HMAC
					)
{
    TPM2B_TYPE(KEY, (sizeof(AUTH_VALUE) * 2));
    TPM2B_KEY    key;
    BYTE         marshalBuffer[sizeof(TPMA_SESSION)];
    BYTE*        buffer;
    UINT32       marshalSize;
    HMAC_STATE   hmacState;
    TPM2B_NONCE* nonceDecrypt;
    TPM2B_NONCE* nonceEncrypt;
    SESSION*     session;
    //
    nonceDecrypt = NULL;
    nonceEncrypt = NULL;

    // Determine if extra nonceTPM values are going to be required.
    // If this is the first session (sessionIndex = 0) and it is an authorization
    // session that uses an HMAC, then check if additional session nonces are to be
    // included.
    if(sessionIndex == 0 && s_associatedHandles[sessionIndex] != TPM_RH_UNASSIGNED)
	{
	    // If there is a decrypt session and if this is not the decrypt session,
	    // then an extra nonce may be needed.
	    if(s_decryptSessionIndex != UNDEFINED_INDEX
	       && s_decryptSessionIndex != sessionIndex)
		{
		    // Will add the nonce for the decrypt session.
		    SESSION* decryptSession =
			SessionGet(s_sessionHandles[s_decryptSessionIndex]);
		    nonceDecrypt = &decryptSession->nonceTPM;
		}
	    // Now repeat for the encrypt session.
	    if(s_encryptSessionIndex != UNDEFINED_INDEX
	       && s_encryptSessionIndex != sessionIndex
	       && s_encryptSessionIndex != s_decryptSessionIndex)
		{
		    // Have to have the nonce for the encrypt session.
		    SESSION* encryptSession =
			SessionGet(s_sessionHandles[s_encryptSessionIndex]);
		    nonceEncrypt = &encryptSession->nonceTPM;
		}
	}

    // Continue with the HMAC processing.
    session = SessionGet(s_sessionHandles[sessionIndex]);

    // Generate HMAC key.
    MemoryCopy2B(&key.b, &session->sessionKey.b, sizeof(key.t.buffer));

    // Check if the session has an associated handle and if the associated entity
    // is the one to which the session is bound. If not, add the authValue of
    // this entity to the HMAC key.
    // If the session is bound to the object or the session is a policy session
    // with no authValue required, do not include the authValue in the HMAC key.
    // Note: For a policy session, its isBound attribute is CLEARED.
    //
    // Include the entity authValue if it is needed
    if(session->attributes.includeAuth == SET)
	{
	    TPM2B_AUTH authValue;
	    // Get the entity authValue with trailing zeros removed
	    EntityGetAuthValue(s_associatedHandles[sessionIndex], &authValue);
	    // add the authValue to the HMAC key
	    MemoryConcat2B(&key.b, &authValue.b, sizeof(key.t.buffer));
	}
    // if the HMAC key size is 0, a NULL string HMAC is allowed
    if(key.t.size == 0 && s_inputAuthValues[sessionIndex].t.size == 0)
	{
	    hmac->t.size = 0;
	    return hmac;
	}
    // Start HMAC
    hmac->t.size = CryptHmacStart2B(&hmacState, session->authHashAlg, &key.b);

    //  Add cpHash
    CryptDigestUpdate2B(&hmacState.hashState,
			&ComputeCpHash(command, session->authHashAlg)->b);
    //  Add nonces as required
    CryptDigestUpdate2B(&hmacState.hashState, &s_nonceCaller[sessionIndex].b);
    CryptDigestUpdate2B(&hmacState.hashState, &session->nonceTPM.b);
    if(nonceDecrypt != NULL)
	CryptDigestUpdate2B(&hmacState.hashState, &nonceDecrypt->b);
    if(nonceEncrypt != NULL)
	CryptDigestUpdate2B(&hmacState.hashState, &nonceEncrypt->b);
    //  Add sessionAttributes
    buffer      = marshalBuffer;
    marshalSize = TPMA_SESSION_Marshal(&(s_attributes[sessionIndex]), &buffer, NULL);
    CryptDigestUpdate(&hmacState.hashState, marshalSize, marshalBuffer);
    // Complete the HMAC computation
    CryptHmacEnd2B(&hmacState, &hmac->b);

    return hmac;
}

//*** CheckSessionHMAC()
// This function checks the HMAC of in a session. It uses ComputeCommandHMAC()
// to compute the expected HMAC value and then compares the result with the
// HMAC in the authorization session. The authorization is successful if they
// are the same.
//
// If the authorizations are not the same, IncrementLockout() is called. It will
// return TPM_RC_AUTH_FAIL if the failure caused the failureCount to increment.
// Otherwise, it will return TPM_RC_BAD_AUTH.
//
//  Return Type: TPM_RC
//      TPM_RC_AUTH_FAIL        authorization failure caused failureCount increment
//      TPM_RC_BAD_AUTH         authorization failure did not cause failureCount
//                              increment
//
static TPM_RC CheckSessionHMAC(
			       COMMAND* command,      // IN: primary control structure
			       UINT32   sessionIndex  // IN: index of session to be processed
			       )
{
    TPM2B_DIGEST hmac;  // authHMAC for comparing
    //
    // Compute authHMAC
    ComputeCommandHMAC(command, sessionIndex, &hmac);

    // Compare the input HMAC with the authHMAC computed above.
    if(!MemoryEqual2B(&s_inputAuthValues[sessionIndex].b, &hmac.b))
	{
	    // If an HMAC session has a failure, invoke the anti-hammering
	    // if it applies to the authorized entity or the session.
	    // Otherwise, just indicate that the authorization is bad.
	    return IncrementLockout(sessionIndex);
	}
    return TPM_RC_SUCCESS;
}

//*** CheckPolicyAuthSession()
//  This function is used to validate the authorization in a policy session.
//  This function performs the following comparisons to see if a policy
//  authorization is properly provided. The check are:
//  a) compare policyDigest in session with authPolicy associated with
//     the entity to be authorized;
//  b) compare timeout if applicable;
//  c) compare commandCode if applicable;
//  d) compare cpHash if applicable; and
//  e) see if PCR values have changed since computed.
//
// If all the above checks succeed, the handle is authorized.
// The order of these comparisons is not important because any failure will
// result in the same error code.
//
//  Return Type: TPM_RC
//      TPM_RC_PCR_CHANGED          PCR value is not current
//      TPM_RC_POLICY_FAIL          policy session fails
//      TPM_RC_LOCALITY             command locality is not allowed
//      TPM_RC_POLICY_CC            CC doesn't match
//      TPM_RC_EXPIRED              policy session has expired
//      TPM_RC_PP                   PP is required but not asserted
//      TPM_RC_NV_UNAVAILABLE       NV is not available for write
//      TPM_RC_NV_RATE              NV is rate limiting
static TPM_RC CheckPolicyAuthSession(
				     COMMAND* command,      // IN: primary parsing structure
				     UINT32   sessionIndex  // IN: index of session to be processed
				     )
{
    SESSION*      session;
    TPM2B_DIGEST  authPolicy;
    TPMI_ALG_HASH policyAlg;
    UINT8         locality;
    //
    // Initialize pointer to the authorization session.
    session = SessionGet(s_sessionHandles[sessionIndex]);

    // If the command is TPM2_PolicySecret(), make sure that
    // either password or authValue is required
    if(command->code == TPM_CC_PolicySecret
       && session->attributes.isPasswordNeeded == CLEAR
       && session->attributes.isAuthValueNeeded == CLEAR)
	return TPM_RC_MODE;
    // See if the PCR counter for the session is still valid.
    if(!SessionPCRValueIsCurrent(session))
	return TPM_RC_PCR_CHANGED;
    // Get authPolicy.
    policyAlg = EntityGetAuthPolicy(s_associatedHandles[sessionIndex], &authPolicy);
    // Compare authPolicy.
    if(!MemoryEqual2B(&session->u2.policyDigest.b, &authPolicy.b))
	return TPM_RC_POLICY_FAIL;
    // Policy is OK so check if the other factors are correct

    // Compare policy hash algorithm.
    if(policyAlg != session->authHashAlg)
	return TPM_RC_POLICY_FAIL;

    // Compare timeout.
    if(session->timeout != 0)
	{
	    // Cannot compare time if clock stop advancing.  An TPM_RC_NV_UNAVAILABLE
	    // or TPM_RC_NV_RATE error may be returned here. This doesn't mean that
	    // a new nonce will be created just that, because TPM time can't advance
	    // we can't do time-based operations.
	    RETURN_IF_NV_IS_NOT_AVAILABLE;

	    if((session->timeout < g_time) || (session->epoch != g_timeEpoch))
		return TPM_RC_EXPIRED;
	}
    // If command code is provided it must match
    if(session->commandCode != 0)
	{
	    if(session->commandCode != command->code)
		return TPM_RC_POLICY_CC;
	}
    else
	{
	    // If command requires a DUP or ADMIN authorization, the session must have
	    // command code set.
	    AUTH_ROLE role = CommandAuthRole(command->index, sessionIndex);
	    if(role == AUTH_ADMIN || role == AUTH_DUP)
		return TPM_RC_POLICY_FAIL;
	}
    // Check command locality.
    {
	BYTE  sessionLocality[sizeof(TPMA_LOCALITY)];
	BYTE* buffer = sessionLocality;

	// Get existing locality setting in canonical form
	sessionLocality[0] = 0;
	TPMA_LOCALITY_Marshal(&session->commandLocality, &buffer, NULL);

	// See if the locality has been set
	if(sessionLocality[0] != 0)
	    {
		// If so, get the current locality
		locality = _plat__LocalityGet();
		if(locality < 5)
		    {
			if(((sessionLocality[0] & (1 << locality)) == 0)
			   || sessionLocality[0] > 31)
			    return TPM_RC_LOCALITY;
		    }
		else if(locality > 31)
		    {
			if(sessionLocality[0] != locality)
			    return TPM_RC_LOCALITY;
		    }
		else
		    {
			// Could throw an assert here but a locality error is just
			// as good. It just means that, whatever the locality is, it isn't
			// the locality requested so...
			return TPM_RC_LOCALITY;
		    }
	    }
    }  // end of locality check
    // Check physical presence.
    if(session->attributes.isPPRequired == SET && !_plat__PhysicalPresenceAsserted())
	return TPM_RC_PP;
    // Compare cpHash/nameHash/pHash/templateHash if defined.
    if(session->u1.cpHash.b.size != 0)
	{
	    BOOL OK = FALSE;
	    if(session->attributes.isCpHashDefined)
		// Compare cpHash.
		OK = MemoryEqual2B(&session->u1.cpHash.b,
				   &ComputeCpHash(command, session->authHashAlg)->b);
	    else if(g_RuntimeProfile.stateFormatLevel >= 4		// libtpms added
		    && session->attributes.isNameHashDefined)
		OK = CompareNameHash(command, session);
	    else if(session->attributes.isParametersHashDefined)
		OK = CompareParametersHash(command, session);
	    else if(session->attributes.isTemplateHashDefined)
		OK = CompareTemplateHash(command, session);
	    else if (g_RuntimeProfile.stateFormatLevel < 4)		// libtpms added: backwards compatibility
                OK = CompareNameHash(command, session);			// libtpms added: backwards compatibility
	    if(!OK)
		return TPM_RCS_POLICY_FAIL;
	}
    if(session->attributes.checkNvWritten)
	{
	    NV_REF    locator;
	    NV_INDEX* nvIndex;
	    //
	    // If this is not an NV index, the policy makes no sense so fail it.
	    if(HandleGetType(s_associatedHandles[sessionIndex]) != TPM_HT_NV_INDEX)
		return TPM_RC_POLICY_FAIL;
	    // Get the index data
	    nvIndex = NvGetIndexInfo(s_associatedHandles[sessionIndex], &locator);

	    // Make sure that the TPMA_WRITTEN_ATTRIBUTE has the desired state
	    if((IS_ATTRIBUTE(nvIndex->publicArea.attributes, TPMA_NV, WRITTEN))
	       != (session->attributes.nvWrittenState == SET))
		return TPM_RC_POLICY_FAIL;
	}
    return TPM_RC_SUCCESS;
}

//*** RetrieveSessionData()
// This function will unmarshal the sessions in the session area of a command. The
// values are placed in the arrays that are defined at the beginning of this file.
// The normal unmarshaling errors are possible.
//
//  Return Type: TPM_RC
//      TPM_RC_SUCCSS       unmarshaled without error
//      TPM_RC_SIZE         the number of bytes unmarshaled is not the same
//                          as the value for authorizationSize in the command
//
static TPM_RC RetrieveSessionData(
				  COMMAND* command  // IN: main parsing structure for command
				  )
{
    int          i;
    TPM_RC       result;
    SESSION*     session;
    TPMA_SESSION sessionAttributes;
    TPM_HT       sessionType;
    INT32        sessionIndex;
    TPM_RC       errorIndex;
    //
    s_decryptSessionIndex = UNDEFINED_INDEX;
    s_encryptSessionIndex = UNDEFINED_INDEX;
    s_auditSessionIndex   = UNDEFINED_INDEX;

    for(sessionIndex = 0; command->authSize > 0; sessionIndex++)
	{
	    errorIndex = TPM_RC_S + g_rcIndex[sessionIndex];

	    // If maximum allowed number of sessions has been parsed, return a size
	    // error with a session number that is larger than the number of allowed
	    // sessions
	    if(sessionIndex == MAX_SESSION_NUM)
		return TPM_RCS_SIZE + errorIndex;
	    // make sure that the associated handle for each session starts out
	    // unassigned
	    s_associatedHandles[sessionIndex] = TPM_RH_UNASSIGNED;

	    // First parameter: Session handle.
	    result = TPMI_SH_AUTH_SESSION_Unmarshal(&s_sessionHandles[sessionIndex],
						    &command->parameterBuffer,
						    &command->authSize,
						    TRUE);
	    if(result != TPM_RC_SUCCESS)
		return result + TPM_RC_S + g_rcIndex[sessionIndex];
	    // Second parameter: Nonce.
	    result = TPM2B_NONCE_Unmarshal(&s_nonceCaller[sessionIndex],
					   &command->parameterBuffer,
					   &command->authSize);
	    if(result != TPM_RC_SUCCESS)
		return result + TPM_RC_S + g_rcIndex[sessionIndex];
	    // Third parameter: sessionAttributes.
	    result = TPMA_SESSION_Unmarshal(&s_attributes[sessionIndex],
					    &command->parameterBuffer,
					    &command->authSize);
	    if(result != TPM_RC_SUCCESS)
		return result + TPM_RC_S + g_rcIndex[sessionIndex];
	    // Fourth parameter: authValue (PW or HMAC).
	    result = TPM2B_AUTH_Unmarshal(&s_inputAuthValues[sessionIndex],
					  &command->parameterBuffer,
					  &command->authSize);
	    if(result != TPM_RC_SUCCESS)
		return result + errorIndex;

	    sessionAttributes = s_attributes[sessionIndex];
	    if(s_sessionHandles[sessionIndex] == TPM_RS_PW)
		{
		    // A PWAP session needs additional processing.
		    //     Can't have any attributes set other than continueSession bit
		    if(IS_ATTRIBUTE(sessionAttributes, TPMA_SESSION, encrypt)
		       || IS_ATTRIBUTE(sessionAttributes, TPMA_SESSION, decrypt)
		       || IS_ATTRIBUTE(sessionAttributes, TPMA_SESSION, audit)
		       || IS_ATTRIBUTE(sessionAttributes, TPMA_SESSION, auditExclusive)
		       || IS_ATTRIBUTE(sessionAttributes, TPMA_SESSION, auditReset))
			return TPM_RCS_ATTRIBUTES + errorIndex;
		    //     The nonce size must be zero.
		    if(s_nonceCaller[sessionIndex].t.size != 0)
			return TPM_RCS_NONCE + errorIndex;
		    continue;
		}
	    // For not password sessions...
	    // Find out if the session is loaded.
	    if(!SessionIsLoaded(s_sessionHandles[sessionIndex]))
		return TPM_RC_REFERENCE_S0 + sessionIndex;
	    sessionType = HandleGetType(s_sessionHandles[sessionIndex]);
	    session     = SessionGet(s_sessionHandles[sessionIndex]);

	    // Check if the session is an HMAC/policy session.
	    if((session->attributes.isPolicy == SET && sessionType == TPM_HT_HMAC_SESSION)
	       || (session->attributes.isPolicy == CLEAR
		   && sessionType == TPM_HT_POLICY_SESSION))
		return TPM_RCS_HANDLE + errorIndex;
	    // Check that this handle has not previously been used.
	    for(i = 0; i < sessionIndex; i++)
		{
		    if(s_sessionHandles[i] == s_sessionHandles[sessionIndex])
			return TPM_RCS_HANDLE + errorIndex;
		}
	    // If the session is used for parameter encryption or audit as well, set
	    // the corresponding Indexes.

	    // First process decrypt.
	    if(IS_ATTRIBUTE(sessionAttributes, TPMA_SESSION, decrypt))
		{
		    // Check if the commandCode allows command parameter encryption.
		    if(DecryptSize(command->index) == 0)
			return TPM_RCS_ATTRIBUTES + errorIndex;
		    // Encrypt attribute can only appear in one session
		    if(s_decryptSessionIndex != UNDEFINED_INDEX)
			return TPM_RCS_ATTRIBUTES + errorIndex;
		    // Can't decrypt if the session's symmetric algorithm is TPM_ALG_NULL
		    if(session->symmetric.algorithm == TPM_ALG_NULL)
			return TPM_RCS_SYMMETRIC + errorIndex;
		    // All checks passed, so set the index for the session used to decrypt
		    // a command parameter.
		    s_decryptSessionIndex = sessionIndex;
		}
	    // Now process encrypt.
	    if(IS_ATTRIBUTE(sessionAttributes, TPMA_SESSION, encrypt))
		{
		    // Check if the commandCode allows response parameter encryption.
		    if(EncryptSize(command->index) == 0)
			return TPM_RCS_ATTRIBUTES + errorIndex;
		    // Encrypt attribute can only appear in one session.
		    if(s_encryptSessionIndex != UNDEFINED_INDEX)
			return TPM_RCS_ATTRIBUTES + errorIndex;
		    // Can't encrypt if the session's symmetric algorithm is TPM_ALG_NULL
		    if(session->symmetric.algorithm == TPM_ALG_NULL)
			return TPM_RCS_SYMMETRIC + errorIndex;
		    // All checks passed, so set the index for the session used to encrypt
		    // a response parameter.
		    s_encryptSessionIndex = sessionIndex;
		}
	    // At last process audit.
	    if(IS_ATTRIBUTE(sessionAttributes, TPMA_SESSION, audit))
		{
		    // Audit attribute can only appear in one session.
		    if(s_auditSessionIndex != UNDEFINED_INDEX)
			return TPM_RCS_ATTRIBUTES + errorIndex;
		    // An audit session can not be policy session.
		    if(HandleGetType(s_sessionHandles[sessionIndex]) == TPM_HT_POLICY_SESSION)
			return TPM_RCS_ATTRIBUTES + errorIndex;
		    // If this is a reset of the audit session, or the first use
		    // of the session as an audit session, it doesn't matter what
		    // the exclusive state is. The session will become exclusive.
		    if(!IS_ATTRIBUTE(sessionAttributes, TPMA_SESSION, auditReset)
		       && session->attributes.isAudit == SET)
			{
			    // Not first use or reset. If auditExlusive is SET, then this
			    // session must be the current exclusive session.
			    if(IS_ATTRIBUTE(sessionAttributes, TPMA_SESSION, auditExclusive)
			       && g_exclusiveAuditSession != s_sessionHandles[sessionIndex])
				return TPM_RC_EXCLUSIVE;
			}
		    s_auditSessionIndex = sessionIndex;
		}
	    // Initialize associated handle as undefined. This will be changed when
	    // the handles are processed.
	    s_associatedHandles[sessionIndex] = TPM_RH_UNASSIGNED;
	}
    command->sessionNum = sessionIndex;
    return TPM_RC_SUCCESS;
}

//*** CheckLockedOut()
// This function checks to see if the TPM is in lockout. This function should only
// be called if the entity being checked is subject to DA protection. The TPM
// is in lockout if the NV is not available and a DA write is pending. Otherwise
// the TPM is locked out if checking for lockoutAuth ('lockoutAuthCheck' == TRUE)
// and use of lockoutAuth is disabled, or 'failedTries' >= 'maxTries'
//  Return Type: TPM_RC
//      TPM_RC_NV_RATE          NV is rate limiting
//      TPM_RC_NV_UNAVAILABLE   NV is not available at this time
//      TPM_RC_LOCKOUT          TPM is in lockout
static TPM_RC CheckLockedOut(
			     BOOL lockoutAuthCheck  // IN: TRUE if checking is for lockoutAuth
			     )
{
    // If NV is unavailable, and current cycle state recorded in NV is not
    // SU_NONE_VALUE, refuse to check any authorization because we would
    // not be able to handle a DA failure.
    if(!NV_IS_AVAILABLE && NV_IS_ORDERLY)
	return g_NvStatus;
    // Check if DA info needs to be updated in NV.
    if(s_DAPendingOnNV)
	{
	    // If NV is accessible,
	    RETURN_IF_NV_IS_NOT_AVAILABLE;

	    // ... write the pending DA data and proceed.
	    NV_SYNC_PERSISTENT(lockOutAuthEnabled);
	    NV_SYNC_PERSISTENT(failedTries);
	    s_DAPendingOnNV = FALSE;
	}
    // Lockout is in effect if checking for lockoutAuth and use of lockoutAuth
    // is disabled...
    if(lockoutAuthCheck)
	{
	    if(gp.lockOutAuthEnabled == FALSE)
		return TPM_RC_LOCKOUT;
	}
    else
	{
	    // ... or if the number of failed tries has been maxed out.
	    if(gp.failedTries >= gp.maxTries)
		return TPM_RC_LOCKOUT;
#if USE_DA_USED
	    // If the daUsed flag is not SET, then no DA validation until the
	    // daUsed state is written to NV
	    if(!g_daUsed)
		{
		    RETURN_IF_NV_IS_NOT_AVAILABLE;
		    g_daUsed        = TRUE;
		    gp.orderlyState = SU_DA_USED_VALUE;
		    NV_SYNC_PERSISTENT(orderlyState);
		    return TPM_RC_SUCCESS; // libtpms changed: was TPM_RC_RETRY;
		}
#endif
	}
    return TPM_RC_SUCCESS;
}

//*** CheckAuthSession()
// This function checks that the authorization session properly authorizes the
// use of the associated handle.
//
//  Return Type: TPM_RC
//      TPM_RC_LOCKOUT              entity is protected by DA and TPM is in
//                                  lockout, or TPM is locked out on NV update
//                                  pending on DA parameters
//
//      TPM_RC_PP                   Physical Presence is required but not provided
//      TPM_RC_AUTH_FAIL            HMAC or PW authorization failed
//                                  with DA side-effects (can be a policy session)
//
//      TPM_RC_BAD_AUTH             HMAC or PW authorization failed without DA
//                                  side-effects (can be a policy session)
//
//      TPM_RC_POLICY_FAIL          if policy session fails
//      TPM_RC_POLICY_CC            command code of policy was wrong
//      TPM_RC_EXPIRED              the policy session has expired
//      TPM_RC_PCR
//      TPM_RC_AUTH_UNAVAILABLE     authValue or authPolicy unavailable
static TPM_RC CheckAuthSession(
			       COMMAND* command,      // IN: primary parsing structure
			       UINT32   sessionIndex  // IN: index of session to be processed
			       )
{
    TPM_RC     result            = TPM_RC_SUCCESS;
    SESSION*   session           = NULL;
    TPM_HANDLE sessionHandle     = s_sessionHandles[sessionIndex];
    TPM_HANDLE associatedHandle  = s_associatedHandles[sessionIndex];
    TPM_HT     sessionHandleType = HandleGetType(sessionHandle);
    BOOL       authUsed;
    //
    pAssert(sessionHandle != TPM_RH_UNASSIGNED);

    // Take care of physical presence
    if(associatedHandle == TPM_RH_PLATFORM)
	{
	    // If the physical presence is required for this command, check for PP
	    // assertion. If it isn't asserted, no point going any further.
	    if(PhysicalPresenceIsRequired(command->index)
	       && !_plat__PhysicalPresenceAsserted())
		return TPM_RC_PP;
	}
    if(sessionHandle != TPM_RS_PW)
	{
	    session = SessionGet(sessionHandle);

	    // Set includeAuth to indicate if DA checking will be required and if the
	    // authValue will be included in any HMAC.
	    if(sessionHandleType == TPM_HT_POLICY_SESSION)
		{
		    // For a policy session, will check the DA status of the entity if either
		    // isAuthValueNeeded or isPasswordNeeded is SET.
		    session->attributes.includeAuth = session->attributes.isAuthValueNeeded
						      || session->attributes.isPasswordNeeded;
		}
	    else
		{
		    // For an HMAC session, need to check unless the session
		    // is bound.
		    session->attributes.includeAuth =
			!IsSessionBindEntity(s_associatedHandles[sessionIndex], session);
		}
	    authUsed = session->attributes.includeAuth;
	}
    else
	// Password session
	authUsed = TRUE;
    // If the authorization session is going to use an authValue, then make sure
    // that access to that authValue isn't locked out.
    if(authUsed)
	{
	    // See if entity is subject to lockout.
	    if(!IsDAExempted(associatedHandle))
		{
		    // See if in lockout
		    result = CheckLockedOut(associatedHandle == TPM_RH_LOCKOUT);
		    if(result != TPM_RC_SUCCESS)
			return result;
		}
	}
    // Policy or HMAC+PW?
    if(sessionHandleType != TPM_HT_POLICY_SESSION)
	{
	    // for non-policy session make sure that a policy session is not required
	    if(IsPolicySessionRequired(command->index, sessionIndex))
		return TPM_RC_AUTH_TYPE;
	    // The authValue must be available.
	    // Note: The authValue is going to be "used" even if it is an EmptyAuth.
	    // and the session is bound.
	    if(!IsAuthValueAvailable(associatedHandle, command->index, sessionIndex))
		return TPM_RC_AUTH_UNAVAILABLE;
	}
    else
	{
	    // ... see if the entity has a policy, ...
	    // Note: IsAuthPolicyAvalable will return FALSE if the sensitive area of the
	    // object is not loaded
	    if(!IsAuthPolicyAvailable(associatedHandle, command->index, sessionIndex))
		return TPM_RC_AUTH_UNAVAILABLE;
	    // ... and check the policy session.
	    result = CheckPolicyAuthSession(command, sessionIndex);
	    if(result != TPM_RC_SUCCESS)
		return result;
	}
    // Check authorization according to the type
    if((TPM_RS_PW == sessionHandle) || (session->attributes.isPasswordNeeded == SET))
	result = CheckPWAuthSession(sessionIndex);
    else
	result = CheckSessionHMAC(command, sessionIndex);
    // Do processing for PIN Indexes are only three possibilities for 'result' at
    // this point: TPM_RC_SUCCESS, TPM_RC_AUTH_FAIL, and TPM_RC_BAD_AUTH.
    // For all these cases, we would have to process a PIN index if the
    // authValue of the index was used for authorization.
    if((TPM_HT_NV_INDEX == HandleGetType(associatedHandle)) && authUsed)
	{
	    NV_REF    locator;
	    NV_INDEX* nvIndex = NvGetIndexInfo(associatedHandle, &locator);
	    NV_PIN    pinData;
	    TPMA_NV   nvAttributes;
	    //
	    pAssert(nvIndex != NULL);
	    nvAttributes = nvIndex->publicArea.attributes;
	    // If this is a PIN FAIL index and the value has been written
	    // then we can update the counter (increment or clear)
	    if(IsNvPinFailIndex(nvAttributes)
	       && IS_ATTRIBUTE(nvAttributes, TPMA_NV, WRITTEN))
		{
		    pinData.intVal = NvGetUINT64Data(nvIndex, locator);
		    if(result != TPM_RC_SUCCESS)
			pinData.pin.pinCount++;
		    else
			pinData.pin.pinCount = 0;
		    NvWriteUINT64Data(nvIndex, pinData.intVal);
		}
	    // If this is a PIN PASS Index, increment if we have used the
	    // authorization value.
	    // NOTE: If the counter has already hit the limit, then we
	    // would not get here because the authorization value would not
	    // be available and the TPM would have returned before it gets here
	    else if(IsNvPinPassIndex(nvAttributes)
		    && IS_ATTRIBUTE(nvAttributes, TPMA_NV, WRITTEN)
		    && result == TPM_RC_SUCCESS)
		{
		    // If the access is valid, then increment the use counter
		    pinData.intVal = NvGetUINT64Data(nvIndex, locator);
		    pinData.pin.pinCount++;
		    NvWriteUINT64Data(nvIndex, pinData.intVal);
		}
	}
    return result;
}

#if CC_GetCommandAuditDigest
//*** CheckCommandAudit()
// This function is called before the command is processed if audit is enabled
// for the command. It will check to see if the audit can be performed and
// will ensure that the cpHash is available for the audit.
//  Return Type: TPM_RC
//      TPM_RC_NV_UNAVAILABLE       NV is not available for write
//      TPM_RC_NV_RATE              NV is rate limiting
static TPM_RC CheckCommandAudit(COMMAND* command)
{
    // If the audit digest is clear and command audit is required, NV must be
    // available so that TPM2_GetCommandAuditDigest() is able to increment
    // audit counter. If NV is not available, the function bails out to prevent
    // the TPM from attempting an operation that would fail anyway.
    if(gr.commandAuditDigest.t.size == 0
       || GetCommandCode(command->index) == TPM_CC_GetCommandAuditDigest)
	{
	    RETURN_IF_NV_IS_NOT_AVAILABLE;
	}
    // Make sure that the cpHash is computed for the algorithm
    ComputeCpHash(command, gp.auditHashAlg);
    return TPM_RC_SUCCESS;
}
#endif

//*** ParseSessionBuffer()
// This function is the entry function for command session processing.
// It iterates sessions in session area and reports if the required authorization
// has been properly provided. It also processes audit session and passes the
// information of encryption sessions to parameter encryption module.
//
//  Return Type: TPM_RC
//        various           parsing failure or authorization failure
//
TPM_RC
ParseSessionBuffer(COMMAND* command  // IN: the structure that contains
		   )
{
    TPM_RC     result;
    UINT32     i;
    INT32      size = 0;
    TPM2B_AUTH extraKey;
    UINT32     sessionIndex;
    TPM_RC     errorIndex;
    SESSION*   session = NULL;
    //
    // Check if a command allows any session in its session area.
    if(!IsSessionAllowed(command->index))
	return TPM_RC_AUTH_CONTEXT;
    // Default-initialization.
    command->sessionNum = 0;

    result              = RetrieveSessionData(command);
    if(result != TPM_RC_SUCCESS)
	return result;
    // There is no command in the TPM spec that has more handles than
    // MAX_SESSION_NUM.
    pAssert(command->handleNum <= MAX_SESSION_NUM);

    // Associate the session with an authorization handle.
    for(i = 0; i < command->handleNum; i++)
	{
	    if(CommandAuthRole(command->index, i) != AUTH_NONE)
		{
		    // If the received session number is less than the number of handles
		    // that requires authorization, an error should be returned.
		    // Note: for all the TPM 2.0 commands, handles requiring
		    // authorization come first in a command input and there are only ever
		    // two values requiring authorization
		    if(command->sessionNum == 0)		// libtpms added begin (Coverity 1550499)
		        return TPM_RC_AUTH_MISSING;		// libtpms added end
		    if(i > (command->sessionNum - 1))
			return TPM_RC_AUTH_MISSING;
		    // Record the handle associated with the authorization session
		    s_associatedHandles[i] = HierarchyNormalizeHandle(command->handles[i]);
		}
	}
    // Consistency checks are done first to avoid authorization failure when the
    // command will not be executed anyway.
    for(sessionIndex = 0; sessionIndex < command->sessionNum; sessionIndex++)
	{
	    errorIndex = TPM_RC_S + g_rcIndex[sessionIndex];
	    // PW session must be an authorization session
	    if(s_sessionHandles[sessionIndex] == TPM_RS_PW)
		{
		    if(s_associatedHandles[sessionIndex] == TPM_RH_UNASSIGNED)
			return TPM_RCS_HANDLE + errorIndex;
		    // a password session can't be audit, encrypt or decrypt
		    if(IS_ATTRIBUTE(s_attributes[sessionIndex], TPMA_SESSION, audit)
		       || IS_ATTRIBUTE(s_attributes[sessionIndex], TPMA_SESSION, encrypt)
		       || IS_ATTRIBUTE(s_attributes[sessionIndex], TPMA_SESSION, decrypt))
			return TPM_RCS_ATTRIBUTES + errorIndex;
		    session = NULL;
		}
	    else
		{
		    session = SessionGet(s_sessionHandles[sessionIndex]);

		    // A trial session can not appear in session area, because it cannot
		    // be used for authorization, audit or encrypt/decrypt.
		    if(session->attributes.isTrialPolicy == SET)
			return TPM_RCS_ATTRIBUTES + errorIndex;

		    // See if the session is bound to a DA protected entity
		    // NOTE: Since a policy session is never bound, a policy is still
		    // usable even if the object is DA protected and the TPM is in
		    // lockout.
		    if(session->attributes.isDaBound == SET)
			{
			    result = CheckLockedOut(session->attributes.isLockoutBound == SET);
			    if(result != TPM_RC_SUCCESS)
				return result;
			}
		    // If this session is for auditing, make sure the cpHash is computed.
		    if(IS_ATTRIBUTE(s_attributes[sessionIndex], TPMA_SESSION, audit))
			ComputeCpHash(command, session->authHashAlg);
		}

	    // if the session has an associated handle, check the authorization
	    if(s_associatedHandles[sessionIndex] != TPM_RH_UNASSIGNED)
		{
		    result = CheckAuthSession(command, sessionIndex);
		    if(result != TPM_RC_SUCCESS)
			return RcSafeAddToResult(result, errorIndex);
		}
	    else
		{
		    // a session that is not for authorization must either be encrypt,
		    // decrypt, or audit
		    if(!IS_ATTRIBUTE(s_attributes[sessionIndex], TPMA_SESSION, audit)
		       && !IS_ATTRIBUTE(s_attributes[sessionIndex], TPMA_SESSION, encrypt)
		       && !IS_ATTRIBUTE(s_attributes[sessionIndex], TPMA_SESSION, decrypt))
			return TPM_RCS_ATTRIBUTES + errorIndex;

		    // no authValue included in any of the HMAC computations
		    pAssert(session != NULL);
		    session->attributes.includeAuth = CLEAR;

		    // check HMAC for encrypt/decrypt/audit only sessions
		    result = CheckSessionHMAC(command, sessionIndex);
		    if(result != TPM_RC_SUCCESS)
			return RcSafeAddToResult(result, errorIndex);
		}
	}
#if CC_GetCommandAuditDigest
    // Check if the command should be audited. Need to do this before any parameter
    // encryption so that the cpHash for the audit is correct
    if(CommandAuditIsRequired(command->index))
	{
	    result = CheckCommandAudit(command);
	    if(result != TPM_RC_SUCCESS)
		return result;  // No session number to reference
	}
#endif
    // Decrypt the first parameter if applicable. This should be the last operation
    // in session processing.
    // If the encrypt session is associated with a handle and the handle's
    // authValue is available, then authValue is concatenated with sessionKey to
    // generate encryption key, no matter if the handle is the session bound entity
    // or not.
    if(s_decryptSessionIndex != UNDEFINED_INDEX)
	{
	    // If this is an authorization session, include the authValue in the
	    // generation of the decryption key
	    if(s_associatedHandles[s_decryptSessionIndex] != TPM_RH_UNASSIGNED)
		{
		    EntityGetAuthValue(s_associatedHandles[s_decryptSessionIndex], &extraKey);
		}
	    else
		{
		    extraKey.b.size = 0;
		}
	    size   = DecryptSize(command->index);
	    result = CryptParameterDecryption(s_sessionHandles[s_decryptSessionIndex],
					      &s_nonceCaller[s_decryptSessionIndex].b,
					      command->parameterSize,
					      (UINT16)size,
					      &extraKey,
					      command->parameterBuffer);
	    if(result != TPM_RC_SUCCESS)
		return RcSafeAddToResult(result,
					 TPM_RC_S + g_rcIndex[s_decryptSessionIndex]);
	}

    return TPM_RC_SUCCESS;
}

//*** CheckAuthNoSession()
// Function to process a command with no session associated.
// The function makes sure all the handles in the command require no authorization.
//
//  Return Type: TPM_RC
//      TPM_RC_AUTH_MISSING         failure - one or more handles require
//                                  authorization
TPM_RC
CheckAuthNoSession(COMMAND* command  // IN: command parsing structure
		   )
{
    UINT32 i;
#if CC_GetCommandAuditDigest
    TPM_RC result = TPM_RC_SUCCESS;
#endif
    //
    // Check if the command requires authorization
    for(i = 0; i < command->handleNum; i++)
	{
	    if(CommandAuthRole(command->index, i) != AUTH_NONE)
		return TPM_RC_AUTH_MISSING;
	}
#if CC_GetCommandAuditDigest
    // Check if the command should be audited.
    if(CommandAuditIsRequired(command->index))
	{
	    result = CheckCommandAudit(command);
	    if(result != TPM_RC_SUCCESS)
		return result;
	}
#endif
    // Initialize number of sessions to be 0
    command->sessionNum = 0;

    return TPM_RC_SUCCESS;
}

//** Response Session Processing
//*** Introduction
//
//  The following functions build the session area in a response and handle
//  the audit sessions (if present).
//

//*** ComputeRpHash()
// Function to compute rpHash (Response Parameter Hash). The rpHash is only
// computed if there is an HMAC authorization session and the return code is
// TPM_RC_SUCCESS.
static TPM2B_DIGEST* ComputeRpHash(
				   COMMAND*   command,  // IN: command structure
				   TPM_ALG_ID hashAlg   // IN: hash algorithm to compute rpHash
				   )
{
    TPM2B_DIGEST* rpHash = GetRpHashPointer(command, hashAlg);
    HASH_STATE    hashState;
    //
    if(rpHash->t.size == 0)
	{
	    //   rpHash := hash(responseCode || commandCode || parameters)

	    // Initiate hash creation.
	    rpHash->t.size = CryptHashStart(&hashState, hashAlg);

	    // Add hash constituents.
	    CryptDigestUpdateInt(&hashState, sizeof(TPM_RC), TPM_RC_SUCCESS);
	    CryptDigestUpdateInt(&hashState, sizeof(TPM_CC), command->code);
	    CryptDigestUpdate(
			      &hashState, command->parameterSize, command->parameterBuffer);
	    // Complete hash computation.
	    CryptHashEnd2B(&hashState, &rpHash->b);
	}
    return rpHash;
}

//*** InitAuditSession()
// This function initializes the audit data in an audit session.
static void InitAuditSession(SESSION* session  // session to be initialized
			     )
{
    // Mark session as an audit session.
    session->attributes.isAudit = SET;

    // Audit session can not be bound.
    session->attributes.isBound = CLEAR;

    // Size of the audit log is the size of session hash algorithm digest.
    session->u2.auditDigest.t.size = CryptHashGetDigestSize(session->authHashAlg);

    // Set the original digest value to be 0.
    MemorySet(&session->u2.auditDigest.t.buffer, 0, session->u2.auditDigest.t.size);
    return;
}

//*** UpdateAuditDigest
// Function to update an audit digest
static void UpdateAuditDigest(
			      COMMAND* command, TPMI_ALG_HASH hashAlg, TPM2B_DIGEST* digest)
{
    HASH_STATE    hashState;
    TPM2B_DIGEST* cpHash = GetCpHash(command, hashAlg);
    TPM2B_DIGEST* rpHash = ComputeRpHash(command, hashAlg);
    //
    pAssert(cpHash != NULL);

    // digestNew :=  hash (digestOld || cpHash || rpHash)
    // Start hash computation.
    digest->t.size = CryptHashStart(&hashState, hashAlg);
    // Add old digest.
    CryptDigestUpdate2B(&hashState, &digest->b);
    // Add cpHash
    CryptDigestUpdate2B(&hashState, &cpHash->b);
    // Add rpHash
    CryptDigestUpdate2B(&hashState, &rpHash->b);
    // Finalize the hash.
    CryptHashEnd2B(&hashState, &digest->b);
}

//*** Audit()
//This function updates the audit digest in an audit session.
static void Audit(COMMAND* command,      // IN: primary control structure
		  SESSION* auditSession  // IN: loaded audit session
		  )
{
    UpdateAuditDigest(
		      command, auditSession->authHashAlg, &auditSession->u2.auditDigest);
    return;
}

#if CC_GetCommandAuditDigest
//*** CommandAudit()
// This function updates the command audit digest.
static void CommandAudit(COMMAND* command  // IN:
			 )
{
    // If the digest.size is one, it indicates the special case of changing
    // the audit hash algorithm. For this case, no audit is done on exit.
    // NOTE: When the hash algorithm is changed, g_updateNV is set in order to
    // force an update to the NV on exit so that the change in digest will
    // be recorded. So, it is safe to exit here without setting any flags
    // because the digest change will be written to NV when this code exits.
    if(gr.commandAuditDigest.t.size == 1)
	{
	    gr.commandAuditDigest.t.size = 0;
	    return;
	}
    // If the digest size is zero, need to start a new digest and increment
    // the audit counter.
    if(gr.commandAuditDigest.t.size == 0)
	{
	    gr.commandAuditDigest.t.size = CryptHashGetDigestSize(gp.auditHashAlg);
	    MemorySet(gr.commandAuditDigest.t.buffer, 0, gr.commandAuditDigest.t.size);

	    // Bump the counter and save its value to NV.
	    gp.auditCounter++;
	    NV_SYNC_PERSISTENT(auditCounter);
	}
    UpdateAuditDigest(command, gp.auditHashAlg, &gr.commandAuditDigest);
    return;
}
#endif

//*** UpdateAuditSessionStatus()
// This function updates the internal audit related states of a session. It will:
//  a) initialize the session as audit session and set it to be exclusive if this
//     is the first time it is used for audit or audit reset was requested;
//  b) report exclusive audit session;
//  c) extend audit log; and
//  d) clear exclusive audit session if no audit session found in the command.
static void UpdateAuditSessionStatus(
				     COMMAND* command  // IN: primary control structure
				     )
{
    UINT32     i;
    TPM_HANDLE auditSession = TPM_RH_UNASSIGNED;
    //
    // Iterate through sessions
    for(i = 0; i < command->sessionNum; i++)
	{
	    SESSION* session;
	    //
	    // PW session do not have a loaded session and can not be an audit
	    // session either.  Skip it.
	    if(s_sessionHandles[i] == TPM_RS_PW)
		continue;
	    session = SessionGet(s_sessionHandles[i]);

	    // If a session is used for audit
	    if(IS_ATTRIBUTE(s_attributes[i], TPMA_SESSION, audit))
		{
		    // An audit session has been found
		    auditSession = s_sessionHandles[i];

		    // If the session has not been an audit session yet, or
		    // the auditSetting bits indicate a reset, initialize it and set
		    // it to be the exclusive session
		    if(session->attributes.isAudit == CLEAR
		       || IS_ATTRIBUTE(s_attributes[i], TPMA_SESSION, auditReset))
			{
			    InitAuditSession(session);
			    g_exclusiveAuditSession = auditSession;
			}
		    else
			{
			    // Check if the audit session is the current exclusive audit
			    // session and, if not, clear previous exclusive audit session.
			    if(g_exclusiveAuditSession != auditSession)
				g_exclusiveAuditSession = TPM_RH_UNASSIGNED;
			}
		    // Report audit session exclusivity.
		    if(g_exclusiveAuditSession == auditSession)
			{
			    SET_ATTRIBUTE(s_attributes[i], TPMA_SESSION, auditExclusive);
			}
		    else
			{
			    CLEAR_ATTRIBUTE(s_attributes[i], TPMA_SESSION, auditExclusive);
			}
		    // Extend audit log.
		    Audit(command, session);
		}
	}
    // If no audit session is found in the command, and the command allows
    // a session then, clear the current exclusive
    // audit session.
    if(auditSession == TPM_RH_UNASSIGNED && IsSessionAllowed(command->index))
	{
	    g_exclusiveAuditSession = TPM_RH_UNASSIGNED;
	}
    return;
}

//*** ComputeResponseHMAC()
// Function to compute HMAC for authorization session in a response.
/*(See part 1 specification)
// Function: Compute HMAC for response sessions
//      The sessionAuth value
//          authHMAC := HMACsHASH((sessionAuth | authValue),
//                    (pHash | nonceTPM | nonceCaller | sessionAttributes))
//  Where:
//      HMACsHASH()     The HMAC algorithm using the hash algorithm specified when
//                      the session was started.
//
//      sessionAuth     A TPMB_MEDIUM computed in a protocol-dependent way, using
//                      KDFa. In an HMAC or KDF, only sessionAuth.buffer is used.
//
//      authValue       A TPM2B_AUTH that is found in the sensitive area of an
//                      object. In an HMAC or KDF, only authValue.buffer is used
//                      and all trailing zeros are removed.
//
//      pHash           Response parameters (rpHash) using the session hash. When
//                      using a pHash in an HMAC computation, both the algorithm ID
//                      and the digest are included.
//
//      nonceTPM        A TPM2B_NONCE that is generated by the entity using the
//                      session. In an HMAC or KDF, only nonceTPM.buffer is used.
//
//      nonceCaller     a TPM2B_NONCE that was received the previous time the
//                      session was used. In an HMAC or KDF, only
//                      nonceCaller.buffer is used.
//
//      sessionAttributes   A TPMA_SESSION that indicates the attributes associated
//                          with a particular use of the session.
*/
static void ComputeResponseHMAC(
				COMMAND*      command,       // IN: command structure
				UINT32        sessionIndex,  // IN: session index to be processed
				SESSION*      session,       // IN: loaded session
				TPM2B_DIGEST* hmac           // OUT: authHMAC
				)
{
    TPM2B_TYPE(KEY, (sizeof(AUTH_VALUE) * 2));
    TPM2B_KEY     key;  // HMAC key
    BYTE          marshalBuffer[sizeof(TPMA_SESSION)];
    BYTE*         buffer;
    UINT32        marshalSize;
    HMAC_STATE    hmacState;
    TPM2B_DIGEST* rpHash = ComputeRpHash(command, session->authHashAlg);
    //
    // Generate HMAC key
    MemoryCopy2B(&key.b, &session->sessionKey.b, sizeof(key.t.buffer));

    // Add the object authValue if required
    if(session->attributes.includeAuth == SET)
	{
	    // Note: includeAuth may be SET for a policy that is used in
	    // UndefineSpaceSpecial(). At this point, the Index has been deleted
	    // so the includeAuth will have no meaning. However, the
	    // s_associatedHandles[] value for the session is now set to TPM_RH_NULL so
	    // this will return the authValue associated with TPM_RH_NULL and that is
	    // and empty buffer.
	    TPM2B_AUTH authValue;
	    //
	    // Get the authValue with trailing zeros removed
	    EntityGetAuthValue(s_associatedHandles[sessionIndex], &authValue);

	    // Add it to the key
	    MemoryConcat2B(&key.b, &authValue.b, sizeof(key.t.buffer));
	}

    // if the HMAC key size is 0, the response HMAC is computed according to the
    // input HMAC
    if(key.t.size == 0 && s_inputAuthValues[sessionIndex].t.size == 0)
	{
	    hmac->t.size = 0;
	    return;
	}
    // Start HMAC computation.
    hmac->t.size = CryptHmacStart2B(&hmacState, session->authHashAlg, &key.b);

    // Add hash components.
    CryptDigestUpdate2B(&hmacState.hashState, &rpHash->b);
    CryptDigestUpdate2B(&hmacState.hashState, &session->nonceTPM.b);
    CryptDigestUpdate2B(&hmacState.hashState, &s_nonceCaller[sessionIndex].b);

    // Add session attributes.
    buffer      = marshalBuffer;
    marshalSize = TPMA_SESSION_Marshal(&s_attributes[sessionIndex], &buffer, NULL);
    CryptDigestUpdate(&hmacState.hashState, marshalSize, marshalBuffer);

    // Finalize HMAC.
    CryptHmacEnd2B(&hmacState, &hmac->b);

    return;
}

//*** UpdateInternalSession()
// This function updates internal sessions by:
// a) restarting session time; and
// b) clearing a policy session since nonce is rolling.
static void UpdateInternalSession(SESSION* session,  // IN: the session structure
				  UINT32   i         // IN: session number
				  )
{
    // If nonce is rolling in a policy session, the policy related data
    // will be re-initialized.
    if(HandleGetType(s_sessionHandles[i]) == TPM_HT_POLICY_SESSION
       && IS_ATTRIBUTE(s_attributes[i], TPMA_SESSION, continueSession))
	{
	    // When the nonce rolls it starts a new timing interval for the
	    // policy session.
	    SessionResetPolicyData(session);
	    SessionSetStartTime(session);
	}
    return;
}

//*** BuildSingleResponseAuth()
//   Function to compute response HMAC value for a policy or HMAC session.
static TPM2B_NONCE* BuildSingleResponseAuth(
					    COMMAND*    command,       // IN: command structure
					    UINT32      sessionIndex,  // IN: session index to be processed
					    TPM2B_AUTH* auth           // OUT: authHMAC
					    )
{
    // Fill in policy/HMAC based session response.
    SESSION* session = SessionGet(s_sessionHandles[sessionIndex]);
    //
    // If the session is a policy session with isPasswordNeeded SET, the
    // authorization field is empty.
    if(HandleGetType(s_sessionHandles[sessionIndex]) == TPM_HT_POLICY_SESSION
       && session->attributes.isPasswordNeeded == SET)
	auth->t.size = 0;
    else
	// Compute response HMAC.
	ComputeResponseHMAC(command, sessionIndex, session, auth);

    UpdateInternalSession(session, sessionIndex);
    return &session->nonceTPM;
}

//*** UpdateAllNonceTPM()
// Updates TPM nonce for all sessions in command.
static void UpdateAllNonceTPM(COMMAND* command  // IN: controlling structure
			      )
{
    UINT32   i;
    SESSION* session;
    //
    for(i = 0; i < command->sessionNum; i++)
	{
	    // If not a PW session, compute the new nonceTPM.
	    if(s_sessionHandles[i] != TPM_RS_PW)
		{
		    session = SessionGet(s_sessionHandles[i]);
		    // Update nonceTPM in both internal session and response.
		    CryptRandomGenerate(session->nonceTPM.t.size, session->nonceTPM.t.buffer);
		}
	}
    return;
}

//*** BuildResponseSession()
// Function to build Session buffer in a response. The authorization data is added
// to the end of command->responseBuffer. The size of the authorization area is
// accumulated in command->authSize.
// When this is called, command->responseBuffer is pointing at the next location
// in the response buffer to be filled. This is where the authorization sessions
// will go, if any. command->parameterSize is the number of bytes that have been
// marshaled as parameters in the output buffer.
TPM_RC
BuildResponseSession(COMMAND* command  // IN: structure that has relevant command
		     //     information
		     )
{
    TPM_RC result = TPM_RC_SUCCESS;

    pAssert(command->authSize == 0);

    // Reset the parameter buffer to point to the start of the parameters so that
    // there is a starting point for any rpHash that might be generated and so there
    // is a place where parameter encryption would start
    command->parameterBuffer = command->responseBuffer - command->parameterSize;

    // Session nonces should be updated before parameter encryption
    if(command->tag == TPM_ST_SESSIONS)
	{
	    UpdateAllNonceTPM(command);

	    // Encrypt first parameter if applicable. Parameter encryption should
	    // happen after nonce update and before any rpHash is computed.
	    // If the encrypt session is associated with a handle, the authValue of
	    // this handle will be concatenated with sessionKey to generate
	    // encryption key, no matter if the handle is the session bound entity
	    // or not. The authValue is added to sessionKey only when the authValue
	    // is available.
	    if(s_encryptSessionIndex != UNDEFINED_INDEX)
		{
		    UINT32     size;
		    TPM2B_AUTH extraKey;
		    //
		    extraKey.b.size = 0;
		    // If this is an authorization session, include the authValue in the
		    // generation of the encryption key
		    if(s_associatedHandles[s_encryptSessionIndex] != TPM_RH_UNASSIGNED)
			{
			    EntityGetAuthValue(s_associatedHandles[s_encryptSessionIndex],
					       &extraKey);
			}
		    size = EncryptSize(command->index);
		    // This function operates on internally-generated data that is
		    // expected to be well-formed for parameter encryption.
		    // In the event that there is a bug elsewhere in the code and the
		    // input data is not well-formed, CryptParameterEncryption will
		    // put the TPM into failure mode instead of allowing the out-of-
		    // band write.
		    CryptParameterEncryption(s_sessionHandles[s_encryptSessionIndex],
					     &s_nonceCaller[s_encryptSessionIndex].b,
					     command->parameterSize,
					     (UINT16)size,
					     &extraKey,
					     command->parameterBuffer);
		    if(g_inFailureMode)
			{
			    result = TPM_RC_FAILURE;
			    goto Cleanup;
			}
		}
	}
    // Audit sessions should be processed regardless of the tag because
    // a command with no session may cause a change of the exclusivity state.
    UpdateAuditSessionStatus(command);
#if CC_GetCommandAuditDigest
    // Command Audit
    if(CommandAuditIsRequired(command->index))
	CommandAudit(command);
#endif
    // Process command with sessions.
    if(command->tag == TPM_ST_SESSIONS)
	{
	    UINT32 i;
	    //
	    pAssert(command->sessionNum > 0);

	    // Iterate over each session in the command session area, and create
	    // corresponding sessions for response.
	    for(i = 0; i < command->sessionNum; i++)
		{
		    TPM2B_NONCE* nonceTPM;
		    TPM2B_DIGEST responseAuth;
		    // Make sure that continueSession is SET on any Password session.
		    // This makes it marginally easier for the management software
		    // to keep track of the closed sessions.
		    if(s_sessionHandles[i] == TPM_RS_PW)
			{
			    SET_ATTRIBUTE(s_attributes[i], TPMA_SESSION, continueSession);
			    responseAuth.t.size = 0;
			    nonceTPM            = (TPM2B_NONCE*)&responseAuth;
			}
		    else
			{
			    // Compute the response HMAC and get a pointer to the nonce used.
			    // This function will also update the values if needed. Note, the
			    nonceTPM = BuildSingleResponseAuth(command, i, &responseAuth);
			}
		    command->authSize +=
			TPM2B_NONCE_Marshal(nonceTPM, &command->responseBuffer, NULL);
		    command->authSize += TPMA_SESSION_Marshal(
							      &s_attributes[i], &command->responseBuffer, NULL);
		    command->authSize +=
			TPM2B_DIGEST_Marshal(&responseAuth, &command->responseBuffer, NULL);
		    if(!IS_ATTRIBUTE(s_attributes[i], TPMA_SESSION, continueSession))
			SessionFlush(s_sessionHandles[i]);
		}
	}

 Cleanup:
    return result;
}

//*** SessionRemoveAssociationToHandle()
// This function deals with the case where an entity associated with an authorization
// is deleted during command processing. The primary use of this is to support
// UndefineSpaceSpecial().
void SessionRemoveAssociationToHandle(TPM_HANDLE handle)
{
    UINT32 i;
    //
    for(i = 0; i < MAX_SESSION_NUM; i++)
	{
	    if(s_associatedHandles[i] == HierarchyNormalizeHandle(handle))
		{
		    s_associatedHandles[i] = TPM_RH_NULL;
		}
	}
}
