Expand description
Sanctuary audit primitives (vault v2, first slice) — the crypto under the decoy-mirroring design.
Three isolated, independently-tested primitives. Nothing here touches the vault yet; the vault
wiring is a later slice. See docs/plans/adr-sanctuary-vault-v2-cbor-decoy-mirroring.md.
- Blind write-only audit channel — an X25519 sealed box (anonymous ECIES): a decoy
session, holding only the audit public key, can
seal_toa record so that only the holder of the audit secret (the real lane) canopen_sealedit. The writer cannot read back what it wrote, and cannot forge or tamper without detection. This is how a coercer’s actions get logged into a channel they can append to but never read. - One-way key wrapping —
wrap_key/unwrap_key: the real lane key wraps the decoy lane key (and the audit secret), so a real session can reach down into the decoy to curate it, but the decoy can never reach up. - Hash-chained content addressing —
chain_hash: an append-only, tamper-evident DAG link (BLAKE3 overparent ‖ payload); rewriting or dropping a record breaks every link after it.
Symmetric AEAD is XChaCha20-Poly1305 (24-byte nonce). Sealed-box key/nonce are derived from the
ECDH shared secret via BLAKE3 derive_key (domain-separated); each seal uses a fresh ephemeral
key, so sealing is non-deterministic (no plaintext-equality leak) and nonce reuse is impossible.
Structs§
- Audit
Keypair - An audit keypair. The public key is exposed to any session (including the decoy) so it can append sealed records; the secret lives only in the real lane (wrapped under the real key) and is the sole means of reading them.
Enums§
Constants§
- GENESIS_
PARENT - Genesis parent for a fresh hash chain / DAG branch.
Functions§
- chain_
hash - One append-only DAG link:
BLAKE3(parent ‖ payload). Rewriting or reordering any record changes its hash and breaks the parent link of everything after it (tamper-evidence). Start a branch fromGENESIS_PARENT. - open_
sealed - Open a sealed box produced by
seal_to. Requires the recipient secret; the public key alone cannot open it (that is the whole point — the decoy session writes but cannot read). - seal_to
- Seal
plaintextso that only the holder of the secret matchingrecipient_publiccan open it. Anonymous: the sealer needs no identity, only the recipient’s public key. Output layout:ephemeral_public(32) ‖ ciphertext ‖ tag(16). Non-deterministic (fresh ephemeral key per call). - unwrap_
key - Unwrap a blob produced by
wrap_key. Fails on the wrong key, wrong AAD, or tampering. - wrap_
key - Wrap
key_materialunderwrapping_key(AEAD). Output:nonce(24) ‖ ciphertext ‖ tag(16). Used for the one-way hierarchy: the real lane key wraps the decoy key + the audit secret.