Files
2026-05-27 23:29:53 +01:00

5.0 KiB

Capability Spec Delta: crypto-curve (crypto-curve-p384-p521)

Package: @suaveplan/crypto-curve Status: PROPOSED — additive delta; folded into canonical spec on archive.

ADDED Requirements

Requirement: NIST P-384 (secp384r1) ECDSA primitive

The package SHALL export a p384 namespace from the root barrel and a matching ./p384 subpath export, providing getPublicKey, sign, verify, and randomPrivateKey. Private keys SHALL be 48 bytes, compressed public keys SHALL be 49 bytes, and compact ECDSA signatures SHALL be 96 bytes. Callers SHALL pre-hash messages; the module operates on digests. The selector SHALL forward to the noble-backed implementation by default; the native implementation SHALL throw PrimitiveNotImplemented("p384") until a WebCrypto bridge lands.

Scenario: P-384 sign + verify round-trip

  • GIVEN a freshly-generated P-384 private key and 48-byte digest
  • WHEN verify(sign(digest, sk), digest, getPublicKey(sk)) is computed
  • THEN the result is true

Scenario: P-384 verify rejects tampered signature

  • WHEN a single byte of a valid P-384 signature is XORed with 0xff
  • THEN verify returns false

Scenario: P-384 verify returns false on malformed inputs

  • WHEN verify is invoked with non-Uint8Array inputs or empty buffers
  • THEN the call returns false (does not throw)

Verified by: packages/crypto/crypto-curve/src/primitives/p384/p384.test.ts::"sign + verify round-trips" AND …::"verify returns false for tampered signature (no throw)" AND …::"verify returns false for bogus input (caught)"


Requirement: NIST P-521 (secp521r1) ECDSA primitive

The package SHALL export a p521 namespace from the root barrel and a matching ./p521 subpath export, providing getPublicKey, sign, verify, and randomPrivateKey. Private keys SHALL be 66 bytes (521-bit scalar left-padded to a byte boundary), compressed public keys SHALL be 67 bytes, and compact ECDSA signatures SHALL be 132 bytes. Callers SHALL pre-hash messages; the module operates on digests. The selector SHALL forward to the noble-backed implementation by default; the native implementation SHALL throw PrimitiveNotImplemented("p521").

Scenario: P-521 sign + verify round-trip

  • GIVEN a freshly-generated P-521 private key and 64-byte digest
  • WHEN verify(sign(digest, sk), digest, getPublicKey(sk)) is computed
  • THEN the result is true and the signature is 132 bytes long

Scenario: P-521 verify rejects tampered signature

  • WHEN a single byte of a valid P-521 signature is XORed with 0xff
  • THEN verify returns false

Scenario: P-521 verify returns false on malformed inputs

  • WHEN verify is invoked with non-Uint8Array inputs or empty buffers
  • THEN the call returns false (does not throw)

Verified by: packages/crypto/crypto-curve/src/primitives/p521/p521.test.ts::"sign + verify round-trips" AND …::"verify returns false for tampered signature (no throw)" AND …::"verify returns false for bogus input (caught)"


Requirement: Native stubs preserve layout

The package SHALL ship native-backend placeholder modules (primitives/p384/impl/p384.native/, primitives/p521/impl/p521.native/) whose every exported function throws PrimitiveNotImplemented(<curve>). The placeholders SHALL match the existing p256.native pattern so the selector switch is mechanical when a real native implementation lands.

Scenario: P-384 native stub throws on every operation

  • WHEN any of getPublicKey, sign, verify, or randomPrivateKey is invoked on the native module
  • THEN the call throws PrimitiveNotImplemented("p384")

Scenario: P-521 native stub throws on every operation

  • WHEN the same operations are invoked on the P-521 native module
  • THEN the call throws PrimitiveNotImplemented("p521")

Verified by: …/p384/p384.test.ts::"every operation throws PrimitiveNotImplemented" AND …/p521/p521.test.ts::"every operation throws PrimitiveNotImplemented"


Requirement: Subpath exports for P-384 and P-521

The package SHALL declare ./p384 and ./p521 entries in package.json exports so consumers tree-shake to the individual primitive. The vite.config.ts build entry list SHALL include both new primitives so the generated dist/ tree carries the expected .d.ts and .js files at the published path.

Scenario: P-384 subpath resolves

  • WHEN a consumer imports @suaveplan/crypto-curve/p384
  • THEN the import resolves with no Module not found error and exposes getPublicKey, sign, verify, randomPrivateKey

Scenario: P-521 subpath resolves

  • WHEN a consumer imports @suaveplan/crypto-curve/p521
  • THEN the import resolves with no Module not found error and exposes the same four symbols

Verified by: TypeScript compile of consumers (e.g. packages/vault/cbor/src/cose/sign1.ts) that import these subpaths.