// 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 "Object_spt_fp.h"
#include "Platform.h"
//
//
//
//          Local Functions
//
//          EqualCryptSet()
//
//     Check if the crypto sets in two public areas are equal
//
//     Error Returns                     Meaning
//
//     TPM_RC_ASYMMETRIC                 mismatched parameters
//     TPM_RC_HASH                       mismatched name algorithm
//     TPM_RC_TYPE                       mismatched type
//
static TPM_RC
EqualCryptSet(
   TPMT_PUBLIC         *publicArea1,        // IN: public area 1
   TPMT_PUBLIC         *publicArea2         // IN: public area 2
   )
{
   UINT16                   size1;
   UINT16                   size2;
   BYTE                     params1[sizeof(TPMU_PUBLIC_PARMS)];
   BYTE                     params2[sizeof(TPMU_PUBLIC_PARMS)];
   BYTE                     *buffer;
   INT32                    bufferSize;
   // Compare name hash
   if(publicArea1->nameAlg != publicArea2->nameAlg)
       return TPM_RC_HASH;
   // Compare algorithm
   if(publicArea1->type != publicArea2->type)
       return TPM_RC_TYPE;
   // TPMU_PUBLIC_PARMS field should be identical
   buffer = params1;
   bufferSize = sizeof(TPMU_PUBLIC_PARMS);
   size1 = TPMU_PUBLIC_PARMS_Marshal(&publicArea1->parameters, &buffer,
                                     &bufferSize, publicArea1->type);
   buffer = params2;
   bufferSize = sizeof(TPMU_PUBLIC_PARMS);
   size2 = TPMU_PUBLIC_PARMS_Marshal(&publicArea2->parameters, &buffer,
                                     &bufferSize, publicArea2->type);
   if(size1 != size2 || !MemoryEqual(params1, params2, size1))
       return TPM_RC_ASYMMETRIC;
   return TPM_RC_SUCCESS;
}
//
//
//          GetIV2BSize()
//
//     Get the size of TPM2B_IV in canonical form that will be append to the start of the sensitive data. It
//     includes both size of size field and size of iv data
//
//     Return Value                      Meaning
//
static UINT16
GetIV2BSize(
   TPM_HANDLE            protectorHandle           // IN: the protector handle
   )
{
   OBJECT                   *protector = NULL; // Pointer to the protector object
   TPM_ALG_ID               symAlg;
//
   UINT16                    keyBits;
   // Determine the symmetric algorithm and size of key
   if(protectorHandle == TPM_RH_NULL)
   {
       // Use the context encryption algorithm and key size
       symAlg = CONTEXT_ENCRYPT_ALG;
       keyBits = CONTEXT_ENCRYPT_KEY_BITS;
   }
   else
   {
       protector = ObjectGet(protectorHandle);
       symAlg = protector->publicArea.parameters.asymDetail.symmetric.algorithm;
       keyBits= protector->publicArea.parameters.asymDetail.symmetric.keyBits.sym;
   }
   // The IV size is a UINT16 size field plus the block size of the symmetric
   // algorithm
   return sizeof(UINT16) + CryptGetSymmetricBlockSize(symAlg, keyBits);
}
//
//
//         ComputeProtectionKeyParms()
//
//     This function retrieves the symmetric protection key parameters for the sensitive data The parameters
//     retrieved from this function include encryption algorithm, key size in bit, and a TPM2B_SYM_KEY
//     containing the key material as well as the key size in bytes This function is used for any action that
//     requires encrypting or decrypting of the sensitive area of an object or a credential blob
//
static void
ComputeProtectionKeyParms(
   TPM_HANDLE          protectorHandle,       //   IN: the protector handle
   TPM_ALG_ID          hashAlg,               //   IN: hash algorithm for KDFa
   TPM2B_NAME         *name,                  //   IN: name of the object
   TPM2B_SEED         *seedIn,                //   IN: optional seed for duplication blob.
                                              //       For non duplication blob, this
                                              //       parameter should be NULL
   TPM_ALG_ID         *symAlg,                //   OUT: the symmetric algorithm
   UINT16             *keyBits,               //   OUT: the symmetric key size in bits
   TPM2B_SYM_KEY      *symKey                 //   OUT: the symmetric key
   )
{
   TPM2B_SEED                *seed = NULL;
   OBJECT                    *protector = NULL; // Pointer to the protector
   // Determine the algorithms for the KDF and the encryption/decryption
   // For TPM_RH_NULL, using context settings
   if(protectorHandle == TPM_RH_NULL)
   {
       // Use the context encryption algorithm and key size
       *symAlg = CONTEXT_ENCRYPT_ALG;
       symKey->t.size = CONTEXT_ENCRYPT_KEY_BYTES;
       *keyBits = CONTEXT_ENCRYPT_KEY_BITS;
   }
   else
   {
       TPMT_SYM_DEF_OBJECT *symDef;
       protector = ObjectGet(protectorHandle);
       symDef = &protector->publicArea.parameters.asymDetail.symmetric;
       *symAlg = symDef->algorithm;
       *keyBits= symDef->keyBits.sym;
       symKey->t.size = (*keyBits + 7) / 8;
   }
   // Get seed for KDF
   seed = GetSeedForKDF(protectorHandle, seedIn);
   // KDFa to generate symmetric key and IV value
   KDFa(hashAlg, (TPM2B *)seed, "STORAGE", (TPM2B *)name, NULL,
        symKey->t.size * 8, symKey->t.buffer, NULL);
   return;
}
//
//
//           ComputeOuterIntegrity()
//
//      The sensitive area parameter is a buffer that holds a space for the integrity value and the marshaled
//      sensitive area. The caller should skip over the area set aside for the integrity value and compute the hash
//      of the remainder of the object. The size field of sensitive is in unmarshaled form and the sensitive area
//      contents is an array of bytes.
//
static void
ComputeOuterIntegrity(
   TPM2B_NAME          *name,                   //   IN: the name of the object
   TPM_HANDLE           protectorHandle,        //   IN: The handle of the object that
                                                //       provides protection. For object, it
                                                //       is parent handle. For credential, it
                                                //       is the handle of encrypt object. For
                                                //       a Temporary Object, it is TPM_RH_NULL
   TPMI_ALG_HASH        hashAlg,                //   IN: algorithm to use for integrity
   TPM2B_SEED          *seedIn,                 //   IN: an external seed may be provided for
                                                //       duplication blob. For non duplication
                                                //       blob, this parameter should be NULL
   UINT32               sensitiveSize,          //   IN: size of the marshaled sensitive data
   BYTE                *sensitiveData,          //   IN: sensitive area
   TPM2B_DIGEST        *integrity               //   OUT: integrity
   )
{
   HMAC_STATE               hmacState;
   TPM2B_DIGEST             hmacKey;
   TPM2B_SEED               *seed = NULL;
   // Get seed for KDF
   seed = GetSeedForKDF(protectorHandle, seedIn);
   // Determine the HMAC key bits
   hmacKey.t.size = CryptGetHashDigestSize(hashAlg);
   // KDFa to generate HMAC key
   KDFa(hashAlg, (TPM2B *)seed, "INTEGRITY", NULL, NULL,
        hmacKey.t.size * 8, hmacKey.t.buffer, NULL);
   // Start HMAC and get the size of the digest which will become the integrity
   integrity->t.size = CryptStartHMAC2B(hashAlg, &hmacKey.b, &hmacState);
   // Adding the marshaled sensitive area to the integrity value
   CryptUpdateDigest(&hmacState, sensitiveSize, sensitiveData);
   // Adding name
   CryptUpdateDigest2B(&hmacState, (TPM2B *)name);
   // Compute HMAC
   CryptCompleteHMAC2B(&hmacState, &integrity->b);
   return;
}
//
//
//           ComputeInnerIntegrity()
//
//      This function computes the integrity of an inner wrap
//
static void
ComputeInnerIntegrity(
    TPM_ALG_ID           hashAlg,           //   IN: hash algorithm for inner wrap
    TPM2B_NAME          *name,              //   IN: the name of the object
    UINT16               dataSize,          //   IN: the size of sensitive data
    BYTE                *sensitiveData,     //   IN: sensitive data
    TPM2B_DIGEST        *integrity          //   OUT: inner integrity
    )
{
    HASH_STATE          hashState;
    // Start hash and get the size of the digest which will become the integrity
    integrity->t.size = CryptStartHash(hashAlg, &hashState);
    // Adding the marshaled sensitive area to the integrity value
    CryptUpdateDigest(&hashState, dataSize, sensitiveData);
    // Adding name
    CryptUpdateDigest2B(&hashState, &name->b);
    // Compute hash
    CryptCompleteHash2B(&hashState, &integrity->b);
    return;
}
//
//
//           ProduceInnerIntegrity()
//
//      This function produces an inner integrity for regular private, credential or duplication blob It requires the
//      sensitive data being marshaled to the innerBuffer, with the leading bytes reserved for integrity hash. It
//      assume the sensitive data starts at address (innerBuffer + integrity size). This function integrity at the
//      beginning of the inner buffer It returns the total size of buffer with the inner wrap
//
static UINT16
ProduceInnerIntegrity(
    TPM2B_NAME          *name,              //   IN: the name of the object
    TPM_ALG_ID           hashAlg,           //   IN: hash algorithm for inner wrap
    UINT16               dataSize,          //   IN: the size of sensitive data, excluding the
                                            //       leading integrity buffer size
    BYTE                *innerBuffer        //   IN/OUT: inner buffer with sensitive data in
                                            //       it. At input, the leading bytes of this
                                            //       buffer is reserved for integrity
    )
{
    BYTE                     *sensitiveData; // pointer to the sensitive data
    TPM2B_DIGEST             integrity;
    UINT16                   integritySize;
    BYTE                     *buffer;             // Auxiliary buffer pointer
    INT32                    bufferSize;
    // sensitiveData points to the beginning of sensitive data in innerBuffer
    integritySize = sizeof(UINT16) + CryptGetHashDigestSize(hashAlg);
    sensitiveData = innerBuffer + integritySize;
    ComputeInnerIntegrity(hashAlg, name, dataSize, sensitiveData, &integrity);
    // Add integrity at the beginning of inner buffer
    buffer = innerBuffer;
    bufferSize = sizeof(TPM2B_DIGEST);
    TPM2B_DIGEST_Marshal(&integrity, &buffer, &bufferSize);
    return dataSize + integritySize;
}
//
//
//           CheckInnerIntegrity()
//
//      This function check integrity of inner blob
//
//      Error Returns                     Meaning
//
//      TPM_RC_INTEGRITY                  if the outer blob integrity is bad
//      unmarshal errors                  unmarshal errors while unmarshaling integrity
//
static TPM_RC
CheckInnerIntegrity(
    TPM2B_NAME          *name,                //   IN: the name of the object
    TPM_ALG_ID           hashAlg,             //   IN: hash algorithm for inner wrap
    UINT16               dataSize,            //   IN: the size of sensitive data, including the
                                              //       leading integrity buffer size
    BYTE                *innerBuffer          //   IN/OUT: inner buffer with sensitive data in
                                              //       it
    )
{
    TPM_RC              result;
    TPM2B_DIGEST        integrity;
    TPM2B_DIGEST        integrityToCompare;
    BYTE                *buffer;                          // Auxiliary buffer pointer
    INT32               size;
    // Unmarshal integrity
    buffer = innerBuffer;
    size = (INT32) dataSize;
    result = TPM2B_DIGEST_Unmarshal(&integrity, &buffer, &size);
    if(result == TPM_RC_SUCCESS)
    {
        // Compute integrity to compare
        ComputeInnerIntegrity(hashAlg, name, (UINT16) size, buffer,
                              &integrityToCompare);
         // Compare outer blob integrity
         if(!Memory2BEqual(&integrity.b, &integrityToCompare.b))
             result = TPM_RC_INTEGRITY;
    }
    return result;
}
//
//
//           Public Functions
//
//           AreAttributesForParent()
//
//      This function is called by create, load, and import functions.
//
//      Return Value                      Meaning
//
//      TRUE                              properties are those of a parent
//      FALSE                             properties are not those of a parent
//
BOOL
AreAttributesForParent(
   OBJECT             *parentObject        // IN: parent handle
   )
{
   // This function is only called when a parent is needed. Any
   // time a "parent" is used, it must be authorized. When
   // the authorization is checked, both the public and sensitive
   // areas must be loaded. Just make sure...
   pAssert(parentObject->attributes.publicOnly == CLEAR);
   if(ObjectDataIsStorage(&parentObject->publicArea))
       return TRUE;
   else
       return FALSE;
}
//
//
//          SchemeChecks()
//
//      This function validates the schemes in the public area of an object. This function is called by
//      TPM2_LoadExternal() and PublicAttributesValidation().
//
//      Error Returns                   Meaning
//
//      TPM_RC_ASYMMETRIC               non-duplicable storage key and its parent have different public
//                                      parameters
//      TPM_RC_ATTRIBUTES               attempt to inject sensitive data for an asymmetric key; or attempt to
//                                      create a symmetric cipher key that is not a decryption key
//      TPM_RC_HASH                     non-duplicable storage key and its parent have different name
//                                      algorithm
//      TPM_RC_KDF                      incorrect KDF specified for decrypting keyed hash object
//      TPM_RC_KEY                      invalid key size values in an asymmetric key public area
//      TPM_RC_SCHEME                   inconsistent attributes decrypt, sign, restricted and key's scheme ID;
//                                      or hash algorithm is inconsistent with the scheme ID for keyed hash
//                                      object
//      TPM_RC_SYMMETRIC                a storage key with no symmetric algorithm specified; or non-storage
//                                      key with symmetric algorithm different from TPM_ALG_NULL
//      TPM_RC_TYPE                     unexpected object type; or non-duplicable storage key and its parent
//                                      have different types
//
TPM_RC
SchemeChecks(
   BOOL                load,               // IN: TRUE if load checks, FALSE if
                                           //     TPM2_Create()
   TPMI_DH_OBJECT      parentHandle,       // IN: input parent handle
   TPMT_PUBLIC        *publicArea          // IN: public area of the object
   )
{
   // Checks for an asymmetric key
   if(CryptIsAsymAlgorithm(publicArea->type))
   {
       TPMT_ASYM_SCHEME        *keyScheme;
       keyScheme = &publicArea->parameters.asymDetail.scheme;
         // An asymmetric key can't be injected
         // This is only checked when creating an object
         if(!load && (publicArea->objectAttributes.sensitiveDataOrigin == CLEAR))
             return TPM_RC_ATTRIBUTES;
         if(load && !CryptAreKeySizesConsistent(publicArea))
             return TPM_RC_KEY;
         // Keys that are both signing and decrypting must have TPM_ALG_NULL
         // for scheme
         if(     publicArea->objectAttributes.sign == SET
             && publicArea->objectAttributes.decrypt == SET
             && keyScheme->scheme != TPM_ALG_NULL)
              return TPM_RC_SCHEME;
         // A restrict sign key must have a non-NULL scheme
         if(     publicArea->objectAttributes.restricted == SET
             && publicArea->objectAttributes.sign == SET
             && keyScheme->scheme == TPM_ALG_NULL)
             return TPM_RC_SCHEME;
         // Keys must have a valid sign or decrypt scheme, or a TPM_ALG_NULL
         // scheme
         // NOTE: The unmarshaling for a public area will unmarshal based on the
         // object type. If the type is an RSA key, then only RSA schemes will be
         // allowed because a TPMI_ALG_RSA_SCHEME will be unmarshaled and it
         // consists only of those algorithms that are allowed with an RSA key.
         // This means that there is no need to again make sure that the algorithm
         // is compatible with the object type.
         if(    keyScheme->scheme != TPM_ALG_NULL
             && (    (    publicArea->objectAttributes.sign == SET
                       && !CryptIsSignScheme(keyScheme->scheme)
                     )
                  || (    publicArea->objectAttributes.decrypt == SET
                       && !CryptIsDecryptScheme(keyScheme->scheme)
                     )
                )
           )
              return TPM_RC_SCHEME;
       // Special checks for an ECC key
#ifdef TPM_ALG_ECC
       if(publicArea->type == TPM_ALG_ECC)
       {
           TPM_ECC_CURVE        curveID = publicArea->parameters.eccDetail.curveID;
           const TPMT_ECC_SCHEME *curveScheme = CryptGetCurveSignScheme(curveID);
           // The curveId must be valid or the unmarshaling is busted.
           pAssert(curveScheme != NULL);
             // If the curveID requires a specific scheme, then the key must select
             // the same scheme
             if(curveScheme->scheme != TPM_ALG_NULL)
             {
                 if(keyScheme->scheme != curveScheme->scheme)
                      return TPM_RC_SCHEME;
                 // The scheme can allow any hash, or not...
                 if(    curveScheme->details.anySig.hashAlg != TPM_ALG_NULL
                     && (   keyScheme->details.anySig.hashAlg
                         != curveScheme->details.anySig.hashAlg
                        )
                   )
                      return TPM_RC_SCHEME;
             }
             // For now, the KDF must be TPM_ALG_NULL
             if(publicArea->parameters.eccDetail.kdf.scheme != TPM_ALG_NULL)
                 return TPM_RC_KDF;
         }
#endif
         // Checks for a storage key (restricted + decryption)
         if(   publicArea->objectAttributes.restricted == SET
              && publicArea->objectAttributes.decrypt == SET)
        {
              // A storage key must have a valid protection key
              if(    publicArea->parameters.asymDetail.symmetric.algorithm
                  == TPM_ALG_NULL)
                   return TPM_RC_SYMMETRIC;
              // A storage key must have a null scheme
              if(publicArea->parameters.asymDetail.scheme.scheme != TPM_ALG_NULL)
                  return TPM_RC_SCHEME;
              // A storage key must match its parent algorithms unless
              // it is duplicable or a primary (including Temporary Primary Objects)
              if(    HandleGetType(parentHandle) != TPM_HT_PERMANENT
                  && publicArea->objectAttributes.fixedParent == SET
                )
              {
                   // If the object to be created is a storage key, and is fixedParent,
                   // its crypto set has to match its parent's crypto set. TPM_RC_TYPE,
                   // TPM_RC_HASH or TPM_RC_ASYMMETRIC may be returned at this point
                   return EqualCryptSet(publicArea,
                                        &(ObjectGet(parentHandle)->publicArea));
              }
        }
        else
        {
              // Non-storage keys must have TPM_ALG_NULL for the symmetric algorithm
              if(    publicArea->parameters.asymDetail.symmetric.algorithm
                  != TPM_ALG_NULL)
                   return TPM_RC_SYMMETRIC;
       }// End of asymmetric decryption key checks
   } // End of asymmetric checks
   // Check for bit attributes
   else if(publicArea->type == TPM_ALG_KEYEDHASH)
   {
       TPMT_KEYEDHASH_SCHEME    *scheme
           = &publicArea->parameters.keyedHashDetail.scheme;
       // If both sign and decrypt are set the scheme must be TPM_ALG_NULL
       // and the scheme selected when the key is used.
       // If neither sign nor decrypt is set, the scheme must be TPM_ALG_NULL
       // because this is a data object.
       if(      publicArea->objectAttributes.sign
           == publicArea->objectAttributes.decrypt)
       {
           if(scheme->scheme != TPM_ALG_NULL)
                return TPM_RC_SCHEME;
           return TPM_RC_SUCCESS;
       }
       // If this is a decryption key, make sure that is is XOR and that there
       // is a KDF
       else if(publicArea->objectAttributes.decrypt)
       {
           if(    scheme->scheme != TPM_ALG_XOR
               || scheme->details.xor_.hashAlg == TPM_ALG_NULL)
                return TPM_RC_SCHEME;
           if(scheme->details.xor_.kdf == TPM_ALG_NULL)
                return TPM_RC_KDF;
           return TPM_RC_SUCCESS;
        }
        // only supported signing scheme for keyedHash object is HMAC
        if(    scheme->scheme != TPM_ALG_HMAC
            || scheme->details.hmac.hashAlg == TPM_ALG_NULL)
             return TPM_RC_SCHEME;
         // end of the checks for keyedHash
         return TPM_RC_SUCCESS;
   }
   else if (publicArea->type == TPM_ALG_SYMCIPHER)
   {
       // Must be a decrypting key and may not be a signing key
       if(    publicArea->objectAttributes.decrypt == CLEAR
           || publicArea->objectAttributes.sign == SET
         )
            return TPM_RC_ATTRIBUTES;
   }
   else
       return TPM_RC_TYPE;
   return TPM_RC_SUCCESS;
}
//
//
//          PublicAttributesValidation()
//
//      This function validates the values in the public area of an object. This function is called by
//      TPM2_Create(), TPM2_Load(), and TPM2_CreatePrimary()
//
//      Error Returns                     Meaning
//
//      TPM_RC_ASYMMETRIC                 non-duplicable storage key and its parent have different public
//                                        parameters
//      TPM_RC_ATTRIBUTES                 fixedTPM, fixedParent, or encryptedDuplication attributes are
//                                        inconsistent between themselves or with those of the parent object;
//                                        inconsistent restricted, decrypt and sign attributes; attempt to inject
//                                        sensitive data for an asymmetric key; attempt to create a symmetric
//                                        cipher key that is not a decryption key
//      TPM_RC_HASH                       non-duplicable storage key and its parent have different name
//                                        algorithm
//      TPM_RC_KDF                        incorrect KDF specified for decrypting keyed hash object
//      TPM_RC_KEY                        invalid key size values in an asymmetric key public area
//      TPM_RC_SCHEME                     inconsistent attributes decrypt, sign, restricted and key's scheme ID;
//                                        or hash algorithm is inconsistent with the scheme ID for keyed hash
//                                        object
//      TPM_RC_SIZE                       authPolicy size does not match digest size of the name algorithm in
//                                        publicArea
//      TPM_RC_SYMMETRIC                  a storage key with no symmetric algorithm specified; or non-storage
//                                        key with symmetric algorithm different from TPM_ALG_NULL
//      TPM_RC_TYPE                       unexpected object type; or non-duplicable storage key and its parent
//                                        have different types
//
TPM_RC
PublicAttributesValidation(
   BOOL                load,                 // IN: TRUE if load checks, FALSE if
                                             //     TPM2_Create()
   TPMI_DH_OBJECT      parentHandle,         // IN: input parent handle
   TPMT_PUBLIC        *publicArea            // IN: public area of the object
   )
{
   OBJECT                  *parentObject = NULL;
   if(HandleGetType(parentHandle) != TPM_HT_PERMANENT)
       parentObject = ObjectGet(parentHandle);
    if (publicArea->nameAlg == TPM_ALG_NULL)
        return TPM_RC_HASH;
    // Check authPolicy digest consistency
    if(   publicArea->authPolicy.t.size != 0
       && (    publicArea->authPolicy.t.size
            != CryptGetHashDigestSize(publicArea->nameAlg)
          )
      )
        return TPM_RC_SIZE;
    // If the parent is fixedTPM (including a Primary Object) the object must have
    // the same value for fixedTPM and fixedParent
    if(     parentObject == NULL
        || parentObject->publicArea.objectAttributes.fixedTPM == SET)
    {
        if(    publicArea->objectAttributes.fixedParent
            != publicArea->objectAttributes.fixedTPM
          )
             return TPM_RC_ATTRIBUTES;
    }
    else
        // The parent is not fixedTPM so the object can't be fixedTPM
        if(publicArea->objectAttributes.fixedTPM == SET)
             return TPM_RC_ATTRIBUTES;
    // A restricted object cannot be both sign and decrypt and it can't be neither
    // sign nor decrypt
    if (    publicArea->objectAttributes.restricted == SET
         && (    publicArea->objectAttributes.decrypt
              == publicArea->objectAttributes.sign)
       )
         return TPM_RC_ATTRIBUTES;
    // A fixedTPM object can not have encryptedDuplication bit SET
    if(    publicArea->objectAttributes.fixedTPM == SET
        && publicArea->objectAttributes.encryptedDuplication == SET)
        return TPM_RC_ATTRIBUTES;
    // If a parent object has fixedTPM CLEAR, the child must have the
    // same encryptedDuplication value as its parent.
    // Primary objects are considered to have a fixedTPM parent (the seeds).
   if(       (   parentObject != NULL
              && parentObject->publicArea.objectAttributes.fixedTPM == CLEAR)
       // Get here if parent is not fixed TPM
       && (     publicArea->objectAttributes.encryptedDuplication
             != parentObject->publicArea.objectAttributes.encryptedDuplication
           )
      )
        return TPM_RC_ATTRIBUTES;
   return SchemeChecks(load, parentHandle, publicArea);
}
//
//
//            FillInCreationData()
//
//      Fill in creation data for an object.
//
void
FillInCreationData(
    TPMI_DH_OBJECT                     parentHandle,    //   IN: handle of parent
    TPMI_ALG_HASH                      nameHashAlg,     //   IN: name hash algorithm
    TPML_PCR_SELECTION                *creationPCR,     //   IN: PCR selection
    TPM2B_DATA                        *outsideData,     //   IN: outside data
    TPM2B_CREATION_DATA               *outCreation,     //   OUT: creation data for output
    TPM2B_DIGEST                      *creationDigest   //   OUT: creation digest
//
   )
{
   BYTE                     creationBuffer[sizeof(TPMS_CREATION_DATA)];
   BYTE                    *buffer;
   INT32                    bufferSize;
   HASH_STATE               hashState;
   // Fill in TPMS_CREATION_DATA in outCreation
   // Compute PCR digest
   PCRComputeCurrentDigest(nameHashAlg, creationPCR,
                           &outCreation->t.creationData.pcrDigest);
   // Put back PCR selection list
   outCreation->t.creationData.pcrSelect = *creationPCR;
   // Get locality
   outCreation->t.creationData.locality
       = LocalityGetAttributes(_plat__LocalityGet());
   outCreation->t.creationData.parentNameAlg = TPM_ALG_NULL;
   // If the parent is is either a primary seed or TPM_ALG_NULL, then the Name
   // and QN of the parent are the parent's handle.
   if(HandleGetType(parentHandle) == TPM_HT_PERMANENT)
   {
       BYTE         *buffer = &outCreation->t.creationData.parentName.t.name[0];
       INT32         bufferSize = sizeof(TPM_HANDLE);
       outCreation->t.creationData.parentName.t.size =
            TPM_HANDLE_Marshal(&parentHandle, &buffer, &bufferSize);
         // Parent qualified name of a Temporary Object is the same as parent's
         // name
         MemoryCopy2B(&outCreation->t.creationData.parentQualifiedName.b,
                      &outCreation->t.creationData.parentName.b,
                     sizeof(outCreation->t.creationData.parentQualifiedName.t.name));
   }
   else           // Regular object
   {
       OBJECT              *parentObject = ObjectGet(parentHandle);
         // Set name algorithm
         outCreation->t.creationData.parentNameAlg =
             parentObject->publicArea.nameAlg;
         // Copy parent name
         outCreation->t.creationData.parentName = parentObject->name;
         // Copy parent qualified name
         outCreation->t.creationData.parentQualifiedName =
             parentObject->qualifiedName;
   }
   // Copy outside information
   outCreation->t.creationData.outsideInfo = *outsideData;
   // Marshal creation data to canonical form
   buffer = creationBuffer;
   bufferSize = sizeof(TPMS_CREATION_DATA);
   outCreation->t.size = TPMS_CREATION_DATA_Marshal(&outCreation->t.creationData,
                         &buffer, &bufferSize);
   // Compute hash for creation field in public template
   creationDigest->t.size = CryptStartHash(nameHashAlg, &hashState);
   CryptUpdateDigest(&hashState, outCreation->t.size, creationBuffer);
   CryptCompleteHash2B(&hashState, &creationDigest->b);
   return;
}
//           GetSeedForKDF()
//
//      Get a seed for KDF. The KDF for encryption and HMAC key use the same seed. It returns a pointer to
//      the seed
//
TPM2B_SEED*
GetSeedForKDF(
    TPM_HANDLE           protectorHandle,          // IN: the protector handle
    TPM2B_SEED          *seedIn                    // IN: the optional input seed
    )
{
    OBJECT                   *protector = NULL; // Pointer to the protector
    // Get seed for encryption key. Use input seed if provided.
    // Otherwise, using protector object's seedValue. TPM_RH_NULL is the only
    // exception that we may not have a loaded object as protector. In such a
    // case, use nullProof as seed.
    if(seedIn != NULL)
    {
        return seedIn;
    }
    else
    {
        if(protectorHandle == TPM_RH_NULL)
        {
             return (TPM2B_SEED *) &gr.nullProof;
        }
        else
        {
             protector = ObjectGet(protectorHandle);
             return (TPM2B_SEED *) &protector->sensitive.seedValue;
        }
    }
}
//
//
//           ProduceOuterWrap()
//
//      This function produce outer wrap for a buffer containing the sensitive data. It requires the sensitive data
//      being marshaled to the outerBuffer, with the leading bytes reserved for integrity hash. If iv is used, iv
//      space should be reserved at the beginning of the buffer. It assumes the sensitive data starts at address
//      (outerBuffer + integrity size {+ iv size}). This function performs:
//      a) Add IV before sensitive area if required
//      b) encrypt sensitive data, if iv is required, encrypt by iv. otherwise, encrypted by a NULL iv
//      c) add HMAC integrity at the beginning of the buffer It returns the total size of blob with outer wrap
//
UINT16
ProduceOuterWrap(
    TPM_HANDLE           protector,          //   IN: The handle of the object that provides
                                             //       protection. For object, it is parent
                                             //       handle. For credential, it is the handle
                                             //       of encrypt object.
    TPM2B_NAME          *name,               //   IN: the name of the object
    TPM_ALG_ID           hashAlg,            //   IN: hash algorithm for outer wrap
    TPM2B_SEED          *seed,               //   IN: an external seed may be provided for
                                             //       duplication blob. For non duplication
                                             //       blob, this parameter should be NULL
    BOOL                 useIV,              //   IN: indicate if an IV is used
    UINT16               dataSize,           //   IN: the size of sensitive data, excluding the
                                             //       leading integrity buffer size or the
                                             //       optional iv size
    BYTE                *outerBuffer         //   IN/OUT: outer buffer with sensitive data in
                                       //     it
   )
{
   TPM_ALG_ID         symAlg;
   UINT16             keyBits;
   TPM2B_SYM_KEY      symKey;
   TPM2B_IV           ivRNG;           // IV from RNG
   TPM2B_IV           *iv = NULL;
   UINT16             ivSize = 0;      // size of iv area, including the size field
   BYTE               *sensitiveData; // pointer to the sensitive data
   TPM2B_DIGEST       integrity;
   UINT16             integritySize;
   BYTE               *buffer;         // Auxiliary buffer pointer
   INT32              bufferSize;
   // Compute the beginning of sensitive data. The outer integrity should
   // always exist if this function function is called to make an outer wrap
   integritySize = sizeof(UINT16) + CryptGetHashDigestSize(hashAlg);
   sensitiveData = outerBuffer + integritySize;
   // If iv is used, adjust the pointer of sensitive data and add iv before it
   if(useIV)
   {
       ivSize = GetIV2BSize(protector);
         // Generate IV from RNG. The iv data size should be the total IV area
         // size minus the size of size field
         ivRNG.t.size = ivSize - sizeof(UINT16);
         CryptGenerateRandom(ivRNG.t.size, ivRNG.t.buffer);
         // Marshal IV to buffer
         buffer = sensitiveData;
         bufferSize = sizeof(TPM2B_IV);
         TPM2B_IV_Marshal(&ivRNG, &buffer, &bufferSize);
         // adjust sensitive data starting after IV area
         sensitiveData += ivSize;
         // Use iv for encryption
         iv = &ivRNG;
   }
   // Compute symmetric key parameters for outer buffer encryption
   ComputeProtectionKeyParms(protector, hashAlg, name, seed,
                             &symAlg, &keyBits, &symKey);
   // Encrypt inner buffer in place
   CryptSymmetricEncrypt(sensitiveData, symAlg, keyBits,
                         TPM_ALG_CFB, symKey.t.buffer, iv, dataSize,
                         sensitiveData);
   // Compute outer integrity. Integrity computation includes the optional IV
   // area
   ComputeOuterIntegrity(name, protector, hashAlg, seed, dataSize + ivSize,
                         outerBuffer + integritySize, &integrity);
   // Add integrity at the beginning of outer buffer
   buffer = outerBuffer;
   bufferSize = sizeof(TPM2B_DIGEST);
   TPM2B_DIGEST_Marshal(&integrity, &buffer, &bufferSize);
   // return the total size in outer wrap
   return dataSize + integritySize + ivSize;
}
//
//
//
//           UnwrapOuter()
//
//      This function remove the outer wrap of a blob containing sensitive data This function performs:
//      a) check integrity of outer blob
//      b) decrypt outer blob
//
//      Error Returns                      Meaning
//
//      TPM_RC_INSUFFICIENT                error during sensitive data unmarshaling
//      TPM_RC_INTEGRITY                   sensitive data integrity is broken
//      TPM_RC_SIZE                        error during sensitive data unmarshaling
//      TPM_RC_VALUE                       IV size for CFB does not match the encryption algorithm block size
//
TPM_RC
UnwrapOuter(
   TPM_HANDLE           protector,             //   IN: The handle of the object that provides
                                               //       protection. For object, it is parent
                                               //       handle. For credential, it is the handle
                                               //       of encrypt object.
   TPM2B_NAME          *name,                  //   IN: the name of the object
   TPM_ALG_ID           hashAlg,               //   IN: hash algorithm for outer wrap
   TPM2B_SEED          *seed,                  //   IN: an external seed may be provided for
                                               //       duplication blob. For non duplication
                                               //       blob, this parameter should be NULL.
   BOOL                 useIV,                 //   IN: indicates if an IV is used
   UINT16               dataSize,              //   IN: size of sensitive data in outerBuffer,
                                               //       including the leading integrity buffer
                                               //       size, and an optional iv area
   BYTE                *outerBuffer            //   IN/OUT: sensitive data
   )
{
   TPM_RC              result;
   TPM_ALG_ID          symAlg = TPM_ALG_NULL;
   TPM2B_SYM_KEY       symKey;
   UINT16              keyBits = 0;
   TPM2B_IV            ivIn;               // input IV retrieved from input buffer
   TPM2B_IV            *iv = NULL;
   BYTE                *sensitiveData;               // pointer to the sensitive data
   TPM2B_DIGEST        integrityToCompare;
   TPM2B_DIGEST        integrity;
   INT32               size;
   // Unmarshal integrity
   sensitiveData = outerBuffer;
   size = (INT32) dataSize;
   result = TPM2B_DIGEST_Unmarshal(&integrity, &sensitiveData, &size);
   if(result == TPM_RC_SUCCESS)
   {
       // Compute integrity to compare
       ComputeOuterIntegrity(name, protector, hashAlg, seed,
                             (UINT16) size, sensitiveData,
                             &integrityToCompare);
         // Compare outer blob integrity
         if(!Memory2BEqual(&integrity.b, &integrityToCompare.b))
             return TPM_RC_INTEGRITY;
         // Get the symmetric algorithm parameters used for encryption
         ComputeProtectionKeyParms(protector, hashAlg, name, seed,
                                          &symAlg, &keyBits, &symKey);
         // Retrieve IV if it is used
         if(useIV)
         {
             result = TPM2B_IV_Unmarshal(&ivIn, &sensitiveData, &size);
             if(result == TPM_RC_SUCCESS)
             {
                 // The input iv size for CFB must match the encryption algorithm
                 // block size
                 if(ivIn.t.size != CryptGetSymmetricBlockSize(symAlg, keyBits))
                     result = TPM_RC_VALUE;
                 else
                     iv = &ivIn;
             }
         }
    }
    // If no errors, decrypt private in place
    if(result == TPM_RC_SUCCESS)
        CryptSymmetricDecrypt(sensitiveData, symAlg, keyBits,
                              TPM_ALG_CFB, symKey.t.buffer, iv,
                              (UINT16) size, sensitiveData);
    return result;
}
//
//
//           SensitiveToPrivate()
//
//      This function prepare the private blob for off the chip storage The operations in this function:
//      a) marshal TPM2B_SENSITIVE structure into the buffer of TPM2B_PRIVATE
//      b) apply encryption to the sensitive area.
//      c) apply outer integrity computation.
//
void
SensitiveToPrivate(
    TPMT_SENSITIVE      *sensitive,         //   IN: sensitive structure
    TPM2B_NAME          *name,              //   IN: the name of the object
    TPM_HANDLE           parentHandle,      //   IN: The parent's handle
    TPM_ALG_ID           nameAlg,           //   IN: hash algorithm in public area. This
                                            //       parameter is used when parentHandle is
                                            //       NULL, in which case the object is
                                            //       temporary.
    TPM2B_PRIVATE       *outPrivate         //   OUT: output private structure
    )
{
    BYTE                     *buffer;                  //   Auxiliary buffer pointer
    INT32                    bufferSize;
    BYTE                     *sensitiveData;           //   pointer to the sensitive data
    UINT16                   dataSize;                 //   data blob size
    TPMI_ALG_HASH            hashAlg;                  //   hash algorithm for integrity
    UINT16                   integritySize;
    UINT16                   ivSize;
    pAssert(name != NULL && name->t.size != 0);
    // Find the hash algorithm for integrity computation
    if(parentHandle == TPM_RH_NULL)
    {
        // For Temporary Object, using self name algorithm
        hashAlg = nameAlg;
    }
    else
   {
         // Otherwise, using parent's name algorithm
         hashAlg = ObjectGetNameAlg(parentHandle);
   }
   // Starting of sensitive data without wrappers
   sensitiveData = outPrivate->t.buffer;
   // Compute the integrity size
   integritySize = sizeof(UINT16) + CryptGetHashDigestSize(hashAlg);
   // Reserve space for integrity
   sensitiveData += integritySize;
   // Get iv size
   ivSize = GetIV2BSize(parentHandle);
   // Reserve space for iv
   sensitiveData += ivSize;
   // Marshal sensitive area, leaving the leading 2 bytes for size
   buffer = sensitiveData + sizeof(UINT16);
   bufferSize = sizeof(TPMT_SENSITIVE);
   dataSize = TPMT_SENSITIVE_Marshal(sensitive, &buffer, &bufferSize);
   // Adding size before the data area
   buffer = sensitiveData;
   bufferSize = sizeof(UINT16);
   UINT16_Marshal(&dataSize, &buffer, &bufferSize);
   // Adjust the dataSize to include the size field
   dataSize += sizeof(UINT16);
   // Adjust the pointer to inner buffer including the iv
   sensitiveData = outPrivate->t.buffer + ivSize;
   //Produce outer wrap, including encryption and HMAC
   outPrivate->t.size = ProduceOuterWrap(parentHandle, name, hashAlg, NULL,
                                         TRUE, dataSize, outPrivate->t.buffer);
   return;
}
//
//
//           PrivateToSensitive()
//
//      Unwrap a input private area. Check the integrity, decrypt and retrieve data to a sensitive structure. The
//      operations in this function:
//      a) check the integrity HMAC of the input private area
//      b) decrypt the private buffer
//      c) unmarshal TPMT_SENSITIVE structure into the buffer of TPMT_SENSITIVE
//
//      Error Returns                   Meaning
//
//      TPM_RC_INTEGRITY                if the private area integrity is bad
//      TPM_RC_SENSITIVE                unmarshal errors while unmarshaling TPMS_ENCRYPT from input
//                                      private
//      TPM_RC_VALUE                    outer wrapper does not have an iV of the correct size
//
TPM_RC
PrivateToSensitive(
   TPM2B_PRIVATE       *inPrivate,          // IN: input private structure
   TPM2B_NAME          *name,               // IN: the name of the object
   TPM_HANDLE          parentHandle,    // IN: The parent's handle
   TPM_ALG_ID          nameAlg,         // IN: hash algorithm in public area. It is
                                        //     passed separately because we only pass
                                        //     name, rather than the whole public area
                                        //     of the object. This parameter is used in
                                        //     the following two cases: 1. primary
                                        //     objects. 2. duplication blob with inner
                                        //     wrap. In other cases, this parameter
                                        //     will be ignored
   TPMT_SENSITIVE     *sensitive        // OUT: sensitive structure
   )
{
   TPM_RC             result;
   BYTE               *buffer;
   INT32              size;
   BYTE               *sensitiveData; // pointer to the sensitive data
   UINT16             dataSize;
   UINT16             dataSizeInput;
   TPMI_ALG_HASH      hashAlg;        // hash algorithm for integrity
   OBJECT             *parent = NULL;
   UINT16             integritySize;
   UINT16             ivSize;
   // Make sure that name is provided
   pAssert(name != NULL && name->t.size != 0);
   // Find the hash algorithm for integrity computation
   if(parentHandle == TPM_RH_NULL)
   {
       // For Temporary Object, using self name algorithm
       hashAlg = nameAlg;
   }
   else
   {
       // Otherwise, using parent's name algorithm
       hashAlg = ObjectGetNameAlg(parentHandle);
   }
   // unwrap outer
   result = UnwrapOuter(parentHandle, name, hashAlg, NULL, TRUE,
                        inPrivate->t.size, inPrivate->t.buffer);
   if(result != TPM_RC_SUCCESS)
       return result;
   // Compute the inner integrity size.
   integritySize = sizeof(UINT16) + CryptGetHashDigestSize(hashAlg);
   // Get iv size
   ivSize = GetIV2BSize(parentHandle);
   // The starting of sensitive data and data size without outer wrapper
   sensitiveData = inPrivate->t.buffer + integritySize + ivSize;
   dataSize = inPrivate->t.size - integritySize - ivSize;
   // Unmarshal input data size
   buffer = sensitiveData;
   size = (INT32) dataSize;
   result = UINT16_Unmarshal(&dataSizeInput, &buffer, &size);
   if(result == TPM_RC_SUCCESS)
   {
       if((dataSizeInput + sizeof(UINT16)) != dataSize)
            result = TPM_RC_SENSITIVE;
       else
       {
              // Unmarshal sensitive buffer to sensitive structure
              result = TPMT_SENSITIVE_Unmarshal(sensitive, &buffer, &size);
              if(result != TPM_RC_SUCCESS || size != 0)
              {
                  pAssert(    (parent == NULL)
                           || parent->publicArea.objectAttributes.fixedTPM == CLEAR);
                  result = TPM_RC_SENSITIVE;
              }
              else
              {
                  // Always remove trailing zeros at load so that it is not necessary
                  // to check
                  // each time auth is checked.
                  MemoryRemoveTrailingZeros(&(sensitive->authValue));
              }
        }
    }
    return result;
}
//
//
//          SensitiveToDuplicate()
//
//      This function prepare the duplication blob from the sensitive area. The operations in this function:
//      a) marshal TPMT_SENSITIVE structure into the buffer of TPM2B_PRIVATE
//      b) apply inner wrap to the sensitive area if required
//      c) apply outer wrap if required
//
void
SensitiveToDuplicate(
    TPMT_SENSITIVE                *sensitive,          //   IN: sensitive structure
    TPM2B_NAME                    *name,               //   IN: the name of the object
    TPM_HANDLE                     parentHandle,       //   IN: The new parent's handle
    TPM_ALG_ID                     nameAlg,            //   IN: hash algorithm in public area. It
                                                       //       is passed separately because we
                                                       //       only pass name, rather than the
                                                       //       whole public area of the object.
    TPM2B_SEED                    *seed,               //   IN: the external seed. If external
                                                       //       seed is provided with size of 0,
                                                       //       no outer wrap should be applied
                                                       //       to duplication blob.
    TPMT_SYM_DEF_OBJECT           *symDef,             //   IN: Symmetric key definition. If the
                                                       //       symmetric key algorithm is NULL,
                                                       //       no inner wrap should be applied.
    TPM2B_DATA                    *innerSymKey,        //   IN/OUT: a symmetric key may be
                                                       //       provided to encrypt the inner
                                                       //       wrap of a duplication blob. May
                                                       //       be generated here if needed.
    TPM2B_PRIVATE                 *outPrivate          //   OUT: output private structure
    )
{
    BYTE                *buffer;        // Auxiliary buffer pointer
    INT32               bufferSize;
    BYTE                *sensitiveData; // pointer to the sensitive data
    TPMI_ALG_HASH       outerHash = TPM_ALG_NULL;// The hash algorithm for outer wrap
    TPMI_ALG_HASH       innerHash = TPM_ALG_NULL;// The hash algorithm for inner wrap
    UINT16              dataSize;       // data blob size
    BOOL                doInnerWrap = FALSE;
    BOOL                doOuterWrap = FALSE;
    // Make sure that name is provided
    pAssert(name != NULL && name->t.size != 0);
    // Make sure symDef and innerSymKey are not NULL
   pAssert(symDef != NULL && innerSymKey != NULL);
   // Starting of sensitive data without wrappers
   sensitiveData = outPrivate->t.buffer;
   // Find out if inner wrap is required
   if(symDef->algorithm != TPM_ALG_NULL)
   {
       doInnerWrap = TRUE;
       // Use self nameAlg as inner hash algorithm
       innerHash = nameAlg;
       // Adjust sensitive data pointer
       sensitiveData += sizeof(UINT16) + CryptGetHashDigestSize(innerHash);
   }
   // Find out if outer wrap is required
   if(seed->t.size != 0)
   {
       doOuterWrap = TRUE;
       // Use parent nameAlg as outer hash algorithm
       outerHash = ObjectGetNameAlg(parentHandle);
       // Adjust sensitive data pointer
       sensitiveData += sizeof(UINT16) + CryptGetHashDigestSize(outerHash);
   }
   // Marshal sensitive area, leaving the leading 2 bytes for size
   buffer = sensitiveData + sizeof(UINT16);
   bufferSize = sizeof(TPMT_SENSITIVE);
   dataSize = TPMT_SENSITIVE_Marshal(sensitive, &buffer, &bufferSize);
   // Adding size before the data area
   buffer = sensitiveData;
   bufferSize = sizeof(UINT16);
   UINT16_Marshal(&dataSize, &buffer, &bufferSize);
   // Adjust the dataSize to include the size field
   dataSize += sizeof(UINT16);
   // Apply inner wrap for duplication blob. It includes both integrity and
   // encryption
   if(doInnerWrap)
   {
       BYTE             *innerBuffer = NULL;
       BOOL             symKeyInput = TRUE;
       innerBuffer = outPrivate->t.buffer;
       // Skip outer integrity space
       if(doOuterWrap)
            innerBuffer += sizeof(UINT16) + CryptGetHashDigestSize(outerHash);
       dataSize = ProduceInnerIntegrity(name, innerHash, dataSize,
                                         innerBuffer);
        // Generate inner encryption key if needed
        if(innerSymKey->t.size == 0)
        {
            innerSymKey->t.size = (symDef->keyBits.sym + 7) / 8;
            CryptGenerateRandom(innerSymKey->t.size, innerSymKey->t.buffer);
             // TPM generates symmetric encryption.   Set the flag to FALSE
             symKeyInput = FALSE;
        }
        else
        {
             // assume the input key size should matches the symmetric definition
             pAssert(innerSymKey->t.size == (symDef->keyBits.sym + 7) / 8);
        }
        // Encrypt inner buffer in place
          CryptSymmetricEncrypt(innerBuffer, symDef->algorithm,
                                symDef->keyBits.sym, TPM_ALG_CFB,
                                innerSymKey->t.buffer, NULL, dataSize,
                                innerBuffer);
          // If the symmetric encryption key is imported, clear the buffer for
          // output
          if(symKeyInput)
              innerSymKey->t.size = 0;
   }
   // Apply outer wrap for duplication blob. It includes both integrity and
   // encryption
   if(doOuterWrap)
   {
       dataSize = ProduceOuterWrap(parentHandle, name, outerHash, seed, FALSE,
                                   dataSize, outPrivate->t.buffer);
   }
   // Data size for output
   outPrivate->t.size = dataSize;
   return;
}
//
//
//           DuplicateToSensitive()
//
//       Unwrap a duplication blob. Check the integrity, decrypt and retrieve data to a sensitive structure. The
//       operations in this function:
//       a) check the integrity HMAC of the input private area
//       b) decrypt the private buffer
//       c) unmarshal TPMT_SENSITIVE structure into the buffer of TPMT_SENSITIVE
//
//       Error Returns                   Meaning
//
//       TPM_RC_INSUFFICIENT             unmarshaling sensitive data from inPrivate failed
//       TPM_RC_INTEGRITY                inPrivate data integrity is broken
//       TPM_RC_SIZE                     unmarshaling sensitive data from inPrivate failed
//
TPM_RC
DuplicateToSensitive(
   TPM2B_PRIVATE                 *inPrivate,           //   IN: input private structure
   TPM2B_NAME                    *name,                //   IN: the name of the object
   TPM_HANDLE                     parentHandle,        //   IN: The parent's handle
   TPM_ALG_ID                     nameAlg,             //   IN: hash algorithm in public area.
   TPM2B_SEED                    *seed,                //   IN: an external seed may be provided.
                                                       //       If external seed is provided with
                                                       //       size of 0, no outer wrap is
                                                       //       applied
   TPMT_SYM_DEF_OBJECT           *symDef,              //   IN: Symmetric key definition. If the
                                                       //       symmetric key algorithm is NULL,
                                                       //       no inner wrap is applied
   TPM2B_DATA                    *innerSymKey,         //   IN: a symmetric key may be provided
                                                       //       to decrypt the inner wrap of a
                                                       //       duplication blob.
   TPMT_SENSITIVE                *sensitive            //   OUT: sensitive structure
   )
{
   TPM_RC              result;
   BYTE               *buffer;
   INT32              size;
   BYTE               *sensitiveData; // pointer to the sensitive data
   UINT16             dataSize;
   UINT16             dataSizeInput;
   // Make sure that name is provided
   pAssert(name != NULL && name->t.size != 0);
   // Make sure symDef and innerSymKey are not NULL
   pAssert(symDef != NULL && innerSymKey != NULL);
   // Starting of sensitive data
   sensitiveData = inPrivate->t.buffer;
   dataSize = inPrivate->t.size;
   // Find out if outer wrap is applied
   if(seed->t.size != 0)
   {
       TPMI_ALG_HASH   outerHash = TPM_ALG_NULL;
        // Use parent nameAlg as outer hash algorithm
        outerHash = ObjectGetNameAlg(parentHandle);
        result = UnwrapOuter(parentHandle, name, outerHash, seed, FALSE,
                             dataSize, sensitiveData);
        if(result != TPM_RC_SUCCESS)
            return result;
        // Adjust sensitive data pointer and size
        sensitiveData += sizeof(UINT16) + CryptGetHashDigestSize(outerHash);
        dataSize -= sizeof(UINT16) + CryptGetHashDigestSize(outerHash);
   }
   // Find out if inner wrap is applied
   if(symDef->algorithm != TPM_ALG_NULL)
   {
       TPMI_ALG_HASH   innerHash = TPM_ALG_NULL;
        // assume the input key size should matches the symmetric definition
        pAssert(innerSymKey->t.size == (symDef->keyBits.sym + 7) / 8);
        // Decrypt inner buffer in place
        CryptSymmetricDecrypt(sensitiveData, symDef->algorithm,
                              symDef->keyBits.sym, TPM_ALG_CFB,
                              innerSymKey->t.buffer, NULL, dataSize,
                              sensitiveData);
        // Use self nameAlg as inner hash algorithm
        innerHash = nameAlg;
        // Check inner integrity
        result = CheckInnerIntegrity(name, innerHash, dataSize, sensitiveData);
        if(result != TPM_RC_SUCCESS)
            return result;
        // Adjust sensitive data pointer and size
        sensitiveData += sizeof(UINT16) + CryptGetHashDigestSize(innerHash);
        dataSize -= sizeof(UINT16) + CryptGetHashDigestSize(innerHash);
   }
   // Unmarshal input data size
   buffer = sensitiveData;
   size = (INT32) dataSize;
   result = UINT16_Unmarshal(&dataSizeInput, &buffer, &size);
   if(result == TPM_RC_SUCCESS)
   {
       if((dataSizeInput + sizeof(UINT16)) != dataSize)
              result = TPM_RC_SIZE;
          else
          {
              // Unmarshal sensitive buffer to sensitive structure
              result = TPMT_SENSITIVE_Unmarshal(sensitive, &buffer, &size);
              // if the results is OK make sure that all the data was unmarshaled
              if(result == TPM_RC_SUCCESS && size != 0)
                  result = TPM_RC_SIZE;
       }
   }
   // Always remove trailing zeros at load so that it is not necessary to check
   // each time auth is checked.
   if(result == TPM_RC_SUCCESS)
       MemoryRemoveTrailingZeros(&(sensitive->authValue));
   return result;
}
//
//
//           SecretToCredential()
//
//       This function prepare the credential blob from a secret (a TPM2B_DIGEST) The operations in this
//       function:
//       a) marshal TPM2B_DIGEST structure into the buffer of TPM2B_ID_OBJECT
//       b) encrypt the private buffer, excluding the leading integrity HMAC area
//       c) compute integrity HMAC and append to the beginning of the buffer.
//       d) Set the total size of TPM2B_ID_OBJECT buffer
//
void
SecretToCredential(
   TPM2B_DIGEST              *secret,          //   IN: secret information
   TPM2B_NAME                *name,            //   IN: the name of the object
   TPM2B_SEED                *seed,            //   IN: an external seed.
   TPM_HANDLE                 protector,       //   IN: The protector's handle
   TPM2B_ID_OBJECT           *outIDObject      //   OUT: output credential
   )
{
   BYTE                      *buffer;          //   Auxiliary buffer pointer
   INT32                      bufferSize;
   BYTE                      *sensitiveData;   //   pointer to the sensitive data
   TPMI_ALG_HASH              outerHash;       //   The hash algorithm for outer wrap
   UINT16                     dataSize;        //   data blob size
   pAssert(secret != NULL && outIDObject != NULL);
   // use protector's name algorithm as outer hash
   outerHash = ObjectGetNameAlg(protector);
   // Marshal secret area to credential buffer, leave space for integrity
   sensitiveData = outIDObject->t.credential
                   + sizeof(UINT16) + CryptGetHashDigestSize(outerHash);
   // Marshal secret area
   buffer = sensitiveData;
   bufferSize = sizeof(TPM2B_DIGEST);
   dataSize = TPM2B_DIGEST_Marshal(secret, &buffer, &bufferSize);
   // Apply outer wrap
   outIDObject->t.size = ProduceOuterWrap(protector,
                                          name,
                                          outerHash,
                                          seed,
                                          FALSE,
                                          dataSize,
                                          outIDObject->t.credential);
   return;
}
//
//
//            CredentialToSecret()
//
//       Unwrap a credential. Check the integrity, decrypt and retrieve data to a TPM2B_DIGEST structure. The
//       operations in this function:
//       a) check the integrity HMAC of the input credential area
//       b) decrypt the credential buffer
//       c) unmarshal TPM2B_DIGEST structure into the buffer of TPM2B_DIGEST
//
//       Error Returns                      Meaning
//
//       TPM_RC_INSUFFICIENT                error during credential unmarshaling
//       TPM_RC_INTEGRITY                   credential integrity is broken
//       TPM_RC_SIZE                        error during credential unmarshaling
//       TPM_RC_VALUE                       IV size does not match the encryption algorithm block size
//
TPM_RC
CredentialToSecret(
   TPM2B_ID_OBJECT          *inIDObject,             //   IN: input credential blob
   TPM2B_NAME               *name,                   //   IN: the name of the object
   TPM2B_SEED               *seed,                   //   IN: an external seed.
   TPM_HANDLE                protector,              //   IN: The protector's handle
   TPM2B_DIGEST             *secret                  //   OUT: secret information
   )
{
   TPM_RC                           result;
   BYTE                            *buffer;
   INT32                            size;
   TPMI_ALG_HASH                    outerHash;     // The hash algorithm for outer wrap
   BYTE                            *sensitiveData; // pointer to the sensitive data
   UINT16                           dataSize;
   // use protector's name algorithm as outer hash
   outerHash = ObjectGetNameAlg(protector);
   // Unwrap outer, a TPM_RC_INTEGRITY error may be returned at this point
   result = UnwrapOuter(protector, name, outerHash, seed, FALSE,
                        inIDObject->t.size, inIDObject->t.credential);
   if(result == TPM_RC_SUCCESS)
   {
       // Compute the beginning of sensitive data
       sensitiveData = inIDObject->t.credential
                       + sizeof(UINT16) + CryptGetHashDigestSize(outerHash);
       dataSize = inIDObject->t.size
                  - (sizeof(UINT16) + CryptGetHashDigestSize(outerHash));
          // Unmarshal secret buffer to TPM2B_DIGEST structure
          buffer = sensitiveData;
          size = (INT32) dataSize;
          result = TPM2B_DIGEST_Unmarshal(secret, &buffer, &size);
          // If there were no other unmarshaling errors, make sure that the
          // expected amount of data was recovered
          if(result == TPM_RC_SUCCESS && size != 0)
              return TPM_RC_SIZE;
   }
   return result;
}