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 #[test]
334 fn test_validate_key_entropy_repeated_pattern() {
335 let validator = EntropyValidator::new().unwrap();
336
337 let repeated_key = vec![1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4];
338 let result = validator.validate_key_entropy(&repeated_key);
339 assert!(result.is_err(), "Should reject key with repeated patterns");
340 }
341
342 #[test]
343 fn test_validate_key_entropy_sequential_pattern() {
344 let validator = EntropyValidator::new().unwrap();
345
346 let sequential_key: Vec<u8> = (1..=16).collect();
348 let result = validator.validate_key_entropy(&sequential_key);
349 assert!(
350 result.is_err(),
351 "Should reject short key that is entirely sequential"
352 );
353 }
354
355 #[cfg(not(feature = "relaxed_entropy_validation"))]
356 #[test]
357 fn test_isolated_sequential_run_in_large_key_is_accepted() {
358 let validator = EntropyValidator::new().unwrap();
359
360 let mut key = Vec::with_capacity(4032);
363 let mut state: u32 = 0xDEAD_BEEF;
364 while key.len() < 4032 {
365 state ^= state << 13;
366 state ^= state >> 17;
367 state ^= state << 5;
368 key.extend_from_slice(&state.to_le_bytes());
369 }
370 key.truncate(4032);
371
372 key[100] = 0x50;
374 key[101] = 0x51;
375 key[102] = 0x52;
376 key[103] = 0x53;
377
378 let result = validator.validate_key_entropy(&key);
379 assert!(
380 result.is_ok(),
381 "A single 4-byte ascending run in a 4032-byte key must not trigger rejection"
382 );
383 }
384
385 #[cfg(not(feature = "relaxed_entropy_validation"))]
386 #[test]
387 fn test_ml_dsa_sized_key_does_not_require_full_byte_alphabet() {
388 let validator = EntropyValidator::new().unwrap();
389
390 let pool: Vec<u8> = (0u8..80).collect();
394 let mut key = Vec::with_capacity(4032);
395 let mut state: u32 = 0xC0FFEE42;
396 while key.len() < 4032 {
397 state ^= state << 13;
398 state ^= state >> 17;
399 state ^= state << 5;
400 let b = state.to_le_bytes()[0];
401 key.push(pool[(b as usize) % pool.len()]);
402 }
403
404 let mut seen = [false; 256];
405 let mut unique = 0usize;
406 for &b in &key {
407 if !seen[b as usize] {
408 seen[b as usize] = true;
409 unique += 1;
410 }
411 }
412 assert!(
413 unique < 256,
414 "fixture should use fewer than 256 byte values (got {unique})"
415 );
416
417 let result = validator.validate_key_entropy(&key);
418 assert!(
419 result.is_ok(),
420 "structured PQ-sized key with modest byte diversity must be accepted: {result:?}"
421 );
422 }
423
424 #[cfg(not(feature = "relaxed_entropy_validation"))]
425 #[test]
426 fn test_ml_kem_1024_sized_key_does_not_require_full_byte_alphabet() {
427 let validator = EntropyValidator::new().unwrap();
428
429 let pool: Vec<u8> = (0u8..200).collect();
432 let mut key = Vec::with_capacity(3168);
433 let mut state: u32 = 0x514B_3000;
434 while key.len() < 3168 {
435 state ^= state << 13;
436 state ^= state >> 17;
437 state ^= state << 5;
438 let b = state.to_le_bytes()[0];
439 key.push(pool[(b as usize) % pool.len()]);
440 }
441
442 let mut seen = [false; 256];
443 let mut unique = 0usize;
444 for &b in &key {
445 if !seen[b as usize] {
446 seen[b as usize] = true;
447 unique += 1;
448 }
449 }
450 assert!(
451 unique < 256,
452 "fixture should use fewer than 256 byte values (got {unique})"
453 );
454
455 let result = validator.validate_key_entropy(&key);
456 assert!(
457 result.is_ok(),
458 "ML-KEM-1024-sized structured key must be accepted: {result:?}"
459 );
460 }
461
462 #[cfg(not(feature = "relaxed_entropy_validation"))]
463 #[test]
464 fn test_massively_sequential_key_rejected() {
465 let validator = EntropyValidator::new().unwrap();
466
467 let key: Vec<u8> = (0..=255).collect();
469 let result = validator.validate_key_entropy(&key);
470 assert!(
471 result.is_err(),
472 "Key that is entirely sequential should be rejected"
473 );
474 }
475
476 #[test]
477 fn test_entropy_validation_control() {
478 let mut validator = EntropyValidator::new().unwrap();
479
480 assert!(
481 validator.is_entropy_validation_enabled(),
482 "Entropy validation should be enabled by default"
483 );
484 assert_eq!(
485 validator.min_entropy_bits(),
486 128,
487 "Default minimum entropy should be 128 bits"
488 );
489
490 validator.set_entropy_validation(false);
491 assert!(
492 !validator.is_entropy_validation_enabled(),
493 "Entropy validation should be disabled"
494 );
495
496 validator.set_entropy_validation(true);
497 assert!(
498 validator.is_entropy_validation_enabled(),
499 "Entropy validation should be enabled"
500 );
501
502 validator.set_min_entropy_bits(256);
503 assert_eq!(
504 validator.min_entropy_bits(),
505 256,
506 "Minimum entropy should be updated"
507 );
508 }
509}