/********************************************************************************/ /* */ /* PCR access and manipulation */ /* Written by Ken Goldman */ /* IBM Thomas J. Watson Research Center */ /* */ /* Licenses and Notices */ /* */ /* 1. Copyright Licenses: */ /* */ /* - Trusted Computing Group (TCG) grants to the user of the source code in */ /* this specification (the "Source Code") a worldwide, irrevocable, */ /* nonexclusive, royalty free, copyright license to reproduce, create */ /* derivative works, distribute, display and perform the Source Code and */ /* derivative works thereof, and to grant others the rights granted herein. */ /* */ /* - The TCG grants to the user of the other parts of the specification */ /* (other than the Source Code) the rights to reproduce, distribute, */ /* display, and perform the specification solely for the purpose of */ /* developing products based on such documents. */ /* */ /* 2. Source Code Distribution Conditions: */ /* */ /* - Redistributions of Source Code must retain the above copyright licenses, */ /* this list of conditions and the following disclaimers. */ /* */ /* - Redistributions in binary form must reproduce the above copyright */ /* licenses, this list of conditions and the following disclaimers in the */ /* documentation and/or other materials provided with the distribution. */ /* */ /* 3. Disclaimers: */ /* */ /* - THE COPYRIGHT LICENSES SET FORTH ABOVE DO NOT REPRESENT ANY FORM OF */ /* LICENSE OR WAIVER, EXPRESS OR IMPLIED, BY ESTOPPEL OR OTHERWISE, WITH */ /* RESPECT TO PATENT RIGHTS HELD BY TCG MEMBERS (OR OTHER THIRD PARTIES) */ /* THAT MAY BE NECESSARY TO IMPLEMENT THIS SPECIFICATION OR OTHERWISE. */ /* Contact TCG Administration (admin@trustedcomputinggroup.org) for */ /* information on specification licensing rights available through TCG */ /* membership agreements. */ /* */ /* - THIS SPECIFICATION IS PROVIDED "AS IS" WITH NO EXPRESS OR IMPLIED */ /* WARRANTIES WHATSOEVER, INCLUDING ANY WARRANTY OF MERCHANTABILITY OR */ /* FITNESS FOR A PARTICULAR PURPOSE, ACCURACY, COMPLETENESS, OR */ /* NONINFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS, OR ANY WARRANTY */ /* OTHERWISE ARISING OUT OF ANY PROPOSAL, SPECIFICATION OR SAMPLE. */ /* */ /* - Without limitation, TCG and its members and licensors disclaim all */ /* liability, including liability for infringement of any proprietary */ /* rights, relating to use of information in this specification and to the */ /* implementation of this specification, and TCG disclaims all liability for */ /* cost of procurement of substitute goods or services, lost profits, loss */ /* of use, loss of data or any incidental, consequential, direct, indirect, */ /* or special damages, whether under contract, tort, warranty or otherwise, */ /* arising in any way out of use or reliance upon this specification or any */ /* information herein. */ /* */ /* (c) Copyright IBM Corp. and others, 2016 - 2023 */ /* */ /********************************************************************************/ //** 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" // verify values from pcrstruct.h. not <= because group #0 is reserved // indicating no auth/policy support TPM_STATIC_ASSERT(NUM_AUTHVALUE_PCR_GROUP < (1 << MAX_PCR_GROUP_BITS)); TPM_STATIC_ASSERT(NUM_POLICY_PCR_GROUP < (1 << MAX_PCR_GROUP_BITS)); //** 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 array index if PCR // belongs to a group that allows authValue. If PCR // does not belong to an authorization // group, the value in this parameter is zero ) { *groupIndex = 0; #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 UINT32 pcr = handle - PCR_FIRST; PCR_Attributes currentPcrAttributes = _platPcr__GetPcrInitializationAttributes(pcr); if(currentPcrAttributes.authValuesGroup != 0) { // turn 1-based group number into actual array index expected by callers *groupIndex = currentPcrAttributes.authValuesGroup - 1; pAssert_BOOL(*groupIndex < NUM_AUTHVALUE_PCR_GROUP); 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: 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 zero ) { *groupIndex = 0; #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. UINT32 pcr = handle - PCR_FIRST; PCR_Attributes currentPcrAttributes = _platPcr__GetPcrInitializationAttributes(pcr); if(currentPcrAttributes.policyAuthGroup != 0) { // turn 1-based group number into actual array index expected by callers *groupIndex = currentPcrAttributes.policyAuthGroup - 1; pAssert_BOOL(*groupIndex < NUM_POLICY_PCR_GROUP); return TRUE; } #endif return FALSE; } //*** PCRBelongsTCBGroup() // This function indicates if a PCR belongs to the TCB group. // return type: BOOL // 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. UINT32 pcr = handle - PCR_FIRST; PCR_Attributes currentPcrAttributes = _platPcr__GetPcrInitializationAttributes(pcr); return currentPcrAttributes.doNotIncrementPcrCounter; #else return FALSE; #endif } //*** PCRPolicyIsAvailable() // This function indicates if a policy is available for a PCR. // return type: BOOL // TRUE the PCR may 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 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; } } //*** PCRManufacture() // 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 PCRManufacture(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++) { TPM_ALG_ID currentBank = CryptHashGetAlgByIndex(gp.pcrAllocated.count); BOOL isBankActive = _platPcr_IsPcrBankDefaultActive(currentBank); gp.pcrAllocated.pcrSelections[gp.pcrAllocated.count].hash = currentBank; 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] = isBankActive ? 0xFF : 0; } } // 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 = NULL; 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_NULL(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; } // Get pointer to particular PCR from bank (array) // CAUTION: This function does not validate the pcrNumber // vs the size of the array. // See Also: GetPcrPointerIfAllocated static BYTE* GetPcrPointerFromPcrArray(PCR* pPcrArray, TPM_ALG_ID alg, // IN: algorithm for bank UINT32 pcrNumber // IN: PCR number ) { switch(alg) { #if ALG_SHA1 case TPM_ALG_SHA1: return pPcrArray[pcrNumber].Sha1Pcr; #endif #if ALG_SHA256 case TPM_ALG_SHA256: return pPcrArray[pcrNumber].Sha256Pcr; #endif #if ALG_SHA384 case TPM_ALG_SHA384: return pPcrArray[pcrNumber].Sha384; #endif #if ALG_SHA512 case TPM_ALG_SHA512: return pPcrArray[pcrNumber].Sha512; #endif #if ALG_SM3_256 case TPM_ALG_SM3_256: return pPcrArray[pcrNumber].Sm3_256; #endif #if ALG_SHA3_256 case TPM_ALG_SHA3_256: return pPcrArray[pcrNumber].Sha3_256; #endif #if ALG_SHA3_384 case TPM_ALG_SHA3_384: return pPcrArray[pcrNumber].Sha3_384; #endif #if ALG_SHA3_512 case TPM_ALG_SHA3_512: return pPcrArray[pcrNumber].Sha3_512; #endif default: FAIL(FATAL_ERROR_INTERNAL); break; } return NULL; } BYTE* GetPcrPointerIfAllocated(PCR* pPcrArray, TPM_ALG_ID alg, // IN: algorithm for bank UINT32 pcrNumber // IN: PCR number ) { // if(!PcrIsAllocated(pcrNumber, alg)) return NULL; return GetPcrPointerFromPcrArray(pPcrArray, alg, // IN: algorithm for bank pcrNumber // IN: PCR number ); } //*** 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 requested PCR BYTE* GetPcrPointer(TPM_ALG_ID alg, // IN: algorithm for bank UINT32 pcrNumber // IN: PCR number ) { return GetPcrPointerIfAllocated(s_pcrs, alg, pcrNumber); } //*** 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; } // check the TPM library and platform are properly paired. // if this fails the platform and library are compiled with different // definitions of the number of PCRs - immediately enter FAILURE mode and // return FALSE pAssert_BOOL(_platPcr__NumberOfPcrs() == IMPLEMENTATION_PCR); // 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; // note structure is a bitfield and returned by value. PCR_Attributes currentPcrAttributes = _platPcr__GetPcrInitializationAttributes(pcr); if(type == SU_RESUME && currentPcrAttributes.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(hash, saveIndex); if(pcrSavedData == NULL) return FALSE; MemoryCopy(pcrData, pcrSavedData, pcrSize); } else // PCR was not restored by state save { // give platform opportunity to provide the PCR initialization // value and it's length. this provides a platform specification // the ability to change the default values without affecting the // core library. if the platform doesn't have a value, then the // result is expected to be TPM_RC_PCR and the size to be 0 and we // provide the original defaults. uint16_t pcrLength = 0; TPM_RC pcrInitialResult = _platPcr__GetInitialValueForPcr( pcr, hash, locality, pcrData, pcrSize, &pcrLength); // any other result is a fatal error pAssert_BOOL(pcrInitialResult == TPM_RC_SUCCESS || pcrInitialResult == TPM_RC_PCR); if(pcrInitialResult == TPM_RC_SUCCESS && pcrLength == pcrSize) { // just use the PCR initialized by platform } else { // If the reset locality contains locality 4, then this // indicates a DRTM PCR where the reset value is all ones, // otherwise it is all zero. Don't check with equal because // resetLocality is a bitfield of multiple values and does // not support extended localities. BYTE defaultValue = 0; if((currentPcrAttributes.resetLocality & 0x10) != 0) { defaultValue = 0xFF; } MemorySet(pcrData, defaultValue, 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++) { PCR_Attributes currentPcrAttributes = _platPcr__GetPcrInitializationAttributes(pcr); UINT32 stateSaved = (currentPcrAttributes.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; PCR_Attributes currentPcrAttributes = _platPcr__GetPcrInitializationAttributes(pcr); if(currentPcrAttributes.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; PCR_Attributes currentPcrAttributes = _platPcr__GetPcrInitializationAttributes(pcr); // 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 & currentPcrAttributes.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; PCR_Attributes currentPcrAttributes = _platPcr__GetPcrInitializationAttributes(pcr); // Check for the locality commandLocality = _plat__LocalityGet(); localityBits = localityBits << commandLocality; if((localityBits & currentPcrAttributes.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 = _platPcr__NumberOfPcrs(); 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; PCR_Attributes currentPcrAttributes = _platPcr__GetPcrInitializationAttributes(pcr); 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((currentPcrAttributes.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 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++) { PCR_Attributes currentPcrAttributes = _platPcr__GetPcrInitializationAttributes(pcr); switch(property) { case TPM_PT_PCR_SAVE: if(currentPcrAttributes.stateSave == SET) PCRSetSelectBit(pcr, select->pcrSelect); break; case TPM_PT_PCR_EXTEND_L0: if((currentPcrAttributes.extendLocality & 0x01) != 0) PCRSetSelectBit(pcr, select->pcrSelect); break; case TPM_PT_PCR_RESET_L0: if((currentPcrAttributes.resetLocality & 0x01) != 0) PCRSetSelectBit(pcr, select->pcrSelect); break; case TPM_PT_PCR_EXTEND_L1: if((currentPcrAttributes.extendLocality & 0x02) != 0) PCRSetSelectBit(pcr, select->pcrSelect); break; case TPM_PT_PCR_RESET_L1: if((currentPcrAttributes.resetLocality & 0x02) != 0) PCRSetSelectBit(pcr, select->pcrSelect); break; case TPM_PT_PCR_EXTEND_L2: if((currentPcrAttributes.extendLocality & 0x04) != 0) PCRSetSelectBit(pcr, select->pcrSelect); break; case TPM_PT_PCR_RESET_L2: if((currentPcrAttributes.resetLocality & 0x04) != 0) PCRSetSelectBit(pcr, select->pcrSelect); break; case TPM_PT_PCR_EXTEND_L3: if((currentPcrAttributes.extendLocality & 0x08) != 0) PCRSetSelectBit(pcr, select->pcrSelect); break; case TPM_PT_PCR_RESET_L3: if((currentPcrAttributes.resetLocality & 0x08) != 0) PCRSetSelectBit(pcr, select->pcrSelect); break; case TPM_PT_PCR_EXTEND_L4: if((currentPcrAttributes.extendLocality & 0x10) != 0) PCRSetSelectBit(pcr, select->pcrSelect); break; case TPM_PT_PCR_RESET_L4: if((currentPcrAttributes.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((currentPcrAttributes.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 MUST_BE(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; } //*** PCRCapGetOneHandle() // This function is used to check whether a PCR handle exists. BOOL PCRCapGetOneHandle(TPMI_DH_PCR handle) // IN: handle { pAssert(HandleGetType(handle) == TPM_HT_PCR); if((handle & HR_HANDLE_MASK) <= PCR_LAST) { return TRUE; } return FALSE; }