iroh_quinn_proto/crypto.rs
1//! Traits and implementations for the QUIC cryptography protocol
2//!
3//! The protocol logic in Quinn is contained in types that abstract over the actual
4//! cryptographic protocol used. This module contains the traits used for this
5//! abstraction layer as well as a single implementation of these traits that uses
6//! *ring* and rustls to implement the TLS protocol support.
7//!
8//! Note that usage of any protocol (version) other than TLS 1.3 does not conform to any
9//! published versions of the specification, and will not be supported in QUIC v1.
10
11use std::{any::Any, str, sync::Arc};
12
13use bytes::BytesMut;
14
15use crate::{
16 ConnectError, PathId, Side, TransportError, shared::ConnectionId,
17 transport_parameters::TransportParameters,
18};
19
20/// Cryptography interface based on *ring*
21#[cfg(any(feature = "aws-lc-rs", feature = "ring"))]
22pub(crate) mod ring_like;
23/// TLS interface based on rustls
24#[cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))]
25pub mod rustls;
26
27/// A cryptographic session (commonly TLS)
28pub trait Session: Send + Sync + 'static {
29 /// Create the initial set of keys given the client's initial destination ConnectionId
30 fn initial_keys(&self, dst_cid: ConnectionId, side: Side) -> Keys;
31
32 /// Get data negotiated during the handshake, if available
33 ///
34 /// Returns `None` until the connection emits `HandshakeDataReady`.
35 fn handshake_data(&self) -> Option<Box<dyn Any>>;
36
37 /// Get the peer's identity, if available
38 fn peer_identity(&self) -> Option<Box<dyn Any>>;
39
40 /// Get the 0-RTT keys if available (clients only)
41 ///
42 /// On the client side, this method can be used to see if 0-RTT key material is available
43 /// to start sending data before the protocol handshake has completed.
44 ///
45 /// Returns `None` if the key material is not available. This might happen if you have
46 /// not connected to this server before.
47 fn early_crypto(&self) -> Option<(Box<dyn HeaderKey>, Box<dyn PacketKey>)>;
48
49 /// If the 0-RTT-encrypted data has been accepted by the peer
50 fn early_data_accepted(&self) -> Option<bool>;
51
52 /// Returns `true` until the connection is fully established.
53 fn is_handshaking(&self) -> bool;
54
55 /// Read bytes of handshake data
56 ///
57 /// This should be called with the contents of `CRYPTO` frames. If it returns `Ok`, the
58 /// caller should call `write_handshake()` to check if the crypto protocol has anything
59 /// to send to the peer. This method will only return `true` the first time that
60 /// handshake data is available. Future calls will always return false.
61 ///
62 /// On success, returns `true` iff `self.handshake_data()` has been populated.
63 fn read_handshake(&mut self, buf: &[u8]) -> Result<bool, TransportError>;
64
65 /// The peer's QUIC transport parameters
66 ///
67 /// These are only available after the first flight from the peer has been received.
68 fn transport_parameters(&self) -> Result<Option<TransportParameters>, TransportError>;
69
70 /// Writes handshake bytes into the given buffer and optionally returns the negotiated keys
71 ///
72 /// When the handshake proceeds to the next phase, this method will return a new set of
73 /// keys to encrypt data with.
74 fn write_handshake(&mut self, buf: &mut Vec<u8>) -> Option<Keys>;
75
76 /// Compute keys for the next key update
77 fn next_1rtt_keys(&mut self) -> Option<KeyPair<Box<dyn PacketKey>>>;
78
79 /// Verify the integrity of a retry packet
80 fn is_valid_retry(&self, orig_dst_cid: ConnectionId, header: &[u8], payload: &[u8]) -> bool;
81
82 /// Fill `output` with `output.len()` bytes of keying material derived
83 /// from the [Session]'s secrets, using `label` and `context` for domain
84 /// separation.
85 ///
86 /// This function will fail, returning [ExportKeyingMaterialError],
87 /// if the requested output length is too large.
88 fn export_keying_material(
89 &self,
90 output: &mut [u8],
91 label: &[u8],
92 context: &[u8],
93 ) -> Result<(), ExportKeyingMaterialError>;
94}
95
96/// A pair of keys for bidirectional communication
97pub struct KeyPair<T> {
98 /// Key for encrypting data
99 pub local: T,
100 /// Key for decrypting data
101 pub remote: T,
102}
103
104/// A complete set of keys for a certain packet space
105pub struct Keys {
106 /// Header protection keys
107 pub header: KeyPair<Box<dyn HeaderKey>>,
108 /// Packet protection keys
109 pub packet: KeyPair<Box<dyn PacketKey>>,
110}
111
112/// Client-side configuration for the crypto protocol
113pub trait ClientConfig: Send + Sync {
114 /// Start a client session with this configuration
115 fn start_session(
116 self: Arc<Self>,
117 version: u32,
118 server_name: &str,
119 params: &TransportParameters,
120 ) -> Result<Box<dyn Session>, ConnectError>;
121}
122
123/// Server-side configuration for the crypto protocol
124pub trait ServerConfig: Send + Sync {
125 /// Create the initial set of keys given the client's initial destination ConnectionId
126 fn initial_keys(&self, version: u32, dst_cid: ConnectionId)
127 -> Result<Keys, UnsupportedVersion>;
128
129 /// Generate the integrity tag for a retry packet
130 ///
131 /// Never called if `initial_keys` rejected `version`.
132 fn retry_tag(&self, version: u32, orig_dst_cid: ConnectionId, packet: &[u8]) -> [u8; 16];
133
134 /// Start a server session with this configuration
135 ///
136 /// Never called if `initial_keys` rejected `version`.
137 fn start_session(
138 self: Arc<Self>,
139 version: u32,
140 params: &TransportParameters,
141 ) -> Box<dyn Session>;
142}
143
144/// Keys used to protect packet payloads
145pub trait PacketKey: Send + Sync {
146 /// Encrypt the packet payload with the given packet number
147 fn encrypt(&self, path_id: PathId, packet: u64, buf: &mut [u8], header_len: usize);
148 /// Decrypt the packet payload with the given packet number
149 fn decrypt(
150 &self,
151 path_id: PathId,
152 packet: u64,
153 header: &[u8],
154 payload: &mut BytesMut,
155 ) -> Result<(), CryptoError>;
156 /// The length of the AEAD tag appended to packets on encryption
157 fn tag_len(&self) -> usize;
158 /// Maximum number of packets that may be sent using a single key
159 fn confidentiality_limit(&self) -> u64;
160 /// Maximum number of incoming packets that may fail decryption before the connection must be
161 /// abandoned
162 fn integrity_limit(&self) -> u64;
163}
164
165/// Keys used to protect packet headers
166pub trait HeaderKey: Send + Sync {
167 /// Decrypt the given packet's header
168 fn decrypt(&self, pn_offset: usize, packet: &mut [u8]);
169 /// Encrypt the given packet's header
170 fn encrypt(&self, pn_offset: usize, packet: &mut [u8]);
171 /// The sample size used for this key's algorithm
172 fn sample_size(&self) -> usize;
173}
174
175/// A key for signing with HMAC-based algorithms
176pub trait HmacKey: Send + Sync {
177 /// Method for signing a message
178 fn sign(&self, data: &[u8], signature_out: &mut [u8]);
179 /// Length of `sign`'s output
180 fn signature_len(&self) -> usize;
181 /// Method for verifying a message
182 fn verify(&self, data: &[u8], signature: &[u8]) -> Result<(), CryptoError>;
183}
184
185/// Error returned by [Session::export_keying_material].
186///
187/// This error occurs if the requested output length is too large.
188#[derive(Debug, PartialEq, Eq)]
189pub struct ExportKeyingMaterialError;
190
191/// A pseudo random key for HKDF
192pub trait HandshakeTokenKey: Send + Sync {
193 /// Derive AEAD using hkdf
194 fn aead_from_hkdf(&self, random_bytes: &[u8]) -> Box<dyn AeadKey>;
195}
196
197/// A key for sealing data with AEAD-based algorithms
198pub trait AeadKey {
199 /// Method for sealing message `data`
200 fn seal(&self, data: &mut Vec<u8>, additional_data: &[u8]) -> Result<(), CryptoError>;
201 /// Method for opening a sealed message `data`
202 fn open<'a>(
203 &self,
204 data: &'a mut [u8],
205 additional_data: &[u8],
206 ) -> Result<&'a mut [u8], CryptoError>;
207}
208
209/// Generic crypto errors
210#[derive(Debug)]
211pub struct CryptoError;
212
213/// Error indicating that the specified QUIC version is not supported
214#[derive(Debug)]
215pub struct UnsupportedVersion;
216
217impl From<UnsupportedVersion> for ConnectError {
218 fn from(_: UnsupportedVersion) -> Self {
219 Self::UnsupportedVersion
220 }
221}