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