title: Is it possible to derive a key from a shared secret (BCryptSecretAgreement) without exposing the derived key (using the TPM)?
body: <p>From a high level, I'm trying to implement a ECDH key agreement scheme that results in the final ephemeral key being <strong>stored on the TPM</strong>.  It seems that the <code>bcrypt</code>/<code>ncrypt</code> interfaces have <em>almost</em> everything that I need.  I can perform the ECDH secret agreement and then derive a key from the shared secret.  As far as I can tell, by using the <code>MS_PLATFORM_CRYPTO_PROVIDER</code> key storage provider, the secret agreement is done inside the TPM. However, all of the key derivation functions that I can find will only output the newly derived key into a plaintext buffer in RAM.  This seems to defeat the purpose of performing the ECDH on the TPM (the ECHD itself is using ephemeral keys).</p>
  <p>Here's a snippet of the exchange that works, albeit with the output of <code>NCryptDeriveKey</code> getting stored in RAM.  Essentially, what I'm looking for is a way to store the output <code>NCryptDeriveKey</code>  in the TPM so I can use it for encryption later without leaking it out.  I assumed this was a normal use case, but I can't seem to find a way to do it.  This example uses <code>NCryptDeriveKey</code>, but <a href="https://stackoverflow.com/questions/40596395/cng-when-to-use-bcrypt-vs-ncrypt-family-of-functions">based on this</a>, I'll eventually want to use <code>BCryptxxx</code> for everything since I don't need it to be persistent.</p>
  <pre><code>
  //NOTE: Some error checking lines have been removed for clarity
  
  class EcdhPartner
  {
  public:
      EcdhPartner(NCRYPT_PROV_HANDLE hProvider, string name) :
          m_hProvider(hProvider),
          m_keyname(&quot;ECDHTestKey1&quot; + name),
          m_name(name)
      {}
          cout &lt;&lt; &quot;New EcdhPartner '&quot; + m_name + &quot;' keyname=&quot; + m_keyname + &quot;\n&quot;;
      }
  
      void GenerateKeypair()
      {
          SECURITY_STATUS secStatus;
          secStatus = NCryptCreatePersistedKey(m_hProvider, &amp;m_hKey, BCRYPT_ECDH_P256_ALGORITHM, s2lpcwstr(m_keyname), 0, NCRYPT_OVERWRITE_KEY_FLAG);
          secStatus = NCryptFinalizeKey(m_hKey, 0);
          
          // export the public key
          DWORD publicBlobLength;
          // get the length first
          secStatus = NCryptExportKey(m_hKey, NULL, BCRYPT_ECCPUBLIC_BLOB, NULL, NULL, NULL, &amp;publicBlobLength, 0);
          // now get the blob
          secStatus = NCryptExportKey(m_hKey, NULL, BCRYPT_ECCPUBLIC_BLOB, NULL, m_publicKeyBlob, 72, &amp;publicBlobLength, 0);
      }
  
      void DoKeyAgreement(EcdhPartner&amp; partner)
      {
          SECURITY_STATUS secStatus;
          NCRYPT_KEY_HANDLE hPartnerPublicKey;
  
          // open partner public key
          secStatus = NCryptImportKey(m_hProvider, NULL, BCRYPT_ECCPUBLIC_BLOB, NULL, &amp;hPartnerPublicKey, partner.m_publicKeyBlob, 72, 0);
          CheckSecStatus(secStatus, m_name, &quot;NCryptImportKey&quot;);
          secStatus = NCryptSecretAgreement(m_hKey, hPartnerPublicKey, &amp;m_hSharedSecret, 0);
          
          // Build KDF parameter list
          const DWORD             BufferLength = 3;
          BCryptBuffer            BufferArray[BufferLength] = { 0 };
          //specify hash algorithm
          BufferArray[0].BufferType = KDF_HASH_ALGORITHM;
          BufferArray[0].cbBuffer = (DWORD)((wcslen(BCRYPT_SHA256_ALGORITHM) + 1) * sizeof(WCHAR));
          BufferArray[0].pvBuffer = (PVOID)BCRYPT_SHA256_ALGORITHM;
          NCryptBufferDesc        parameterList = { 0 };
          parameterList.cBuffers = 1;
          parameterList.pBuffers = BufferArray;
          parameterList.ulVersion = BCRYPTBUFFER_VERSION;
          DWORD derivedKeyLength;
          // I NEED THE OUTPUT OF THIS TO REMAIN ON THE TPM!!!
          secStatus = NCryptDeriveKey(m_hSharedSecret, BCRYPT_KDF_HMAC, &amp;parameterList, m_derivedKey, sizeof(m_derivedKey), &amp;derivedKeyLength, KDF_USE_SECRET_AS_HMAC_KEY_FLAG);     
  
      }
  
      const string m_keyname;
      const string m_name;
      NCRYPT_PROV_HANDLE m_hProvider;
      NCRYPT_KEY_HANDLE m_hKey;
      BYTE m_publicKeyBlob[72]; //64 + 8-byte header
      NCRYPT_SECRET_HANDLE m_hSharedSecret;
      BYTE m_derivedKey[32];
  };
  
  
  int main()
  {
      LPCWSTR providerName = MS_PLATFORM_CRYPTO_PROVIDER;
      NCRYPT_PROV_HANDLE hProvider;
      SECURITY_STATUS secStatus;    
      secStatus = NCryptOpenStorageProvider(&amp;hProvider, providerName, 0);
  
      EcdhPartner alice(hProvider, &quot;Alice&quot;);
      EcdhPartner bob(hProvider, &quot;Bob&quot;);
      alice.GenerateKeypair();
      bob.GenerateKeypair();
      alice.DoKeyAgreement(bob);
      bob.DoKeyAgreement(alice);
  }
  </code></pre>