4.6 KiB
Capability Spec Delta: crypto-curve (crypto-completion)
Package: @suaveplan/crypto-curve
Status: PROPOSED
Every requirement below carries a
**Verified by:**line at authoring time.
ADDED Requirements
Requirement: secp256k1 ECDSA signing with recovery byte
The secp256k1 primitive SHALL expose signWithRecovery(digest, privateKey) returning a 65-byte signature (64-byte compact signature followed by a single recovery byte). The recovery byte SHALL be the byte returned by secp256k1.sign(digest, privateKey).recovery from the @noble/curves library; valid values are 0 or 1.
Scenario: produces 65-byte signature
- WHEN
signWithRecovery(digest, privateKey)is called for a valid digest and private key - THEN the result is a
Uint8Arrayof length 65 ANDresult[64]is0or1
Scenario: signWithRecovery output verifies via standard verify
- WHEN the first 64 bytes of
signWithRecovery(digest, privateKey)are passed to the existingverify(digest, sig64, publicKey) - THEN the signature verifies successfully
Verified by: packages/crypto/crypto-curve/src/primitives/secp256k1/secp256k1.test.ts::"signWithRecovery returns 65-byte signature with recovery byte 0 or 1" AND packages/crypto/crypto-curve/src/primitives/secp256k1/secp256k1.test.ts::"signWithRecovery output verifies via standard verify on first 64 bytes"
Requirement: secp256k1 public-key recovery
The secp256k1 primitive SHALL expose recoverPublicKey(signatureWithRecovery, digest) returning the 33-byte compressed public key recovered from a 65-byte signature-with-recovery and the original digest. Inputs may be supplied as Uint8Array or hex strings.
Scenario: recovers the signing public key
- WHEN
signWithRecovery(digest, privateKey)producessigandrecoverPublicKey(sig, digest)is called - THEN the recovered public key equals
getPublicKey(privateKey)byte-for-byte
Scenario: missing recovery byte rejected
- WHEN a 64-byte signature (without recovery byte) is passed to
recoverPublicKey - THEN it throws
SuaveplanErrorwith codeCRYPTO_CURVE.RECOVERY_BYTE_MISSINGandcontext.signatureLength === 64
Scenario: invalid recovery byte rejected
- WHEN a 65-byte signature is passed but
signatureWithRecovery[64]is2,3, or any value > 3 - THEN it throws
SuaveplanErrorwith codeCRYPTO_CURVE.RECOVERY_BYTE_INVALIDandcontext.recoveryBytereflects the offending value
Verified by: packages/crypto/crypto-curve/src/primitives/secp256k1/secp256k1.test.ts::"recoverPublicKey returns the same key as getPublicKey for the signer" AND packages/crypto/crypto-curve/src/primitives/secp256k1/secp256k1.spec-conformance.test.ts::"throws RECOVERY_BYTE_MISSING for 64-byte signature" AND packages/crypto/crypto-curve/src/primitives/secp256k1/secp256k1.spec-conformance.test.ts::"throws RECOVERY_BYTE_INVALID when recovery byte is out of range"
Requirement: secp256k1 verify-with-recovery
The secp256k1 primitive SHALL expose verifyWithRecovery(signatureWithRecovery, digest, expectedPublicKey) returning true only when the public key recovered from the signature equals expectedPublicKey (compared in constant time on the byte buffer). When the recovered key differs, the function SHALL return false (it does NOT throw on mismatch — mismatch is a routine path).
Scenario: returns true on matching key
- WHEN
verifyWithRecovery(sig, digest, expectedPublicKey)is called with the public key that producedsig - THEN the result is
true
Scenario: returns false on non-matching key
- WHEN
verifyWithRecovery(sig, digest, otherPublicKey)is called with a public key that did not producesig - THEN the result is
false(no exception is thrown)
Scenario: comparison is constant-time
- WHEN the recovered public key and
expectedPublicKeydiffer in their first byte vs differ only in their last byte - THEN the timing distribution observed across many invocations does not reveal the position of the difference (verified via a statistical timing test)
Verified by: packages/crypto/crypto-curve/src/primitives/secp256k1/secp256k1.test.ts::"verifyWithRecovery returns true on matching public key" AND packages/crypto/crypto-curve/src/primitives/secp256k1/secp256k1.test.ts::"verifyWithRecovery returns false on non-matching public key" AND packages/crypto/crypto-curve/src/primitives/secp256k1/secp256k1.spec-conformance.test.ts::"verifyWithRecovery comparison is constant-time"