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}