commonware-cryptography 2026.10.0

Generate keys, sign arbitrary messages, and deterministically verify signatures.
Documentation
//! Simple Authenticated Key Exchange (SAKE).
//!
//! SAKE establishes a shared secret [Transcript](crate::transcript::Transcript) from signed
//! ephemeral X25519 keys. It uses BLAKE3 and identity signatures from any [Signer](crate::Signer).
//!
//! _This construction is unrelated to [EAP-SAKE] or the [symmetric-key SAKE] protocol._
//!
//! # Protocol
//!
//! Each peer supplies a [Context] with an application namespace, its signer, the expected peer
//! identity, a local timestamp, an accepted peer timestamp range, and a [Version]. Timestamps
//! are in milliseconds. Let `D` and `L` be the public identities, `t_D` and `t_L` their
//! timestamps, and `X` and `Y` their fresh ephemeral public keys.
//!
//! ```text
//! Dialer                                       Listener
//!   |-- Syn(t_D, X, sig_D) ----------------------->|
//!   |<-- SynAck(t_L, Y, sig_L, confirmation_l2d) --|
//!   |-- Ack(confirmation_d2l) -------------------->|
//! ```
//!
//! 1. [dial_start] generates `X` and signs the initial transcript to produce [Syn].
//! 2. [listen_start] checks `t_D` against the accepted range and verifies `sig_D` before
//!    generating `Y` and signing the extended transcript. It rejects a non-contributory X25519
//!    exchange, commits the shared secret, and returns [SynAck] with its key confirmation.
//! 3. [dial_end] checks `t_L`, verifies `sig_L`, and performs the same exchange. It rejects a
//!    non-contributory result or incorrect listener confirmation, then returns [Ack] and the
//!    secret transcript. The dialer must send [Ack] before application data.
//! 4. [listen_end] verifies the dialer's confirmation before returning the same secret
//!    transcript. The listener may then accept application data.
//!
//! # Transcript
//!
//! The transcript commits the application namespace as one packet, then forks it with the
//! version's protocol namespace. V1 then commits a single-byte mode packet containing `1`. V0
//! omits it. Each field below is committed as a separate encoded packet, in order. Signatures
//! authenticate the transcript at the indicated point and are not themselves committed.
//!
//! | Point | V0 fields | V1 fields |
//! |-------|-----------|-----------|
//! | Before `sig_D` | `t_D, L, X` | `t_D, L, D, X` |
//! | Before `sig_L` | `t_D, L, X, D, t_L, Y` | `t_D, L, D, X, t_L, Y` |
//! | Before confirmation | Append the X25519 shared secret | Append the X25519 shared secret |
//!
//! From the final transcript `T`, the confirmations are
//! `T.fork(b"confirmation_l2d").summarize()` and `T.fork(b"confirmation_d2l").summarize()`.
//! Separate labels bind each confirmation to its direction. Successful completion returns `T`
//! for a record protocol to derive its traffic keys.
//!
//! # Versions
//!
//! Both peers must use the same [Version]. Versions have identical message encodings but
//! different signatures and secret transcripts. A mismatch fails signature verification.
//!
//! - [Version::V0] uses `_COMMONWARE_CRYPTOGRAPHY_HANDSHAKE` and
//!   [transcript::Version::V0](crate::transcript::Version::V0). Its fixed packet schema makes
//!   this framing unambiguous. It commits `D` after `sig_D`, so authentication requires a
//!   signature scheme with conservative exclusive ownership: a signature must not also verify
//!   under a substituted public key. Otherwise a dialer can complete the exchange under such
//!   a key if the listener admits it.
//! - [Version::V1] uses `_COMMONWARE_CRYPTOGRAPHY_SAKE` and
//!   [transcript::Version::V1](crate::transcript::Version::V1). Both identities precede every
//!   signature, binding each signer to its declared identity.
//!
//! # Security
//!
//! The listener checks the signed [Syn] before responding. A valid [Syn] can be replayed within
//! the accepted timestamp range. The final confirmation proves possession of this exchange's
//! shared secret. Timestamps alone do not prove fresh participation.
//!
//! Fresh ephemeral secrets provide forward secrecy against later compromise of the identity
//! signing keys, provided the ephemeral secrets and secret transcript state have been erased.
//! Protecting application messages requires a record protocol using keys derived from the
//! transcript.
//!
//! The construction does not hide identities or provide 0-RTT data or resumption. The transcript
//! does not bind a cipher or record format. Peers must agree on them out of band.
//! Callers must enforce a handshake deadline separately from the accepted timestamp range.
//!
//! [EAP-SAKE]: https://www.rfc-editor.org/rfc/rfc4763
//! [symmetric-key SAKE]: https://eprint.iacr.org/2019/444

mod error;
pub use error::Error;

mod exchange;

mod protocol;
pub use protocol::{
    Ack, Context, DialState, ListenState, Syn, SynAck, Version, dial_end, dial_start, listen_end,
    listen_start,
};

#[cfg(all(test, feature = "arbitrary"))]
mod conformance;