From 539b33caa8f5fc5d0b3fccf6a9becc6e4770b4d2 Mon Sep 17 00:00:00 2001 From: bcoll Date: Thu, 4 May 2023 23:28:00 +0100 Subject: [PATCH] Assorted WebCrypto API fixes - Accept `Ed25519` algorithm (supported as of cloudflare/workerd#500) - Return `Ed25519` as the algorithm for `NODE-ED25519` keys - Mark `X448` and `Ed448` algorithms as unsupported This passes Miniflare's test suite on Node 16.13.0 and 20.1.0. It also passes `panva/jose`'s `workerd` tap tests on Node 16.17.0 and 20.1.0. Some of those tests fail on 16.13.0 as support for the `X25519` algorithm was only added in 16.17.0. See https://nodejs.org/api/webcrypto.html#web-crypto-api for details. --- packages/core/src/standards/crypto.ts | 24 ++++++++++++++++----- packages/core/test/standards/crypto.spec.ts | 6 +++--- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/packages/core/src/standards/crypto.ts b/packages/core/src/standards/crypto.ts index 1a1949db5..5b8183f76 100644 --- a/packages/core/src/standards/crypto.ts +++ b/packages/core/src/standards/crypto.ts @@ -70,6 +70,20 @@ const usesModernEd25519 = (async () => { async function ensureValidNodeAlgorithm( algorithm: webcrypto.AlgorithmIdentifier | webcrypto.EcKeyAlgorithm ): Promise { + // "X448" and "Ed448" are not supported by Workers + if ( + typeof algorithm === "object" && + (algorithm.name === "X448" || algorithm.name === "Ed448") + ) { + throw new DOMException("Unrecognized name.", "NotSupportedError"); + } + if ( + typeof algorithm === "object" && + algorithm.name === "Ed25519" && + !(await usesModernEd25519) + ) { + return { name: "NODE-ED25519", namedCurve: "NODE-ED25519" }; + } if ( typeof algorithm === "object" && algorithm.name === "NODE-ED25519" && @@ -83,19 +97,19 @@ async function ensureValidNodeAlgorithm( } function ensureValidWorkerKey(key: webcrypto.CryptoKey): webcrypto.CryptoKey { - // Users' workers will expect to see the `NODE-ED25519` algorithm, even if - // we're using "Ed25519" internally (https://github.com/panva/jose/issues/446) - if (key.algorithm.name === "Ed25519") key.algorithm.name = "NODE-ED25519"; + // Users' workers will expect to see the `Ed25519` algorithm, even if we're + // using on an old Node version and using "Ed25519" internally + if (key.algorithm.name === "NODE-ED25519") key.algorithm.name = "Ed25519"; return key; } async function ensureValidNodeKey( key: webcrypto.CryptoKey ): Promise { - if (key.algorithm.name === "NODE-ED25519" && (await usesModernEd25519)) { + if (key.algorithm.name === "Ed25519" && !(await usesModernEd25519)) { return new Proxy(key, { get(target, property, receiver) { - if (property === "algorithm") return { name: "Ed25519" }; + if (property === "algorithm") return { name: "NODE-ED25519" }; return Reflect.get(target, property, receiver); }, }); diff --git a/packages/core/test/standards/crypto.spec.ts b/packages/core/test/standards/crypto.spec.ts index a640debcc..4fcba2286 100644 --- a/packages/core/test/standards/crypto.spec.ts +++ b/packages/core/test/standards/crypto.spec.ts @@ -100,8 +100,8 @@ test("crypto: generateKey/exportKey: supports NODE-ED25519 algorithm", async (t) true, ["sign", "verify"] ); - t.is(keyPair.publicKey.algorithm.name, "NODE-ED25519"); - t.is(keyPair.privateKey.algorithm.name, "NODE-ED25519"); + t.is(keyPair.publicKey.algorithm.name, "Ed25519"); + t.is(keyPair.privateKey.algorithm.name, "Ed25519"); const exported = await crypto.subtle.exportKey("raw", keyPair.publicKey); t.is(exported.byteLength, 32); }); @@ -126,7 +126,7 @@ test("crypto: importKey/exportKey: supports NODE-ED25519 public keys", async (t) true, ["verify"] ); - t.is(publicKey.algorithm.name, "NODE-ED25519"); + t.is(publicKey.algorithm.name, "Ed25519"); const exported = await crypto.subtle.exportKey("raw", publicKey); t.is(Buffer.from(exported).toString("hex"), keyData); });