ant-quic 0.27.1

QUIC transport protocol with advanced NAT traversal for P2P networks
Documentation
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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
// Copyright 2024 Saorsa Labs Ltd.
//
// This Saorsa Network Software is licensed under the General Public License (GPL), version 3.
// Please see the file LICENSE-GPL, or visit <http://www.gnu.org/licenses/> for the full text.
//
// Full details available at https://saorsalabs.com/licenses
#![allow(missing_docs)]

//! PQC-aware packet handling for larger handshakes
//!
//! v0.13.0+: PQC is always enabled. This module extends QUIC packet handling
//! to accommodate the larger handshake messages required by post-quantum
//! cryptography. It provides:
//!
//! - Detection of PQC handshakes based on TLS extensions
//! - Dynamic MTU adjustment for PQC handshakes
//! - Efficient fragmentation of large CRYPTO frames
//! - Coalescing logic aware of PQC constraints

use crate::{MAX_UDP_PAYLOAD, MtuDiscoveryConfig, frame::Crypto, packet::SpaceId};
use std::cmp;
use tracing::{debug, trace};

/// Size constants for PQC algorithms
pub const ML_KEM_768_HANDSHAKE_OVERHEAD: u16 = 1184 + 1088; // Public key + ciphertext
pub const ML_DSA_65_HANDSHAKE_OVERHEAD: u16 = 1952 + 3309; // Public key + signature
pub const HYBRID_HANDSHAKE_OVERHEAD: u16 =
    ML_KEM_768_HANDSHAKE_OVERHEAD + ML_DSA_65_HANDSHAKE_OVERHEAD + 256; // Plus classical overhead

/// Minimum MTU required for efficient PQC handshakes
pub const PQC_MIN_MTU: u16 = 2048;

/// Recommended MTU for PQC handshakes
pub const PQC_RECOMMENDED_MTU: u16 = 4096;

/// Maximum CRYPTO frame size for fragmentation
pub const MAX_CRYPTO_FRAME_SIZE: u16 = 1200;

/// PQC-aware packet handler
///
/// v0.13.0+: PQC is always enabled on every connection.
#[derive(Debug, Clone)]
pub struct PqcPacketHandler {
    /// Whether PQC is detected in the current handshake
    pqc_detected: bool,
    /// Current estimated handshake size
    estimated_handshake_size: u32,
    /// Whether we've initiated MTU discovery for PQC
    mtu_discovery_triggered: bool,
}

impl PqcPacketHandler {
    /// Create a new PQC packet handler
    ///
    /// v0.13.0+: PQC is always enabled on every connection.
    pub fn new() -> Self {
        Self {
            pqc_detected: false,
            estimated_handshake_size: 0,
            mtu_discovery_triggered: false,
        }
    }

    /// Detect if the handshake is using PQC based on TLS extensions
    ///
    /// v0.13.0+: PQC is always enabled, so this always returns true for valid
    /// handshakes. The detection is used for MTU optimization purposes.
    pub fn detect_pqc_handshake(&mut self, crypto_data: &[u8], space: SpaceId) -> bool {
        // Only check in Initial and Handshake spaces
        if !matches!(space, SpaceId::Initial | SpaceId::Handshake) {
            return self.pqc_detected;
        }

        // Look for TLS handshake messages
        if crypto_data.is_empty() {
            return self.pqc_detected;
        }

        // Check for ClientHello (type 1) or ServerHello (type 2)
        let msg_type = crypto_data[0];

        // Need at least 4 bytes for a valid handshake message
        if crypto_data.len() < 4 {
            return self.pqc_detected;
        }
        if msg_type == 1 || msg_type == 2 {
            // v0.13.0+: All handshakes use PQC, detect based on message size
            if self.detect_pqc_in_extensions(crypto_data) {
                debug!("Detected PQC handshake");
                self.pqc_detected = true;
                // v0.13.0+: Always use PQC handshake size estimate
                self.estimated_handshake_size = Self::pqc_handshake_size();
                return true;
            }
        }

        self.pqc_detected
    }

    /// Detect PQC usage in TLS extensions
    ///
    /// v0.13.0+: Simplified detection - all connections use PQC.
    fn detect_pqc_in_extensions(&self, data: &[u8]) -> bool {
        // Larger handshakes indicate PQC usage
        // v0.13.0+: All connections should be using PQC
        data.len() > 100
    }

    /// Get the estimated handshake size for PQC connections
    ///
    /// v0.13.0+: Always returns the PQC handshake size.
    fn pqc_handshake_size() -> u32 {
        // PQC handshake with ML-KEM-768 hybrid key exchange
        16384
    }

    /// Check if MTU discovery should be triggered for PQC
    pub fn should_trigger_mtu_discovery(&mut self) -> bool {
        if self.pqc_detected && !self.mtu_discovery_triggered {
            self.mtu_discovery_triggered = true;
            true
        } else {
            false
        }
    }

