summaryrefslogtreecommitdiffstats
path: root/src/tpm2/SessionProcess.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/tpm2/SessionProcess.c1990
1 files changed, 1990 insertions, 0 deletions
diff --git a/src/tpm2/SessionProcess.c b/src/tpm2/SessionProcess.c
new file mode 100644
index 0000000..f8efc68
--- /dev/null
+++ b/src/tpm2/SessionProcess.c
@@ -0,0 +1,1990 @@
+/********************************************************************************/
+/* */
+/* Process the Authorization Sessions */
+/* Written by Ken Goldman */
+/* IBM Thomas J. Watson Research Center */
+/* $Id: SessionProcess.c 1658 2021-01-22 23:14:01Z kgoldman $ */
+/* */
+/* 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 - 2021 */
+/* */
+/********************************************************************************/
+
+/* 6.4 SessionProcess.c */
+/* 6.4.1 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 */
+#define SESSION_PROCESS_C
+#include "Tpm.h"
+#include "ACT.h"
+/* 6.4.3.1 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. */
+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;
+}
+/* 6.4.3.2 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. */
+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;
+}
+/* 6.4.3.3 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. */
+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;
+}
+/* 6.4.3.4 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, or */
+/* c) the command requires the ADMIN role and the authorized entity is a permanent handle or an NV
+ Index. */
+/* d) The authorized entity is a PCR belonging to a policy group, and has its policy initialized */
+/* Return Values Meaning */
+/* TRUE policy session is required */
+/* FALSE 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;
+}
+/* 6.4.3.5 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. */
+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:
+#ifdef VENDOR_PERMANENT
+ // This vendor defined handle associated with the
+ // manufacturer's shared secret
+ case VENDOR_PERMANENT:
+#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 added begin
+ FOR_EACH_ACT(CASE_ACT_HANDLE)
+ {
+ // The ACT auth value is not available if the platform is disabled
+ result = g_phEnable == SET;
+ break;
+ }
+#endif // libtpms added end
+ 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
+ // 1. userWithAuth bit is SET, or
+ // 2. 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;
+}
+
+/* 6.4.3.6 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 Values Meaning */
+/* TRUE authPolicy is available */
+/* FALSE 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;
+#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)
+
+ 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;
+}
+/* 6.4.4 Session Parsing Functions */
+/* 6.4.4.1 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)
+}
+
+/* 6.4.4.2 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;
+}
+
+/* 6.4.4.3 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;
+}
+
+/* 6.4.4.4 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;
+}
+/* 6.4.4.5 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;
+}
+/* 6.4.4.6 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() */
+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));
+}
+/* 6.4.4.7 CompareNameHash() */
+/* This function computes the name hash and compares it to the nameHash in the session data. */
+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);
+}
+/* 6.4.4.8 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[]. */
+/* Error Returns Meaning */
+/* 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);
+ }
+}
+/* 6.4.4.9 ComputeCommandHMAC() */
+/* This function computes the HMAC for an authorization session in a command. */
+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;
+}
+/* 6.4.4.10 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. */
+/* Error Returns Meaning */
+/* 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;
+}
+/* 6.4.4.11 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. */
+/* Error Returns Meaning */
+/* 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 if defined, or if the command requires an ADMIN or
+ // DUP role for this handle.
+ if(session->u1.cpHash.b.size != 0)
+ {
+ BOOL OK;
+ if(session->attributes.isCpHashDefined)
+ // Compare cpHash.
+ OK = MemoryEqual2B(&session->u1.cpHash.b,
+ &ComputeCpHash(command, session->authHashAlg)->b);
+ else if(session->attributes.isTemplateSet)
+ OK = CompareTemplateHash(command, session);
+ else
+ OK = CompareNameHash(command, session);
+ 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;
+}
+/* 6.4.4.12 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. */
+/* Error Returns Meaning */
+/* 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;
+}
+/* 6.4.4.13 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 */
+/* Error Returns Meaning */
+/* 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_RETRY;
+ }
+#endif
+ }
+ return TPM_RC_SUCCESS;
+}
+/* 6.4.4.14 CheckAuthSession() */
+/* This function checks that the authorization session properly authorizes the use of the associated
+ handle. */
+/* Error Returns Meaning */
+/* 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: IsAuthPolicyAvailable 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, 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
+/* 6.4.4.15 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. */
+/* Error Returns Meaning */
+/* 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
+/* 6.4.4.16 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. */
+/* Error Returns Meaning */
+/* 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(i > (command->sessionNum - 1))
+ return TPM_RC_AUTH_MISSING;
+ // Record the handle associated with the authorization session
+ s_associatedHandles[i] = 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;
+}
+/* 6.4.4.17 CheckAuthNoSession() */
+/* Function to process a command with no session associated. The function makes sure all the handles
+ in the command require no authorization. */
+/* Error Returns Meaning */
+/* TPM_RC_AUTH_MISSING failure - one or more handles require authorization */
+TPM_RC
+CheckAuthNoSession(
+ COMMAND *command // IN: command parsing structure
+ )
+{
+ UINT32 i;
+ TPM_RC result = TPM_RC_SUCCESS;
+ // 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;
+}
+/* 6.4.5 Response Session Processing */
+/* 6.4.5.1 Introduction */
+/* The following functions build the session area in a response and handle the audit sessions (if
+ present). */
+/* 6.4.5.2 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;
+}
+/* 6.4.5.3 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;
+}
+/* 6.4.5.4 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);
+}
+/* 6.4.5.5 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
+/* 6.4.5.6 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
+/* 6.4.5.7 UpdateAuditSessionStatus() */
+/* Function to update the internal audit related states of a session. It */
+/* a) initializes the session as audit session and sets it to be exclusive if this is the first time
+ it is used for audit or audit reset was requested; */
+/* b) reports exclusive audit session; */
+/* c) extends audit log; and */
+/* d) clears 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;
+}
+/* 6.4.5.8 ComputeResponseHMAC() */
+/* Function to compute HMAC for authorization session in a response. */
+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;
+}
+/* 6.4.5.9 UpdateInternalSession() */
+/* Updates internal sessions: */
+/* a) Restarts session time. */
+/* b) Clears 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;
+}
+/* 6.4.5.10 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;
+}
+/* 6.4.5.11 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;
+}
+/* 6.4.5.12 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. */
+void
+BuildResponseSession(
+ COMMAND *command // IN: structure that has relevant command
+ // information
+ )
+{
+ 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);
+ CryptParameterEncryption(s_sessionHandles[s_encryptSessionIndex],
+ &s_nonceCaller[s_encryptSessionIndex].b,
+ (UINT16)size,
+ &extraKey,
+ command->parameterBuffer);
+ }
+ }
+ // 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]);
+ }
+ }
+ return;
+}
+/* 6.4.5.13 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] == handle)
+ {
+ s_associatedHandles[i] = TPM_RH_NULL;
+ }
+ }
+}