mirror of
https://github.com/stefanberger/libtpms
synced 2025-12-28 07:43:10 +00:00
This patch fixes a bug described in the following TCG Document ERRATA; Errata Version 1.9, August 23, 2019; For TCG Trusted Platform Module Library; Family "2.0", Level 00, Revision 1.38, Sept 29, 2016 section 2.34: NV PIN Indices Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
1992 lines
74 KiB
C
1992 lines
74 KiB
C
/********************************************************************************/
|
|
/* */
|
|
/* Process the Authorization Sessions */
|
|
/* Written by Ken Goldman */
|
|
/* IBM Thomas J. Watson Research Center */
|
|
/* $Id: SessionProcess.c 1493 2019-09-04 13:31:35Z 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 - 2018 */
|
|
/* */
|
|
/********************************************************************************/
|
|
|
|
/* 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"
|
|
/* 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;
|
|
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;
|
|
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
|
|
)
|
|
{
|
|
#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
|
|
}
|
|
/* 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
|
|
)
|
|
{
|
|
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;
|
|
}
|
|
/* 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
|
|
)
|
|
{
|
|
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;
|
|
}
|
|
/* 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(session == NULL || 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;
|
|
}
|
|
}
|
|
}
|