WARNING The following is not intended as an endorsement of converting passwords to ECDH keys. Create ECDH keys from high-entropy, crypto-safe PRNGs.
I want to take a secret and generate a ECDH public/private key from it.
In the browser, a usual way would be to use PBKDF2 (or other deterministic bytes) to generate an ECDH public/private key pair in WebCrypto.
The following sample code should do this, but it throws a DOM Exception in Chrome:
// Generate a random KDF key.
const priv = new Uint8Array(24)
crypto.getRandomValues(priv)
const kdfKey = await crypto.subtle.importKey(
'raw', priv, { name: 'PBKDF2' }, false, ['deriveKey'])
// Derive the ECDH key.
const salt = new Uint8Array(16)
const iterations = 2000
const hash = { name: 'SHA-512' }
const curve = { name: 'ECDH', namedCurve: 'P-384' }
const usages = ['deriveKey']
crypto.getRandomValues(salt)
const ecdhKey = await crypto.subtle.deriveKey({
name: 'PBKDF2', salt, iterations, hash
}, kdfKey, curve, true, usages) // throws.
The above works when the algorithm is AES-GCM
(i.e. when curve is replaced with e.g. { name: 'AES-GCM', length: 256 }
), but other algorithms throw exceptions as well, so I suspect I'm missing something ... subtle.
My hope was/is that WebCrypto would be suited to accepting random bits and generating the ECDH public/private key pair. It looks like this might not be the case.
The alternative would be to use PBKDF2
to deriveBits
that can be used to manually create the ECDH key pair. If this is indeed the only option, what is the usual algorithm for turning random bits into a public/private key (i.e. references & public implementations)? If I have to develop something, I'll likely post it here interest & review.
EDIT: Additional details of the use-case
The use of PBKDF is an attempt to avoid having to generate the public key (x
and y
) of the ECDH keypair when given the (private) d
parameter. The x
and y
are derivative and so needn't be stored, and we've a very limited datastore — suitable only for the private key e.g. 192 bits, more-or-less (PBKDF can smooth out the bit size, too, but that's an aside).
If WebCrypto computed the x
and y
when given (pseudo)random d
parameter, the desired outcome could be achieved/illustrated as follows:
>>> curve = { name: 'ECDH', namedCurve: 'P-256' }
>>> k = await crypto.subtle.generateKey(curve, true, ['deriveKey'])
>>> pri = await crypto.subtle.exportKey('jwk', k.privateKey)
>>> delete pri.x
>>> delete pri.y
>>> k2 = await crypto.subtle.importKey('jwk', pri)
^^ errors
PBKDF is used to generate (AES) keys in numerous examples. I was hoping the functionality for calculating x
and y
for elliptical curves, it already existing in WebCrypto, would be available through PBKDF2 deriveKey
.
The do-it-yourself alternative in Javascript is to parse JWK/Base64 (URL variant), then use a large-integer function with modulo arithmetic (e.g. Fermat's Little Theorem), and finally write functions for elliptical curve point addition, doubling, and multiplication. Which is what I've done (ECC math here). But I was hoping that'd all be unnecessary, seeing as the code for doing exactly this exists in WebCrypto, and I was just hoping to use either importKey
or deriveKey
to wield it.
Just to reiterate: There are no user-generated passwords; using such to generate the ECDH key is considered unwise.