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