Skip to main content

Crate aph_sealed

Crate aph_sealed 

Source
Expand description

RFC 0008 — sealed payloads: carriage without readership. The reference implementation, spec/aph-0.2.md §§1-4 (v0.2, cut 2026-08-29).

RFC 0008 is Accepted onto the v0.2 line and implemented here: the WIRE types live in aph-core (re-exported below) so the envelope can carry them, the wire-version rule (sealed_payload_is_declared) keeps them off any aphVersion-0.1 envelope, and THIS crate holds the cryptography plus the envelope-level operations — seal_into_envelope / unseal_from_envelope — that RFC 0008 §§3-4 specify. v0.1.0 itself is untouched: a v0.1-only verifier still refuses the member at strict parse, correctly.

Two scenarios, one mechanism (RFC 0008 §“The problem”):

  • sealed TO THE RECEIVER: intermediate agents verify the envelope and carry what they cannot read;
  • sealed TO THE SENDER (or any designated third key): the counterparty carries and proves receipt of what it cannot open.

The cryptography is RFC 9180 HPKE single-shot, one pinned suite (X25519-HKDF-SHA256 / HKDF-SHA256 / ChaCha20-Poly1305), through the pure-Rust hpke crate — this repository writes no cryptography. The load-bearing choice is the AAD: a canonical CONTEXT — suite, reader id, reader kid, envelope id — authenticates every seal, so a ciphertext lifted into a different envelope OR relabeled about itself (a different claimed reader, a different claimed suite) fails open even for its rightful key. The audit probe that forced the widening is kept as a test. Errors are this crate’s own type, deliberately NOT APH_E-prefixed: the §11 set is closed, and RFC 0008 §5 assigns codes when a version exists that can declare them.

Structs§

SealedPayload
The wire types are aph-core’s — the envelope carries them — and this crate re-exports them so a sealing caller needs one import path. A payload the envelope authorizes but only SealedReader can read (spec/aph-0.2.md §1, RFC 0008): verification and readership as independent capabilities. Every hop runs §8.3 in full — the signature covers this ciphertext, bodySha256 MAY bind its raw decoded octets — and none but the reader learns the plaintext.
SealedReader
The wire types are aph-core’s — the envelope carries them — and this crate re-exports them so a sealing caller needs one import path. Who may open a sealed payload: a DID and which of its keyAgreement keys (spec/aph-0.2.md §1, RFC 0008 §2). Signing keys are never converted to encryption keys; the reader publishes a distinct keyAgreement entry through the §8.4 surfaces.

Enums§

EnvelopeSealError
Why an envelope-level unseal can fail — three DIFFERENT kinds of failure with three different repairs, kept as distinct as the codes that carry two of them.
SealError
This crate’s own refusals — deliberately NOT APH_E codes (RFC 0008 §5: the §11 set is closed, and names are minted by the version that can declare them, not by a draft).

Constants§

SUITE
The one suite this draft compiles. A wire member exists so a future version CAN move; an unseal of any other value refuses. Closed-set discipline applied to ciphersuites from birth (RFC 0008 §1).

Functions§

seal
Seals plaintext so that ONLY the holder of reader’s private key can open it, bound to the full seal CONTEXT — suite, reader, envelope id — as HPKE additional authenticated data (RFC 0008 §3). Sealing happens BEFORE the envelope is signed, so the signature covers the ciphertext and every hop verifies blind.
seal_into_envelope
Seals plaintext INTO an envelope (RFC 0008 §3): binds the seal to the envelope’s own id and claimed context, then places it on credentialSubject.sealedPayload. Call this BEFORE signing — the signature must cover the ciphertext — and only on an envelope whose aphVersion declares the member; anything else refuses up front rather than minting a wire no conformant verifier admits.
unseal
Opens a seal with the reader’s private key, under the SAME context it was sealed to — with suite and reader taken from the sealed payload’s OWN claims, so a payload relabeled about itself refuses. Any mismatch — key, bytes, envelope, or claimed context — refuses with one indistinguishable SealError::OpenRefused, and RFC 0008 §4 tells a reader-verifier what that refusal means: refuse the ENVELOPE, because an unopenable seal addressed to you is evidence, not an inconvenience.
unseal_from_envelope
Opens the seal an envelope carries, for the reader holding the private key (RFC 0008 §4): run AFTER the envelope itself verifies, and treat a EnvelopeSealError::Refused as a refusal of the ENVELOPE — an unopenable seal addressed to you is evidence, never a shrug. The wire-version rule runs first, so a mis-versioned envelope refuses before any cryptography.