    /// Get recommended MTU configuration for PQC
    pub fn get_pqc_mtu_config(&self) -> MtuDiscoveryConfig {
        let mut config = MtuDiscoveryConfig::default();

        if self.pqc_detected {
            // Set higher upper bound for PQC
            config.upper_bound(PQC_RECOMMENDED_MTU.min(MAX_UDP_PAYLOAD));

            // More aggressive probing for PQC
            config.minimum_change = 128;

            // Shorter interval between probes
            config.interval = std::time::Duration::from_millis(100);
        }

        config
    }

    /// Calculate optimal CRYPTO frame size for fragmentation
    pub fn calculate_crypto_frame_size(
        &self,
        available_space: usize,
        remaining_data: usize,
    ) -> usize {
        let max_frame_size = if self.pqc_detected {
            // Use larger frames for PQC to reduce overhead
            available_space.min(MAX_CRYPTO_FRAME_SIZE as usize)
        } else {
            // Standard frame size for classical
            available_space.min(600)
        };

        cmp::min(max_frame_size, remaining_data)
    }

    /// Check if packet coalescing should be adjusted for PQC
    pub fn adjust_coalescing_for_pqc(&self, current_size: usize, space: SpaceId) -> bool {
        if !self.pqc_detected {
            return false;
        }

        // Don't coalesce Initial packets with others if using PQC
        // to maximize space for large CRYPTO frames
        matches!(space, SpaceId::Initial) && current_size > 600
    }

    /// Get the minimum packet size for PQC handshakes
    pub fn get_min_packet_size(&self, space: SpaceId) -> u16 {
        if !self.pqc_detected {
            return 1200; // Standard QUIC minimum
        }

        match space {
            SpaceId::Initial => PQC_MIN_MTU,
            SpaceId::Handshake => 1500, // Can be smaller after Initial
            _ => 1200,
        }
    }

    /// Check if handshake is complete based on estimated size
    pub fn is_handshake_complete(&self, bytes_sent: u64) -> bool {
        if !self.pqc_detected {
            return false; // Let normal logic handle
        }

        bytes_sent >= self.estimated_handshake_size as u64
    }

    /// Fragment large CRYPTO data into multiple frames
    pub fn fragment_crypto_data(
        &self,
        data: &[u8],
        offset: u64,
        max_packet_size: usize,
    ) -> Vec<Crypto> {
        let mut frames = Vec::new();
        let mut current_offset = offset;
        let mut remaining = data;

        while !remaining.is_empty() {
            // Reserve space for frame header (worst case ~16 bytes)
            let available_space = max_packet_size.saturating_sub(16);
            let frame_size = self.calculate_crypto_frame_size(available_space, remaining.len());

            let (chunk, rest) = remaining.split_at(frame_size);

            frames.push(Crypto {
                offset: current_offset,
                data: chunk.to_vec().into(),
            });

            current_offset += frame_size as u64;
            remaining = rest;
        }

        trace!(
            "Fragmented {} bytes into {} CRYPTO frames",
            data.len(),
            frames.len()
        );

        frames
    }

    /// Update statistics after packet sent
    pub fn on_packet_sent(&mut self, space: SpaceId, size: u16) {
        if self.pqc_detected && matches!(space, SpaceId::Initial | SpaceId::Handshake) {
            trace!("PQC packet sent in {:?}: {} bytes", space, size);
        }
    }

    /// Reset handler state (e.g., on retry)
    pub fn reset(&mut self) {
        self.pqc_detected = false;
        self.estimated_handshake_size = 0;
        self.mtu_discovery_triggered = false;
    }
}

impl Default for PqcPacketHandler {
    fn default() -> Self {
        Self::new()
    }
}

/// Extension methods for Connection to handle PQC packets
pub trait PqcPacketHandling {
    /// Get the PQC packet handler
    fn pqc_packet_handler(&mut self) -> &mut PqcPacketHandler;

    /// Check and handle PQC detection from CRYPTO frames
    fn handle_pqc_detection(&mut self, crypto_data: &[u8], space: SpaceId);

    /// Adjust MTU discovery for PQC if needed
    fn adjust_mtu_for_pqc(&mut self);

    /// Get optimal packet size for current state
    fn get_pqc_optimal_packet_size(&self, space: SpaceId) -> u16;
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_pqc_packet_handler_creation() {
        let handler = PqcPacketHandler::new();
        assert!(!handler.pqc_detected);
        assert_eq!(handler.estimated_handshake_size, 0);
        assert!(!handler.mtu_discovery_triggered);
    }

    #[test]
    fn test_mtu_discovery_trigger() {
        let mut handler = PqcPacketHandler::new();

        // Should not trigger without PQC detection
        assert!(!handler.should_trigger_mtu_discovery());

        // Simulate PQC detection
        handler.pqc_detected = true;
        assert!(handler.should_trigger_mtu_discovery());

        // Should not trigger again
        assert!(!handler.should_trigger_mtu_discovery());
    }

