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

#include "InternalRoutines.h"
//
//
//           Functions
//
//           CommandAuditPreInstall_Init()
//
//     This function initializes the command audit list. This function is simulates the behavior of manufacturing. A
//     function is used instead of a structure definition because this is easier than figuring out the initialization
//     value for a bit array.
//     This function would not be implemented outside of a manufacturing or simulation environment.
//
void
CommandAuditPreInstall_Init(
     void
     )
{
     // Clear all the audit commands
     MemorySet(gp.auditComands, 0x00,
               ((TPM_CC_LAST - TPM_CC_FIRST + 1) + 7) / 8);
     // TPM_CC_SetCommandCodeAuditStatus always being audited
     if(CommandIsImplemented(TPM_CC_SetCommandCodeAuditStatus))
         CommandAuditSet(TPM_CC_SetCommandCodeAuditStatus);
     // Set initial command audit hash algorithm to be context integrity hash
     // algorithm
     gp.auditHashAlg = CONTEXT_INTEGRITY_HASH_ALG;
     // Set up audit counter to be 0
     gp.auditCounter = 0;
     // Write command audit persistent data to NV
     NvWriteReserved(NV_AUDIT_COMMANDS, &gp.auditComands);
     NvWriteReserved(NV_AUDIT_HASH_ALG, &gp.auditHashAlg);
     NvWriteReserved(NV_AUDIT_COUNTER, &gp.auditCounter);
     return;
}
//
//
//           CommandAuditStartup()
//
//     This function clears the command audit digest on a TPM Reset.
//
void
CommandAuditStartup(
     STARTUP_TYPE        type               // IN: start up type
     )
{
   if(type == SU_RESET)
   {
       // Reset the digest size to initialize the digest
       gr.commandAuditDigest.t.size = 0;
   }
}
//
//
//         CommandAuditSet()
//
//     This function will SET the audit flag for a command. This function will not SET the audit flag for a
//     command that is not implemented. This ensures that the audit status is not SET when
//     TPM2_GetCapability() is used to read the list of audited commands.
//     This function is only used by TPM2_SetCommandCodeAuditStatus().
//     The actions in TPM2_SetCommandCodeAuditStatus() are expected to cause the changes to be saved to
//     NV after it is setting and clearing bits.
//
//     Return Value                      Meaning
//
//     TRUE                              the command code audit status was changed
//     FALSE                             the command code audit status was not changed
//
BOOL
CommandAuditSet(
   TPM_CC              commandCode          // IN: command code
   )
{
   UINT32         bitPos;
   // Only SET a bit if the corresponding command is implemented
   if(CommandIsImplemented(commandCode))
   {
       // Can't audit shutdown
       if(commandCode != TPM_CC_Shutdown)
       {
           bitPos = commandCode - TPM_CC_FIRST;
           if(!BitIsSet(bitPos, &gp.auditComands[0], sizeof(gp.auditComands)))
           {
               // Set bit
               BitSet(bitPos, &gp.auditComands[0], sizeof(gp.auditComands));
               return TRUE;
           }
       }
   }
   // No change
   return FALSE;
}
//
//
//         CommandAuditClear()
//
//     This function will CLEAR the audit flag for a command. It will not CLEAR the audit flag for
//     TPM_CC_SetCommandCodeAuditStatus().
//     This function is only used by TPM2_SetCommandCodeAuditStatus().
//     The actions in TPM2_SetCommandCodeAuditStatus() are expected to cause the changes to be saved to
//     NV after it is setting and clearing bits.
//
//
//
//      Return Value                     Meaning
//
//      TRUE                             the command code audit status was changed
//      FALSE                            the command code audit status was not changed
//
BOOL
CommandAuditClear(
    TPM_CC               commandCode        // IN: command code
    )
{
    UINT32         bitPos;
    // Do nothing if the command is not implemented
    if(CommandIsImplemented(commandCode))
    {
        // The bit associated with TPM_CC_SetCommandCodeAuditStatus() cannot be
        // cleared
        if(commandCode != TPM_CC_SetCommandCodeAuditStatus)
        {
            bitPos = commandCode - TPM_CC_FIRST;
            if(BitIsSet(bitPos, &gp.auditComands[0], sizeof(gp.auditComands)))
            {
                // Clear bit
                BitClear(bitPos, &gp.auditComands[0], sizeof(gp.auditComands));
                return TRUE;
            }
        }
    }
    // No change
    return FALSE;
}
//
//
//           CommandAuditIsRequired()
//
//      This function indicates if the audit flag is SET for a command.
//
//      Return Value                     Meaning
//
//      TRUE                             if command is audited
//      FALSE                            if command is not audited
//
BOOL
CommandAuditIsRequired(
    TPM_CC               commandCode        // IN: command code
    )
{
    UINT32         bitPos;
    bitPos = commandCode - TPM_CC_FIRST;
    // Check the bit map. If the bit is SET, command audit is required
    if((gp.auditComands[bitPos/8] & (1 << (bitPos % 8))) != 0)
        return TRUE;
    else
        return FALSE;
}
//
//
//           CommandAuditCapGetCCList()
//
//      This function returns a list of commands that have their audit bit SET.
//      Family "2.0"                                 TCG Published                                        Page 111
//      Level 00 Revision 01.16              Copyright © TCG 2006-2014                           October 30, 2014
//      Trusted Platform Module Library                                                  Part 4: Supporting Routines
//
//
//      The list starts at the input commandCode.
//
//      Return Value                      Meaning
//
//      YES                               if there are more command code available
//      NO                                all the available command code has been returned
//
TPMI_YES_NO
CommandAuditCapGetCCList(
     TPM_CC            commandCode,          // IN: start command code
     UINT32            count,                // IN: count of returned TPM_CC
     TPML_CC          *commandList           // OUT: list of TPM_CC
     )
{
     TPMI_YES_NO      more = NO;
     UINT32           i;
     // Initialize output handle list
     commandList->count = 0;
     // The maximum count of command we may return is MAX_CAP_CC
     if(count > MAX_CAP_CC) count = MAX_CAP_CC;
     // If the command code is smaller than TPM_CC_FIRST, start from TPM_CC_FIRST
     if(commandCode < TPM_CC_FIRST) commandCode = TPM_CC_FIRST;
     // Collect audit commands
     for(i = commandCode; i <= TPM_CC_LAST; i++)
     {
         if(CommandAuditIsRequired(i))
         {
             if(commandList->count < count)
             {
                 // If we have not filled up the return list, add this command
                 // code to it
                 commandList->commandCodes[commandList->count] = i;
                 commandList->count++;
             }
             else
             {
                 // If the return list is full but we still have command
                 // available, report this and stop iterating
                 more = YES;
                 break;
             }
         }
     }
     return more;
}
//
//
//          CommandAuditGetDigest
//
//      This command is used to create a digest of the commands being audited. The commands are processed
//      in ascending numeric order with a list of TPM_CC being added to a hash. This operates as if all the
//      audited command codes were concatenated and then hashed.
//
void
CommandAuditGetDigest(
     TPM2B_DIGEST     *digest                // OUT: command digest
     )
{
     TPM_CC                               i;
     HASH_STATE                           hashState;
     // Start hash
     digest->t.size = CryptStartHash(gp.auditHashAlg, &hashState);
     // Add command code
     for(i = TPM_CC_FIRST; i <= TPM_CC_LAST; i++)
     {
         if(CommandAuditIsRequired(i))
         {
             CryptUpdateDigestInt(&hashState, sizeof(i), &i);
         }
     }
     // Complete hash
     CryptCompleteHash2B(&hashState, &digest->b);
     return;
}