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