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