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

130 lines
5.0 KiB
Markdown

<!-- markdownlint-disable MD024 -->
# 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.
<!-- markdownlint-enable MD024 -->