Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using WebCrypto to generate ECDH key from PBKDF2

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.

like image 854
Brian M. Hunt Avatar asked Jan 27 '23 02:01

Brian M. Hunt


1 Answers

Brian,

It is not possible to deterministically create a keypair from a password utilizing WebCrypto.

Sorry.

You could deterministically create a key pair from a password in Javascript and then import that keypair as long as its a secp256r1 key in the right representation.

There are a few javascript libraries out there that support deterministically creating a key pair from a password. I can not make any statements on the correctness or security of those libraries or if there are ones that do this for secp256r1. Of course, key generation is one of the most important aspects to consider when using crypto and doing so based on a weak source of entropy is bad.

If you decide to go forward with that approach despite this importing a key of a given type is quite straightforward, see: https://github.com/diafygi/webcrypto-examples#ecdsa---importkey

TL;DR your miles will vary with this approach but it is possible.

Ryan

like image 108
rmhrisk Avatar answered Feb 09 '23 02:02

rmhrisk