// This file was extracted from the TCG Published
// Trusted Platform Module Library
// Part 4: Supporting Routines
// Family "2.0"
// Level 00 Revision 01.16
// October 30, 2014
#define PCR_C
#include "InternalRoutines.h"
#include "Platform.h"
//
// The initial value of PCR attributes. The value of these fields should be consistent with PC Client
// specification In this implementation, we assume the total number of implemented PCR is 24.
//
static const PCR_Attributes s_initAttributes[] =
{
// PCR 0 - 15, static RTM
{1, 0, 0x1F}, {1, 0, 0x1F}, {1, 0, 0x1F}, {1, 0, 0x1F},
{1, 0, 0x1F}, {1, 0, 0x1F}, {1, 0, 0x1F}, {1, 0, 0x1F},
{1, 0, 0x1F}, {1, 0, 0x1F}, {1, 0, 0x1F}, {1, 0, 0x1F},
{1, 0, 0x1F}, {1, 0, 0x1F}, {1, 0, 0x1F}, {1, 0, 0x1F},
{0, 0x0F, 0x1F}, // PCR 16, Debug
{0, 0x10, 0x1C}, // PCR 17, Locality 4
{0, 0x10, 0x1C}, // PCR 18, Locality 3
{0, 0x10, 0x0C}, // PCR 19, Locality 2
{0, 0x1C, 0x0E}, // PCR 20, Locality 1
{0, 0x1C, 0x04}, // PCR 21, Dynamic OS
{0, 0x1C, 0x04}, // PCR 22, Dynamic OS
{0, 0x0F, 0x1F}, // PCR 23, App specific
{0, 0x0F, 0x1F} // PCR 24, testing policy
};
//
//
// Functions
//
// PCRBelongsAuthGroup()
//
// This function indicates if a PCR belongs to a group that requires an authValue in order to modify the
// PCR. If it does, groupIndex is set to value of the group index. This feature of PCR is decided by the
// platform specification.
//
// Return Value Meaning
//
// TRUE: PCR belongs an auth group
// FALSE: PCR does not belong an auth group
//
BOOL
PCRBelongsAuthGroup(
TPMI_DH_PCR handle, // IN: handle of PCR
UINT32 *groupIndex // OUT: group index if PCR belongs a
// group that allows authValue. If PCR
// does not belong to an auth group,
// the value in this parameter is
// invalid
)
{
// None of the PCRs belong to a group requiring an authValue, as defined in
// Table 4 "PCR Attributes" of the "TCG PC Client Platform TPM Profile (TPT)
// Specification Level 00 Revision 00.43".
return FALSE;
}
//
//
// PCRBelongsPolicyGroup()
//
// This function indicates if a PCR belongs to a group that requires a policy authorization in order to modify
// the PCR. If it does, groupIndex is set to value of the group index. This feature of PCR is decided by the
// platform specification.
// Family "2.0" TCG Published Page 169
// Level 00 Revision 01.16 Copyright © TCG 2006-2014 October 30, 2014
// Trusted Platform Module Library Part 4: Supporting Routines
//
//
// Return Value Meaning
//
// TRUE: PCR belongs a policy group
// FALSE: PCR does not belong a policy group
//
BOOL
PCRBelongsPolicyGroup(
TPMI_DH_PCR handle, // IN: handle of PCR
UINT32 *groupIndex // OUT: group index if PCR belongs a group that
// allows policy. If PCR does not belong to
// a policy group, the value in this
// parameter is invalid
)
{
// None of the PCRs belong to the policy group, as defined in Table 4
// "PCR Attributes" of the "TCG PC Client Platform TPM Profile (TPT)
// Specification Level 00 Revision 00.43".
return FALSE;
}
//
//
// PCRBelongsTCBGroup()
//
// This function indicates if a PCR belongs to the TCB group.
//
// Return Value Meaning
//
// TRUE: PCR belongs to TCB group
// FALSE: PCR does not belong to TCB group
//
static BOOL
PCRBelongsTCBGroup(
TPMI_DH_PCR handle // IN: handle of PCR
)
{
#if ENABLE_PCR_NO_INCREMENT == YES
// Platform specification decides if a PCR belongs to a TCB group. In this
// implementation, we assume PCR[16, 21-23] belong to TCB group as defined
// in Table 4. If the platform specification requires differently, the
// implementation should be changed accordingly
if(handle == 16 || (handle >= 21 && handle <= 23))
return TRUE;
#endif
return FALSE;
}
//
//
// PCRPolicyIsAvailable()
//
// This function indicates if a policy is available for a PCR.
//
//
//
//
// Return Value Meaning
//
// TRUE the PCR should be authorized by policy
// FALSE the PCR does not allow policy
//
BOOL
PCRPolicyIsAvailable(
TPMI_DH_PCR handle // IN: PCR handle
)
{
UINT32 groupIndex;
return PCRBelongsPolicyGroup(handle, &groupIndex);
}
//
//
// PCRGetAuthValue()
//
// This function is used to access the authValue of a PCR. If PCR does not belong to an authValue group,
// an Empty Auth will be returned.
//
void
PCRGetAuthValue(
TPMI_DH_PCR handle, // IN: PCR handle
TPM2B_AUTH *auth // OUT: authValue of PCR
)
{
UINT32 groupIndex;
if(PCRBelongsAuthGroup(handle, &groupIndex))
{
*auth = gc.pcrAuthValues.auth[groupIndex];
}
else
{
auth->t.size = 0;
}
return;
}
//
//
// PCRGetAuthPolicy()
//
// This function is used to access the authorization policy of a PCR. It sets policy to the authorization policy
// and returns the hash algorithm for policy If the PCR does not allow a policy, TPM_ALG_NULL is returned.
//
TPMI_ALG_HASH
PCRGetAuthPolicy(
TPMI_DH_PCR handle, // IN: PCR handle
TPM2B_DIGEST *policy // OUT: policy of PCR
)
{
UINT32 groupIndex;
if(PCRBelongsPolicyGroup(handle, &groupIndex))
{
*policy = gp.pcrPolicies.policy[groupIndex];
return gp.pcrPolicies.hashAlg[groupIndex];
}
else
{
policy->t.size = 0;
return TPM_ALG_NULL;
}
}
//
//
// PCRSimStart()
//
// This function is used to initialize the policies when a TPM is manufactured. This function would only be
// called in a manufacturing environment or in a TPM simulator.
//
void
PCRSimStart(
void
)
{
UINT32 i;
for(i = 0; i < NUM_POLICY_PCR_GROUP; i++)
{
gp.pcrPolicies.hashAlg[i] = TPM_ALG_NULL;
gp.pcrPolicies.policy[i].t.size = 0;
}
for(i = 0; i < NUM_AUTHVALUE_PCR_GROUP; i++)
{
gc.pcrAuthValues.auth[i].t.size = 0;
}
// We need to give an initial configuration on allocated PCR before
// receiving any TPM2_PCR_Allocate command to change this configuration
// When the simulation environment starts, we allocate all the PCRs
for(gp.pcrAllocated.count = 0; gp.pcrAllocated.count < HASH_COUNT;
gp.pcrAllocated.count++)
{
gp.pcrAllocated.pcrSelections[gp.pcrAllocated.count].hash
= CryptGetHashAlgByIndex(gp.pcrAllocated.count);
gp.pcrAllocated.pcrSelections[gp.pcrAllocated.count].sizeofSelect
= PCR_SELECT_MAX;
for(i = 0; i < PCR_SELECT_MAX; i++)
gp.pcrAllocated.pcrSelections[gp.pcrAllocated.count].pcrSelect[i]
= 0xFF;
}
// Store the initial configuration to NV
NvWriteReserved(NV_PCR_POLICIES, &gp.pcrPolicies);
NvWriteReserved(NV_PCR_ALLOCATED, &gp.pcrAllocated);
return;
}
//
//
// GetSavedPcrPointer()
//
// This function returns the address of an array of state saved PCR based on the hash algorithm.
//
// Return Value Meaning
//
// NULL no such algorithm
// not NULL pointer to the 0th byte of the 0th PCR
//
static BYTE *
GetSavedPcrPointer (
TPM_ALG_ID alg, // IN: algorithm for bank
UINT32 pcrIndex // IN: PCR index in PCR_SAVE
)
{
switch(alg)
{
#ifdef TPM_ALG_SHA1
case TPM_ALG_SHA1:
return gc.pcrSave.sha1[pcrIndex];
break;
#endif
#ifdef TPM_ALG_SHA256
case TPM_ALG_SHA256:
return gc.pcrSave.sha256[pcrIndex];
break;
#endif
#ifdef TPM_ALG_SHA384
case TPM_ALG_SHA384:
return gc.pcrSave.sha384[pcrIndex];
break;
#endif
#ifdef TPM_ALG_SHA512
case TPM_ALG_SHA512:
return gc.pcrSave.sha512[pcrIndex];
break;
#endif
#ifdef TPM_ALG_SM3_256
case TPM_ALG_SM3_256:
return gc.pcrSave.sm3_256[pcrIndex];
break;
#endif
default:
FAIL(FATAL_ERROR_INTERNAL);
}
return NULL; // Never reached.
}
//
//
// PcrIsAllocated()
//
// This function indicates if a PCR number for the particular hash algorithm is allocated.
//
// Return Value Meaning
//
// FALSE PCR is not allocated
// TRUE PCR is allocated
//
BOOL
PcrIsAllocated (
UINT32 pcr, // IN: The number of the PCR
TPMI_ALG_HASH hashAlg // IN: The PCR algorithm
)
{
UINT32 i;
BOOL allocated = FALSE;
if(pcr < IMPLEMENTATION_PCR)
{
for(i = 0; i < gp.pcrAllocated.count; i++)
{
if(gp.pcrAllocated.pcrSelections[i].hash == hashAlg)
{
if(((gp.pcrAllocated.pcrSelections[i].pcrSelect[pcr/8])
& (1 << (pcr % 8))) != 0)
//
allocated = TRUE;
else
allocated = FALSE;
break;
}
}
}
return allocated;
}
//
//
// GetPcrPointer()
//
// This function returns the address of an array of PCR based on the hash algorithm.
//
// Return Value Meaning
//
// NULL no such algorithm
// not NULL pointer to the 0th byte of the 0th PCR
//
static BYTE *
GetPcrPointer (
TPM_ALG_ID alg, // IN: algorithm for bank
UINT32 pcrNumber // IN: PCR number
)
{
static BYTE *pcr = NULL;
if(!PcrIsAllocated(pcrNumber, alg))
return NULL;
switch(alg)
{
#ifdef TPM_ALG_SHA1
case TPM_ALG_SHA1:
pcr = s_pcrs[pcrNumber].sha1Pcr;
break;
#endif
#ifdef TPM_ALG_SHA256
case TPM_ALG_SHA256:
pcr = s_pcrs[pcrNumber].sha256Pcr;
break;
#endif
#ifdef TPM_ALG_SHA384
case TPM_ALG_SHA384:
pcr = s_pcrs[pcrNumber].sha384Pcr;
break;
#endif
#ifdef TPM_ALG_SHA512
case TPM_ALG_SHA512:
pcr = s_pcrs[pcrNumber].sha512Pcr;
break;
#endif
#ifdef TPM_ALG_SM3_256
case TPM_ALG_SM3_256:
pcr = s_pcrs[pcrNumber].sm3_256Pcr;
break;
#endif
default:
pAssert(FALSE);
break;
}
return pcr;
//
}
//
//
// IsPcrSelected()
//
// This function indicates if an indicated PCR number is selected by the bit map in selection.
//
// Return Value Meaning
//
// FALSE PCR is not selected
// TRUE PCR is selected
//
static BOOL
IsPcrSelected (
UINT32 pcr, // IN: The number of the PCR
TPMS_PCR_SELECTION *selection // IN: The selection structure
)
{
BOOL selected = FALSE;
if( pcr < IMPLEMENTATION_PCR
&& ((selection->pcrSelect[pcr/8]) & (1 << (pcr % 8))) != 0)
selected = TRUE;
return selected;
}
//
//
// FilterPcr()
//
// This function modifies a PCR selection array based on the implemented PCR.
//
static void
FilterPcr(
TPMS_PCR_SELECTION *selection // IN: input PCR selection
)
{
UINT32 i;
TPMS_PCR_SELECTION *allocated = NULL;
// If size of select is less than PCR_SELECT_MAX, zero the unspecified PCR
for(i = selection->sizeofSelect; i < PCR_SELECT_MAX; i++)
selection->pcrSelect[i] = 0;
// Find the internal configuration for the bank
for(i = 0; i < gp.pcrAllocated.count; i++)
{
if(gp.pcrAllocated.pcrSelections[i].hash == selection->hash)
{
allocated = &gp.pcrAllocated.pcrSelections[i];
break;
}
}
for (i = 0; i < selection->sizeofSelect; i++)
{
if(allocated == NULL)
{
// If the required bank does not exist, clear input selection
selection->pcrSelect[i] = 0;
}
else
selection->pcrSelect[i] &= allocated->pcrSelect[i];
}
return;
}
//
//
// PcrDrtm()
//
// This function does the DRTM and H-CRTM processing it is called from _TPM_Hash_End().
//
void
PcrDrtm(
const TPMI_DH_PCR pcrHandle, // IN: the index of the PCR to be
// modified
const TPMI_ALG_HASH hash, // IN: the bank identifier
const TPM2B_DIGEST *digest // IN: the digest to modify the PCR
)
{
BYTE *pcrData = GetPcrPointer(hash, pcrHandle);
if(pcrData != NULL)
{
// Rest the PCR to zeros
MemorySet(pcrData, 0, digest->t.size);
// if the TPM has not started, then set the PCR to 0...04 and then extend
if(!TPMIsStarted())
{
pcrData[digest->t.size - 1] = 4;
}
// Now, extend the value
PCRExtend(pcrHandle, hash, digest->t.size, (BYTE *)digest->t.buffer);
}
}
//
//
// PCRStartup()
//
// This function initializes the PCR subsystem at TPM2_Startup().
//
void
PCRStartup(
STARTUP_TYPE type, // IN: startup type
BYTE locality // IN: startup locality
)
{
UINT32 pcr, j;
UINT32 saveIndex = 0;
g_pcrReConfig = FALSE;
if(type != SU_RESUME)
{
// PCR generation counter is cleared at TPM_RESET and TPM_RESTART
gr.pcrCounter = 0;
}
// Initialize/Restore PCR values
for(pcr = 0; pcr < IMPLEMENTATION_PCR; pcr++)
{
// On resume, need to know if this PCR had its state saved or not
UINT32 stateSaved =
(type == SU_RESUME && s_initAttributes[pcr].stateSave == SET) ? 1 : 0;
// If this is the H-CRTM PCR and we are not doing a resume and we
// had an H-CRTM event, then we don't change this PCR
if(pcr == HCRTM_PCR && type != SU_RESUME && g_DrtmPreStartup == TRUE)
continue;
// Iterate each hash algorithm bank
for(j = 0; j < gp.pcrAllocated.count; j++)
{
TPMI_ALG_HASH hash = gp.pcrAllocated.pcrSelections[j].hash;
BYTE *pcrData = GetPcrPointer(hash, pcr);
UINT16 pcrSize = CryptGetHashDigestSize(hash);
if(pcrData != NULL)
{
// if state was saved
if(stateSaved == 1)
{
// Restore saved PCR value
BYTE *pcrSavedData;
pcrSavedData = GetSavedPcrPointer(
gp.pcrAllocated.pcrSelections[j].hash,
saveIndex);
MemoryCopy(pcrData, pcrSavedData, pcrSize, pcrSize);
}
else
// PCR was not restored by state save
{
// If the reset locality of the PCR is 4, then
// the reset value is all one's, otherwise it is
// all zero.
if((s_initAttributes[pcr].resetLocality & 0x10) != 0)
MemorySet(pcrData, 0xFF, pcrSize);
else
{
MemorySet(pcrData, 0, pcrSize);
if(pcr == HCRTM_PCR)
pcrData[pcrSize-1] = locality;
}
}
}
}
saveIndex += stateSaved;
}
// Reset authValues
if(type != SU_RESUME)
{
for(j = 0; j < NUM_AUTHVALUE_PCR_GROUP; j++)
{
gc.pcrAuthValues.auth[j].t.size = 0;
}
}
}
//
//
// PCRStateSave()
//
// This function is used to save the PCR values that will be restored on TPM Resume.
//
void
PCRStateSave(
TPM_SU type // IN: startup type
)
{
UINT32 pcr, j;
UINT32 saveIndex = 0;
//
// if state save CLEAR, nothing to be done. Return here
if(type == TPM_SU_CLEAR) return;
// Copy PCR values to the structure that should be saved to NV
for(pcr = 0; pcr < IMPLEMENTATION_PCR; pcr++)
{
UINT32 stateSaved = (s_initAttributes[pcr].stateSave == SET) ? 1 : 0;
// Iterate each hash algorithm bank
for(j = 0; j < gp.pcrAllocated.count; j++)
{
BYTE *pcrData;
UINT32 pcrSize;
pcrData = GetPcrPointer(gp.pcrAllocated.pcrSelections[j].hash, pcr);
if(pcrData != NULL)
{
pcrSize
= CryptGetHashDigestSize(gp.pcrAllocated.pcrSelections[j].hash);
if(stateSaved == 1)
{
// Restore saved PCR value
BYTE *pcrSavedData;
pcrSavedData
= GetSavedPcrPointer(gp.pcrAllocated.pcrSelections[j].hash,
saveIndex);
MemoryCopy(pcrSavedData, pcrData, pcrSize, pcrSize);
}
}
}
saveIndex += stateSaved;
}
return;
}
//
//
// PCRIsStateSaved()
//
// This function indicates if the selected PCR is a PCR that is state saved on TPM2_Shutdown(STATE). The
// return value is based on PCR attributes.
//
// Return Value Meaning
//
// TRUE PCR is state saved
// FALSE PCR is not state saved
//
BOOL
PCRIsStateSaved(
TPMI_DH_PCR handle // IN: PCR handle to be extended
)
{
UINT32 pcr = handle - PCR_FIRST;
if(s_initAttributes[pcr].stateSave == SET)
return TRUE;
else
return FALSE;
}
//
//
//
// PCRIsResetAllowed()
//
// This function indicates if a PCR may be reset by the current command locality. The return value is based
// on PCR attributes, and not the PCR allocation.
//
// Return Value Meaning
//
// TRUE TPM2_PCR_Reset() is allowed
// FALSE TPM2_PCR_Reset() is not allowed
//
BOOL
PCRIsResetAllowed(
TPMI_DH_PCR handle // IN: PCR handle to be extended
)
{
UINT8 commandLocality;
UINT8 localityBits = 1;
UINT32 pcr = handle - PCR_FIRST;
// Check for the locality
commandLocality = _plat__LocalityGet();
#ifdef DRTM_PCR
// For a TPM that does DRTM, Reset is not allowed at locality 4
if(commandLocality == 4)
return FALSE;
#endif
localityBits = localityBits << commandLocality;
if((localityBits & s_initAttributes[pcr].resetLocality) == 0)
return FALSE;
else
return TRUE;
}
//
//
// PCRChanged()
//
// This function checks a PCR handle to see if the attributes for the PCR are set so that any change to the
// PCR causes an increment of the pcrCounter. If it does, then the function increments the counter.
//
void
PCRChanged(
TPM_HANDLE pcrHandle // IN: the handle of the PCR that changed.
)
{
// For the reference implementation, the only change that does not cause
// increment is a change to a PCR in the TCB group.
if(!PCRBelongsTCBGroup(pcrHandle))
gr.pcrCounter++;
}
//
//
// PCRIsExtendAllowed()
//
// This function indicates a PCR may be extended at the current command locality. The return value is
// based on PCR attributes, and not the PCR allocation.
//
//
//
//
// Return Value Meaning
//
// TRUE extend is allowed
// FALSE extend is not allowed
//
BOOL
PCRIsExtendAllowed(
TPMI_DH_PCR handle // IN: PCR handle to be extended
)
{
UINT8 commandLocality;
UINT8 localityBits = 1;
UINT32 pcr = handle - PCR_FIRST;
// Check for the locality
commandLocality = _plat__LocalityGet();
localityBits = localityBits << commandLocality;
if((localityBits & s_initAttributes[pcr].extendLocality) == 0)
return FALSE;
else
return TRUE;
}
//
//
// PCRExtend()
//
// This function is used to extend a PCR in a specific bank.
//
void
PCRExtend(
TPMI_DH_PCR handle, // IN: PCR handle to be extended
TPMI_ALG_HASH hash, // IN: hash algorithm of PCR
UINT32 size, // IN: size of data to be extended
BYTE *data // IN: data to be extended
)
{
UINT32 pcr = handle - PCR_FIRST;
BYTE *pcrData;
HASH_STATE hashState;
UINT16 pcrSize;
pcrData = GetPcrPointer(hash, pcr);
// Extend PCR if it is allocated
if(pcrData != NULL)
{
pcrSize = CryptGetHashDigestSize(hash);
CryptStartHash(hash, &hashState);
CryptUpdateDigest(&hashState, pcrSize, pcrData);
CryptUpdateDigest(&hashState, size, data);
CryptCompleteHash(&hashState, pcrSize, pcrData);
// If PCR does not belong to TCB group, increment PCR counter
if(!PCRBelongsTCBGroup(handle))
gr.pcrCounter++;
}
return;
}
//
//
//
// PCRComputeCurrentDigest()
//
// This function computes the digest of the selected PCR.
// As a side-effect, selection is modified so that only the implemented PCR will have their bits still set.
//
void
PCRComputeCurrentDigest(
TPMI_ALG_HASH hashAlg, // IN: hash algorithm to compute digest
TPML_PCR_SELECTION *selection, // IN/OUT: PCR selection (filtered on
// output)
TPM2B_DIGEST *digest // OUT: digest
)
{
HASH_STATE hashState;
TPMS_PCR_SELECTION *select;
BYTE *pcrData; // will point to a digest
UINT32 pcrSize;
UINT32 pcr;
UINT32 i;
// Initialize the hash
digest->t.size = CryptStartHash(hashAlg, &hashState);
pAssert(digest->t.size > 0 && digest->t.size < UINT16_MAX);
// Iterate through the list of PCR selection structures
for(i = 0; i < selection->count; i++)
{
// Point to the current selection
select = &selection->pcrSelections[i]; // Point to the current selection
FilterPcr(select); // Clear out the bits for unimplemented PCR
// Need the size of each digest
pcrSize = CryptGetHashDigestSize(selection->pcrSelections[i].hash);
// Iterate through the selection
for(pcr = 0; pcr < IMPLEMENTATION_PCR; pcr++)
{
if(IsPcrSelected(pcr, select)) // Is this PCR selected
{
// Get pointer to the digest data for the bank
pcrData = GetPcrPointer(selection->pcrSelections[i].hash, pcr);
pAssert(pcrData != NULL);
CryptUpdateDigest(&hashState, pcrSize, pcrData); // add to digest
}
}
}
// Complete hash stack
CryptCompleteHash2B(&hashState, &digest->b);
return;
}
//
//
// PCRRead()
//
// This function is used to read a list of selected PCR. If the requested PCR number exceeds the maximum
// number that can be output, the selection is adjusted to reflect the actual output PCR.
//
void
PCRRead(
TPML_PCR_SELECTION *selection, // IN/OUT: PCR selection (filtered on
// output)
TPML_DIGEST *digest, // OUT: digest
UINT32 *pcrCounter // OUT: the current value of PCR generation
// number
)
{
TPMS_PCR_SELECTION *select;
BYTE *pcrData; // will point to a digest
UINT32 pcr;
UINT32 i;
digest->count = 0;
// Iterate through the list of PCR selection structures
for(i = 0; i < selection->count; i++)
{
// Point to the current selection
select = &selection->pcrSelections[i]; // Point to the current selection
FilterPcr(select); // Clear out the bits for unimplemented PCR
// Iterate through the selection
for (pcr = 0; pcr < IMPLEMENTATION_PCR; pcr++)
{
if(IsPcrSelected(pcr, select)) // Is this PCR selected
{
// Check if number of digest exceed upper bound
if(digest->count > 7)
{
// Clear rest of the current select bitmap
while( pcr < IMPLEMENTATION_PCR
// do not round up!
&& (pcr / 8) < select->sizeofSelect)
{
// do not round up!
select->pcrSelect[pcr/8] &= (BYTE) ~(1 << (pcr % 8));
pcr++;
}
// Exit inner loop
break;;
}
// Need the size of each digest
digest->digests[digest->count].t.size =
CryptGetHashDigestSize(selection->pcrSelections[i].hash);
// Get pointer to the digest data for the bank
pcrData = GetPcrPointer(selection->pcrSelections[i].hash, pcr);
pAssert(pcrData != NULL);
// Add to the data to digest
MemoryCopy(digest->digests[digest->count].t.buffer,
pcrData,
digest->digests[digest->count].t.size,
digest->digests[digest->count].t.size);
digest->count++;
}
}
// If we exit inner loop because we have exceed the output upper bound
if(digest->count > 7 && pcr < IMPLEMENTATION_PCR)
{
// Clear rest of the selection
while(i < selection->count)
{
MemorySet(selection->pcrSelections[i].pcrSelect, 0,
selection->pcrSelections[i].sizeofSelect);
i++;
}
// exit outer loop
break;
}
}
*pcrCounter = gr.pcrCounter;
return;
}
//
//
// PcrWrite()
//
// This function is used by _TPM_Hash_End() to set a PCR to the computed hash of the H-CRTM event.
//
void
PcrWrite(
TPMI_DH_PCR handle, // IN: PCR handle to be extended
TPMI_ALG_HASH hash, // IN: hash algorithm of PCR
TPM2B_DIGEST *digest // IN: the new value
)
{
UINT32 pcr = handle - PCR_FIRST;
BYTE *pcrData;
// Copy value to the PCR if it is allocated
pcrData = GetPcrPointer(hash, pcr);
if(pcrData != NULL)
{
MemoryCopy(pcrData, digest->t.buffer, digest->t.size, digest->t.size); ;
}
return;
}
//
//
// PCRAllocate()
//
// This function is used to change the PCR allocation.
//
// Error Returns Meaning
//
// TPM_RC_SUCCESS allocate success
// TPM_RC_NO_RESULTS allocate failed
// TPM_RC_PCR improper allocation
//
TPM_RC
PCRAllocate(
TPML_PCR_SELECTION *allocate, // IN: required allocation
UINT32 *maxPCR, // OUT: Maximum number of PCR
UINT32 *sizeNeeded, // OUT: required space
UINT32 *sizeAvailable // OUT: available space
)
{
UINT32 i, j, k;
TPML_PCR_SELECTION newAllocate;
// Initialize the flags to indicate if HCRTM PCR and DRTM PCR are allocated.
BOOL pcrHcrtm = FALSE;
BOOL pcrDrtm = FALSE;
// Create the expected new PCR allocation based on the existing allocation
// and the new input:
// 1. if a PCR bank does not appear in the new allocation, the existing
// allocation of this PCR bank will be preserved.
// 2. if a PCR bank appears multiple times in the new allocation, only the
// last one will be in effect.
newAllocate = gp.pcrAllocated;
for(i = 0; i < allocate->count; i++)
{
for(j = 0; j < newAllocate.count; j++)
{
// If hash matches, the new allocation covers the old allocation
// for this particular bank.
// The assumption is the initial PCR allocation (from manufacture)
// has all the supported hash algorithms with an assigned bank
// (possibly empty). So there must be a match for any new bank
// allocation from the input.
if(newAllocate.pcrSelections[j].hash ==
allocate->pcrSelections[i].hash)
{
newAllocate.pcrSelections[j] = allocate->pcrSelections[i];
break;
}
}
// The j loop must exit with a match.
pAssert(j < newAllocate.count);
}
// Max PCR in a bank is MIN(implemented PCR, PCR with attributes defined)
*maxPCR = sizeof(s_initAttributes) / sizeof(PCR_Attributes);
if(*maxPCR > IMPLEMENTATION_PCR)
*maxPCR = IMPLEMENTATION_PCR;
// Compute required size for allocation
*sizeNeeded = 0;
for(i = 0; i < newAllocate.count; i++)
{
UINT32 digestSize
= CryptGetHashDigestSize(newAllocate.pcrSelections[i].hash);
#if defined(DRTM_PCR)
// Make sure that we end up with at least one DRTM PCR
# define PCR_DRTM (PCR_FIRST + DRTM_PCR) // for cosmetics
pcrDrtm = pcrDrtm || TEST_BIT(PCR_DRTM, newAllocate.pcrSelections[i]);
#else // if DRTM PCR is not required, indicate that the allocation is OK
pcrDrtm = TRUE;
#endif
#if defined(HCRTM_PCR)
// and one HCRTM PCR (since this is usually PCR 0...)
# define PCR_HCRTM (PCR_FIRST + HCRTM_PCR)
pcrHcrtm = pcrDrtm || TEST_BIT(PCR_HCRTM, newAllocate.pcrSelections[i]);
#else
pcrHcrtm = TRUE;
#endif
for(j = 0; j < newAllocate.pcrSelections[i].sizeofSelect; j++)
{
BYTE mask = 1;
for(k = 0; k < 8; k++)
{
if((newAllocate.pcrSelections[i].pcrSelect[j] & mask) != 0)
*sizeNeeded += digestSize;
mask = mask << 1;
}
}
}
if(!pcrDrtm || !pcrHcrtm)
return TPM_RC_PCR;
// In this particular implementation, we always have enough space to
// allocate PCR. Different implementation may return a sizeAvailable less
// than the sizeNeed.
*sizeAvailable = sizeof(s_pcrs);
// Save the required allocation to NV. Note that after NV is written, the
// PCR allocation in NV is no longer consistent with the RAM data
// gp.pcrAllocated. The NV version reflect the allocate after next
// TPM_RESET, while the RAM version reflects the current allocation
NvWriteReserved(NV_PCR_ALLOCATED, &newAllocate);
return TPM_RC_SUCCESS;
}
//
//
// PCRSetValue()
//
// This function is used to set the designated PCR in all banks to an initial value. The initial value is signed
// and will be sign extended into the entire PCR.
//
void
PCRSetValue(
TPM_HANDLE handle, // IN: the handle of the PCR to set
INT8 initialValue // IN: the value to set
)
{
int i;
UINT32 pcr = handle - PCR_FIRST;
TPMI_ALG_HASH hash;
UINT16 digestSize;
BYTE *pcrData;
// Iterate supported PCR bank algorithms to reset
for(i = 0; i < HASH_COUNT; i++)
{
hash = CryptGetHashAlgByIndex(i);
// Prevent runaway
if(hash == TPM_ALG_NULL)
break;
// Get a pointer to the data
pcrData = GetPcrPointer(gp.pcrAllocated.pcrSelections[i].hash, pcr);
// If the PCR is allocated
if(pcrData != NULL)
{
// And the size of the digest
digestSize = CryptGetHashDigestSize(hash);
// Set the LSO to the input value
pcrData[digestSize - 1] = initialValue;
// Sign extend
if(initialValue >= 0)
MemorySet(pcrData, 0, digestSize - 1);
else
MemorySet(pcrData, -1, digestSize - 1);
}
}
}
//
//
// PCRResetDynamics
//
// This function is used to reset a dynamic PCR to 0. This function is used in DRTM sequence.
//
void
PCRResetDynamics(
void
)
{
UINT32 pcr, i;
// Initialize PCR values
for(pcr = 0; pcr < IMPLEMENTATION_PCR; pcr++)
{
// Iterate each hash algorithm bank
for(i = 0; i < gp.pcrAllocated.count; i++)
{
BYTE *pcrData;
UINT32 pcrSize;
pcrData = GetPcrPointer(gp.pcrAllocated.pcrSelections[i].hash, pcr);
if(pcrData != NULL)
{
pcrSize =
CryptGetHashDigestSize(gp.pcrAllocated.pcrSelections[i].hash);
// Reset PCR
// Any PCR can be reset by locality 4 should be reset to 0
if((s_initAttributes[pcr].resetLocality & 0x10) != 0)
MemorySet(pcrData, 0, pcrSize);
}
}
}
return;
}
//
//
// PCRCapGetAllocation()
//
// This function is used to get the current allocation of PCR banks.
//
// Return Value Meaning
//
// YES: if the return count is 0
// NO: if the return count is not 0
//
TPMI_YES_NO
PCRCapGetAllocation(
UINT32 count, // IN: count of return
TPML_PCR_SELECTION *pcrSelection // OUT: PCR allocation list
)
{
if(count == 0)
{
pcrSelection->count = 0;
return YES;
}
else
{
*pcrSelection = gp.pcrAllocated;
return NO;
}
}
//
//
// PCRSetSelectBit()
//
// This function sets a bit in a bitmap array.
//
static void
PCRSetSelectBit(
UINT32 pcr, // IN: PCR number
BYTE *bitmap // OUT: bit map to be set
)
{
bitmap[pcr / 8] |= (1 << (pcr % 8));
return;
}
//
//
// PCRGetProperty()
//
// This function returns the selected PCR property.
//
// Return Value Meaning
//
// TRUE the property type is implemented
// FALSE the property type is not implemented
//
static BOOL
PCRGetProperty(
TPM_PT_PCR property,
TPMS_TAGGED_PCR_SELECT *select
)
{
UINT32 pcr;
UINT32 groupIndex;
select->tag = property;
// Always set the bitmap to be the size of all PCR
select->sizeofSelect = (IMPLEMENTATION_PCR + 7) / 8;
// Initialize bitmap
MemorySet(select->pcrSelect, 0, select->sizeofSelect);
// Collecting properties
for(pcr = 0; pcr < IMPLEMENTATION_PCR; pcr++)
{
switch(property)
{
case TPM_PT_PCR_SAVE:
if(s_initAttributes[pcr].stateSave == SET)
PCRSetSelectBit(pcr, select->pcrSelect);
break;
case TPM_PT_PCR_EXTEND_L0:
if((s_initAttributes[pcr].extendLocality & 0x01) != 0)
PCRSetSelectBit(pcr, select->pcrSelect);
break;
case TPM_PT_PCR_RESET_L0:
if((s_initAttributes[pcr].resetLocality & 0x01) != 0)
PCRSetSelectBit(pcr, select->pcrSelect);
break;
case TPM_PT_PCR_EXTEND_L1:
if((s_initAttributes[pcr].extendLocality & 0x02) != 0)
PCRSetSelectBit(pcr, select->pcrSelect);
break;
case TPM_PT_PCR_RESET_L1:
if((s_initAttributes[pcr].resetLocality & 0x02) != 0)
PCRSetSelectBit(pcr, select->pcrSelect);
break;
case TPM_PT_PCR_EXTEND_L2:
if((s_initAttributes[pcr].extendLocality & 0x04) != 0)
PCRSetSelectBit(pcr, select->pcrSelect);
//
break;
case TPM_PT_PCR_RESET_L2:
if((s_initAttributes[pcr].resetLocality & 0x04) != 0)
PCRSetSelectBit(pcr, select->pcrSelect);
break;
case TPM_PT_PCR_EXTEND_L3:
if((s_initAttributes[pcr].extendLocality & 0x08) != 0)
PCRSetSelectBit(pcr, select->pcrSelect);
break;
case TPM_PT_PCR_RESET_L3:
if((s_initAttributes[pcr].resetLocality & 0x08) != 0)
PCRSetSelectBit(pcr, select->pcrSelect);
break;
case TPM_PT_PCR_EXTEND_L4:
if((s_initAttributes[pcr].extendLocality & 0x10) != 0)
PCRSetSelectBit(pcr, select->pcrSelect);
break;
case TPM_PT_PCR_RESET_L4:
if((s_initAttributes[pcr].resetLocality & 0x10) != 0)
PCRSetSelectBit(pcr, select->pcrSelect);
break;
case TPM_PT_PCR_DRTM_RESET:
// DRTM reset PCRs are the PCR reset by locality 4
if((s_initAttributes[pcr].resetLocality & 0x10) != 0)
PCRSetSelectBit(pcr, select->pcrSelect);
break;
#if NUM_POLICY_PCR_GROUP > 0
case TPM_PT_PCR_POLICY:
if(PCRBelongsPolicyGroup(pcr + PCR_FIRST, &groupIndex))
PCRSetSelectBit(pcr, select->pcrSelect);
break;
#endif
#if NUM_AUTHVALUE_PCR_GROUP > 0
case TPM_PT_PCR_AUTH:
if(PCRBelongsAuthGroup(pcr + PCR_FIRST, &groupIndex))
PCRSetSelectBit(pcr, select->pcrSelect);
break;
#endif
#if ENABLE_PCR_NO_INCREMENT == YES
case TPM_PT_PCR_NO_INCREMENT:
if(PCRBelongsTCBGroup(pcr + PCR_FIRST))
PCRSetSelectBit(pcr, select->pcrSelect);
break;
#endif
default:
// If property is not supported, stop scanning PCR attributes
// and return.
return FALSE;
break;
}
}
return TRUE;
}
//
//
// PCRCapGetProperties()
//
// This function returns a list of PCR properties starting at property.
//
//
//
//
// Return Value Meaning
//
// YES: if no more property is available
// NO: if there are more properties not reported
//
TPMI_YES_NO
PCRCapGetProperties(
TPM_PT_PCR property, // IN: the starting PCR property
UINT32 count, // IN: count of returned propertie
TPML_TAGGED_PCR_PROPERTY *select // OUT: PCR select
)
{
TPMI_YES_NO more = NO;
UINT32 i;
// Initialize output property list
select->count = 0;
// The maximum count of properties we may return is MAX_PCR_PROPERTIES
if(count > MAX_PCR_PROPERTIES) count = MAX_PCR_PROPERTIES;
// TPM_PT_PCR_FIRST is defined as 0 in spec. It ensures that property
// value would never be less than TPM_PT_PCR_FIRST
pAssert(TPM_PT_PCR_FIRST == 0);
// Iterate PCR properties. TPM_PT_PCR_LAST is the index of the last property
// implemented on the TPM.
for(i = property; i <= TPM_PT_PCR_LAST; i++)
{
if(select->count < count)
{
// If we have not filled up the return list, add more properties to it
if(PCRGetProperty(i, &select->pcrProperty[select->count]))
// only increment if the property is implemented
select->count++;
}
else
{
// If the return list is full but we still have properties
// available, report this and stop iterating.
more = YES;
break;
}
}
return more;
}
//
//
// PCRCapGetHandles()
//
// This function is used to get a list of handles of PCR, started from handle. If handle exceeds the maximum
// PCR handle range, an empty list will be returned and the return value will be NO.
//
// Return Value Meaning
//
// YES if there are more handles available
// NO all the available handles has been returned
//
TPMI_YES_NO
PCRCapGetHandles(
TPMI_DH_PCR handle, // IN: start handle
UINT32 count, // IN: count of returned handle
TPML_HANDLE *handleList // OUT: list of handle
)
{
TPMI_YES_NO more = NO;
UINT32 i;
pAssert(HandleGetType(handle) == TPM_HT_PCR);
// Initialize output handle list
handleList->count = 0;
// The maximum count of handles we may return is MAX_CAP_HANDLES
if(count > MAX_CAP_HANDLES) count = MAX_CAP_HANDLES;
// Iterate PCR handle range
for(i = handle & HR_HANDLE_MASK; i <= PCR_LAST; i++)
{
if(handleList->count < count)
{
// If we have not filled up the return list, add this PCR
// handle to it
handleList->handle[handleList->count] = i + PCR_FIRST;
handleList->count++;
}
else
{
// If the return list is full but we still have PCR handle
// available, report this and stop iterating
more = YES;
break;
}
}
return more;
}