Expand description
§sealed-channel
sealed-channel is a small, transport-agnostic authenticated record channel.
It derives directional ChaCha20-Poly1305 record keys from:
- a high-entropy pre-shared secret (PSK),
- an externally supplied ephemeral Diffie-Hellman shared secret,
- the exact wire bytes of the client and server handshake messages.
The crate is pure compute. It performs no networking, no I/O, and no random number generation. Callers own the transport, the ephemeral key exchange, and all randomness.
§What It Provides
- HKDF-SHA256 key schedule with transcript binding.
- Directional client-to-server and server-to-client AEAD keys.
- Strict in-order record opening with replay and reordering rejection.
- Deterministic nonce construction that fails closed on counter exhaustion.
#![no_std]withalloc.#![forbid(unsafe_code)].
§Usage
use sealed_channel::schedule;
fn main() -> Result<(), sealed_channel::Error> {
let psk = b"a-high-entropy-pre-shared-secret";
let dh_shared_secret = [7_u8; 32];
let client_hello = b"client hello wire bytes";
let server_challenge = b"server challenge wire bytes";
let client_keys = schedule::derive(
psk,
&dh_shared_secret,
client_hello,
server_challenge,
)?;
let server_keys = schedule::derive(
psk,
&dh_shared_secret,
client_hello,
server_challenge,
)?;
let (mut client_seal, _client_open) = client_keys.into_client();
let (_server_seal, mut server_open) = server_keys.into_server();
let frame = client_seal.seal(b"hello")?;
let plaintext = server_open.open(&frame)?;
assert_eq!(plaintext.as_slice(), b"hello");
Ok(())
}In a real protocol, each side should use fresh ephemeral keys per connection, derive the same 32-byte DH shared secret, and pass the exact handshake bytes seen on the wire.
§Construction
The key schedule computes:
transcript = SHA256(domain || len(client_hello) || client_hello
|| len(server_challenge) || server_challenge)
ikm = dh_shared_secret || psk
hk = HKDF-SHA256(salt = transcript, ikm)Four domain-separated labels expand to:
- client-to-server AEAD key,
- server-to-client AEAD key,
- client-to-server nonce prefix,
- server-to-client nonce prefix.
Records are encoded as:
[0xE0] || seq:u64be || ChaCha20-Poly1305(ciphertext || tag)The clear 0xE0 || seq header is authenticated as AEAD additional data. The
nonce is:
nonce_prefix:4 || seq:u64beThe sequence number is strict and monotonic per direction. Replays, reordering, malformed frames, and authentication failures return errors without advancing opener state.
§Security Boundary
Authentication strength is the entropy of the PSK.
An active relay can run its own Diffie-Hellman exchange with each peer. The PSK is therefore the secret that authenticates the channel against that active attacker. Use a high-entropy PSK, at least 128 bits and preferably 256 bits.
Do not use a PIN, passphrase, or other low-entropy human secret as the PSK. Authenticating weak secrets against active attackers requires a PAKE such as SPAKE2+ or OPAQUE. This crate is not a PAKE.
Forward secrecy depends on the caller using fresh ephemeral DH keys and discarding the ephemeral private keys after the handshake.
§Non-Goals
- No transport security or metadata confidentiality.
- No TLS replacement.
- No PAKE.
- No randomness.
- No handshake format.
- No network protocol.
§Status
This is a pre-1.0 crate and has not had an external security audit. Treat the API and wire format as subject to change until the crate reaches a stable release.
§License
Licensed under either of Apache-2.0 or MIT at your option.
§Construction
sealed-channel is a transport-agnostic, forward-secret authenticated
record channel. It performs pure compute only: it knows nothing about
sockets, WebSockets, JSON, browsers, tokens, or any transport, and it
performs no RNG and no I/O. The caller supplies all randomness, the
ephemeral keys, and the Diffie-Hellman shared secret.
Keys are derived with HKDF-SHA256 from the pre-shared secret (PSK), the
externally-supplied ephemeral DH shared secret, and a transcript hash that
binds the exact bytes of both handshake messages. Records are sealed with
ChaCha20-Poly1305, with the cleartext magic || seq header used as the
AEAD additional authenticated data and a per-direction nonce prefix
concatenated with the big-endian sequence number used as the nonce.
Forward secrecy comes from the ephemeral DH shared secret supplied by the caller: once the ephemeral private keys are discarded, past traffic cannot be decrypted even if the PSK is later compromised.
§CRITICAL SECURITY INVARIANT
Authentication strength equals the entropy of the PSK. An active man-in-the-middle (e.g. a relay) can perform its own DH with each side and therefore knows both DH shared secrets; the ONLY thing keeping it out is the PSK, which it does not know. The PSK MUST be high-entropy (at least 128 bits, 256 recommended). NEVER pass a low-entropy secret such as a PIN or password as the PSK — authenticating a weak secret against an active MITM requires a PAKE (e.g. SPAKE2+ / OPAQUE), which this crate is NOT.
Re-exports§
pub use error::Error;pub use record::RecordOpener;pub use record::RecordSealer;pub use schedule::SessionKeys;pub use transcript::transcript_hash;
Modules§
- error
- Error type for the sealed channel.
- record
- Authenticated record framing.
- schedule
- Key schedule: derives directional AEAD keys and nonce prefixes from the pre-shared secret, the ephemeral DH shared secret, and the handshake transcript using HKDF-SHA256.
- transcript
- Handshake transcript hashing.