ant_quic/crypto.rs
1// Copyright 2024 Saorsa Labs Ltd.
2//
3// This Saorsa Network Software is licensed under the General Public License (GPL), version 3.
4// Please see the file LICENSE-GPL, or visit <http://www.gnu.org/licenses/> for the full text.
5//
6// Full details available at https://saorsalabs.com/licenses
7
8//! Traits and implementations for the QUIC cryptography protocol
9#![allow(rustdoc::bare_urls)]
10//!
11//! The protocol logic in Quinn is contained in types that abstract over the actual
12//! cryptographic protocol used. This module contains the traits used for this
13//! abstraction layer as well as a single implementation of these traits that uses
14//! *ring* and rustls to implement the TLS protocol support.
15//!
16//! Note that usage of any protocol (version) other than TLS 1.3 does not conform to any
17//! published versions of the specification, and will not be supported in QUIC v1.
18
19use std::{any::Any, str, sync::Arc};
20
21use bytes::BytesMut;
22
23use crate::{
24 ConnectError, Side, TransportError, shared::ConnectionId,
25 transport_parameters::TransportParameters,
26};
27
28/// Cryptography interface based on *ring*
29#[cfg(any(feature = "aws-lc-rs", feature = "ring"))]
30pub(crate) mod ring_like;
31/// TLS interface based on rustls
32#[cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))]
33pub mod rustls;
34
35/// Certificate management
36#[cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))]
37pub mod certificate_manager;
38
39/// RFC 7250 Raw Public Keys support
40#[cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))]
41pub mod raw_public_keys;
42
43/// Ed25519 key pair implementation
44pub mod raw_keys;
45
46/// Post-Quantum Cryptography support - always available
47pub mod pqc;
48
49// NOTE: The following modules were removed because they were written as external
50// integrations with Quinn, but ant-quic IS a fork of Quinn, not something that
51// integrates with it. These need to be rewritten as part of the Quinn implementation
52// if their functionality is needed.
53
54// Removed modules:
55// - rpk_integration (tried to integrate RPK with Quinn from outside)
56// - quinn_integration (tried to wrap Quinn endpoints)
57// - bootstrap_support (tried to add bootstrap support on top of Quinn)
58// - peer_discovery (distributed discovery layered on Quinn)
59// - enterprise_cert_mgmt (enterprise features added on top)
60// - performance_monitoring (monitoring Quinn from outside)
61// - performance_optimization (optimizing Quinn externally)
62// - zero_rtt_rpk (0-RTT features added on top)
63// - nat_rpk_integration (NAT traversal integration)
64
65/// TLS Extensions for RFC 7250 certificate type negotiation
66pub mod tls_extensions;
67
68/// TLS Extension Simulation for RFC 7250 Raw Public Keys
69pub mod tls_extension_simulation;
70
71/// rustls Extension Handlers for certificate type negotiation
72pub mod extension_handlers;
73
74/// Certificate Type Negotiation Protocol Implementation
75pub mod certificate_negotiation;
76
77/// Test module for TLS extension simulation
78#[cfg(test)]
79mod test_tls_simulation;
80
81/// A cryptographic session (commonly TLS)
82pub trait Session: Send + Sync + 'static {
83 /// Create the initial set of keys given the client's initial destination ConnectionId
84 fn initial_keys(&self, dst_cid: &ConnectionId, side: Side) -> Keys;
85
86 /// Get data negotiated during the handshake, if available
87 ///
88 /// Returns `None` until the connection emits `HandshakeDataReady`.
89 fn handshake_data(&self) -> Option<Box<dyn Any>>;
90
91 /// Get the peer's identity, if available
92 fn peer_identity(&self) -> Option<Box<dyn Any>>;
93
94 /// Get the 0-RTT keys if available (clients only)
95 ///
96 /// On the client side, this method can be used to see if 0-RTT key material is available
97 /// to start sending data before the protocol handshake has completed.
98 ///
99 /// Returns `None` if the key material is not available. This might happen if you have
100 /// not connected to this server before.
101 fn early_crypto(&self) -> Option<(Box<dyn HeaderKey>, Box<dyn PacketKey>)>;
102
103 /// If the 0-RTT-encrypted data has been accepted by the peer
104 fn early_data_accepted(&self) -> Option<bool>;
105
106 /// Returns `true` until the connection is fully established.
107 fn is_handshaking(&self) -> bool;
108
109 /// Read bytes of handshake data
110 ///
111 /// This should be called with the contents of `CRYPTO` frames. If it returns `Ok`, the
112 /// caller should call `write_handshake()` to check if the crypto protocol has anything
113 /// to send to the peer. This method will only return `true` the first time that
114 /// handshake data is available. Future calls will always return false.
115 ///
116 /// On success, returns `true` iff `self.handshake_data()` has been populated.
117 fn read_handshake(&mut self, buf: &[u8]) -> Result<bool, TransportError>;
118
119 /// The peer's QUIC transport parameters
120 ///
121 /// These are only available after the first flight from the peer has been received.
122 fn transport_parameters(&self) -> Result<Option<TransportParameters>, TransportError>;
123
124 /// Writes handshake bytes into the given buffer and optionally returns the negotiated keys
125 ///
126 /// When the handshake proceeds to the next phase, this method will return a new set of
127 /// keys to encrypt data with.
128 fn write_handshake(&mut self, buf: &mut Vec<u8>) -> Option<Keys>;
129
130 /// Compute keys for the next key update
131 fn next_1rtt_keys(&mut self) -> Option<KeyPair<Box<dyn PacketKey>>>;
132
133 /// Verify the integrity of a retry packet
134 fn is_valid_retry(&self, orig_dst_cid: &ConnectionId, header: &[u8], payload: &[u8]) -> bool;
135
136 /// Fill `output` with `output.len()` bytes of keying material derived
137 /// from the [Session]'s secrets, using `label` and `context` for domain
138 /// separation.
139 ///
140 /// This function will fail, returning [ExportKeyingMaterialError],
141 /// if the requested output length is too large.
142 fn export_keying_material(
143 &self,
144 output: &mut [u8],
145 label: &[u8],
146 context: &[u8],
147 ) -> Result<(), ExportKeyingMaterialError>;
148}
149
150/// A pair of keys for bidirectional communication
151pub struct KeyPair<T> {
152 /// Key for encrypting data
153 pub local: T,
154 /// Key for decrypting data
155 pub remote: T,
156}
157
158/// A complete set of keys for a certain packet space
159pub struct Keys {
160 /// Header protection keys
161 pub header: KeyPair<Box<dyn HeaderKey>>,
162 /// Packet protection keys
163 pub packet: KeyPair<Box<dyn PacketKey>>,
164}
165
166/// Client-side configuration for the crypto protocol
167pub trait ClientConfig: Send + Sync {
168 /// Start a client session with this configuration
169 fn start_session(
170 self: Arc<Self>,
171 version: u32,
172 server_name: &str,
173 params: &TransportParameters,
174 ) -> Result<Box<dyn Session>, ConnectError>;
175}
176
177/// Server-side configuration for the crypto protocol
178pub trait ServerConfig: Send + Sync {
179 /// Create the initial set of keys given the client's initial destination ConnectionId
180 fn initial_keys(
181 &self,
182 version: u32,
183 dst_cid: &ConnectionId,
184 ) -> Result<Keys, UnsupportedVersion>;
185
186 /// Generate the integrity tag for a retry packet
187 ///
188 /// Never called if `initial_keys` rejected `version`.
189 fn retry_tag(&self, version: u32, orig_dst_cid: &ConnectionId, packet: &[u8]) -> [u8; 16];
190
191 /// Start a server session with this configuration
192 ///
193 /// Never called if `initial_keys` rejected `version`.
194 fn start_session(
195 self: Arc<Self>,
196 version: u32,
197 params: &TransportParameters,
198 ) -> Box<dyn Session>;
199}
200
201/// Keys used to protect packet payloads
202pub trait PacketKey: Send + Sync {
203 /// Encrypt the packet payload with the given packet number
204 fn encrypt(&self, packet: u64, buf: &mut [u8], header_len: usize);
205 /// Decrypt the packet payload with the given packet number
206 fn decrypt(
207 &self,
208 packet: u64,
209 header: &[u8],
210 payload: &mut BytesMut,
211 ) -> Result<(), CryptoError>;
212 /// The length of the AEAD tag appended to packets on encryption
213 fn tag_len(&self) -> usize;
214 /// Maximum number of packets that may be sent using a single key
215 fn confidentiality_limit(&self) -> u64;
216 /// Maximum number of incoming packets that may fail decryption before the connection must be
217 /// abandoned
218 fn integrity_limit(&self) -> u64;
219}
220
221/// Keys used to protect packet headers
222pub trait HeaderKey: Send + Sync {
223 /// Decrypt the given packet's header
224 fn decrypt(&self, pn_offset: usize, packet: &mut [u8]);
225 /// Encrypt the given packet's header
226 fn encrypt(&self, pn_offset: usize, packet: &mut [u8]);
227 /// The sample size used for this key's algorithm
228 fn sample_size(&self) -> usize;
229}
230
231/// A key for signing with HMAC-based algorithms
232pub trait HmacKey: Send + Sync {
233 /// Method for signing a message
234 fn sign(&self, data: &[u8], signature_out: &mut [u8]);
235 /// Length of `sign`'s output
236 fn signature_len(&self) -> usize;
237 /// Method for verifying a message
238 fn verify(&self, data: &[u8], signature: &[u8]) -> Result<(), CryptoError>;
239}
240
241/// Error returned by [Session::export_keying_material].
242///
243/// This error occurs if the requested output length is too large.
244#[derive(Debug, PartialEq, Eq)]
245pub struct ExportKeyingMaterialError;
246
247/// A pseudo random key for HKDF
248pub trait HandshakeTokenKey: Send + Sync {
249 /// Derive AEAD using hkdf
250 fn aead_from_hkdf(&self, random_bytes: &[u8]) -> Box<dyn AeadKey>;
251}
252
253/// A key for sealing data with AEAD-based algorithms
254pub trait AeadKey {
255 /// Method for sealing message `data`
256 fn seal(&self, data: &mut Vec<u8>, additional_data: &[u8]) -> Result<(), CryptoError>;
257 /// Method for opening a sealed message `data`
258 fn open<'a>(
259 &self,
260 data: &'a mut [u8],
261 additional_data: &[u8],
262 ) -> Result<&'a mut [u8], CryptoError>;
263}
264
265/// Generic crypto errors
266#[derive(Debug)]
267pub struct CryptoError;
268
269/// Error indicating that the specified QUIC version is not supported
270#[derive(Debug)]
271pub struct UnsupportedVersion;
272
273impl From<UnsupportedVersion> for ConnectError {
274 fn from(_: UnsupportedVersion) -> Self {
275 Self::UnsupportedVersion
276 }
277}
278
279impl From<crate::TransportError> for ConnectError {
280 fn from(_err: crate::TransportError) -> Self {
281 // Convert TransportError to ConnectError - this is a generic conversion
282 // since transport parameter errors during connection setup are connection-level issues
283 Self::EndpointStopping
284 }
285}