    #[test]
    fn test_crypto_frame_size_calculation() {
        let handler = PqcPacketHandler::new();

        // Without PQC
        assert_eq!(handler.calculate_crypto_frame_size(1000, 2000), 600);
        assert_eq!(handler.calculate_crypto_frame_size(500, 2000), 500);
        assert_eq!(handler.calculate_crypto_frame_size(1000, 400), 400);

        // With PQC
        let mut handler = PqcPacketHandler::new();
        handler.pqc_detected = true;
        assert_eq!(handler.calculate_crypto_frame_size(1500, 2000), 1200);
        assert_eq!(handler.calculate_crypto_frame_size(500, 2000), 500);
    }

    #[test]
    fn test_min_packet_size() {
        let handler = PqcPacketHandler::new();

        // Without PQC
        assert_eq!(handler.get_min_packet_size(SpaceId::Initial), 1200);
        assert_eq!(handler.get_min_packet_size(SpaceId::Handshake), 1200);
        assert_eq!(handler.get_min_packet_size(SpaceId::Data), 1200);

        // With PQC
        let mut handler = PqcPacketHandler::new();
        handler.pqc_detected = true;
        assert_eq!(handler.get_min_packet_size(SpaceId::Initial), PQC_MIN_MTU);
        assert_eq!(handler.get_min_packet_size(SpaceId::Handshake), 1500);
        assert_eq!(handler.get_min_packet_size(SpaceId::Data), 1200);
    }

    #[test]
    fn test_crypto_data_fragmentation() {
        let handler = PqcPacketHandler::new();

        // Test small data (no fragmentation)
        let data = vec![0u8; 500];
        let frames = handler.fragment_crypto_data(&data, 1000, 1200);
        assert_eq!(frames.len(), 1);
        assert_eq!(frames[0].offset, 1000);
        assert_eq!(frames[0].data.len(), 500);

        // Test large data (requires fragmentation)
        // With max_packet_size=700, available_space=684, but limited to 600 for non-PQC
        let data = vec![0u8; 3000];
        let frames = handler.fragment_crypto_data(&data, 0, 700);
        assert_eq!(frames.len(), 5); // 600 * 5
        assert_eq!(frames[0].offset, 0);
        assert_eq!(frames[0].data.len(), 600);
        assert_eq!(frames[4].offset, 2400);
        assert_eq!(frames[4].data.len(), 600);
    }

    #[test]
    fn test_pqc_handshake_size() {
        // v0.13.0+: All connections use PQC
        assert_eq!(PqcPacketHandler::pqc_handshake_size(), 16384);
    }

    #[test]
    fn test_coalescing_adjustment() {
        let handler = PqcPacketHandler::new();

        // Without PQC
        assert!(!handler.adjust_coalescing_for_pqc(800, SpaceId::Initial));
        assert!(!handler.adjust_coalescing_for_pqc(500, SpaceId::Initial));

        // With PQC
        let mut handler = PqcPacketHandler::new();
        handler.pqc_detected = true;
        assert!(handler.adjust_coalescing_for_pqc(800, SpaceId::Initial));
        assert!(!handler.adjust_coalescing_for_pqc(500, SpaceId::Initial));
        assert!(!handler.adjust_coalescing_for_pqc(800, SpaceId::Handshake));
    }

    #[test]
    fn test_handshake_completion_check() {
        let mut handler = PqcPacketHandler::new();

        // Without PQC detection
        assert!(!handler.is_handshake_complete(10000));

        // With PQC detection
        handler.pqc_detected = true;
        handler.estimated_handshake_size = 16384;
        assert!(!handler.is_handshake_complete(8000));
        assert!(handler.is_handshake_complete(16384));
        assert!(handler.is_handshake_complete(20000));
    }

    #[test]
    fn test_handler_reset() {
        let mut handler = PqcPacketHandler::new();
        handler.pqc_detected = true;
        handler.estimated_handshake_size = 16384;
        handler.mtu_discovery_triggered = true;

        handler.reset();

        assert!(!handler.pqc_detected);
        assert_eq!(handler.estimated_handshake_size, 0);
        assert!(!handler.mtu_discovery_triggered);
    }

    #[test]
    fn test_pqc_mtu_config() {
        let mut handler = PqcPacketHandler::new();

        // Without PQC detection
        let config = handler.get_pqc_mtu_config();
        assert_eq!(config.upper_bound, 1452); // Default upper bound

        // With PQC detection
        handler.pqc_detected = true;
        let config = handler.get_pqc_mtu_config();
        assert_eq!(
            config.upper_bound,
            PQC_RECOMMENDED_MTU.min(crate::MAX_UDP_PAYLOAD)
        );
        assert_eq!(config.minimum_change, 128);
    }

    #[test]
    fn test_pqc_constants() {
        assert_eq!(ML_KEM_768_HANDSHAKE_OVERHEAD, 2272);
        assert_eq!(ML_DSA_65_HANDSHAKE_OVERHEAD, 5261);
        assert_eq!(HYBRID_HANDSHAKE_OVERHEAD, 7789);
        assert_eq!(PQC_MIN_MTU, 2048);
        assert_eq!(PQC_RECOMMENDED_MTU, 4096);
        assert_eq!(MAX_CRYPTO_FRAME_SIZE, 1200);
    }
}