Diffie-Hellman Confusion

Boris Reitman
4 min readDec 21, 2018

--

This article is oriented towards software developers that have worked with encryption technologies. It focuses on CommonCrypto in iOS and OSX, and Web Crypto.

Sometimes you tread on the wrong path until you come to a point of extreme confusion. Hopefully then you get an aha-moment and understand that you held a wrong premise all along. It is one of those “check your premises” cases that Ayn Rand has talked about.

I wanted to implement the Elliptic Curve Diffie Hellman (ECDH) protocol on iOS, using the Swift programming language. Turns out that cryptographic operations are available in a C-library called CommonCrypto. Because interfacing to it from Swift is complicated, one developer wrote a library SwCrypt that makes it straightforward.

Thus, I have decided to use SwCrypt, and my resource was this sample usage documentation:

Elliptic Curve Functionslet keys = try? CC.EC.generateKeyPair(384)
let signed = try? CC.EC.signHash(keys!.0, hash: hash)
let verified = try? CC.EC.verifyHash(keys!.1, hash: hash, signedData: signed!)
let shared = try? CC.EC.computeSharedSecret(keys!.0, publicKey: partnerPubKey)let privComponents = try? CC.EC.getPrivateKeyComponents(keys!.0)
let pubComponents = try? CC.EC.getPublicKeyComponents(keys!.1)
let pubKey = try? CC.EC.createFromData(keySize, x, y)
let pubKey = try? CC.EC.getPublicKeyFromPrivateKey(keys!.0)

Diffie-Hellman Functions
let dh = try CC.DH.DH(dhParam: .rfc3526Group5)
let myPubKey = try dh.generateKey()
let commonKey = try dh.computeKey(partnerPubKey!)

Before you read any further, take a moment and think how would you use this to do a key exchange with another party that is not using Swift, but uses Web Crypto. The task is then to import his JWK key, generate a shared secret, and also to send back your own public key in JWK.

As you know, there is plain Diffie-Hellman exchange (DH), and Elliptic Curve variant (ECDH). The first section of the documentation refers to the ECDH variant, and the second section refers to the DH variant. This, however, I only understand now. I made the error assuming that both sections are referring to the ECDH variant. Why did I make this error? I’m coming to Swift after having experience with Web Crypto, in which only ECDH works, but DH does not. (Tested only in Google Chrome).

I also was careful to check that the group .rfc3526Group5 is the same one that Web Crypto uses under the name “P-256”, in an invocation like this:

crypto.subtle.generateKey({name:"ECDH", namedCurve: "P-256"}, true, ["deriveKey"])

Somehow I found on the internet that indeed this group is known under that name, but I can no longer find the reference. This has further placed in my mind, that I’m on the right track.

My next step was to import the public key that I have received from the peer, for which I used the CC.EC.createFromData function, thereby getting a partnerKey. After that, I planned to plug it into the dh.computeKey call.

However, after I did so, I did not get an error. It should have been an error because I was plugging an ECDH key into a DH algorithm. Instead, I got a shared secret that is 192 bytes long.

This is when I became quite confused. Why is the key so long? Remembering that DH keys are much longer than ECDH keys should have been a hint, but that thought did not occur to me. It also hasn’t occurred to me that since the shared secret operation is an operation inside the elliptic curve group, the shared secret can’t be larger than 32 + 32 bytes, the size of each coordinate. Instead, I thought that the formula for computing the shared secret has to do with exponentiation in basic mod p, even when we are in the elliptic curve group.

So, now I have 192 bytes worth of shared secret, and I need to make it into a 32 byte long AES key. How do I proceed? Furthermore, how do I make it the same as the following Derive Key algorithm found in Web Crypto?

crypto.subtle.deriveKey({
name: "ECDH",
namedCurve: "P-256",
public: publicKey, // peer ECDH public key
}, privateKey, // your ECDH private key
{ name: "AES-GCM", length: 256 }) // derived key

Someone on IRC told me to plug the 192 bytes shared secret into HKDF key generation function. I thought that maybe Web Crypto does this too, behind the scenes. However, upon further digging into Web Crypto documentation, it appears that HKDF is not used, and a key is derived directly from the shared secret bits.

My epiphany moment came when I tried to export the “x” and “y” coordinates from myPubKey returned by dh.generateKey call, because I got an incompatibility error. I began looking into the format of this value. Turns out the format is described in the document “PKCS#3: Diffie-Hellman KeyAgreement Standard” which doesn’t mention any elliptic curves. It uses however the “x” and “y” terminology for exponents (instead of coordinates) which was yet another stumbling block before it finally hit me that I’m looking at plain Diffie-Hellman algorithm, instead of ECDH.

Once I understood that I was trying to mix DH and ECDH everything fell into place, and I decided to write this down, before I forget about this roller coaster.

--

--

Boris Reitman
Boris Reitman

Written by Boris Reitman

The course of history is determined by the spreading of ideas. I’m spreading the good ones.

No responses yet