WebCrypto ECDH - Unrecognized or unimplemented key generation algorithm requested

Hello,

I want to use the ECDH algorithm to exchange public keys, then obtain the same derivedKey for client and server, then crypt / encrypt their messages, so my PWA is using End-to-End encryption.

My code works 100% fine in Chrome, on both client and simulated server on client.

When I try the code for real on server, especially when I generate a ECDH key pair (privateKey / publicKey), I receive the following error message from my Cloudflare Worker :

 Unrecognized or unimplemented key generation algorithm requested

Is there anything I can do to bypass this error ?

Regards,

EDCH module

const name = 'ECDH';
const namedCurve = 'P-256'; // P-256, P-384, P-521
const usage: KeyUsage[] = ['deriveKey']; // deriveKey, deriveBits
const aes = {
  name: 'AES-GCM', // AES-CTR, AES-CBC, AES-CMAC, AES-GCM, AES-CFB, AES-KW, ECDH, DH, HMAC
  length: 256, // 128, 192, 256
};
const aesUsage: KeyUsage[] = ['encrypt', 'decrypt'];

// https://github.com/diafygi/webcrypto-examples#ecdh

export const keys = async () => {
  const o = await crypto.subtle.generateKey({ name, namedCurve }, true, usage)
  return { publicKey: await exportKey(o.publicKey), privateKey: await exportKey(o.privateKey) };
}

export const deriveKey = async (privateKey, publicKey) => {
  const _private = await importPrivateKey(privateKey);
  const _public = await importPublicKey(publicKey);
  const cryptoKey = await crypto.subtle.deriveKey({ name, namedCurve, public: _public } as any, _private, aes, true, aesUsage);
  return ab2hex(await crypto.subtle.exportKey('raw', cryptoKey));
}

// "jwk" (public or private), "spki" (public only), or "pkcs8" (private only)
const exportKey = async (cryptoKey) => JSON.stringify(await crypto.subtle.exportKey('jwk', cryptoKey));
const importPublicKey = async (key) => importKey(key, true);
const importPrivateKey = async (key) => importKey(key, false);
const importKey = async (key, isPublic) => crypto.subtle.importKey('jwk', JSON.parse(key), { name, namedCurve }, true, isPublic ? [] : usage);
const ab2hex = async (ab) => [...new Uint8Array(ab)].map(b => b.toString(16).padStart(2, '0')).join('');

Test code

const main = async () => {
  const ecdh = (await import('crypto-ecdh'));

  const clientKeys = await ecdh.keys();
  console.log({ clientKeys });

  const serverKeys = await ecdh.keys();
  console.log({ serverKeys });

  const clientDerivedKey = await ecdh.deriveKey(clientKeys.privateKey, serverKeys.publicKey);
  console.log({ clientDerivedKey });

  const serverDerivedKey = await ecdh.deriveKey(serverKeys.privateKey, clientKeys.publicKey);
  console.log({ serverDerivedKey });
}
main();

ECDSA is implemented, but not ECDH. While they might be interchangeable (they’re basically the same), it will probably be denied by Cloudflare since it’s not “officially” supported.

You can get it to work without native crypto though, by using KJUR, but you’ll end up just at the edge of those 50ms CPU-time. So you’ll need to run on Workers Unbound.

Edit: I see generateKey() is also not supported on ECDSA.

2 Likes

Thanks for the information.

In the same time, because there is some discussion around NIST curves, I think i’ll give a try to X25519 through this WASM project :

Client-side, it appears to load and execute fast enough

  • generate key pair : 0.26 ms
  • deriveKey : 0.63 ms
  • Total (load, generate key pair, deriveKey) : 13ms

I suppose these times will be approximatively OK for Cloudflare Workers (just need to add 1 or 2 KV read/writes in the process).

As long as you don’t need to parse larger bodies of XML/HTML it should be fine.

There’s also some effort of making the rust crate “RING” to work in WASM.

1 Like

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.