// 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 "Rewrap_fp.h"
#include "Object_spt_fp.h"
//
//
//     Error Returns                 Meaning
//
//     TPM_RC_ATTRIBUTES             newParent is not a decryption key
//     TPM_RC_HANDLE                 oldParent does not consistent with inSymSeed
//     TPM_RC_INTEGRITY              the integrity check of inDuplicate failed
//     TPM_RC_KEY                    for an ECC key, the public key is not on the curve of the curve ID
//     TPM_RC_KEY_SIZE               the decrypted input symmetric key size does not matches the
//                                   symmetric algorithm key size of oldParent
//     TPM_RC_TYPE                   oldParent is not a storage key, or 'newParent is not a storage key
//     TPM_RC_VALUE                  for an 'oldParent; RSA key, the data to be decrypted is greater than
//                                   the public exponent
//     Unmarshal errors              errors during unmarshaling the input encrypted buffer to a ECC public
//                                   key, or unmarshal the private buffer to sensitive
//
TPM_RC
TPM2_Rewrap(
   Rewrap_In         *in,             // IN: input parameter list
   Rewrap_Out        *out             // OUT: output parameter list
   )
{
   TPM_RC                   result = TPM_RC_SUCCESS;
   OBJECT                   *oldParent;
   TPM2B_DATA               data;               // symmetric key
   UINT16                   hashSize = 0;
   TPM2B_PRIVATE            privateBlob;        // A temporary private blob
                                                // to transit between old
                                                // and new wrappers

// Input Validation

   if((in->inSymSeed.t.size == 0 && in->oldParent != TPM_RH_NULL)
           || (in->inSymSeed.t.size != 0 && in->oldParent == TPM_RH_NULL))
       return TPM_RC_HANDLE + RC_Rewrap_oldParent;

   if(in->oldParent != TPM_RH_NULL)
   {
       // Get old parent pointer
       oldParent = ObjectGet(in->oldParent);

         // old parent key must be a storage object
         if(!ObjectIsStorage(in->oldParent))
             return TPM_RC_TYPE + RC_Rewrap_oldParent;

         // Decrypt input secret data via asymmetric decryption. A
         // TPM_RC_VALUE, TPM_RC_KEY or unmarshal errors may be returned at this
         // point
         result = CryptSecretDecrypt(in->oldParent, NULL,
                                     "DUPLICATE", &in->inSymSeed, &data);
         if(result != TPM_RC_SUCCESS)
             return TPM_RC_VALUE + RC_Rewrap_inSymSeed;

       // Unwrap Outer
       result = UnwrapOuter(in->oldParent, &in->name,
                            oldParent->publicArea.nameAlg, (TPM2B_SEED *) &data,
                            FALSE,
                            in->inDuplicate.t.size, in->inDuplicate.t.buffer);
       if(result != TPM_RC_SUCCESS)
           return RcSafeAddToResult(result, RC_Rewrap_inDuplicate);

       // Copy unwrapped data to temporary variable, remove the integrity field
       hashSize = sizeof(UINT16) +
                  CryptGetHashDigestSize(oldParent->publicArea.nameAlg);
       privateBlob.t.size = in->inDuplicate.t.size - hashSize;
       MemoryCopy(privateBlob.t.buffer, in->inDuplicate.t.buffer + hashSize,
                  privateBlob.t.size, sizeof(privateBlob.t.buffer));
   }
   else
   {
       // No outer wrap from input blob.   Direct copy.
       privateBlob = in->inDuplicate;
   }

   if(in->newParent != TPM_RH_NULL)
   {
       OBJECT          *newParent;
       newParent = ObjectGet(in->newParent);

       // New parent must be a storage object
       if(!ObjectIsStorage(in->newParent))
           return TPM_RC_TYPE + RC_Rewrap_newParent;

       // Make new encrypt key and its associated secret structure. A
       // TPM_RC_VALUE error may be returned at this point if RSA algorithm is
       // enabled in TPM
       out->outSymSeed.t.size = sizeof(out->outSymSeed.t.secret);
       result = CryptSecretEncrypt(in->newParent,
                                   "DUPLICATE", &data, &out->outSymSeed);
       if(result != TPM_RC_SUCCESS) return result;

// Command output
       // Copy temporary variable to output, reserve the space for integrity
       hashSize = sizeof(UINT16) +
                  CryptGetHashDigestSize(newParent->publicArea.nameAlg);
       out->outDuplicate.t.size = privateBlob.t.size;
       MemoryCopy(out->outDuplicate.t.buffer + hashSize, privateBlob.t.buffer,
                  privateBlob.t.size, sizeof(out->outDuplicate.t.buffer));

       // Produce outer wrapper for output
       out->outDuplicate.t.size = ProduceOuterWrap(in->newParent, &in->name,
                                  newParent->publicArea.nameAlg,
                                  (TPM2B_SEED *) &data,
                                  FALSE,
                                  out->outDuplicate.t.size,
                                  out->outDuplicate.t.buffer);

   }
   else // New parent is a null key so there is no seed
   {
       out->outSymSeed.t.size = 0;

       // Copy privateBlob directly
       out->outDuplicate = privateBlob;
   }

   return TPM_RC_SUCCESS;
}