SubtleCrypto not verifying but node crypto is

92 views Asked by At

I'm working on doing WebAuthN on the server, and I'm running this in Node:

import nCrypto from "crypto";
const base64UrlToUintArray = (src) => {
  const raw = atob(src.replaceAll("-", "+").replaceAll("_", "/"));
  return Uint8Array.from(raw, (char) => char.charCodeAt(0));
};

const jwk = {
  kty: "EC",
  alg: "ES256",
  crv: "P-256",
  x: "uymD4lAdTz26QJV30-DRkEt1d9WTazEKPfuWN3Fyd9U",
  y: "BZvD93pXLYfqXkp9sqylz_UN88OLapiV8drZQHdUCZE",
};
const input = Uint8Array.from([
  177, 105, 230, 129, 73, 100, 147, 50, 55, 183, 116, 206, 222, 74, 197, 72,
  177, 173, 14, 226, 46, 148, 229, 253, 77, 144, 96, 248, 233, 108, 69, 171, 5,
  101, 81, 31, 94, 138, 188, 129, 159, 72, 206, 19, 189, 169, 103, 118, 178, 17,
  79, 84, 126, 190, 133, 144, 77, 172, 161, 115, 67, 100, 154, 222, 235, 62, 5,
  6, 187,
]);
const signature =
  "MEQCIAE8ZT7CC9uQFaOIzPaEtLwnIGgjJLDYeZTyOc9hzxmkAiAphf71sZD5zxfAu0ELOOcn6yAfzu-Jbm-XT3SLe9dQgw";

const key = await nCrypto.subtle.importKey(
  "jwk",
  jwk,
  { name: "ECDSA", namedCurve: "P-256" },
  false,
  ["verify"]
);
const verified = await nCrypto.subtle.verify(
  {
    name: "ECDSA",
    hash: "SHA-256",
  },
  key,
  base64UrlToUintArray(signature),
  input
);
const verified2 = nCrypto
  .createVerify("sha256")
  .update(Buffer.from(input))
  .verify(
    nCrypto.createPublicKey({ key: jwk, format: "jwk" }),
    Buffer.from(base64UrlToUintArray(signature))
  );
console.log("web crypto verification", verified);
console.log("node crypto verification", verified2);

The Node implementation returns true as it should be, but the Web Crypto implementation returns false. How do I fix this?

* I want to use Web Crypto because eventually this will run on a serverless platform, my dev server is node-based though

1

There are 1 answers

0
KTibow On

I converted it to R-S format with this code:

const asn1Sig = base64UrlToUintArray(signature);
let lenR = asn1Sig[3],
  lenS = asn1Sig[5 + lenR];
let r = asn1Sig.slice(4, 4 + lenR),
  s = asn1Sig.slice(6 + lenR, 6 + lenR + lenS);

if (r[0] === 0) {
  r = r.slice(1);
}
if (s[0] === 0) {
  s = s.slice(1);
}

while (r.length < 32) {
  r = new Uint8Array([0, ...r]);
}
while (s.length < 32) {
  s = new Uint8Array([0, ...s]);
}

const rsSig = new Uint8Array([...r, ...s]);