// 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 "NV_DefineSpace_fp.h"
//
//
//     Error Returns                 Meaning
//
//     TPM_RC_NV_ATTRIBUTES          attributes of the index are not consistent
//     TPM_RC_NV_DEFINED             index already exists
//     TPM_RC_HIERARCHY              for authorizations using TPM_RH_PLATFORM phEnable_NV is
//                                   clear.
//     TPM_RC_NV_SPACE               Insufficient space for the index
//     TPM_RC_SIZE                   'auth->size' or 'publicInfo->authPolicy.size' is larger than the digest
//                                   size of 'publicInfo->nameAlg', or 'publicInfo->dataSize' is not
//                                   consistent with 'publicInfo->attributes'.
//
TPM_RC
TPM2_NV_DefineSpace(
   NV_DefineSpace_In   *in                  // IN: input parameter list
   )
{
   TPM_RC          result;
   TPMA_NV         attributes;
   UINT16          nameSize;

   nameSize = CryptGetHashDigestSize(in->publicInfo.t.nvPublic.nameAlg);

   // Check if NV is available. NvIsAvailable may return TPM_RC_NV_UNAVAILABLE
   // TPM_RC_NV_RATE or TPM_RC_SUCCESS.
   result = NvIsAvailable();
   if(result != TPM_RC_SUCCESS)
       return result;

// Input Validation
   // If an index is being created by the owner and shEnable is
   // clear, then we would not reach this point because ownerAuth
   // can't be given when shEnable is CLEAR. However, if phEnable
   // is SET but phEnableNV is CLEAR, we have to check here
   if(in->authHandle == TPM_RH_PLATFORM && gc.phEnableNV == CLEAR)
       return TPM_RC_HIERARCHY + RC_NV_DefineSpace_authHandle;

   attributes = in->publicInfo.t.nvPublic.attributes;

   //TPMS_NV_PUBLIC validation.
   // Counters and bit fields must have a size of 8
   if (   (attributes.TPMA_NV_COUNTER == SET || attributes.TPMA_NV_BITS == SET)
       && (in->publicInfo.t.nvPublic.dataSize != 8))
       return TPM_RC_SIZE + RC_NV_DefineSpace_publicInfo;

   // check that the authPolicy consistent with hash algorithm
   if(   in->publicInfo.t.nvPublic.authPolicy.t.size != 0
      && in->publicInfo.t.nvPublic.authPolicy.t.size != nameSize)
       return TPM_RC_SIZE + RC_NV_DefineSpace_publicInfo;

   // make sure that the authValue is not too large
   MemoryRemoveTrailingZeros(&in->auth);
   if(in->auth.t.size > nameSize)
       return TPM_RC_SIZE + RC_NV_DefineSpace_auth;

   //TPMA_NV validation.
   // Locks may not be SET and written cannot be SET
   if(   attributes.TPMA_NV_WRITTEN == SET
      || attributes.TPMA_NV_WRITELOCKED == SET
      || attributes.TPMA_NV_READLOCKED == SET)
       return TPM_RC_ATTRIBUTES + RC_NV_DefineSpace_publicInfo;

   // There must be a way to read the index
   if(   attributes.TPMA_NV_OWNERREAD == CLEAR
      && attributes.TPMA_NV_PPREAD == CLEAR
      && attributes.TPMA_NV_AUTHREAD == CLEAR
      && attributes.TPMA_NV_POLICYREAD == CLEAR)
       return TPM_RC_ATTRIBUTES + RC_NV_DefineSpace_publicInfo;

   // There must be a way to write the index
   if(   attributes.TPMA_NV_OWNERWRITE == CLEAR
      && attributes.TPMA_NV_PPWRITE == CLEAR
      && attributes.TPMA_NV_AUTHWRITE == CLEAR
      && attributes.TPMA_NV_POLICYWRITE == CLEAR)
       return TPM_RC_ATTRIBUTES + RC_NV_DefineSpace_publicInfo;

   // Make sure that no attribute is used that is not supported by the proper
   // command
#if CC_NV_Increment == NO
   if( attributes.TPMA_NV_COUNTER == SET)
       return TPM_RC_ATTRIBUTES + RC_NV_DefineSpace_publicInfo;
#endif
#if CC_NV_SetBits == NO
      if( attributes.TPMA_NV_BITS == SET)
          return TPM_RC_ATTRIBUTES + RC_NV_DefineSpace_publicInfo;
#endif
#if CC_NV_Extend == NO
     if( attributes.TPMA_NV_EXTEND == SET)
         return TPM_RC_ATTRIBUTES + RC_NV_DefineSpace_publicInfo;
#endif
#if CC_NV_UndefineSpaceSpecial == NO
    if( attributes.TPMA_NV_POLICY_DELETE == SET)
        return TPM_RC_ATTRIBUTES + RC_NV_DefineSpace_publicInfo;
#endif

   // Can be COUNTER or BITS or EXTEND but not more than one
   if( attributes.TPMA_NV_COUNTER == SET
      && attributes.TPMA_NV_BITS == SET)
       return TPM_RC_ATTRIBUTES + RC_NV_DefineSpace_publicInfo;
   if(   attributes.TPMA_NV_COUNTER == SET
      && attributes.TPMA_NV_EXTEND == SET)
       return TPM_RC_ATTRIBUTES + RC_NV_DefineSpace_publicInfo;
   if(   attributes.TPMA_NV_BITS == SET
      && attributes.TPMA_NV_EXTEND == SET)
       return TPM_RC_ATTRIBUTES + RC_NV_DefineSpace_publicInfo;

   // An index with TPMA_NV_CLEAR_STCLEAR can't be a counter and can't have
   // TPMA_NV_WRITEDEFINE SET
   if(     attributes.TPMA_NV_CLEAR_STCLEAR == SET
       && (    attributes.TPMA_NV_COUNTER == SET
            || attributes.TPMA_NV_WRITEDEFINE == SET)
      )
       return TPM_RC_ATTRIBUTES + RC_NV_DefineSpace_publicInfo;

   // Make sure that the creator of the index can delete the index
   if( (    in->publicInfo.t.nvPublic.attributes.TPMA_NV_PLATFORMCREATE == SET
          && in->authHandle == TPM_RH_OWNER
         )
      || (   in->publicInfo.t.nvPublic.attributes.TPMA_NV_PLATFORMCREATE == CLEAR
          && in->authHandle == TPM_RH_PLATFORM
         )
     )
         return TPM_RC_ATTRIBUTES + RC_NV_DefineSpace_authHandle;

  // If TPMA_NV_POLICY_DELETE is SET, then the index must be defined by
  // the platform
  if(    in->publicInfo.t.nvPublic.attributes.TPMA_NV_POLICY_DELETE == SET
     && TPM_RH_PLATFORM != in->authHandle
    )
      return TPM_RC_ATTRIBUTES + RC_NV_DefineSpace_publicInfo;

  // If the NV index is used as a PCR, the data size must match the digest
  // size
  if(   in->publicInfo.t.nvPublic.attributes.TPMA_NV_EXTEND == SET
     && in->publicInfo.t.nvPublic.dataSize != nameSize
    )
      return TPM_RC_ATTRIBUTES + RC_NV_DefineSpace_publicInfo;

  // See if the index is already defined.
  if(NvIsUndefinedIndex(in->publicInfo.t.nvPublic.nvIndex))
      return TPM_RC_NV_DEFINED;

// Internal Data Update
   // define the space. A TPM_RC_NV_SPACE error may be returned at this point
   result = NvDefineIndex(&in->publicInfo.t.nvPublic, &in->auth);
   if(result != TPM_RC_SUCCESS)
       return result;

  return TPM_RC_SUCCESS;

}