blob: c85318b62e45abadcae1902a31824c32b75e12e5 [file] [log] [blame]
/* Microsoft Reference Implementation for TPM 2.0
*
* The copyright in this software is being made available under the BSD License,
* included below. This software may be subject to other third party and
* contributor rights, including patent rights, and no such rights are granted
* under this license.
*
* Copyright (c) Microsoft Corporation
*
* All rights reserved.
*
* BSD License
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this list
* of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ""AS IS""
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
//** Introduction
//
// This function contains the functions needed for PCR access and manipulation.
//
// This implementation uses a static allocation for the PCR. The amount of
// memory is allocated based on the number of PCR in the implementation and
// the number of implemented hash algorithms. This is not the expected
// implementation. PCR SPACE DEFINITIONS.
//
// In the definitions below, the g_hashPcrMap is a bit array that indicates
// which of the PCR are implemented. The g_hashPcr array is an array of digests.
// In this implementation, the space is allocated whether the PCR is implemented
// or not.
//** Includes, Defines, and Data Definitions
#define PCR_C
#include "Tpm.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, 0x14, 0x0E}, // PCR 20, Locality 1
{0, 0x14, 0x04}, // PCR 21, Dynamic OS
{0, 0x14, 0x04}, // PCR 22, Dynamic OS
{0, 0x0F, 0x1F}, // PCR 23, Application 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 Type: BOOL
// TRUE(1) PCR belongs an authorization group
// FALSE(0) PCR does not belong an authorization 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 authorization
// group, the value in this parameter is
// invalid
)
{
#if defined NUM_AUTHVALUE_PCR_GROUP && NUM_AUTHVALUE_PCR_GROUP > 0
// Platform specification determines to which authorization group a PCR belongs
// (if any). In this implementation, we assume there is only
// one authorization group which contains PCR[20-22]. If the platform
// specification requires differently, the implementation should be changed
// accordingly
if(handle >= 20 && handle <= 22)
{
*groupIndex = 0;
return TRUE;
}
#endif
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.
//
// Return Type: BOOL
// TRUE(1) PCR belongs to a policy group
// FALSE(0) PCR does not belong to 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
)
{
#if defined NUM_POLICY_PCR_GROUP && NUM_POLICY_PCR_GROUP > 0
// Platform specification decides if a PCR belongs to a policy group and
// belongs to which group. In this implementation, we assume there is only
// one policy group which contains PCR20-22. If the platform specification
// requires differently, the implementation should be changed accordingly
if(handle >= 20 && handle <= 22)
{
*groupIndex = 0;
return TRUE;
}
#endif
return FALSE;
}
//*** PCRBelongsTCBGroup()
// This function indicates if a PCR belongs to the TCB group.
//
// Return Type: BOOL
// TRUE(1) PCR belongs to a TCB group
// FALSE(0) PCR does not belong to a 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[20-22] belong to TCB group. If the platform
// specification requires differently, the implementation should be
// changed accordingly
if(handle >= 20 && handle <= 22)
return TRUE;
#endif
return FALSE;
}
//*** PCRPolicyIsAvailable()
// This function indicates if a policy is available for a PCR.
//
// Return Type: BOOL
// TRUE(1) the PCR may be authorized by policy
// FALSE(0) 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 EmptyAuth will be returned.
TPM2B_AUTH *
PCRGetAuthValue(
TPMI_DH_PCR handle // IN: PCR handle
)
{
UINT32 groupIndex;
if(PCRBelongsAuthGroup(handle, &groupIndex))
{
return &gc.pcrAuthValues.auth[groupIndex];
}
else
{
return NULL;
}
}
//*** 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;
#if defined NUM_POLICY_PCR_GROUP && NUM_POLICY_PCR_GROUP > 0
for(i = 0; i < NUM_POLICY_PCR_GROUP; i++)
{
gp.pcrPolicies.hashAlg[i] = TPM_ALG_NULL;
gp.pcrPolicies.policy[i].t.size = 0;
}
#endif
#if defined NUM_AUTHVALUE_PCR_GROUP && NUM_AUTHVALUE_PCR_GROUP > 0
for(i = 0; i < NUM_AUTHVALUE_PCR_GROUP; i++)
{
gc.pcrAuthValues.auth[i].t.size = 0;
}
#endif
// 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
= CryptHashGetAlgByIndex(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
NV_SYNC_PERSISTENT(pcrPolicies);
NV_SYNC_PERSISTENT(pcrAllocated);
return;
}
//*** GetSavedPcrPointer()
// This function returns the address of an array of state saved PCR based
// on the hash algorithm.
//
// Return Type: BYTE *
// NULL no such algorithm
// != 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
)
{
BYTE *retVal;
switch(alg)
{
#define HASH_CASE(HASH, Hash) \
case TPM_ALG_##HASH: \
retVal = gc.pcrSave.Hash[pcrIndex]; \
break;
FOR_EACH_HASH(HASH_CASE)
#undef HASH_CASE
default:
FAIL(FATAL_ERROR_INTERNAL);
}
return retVal;
}
//*** PcrIsAllocated()
// This function indicates if a PCR number for the particular hash algorithm
// is allocated.
// Return Type: BOOL
// TRUE(1) PCR is allocated
// FALSE(0) PCR is not 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 Type: BYTE *
// NULL no such algorithm
// != 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)
{
#define HASH_CASE(HASH, Hash) \
case TPM_ALG_##HASH: \
pcr = s_pcrs[pcrNumber].Hash##Pcr; \
break;
FOR_EACH_HASH(HASH_CASE)
#undef HASH_CASE
default:
FAIL(FATAL_ERROR_INTERNAL);
break;
}
return pcr;
}
//*** IsPcrSelected()
// This function indicates if an indicated PCR number is selected by the bit map in
// 'selection'.
//
// Return Type: BOOL
// TRUE(1) PCR is selected
// FALSE(0) PCR is not selected
static BOOL
IsPcrSelected(
UINT32 pcr, // IN: The number of the PCR
TPMS_PCR_SELECTION *selection // IN: The selection structure
)
{
BOOL selected;
selected = (pcr < IMPLEMENTATION_PCR
&& ((selection->pcrSelect[pcr / 8]) & (1 << (pcr % 8))) != 0);
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);
}
}
//*** PCR_ClearAuth()
// This function is used to reset the PCR authorization values. It is called
// on TPM2_Startup(CLEAR) and TPM2_Clear().
void
PCR_ClearAuth(
void
)
{
#if defined NUM_AUTHVALUE_PCR_GROUP && NUM_AUTHVALUE_PCR_GROUP > 0
int j;
for(j = 0; j < NUM_AUTHVALUE_PCR_GROUP; j++)
{
gc.pcrAuthValues.auth[j].t.size = 0;
}
#endif
}
//*** PCRStartup()
// This function initializes the PCR subsystem at TPM2_Startup().
BOOL
PCRStartup(
STARTUP_TYPE type, // IN: startup type
BYTE locality // IN: startup locality
)
{
UINT32 pcr, j;
UINT32 saveIndex = 0;
g_pcrReConfig = FALSE;
// Don't test for SU_RESET because that should be the default when nothing
// else is selected
if(type != SU_RESUME && type != SU_RESTART)
{
// PCR generation counter is cleared at TPM_RESET
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;
if(type == SU_RESUME
&& s_initAttributes[pcr].stateSave == SET)
{
stateSaved = 1;
}
else
{
stateSaved = 0;
PCRChanged(pcr);
}
// 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 = CryptHashGetDigestSize(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);
if(pcrSavedData == NULL)
return FALSE;
MemoryCopy(pcrData, pcrSavedData, 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 on TPM2_Startup(CLEAR)
if(type != SU_RESUME)
PCR_ClearAuth();
return TRUE;
}
//*** 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
= CryptHashGetDigestSize(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);
}
}
}
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 Type: BOOL
// TRUE(1) PCR is state saved
// FALSE(0) 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 Type: BOOL
// TRUE(1) TPM2_PCR_Reset is allowed
// FALSE(0) 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. Will also bump the counter if the
// handle is zero which means that PCR 0 can not be in the TCB group. Bump on zero
// is used by TPM2_Clear().
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((pcrHandle == 0) || !PCRBelongsTCBGroup(pcrHandle))
{
gr.pcrCounter++;
if(gr.pcrCounter == 0)
FAIL(FATAL_ERROR_COUNTER_OVERFLOW);
}
}
//*** 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 Type: BOOL
// TRUE(1) extend is allowed
// FALSE(0) 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
)
{
BYTE *pcrData;
HASH_STATE hashState;
UINT16 pcrSize;
pcrData = GetPcrPointer(hash, handle - PCR_FIRST);
// Extend PCR if it is allocated
if(pcrData != NULL)
{
pcrSize = CryptHashGetDigestSize(hash);
CryptHashStart(&hashState, hash);
CryptDigestUpdate(&hashState, pcrSize, pcrData);
CryptDigestUpdate(&hashState, size, data);
CryptHashEnd(&hashState, pcrSize, pcrData);
// PCR has changed so update the pcrCounter if necessary
PCRChanged(handle);
}
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 = CryptHashStart(&hashState, hashAlg);
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 = CryptHashGetDigestSize(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);
CryptDigestUpdate(&hashState, pcrSize, pcrData); // add to digest
}
}
}
// Complete hash stack
CryptHashEnd2B(&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 =
CryptHashGetDigestSize(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->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;
}
//*** PCRAllocate()
// This function is used to change the PCR allocation.
// Return Type: TPM_RC
// TPM_RC_NO_RESULT 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
= CryptHashGetDigestSize(newAllocate.pcrSelections[i].hash);
#if defined(DRTM_PCR)
// Make sure that we end up with at least one DRTM PCR
pcrDrtm = pcrDrtm || TestBit(DRTM_PCR,
newAllocate.pcrSelections[i].pcrSelect,
newAllocate.pcrSelections[i].sizeofSelect);
#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...)
pcrHcrtm = pcrHcrtm || TestBit(HCRTM_PCR,
newAllocate.pcrSelections[i].pcrSelect,
newAllocate.pcrSelections[i].sizeofSelect);
#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
NV_WRITE_PERSISTENT(pcrAllocated, 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 = CryptHashGetAlgByIndex(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 = CryptHashGetDigestSize(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 =
CryptHashGetDigestSize(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 Type: TPMI_YES_NO
// 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 Type: BOOL
// TRUE(1) the property type is implemented
// FALSE(0) 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 defined NUM_POLICY_PCR_GROUP && NUM_POLICY_PCR_GROUP > 0
case TPM_PT_PCR_POLICY:
if(PCRBelongsPolicyGroup(pcr + PCR_FIRST, &groupIndex))
PCRSetSelectBit(pcr, select->pcrSelect);
break;
#endif
#if defined NUM_AUTHVALUE_PCR_GROUP && 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 Type: TPMI_YES_NO
// 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 properties
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
cAssert(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 Type: TPMI_YES_NO
// 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 handles
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;
}