# 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()`. 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.