ant_quic/crypto/pqc/
config.rs

1//! Configuration for Post-Quantum Cryptography (PQC) in QUIC
2//!
3//! This module provides a flexible configuration system for controlling
4//! PQC behavior, including algorithm selection, operation modes, and
5//! performance tuning parameters.
6
7use std::fmt;
8
9/// Configuration for Post-Quantum Cryptography behavior
10#[derive(Debug, Clone, PartialEq)]
11pub struct PqcConfig {
12    /// Operation mode for PQC algorithms
13    pub mode: PqcMode,
14    /// Enable ML-KEM-768 for key encapsulation
15    pub ml_kem_enabled: bool,
16    /// Enable ML-DSA-65 for digital signatures
17    pub ml_dsa_enabled: bool,
18    /// Preference for hybrid algorithm selection
19    pub hybrid_preference: HybridPreference,
20    /// Size of the memory pool for PQC objects
21    pub memory_pool_size: usize,
22    /// Multiplier for handshake timeout to account for larger PQC messages
23    pub handshake_timeout_multiplier: f32,
24}
25
26/// Operation mode for Post-Quantum Cryptography
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub enum PqcMode {
29    /// Use only classical cryptography (no PQC)
30    ClassicalOnly,
31    /// Use hybrid mode with both classical and PQC (recommended)
32    Hybrid,
33    /// Require PQC algorithms only (no classical fallback)
34    PqcOnly,
35}
36
37/// Preference for algorithm selection in hybrid mode
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39pub enum HybridPreference {
40    /// Prefer classical algorithms when both are available
41    PreferClassical,
42    /// No preference, use first mutually supported algorithm
43    Balanced,
44    /// Prefer PQC algorithms when both are available
45    PreferPqc,
46}
47
48/// Error type for PQC configuration
49#[derive(Debug, Clone, PartialEq)]
50pub enum ConfigError {
51    /// No PQC algorithms enabled in PqcOnly mode
52    NoPqcAlgorithmsEnabled,
53    /// Invalid memory pool size
54    InvalidMemoryPoolSize(usize),
55    /// Invalid timeout multiplier
56    InvalidTimeoutMultiplier(f32),
57    /// Conflicting configuration options
58    ConflictingOptions(String),
59}
60
61impl fmt::Display for ConfigError {
62    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63        match self {
64            ConfigError::NoPqcAlgorithmsEnabled => {
65                write!(
66                    f,
67                    "PqcOnly mode requires at least one PQC algorithm enabled"
68                )
69            }
70            ConfigError::InvalidMemoryPoolSize(size) => {
71                write!(
72                    f,
73                    "Invalid memory pool size {}: must be between 1 and 1000",
74                    size
75                )
76            }
77            ConfigError::InvalidTimeoutMultiplier(mult) => {
78                write!(
79                    f,
80                    "Invalid timeout multiplier {}: must be between 1.0 and 10.0",
81                    mult
82                )
83            }
84            ConfigError::ConflictingOptions(msg) => {
85                write!(f, "Conflicting configuration options: {}", msg)
86            }
87        }
88    }
89}
90
91impl std::error::Error for ConfigError {}
92
93impl Default for PqcConfig {
94    fn default() -> Self {
95        Self {
96            mode: PqcMode::Hybrid,
97            ml_kem_enabled: true,
98            ml_dsa_enabled: true,
99            hybrid_preference: HybridPreference::PreferPqc, // Prefer PQC by default
100            memory_pool_size: 10,
101            handshake_timeout_multiplier: 2.0,
102        }
103    }
104}
105
106impl PqcConfig {
107    /// Create a new PqcConfig with default values
108    pub fn new() -> Self {
109        Self::default()
110    }
111
112    /// Create a builder for constructing PqcConfig
113    pub fn builder() -> PqcConfigBuilder {
114        PqcConfigBuilder::new()
115    }
116
117    /// Validate the configuration
118    pub fn validate(&self) -> Result<(), ConfigError> {
119        // PqcOnly mode requires at least one PQC algorithm
120        if self.mode == PqcMode::PqcOnly && !self.ml_kem_enabled && !self.ml_dsa_enabled {
121            return Err(ConfigError::NoPqcAlgorithmsEnabled);
122        }
123
124        // Validate memory pool size
125        if self.memory_pool_size == 0 || self.memory_pool_size > 1000 {
126            return Err(ConfigError::InvalidMemoryPoolSize(self.memory_pool_size));
127        }
128
129        // Validate timeout multiplier
130        if self.handshake_timeout_multiplier < 1.0 || self.handshake_timeout_multiplier > 10.0 {
131            return Err(ConfigError::InvalidTimeoutMultiplier(
132                self.handshake_timeout_multiplier,
133            ));
134        }
135
136        Ok(())
137    }
138}
139
140/// Builder for PqcConfig
141#[derive(Debug, Clone)]
142pub struct PqcConfigBuilder {
143    mode: PqcMode,
144    ml_kem_enabled: bool,
145    ml_dsa_enabled: bool,
146    hybrid_preference: HybridPreference,
147    memory_pool_size: usize,
148    handshake_timeout_multiplier: f32,
149}
150
151impl Default for PqcConfigBuilder {
152    fn default() -> Self {
153        Self::new()
154    }
155}
156
157impl PqcConfigBuilder {
158    /// Create a new builder with default values
159    pub fn new() -> Self {
160        let default = PqcConfig::default();
161        Self {
162            mode: default.mode,
163            ml_kem_enabled: default.ml_kem_enabled,
164            ml_dsa_enabled: default.ml_dsa_enabled,
165            hybrid_preference: default.hybrid_preference,
166            memory_pool_size: default.memory_pool_size,
167            handshake_timeout_multiplier: default.handshake_timeout_multiplier,
168        }
169    }
170
171    /// Set the PQC operation mode
172    pub fn mode(mut self, mode: PqcMode) -> Self {
173        self.mode = mode;
174        self
175    }
176
177    /// Enable or disable ML-KEM-768
178    pub fn ml_kem(mut self, enabled: bool) -> Self {
179        self.ml_kem_enabled = enabled;
180        self
181    }
182
183    /// Enable or disable ML-DSA-65
184    pub fn ml_dsa(mut self, enabled: bool) -> Self {
185        self.ml_dsa_enabled = enabled;
186        self
187    }
188
189    /// Set the hybrid algorithm preference
190    pub fn hybrid_preference(mut self, preference: HybridPreference) -> Self {
191        self.hybrid_preference = preference;
192        self
193    }
194
195    /// Set the memory pool size
196    pub fn memory_pool_size(mut self, size: usize) -> Self {
197        self.memory_pool_size = size;
198        self
199    }
200
201    /// Set the handshake timeout multiplier
202    pub fn handshake_timeout_multiplier(mut self, multiplier: f32) -> Self {
203        self.handshake_timeout_multiplier = multiplier;
204        self
205    }
206
207    /// Build the PqcConfig, validating all settings
208    pub fn build(self) -> Result<PqcConfig, ConfigError> {
209        let config = PqcConfig {
210            mode: self.mode,
211            ml_kem_enabled: self.ml_kem_enabled,
212            ml_dsa_enabled: self.ml_dsa_enabled,
213            hybrid_preference: self.hybrid_preference,
214            memory_pool_size: self.memory_pool_size,
215            handshake_timeout_multiplier: self.handshake_timeout_multiplier,
216        };
217
218        config.validate()?;
219        Ok(config)
220    }
221}
222
223#[cfg(test)]
224mod tests {
225    use super::*;
226
227    #[test]
228    fn test_default_config() {
229        let config = PqcConfig::default();
230        assert_eq!(config.mode, PqcMode::Hybrid);
231        assert!(config.ml_kem_enabled);
232        assert!(config.ml_dsa_enabled);
233        assert_eq!(config.hybrid_preference, HybridPreference::PreferPqc);
234        assert_eq!(config.memory_pool_size, 10);
235        assert_eq!(config.handshake_timeout_multiplier, 2.0);
236        assert!(config.validate().is_ok());
237    }
238
239    #[test]
240    fn test_builder_basic() {
241        let config = PqcConfig::builder()
242            .mode(PqcMode::PqcOnly)
243            .ml_kem(true)
244            .ml_dsa(true)
245            .build()
246            .unwrap();
247
248        assert_eq!(config.mode, PqcMode::PqcOnly);
249        assert!(config.ml_kem_enabled);
250        assert!(config.ml_dsa_enabled);
251    }
252
253    #[test]
254    fn test_pqc_only_requires_algorithms() {
255        // Should fail with no algorithms
256        let result = PqcConfig::builder()
257            .mode(PqcMode::PqcOnly)
258            .ml_kem(false)
259            .ml_dsa(false)
260            .build();
261
262        assert!(matches!(result, Err(ConfigError::NoPqcAlgorithmsEnabled)));
263
264        // Should succeed with at least one algorithm
265        let config = PqcConfig::builder()
266            .mode(PqcMode::PqcOnly)
267            .ml_kem(true)
268            .ml_dsa(false)
269            .build()
270            .unwrap();
271
272        assert!(config.ml_kem_enabled);
273        assert!(!config.ml_dsa_enabled);
274    }
275
276    #[test]
277    fn test_memory_pool_validation() {
278        // Zero should fail
279        let result = PqcConfig::builder().memory_pool_size(0).build();
280
281        assert!(matches!(result, Err(ConfigError::InvalidMemoryPoolSize(0))));
282
283        // Too large should fail
284        let result = PqcConfig::builder().memory_pool_size(1001).build();
285
286        assert!(matches!(
287            result,
288            Err(ConfigError::InvalidMemoryPoolSize(1001))
289        ));
290
291        // Valid range should succeed
292        let config = PqcConfig::builder().memory_pool_size(100).build().unwrap();
293
294        assert_eq!(config.memory_pool_size, 100);
295    }
296
297    #[test]
298    fn test_timeout_multiplier_validation() {
299        // Too small should fail
300        let result = PqcConfig::builder()
301            .handshake_timeout_multiplier(0.5)
302            .build();
303
304        assert!(matches!(
305            result,
306            Err(ConfigError::InvalidTimeoutMultiplier(_))
307        ));
308
309        // Too large should fail
310        let result = PqcConfig::builder()
311            .handshake_timeout_multiplier(11.0)
312            .build();
313
314        assert!(matches!(
315            result,
316            Err(ConfigError::InvalidTimeoutMultiplier(_))
317        ));
318
319        // Valid range should succeed
320        let config = PqcConfig::builder()
321            .handshake_timeout_multiplier(3.0)
322            .build()
323            .unwrap();
324
325        assert_eq!(config.handshake_timeout_multiplier, 3.0);
326    }
327
328    #[test]
329    fn test_classical_only_mode() {
330        let config = PqcConfig::builder()
331            .mode(PqcMode::ClassicalOnly)
332            .ml_kem(false)
333            .ml_dsa(false)
334            .build()
335            .unwrap();
336
337        assert_eq!(config.mode, PqcMode::ClassicalOnly);
338        assert!(!config.ml_kem_enabled);
339        assert!(!config.ml_dsa_enabled);
340    }
341
342    #[test]
343    fn test_hybrid_preferences() {
344        let config = PqcConfig::builder()
345            .mode(PqcMode::Hybrid)
346            .hybrid_preference(HybridPreference::PreferPqc)
347            .build()
348            .unwrap();
349
350        assert_eq!(config.hybrid_preference, HybridPreference::PreferPqc);
351    }
352}