/* 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; }