1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
//! 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
pub use Error;
pub use ;