# ECDSA algorithms (`ES256` / `ES384` / `ES512`) ## Purpose RFC 7518 §3.4 ECDSA signing and verification over NIST P-256, P-384, and P-521. The signing input is hashed with the curve-matched SHA-2 variant, signed via `@suaveplan/crypto-curve`'s curve primitives, and serialised as the fixed-length concatenation `R || S` of the two coordinates. DER-encoded signatures in the JOSE position are rejected with `JWS.SIG_FORMAT_INVALID` — silently parsing them would hide protocol drift between producers, which is the source of multiple JOSE interop CVEs. ## Features - `ES256` → P-256 + SHA-256, 64-byte `R||S`. - `ES384` → P-384 + SHA-384, 96-byte `R||S`. - `ES512` → P-521 + SHA-512, 132-byte `R||S` (R and S are 66 bytes each). - DER-encoded signature in JOSE position → `JWS.SIG_FORMAT_INVALID`. - Key shape: raw uncompressed SEC1 public-key bytes (`0x04 || X || Y`) for verify; raw scalar for sign. ## Usage: Basic Example ```ts import { p256 } from "@suaveplan/crypto-curve"; import { compactSign, compactVerify } from "@suaveplan/jws"; const priv = p256.randomPrivateKey(); const pub = p256.getPublicKey(priv); const token = await compactSign("hello", priv, "ES256"); await compactVerify(token, pub, { algorithms: ["ES256"] }); ``` ## Usage: Advanced Example ```ts import { es256, JwsError } from "@suaveplan/jws"; // Reject DER-encoded signature in JOSE position: const derSig = new Uint8Array([0x30, 0x46, /* ... */]); try { await es256.verify(signingInput, derSig, pub); } catch (err) { if (err instanceof JwsError && err.code === "JWS.SIG_FORMAT_INVALID") { // DER detected by 0x30 lead byte + length mismatch. } } // Wrong-length raw R||S also rejected: try { await es256.verify(signingInput, new Uint8Array(63), pub); } catch (err) { if (err instanceof JwsError && err.code === "JWS.SIG_FORMAT_INVALID") { // Generic length mismatch. } } ``` ## API Reference - `es256: AlgorithmImpl` — verify `(input, sig: 64 bytes, pub)`, sign `(input, priv: 32 bytes)`. - `es384: AlgorithmImpl` — sig 96 bytes, priv 48 bytes. - `es512: AlgorithmImpl` — sig 132 bytes, priv 66 bytes. ## Implementation Notes Public keys MUST be uncompressed SEC1 (`0x04 || X || Y` of the relevant coordinate length). The verifier delegates to `@suaveplan/crypto-curve`'s `p256.verify` / `p384.verify` / `p521.verify`, which use the Noble curves library with constant-time scalar multiplication. Signing uses RFC 6979 deterministic `k` (no per-signature randomness required), which makes ECDSA JWS production deterministic for a given `(privateKey, payload)` pair — this is a feature for reproducible-build test vectors but means consumers should NOT reuse `k` across messages outside this library. The 132-byte length for ES512 surprises producers expecting 128: P-521 coordinates are 66 bytes each, not 64.