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