Skip to main content

Module verify

Module verify 

Source
Expand description

The browser content-VERIFICATION contract — how a blind client turns opaque gateway bytes into verified plaintext, fail-closed, over INJECTED crypto primitives.

§Trust model

On the blind (rpc/gateway) tier a client fetches opaque ciphertext + an inclusion proof from an UNTRUSTED public gateway. The gateway can lie about anything except what the chain anchors, so the client MUST verify every byte against the URN’s PINNED root (obtained from the chain, NEVER from the gateway) before trusting it. The node tier does NOT use this contract — a loopback node decrypts + verifies server-side and returns plaintext under a loopback trust boundary.

§The normative rules (all enforced here, fail-closed)

  1. Rootless-URN rejection. A rootless URN cannot be verified on the blind tier (there is no trusted root) → ResolveError::RootRequired. Use require_blind_root.
  2. Leaf binding. leaf == SHA-256(ciphertext) — the served ciphertext MUST be the proof’s declared leaf. (This crate owns this SHA-256 check; see resource_leaf.)
  3. Path fold. The proof’s merkle path MUST fold consistently to proof.root — enforced by the injected ContentCrypto::decode_and_fold (returns None on any inconsistency).
  4. Root anchoring. proof.root == trusted_root — the folded root MUST equal the chain-anchored root pinned by the URN. A decoy / wrong-store / tampered response can never chain to the real root.
  5. Gate-then-decrypt. Decryption happens ONLY after 1–4 pass; the AEAD tag is the final gate.
  6. u64-bounded chunk split. The gateway-supplied chunk_lens is NOT covered by the proof, so it is UNTRUSTED: it is accumulated and bounded in u64 and sliced against the remaining buffer so a crafted length can never wrap usize on wasm32 and slice out of bounds (→ panic=abort, a wallet crash). Any inconsistency fails closed as ResolveError::DecryptFailed.

The merkle-fold and AES primitives are supplied by the caller (digstore_core) via ContentCrypto — this crate reimplements NO merkle or AES crypto, so it can never skew from the canonical read-crypto.

Structs§

FoldedProof
A decoded, folded inclusion proof: its declared leaf and the root its merkle path folds to.

Traits§

ContentCrypto
The crypto primitives this contract INJECTS from digstore_core (never reimplemented here).

Functions§

chunk_ranges
Rule 6: split concatenated chunk ciphertexts into byte ranges under a u64-bounded plan.
decrypt
Rule 5 (confidentiality half): decrypt the verified ciphertext. Splits by chunk_ranges and AES-opens each chunk in order via the injected ContentCrypto::decrypt_chunk. A tag failure on any chunk fails closed with ResolveError::DecryptFailed.
require_blind_root
Rule 1: obtain the trusted root for a BLIND-tier verify, rejecting a rootless URN.
resource_leaf
The content leaf: SHA-256(ciphertext) (rule 2). This is the only crypto this leaf crate performs directly; it matches digstore_core::resource_leaf.
verify_and_decrypt
The full blind-tier pipeline: gate-then-decrypt (rules 1–6). Rejects a rootless URN, verifies inclusion against trusted_root, then decrypts — decryption is reached ONLY after verification passes.
verify_inclusion
Rules 2–4: the integrity gate. The served ciphertext must be the proof’s leaf, the path must fold to a root (via the injected decoder), and that root must equal trusted_root. Any failure is a hard fail-closed ResolveError::VerifyFailed.