1

From a high level, I'm trying to implement a ECDH key agreement scheme that results in the final ephemeral key being stored on the TPM. It seems that the bcrypt/ncrypt interfaces have almost 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 MS_PLATFORM_CRYPTO_PROVIDER 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).

Here's a snippet of the exchange that works, albeit with the output of NCryptDeriveKey getting stored in RAM. Essentially, what I'm looking for is a way to store the output NCryptDeriveKey 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 NCryptDeriveKey, but based on this, I'll eventually want to use BCryptxxx for everything since I don't need it to be persistent.


//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("ECDHTestKey1" + name),
        m_name(name)
    {}
        cout << "New EcdhPartner '" + m_name + "' keyname=" + m_keyname + "\n";
    }

    void GenerateKeypair()
    {
        SECURITY_STATUS secStatus;
        secStatus = NCryptCreatePersistedKey(m_hProvider, &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, &publicBlobLength, 0);
        // now get the blob
        secStatus = NCryptExportKey(m_hKey, NULL, BCRYPT_ECCPUBLIC_BLOB, NULL, m_publicKeyBlob, 72, &publicBlobLength, 0);
    }

    void DoKeyAgreement(EcdhPartner& partner)
    {
        SECURITY_STATUS secStatus;
        NCRYPT_KEY_HANDLE hPartnerPublicKey;

        // open partner public key
        secStatus = NCryptImportKey(m_hProvider, NULL, BCRYPT_ECCPUBLIC_BLOB, NULL, &hPartnerPublicKey, partner.m_publicKeyBlob, 72, 0);
        CheckSecStatus(secStatus, m_name, "NCryptImportKey");
        secStatus = NCryptSecretAgreement(m_hKey, hPartnerPublicKey, &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, &parameterList, m_derivedKey, sizeof(m_derivedKey), &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(&hProvider, providerName, 0);

    EcdhPartner alice(hProvider, "Alice");
    EcdhPartner bob(hProvider, "Bob");
    alice.GenerateKeypair();
    bob.GenerateKeypair();
    alice.DoKeyAgreement(bob);
    bob.DoKeyAgreement(alice);
}
rkor
  • 63
  • 7

1 Answers1

0

Short answer is yes, there is not such a things as an unhackeable code, bad news it requires years of computing process

  • Stack Overflow is a Q&A site, not a forum. If you post an answer it needs to answer the question. This one doesn't. Time to take the [tour]. – IInspectable Aug 23 '21 at 21:59
  • 1
    What are you talking about ? What you just said doesn’t have fundamentals –  Aug 23 '21 at 22:01