/* 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.
 */
//** Includes, Defines, and Types
#include "Tpm.h"
#include <stdio.h>
#include <assert.h>
#include "Marshal.h"

#if RUNTIME_SIZE_CHECKS

#  if DEBUG
static int once = 0;
#  endif

//** TpmSizeChecks()
// This function is used during the development process to make sure that the
// vendor-specific values result in a consistent implementation. When possible,
// the code contains #if to do compile-time checks. However, in some cases, the
// values require the use of "sizeof()" and that can't be used in an #if.
BOOL TpmSizeChecks(void)
{
    BOOL PASS = TRUE;

#  if DEBUG
    //
    if(once++ != 0)
        return 1;

#    if ALG_ECC
    {
        // This is just to allow simple access to the ecc curve data during debug
        const ECC_CURVE* ecc = CryptEccGetParametersByCurveId(3);
        if(ecc == NULL)
            ecc = NULL;
    }
#    endif  // ALG_ECC
    {
        UINT32 maxAsymSecurityStrength  = MAX_ASYM_SECURITY_STRENGTH;
        UINT32 maxHashSecurityStrength  = MAX_HASH_SECURITY_STRENGTH;
        UINT32 maxSymSecurityStrength   = MAX_SYM_SECURITY_STRENGTH;
        UINT32 maxSecurityStrengthBits  = MAX_SECURITY_STRENGTH_BITS;
        UINT32 proofSize                = PROOF_SIZE;
        UINT32 compliantProofSize       = COMPLIANT_PROOF_SIZE;
        UINT32 compliantPrimarySeedSize = COMPLIANT_PRIMARY_SEED_SIZE;
        UINT32 primarySeedSize          = PRIMARY_SEED_SIZE;

        UINT32 cmacState                = sizeof(tpmCmacState_t);
        UINT32 hashState                = sizeof(HASH_STATE);
        UINT32 keyScheduleSize          = sizeof(tpmCryptKeySchedule_t);
        //
        NOT_REFERENCED(cmacState);
        NOT_REFERENCED(hashState);
        NOT_REFERENCED(keyScheduleSize);
        NOT_REFERENCED(maxAsymSecurityStrength);
        NOT_REFERENCED(maxHashSecurityStrength);
        NOT_REFERENCED(maxSymSecurityStrength);
        NOT_REFERENCED(maxSecurityStrengthBits);
        NOT_REFERENCED(proofSize);
        NOT_REFERENCED(compliantProofSize);
        NOT_REFERENCED(compliantPrimarySeedSize);
        NOT_REFERENCED(primarySeedSize);

        {
            TPMT_SENSITIVE* p;
            // This assignment keeps compiler from complaining about a conditional
            // comparison being between two constants
            UINT16 max_rsa_key_bytes = MAX_RSA_KEY_BYTES;
            if((max_rsa_key_bytes / 2) != (sizeof(p->sensitive.rsa.t.buffer) / 5))
            {
                printf("Sensitive part of TPMT_SENSITIVE is undersized. May be caused"
                       " by use of wrong version of Part 2.\n");
                PASS = FALSE;
            }
        }
#    if TABLE_DRIVEN_MARSHAL
        printf("sizeof(MarshalData) = %zu\n", sizeof(MarshalData_st));
#    endif

        printf("Size of OBJECT = %zu\n", sizeof(OBJECT));
        printf("Size of components in TPMT_SENSITIVE = %zu\n",
               sizeof(TPMT_SENSITIVE));
        printf("    TPMI_ALG_PUBLIC                 %zu\n", sizeof(TPMI_ALG_PUBLIC));
        printf("    TPM2B_AUTH                      %zu\n", sizeof(TPM2B_AUTH));
        printf("    TPM2B_DIGEST                    %zu\n", sizeof(TPM2B_DIGEST));
        printf("    TPMU_SENSITIVE_COMPOSITE        %zu\n",
               sizeof(TPMU_SENSITIVE_COMPOSITE));
    }
    // Make sure that the size of the context blob is large enough for the largest
    // context
    // TPMS_CONTEXT_DATA contains two TPM2B values. That is not how this is
    // implemented. Rather, the size field of the TPM2B_CONTEXT_DATA is used to
    // determine the amount of data in the encrypted data. That part is not
    // independently sized. This makes the actual size 2 bytes smaller than
    // calculated using Part 2. Since this is opaque to the caller, it is not
    // necessary to fix. The actual size is returned by TPM2_GetCapabilties().

    // Initialize output handle.  At the end of command action, the output
    // handle of an object will be replaced, while the output handle
    // for a session will be the same as input

    // Get the size of fingerprint in context blob.  The sequence value in
    // TPMS_CONTEXT structure is used as the fingerprint
    {
        UINT32 fingerprintSize = sizeof(UINT64);
        UINT32 integritySize =
            sizeof(UINT16) + CryptHashGetDigestSize(CONTEXT_INTEGRITY_HASH_ALG);
        UINT32 biggestObject =
            MAX(MAX(sizeof(HASH_OBJECT), sizeof(OBJECT)), sizeof(SESSION));
        UINT32 biggestContext = fingerprintSize + integritySize + biggestObject;

        // round required size up to nearest 8 byte boundary.
        biggestContext = 8 * ((biggestContext + 7) / 8);

        if(MAX_CONTEXT_SIZE < biggestContext)
        {
            printf("MAX_CONTEXT_SIZE needs to be increased to at least %d (%d)\n",
                   biggestContext,
                   MAX_CONTEXT_SIZE);
            PASS = FALSE;
        }
        else if(MAX_CONTEXT_SIZE > biggestContext)
        {
            printf("MAX_CONTEXT_SIZE can be reduced to %d (%d)\n",
                   biggestContext,
                   MAX_CONTEXT_SIZE);
        }
    }
    {
        union u
        {
            TPMA_OBJECT attributes;
            UINT32      uint32Value;
        } u;
        // these are defined so that compiler doesn't complain about conditional
        // expressions comparing two constants.
        int aSize     = sizeof(u.attributes);
        int uSize     = sizeof(u.uint32Value);
        u.uint32Value = 0;
        SET_ATTRIBUTE(u.attributes, TPMA_OBJECT, fixedTPM);
        if(u.uint32Value != 2)
        {
            printf("The bit allocation in a TPMA_OBJECT is not as expected");
            PASS = FALSE;
        }
        if(aSize != uSize)  // comparison of two sizeof() values annoys compiler
        {
            printf("A TPMA_OBJECT is not the expected size.");
            PASS = FALSE;
        }
    }
    // Check that the platorm implementes each of the ACT that the TPM thinks are
    // present
    {
        uint32_t act;
        for(act = 0; act < 16; act++)
        {
            switch(act)
            {
                FOR_EACH_ACT(CASE_ACT_NUMBER)
                if(!_plat__ACT_GetImplemented(act))
                {
                    printf("TPM_RH_ACT_%1X is not implemented by platform\n", act);
                    PASS = FALSE;
                }
                default:
                    break;
            }
        }
    }
    {
        // Had a problem with the macros coming up with some bad values. Make sure
        // the size is rational
        int t = MAX_DIGEST_SIZE;
        if(t < 20)
        {
            printf("Check the MAX_DIGEST_SIZE computation (%d)", MAX_DIGEST_SIZE);
            PASS = FALSE;
        }
    }
#  endif  // DEBUG
    return (PASS);
}

#endif  // RUNTIME_SIZE_CHECKS