Raghu
Raghu

Reputation: 3091

Questions about secret agreement from ECDiffieHellmanCng implementation in .net framework 4.7?

I have following code:

        var curve = ECCurve.NamedCurves.nistP256;
        var ecdhSender = ECDiffieHellman.Create(curve);
        var ecdhReceiver = ECDiffieHellman.Create(curve);

My understanding is that I should be able to compute secret agreement either from using ecdhSender object using ecdhReceiver.PublicKey or using ecdhReceiver object with ecdhSender.PublicKey and that both secret agreement values should be same. If this is a wrong assumption, please let me know.

Since the ECDiffieHellman.Create returns ECDiffieHellman type, I wrote following code to get the secret agreement:

        string receiverHexString = null;
        using (SafeNCryptSecretHandle secretAgreement = (ecdhReceiver as ECDiffieHellmanCng).DeriveSecretAgreementHandle(ecdhSender.PublicKey))
        {
            byte[] secretAgreementBytes = new byte[32];
            IntPtr pointer = secretAgreement.DangerousGetHandle();
            Marshal.Copy(pointer, secretAgreementBytes, 0, secretAgreementBytes.Length);
            receiverHexString = BitConverter.ToString(secretAgreementBytes).Replace("-", string.Empty).ToLower();
            Console.WriteLine($"receiver secretAgreement: 0x{receiverHexString}");
        }

        string senderHexString = null;
        using (SafeNCryptSecretHandle secretAgreement = (ecdhSender as ECDiffieHellmanCng).DeriveSecretAgreementHandle(ecdhReceiver.PublicKey))
        {
            byte[] secretAgreementBytes = new byte[32];
            IntPtr pointer = secretAgreement.DangerousGetHandle();
            Marshal.Copy(pointer, secretAgreementBytes, 0, secretAgreementBytes.Length);
            senderHexString = BitConverter.ToString(secretAgreementBytes).Replace("-", string.Empty).ToLower();
            Console.WriteLine($"sender secretAgreement: 0x{senderHexString}");
        }

        Assert.AreEqual(receiverHexString, senderHexString);

My assertion is failing. Obviously I am doing some thing wrong (if the secret agreements are supposed to be same).

Is it how I am extracting the bytes out of handle? Or something else?

Upvotes: 2

Views: 603

Answers (1)

bartonjs
bartonjs

Reputation: 33256

As of the last time I checked there is no documented way to get the raw secret agreement value from CNG, which is why .NET doesn't expose it.

The expected use of ECDH in .NET is

ECCurve curve = ECCurve.NamedCurves.nistP256;

// Usually you'll only have one private key (yours), so this isn't representative of
// real code.  An `ECDiffieHellman` object doesn't necessarily have a private key,
// but an `ECDiffieHellmanPublicKey` object definitely doesn't.
using (ECDiffieHellman bobPrivate = ECDiffieHellman.Create(curve))
using (ECDiffieHellman alicePrivate = ECDiffieHellman.Create(curve))
using (ECDiffieHellmanPublicKey bobPublic = bobPrivate.PublicKey)
using (ECDiffieHellmanPublicKey alicePublic = alicePrivate.PublicKey)
{
    byte[] ba = bobPrivate.DeriveKeyFromHash(alicePublic, HashAlgorithmName.SHA256);
    byte[] ab = alicePrivate.DeriveKeyFromHash(bobPublic, HashAlgorithmName.SHA256);

    // ba and ab have the same contents.
}

There's also

  • DeriveKeyFromHash(ECDiffieHellmanPublicKey otherPartyPublicKey, HashAlgorithmName hashAlgorithm, byte[] secretPrepend, byte[] secretAppend)
  • DeriveKeyFromHmac(ECDiffieHellmanPublicKey otherPartyPublicKey, HashAlgorithmName hashAlgorithm, byte[] hmacKey)
  • DeriveKeyFromHmac(ECDiffieHellmanPublicKey otherPartyPublicKey, HashAlgorithmName hashAlgorithm, byte[] hmacKey, byte[] secretPrepend, byte[] secretAppend)
  • DeriveKeyTls(ECDiffieHellmanPublicKey otherPartyPublicKey, byte[] prfLabel, byte[] prfSeed)

If bobPrivate and alicePublic (or, conversely, alicePrivate and bobPublic) are unchanged then providing the same inputs will yield the same outputs.

Note: You should avoid talking about ECDiffieHellmanCng specifically, if you can. When ECDH eventually makes it into .NET Standard, the ECDHCng class won't. As of .NET 4.6.2 everything can be done on the base class (except opening persisted keys); and .NET Core tends to not return public types from its Create() factory-methods.

Upvotes: 4

Related Questions