Skip to main content

lib_q_core/security/
constants.rs

1//! Security constants for lib-Q
2//!
3//! This module provides security-related constants used throughout the library
4//! for validation and configuration.
5
6use lib_q_types::hqc;
7
8use crate::api::Algorithm;
9use crate::error::Result;
10
11/// Security constants for lib-Q
12///
13/// This struct provides access to security-related constants used throughout
14/// the library for validation and configuration.
15#[cfg(feature = "alloc")]
16#[derive(Clone)]
17pub struct SecurityConstants {
18    /// Maximum plaintext, ciphertext, or associated-data size for a single AEAD operation.
19    ///
20    /// A modest default encourages chunking and reduces accidental nonce reuse under streaming
21    /// misuse; raise via [`Self::set_max_aead_message_size`] when your deployment accepts larger
22    /// single-shot bindings.
23    max_aead_message_size: usize,
24    /// Maximum input length for hash absorb and for signature message preimages.
25    ///
26    /// Defaults to [`usize::MAX`] so digest and sign APIs are not capped by the AEAD binding
27    /// policy. Lower this in environments that need a hard resource ceiling on preimage size.
28    max_hash_message_size: usize,
29    // Standard nonce size in bytes (16 bytes)
30    standard_nonce_size: usize,
31    // Minimum randomness size in bytes (32 bytes)
32    min_randomness_size: usize,
33}
34
35#[cfg(feature = "alloc")]
36impl SecurityConstants {
37    /// Create a new SecurityConstants instance
38    ///
39    /// # Returns
40    ///
41    /// A new instance of SecurityConstants with default values.
42    pub fn new() -> Self {
43        Self {
44            max_aead_message_size: 1024 * 1024, // 1 MiB
45            max_hash_message_size: usize::MAX,
46            standard_nonce_size: 16, // 16 bytes
47            min_randomness_size: 32, // 32 bytes
48        }
49    }
50}
51
52#[cfg(feature = "alloc")]
53impl Default for SecurityConstants {
54    fn default() -> Self {
55        Self::new()
56    }
57}
58
59impl SecurityConstants {
60    /// Maximum plaintext, ciphertext, or AAD size for one AEAD call.
61    pub fn max_aead_message_size(&self) -> usize {
62        self.max_aead_message_size
63    }
64
65    /// Maximum hash input length and signature message length.
66    pub fn max_hash_message_size(&self) -> usize {
67        self.max_hash_message_size
68    }
69
70    /// Get the standard nonce size
71    ///
72    /// # Returns
73    ///
74    /// Returns the standard nonce size in bytes.
75    pub fn standard_nonce_size(&self) -> usize {
76        self.standard_nonce_size
77    }
78
79    /// Get the minimum randomness size
80    ///
81    /// # Returns
82    ///
83    /// Returns the minimum required randomness size in bytes.
84    pub fn min_randomness_size(&self) -> usize {
85        self.min_randomness_size
86    }
87
88    /// Get the expected key size for a given algorithm
89    ///
90    /// # Arguments
91    ///
92    /// * `algorithm` - The algorithm to get the key size for
93    /// * `is_secret` - Whether this is a secret key (affects expected size)
94    ///
95    /// # Returns
96    ///
97    /// Returns the expected key size in bytes, or an error if the algorithm
98    /// doesn't use keys or is not supported.
99    pub fn get_expected_key_size(&self, algorithm: Algorithm, is_secret: bool) -> Result<usize> {
100        let expected_size = match algorithm {
101            // KEM algorithms
102            Algorithm::MlKem512 => {
103                if is_secret {
104                    1632
105                } else {
106                    800
107                }
108            }
109            Algorithm::MlKem768 => {
110                if is_secret {
111                    2400
112                } else {
113                    1184
114                }
115            }
116            Algorithm::MlKem1024 => {
117                if is_secret {
118                    3168
119                } else {
120                    1568
121                }
122            }
123            // CB-KEM algorithms
124            Algorithm::CbKem348864 => {
125                if is_secret {
126                    6492 // CB-KEM-348864 secret key size
127                } else {
128                    261120 // CB-KEM-348864 public key size
129                }
130            }
131            Algorithm::CbKem460896 => {
132                if is_secret {
133                    13608 // CB-KEM-460896 secret key size
134                } else {
135                    524160 // CB-KEM-460896 public key size
136                }
137            }
138            Algorithm::CbKem6688128 => {
139                if is_secret {
140                    13932 // CB-KEM-6688128 secret key size
141                } else {
142                    1044992 // CB-KEM-6688128 public key size
143                }
144            }
145            Algorithm::CbKem6960119 => {
146                if is_secret {
147                    13948 // CB-KEM-6960119 secret key size
148                } else {
149                    1047319 // CB-KEM-6960119 public key size
150                }
151            }
152            Algorithm::CbKem8192128 => {
153                if is_secret {
154                    14120 // CB-KEM-8192128 secret key size
155                } else {
156                    1357824 // CB-KEM-8192128 public key size
157                }
158            }
159
160            // HQC KEM — sizes from `lib_q_types::hqc` (single source of truth).
161            Algorithm::Hqc128 => {
162                if is_secret {
163                    hqc::HQC128_SECRET_KEY_BYTES
164                } else {
165                    hqc::HQC128_PUBLIC_KEY_BYTES
166                }
167            }
168            Algorithm::Hqc192 => {
169                if is_secret {
170                    hqc::HQC192_SECRET_KEY_BYTES
171                } else {
172                    hqc::HQC192_PUBLIC_KEY_BYTES
173                }
174            }
175            Algorithm::Hqc256 => {
176                if is_secret {
177                    hqc::HQC256_SECRET_KEY_BYTES
178                } else {
179                    hqc::HQC256_PUBLIC_KEY_BYTES
180                }
181            }
182
183            // Signature algorithms
184            Algorithm::MlDsa44 => {
185                if is_secret {
186                    2560 // ML-DSA-44 secret key size
187                } else {
188                    1312 // ML-DSA-44 public key size
189                }
190            }
191            Algorithm::MlDsa65 => {
192                if is_secret {
193                    4032 // ML-DSA-65 secret key size
194                } else {
195                    1952 // ML-DSA-65 public key size
196                }
197            }
198            Algorithm::MlDsa87 => {
199                if is_secret {
200                    4896 // ML-DSA-87 secret key size
201                } else {
202                    2592 // ML-DSA-87 public key size
203                }
204            }
205            Algorithm::FnDsa => {
206                if is_secret {
207                    1281 // FN-DSA-512 secret key size (logn=9)
208                } else {
209                    897 // FN-DSA-512 public key size (logn=9)
210                }
211            }
212            Algorithm::FnDsa512 => {
213                if is_secret {
214                    1281 // FN-DSA-512 secret key size (logn=9)
215                } else {
216                    897 // FN-DSA-512 public key size (logn=9)
217                }
218            }
219            Algorithm::FnDsa1024 => {
220                if is_secret {
221                    2561 // FN-DSA-1024 secret key size (logn=10)
222                } else {
223                    1793 // FN-DSA-1024 public key size (logn=10)
224                }
225            }
226
227            // SLH-DSA algorithms
228            Algorithm::SlhDsaSha256128fRobust => {
229                if is_secret {
230                    64 // SLH-DSA SHA256-128f secret key size (4 * N where N=16)
231                } else {
232                    32 // SLH-DSA SHA256-128f public key size (2 * N where N=16)
233                }
234            }
235            Algorithm::SlhDsaSha256192fRobust => {
236                if is_secret {
237                    96 // SLH-DSA SHA256-192f secret key size (4 * N where N=24)
238                } else {
239                    48 // SLH-DSA SHA256-192f public key size (2 * N where N=24)
240                }
241            }
242            Algorithm::SlhDsaSha256256fRobust => {
243                if is_secret {
244                    128 // SLH-DSA SHA256-256f secret key size (4 * N where N=32)
245                } else {
246                    64 // SLH-DSA SHA256-256f public key size (2 * N where N=32)
247                }
248            }
249            Algorithm::SlhDsaShake256128fRobust => {
250                if is_secret {
251                    64 // SLH-DSA SHAKE256-128f secret key size (4 * N where N=16)
252                } else {
253                    32 // SLH-DSA SHAKE256-128f public key size (2 * N where N=16)
254                }
255            }
256            Algorithm::SlhDsaShake256192fRobust => {
257                if is_secret {
258                    96 // SLH-DSA SHAKE256-192f secret key size (4 * N where N=24)
259                } else {
260                    48 // SLH-DSA SHAKE256-192f public key size (2 * N where N=24)
261                }
262            }
263            Algorithm::SlhDsaShake256256fRobust => {
264                if is_secret {
265                    128 // SLH-DSA SHAKE256-256f secret key size (4 * N where N=32)
266                } else {
267                    64 // SLH-DSA SHAKE256-256f public key size (2 * N where N=32)
268                }
269            }
270
271            // Hash algorithms don't have keys
272            _ => {
273                return Err(crate::error::Error::InvalidAlgorithm {
274                    algorithm: "Algorithm does not use keys",
275                });
276            }
277        };
278
279        Ok(expected_size)
280    }
281
282    /// Get the expected ciphertext size for a given algorithm
283    ///
284    /// # Arguments
285    ///
286    /// * `algorithm` - The algorithm to get the ciphertext size for
287    ///
288    /// # Returns
289    ///
290    /// Returns the expected ciphertext size in bytes, or an error if the algorithm
291    /// doesn't produce ciphertext or is not supported.
292    pub fn get_expected_ciphertext_size(&self, algorithm: Algorithm) -> Result<usize> {
293        let expected_size = match algorithm {
294            Algorithm::MlKem512 => 768,   // ML-KEM-512 ciphertext size
295            Algorithm::MlKem768 => 1088,  // ML-KEM-768 ciphertext size
296            Algorithm::MlKem1024 => 1568, // ML-KEM-1024 ciphertext size
297
298            // CB-KEM algorithms
299            Algorithm::CbKem348864 => 96, // CB-KEM-348864 ciphertext size
300            Algorithm::CbKem460896 => 156, // CB-KEM-460896 ciphertext size
301            Algorithm::CbKem6688128 => 208, // CB-KEM-6688128 ciphertext size
302            Algorithm::CbKem6960119 => 194, // CB-KEM-6960119 ciphertext size
303            Algorithm::CbKem8192128 => 208, // CB-KEM-8192128 ciphertext size
304
305            Algorithm::Hqc128 => hqc::HQC128_CIPHERTEXT_BYTES,
306            Algorithm::Hqc192 => hqc::HQC192_CIPHERTEXT_BYTES,
307            Algorithm::Hqc256 => hqc::HQC256_CIPHERTEXT_BYTES,
308
309            _ => {
310                return Err(crate::error::Error::InvalidAlgorithm {
311                    algorithm: "Algorithm does not produce ciphertext",
312                });
313            }
314        };
315
316        Ok(expected_size)
317    }
318
319    /// Get the expected signature size for a given algorithm
320    ///
321    /// # Arguments
322    ///
323    /// * `algorithm` - The algorithm to get the signature size for
324    ///
325    /// # Returns
326    ///
327    /// Returns the expected signature size in bytes, or an error if the algorithm
328    /// doesn't produce signatures or is not supported.
329    pub fn get_expected_signature_size(&self, algorithm: Algorithm) -> Result<usize> {
330        let expected_size = match algorithm {
331            Algorithm::MlDsa44 => 2420,   // ML-DSA-44 signature size
332            Algorithm::MlDsa65 => 3309,   // ML-DSA-65 signature size
333            Algorithm::MlDsa87 => 4627,   // ML-DSA-87 signature size
334            Algorithm::FnDsa => 666,      // FN-DSA-512 signature size (logn=9)
335            Algorithm::FnDsa512 => 666,   // FN-DSA-512 signature size (logn=9)
336            Algorithm::FnDsa1024 => 1280, // FN-DSA-1024 signature size (logn=10)
337
338            // SLH-DSA signature sizes (actual sizes from implementation)
339            Algorithm::SlhDsaSha256128fRobust => 17088, // SLH-DSA SHA256-128f signature size
340            Algorithm::SlhDsaSha256192fRobust => 35664, // SLH-DSA SHA256-192f signature size
341            Algorithm::SlhDsaSha256256fRobust => 49856, // SLH-DSA SHA256-256f signature size
342            Algorithm::SlhDsaShake256128fRobust => 17088, // SLH-DSA SHAKE256-128f signature size
343            Algorithm::SlhDsaShake256192fRobust => 35664, // SLH-DSA SHAKE256-192f signature size
344            Algorithm::SlhDsaShake256256fRobust => 49856, // SLH-DSA SHAKE256-256f signature size
345
346            _ => {
347                return Err(crate::error::Error::InvalidAlgorithm {
348                    algorithm: "Algorithm does not produce signatures",
349                });
350            }
351        };
352
353        Ok(expected_size)
354    }
355
356    /// Set the maximum AEAD plaintext, ciphertext, or AAD size (bytes) for one operation.
357    pub fn set_max_aead_message_size(&mut self, max_size: usize) {
358        self.max_aead_message_size = max_size;
359    }
360
361    /// Set the maximum hash input and signature message size (bytes).
362    pub fn set_max_hash_message_size(&mut self, max_size: usize) {
363        self.max_hash_message_size = max_size;
364    }
365
366    /// Set the standard nonce size
367    ///
368    /// # Arguments
369    ///
370    /// * `nonce_size` - The standard nonce size in bytes
371    pub fn set_standard_nonce_size(&mut self, nonce_size: usize) {
372        self.standard_nonce_size = nonce_size;
373    }
374
375    /// Set the minimum randomness size
376    ///
377    /// # Arguments
378    ///
379    /// * `min_size` - The minimum randomness size in bytes
380    pub fn set_min_randomness_size(&mut self, min_size: usize) {
381        self.min_randomness_size = min_size;
382    }
383}
384
385#[cfg(test)]
386mod tests {
387    use lib_q_types::hqc;
388
389    use super::*;
390
391    #[test]
392    fn test_security_constants_creation() {
393        let constants = SecurityConstants::new();
394        assert_eq!(constants.max_aead_message_size(), 1024 * 1024);
395        assert_eq!(constants.max_hash_message_size(), usize::MAX);
396        assert_eq!(constants.standard_nonce_size(), 16);
397        assert_eq!(constants.min_randomness_size(), 32);
398    }
399
400    #[test]
401    fn test_get_expected_key_size() {
402        let constants = SecurityConstants::new();
403
404        // Test ML-KEM-512
405        let public_size = constants
406            .get_expected_key_size(Algorithm::MlKem512, false)
407            .unwrap();
408        assert_eq!(public_size, 800);
409
410        let secret_size = constants
411            .get_expected_key_size(Algorithm::MlKem512, true)
412            .unwrap();
413        assert_eq!(secret_size, 1632);
414
415        // Test ML-DSA-65
416        let public_size = constants
417            .get_expected_key_size(Algorithm::MlDsa65, false)
418            .unwrap();
419        assert_eq!(public_size, 1952);
420
421        let secret_size = constants
422            .get_expected_key_size(Algorithm::MlDsa65, true)
423            .unwrap();
424        assert_eq!(secret_size, 4032);
425
426        assert_eq!(
427            constants
428                .get_expected_key_size(Algorithm::Hqc128, false)
429                .unwrap(),
430            hqc::HQC128_PUBLIC_KEY_BYTES
431        );
432        assert_eq!(
433            constants
434                .get_expected_key_size(Algorithm::Hqc128, true)
435                .unwrap(),
436            hqc::HQC128_SECRET_KEY_BYTES
437        );
438
439        // Test hash algorithm (should fail)
440        let result = constants.get_expected_key_size(Algorithm::Sha3_256, false);
441        assert!(result.is_err(), "Hash algorithms should not have keys");
442    }
443
444    #[test]
445    fn test_get_expected_ciphertext_size() {
446        let constants = SecurityConstants::new();
447
448        // Test ML-KEM algorithms
449        assert_eq!(
450            constants
451                .get_expected_ciphertext_size(Algorithm::MlKem512)
452                .unwrap(),
453            768
454        );
455        assert_eq!(
456            constants
457                .get_expected_ciphertext_size(Algorithm::MlKem768)
458                .unwrap(),
459            1088
460        );
461        assert_eq!(
462            constants
463                .get_expected_ciphertext_size(Algorithm::MlKem1024)
464                .unwrap(),
465            1568
466        );
467
468        assert_eq!(
469            constants
470                .get_expected_ciphertext_size(Algorithm::Hqc128)
471                .unwrap(),
472            hqc::HQC128_CIPHERTEXT_BYTES
473        );
474        assert_eq!(
475            constants
476                .get_expected_ciphertext_size(Algorithm::Hqc192)
477                .unwrap(),
478            hqc::HQC192_CIPHERTEXT_BYTES
479        );
480        assert_eq!(
481            constants
482                .get_expected_ciphertext_size(Algorithm::Hqc256)
483                .unwrap(),
484            hqc::HQC256_CIPHERTEXT_BYTES
485        );
486
487        // Test non-KEM algorithm (should fail)
488        let result = constants.get_expected_ciphertext_size(Algorithm::Sha3_256);
489        assert!(
490            result.is_err(),
491            "Non-KEM algorithms should not produce ciphertext"
492        );
493    }
494
495    #[test]
496    fn test_get_expected_signature_size() {
497        let constants = SecurityConstants::new();
498
499        // Test ML-DSA algorithms
500        assert_eq!(
501            constants
502                .get_expected_signature_size(Algorithm::MlDsa44)
503                .unwrap(),
504            2420
505        );
506        assert_eq!(
507            constants
508                .get_expected_signature_size(Algorithm::MlDsa65)
509                .unwrap(),
510            3309
511        );
512        assert_eq!(
513            constants
514                .get_expected_signature_size(Algorithm::MlDsa87)
515                .unwrap(),
516            4627
517        );
518
519        // Test FN-DSA algorithms
520        assert_eq!(
521            constants
522                .get_expected_signature_size(Algorithm::FnDsa)
523                .unwrap(),
524            666
525        );
526        assert_eq!(
527            constants
528                .get_expected_signature_size(Algorithm::FnDsa512)
529                .unwrap(),
530            666
531        );
532        assert_eq!(
533            constants
534                .get_expected_signature_size(Algorithm::FnDsa1024)
535                .unwrap(),
536            1280
537        );
538
539        // Test non-signature algorithm (should fail)
540        let result = constants.get_expected_signature_size(Algorithm::Sha3_256);
541        assert!(
542            result.is_err(),
543            "Non-signature algorithms should not produce signatures"
544        );
545    }
546
547    #[test]
548    fn test_set_constants() {
549        let mut constants = SecurityConstants::new();
550
551        constants.set_max_aead_message_size(2048 * 1024);
552        assert_eq!(constants.max_aead_message_size(), 2048 * 1024);
553
554        constants.set_max_hash_message_size(4096);
555        assert_eq!(constants.max_hash_message_size(), 4096);
556
557        // Test setting nonce size
558        constants.set_standard_nonce_size(32);
559        assert_eq!(constants.standard_nonce_size(), 32);
560
561        // Test setting minimum randomness size
562        constants.set_min_randomness_size(64);
563        assert_eq!(constants.min_randomness_size(), 64);
564    }
565}