// 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 "SequenceComplete_fp.h"
#include "Platform.h"
//
//
//     Error Returns               Meaning
//
//     TPM_RC_TYPE                 sequenceHandle does not reference a hash or HMAC sequence
//                                 object
//
TPM_RC
TPM2_SequenceComplete(
   SequenceComplete_In    *in,               // IN: input parameter list
   SequenceComplete_Out   *out               // OUT: output parameter list
   )
{
   OBJECT                     *object;

// Input validation

   // Get hash object pointer
   object = ObjectGet(in->sequenceHandle);

   // input handle must be a hash or HMAC sequence object.
   if(   object->attributes.hashSeq == CLEAR
      && object->attributes.hmacSeq == CLEAR)
       return TPM_RC_MODE + RC_SequenceComplete_sequenceHandle;

// Command Output

   if(object->attributes.hashSeq == SET)           // sequence object for hash
   {
       // Update last piece of data
       HASH_OBJECT     *hashObject = (HASH_OBJECT *)object;

      // Get the hash algorithm before the algorithm is lost in CryptCompleteHash
       TPM_ALG_ID       hashAlg = hashObject->state.hashState[0].state.hashAlg;

       CryptUpdateDigest2B(&hashObject->state.hashState[0], &in->buffer.b);

       // Complete hash
       out->result.t.size
           = CryptGetHashDigestSize(
                 CryptGetContextAlg(&hashObject->state.hashState[0]));

       CryptCompleteHash2B(&hashObject->state.hashState[0], &out->result.b);

       // Check if the first block of the sequence has been received
       if(hashObject->attributes.firstBlock == CLEAR)
       {
           // If not, then this is the first block so see if it is 'safe'
           // to sign.
           if(TicketIsSafe(&in->buffer.b))
               hashObject->attributes.ticketSafe = SET;
       }

       // Output ticket
       out->validation.tag = TPM_ST_HASHCHECK;
       out->validation.hierarchy = in->hierarchy;

       if(in->hierarchy == TPM_RH_NULL)
       {
            // Ticket is not required
            out->validation.digest.t.size = 0;
       }
       else if(object->attributes.ticketSafe == CLEAR)
       {
           // Ticket is not safe to generate
           out->validation.hierarchy = TPM_RH_NULL;
           out->validation.digest.t.size = 0;
       }
       else
       {
           // Compute ticket
           TicketComputeHashCheck(out->validation.hierarchy, hashAlg,
                                  &out->result, &out->validation);
       }
   }
   else
   {
       HASH_OBJECT       *hashObject = (HASH_OBJECT *)object;

       //   Update last piece of data
       CryptUpdateDigest2B(&hashObject->state.hmacState, &in->buffer.b);
       // Complete hash/HMAC
       out->result.t.size =
           CryptGetHashDigestSize(
               CryptGetContextAlg(&hashObject->state.hmacState.hashState));
       CryptCompleteHMAC2B(&(hashObject->state.hmacState), &out->result.b);

       // No ticket is generated for HMAC sequence
       out->validation.tag = TPM_ST_HASHCHECK;
       out->validation.hierarchy = TPM_RH_NULL;
       out->validation.digest.t.size = 0;
   }

// Internal Data Update

   // mark sequence object as evict so it will be flushed on the way out
   object->attributes.evict = SET;

   return TPM_RC_SUCCESS;
}