/** * Test-only DI bindings for `@suaveplan/bitpacker-crypto` primitives. * * Implemented directly against the lower-level `@suaveplan/crypto-core` * (sign/verify) and `@suaveplan/crypto-cipher` (AEAD) packages so this * test surface has NO dependency on `@suaveplan/crypto`. That breaks an * otherwise-circular dep chain: `crypto` may depend on `bitpacker-crypto` * (e.g. for `EnvelopeSchema` in `chain-bundle`) without inducing a cycle * via this test helper. * * Production callers should still wire `verifyAuthoritySignature` from * `@suaveplan/crypto/authority` and `aeadEncrypt` / `aeadDecrypt` from * `@suaveplan/crypto` — those are the canonical implementations. This * file mirrors their shape for test purposes only. */ import * as aesGcm from "@suaveplan/crypto-cipher/aes-256-gcm"; import * as chacha20 from "@suaveplan/crypto-cipher/chacha20-poly1305"; import * as xchacha20 from "@suaveplan/crypto-cipher/xchacha20-poly1305"; import { ed25519 } from "@suaveplan/crypto-core/sign"; import type { AeadAlgorithm, AeadOps, AeadOpsOptions, AuthorityVerifier, DomainTag, } from "@suaveplan/types"; const ED25519_ALGORITHM_ID = 0; /** * Inline copy of `buildSigningInput` from `@suaveplan/crypto/domain-tags`. * Reproduced here to keep this test helper free of any `@suaveplan/crypto` * runtime dep. The byte layout is identical: `tag-bytes ‖ 0x00 ‖ payload`. */ function buildSigningInput(domain: DomainTag, payload: Uint8Array): Uint8Array { const tag = new TextEncoder().encode(domain); const out = new Uint8Array(tag.length + 1 + payload.length); out.set(tag, 0); out[tag.length] = 0; out.set(payload, tag.length + 1); return out; } /** * Test verifier — mirrors `verifyAuthoritySignature` from * `@suaveplan/crypto/authority` byte-for-byte. Only ed25519 is supported * (matches the canonical impl); other algorithms throw to preserve the * "structural error → swallow as false" convention used by primitives. */ export const cryptoVerifier: AuthorityVerifier = ( domain, publicSlot, payload, signature ) => { if (publicSlot.algorithm !== ED25519_ALGORITHM_ID) { throw new Error( `cryptoVerifier: unsupported algorithm ${publicSlot.algorithm}` ); } const message = buildSigningInput(domain, payload); return ed25519.verify(publicSlot.publicKey, signature, message); }; function encrypt( algorithm: AeadAlgorithm, key: Uint8Array, plaintext: Uint8Array, options: AeadOpsOptions ): Uint8Array { switch (algorithm) { case "AES-256-GCM": return aesGcm.encrypt(key, options.nonce, plaintext, options.aad); case "ChaCha20-Poly1305": return chacha20.encrypt(key, options.nonce, plaintext, options.aad); case "XChaCha20-Poly1305": return xchacha20.encrypt( key, options.nonce, plaintext, options.aad ); } } function decrypt( algorithm: AeadAlgorithm, key: Uint8Array, ciphertext: Uint8Array, options: AeadOpsOptions ): Uint8Array { switch (algorithm) { case "AES-256-GCM": return aesGcm.decrypt(key, options.nonce, ciphertext, options.aad); case "ChaCha20-Poly1305": return chacha20.decrypt( key, options.nonce, ciphertext, options.aad ); case "XChaCha20-Poly1305": return xchacha20.decrypt( key, options.nonce, ciphertext, options.aad ); } } /** * Test AEAD bundle — dispatches to the per-algorithm functions exposed by * `@suaveplan/crypto-cipher`. Behaviorally identical to the canonical * `{ encrypt: aeadEncrypt, decrypt: aeadDecrypt }` shape from * `@suaveplan/crypto`. */ export const cryptoAead: AeadOps = { encrypt, decrypt };