// This file was extracted from the TCG Published
// Trusted Platform Module Library
// Part 3: Commands
// Family "2.0"
// Level 00 Revision 01.16
// October 30, 2014

#include "InternalRoutines.h"
#include "PolicyNV_fp.h"
#include "Policy_spt_fp.h"
#include "NV_spt_fp.h"         // Include NV support routine for read access check
//
//
//     Error Returns                     Meaning
//
//     TPM_RC_AUTH_TYPE                  NV index authorization type is not correct
//     TPM_RC_NV_LOCKED                  NV index read locked
//     TPM_RC_NV_UNINITIALIZED           the NV index has not been initialized
//     TPM_RC_POLICY                     the comparison to the NV contents failed
//     TPM_RC_SIZE                       the size of nvIndex data starting at offset is less than the size of
//                                       operandB
//
TPM_RC
TPM2_PolicyNV(
   PolicyNV_In       *in                  // IN: input parameter list
   )
{
   TPM_RC                   result;
   SESSION                 *session;
   NV_INDEX                 nvIndex;
   BYTE                     nvBuffer[sizeof(in->operandB.t.buffer)];
   TPM2B_NAME               nvName;
   TPM_CC                   commandCode = TPM_CC_PolicyNV;
   HASH_STATE               hashState;
   TPM2B_DIGEST             argHash;

// Input Validation

   // Get NV index information
   NvGetIndexInfo(in->nvIndex, &nvIndex);

   // Get pointer to the session structure
   session = SessionGet(in->policySession);

   //If this is a trial policy, skip all validations and the operation
   if(session->attributes.isTrialPolicy == CLEAR)
   {
       // NV Read access check. NV index should be allowed for read. A
       // TPM_RC_AUTH_TYPE or TPM_RC_NV_LOCKED error may be return at this
       // point
       result = NvReadAccessChecks(in->authHandle, in->nvIndex);
       if(result != TPM_RC_SUCCESS) return result;

       // Valid NV data size should not be smaller than input operandB size
       if((nvIndex.publicArea.dataSize - in->offset) < in->operandB.t.size)
           return TPM_RC_SIZE + RC_PolicyNV_operandB;

       // Arithmetic Comparison

       // Get NV data. The size of NV data equals the input operand B size
       NvGetIndexData(in->nvIndex, &nvIndex, in->offset,
                      in->operandB.t.size, nvBuffer);

       switch(in->operation)
       {
          case TPM_EO_EQ:
              // compare A = B
              if(CryptCompare(in->operandB.t.size, nvBuffer,
                              in->operandB.t.size, in->operandB.t.buffer)   != 0)
                  return TPM_RC_POLICY;
              break;
          case TPM_EO_NEQ:
              // compare A != B
              if(CryptCompare(in->operandB.t.size, nvBuffer,
                              in->operandB.t.size, in->operandB.t.buffer)   == 0)
                  return TPM_RC_POLICY;
              break;
          case TPM_EO_SIGNED_GT:
              // compare A > B signed
              if(CryptCompareSigned(in->operandB.t.size, nvBuffer,
                              in->operandB.t.size, in->operandB.t.buffer)   <= 0)
                  return TPM_RC_POLICY;
              break;
          case TPM_EO_UNSIGNED_GT:
              // compare A > B unsigned
              if(CryptCompare(in->operandB.t.size, nvBuffer,
                              in->operandB.t.size, in->operandB.t.buffer)   <= 0)
                  return TPM_RC_POLICY;
              break;
          case TPM_EO_SIGNED_LT:
              // compare A < B signed
              if(CryptCompareSigned(in->operandB.t.size, nvBuffer,
                              in->operandB.t.size, in->operandB.t.buffer)   >= 0)
                  return TPM_RC_POLICY;
              break;
          case TPM_EO_UNSIGNED_LT:
              // compare A < B unsigned
              if(CryptCompare(in->operandB.t.size, nvBuffer,
                              in->operandB.t.size, in->operandB.t.buffer)   >= 0)
                  return TPM_RC_POLICY;
              break;
          case TPM_EO_SIGNED_GE:
              // compare A >= B signed
              if(CryptCompareSigned(in->operandB.t.size, nvBuffer,
                              in->operandB.t.size, in->operandB.t.buffer)   < 0)
                  return TPM_RC_POLICY;
              break;
          case TPM_EO_UNSIGNED_GE:
              // compare A >= B unsigned
              if(CryptCompare(in->operandB.t.size, nvBuffer,
                              in->operandB.t.size, in->operandB.t.buffer)   < 0)
                  return TPM_RC_POLICY;
              break;
          case TPM_EO_SIGNED_LE:
              // compare A <= B signed
              if(CryptCompareSigned(in->operandB.t.size, nvBuffer,
                              in->operandB.t.size, in->operandB.t.buffer)   > 0)
                  return TPM_RC_POLICY;
              break;
          case TPM_EO_UNSIGNED_LE:
              // compare A <= B unsigned
              if(CryptCompare(in->operandB.t.size, nvBuffer,
                              in->operandB.t.size, in->operandB.t.buffer)   > 0)
                  return TPM_RC_POLICY;
              break;
          case TPM_EO_BITSET:
              // All bits SET in B are SET in A. ((A&B)=B)
          {
              UINT32 i;
              for (i = 0; i < in->operandB.t.size; i++)
                  if((nvBuffer[i] & in->operandB.t.buffer[i])
                             != in->operandB.t.buffer[i])
                         return TPM_RC_POLICY;
            }
            break;
            case TPM_EO_BITCLEAR:
                // All bits SET in B are CLEAR in A. ((A&B)=0)
            {
                UINT32 i;
                for (i = 0; i < in->operandB.t.size; i++)
                    if((nvBuffer[i] & in->operandB.t.buffer[i]) != 0)
                        return TPM_RC_POLICY;
            }
            break;
            default:
                pAssert(FALSE);
                break;
       }
   }

// Internal Data Update

   // Start argument hash
   argHash.t.size = CryptStartHash(session->authHashAlg, &hashState);

   // add operandB
   CryptUpdateDigest2B(&hashState, &in->operandB.b);

   // add offset
   CryptUpdateDigestInt(&hashState, sizeof(UINT16), &in->offset);

   // add operation
   CryptUpdateDigestInt(&hashState, sizeof(TPM_EO), &in->operation);

   // complete argument digest
   CryptCompleteHash2B(&hashState, &argHash.b);

   // Update policyDigest
   // Start digest
   CryptStartHash(session->authHashAlg, &hashState);

   // add old digest
   CryptUpdateDigest2B(&hashState, &session->u2.policyDigest.b);

   // add commandCode
   CryptUpdateDigestInt(&hashState, sizeof(TPM_CC), &commandCode);

   // add argument digest
   CryptUpdateDigest2B(&hashState, &argHash.b);

   // Adding nvName
   nvName.t.size = EntityGetName(in->nvIndex, &nvName.t.name);
   CryptUpdateDigest2B(&hashState, &nvName.b);

   // complete the digest
   CryptCompleteHash2B(&hashState, &session->u2.policyDigest.b);

   return TPM_RC_SUCCESS;
}