Initial Commit
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
/**
|
||||
* Deterministic test keys for the webauthn fixture suite.
|
||||
*
|
||||
* ES256 (P-256), ES384, ES512 use hardcoded scalars; ECDSA via
|
||||
* `@noble/curves` uses RFC 6979 deterministic-k so signatures round-trip
|
||||
* byte-stably given the same message. Ed25519 is deterministic by spec.
|
||||
* Public keys are memoised across the suite.
|
||||
*
|
||||
* `@noble/curves` is added as a devDependency on `@suaveplan/webauthn`
|
||||
* specifically for these fixtures (it is the same transitive dep that
|
||||
* `@suaveplan/crypto-curve` already depends on; fixture-only).
|
||||
*/
|
||||
import { p256 as nobleP256 } from "@noble/curves/p256";
|
||||
import { p384 as nobleP384 } from "@noble/curves/p384";
|
||||
import { p521 as nobleP521 } from "@noble/curves/p521";
|
||||
import type { Ec2Key, OkpKey } from "@suaveplan/cbor/cose";
|
||||
import { ed25519 } from "@suaveplan/crypto-curve";
|
||||
|
||||
const P256_PRIV = Uint8Array.from([
|
||||
0x70, 0x83, 0x95, 0xab, 0x2c, 0x46, 0x3d, 0x68, 0xa1, 0x10, 0x4f, 0x37,
|
||||
0x29, 0xb5, 0xd5, 0x42, 0x0c, 0x18, 0x21, 0x9f, 0xa0, 0x60, 0xff, 0xff,
|
||||
0x5c, 0x4d, 0x16, 0x97, 0x21, 0x83, 0x9b, 0x55,
|
||||
]);
|
||||
|
||||
const P384_PRIV = Uint8Array.from(
|
||||
Array.from({ length: 48 }, (_v, i) => (i * 7 + 3) & 0xff)
|
||||
);
|
||||
|
||||
const P521_PRIV = Uint8Array.from([
|
||||
0x00, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45,
|
||||
0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd,
|
||||
0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45,
|
||||
0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd,
|
||||
0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45,
|
||||
0x67, 0x89, 0xab, 0xcd, 0xef, 0x77,
|
||||
]);
|
||||
|
||||
const ED25519_PRIV = new Uint8Array(32).fill(0xa5);
|
||||
|
||||
export interface EcTestKey {
|
||||
readonly priv: Uint8Array;
|
||||
readonly pubUncompressed: Uint8Array;
|
||||
readonly x: Uint8Array;
|
||||
readonly y: Uint8Array;
|
||||
readonly coseKey: Ec2Key;
|
||||
}
|
||||
|
||||
export interface EddsaTestKey {
|
||||
readonly priv: Uint8Array;
|
||||
readonly pub: Uint8Array;
|
||||
readonly coseKey: OkpKey;
|
||||
}
|
||||
|
||||
let p256Cache: EcTestKey | undefined;
|
||||
let p384Cache: EcTestKey | undefined;
|
||||
let p521Cache: EcTestKey | undefined;
|
||||
let edCache: EddsaTestKey | undefined;
|
||||
|
||||
function ecBuild(
|
||||
priv: Uint8Array,
|
||||
pubUncompressed: Uint8Array,
|
||||
coordLen: number,
|
||||
crv: "P-256" | "P-384" | "P-521",
|
||||
alg: "ES256" | "ES384" | "ES512"
|
||||
): EcTestKey {
|
||||
const x = pubUncompressed.slice(1, 1 + coordLen);
|
||||
const y = pubUncompressed.slice(1 + coordLen, 1 + 2 * coordLen);
|
||||
return {
|
||||
priv,
|
||||
pubUncompressed,
|
||||
x,
|
||||
y,
|
||||
coseKey: {
|
||||
kty: "EC2",
|
||||
crv,
|
||||
alg,
|
||||
x,
|
||||
y,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/** Deterministic P-256 keypair for ES256 webauthn tests. */
|
||||
export function getEcdsaP256TestKey(): EcTestKey {
|
||||
if (p256Cache !== undefined) {
|
||||
return p256Cache;
|
||||
}
|
||||
const pub = nobleP256.getPublicKey(P256_PRIV, false);
|
||||
p256Cache = ecBuild(P256_PRIV, pub, 32, "P-256", "ES256");
|
||||
return p256Cache;
|
||||
}
|
||||
|
||||
/** Deterministic P-384 keypair for ES384 webauthn tests. */
|
||||
export function getEcdsaP384TestKey(): EcTestKey {
|
||||
if (p384Cache !== undefined) {
|
||||
return p384Cache;
|
||||
}
|
||||
const pub = nobleP384.getPublicKey(P384_PRIV, false);
|
||||
p384Cache = ecBuild(P384_PRIV, pub, 48, "P-384", "ES384");
|
||||
return p384Cache;
|
||||
}
|
||||
|
||||
/** Deterministic P-521 keypair for ES512 webauthn tests. */
|
||||
export function getEcdsaP521TestKey(): EcTestKey {
|
||||
if (p521Cache !== undefined) {
|
||||
return p521Cache;
|
||||
}
|
||||
const pub = nobleP521.getPublicKey(P521_PRIV, false);
|
||||
p521Cache = ecBuild(P521_PRIV, pub, 66, "P-521", "ES512");
|
||||
return p521Cache;
|
||||
}
|
||||
|
||||
/** Deterministic Ed25519 keypair for EdDSA webauthn tests. */
|
||||
export function getEd25519TestKey(): EddsaTestKey {
|
||||
if (edCache !== undefined) {
|
||||
return edCache;
|
||||
}
|
||||
const pub = ed25519.getPublicKey(ED25519_PRIV);
|
||||
edCache = {
|
||||
priv: ED25519_PRIV,
|
||||
pub,
|
||||
coseKey: {
|
||||
kty: "OKP",
|
||||
crv: "Ed25519",
|
||||
alg: "EdDSA",
|
||||
x: pub,
|
||||
},
|
||||
};
|
||||
return edCache;
|
||||
}
|
||||
|
||||
/** ECDSA-sign a digest with P-256. Returns DER signature. */
|
||||
export function signEcdsaP256Der(
|
||||
digest: Uint8Array,
|
||||
priv: Uint8Array
|
||||
): Uint8Array {
|
||||
return nobleP256.sign(digest, priv).toBytes("der");
|
||||
}
|
||||
|
||||
export function signEcdsaP384Der(
|
||||
digest: Uint8Array,
|
||||
priv: Uint8Array
|
||||
): Uint8Array {
|
||||
return nobleP384.sign(digest, priv).toBytes("der");
|
||||
}
|
||||
|
||||
export function signEcdsaP521Der(
|
||||
digest: Uint8Array,
|
||||
priv: Uint8Array
|
||||
): Uint8Array {
|
||||
return nobleP521.sign(digest, priv).toBytes("der");
|
||||
}
|
||||
Reference in New Issue
Block a user