commonware_stream/public_key/mod.rs
1//! Communicate with an authenticated peer over an encrypted connection.
2//!
3//! This module provides a lightweight, self-contained transport layer for systems where peers
4//! already know one another's cryptographic identities (any [commonware_cryptography::Signer]).
5//! It offers mutual authentication and encrypted communication using a simplified handshake,
6//! eliminating the need for complex protocols like TLS or X.509 certificates. The implementation
7//! uses a fixed, non-negotiable cryptographic protocol to simplify implementation and reduce
8//! overhead, resulting in a minimal performance impact.
9//!
10//! # Design
11//!
12//! ## Handshake
13//!
14//! A three-message handshake is used to authenticate peers and establish a shared secret. The
15//! **dialer** initiates the connection, and the **listener** responds.
16//!
17//! The **dialer** initiates the connection to a known peer identity, while the **listener** accepts
18//! incoming connections. Much like a SYN / SYN-ACK / ACK handshake, the dialer and listener
19//! exchange messages in three rounds.
20//!
21//! The SYN-equivalent is a [handshake::Hello] message that contains:
22//! - The listener's expected public key (prevents wrong-target attacks)
23//! - The dialer's ephemeral public key (for Diffie-Hellman key exchange)
24//! - The current timestamp (prevents replay attacks)
25//! - The dialer's static public key and signature
26//!
27//! The ACK-equivalent is a [handshake::Confirmation] message that proves that each party can derive
28//! the correct shared secret.
29//!
30//! Thus:
31//! - Message 1 is a `Hello` message from the dialer to the listener
32//! - Message 2 is a `Hello` and `Confirmation` message from the listener to the dialer
33//! - Message 3 is a `Confirmation` message from the dialer to the listener
34//!
35//! ## Encryption
36//!
37//! All traffic is encrypted using ChaCha20-Poly1305. A shared secret is established using an
38//! ephemeral X25519 Diffie-Hellman key exchange. This secret, combined with the handshake
39//! transcript, is used to derive keys for both the handshake's key confirmation messages and
40//! the post-handshake data traffic. Binding the derived keys to the handshake transcript prevents
41//! man-in-the-middle and transcript substitution attacks.
42//!
43//! Each directional cipher uses a 12-byte nonce derived from a counter that is incremented for each
44//! message sent. This counter has sufficient cardinality for over 2.5 trillion years of continuous
45//! communication at a rate of 1 billion messages per second—sufficient for all practical use cases.
46//! This ensures that well-behaving peers can remain connected indefinitely as long as they both
47//! remain online (maximizing p2p network stability). In the unlikely case of counter overflow, the
48//! connection will be terminated and a new connection should be established. This method prevents
49//! nonce reuse (which would compromise message confidentiality) while saving bandwidth (as there is
50//! no need to transmit nonces explicitly).
51//!
52//! # Security
53//!
54//! ## Requirements
55//!
56//! - **Pre-Shared Namespace**: Peers must agree on a unique, application-specific namespace
57//! out-of-band to prevent cross-application replay attacks.
58//! - **Time Synchronization**: Peer clocks must be synchronized to within the `synchrony_bound`
59//! to correctly validate timestamps.
60//!
61//! ## Provided
62//!
63//! - **Mutual Authentication**: Both parties prove ownership of their static private keys through
64//! signatures.
65//! - **Forward Secrecy**: Ephemeral encryption keys ensure that any compromise of long-term static keys
66//! doesn't expose the contents of previous sessions.
67//! - **Session Uniqueness**: Confirmations are bound to the complete handshake transcript (including
68//! the randomly generated session key), preventing replay attacks and ensuring message integrity.
69//! - **Handshake Timeout**: A configurable deadline is enforced for handshake completion to protect
70//! against malicious peers that create connections but abandon handshakes.
71//!
72//! ## Not Provided
73//!
74//! - **Anonymity**: Peer identities are not hidden during handshakes from network observers (both active
75//! and passive).
76//! - **Padding**: Messages are encrypted as-is, allowing an attacker to perform traffic analysis.
77//! - **Future Secrecy**: If a peer's static private key is compromised, future sessions will be exposed.
78//! - **0-RTT**: The protocol does not support 0-RTT handshakes (resumed sessions).
79
80use chacha20poly1305::{
81 aead::{generic_array::typenum::Unsigned, AeadCore},
82 ChaCha20Poly1305,
83};
84use std::time::Duration;
85
86mod cipher;
87mod connection;
88use commonware_cryptography::Signer;
89pub use connection::{Connection, IncomingConnection, Receiver, Sender};
90pub mod handshake;
91mod nonce;
92pub mod x25519;
93
94// When encrypting data, an authentication tag is appended to the ciphertext.
95// This constant represents the size of the authentication tag in bytes.
96const AUTHENTICATION_TAG_LENGTH: usize = <ChaCha20Poly1305 as AeadCore>::TagSize::USIZE;
97
98/// Configuration for a connection.
99///
100/// # Warning
101///
102/// Synchronize this configuration across all peers.
103/// Mismatched configurations may cause dropped connections or parsing errors.
104#[derive(Clone)]
105pub struct Config<C: Signer> {
106 /// Cryptographic primitives for signing and verification.
107 pub crypto: C,
108
109 /// Unique prefix for all signed messages. Should be application-specific.
110 /// Prevents replay attacks across different applications using the same keys.
111 pub namespace: Vec<u8>,
112
113 /// Maximum message size (in bytes). Prevents memory exhaustion DoS attacks.
114 pub max_message_size: usize,
115
116 /// Maximum time drift allowed for future timestamps. Handles clock skew.
117 pub synchrony_bound: Duration,
118
119 /// Maximum age of handshake messages before rejection.
120 pub max_handshake_age: Duration,
121
122 /// Maximum time allowed for completing the handshake.
123 pub handshake_timeout: Duration,
124}