1#[cfg(feature = "alloc")]
7use alloc::string::ToString;
8
9use crate::error::Result;
10
11#[cfg(feature = "alloc")]
16#[derive(Clone)]
17pub struct EntropyValidator {
18 min_entropy_bits: usize,
19 enable_entropy_validation: bool,
20}
21
22#[cfg(feature = "alloc")]
23impl EntropyValidator {
24 pub fn new() -> Result<Self> {
34 Ok(Self {
35 min_entropy_bits: 128, enable_entropy_validation: true,
37 })
38 }
39
40 pub fn validate_key_entropy(&self, key_data: &[u8]) -> Result<()> {
53 if !self.enable_entropy_validation {
54 return Ok(());
55 }
56
57 #[cfg(feature = "relaxed_entropy_validation")]
59 {
60 self.validate_key_entropy_relaxed(key_data)
61 }
62
63 #[cfg(not(feature = "relaxed_entropy_validation"))]
65 {
66 self.validate_key_entropy_strict(key_data)
67 }
68 }
69
70 #[cfg(not(feature = "relaxed_entropy_validation"))]
77 fn validate_key_entropy_strict(&self, key_data: &[u8]) -> Result<()> {
78 let min_key_length = self.min_entropy_bits / 8;
79 if key_data.len() < min_key_length {
80 return Err(crate::error::Error::InvalidKeySize {
81 expected: min_key_length,
82 actual: key_data.len(),
83 });
84 }
85
86 if self.has_repeated_pattern(key_data) {
87 return Err(crate::error::Error::InvalidKey {
88 key_type: "key".to_string(),
89 reason: "Key contains repeated patterns indicating low entropy".to_string(),
90 });
91 }
92
93 if self.has_sequential_pattern(key_data) {
94 return Err(crate::error::Error::InvalidKey {
95 key_type: "key".to_string(),
96 reason: "Key contains sequential patterns indicating low entropy".to_string(),
97 });
98 }
99
100 if !self.has_sufficient_entropy(key_data) {
101 return Err(crate::error::Error::InvalidKey {
102 key_type: "key".to_string(),
103 reason: "Key does not have sufficient entropy".to_string(),
104 });
105 }
106
107 Ok(())
108 }
109
110 #[cfg(feature = "relaxed_entropy_validation")]
116 fn validate_key_entropy_relaxed(&self, key_data: &[u8]) -> Result<()> {
117 let min_key_length = 16; if key_data.len() < min_key_length {
120 return Err(crate::error::Error::InvalidKeySize {
121 expected: min_key_length,
122 actual: key_data.len(),
123 });
124 }
125
126 if key_data.iter().all(|&b| b == 0) {
128 return Err(crate::error::Error::InvalidKey {
129 key_type: "key".to_string(),
130 reason: "Key cannot be all zeros".to_string(),
131 });
132 }
133
134 if key_data.iter().all(|&b| b == 0xFF) {
135 return Err(crate::error::Error::InvalidKey {
136 key_type: "key".to_string(),
137 reason: "Key cannot be all ones".to_string(),
138 });
139 }
140
141 Ok(())
143 }
144
145 #[cfg(not(feature = "relaxed_entropy_validation"))]
153 fn has_repeated_pattern(&self, data: &[u8]) -> bool {
154 if data.len() < 8 {
155 return false;
156 }
157
158 let windows = data.len() - 3;
159 let threshold = if data.len() <= 256 {
160 2usize
161 } else {
162 windows / 8
163 };
164
165 let mut hits = 0usize;
166 for i in 0..windows {
167 let pattern = &data[i..i + 4];
168 for j in i + 4..windows {
169 if &data[j..j + 4] == pattern {
170 hits += 1;
171 if hits >= threshold {
172 return true;
173 }
174 }
175 }
176 }
177
178 false
179 }
180
181 #[cfg(not(feature = "relaxed_entropy_validation"))]
192 fn has_sequential_pattern(&self, data: &[u8]) -> bool {
193 if data.len() < 4 {
194 return false;
195 }
196
197 let windows = data.len() - 3;
198 let threshold = if data.len() <= 64 {
199 1usize
200 } else {
201 (windows / 20).max(4)
203 };
204
205 let mut hits = 0usize;
206
207 for i in 0..windows {
208 let ascending = data[i].wrapping_add(1) == data[i + 1] &&
209 data[i + 1].wrapping_add(1) == data[i + 2] &&
210 data[i + 2].wrapping_add(1) == data[i + 3];
211 let descending = data[i] == data[i + 1].wrapping_add(1) &&
212 data[i + 1] == data[i + 2].wrapping_add(1) &&
213 data[i + 2] == data[i + 3].wrapping_add(1);
214 if ascending || descending {
215 hits += 1;
216 if hits >= threshold {
217 return true;
218 }
219 }
220 }
221
222 false
223 }
224
225 #[cfg(not(feature = "relaxed_entropy_validation"))]
232 fn has_sufficient_entropy(&self, data: &[u8]) -> bool {
233 if data.len() < 16 {
234 return false;
235 }
236
237 let mut byte_counts = [0u32; 256];
238 for &byte in data {
239 byte_counts[byte as usize] += 1;
240 }
241
242 let unique_bytes = byte_counts.iter().filter(|&&c| c > 0).count();
243
244 if data.len() > 10240 {
245 unique_bytes >= 64
246 } else if data.len() <= 128 {
247 unique_bytes >= (data.len() * 2 / 5).max(4)
250 } else {
251 let required = (data.len() / 10).clamp(1, 64);
256 unique_bytes >= required
257 }
258 }
259
260 pub fn set_min_entropy_bits(&mut self, min_entropy_bits: usize) {
266 self.min_entropy_bits = min_entropy_bits;
267 }
268
269 pub fn min_entropy_bits(&self) -> usize {
275 self.min_entropy_bits
276 }
277
278 pub fn set_entropy_validation(&mut self, enabled: bool) {
284 self.enable_entropy_validation = enabled;
285 }
286
287 pub fn is_entropy_validation_enabled(&self) -> bool {
293 self.enable_entropy_validation
294 }
295}
296
297#[cfg(test)]
298mod tests {
299 use super::*;
300
301 #[test]
302 fn test_entropy_validator_creation() {
303 let validator = EntropyValidator::new();
304 assert!(
305 validator.is_ok(),
306 "EntropyValidator should be created successfully"
307 );
308 }
309
310 #[test]
311 fn test_validate_key_entropy_valid() {
312 let validator = EntropyValidator::new().unwrap();
313
314 let high_entropy_key = vec![
316 0xA3, 0x17, 0x5B, 0xE2, 0x94, 0x0D, 0x68, 0xF1, 0x3C, 0x86, 0xD5, 0x4A, 0x72, 0xBE,
317 0x09, 0xC7, 0x58, 0xE4, 0x1F, 0x8B, 0xA0, 0x63, 0xD9, 0x2E, 0x7D, 0x45, 0xFB, 0x16,
318 0xCA, 0x30, 0x9E, 0x54,
319 ];
320 let result = validator.validate_key_entropy(&high_entropy_key);
321 assert!(result.is_ok(), "Should accept high-entropy key");
322 }
323
324 #[test]
325 fn test_validate_key_entropy_too_short() {
326 let validator = EntropyValidator::new().unwrap();
327
328 let short_key = vec![1, 2, 3, 4];
329 let result = validator.validate_key_entropy(&short_key);
330 assert!(result.is_err(), "Should reject too short key");
331 }
332
333 #[cfg(not(feature = "relaxed_entropy_validation"))]
337 #[test]
338 fn test_validate_key_entropy_repeated_pattern() {
339 let validator = EntropyValidator::new().unwrap();
340
341 let repeated_key = vec![1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4];
342 let result = validator.validate_key_entropy(&repeated_key);
343 assert!(result.is_err(), "Should reject key with repeated patterns");
344 }
345
346 #[cfg(not(feature = "relaxed_entropy_validation"))]
350 #[test]
351 fn test_validate_key_entropy_sequential_pattern() {
352 let validator = EntropyValidator::new().unwrap();
353
354 let sequential_key: Vec<u8> = (1..=16).collect();
356 let result = validator.validate_key_entropy(&sequential_key);
357 assert!(
358 result.is_err(),
359 "Should reject short key that is entirely sequential"
360 );
361 }
362
363 #[cfg(not(feature = "relaxed_entropy_validation"))]
364 #[test]
365 fn test_isolated_sequential_run_in_large_key_is_accepted() {
366 let validator = EntropyValidator::new().unwrap();
367
368 let mut key = Vec::with_capacity(4032);
371 let mut state: u32 = 0xDEAD_BEEF;
372 while key.len() < 4032 {
373 state ^= state << 13;
374 state ^= state >> 17;
375 state ^= state << 5;
376 key.extend_from_slice(&state.to_le_bytes());
377 }
378 key.truncate(4032);
379
380 key[100] = 0x50;
382 key[101] = 0x51;
383 key[102] = 0x52;
384 key[103] = 0x53;
385
386 let result = validator.validate_key_entropy(&key);
387 assert!(
388 result.is_ok(),
389 "A single 4-byte ascending run in a 4032-byte key must not trigger rejection"
390 );
391 }
392
393 #[cfg(not(feature = "relaxed_entropy_validation"))]
394 #[test]
395 fn test_ml_dsa_sized_key_does_not_require_full_byte_alphabet() {
396 let validator = EntropyValidator::new().unwrap();
397
398 let pool: Vec<u8> = (0u8..80).collect();
402 let mut key = Vec::with_capacity(4032);
403 let mut state: u32 = 0xC0FFEE42;
404 while key.len() < 4032 {
405 state ^= state << 13;
406 state ^= state >> 17;
407 state ^= state << 5;
408 let b = state.to_le_bytes()[0];
409 key.push(pool[(b as usize) % pool.len()]);
410 }
411
412 let mut seen = [false; 256];
413 let mut unique = 0usize;
414 for &b in &key {
415 if !seen[b as usize] {
416 seen[b as usize] = true;
417 unique += 1;
418 }
419 }
420 assert!(
421 unique < 256,
422 "fixture should use fewer than 256 byte values (got {unique})"
423 );
424
425 let result = validator.validate_key_entropy(&key);
426 assert!(
427 result.is_ok(),
428 "structured PQ-sized key with modest byte diversity must be accepted: {result:?}"
429 );
430 }
431
432 #[cfg(not(feature = "relaxed_entropy_validation"))]
433 #[test]
434 fn test_ml_kem_1024_sized_key_does_not_require_full_byte_alphabet() {
435 let validator = EntropyValidator::new().unwrap();
436
437 let pool: Vec<u8> = (0u8..200).collect();
440 let mut key = Vec::with_capacity(3168);
441 let mut state: u32 = 0x514B_3000;
442 while key.len() < 3168 {
443 state ^= state << 13;
444 state ^= state >> 17;
445 state ^= state << 5;
446 let b = state.to_le_bytes()[0];
447 key.push(pool[(b as usize) % pool.len()]);
448 }
449
450 let mut seen = [false; 256];
451 let mut unique = 0usize;
452 for &b in &key {
453 if !seen[b as usize] {
454 seen[b as usize] = true;
455 unique += 1;
456 }
457 }
458 assert!(
459 unique < 256,
460 "fixture should use fewer than 256 byte values (got {unique})"
461 );
462
463 let result = validator.validate_key_entropy(&key);
464 assert!(
465 result.is_ok(),
466 "ML-KEM-1024-sized structured key must be accepted: {result:?}"
467 );
468 }
469
470 #[cfg(not(feature = "relaxed_entropy_validation"))]
471 #[test]
472 fn test_massively_sequential_key_rejected() {
473 let validator = EntropyValidator::new().unwrap();
474
475 let key: Vec<u8> = (0..=255).collect();
477 let result = validator.validate_key_entropy(&key);
478 assert!(
479 result.is_err(),
480 "Key that is entirely sequential should be rejected"
481 );
482 }
483
484 #[test]
485 fn test_entropy_validation_control() {
486 let mut validator = EntropyValidator::new().unwrap();
487
488 assert!(
489 validator.is_entropy_validation_enabled(),
490 "Entropy validation should be enabled by default"
491 );
492 assert_eq!(
493 validator.min_entropy_bits(),
494 128,
495 "Default minimum entropy should be 128 bits"
496 );
497
498 validator.set_entropy_validation(false);
499 assert!(
500 !validator.is_entropy_validation_enabled(),
501 "Entropy validation should be disabled"
502 );
503
504 validator.set_entropy_validation(true);
505 assert!(
506 validator.is_entropy_validation_enabled(),
507 "Entropy validation should be enabled"
508 );
509
510 validator.set_min_entropy_bits(256);
511 assert_eq!(
512 validator.min_entropy_bits(),
513 256,
514 "Minimum entropy should be updated"
515 );
516 }
517}