// 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"
#include "Attest_spt_fp.h"
//
//
//          Functions
//
//          FillInAttestInfo()
//
//     Fill in common fields of TPMS_ATTEST structure.
//
//     Error Returns                     Meaning
//
//     TPM_RC_KEY                        key referenced by signHandle is not a signing key
//     TPM_RC_SCHEME                     both scheme and key's default scheme are empty; or scheme is
//                                       empty while key's default scheme requires explicit input scheme (split
//                                       signing); or non-empty default key scheme differs from scheme
//
TPM_RC
FillInAttestInfo(
     TPMI_DH_OBJECT         signHandle,            //   IN: handle of signing object
     TPMT_SIG_SCHEME       *scheme,                //   IN/OUT: scheme to be used for signing
     TPM2B_DATA            *data,                  //   IN: qualifying data
     TPMS_ATTEST           *attest                 //   OUT: attest structure
     )
{
     TPM_RC                         result;
     TPMI_RH_HIERARCHY              signHierarhcy;
     result = CryptSelectSignScheme(signHandle, scheme);
     if(result != TPM_RC_SUCCESS)
         return result;
     // Magic number
     attest->magic = TPM_GENERATED_VALUE;
     if(signHandle == TPM_RH_NULL)
     {
         BYTE     *buffer;
         INT32     bufferSize;
         // For null sign handle, the QN is TPM_RH_NULL
         buffer = attest->qualifiedSigner.t.name;
         bufferSize = sizeof(TPM_HANDLE);
         attest->qualifiedSigner.t.size =
              TPM_HANDLE_Marshal(&signHandle, &buffer, &bufferSize);
     }
     else
     {
         // Certifying object qualified name
         // if the scheme is anonymous, this is an empty buffer
         if(CryptIsSchemeAnonymous(scheme->scheme))
              attest->qualifiedSigner.t.size = 0;
         else
              ObjectGetQualifiedName(signHandle, &attest->qualifiedSigner);
   }
   // current clock in plain text
   TimeFillInfo(&attest->clockInfo);
   // Firmware version in plain text
   attest->firmwareVersion = ((UINT64) gp.firmwareV1 << (sizeof(UINT32) * 8));
   attest->firmwareVersion += gp.firmwareV2;
   // Get the hierarchy of sign object. For NULL sign handle, the hierarchy
   // will be TPM_RH_NULL
   signHierarhcy = EntityGetHierarchy(signHandle);
   if(signHierarhcy != TPM_RH_PLATFORM && signHierarhcy != TPM_RH_ENDORSEMENT)
   {
       // For sign object is not in platform or endorsement hierarchy,
       // obfuscate the clock and firmwereVersion information
       UINT64          obfuscation[2];
       TPMI_ALG_HASH   hashAlg;
         // Get hash algorithm
         if(signHandle == TPM_RH_NULL || signHandle == TPM_RH_OWNER)
         {
              hashAlg = CONTEXT_INTEGRITY_HASH_ALG;
         }
         else
         {
              OBJECT          *signObject = NULL;
              signObject = ObjectGet(signHandle);
              hashAlg = signObject->publicArea.nameAlg;
         }
         KDFa(hashAlg, &gp.shProof.b, "OBFUSCATE",
               &attest->qualifiedSigner.b, NULL, 128, (BYTE *)&obfuscation[0], NULL);
         // Obfuscate data
         attest->firmwareVersion += obfuscation[0];
         attest->clockInfo.resetCount += (UINT32)(obfuscation[1] >> 32);
         attest->clockInfo.restartCount += (UINT32)obfuscation[1];
   }
   // External data
   if(CryptIsSchemeAnonymous(scheme->scheme))
       attest->extraData.t.size = 0;
   else
   {
       // If we move the data to the attestation structure, then we will not use
       // it in the signing operation except as part of the signed data
       attest->extraData = *data;
       data->t.size = 0;
   }
   return TPM_RC_SUCCESS;
}
//
//
//          SignAttestInfo()
//
//     Sign a TPMS_ATTEST structure. If signHandle is TPM_RH_NULL, a null signature is returned.
//
//
//
//
//      Error Returns                     Meaning
//
//      TPM_RC_ATTRIBUTES                 signHandle references not a signing key
//      TPM_RC_SCHEME                     scheme is not compatible with signHandle type
//      TPM_RC_VALUE                      digest generated for the given scheme is greater than the modulus of
//                                        signHandle (for an RSA key); invalid commit status or failed to
//                                        generate r value (for an ECC key)
//
TPM_RC
SignAttestInfo(
   TPMI_DH_OBJECT           signHandle,                //   IN: handle of sign object
   TPMT_SIG_SCHEME         *scheme,                    //   IN: sign scheme
   TPMS_ATTEST             *certifyInfo,               //   IN: the data to be signed
   TPM2B_DATA              *qualifyingData,            //   IN: extra data for the signing proce
   TPM2B_ATTEST            *attest,                    //   OUT: marshaled attest blob to be
                                                       //       signed
   TPMT_SIGNATURE          *signature                  //   OUT: signature
   )
{
   TPM_RC                         result;
   TPMI_ALG_HASH                  hashAlg;
   BYTE                           *buffer;
   INT32                          bufferSize;
   HASH_STATE                     hashState;
   TPM2B_DIGEST                   digest;
   // Marshal TPMS_ATTEST structure for hash
   buffer = attest->t.attestationData;
   bufferSize = sizeof(TPMS_ATTEST);
   attest->t.size = TPMS_ATTEST_Marshal(certifyInfo, &buffer, &bufferSize);
   if(signHandle == TPM_RH_NULL)
   {
       signature->sigAlg = TPM_ALG_NULL;
   }
   else
   {
       // Attestation command may cause the orderlyState to be cleared due to
       // the reporting of clock info. If this is the case, check if NV is
       // available first
       if(gp.orderlyState != SHUTDOWN_NONE)
       {
           // The command needs NV update. Check if NV is available.
           // A TPM_RC_NV_UNAVAILABLE or TPM_RC_NV_RATE error may be returned at
           // this point
           result = NvIsAvailable();
           if(result != TPM_RC_SUCCESS)
               return result;
       }
         // Compute hash
         hashAlg = scheme->details.any.hashAlg;
         digest.t.size = CryptStartHash(hashAlg, &hashState);
         CryptUpdateDigest(&hashState, attest->t.size, attest->t.attestationData);
         CryptCompleteHash2B(&hashState, &digest.b);
         // If there is qualifying data, need to rehash the the data
         // hash(qualifyingData || hash(attestationData))
         if(qualifyingData->t.size != 0)
         {
             CryptStartHash(hashAlg, &hashState);
             CryptUpdateDigest(&hashState,
                               qualifyingData->t.size,
                               qualifyingData->t.buffer);
             CryptUpdateDigest(&hashState, digest.t.size, digest.t.buffer);
             CryptCompleteHash2B(&hashState, &digest.b);
          }
          // Sign the hash. A TPM_RC_VALUE, TPM_RC_SCHEME, or
          // TPM_RC_ATTRIBUTES error may be returned at this point
          return CryptSign(signHandle,
                           scheme,
                           &digest,
                           signature);
     }
     return TPM_RC_SUCCESS;
}