// 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 "ECDH_KeyGen_fp.h"
#ifdef TPM_ALG_ECC
//
//
//     Error Returns                     Meaning
//
//     TPM_RC_ATTRIBUTES                 If the key is restricted or the key is not a decryption key
//     TPM_RC_KEY                        keyHandle does not reference a non-restricted decryption ECC key
//
TPM_RC
TPM2_ECDH_KeyGen(
   ECDH_KeyGen_In        *in,                 // IN: input parameter list
   ECDH_KeyGen_Out       *out                 // OUT: output parameter list
   )
{
   OBJECT                    *eccKey;
   TPM2B_ECC_PARAMETER        sensitive;
   TPM_RC                     result;

// Input Validation

   eccKey = ObjectGet(in->keyHandle);

   // Input key must be a non-restricted, decrypt ECC key
   if(   eccKey->publicArea.type != TPM_ALG_ECC)
       return TPM_RC_KEY + RC_ECDH_KeyGen_keyHandle;

   if(     eccKey->publicArea.objectAttributes.restricted == SET
      ||   eccKey->publicArea.objectAttributes.decrypt != SET
     )
       return TPM_RC_ATTRIBUTES + RC_ECDH_KeyGen_keyHandle;

// Command Output
   do
   {
       // Create ephemeral ECC key
       CryptNewEccKey(eccKey->publicArea.parameters.eccDetail.curveID,
                      &out->pubPoint.t.point, &sensitive);

       out->pubPoint.t.size = TPMS_ECC_POINT_Marshal(&out->pubPoint.t.point,
                              NULL, NULL);

       // Compute Z
       result = CryptEccPointMultiply(&out->zPoint.t.point,
                                  eccKey->publicArea.parameters.eccDetail.curveID,
                                  &sensitive, &eccKey->publicArea.unique.ecc);
       // The point in the key is not on the curve. Indicate that the key is bad.
       if(result == TPM_RC_ECC_POINT)
           return TPM_RC_KEY + RC_ECDH_KeyGen_keyHandle;
       // The other possible error is TPM_RC_NO_RESULT indicating that the
       // multiplication resulted in the point at infinity, so get a new
       // random key and start over (hardly ever happens).
   }
   while(result == TPM_RC_NO_RESULT);

   if(result == TPM_RC_SUCCESS)
       // Marshal the values to generate the point.
       out->zPoint.t.size = TPMS_ECC_POINT_Marshal(&out->zPoint.t.point,
                                                   NULL, NULL);

   return result;
}
#endif