blob: 6832e22d04b66b9f6999d2734556852f550000ea [file] [log] [blame]
/* Microsoft Reference Implementation for TPM 2.0
*
* The copyright in this software is being made available under the BSD License,
* included below. This software may be subject to other third party and
* contributor rights, including patent rights, and no such rights are granted
* under this license.
*
* Copyright (c) Microsoft Corporation
*
* All rights reserved.
*
* BSD License
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this list
* of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ""AS IS""
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
//** 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"
//
//** Authorization Support Functions
//
//*** IsDAExempted()
// This function indicates if a handle is exempted from DA logic.
// A handle is exempted if it is
// 1. a primary seed handle,
// 2. an object with noDA bit SET,
// 3. an NV Index with TPMA_NV_NO_DA bit SET, or
// 4. a PCR handle.
//
// return type: BOOL
// TRUE handle is exempted from DA logic
// FALSE 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:
{
result = (ObjectGetPublicAttributes(handle).noDA == SET);
break;
}
case TPM_HT_NV_INDEX:
{
NV_INDEX *nvIndex = NvGetIndexInfo(handle, NULL);
result = (nvIndex->publicArea.attributes.TPMA_NV_NO_DA == SET);
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 handle points to the session start entity
// FALSE 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:
// 1. the command requires the DUP role,
// 2. the command requires the ADMIN role and the authorized entity
// is an object and its adminWithPolicy bit is SET, or
// 3. the command requires the ADMIN role and the authorized entity
// is a permanent handle or an NV Index.
// 4. The authorized entity is a PCR belonging to a policy group, and
// has its policy initialized
// return type: BOOL
// 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(object->publicArea.objectAttributes.adminWithPolicy == CLEAR)
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 authValue is available
// FALSE 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:
#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;
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;
object = HandleToObject(handle);
// authValue is always available for a sequence object.
// An alternative for this is to SET
// object->publicArea.objectAttributes.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
&& (object->publicArea.objectAttributes.userWithAuth == SET
|| (CommandAuthRole(commandIndex, sessionIndex) == AUTH_ADMIN
&& object->publicArea.objectAttributes.adminWithPolicy
== CLEAR)))
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(nvAttributes.TPMA_NV_AUTHWRITE == SET)
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(nvAttributes.TPMA_NV_WRITTEN != SET)
break; // return false
// get the index values
pin.intVal = NvGetUINT64Data(nvIndex, locator);
if(pin.pin.pinCount < pin.pin.pinLimit)
result = TRUE;
}
// For non-PIN Indices, need to allow use of the authValue
else if(nvAttributes.TPMA_NV_AUTHREAD == SET)
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 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;
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 attributes = 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(IsNv_TPMA_NV_POLICYWRITE(attributes))
result = TRUE;
}
else
{
if(IsNv_TPMA_NV_POLICYREAD(attributes))
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
)
{
#if ALG_SHA1
command->sha1CpHash.t.size = 0;
command->sha1RpHash.t.size = 0;
#endif
#if ALG_SHA256
command->sha256CpHash.t.size = 0;
command->sha256RpHash.t.size = 0;
#endif
#if ALG_SHA384
command->sha384CpHash.t.size = 0;
command->sha384RpHash.t.size = 0;
#endif
#if ALG_SHA512
command->sha512CpHash.t.size = 0;
command->sha512RpHash.t.size = 0;
#endif
#if ALG_SM3_256
command->sm3_256CpHash.t.size = 0;
command->sm3_256RpHash.t.size = 0;
#endif
}
//*** GetCpHashPointer()
// Function to get a pointer to the cpHash of the command
static TPM2B_DIGEST *
GetCpHashPointer(
COMMAND *command,
TPMI_ALG_HASH hashAlg
)
{
switch(hashAlg)
{
#if ALG_SHA1
case TPM_ALG_SHA1:
return (TPM2B_DIGEST *)&command->sha1CpHash;
#endif
#if ALG_SHA256
case TPM_ALG_SHA256:
return (TPM2B_DIGEST *)&command->sha256CpHash;
#endif
#if ALG_SHA384
case TPM_ALG_SHA384:
return (TPM2B_DIGEST *)&command->sha384CpHash;
#endif
#if ALG_SHA512
case TPM_ALG_SHA512:
return (TPM2B_DIGEST *)&command->sha512CpHash;
#endif
#if ALG_SM3_256
case TPM_ALG_SM3_256:
return (TPM2B_DIGEST *)&command->sm3_256CpHash;
#endif
default:
break;
}
return NULL;
}
//*** GetRpHashPointer()
// Function to get a pointer to the RpHash of the command
static TPM2B_DIGEST *
GetRpHashPointer(
COMMAND *command,
TPMI_ALG_HASH hashAlg
)
{
switch(hashAlg)
{
#if ALG_SHA1
case TPM_ALG_SHA1:
return (TPM2B_DIGEST *)&command->sha1RpHash;
#endif
#if ALG_SHA256
case TPM_ALG_SHA256:
return (TPM2B_DIGEST *)&command->sha256RpHash;
#endif
#if ALG_SHA384
case TPM_ALG_SHA384:
return (TPM2B_DIGEST *)&command->sha384RpHash;
#endif
#if ALG_SHA512
case TPM_ALG_SHA512:
return (TPM2B_DIGEST *)&command->sha512RpHash;
#endif
#if ALG_SM3_256
case TPM_ALG_SM3_256:
return (TPM2B_DIGEST *)&command->sm3_256RpHash;
#endif
default:
break;
}
return NULL;
}
//*** 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()
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
#ifdef TPM_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.
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);
}
//*** 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:
// 1. compare policyDigest in session with authPolicy associated with
// the entity to be authorized;
// 2. compare timeout if applicable;
// 3. compare commandCode if applicable;
// 4. compare cpHash if applicable; and
// 5. 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; // Code analysis says that this is not initialized
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((IsNv_TPMA_NV_WRITTEN(nvIndex->publicArea.attributes))
!= (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;
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;
if(s_sessionHandles[sessionIndex] == TPM_RS_PW)
{
// A PWAP session needs additional processing.
// Can't have any attributes set other than continueSession bit
if(s_attributes[sessionIndex].encrypt
|| s_attributes[sessionIndex].decrypt
|| s_attributes[sessionIndex].audit
|| s_attributes[sessionIndex].auditExclusive
|| s_attributes[sessionIndex].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 indices.
// First process decrypt.
if(s_attributes[sessionIndex].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(s_attributes[sessionIndex].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(s_attributes[sessionIndex].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(s_attributes[sessionIndex].auditReset == CLEAR
&& session->attributes.isAudit == SET)
{
// Not first use or reset. If auditExlusive is SET, then this
// session must be the current exclusive session.
if(s_attributes[sessionIndex].auditExclusive == SET
&& 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;
#ifdef 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;
}
//*** 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);
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);
}
}
// If the authorization session is going to use an authValue, then make sure
// that access to that authValue isn't locked out.
// Note: session == NULL for a PW session.
if(session == NULL || session->attributes.includeAuth)
{
// 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: IsAutPolciyAvalable 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(session == NULL || session->attributes.isPasswordNeeded == SET)
result = CheckPWAuthSession(sessionIndex);
else
result = CheckSessionHMAC(command, sessionIndex);
// Do processing for PIN indices 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.
// See if we need to do anything to a PIN index
if(TPM_HT_NV_INDEX == HandleGetType(associatedHandle))
{
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) && nvAttributes.TPMA_NV_WRITTEN == SET)
{
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 for anything other than NV_Read.
// 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)
&& nvAttributes.TPMA_NV_WRITTEN == SET
&& 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;
}
#ifdef TPM_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(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(s_attributes[sessionIndex].audit == SET
|| s_attributes[sessionIndex].encrypt == SET
|| s_attributes[sessionIndex].decrypt == SET)
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(s_attributes[sessionIndex].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(s_attributes[sessionIndex].audit == CLEAR
&& s_attributes[sessionIndex].encrypt == CLEAR
&& s_attributes[sessionIndex].decrypt == CLEAR)
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);
}
}
#ifdef TPM_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;
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;
}
#ifdef TPM_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;
}
#ifdef TPM_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()
// Function to update the internal audit related states of a session. It
// 1. 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;
// 2. reports exclusive audit session;
// 3. extends audit log; and
// 4. 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(s_attributes[i].audit == SET)
{
// 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
|| s_attributes[i].auditReset == SET)
{
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)
{
s_attributes[i].auditExclusive = SET;
}
else
{
s_attributes[i].auditExclusive = CLEAR;
}
// 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.
// Get the authValue with trailing zeros removed
TPM2B_AUTH authValue;
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()
// Updates internal sessions:
// 1. Restarts session time.
// 2. 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
&& s_attributes[i].continueSession != CLEAR)
{
// 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.
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);
#ifdef TPM_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)
{
s_attributes[i].continueSession = SET;
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(s_attributes[i].continueSession == CLEAR)
SessionFlush(s_sessionHandles[i]);
}
}
return;
}
//*** 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;
}
}
}