Files
suaveplan/openspec/archive/2026-05-10-crypto-curve-p384-p521/proposal.md
2026-05-27 23:29:53 +01:00

4.0 KiB

@suaveplan/crypto-curve — Add P-384 (secp384r1) and P-521 (secp521r1) primitives

Package: @suaveplan/crypto-curve (extension) Status: PROPOSED Type: capability-extension Date: 2026-05-10 Tier: universal

Why

@suaveplan/cbor ships RFC 9052 COSE_Sign1 verify but the ES384 / ES512 algorithm branches throw CborError(UNEXPECTED_TYPE) with the message "not implemented in @suaveplan/crypto-curve yet" because the curve primitives were not previously exposed. Downstream consumers (WebAuthn attestation, federation tokens, FIDO2 device certificates) need both algorithms to interoperate with mainstream issuers — Apple's WebAuthn attestation, in particular, signs with ES384 in some configurations.

@noble/curves@1.9.7 (already installed across the workspace) exposes p384 and p521 with the same surface contract as p256. The work is purely additive — mirroring the existing p256 per-primitive layout for two more curves and threading them through to the COSE verifier.

What changes

New primitives in @suaveplan/crypto-curve

P-384

  • ADDED: primitives/p384/p384.ts — re-exports the select layer.
  • ADDED: primitives/p384/p384.select/p384.select.ts — selector pinned to the noble implementation (generated forwarder, mirroring p256).
  • ADDED: primitives/p384/impl/p384.noble/p384.noble.ts — noble-backed getPublicKey, sign, verify, randomPrivateKey.
  • ADDED: primitives/p384/impl/p384.native/p384.native.ts — PrimitiveNotImplemented stubs matching the p256 native pattern.

P-521

  • ADDED: primitives/p521/p521.ts and full mirror of the p384 layout (p521.select, impl/p521.noble, impl/p521.native).

Barrel + subpath exports

  • ADDED: export * as p384 from "./primitives/p384/p384.js" to root barrel.
  • ADDED: export * as p521 from "./primitives/p521/p521.js" to root barrel.
  • ADDED: ./p384 and ./p521 subpath exports in package.json.
  • ADDED: corresponding entries in vite.config.ts external list (so consumers can tree-shake to the individual primitive).

COSE wiring in @suaveplan/cbor

  • MODIFIED: verifyCoseSign1 switch — ES384 and ES512 branches call p384.verify(sig, sha384(tbs), pk) and p521.verify(sig, sha512(tbs), pk) respectively (replacing the UNEXPECTED_TYPE placeholder throws).
  • MODIFIED: ec2ToCompressedPoint — generalised to accept coordinate length (32 / 48 / 66) so the same helper compresses P-256 / P-384 / P-521.

Dependencies

No new dependencies. @noble/curves@1.9.7 is the catalog-pinned version that ships both p384 and p521 subpath exports.

Out of scope

  • WebCrypto-native impls — the existing p256.native stub pattern (PrimitiveNotImplemented) is preserved for both new curves; an opt-in WebCrypto path lands in a separate change when needed.
  • COSE Sign1 / Mac0 / Encrypt0 surfaces beyond the alg switch — already wired generically and need no changes.

Source mapping

Requirement Source Notes
P-384 ECDSA FIPS 186-4 §6 + NIST SP 800-186 @noble/curves/p384
P-521 ECDSA FIPS 186-4 §6 + NIST SP 800-186 @noble/curves/p521
COSE ES384 / ES512 RFC 9053 §2.1 Already mapped in alg-map.ts
Per-primitive selector layout Existing crypto-curve convention mirrors p256

Verification approach

  • packages/crypto/crypto-curve/src/primitives/p384/p384.test.ts — noble
    • native impl: sign / verify round-trip, signature length, tampered-sig rejection, native PrimitiveNotImplemented throw.
  • packages/crypto/crypto-curve/src/primitives/p521/p521.test.ts — same.
  • packages/vault/cbor/src/cose/sign1.test.ts — extend to cover ES384 and ES512 round-trip via the new primitives.

Migration impact

Additive. No existing consumers break — the deferred branches in cbor were already throwing.

Impact

Affected dependents

@suaveplan/cbor — ES384 / ES512 paths become operational; previously threw UNEXPECTED_TYPE.

For all other packages: no behavioural change. The new primitives are opt-in subpath imports.