text: /* Microsoft Reference Implementation for TPM 2.0
   *
   *  The copyright in this software is being made available under the BSD License,
   *  included below. This software may be subject to other third party and
   *  contributor rights, including patent rights, and no such rights are granted
   *  under this license.
   *
   *  Copyright (c) Microsoft Corporation
   *
   *  All rights reserved.
   *
   *  BSD License
   *
   *  Redistribution and use in source and binary forms, with or without modification,
   *  are permitted provided that the following conditions are met:
   *
   *  Redistributions of source code must retain the above copyright notice, this list
   *  of conditions and the following disclaimer.
   *
   *  Redistributions in binary form must reproduce the above copyright notice, this
   *  list of conditions and the following disclaimer in the documentation and/or
   *  other materials provided with the distribution.
   *
   *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ""AS IS""
   *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   *  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
   *  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
   *  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
   *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   */
  //** Description
  // This file contains the functions that are used for accessing the
  // TPM_CAP_TPM_PROPERTY values.
  
  //** Includes
  
  #include "Tpm.h"
  
  //** Functions
  
  //*** TPMPropertyIsDefined()
  // This function accepts a property selection and, if so, sets 'value'
  // to the value of the property.
  //
  // All the fixed values are vendor dependent or determined by a
  // platform-specific specification. The values in the table below
  // are examples and should be changed by the vendor.
  //  Return Type: BOOL
  //      TRUE(1)         referenced property exists and 'value' set
  //      FALSE(0)        referenced property does not exist
  static BOOL
  TPMPropertyIsDefined(
      TPM_PT           property,      // IN: property
      UINT32          *value          // OUT: property value
      )
  {
      switch(property)
      {
          case TPM_PT_FAMILY_INDICATOR:
              // from the title page of the specification
              // For this specification, the value is "2.0".
              *value = TPM_SPEC_FAMILY;
              break;
          case TPM_PT_LEVEL:
              // from the title page of the specification
              *value = TPM_SPEC_LEVEL;
              break;
          case TPM_PT_REVISION:
              // from the title page of the specification
              *value = TPM_SPEC_VERSION;
              break;
          case TPM_PT_DAY_OF_YEAR:
              // computed from the date value on the title page of the specification
              *value = TPM_SPEC_DAY_OF_YEAR;
              break;
          case TPM_PT_YEAR:
              // from the title page of the specification
              *value = TPM_SPEC_YEAR;
              break;
          case TPM_PT_MANUFACTURER:
              // vendor ID unique to each TPM manufacturer
              *value = BYTE_ARRAY_TO_UINT32(MANUFACTURER);
              break;
          case TPM_PT_VENDOR_STRING_1:
              // first four characters of the vendor ID string
              *value = BYTE_ARRAY_TO_UINT32(VENDOR_STRING_1);
              break;
          case TPM_PT_VENDOR_STRING_2:
              // second four characters of the vendor ID string
  #ifdef VENDOR_STRING_2
              *value = BYTE_ARRAY_TO_UINT32(VENDOR_STRING_2);
  #else
              *value = 0;
  #endif
              break;
          case TPM_PT_VENDOR_STRING_3:
              // third four characters of the vendor ID string
  #ifdef VENDOR_STRING_3
              *value = BYTE_ARRAY_TO_UINT32(VENDOR_STRING_3);
  #else
              *value = 0;
  #endif
              break;
          case TPM_PT_VENDOR_STRING_4:
              // fourth four characters of the vendor ID string
  #ifdef VENDOR_STRING_4
              *value = BYTE_ARRAY_TO_UINT32(VENDOR_STRING_4);
  #else
              *value = 0;
  #endif
              break;
          case TPM_PT_VENDOR_TPM_TYPE:
              // vendor-defined value indicating the TPM model
              *value = 1;
              break;
          case TPM_PT_FIRMWARE_VERSION_1:
              // more significant 32-bits of a vendor-specific value
              *value = gp.firmwareV1;
              break;
          case TPM_PT_FIRMWARE_VERSION_2:
              // less significant 32-bits of a vendor-specific value
              *value = gp.firmwareV2;
              break;
          case TPM_PT_INPUT_BUFFER:
              // maximum size of TPM2B_MAX_BUFFER
              *value = MAX_DIGEST_BUFFER;
              break;
          case TPM_PT_HR_TRANSIENT_MIN:
              // minimum number of transient objects that can be held in TPM
              // RAM
              *value = MAX_LOADED_OBJECTS;
              break;
          case TPM_PT_HR_PERSISTENT_MIN:
              // minimum number of persistent objects that can be held in
              // TPM NV memory
              // In this implementation, there is no minimum number of
              // persistent objects.
              *value = MIN_EVICT_OBJECTS;
              break;
          case TPM_PT_HR_LOADED_MIN:
              // minimum number of authorization sessions that can be held in
              // TPM RAM
              *value = MAX_LOADED_SESSIONS;
              break;
          case TPM_PT_ACTIVE_SESSIONS_MAX:
              // number of authorization sessions that may be active at a time
              *value = MAX_ACTIVE_SESSIONS;
              break;
          case TPM_PT_PCR_COUNT:
              // number of PCR implemented
              *value = IMPLEMENTATION_PCR;
              break;
          case TPM_PT_PCR_SELECT_MIN:
              // minimum number of bytes in a TPMS_PCR_SELECT.sizeOfSelect
              *value = PCR_SELECT_MIN;
              break;
          case TPM_PT_CONTEXT_GAP_MAX:
              // maximum allowed difference (unsigned) between the contextID
              // values of two saved session contexts
              *value = ((UINT32)1 << (sizeof(CONTEXT_SLOT) * 8)) - 1;
              break;
          case TPM_PT_NV_COUNTERS_MAX:
              // maximum number of NV indexes that are allowed to have the
              // TPMA_NV_COUNTER attribute SET
              // In this implementation, there is no limitation on the number
              // of counters, except for the size of the NV Index memory.
              *value = 0;
              break;
          case TPM_PT_NV_INDEX_MAX:
              // maximum size of an NV index data area
              *value = MAX_NV_INDEX_SIZE;
              break;
          case TPM_PT_MEMORY:
              // a TPMA_MEMORY indicating the memory management method for the TPM
          {
              union
              {
                  TPMA_MEMORY     att;
                  UINT32          u32;
              } attributes = { TPMA_ZERO_INITIALIZER() };
              SET_ATTRIBUTE(attributes.att, TPMA_MEMORY, sharedNV);
              SET_ATTRIBUTE(attributes.att, TPMA_MEMORY, objectCopiedToRam);
  
              // Note: For a LSb0 machine, the bits in a bit field are in the correct
              // order even if the machine is MSB0. For a MSb0 machine, a TPMA will
              // be an integer manipulated by masking (USE_BIT_FIELD_STRUCTURES will
              // be NO) so the bits are manipulate correctly.
              *value = attributes.u32;
              break;
          }
          case TPM_PT_CLOCK_UPDATE:
              // interval, in seconds, between updates to the copy of
              // TPMS_TIME_INFO .clock in NV
              *value = (1 << NV_CLOCK_UPDATE_INTERVAL);
              break;
          case TPM_PT_CONTEXT_HASH:
              // algorithm used for the integrity hash on saved contexts and
              // for digesting the fuData of TPM2_FirmwareRead()
              *value = CONTEXT_INTEGRITY_HASH_ALG;
              break;
          case TPM_PT_CONTEXT_SYM:
              // algorithm used for encryption of saved contexts
              *value = CONTEXT_ENCRYPT_ALG;
              break;
          case TPM_PT_CONTEXT_SYM_SIZE:
              // size of the key used for encryption of saved contexts
              *value = CONTEXT_ENCRYPT_KEY_BITS;
              break;
          case TPM_PT_ORDERLY_COUNT:
              // maximum difference between the volatile and non-volatile
              // versions of TPMA_NV_COUNTER that have TPMA_NV_ORDERLY SET
              *value = MAX_ORDERLY_COUNT;
              break;
          case TPM_PT_MAX_COMMAND_SIZE:
              // maximum value for 'commandSize'
              *value = MAX_COMMAND_SIZE;
              break;
          case TPM_PT_MAX_RESPONSE_SIZE:
              // maximum value for 'responseSize'
              *value = MAX_RESPONSE_SIZE;
              break;
          case TPM_PT_MAX_DIGEST:
              // maximum size of a digest that can be produced by the TPM
              *value = sizeof(TPMU_HA);
              break;
          case TPM_PT_MAX_OBJECT_CONTEXT:
  // Header has 'sequence', 'handle' and 'hierarchy'
  #define SIZE_OF_CONTEXT_HEADER                  \
      sizeof(UINT64) + sizeof(TPMI_DH_CONTEXT) + sizeof(TPMI_RH_HIERARCHY)
  #define SIZE_OF_CONTEXT_INTEGRITY (sizeof(UINT16) + CONTEXT_INTEGRITY_HASH_SIZE)
  #define SIZE_OF_FINGERPRINT     sizeof(UINT64)
  #define SIZE_OF_CONTEXT_BLOB_OVERHEAD                \
          (sizeof(UINT16)  + SIZE_OF_CONTEXT_INTEGRITY + SIZE_OF_FINGERPRINT)
  #define SIZE_OF_CONTEXT_OVERHEAD                    \
          (SIZE_OF_CONTEXT_HEADER + SIZE_OF_CONTEXT_BLOB_OVERHEAD)
  #if 0
              // maximum size of a TPMS_CONTEXT that will be returned by
              // TPM2_ContextSave for object context
              *value = 0;
              // adding sequence, saved handle and hierarchy
              *value += sizeof(UINT64) + sizeof(TPMI_DH_CONTEXT) +
                  sizeof(TPMI_RH_HIERARCHY);
              // add size field in TPM2B_CONTEXT
              *value += sizeof(UINT16);
              // add integrity hash size
              *value += sizeof(UINT16) +
                  CryptHashGetDigestSize(CONTEXT_INTEGRITY_HASH_ALG);
              // Add fingerprint size, which is the same as sequence size
              *value += sizeof(UINT64);
              // Add OBJECT structure size
              *value += sizeof(OBJECT);
  #else
              // the maximum size of a TPMS_CONTEXT that will be returned by
              // TPM2_ContextSave for object context
              *value = SIZE_OF_CONTEXT_OVERHEAD + sizeof(OBJECT);
  #endif
              break;
          case TPM_PT_MAX_SESSION_CONTEXT:
  #if 0
  
              // the maximum size of a TPMS_CONTEXT that will be returned by
              // TPM2_ContextSave for object context
              *value = 0;
              // adding sequence, saved handle and hierarchy
              *value += sizeof(UINT64) + sizeof(TPMI_DH_CONTEXT) +
                  sizeof(TPMI_RH_HIERARCHY);
              // Add size field in TPM2B_CONTEXT
              *value += sizeof(UINT16);
  // Add integrity hash size
              *value += sizeof(UINT16) +
                  CryptHashGetDigestSize(CONTEXT_INTEGRITY_HASH_ALG);
        // Add fingerprint size, which is the same as sequence size
              *value += sizeof(UINT64);
              // Add SESSION structure size
              *value += sizeof(SESSION);
  #else
               // the maximum size of a TPMS_CONTEXT that will be returned by
              // TPM2_ContextSave for object context
              *value = SIZE_OF_CONTEXT_OVERHEAD + sizeof(SESSION);
  #endif
              break;
          case TPM_PT_PS_FAMILY_INDICATOR:
              // platform specific values for the TPM_PT_PS parameters from
              // the relevant platform-specific specification
              // In this reference implementation, all of these values are 0.
              *value = PLATFORM_FAMILY;
              break;
          case TPM_PT_PS_LEVEL:
              // level of the platform-specific specification
              *value = PLATFORM_LEVEL;
              break;
          case TPM_PT_PS_REVISION:
              // specification Revision times 100 for the platform-specific
              // specification
              *value = PLATFORM_VERSION;
              break;
          case TPM_PT_PS_DAY_OF_YEAR:
              // platform-specific specification day of year using TCG calendar
              *value = PLATFORM_DAY_OF_YEAR;
              break;
          case TPM_PT_PS_YEAR:
              // platform-specific specification year using the CE
              *value = PLATFORM_YEAR;
              break;
          case TPM_PT_SPLIT_MAX:
              // number of split signing operations supported by the TPM
              *value = 0;
  #if ALG_ECC
              *value = sizeof(gr.commitArray) * 8;
  #endif
              break;
          case TPM_PT_TOTAL_COMMANDS:
              // total number of commands implemented in the TPM
              // Since the reference implementation does not have any
              // vendor-defined commands, this will be the same as the
              // number of library commands.
          {
  #if COMPRESSED_LISTS
              (*value) = COMMAND_COUNT;
  #else
              COMMAND_INDEX       commandIndex;
              *value = 0;
  
              // scan all implemented commands
              for(commandIndex = GetClosestCommandIndex(0);
              commandIndex != UNIMPLEMENTED_COMMAND_INDEX;
                  commandIndex = GetNextCommandIndex(commandIndex))
              {
                  (*value)++;     // count of all implemented
              }
  #endif
              break;
          }
          case TPM_PT_LIBRARY_COMMANDS:
              // number of commands from the TPM library that are implemented
          {
  #if COMPRESSED_LISTS
              *value = LIBRARY_COMMAND_ARRAY_SIZE;
  #else
              COMMAND_INDEX       commandIndex;
              *value = 0;
  
              // scan all implemented commands
              for(commandIndex = GetClosestCommandIndex(0);
              commandIndex < LIBRARY_COMMAND_ARRAY_SIZE;
                  commandIndex = GetNextCommandIndex(commandIndex))
              {
                  (*value)++;
              }
  #endif
              break;
          }
          case TPM_PT_VENDOR_COMMANDS:
              // number of vendor commands that are implemented
              *value = VENDOR_COMMAND_ARRAY_SIZE;
              break;
          case TPM_PT_NV_BUFFER_MAX:
              // Maximum data size in an NV write command
              *value = MAX_NV_BUFFER_SIZE;
              break;
          case TPM_PT_MODES:
  #if FIPS_COMPLIANT
              *value = 1;
  #else
              *value = 0;
  #endif
              break;
          case TPM_PT_MAX_CAP_BUFFER:
              *value = MAX_CAP_BUFFER;
              break;
  
          // Start of variable commands
          case TPM_PT_PERMANENT:
              // TPMA_PERMANENT
          {
              union {
                  TPMA_PERMANENT      attr;
                  UINT32              u32;
              } flags = { TPMA_ZERO_INITIALIZER() };
              if(gp.ownerAuth.t.size != 0)
                  SET_ATTRIBUTE(flags.attr, TPMA_PERMANENT, ownerAuthSet);
              if(gp.endorsementAuth.t.size != 0)
                  SET_ATTRIBUTE(flags.attr, TPMA_PERMANENT, endorsementAuthSet);
              if(gp.lockoutAuth.t.size != 0)
                  SET_ATTRIBUTE(flags.attr, TPMA_PERMANENT, lockoutAuthSet);
              if(gp.disableClear)
                  SET_ATTRIBUTE(flags.attr, TPMA_PERMANENT, disableClear);
              if(gp.failedTries >= gp.maxTries)
                  SET_ATTRIBUTE(flags.attr, TPMA_PERMANENT, inLockout);
              // In this implementation, EPS is always generated by TPM
              SET_ATTRIBUTE(flags.attr, TPMA_PERMANENT, tpmGeneratedEPS);
  
              // Note: For a LSb0 machine, the bits in a bit field are in the correct
              // order even if the machine is MSB0. For a MSb0 machine, a TPMA will
              // be an integer manipulated by masking (USE_BIT_FIELD_STRUCTURES will
              // be NO) so the bits are manipulate correctly.
              *value = flags.u32;
              break;
          }
          case TPM_PT_STARTUP_CLEAR:
              // TPMA_STARTUP_CLEAR
          {
              union {
                  TPMA_STARTUP_CLEAR  attr;
                  UINT32              u32;
              } flags = { TPMA_ZERO_INITIALIZER() };
  //
              if(g_phEnable)
                  SET_ATTRIBUTE(flags.attr, TPMA_STARTUP_CLEAR, phEnable);
              if(gc.shEnable)
                  SET_ATTRIBUTE(flags.attr, TPMA_STARTUP_CLEAR, shEnable);
              if(gc.ehEnable)
                  SET_ATTRIBUTE(flags.attr, TPMA_STARTUP_CLEAR, ehEnable);
              if(gc.phEnableNV)
                  SET_ATTRIBUTE(flags.attr, TPMA_STARTUP_CLEAR, phEnableNV);
              if(g_prevOrderlyState != SU_NONE_VALUE)
                  SET_ATTRIBUTE(flags.attr, TPMA_STARTUP_CLEAR, orderly);
  
              // Note: For a LSb0 machine, the bits in a bit field are in the correct
              // order even if the machine is MSB0. For a MSb0 machine, a TPMA will
              // be an integer manipulated by masking (USE_BIT_FIELD_STRUCTURES will
              // be NO) so the bits are manipulate correctly.
              *value = flags.u32;
              break;
          }
          case TPM_PT_HR_NV_INDEX:
              // number of NV indexes currently defined
              *value = NvCapGetIndexNumber();
              break;
          case TPM_PT_HR_LOADED:
              // number of authorization sessions currently loaded into TPM
              // RAM
              *value = SessionCapGetLoadedNumber();
              break;
          case TPM_PT_HR_LOADED_AVAIL:
              // number of additional authorization sessions, of any type,
              // that could be loaded into TPM RAM
              *value = SessionCapGetLoadedAvail();
              break;
          case TPM_PT_HR_ACTIVE:
              // number of active authorization sessions currently being
              // tracked by the TPM
              *value = SessionCapGetActiveNumber();
              break;
          case TPM_PT_HR_ACTIVE_AVAIL:
              // number of additional authorization sessions, of any type,
              // that could be created
              *value = SessionCapGetActiveAvail();
              break;
          case TPM_PT_HR_TRANSIENT_AVAIL:
              // estimate of the number of additional transient objects that
              // could be loaded into TPM RAM
              *value = ObjectCapGetTransientAvail();
              break;
          case TPM_PT_HR_PERSISTENT:
              // number of persistent objects currently loaded into TPM
              // NV memory
              *value = NvCapGetPersistentNumber();
              break;
          case TPM_PT_HR_PERSISTENT_AVAIL:
              // number of additional persistent objects that could be loaded
              // into NV memory
              *value = NvCapGetPersistentAvail();
              break;
          case TPM_PT_NV_COUNTERS:
              // number of defined NV indexes that have NV TPMA_NV_COUNTER
              // attribute SET
              *value = NvCapGetCounterNumber();
              break;
          case TPM_PT_NV_COUNTERS_AVAIL:
              // number of additional NV indexes that can be defined with their
              // TPMA_NV_COUNTER attribute SET
              *value = NvCapGetCounterAvail();
              break;
          case TPM_PT_ALGORITHM_SET:
              // region code for the TPM
              *value = gp.algorithmSet;
              break;
          case TPM_PT_LOADED_CURVES:
  #if ALG_ECC
          // number of loaded ECC curves
              *value = ECC_CURVE_COUNT;
  #else // ALG_ECC
              *value = 0;
  #endif // ALG_ECC
              break;
          case TPM_PT_LOCKOUT_COUNTER:
              // current value of the lockout counter
              *value = gp.failedTries;
              break;
          case TPM_PT_MAX_AUTH_FAIL:
              // number of authorization failures before DA lockout is invoked
              *value = gp.maxTries;
              break;
          case TPM_PT_LOCKOUT_INTERVAL:
              // number of seconds before the value reported by
              // TPM_PT_LOCKOUT_COUNTER is decremented
              *value = gp.recoveryTime;
              break;
          case TPM_PT_LOCKOUT_RECOVERY:
              // number of seconds after a lockoutAuth failure before use of
              // lockoutAuth may be attempted again
              *value = gp.lockoutRecovery;
              break;
          case TPM_PT_NV_WRITE_RECOVERY:
              // number of milliseconds before the TPM will accept another command
              // that will modify NV.
              // This should make a call to the platform code that is doing rate
              // limiting of NV. Rate limiting is not implemented in the reference
              // code so no call is made.
              *value = 0;
              break;
          case TPM_PT_AUDIT_COUNTER_0:
              // high-order 32 bits of the command audit counter
              *value = (UINT32)(gp.auditCounter >> 32);
              break;
          case TPM_PT_AUDIT_COUNTER_1:
              // low-order 32 bits of the command audit counter
              *value = (UINT32)(gp.auditCounter);
              break;
          default:
              // property is not defined
              return FALSE;
              break;
      }
      return TRUE;
  }
  
  //*** TPMCapGetProperties()
  // This function is used to get the TPM_PT values. The search of properties will
  // start at 'property' and continue until 'propertyList' has as many values as
  // will fit, or the last property has been reported, or the list has as many
  // values as requested in 'count'.
  //  Return Type: TPMI_YES_NO
  //  YES        more properties are available
  //  NO         no more properties to be reported
  TPMI_YES_NO
  TPMCapGetProperties(
      TPM_PT                       property,      // IN: the starting TPM property
      UINT32                       count,         // IN: maximum number of returned
                                                  //     properties
      TPML_TAGGED_TPM_PROPERTY    *propertyList   // OUT: property list
      )
  {
      TPMI_YES_NO     more = NO;
      UINT32          i;
      UINT32          nextGroup;
  
      // initialize output property list
      propertyList->count = 0;
  
      // maximum count of properties we may return is MAX_PCR_PROPERTIES
      if(count > MAX_TPM_PROPERTIES) count = MAX_TPM_PROPERTIES;
  
      // if property is less than PT_FIXED, start from PT_FIXED
      if(property < PT_FIXED)
          property = PT_FIXED;
      // There is only the fixed and variable groups with the variable group coming
      // last
      if(property >= (PT_VAR + PT_GROUP))
          return more;
  
      // Don't read past the end of the selected group
      nextGroup = ((property / PT_GROUP) * PT_GROUP) + PT_GROUP;
  
      // Scan through the TPM properties of the requested group.
      for(i = property; i < nextGroup; i++)
      {
          UINT32          value;
          // if we have hit the end of the group, quit
          if(i != property && ((i % PT_GROUP) == 0))
              break;
          if(TPMPropertyIsDefined((TPM_PT)i, &value))
          {
              if(propertyList->count < count)
              {
                  // If the list is not full, add this property
                  propertyList->tpmProperty[propertyList->count].property =
                      (TPM_PT)i;
                  propertyList->tpmProperty[propertyList->count].value = value;
                  propertyList->count++;
              }
              else
              {
                  // If the return list is full but there are more properties
                  // available, set the indication and exit the loop.
                  more = YES;
                  break;
              }
          }
      }
      return more;
  }