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

7.1 KiB

Capability Spec Delta: bitpacker-crypto (extract-shared-crypto-interfaces)

Package: @suaveplan/bitpacker-crypto Status: PROPOSED — gated on user sign-off of the API shape

Modifies the four DI-using primitives shipped by the archived bitpacker-crypto-rewrite (2026-05-02) to accept verifier and aead via dependency injection. No wire-format change.

MODIFIED Requirements

Requirement: SignedBitSchema integrates with Authority + DomainTag (verifier injected)

The defineSignedBitSchema<T>(bodyCodec, options) function SHALL accept a required verifier: AuthorityVerifier field on its options bundle. The primitive SHALL call verifier(domain, parentPublicSlot, body, signature) instead of importing verifyAuthoritySignature directly. All other behavior (verify-then-decode order, unpackSelf trust-anchor flow, cross-domain replay rejection, CRYPTO_VERIFY_FAILED on tamper) remains identical to the archived bitpacker-crypto-rewrite spec.

The primitive SHALL import Authority, AuthorityVerifier, DomainTag, KeySlotPublic, and SlotId types from @suaveplan/types instead of from @suaveplan/crypto/authority and @suaveplan/crypto/domain-tags.

Scenario: missing verifier rejects at construction time

  • WHEN defineSignedBitSchema(codec, { domain: D }) is called WITHOUT a verifier field
  • THEN the Zod options-validation throws AND no schema instance is returned

Scenario: DI'd verifier produces same boolean answer as direct import

  • WHEN defineSignedBitSchema(codec, { domain, verifier: verifyAuthoritySignature }) packs a payload and the result is unpack-ed
  • THEN the round-trip matches the original AND the verify path emits the same OTel span as the archived rewrite

Scenario: bitpacker-crypto's compiled dist/ contains zero references to @suaveplan/crypto

  • WHEN bunx turbo build --filter=@suaveplan/bitpacker-crypto runs
  • THEN grep -r "@suaveplan/crypto" packages/bitpacker/bitpacker-crypto/dist/ returns zero matches in *.js and *.d.ts files

Verified by: packages/bitpacker/bitpacker-crypto/src/signed/signed-bitschema.test.ts::"missing verifier rejects at construction time" AND packages/bitpacker/bitpacker-crypto/src/signed/signed-bitschema.test.ts::"pack signs via Authority under the configured domain" AND packages/bitpacker/bitpacker-crypto/src/signed/signed-bitschema.spec-conformance.test.ts::"signed-pack emits OTel span with domain attribute"


Requirement: MultiSignedBitSchema accepts injected verifier

The defineMultiSignedBitSchema<T>(bodyCodec, options) function SHALL accept a required verifier: AuthorityVerifier field on its options bundle. The primitive SHALL call verifier(...) for each signature verification instead of importing verifyAuthoritySignature. Wire format, count enforcement, firstBadSignerIndex reporting, CRYPTO_MULTISIGN_COUNT_MISMATCH semantics — all unchanged.

Scenario: missing verifier rejects at construction time

  • WHEN defineMultiSignedBitSchema(codec, { domain: D, signerCount: 2 }) is called WITHOUT a verifier field
  • THEN the Zod options-validation throws

Scenario: DI'd verifier preserves firstBadSignerIndex semantics

  • WHEN a 3-signer payload's second signature is flipped and verify(bytes, [pubA, pubB, pubC]) is called with the DI'd verifyAuthoritySignature
  • THEN the result is { valid: false, firstBadSignerIndex: 1 } identical to the archived rewrite

Verified by: packages/bitpacker/bitpacker-crypto/src/multisigned/multisigned-bitschema.test.ts::"missing verifier rejects at construction time" AND packages/bitpacker/bitpacker-crypto/src/multisigned/multisigned-bitschema.test.ts::"verify pinpoints which signer failed"


Requirement: HashChainSchema accepts injected verifier

The defineHashChainSchema<TPayload>(options) function SHALL accept a required verifier: AuthorityVerifier field on its options bundle. verifyEntry and verifyChain SHALL use the DI'd verifier. All chain-walk, prev-hash linkage, tamper detection (firstBadIndex), pluggable hash registry behavior — all unchanged.

Scenario: missing verifier rejects at construction time

  • WHEN defineHashChainSchema({ domain: D, payloadCodec: c }) is called WITHOUT a verifier field
  • THEN the Zod options-validation throws

Scenario: chain verification detects tamper at exact index with DI'd verifier

  • WHEN a 5-entry chain is built using the DI'd verifier, then entry-2's payload is mutated and verifyChain is called
  • THEN the result is { valid: false, firstBadIndex: 2 } identical to the archived rewrite

Verified by: packages/bitpacker/bitpacker-crypto/src/hashchain/hashchain-schema.test.ts::"missing verifier rejects at construction time" AND packages/bitpacker/bitpacker-crypto/src/hashchain/hashchain-schema.test.ts::"chain verification detects tamper at exact index"


Requirement: EncryptedBitSchema accepts injected AEAD operations

The defineEncryptedBitSchema<T>(bodyCodec, options) function SHALL accept a required aead: AeadOps field on its options bundle. The primitive SHALL call aead.encrypt(...) and aead.decrypt(...) instead of importing aeadEncrypt / aeadDecrypt directly.

The constants AEAD_KEY_LENGTH = 32, AEAD_NONCE_LENGTH = 12, and AEAD_XCHACHA_NONCE_LENGTH = 24 SHALL be declared locally in encrypted-bitschema.ts (not imported from @suaveplan/crypto). Duplication is acceptable because these values are part of the bit-packer-crypto wire format.

The primitive SHALL import AeadAlgorithm, AeadOps, Rng from @suaveplan/types. Wire format [version u8][algTag u8][nonceLen u8] [nonce][ciphertext + tag] and the closed ENCRYPTED_ALG_TAGS enum remain unchanged.

Scenario: missing aead rejects at construction time

  • WHEN defineEncryptedBitSchema(codec, { key }) is called WITHOUT an aead field
  • THEN the Zod options-validation throws

Scenario: DI'd AEAD bundle preserves round-trip semantics

  • WHEN defineEncryptedBitSchema(codec, { key, aead: { encrypt: aeadEncrypt, decrypt: aeadDecrypt } }) packs and unpacks a payload
  • THEN the round-trip matches the original AND tampered ciphertext rejects with CRYPTO_DECRYPT_FAILED identical to the archived rewrite

Scenario: bitpacker-crypto's package.json lists @suaveplan/crypto only as devDependency

  • WHEN cat packages/bitpacker/bitpacker-crypto/package.json | jq -r '.dependencies | keys[]' runs
  • THEN the output does NOT include @suaveplan/crypto
  • AND cat packages/bitpacker/bitpacker-crypto/package.json | jq -r '.devDependencies | keys[]' DOES include @suaveplan/crypto

Verified by: packages/bitpacker/bitpacker-crypto/src/encrypted/encrypted-bitschema.test.ts::"missing aead rejects at construction time" AND packages/bitpacker/bitpacker-crypto/src/encrypted/encrypted-bitschema.test.ts::"round-trips through XChaCha20-Poly1305" AND packages/bitpacker/bitpacker-crypto/src/encrypted/encrypted-bitschema.test.ts::"tampered ciphertext rejects with CRYPTO_DECRYPT_FAILED"