1use lib_q_core::{
7 Error,
8 Result,
9};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub struct ValidationConfig {
14 pub max_key_size: usize,
16 pub max_nonce_size: usize,
18 pub max_plaintext_size: usize,
20 pub max_ciphertext_size: usize,
22 pub max_associated_data_size: usize,
24 pub strict: bool,
26 pub validate_key_entropy: bool,
28 pub validate_nonce_uniqueness: bool,
30}
31
32impl Default for ValidationConfig {
33 fn default() -> Self {
34 Self {
35 max_key_size: 1024, max_nonce_size: 256, max_plaintext_size: 1024 * 1024, max_ciphertext_size: 1024 * 1024 + 1024, max_associated_data_size: 1024 * 1024, strict: true,
41 validate_key_entropy: true,
42 validate_nonce_uniqueness: false, }
44 }
45}
46
47impl ValidationConfig {
48 pub fn strict() -> Self {
50 Self {
51 max_key_size: 512,
52 max_nonce_size: 128,
53 max_plaintext_size: 512 * 1024, max_ciphertext_size: 512 * 1024 + 1024,
55 max_associated_data_size: 512 * 1024,
56 strict: true,
57 validate_key_entropy: true,
58 validate_nonce_uniqueness: true,
59 }
60 }
61
62 pub fn permissive() -> Self {
64 Self {
65 max_key_size: 2048,
66 max_nonce_size: 512,
67 max_plaintext_size: 10 * 1024 * 1024, max_ciphertext_size: 10 * 1024 * 1024 + 2048,
69 max_associated_data_size: 10 * 1024 * 1024,
70 strict: false,
71 validate_key_entropy: false,
72 validate_nonce_uniqueness: false,
73 }
74 }
75}
76
77#[derive(Clone)]
79pub struct InputValidator {
80 config: ValidationConfig,
81}
82
83impl InputValidator {
84 pub fn new() -> Self {
86 Self {
87 config: ValidationConfig::default(),
88 }
89 }
90
91 pub fn with_config(config: ValidationConfig) -> Self {
93 Self { config }
94 }
95
96 pub fn validate_key(&self, key: &[u8]) -> Result<()> {
98 if key.is_empty() {
100 return Err(Error::InvalidKeySize {
101 expected: 1,
102 actual: 0,
103 });
104 }
105
106 if key.len() > self.config.max_key_size {
107 return Err(Error::InvalidKeySize {
108 expected: self.config.max_key_size,
109 actual: key.len(),
110 });
111 }
112
113 if key.iter().all(|&b| b == 0) {
115 return Err(Error::InvalidKeyFormat);
116 }
117
118 if key.iter().all(|&b| b == 0xFF) {
120 return Err(Error::InvalidKeyFormat);
121 }
122
123 if self.config.strict && self.has_repeated_pattern(key) {
125 return Err(Error::InvalidKeyFormat);
126 }
127
128 if self.config.validate_key_entropy &&
130 self.config.strict &&
131 !self.has_sufficient_entropy(key)
132 {
133 return Err(Error::InvalidKeyFormat);
134 }
135
136 Ok(())
137 }
138
139 pub fn validate_nonce(&self, nonce: &[u8]) -> Result<()> {
141 if nonce.is_empty() {
143 return Err(Error::InvalidNonceSize {
144 expected: 1,
145 actual: 0,
146 });
147 }
148
149 if nonce.len() > self.config.max_nonce_size {
150 return Err(Error::InvalidNonceSize {
151 expected: self.config.max_nonce_size,
152 actual: nonce.len(),
153 });
154 }
155
156 if nonce.iter().all(|&b| b == 0) {
158 return Err(Error::InvalidNonceSize {
159 expected: 1,
160 actual: 0,
161 });
162 }
163
164 if nonce.iter().all(|&b| b == 0xFF) {
166 return Err(Error::InvalidNonceSize {
167 expected: 1,
168 actual: 0,
169 });
170 }
171
172 if self.config.strict && self.has_repeated_pattern(nonce) {
174 return Err(Error::InvalidNonceSize {
175 expected: 1,
176 actual: 0,
177 });
178 }
179
180 Ok(())
181 }
182
183 pub fn validate_plaintext(&self, plaintext: &[u8]) -> Result<()> {
185 if plaintext.len() > self.config.max_plaintext_size {
187 return Err(Error::InvalidPlaintextSize {
188 expected: self.config.max_plaintext_size,
189 actual: plaintext.len(),
190 });
191 }
192
193 if self.config.strict && self.has_suspicious_pattern(plaintext) {
195 return Err(Error::InvalidPlaintextSize {
196 expected: 0,
197 actual: plaintext.len(),
198 });
199 }
200
201 Ok(())
202 }
203
204 pub fn validate_ciphertext(&self, ciphertext: &[u8]) -> Result<()> {
206 if ciphertext.is_empty() {
208 return Err(Error::InvalidCiphertextSize {
209 expected: 1,
210 actual: 0,
211 });
212 }
213
214 if ciphertext.len() > self.config.max_ciphertext_size {
215 return Err(Error::InvalidCiphertextSize {
216 expected: self.config.max_ciphertext_size,
217 actual: ciphertext.len(),
218 });
219 }
220
221 Ok(())
222 }
223
224 pub fn validate_associated_data(&self, associated_data: &[u8]) -> Result<()> {
226 if associated_data.len() > self.config.max_associated_data_size {
228 return Err(Error::InvalidMessageSize {
229 max: self.config.max_associated_data_size,
230 actual: associated_data.len(),
231 });
232 }
233
234 Ok(())
235 }
236
237 pub fn validate_key_size(&self, key_size: usize, expected_size: usize) -> Result<()> {
239 if key_size != expected_size {
240 return Err(Error::InvalidKeySize {
241 expected: expected_size,
242 actual: key_size,
243 });
244 }
245
246 if key_size > self.config.max_key_size {
247 return Err(Error::InvalidKeySize {
248 expected: self.config.max_key_size,
249 actual: key_size,
250 });
251 }
252
253 Ok(())
254 }
255
256 pub fn validate_nonce_size(&self, nonce_size: usize, expected_size: usize) -> Result<()> {
258 if nonce_size != expected_size {
259 return Err(Error::InvalidNonceSize {
260 expected: expected_size,
261 actual: nonce_size,
262 });
263 }
264
265 if nonce_size > self.config.max_nonce_size {
266 return Err(Error::InvalidNonceSize {
267 expected: self.config.max_nonce_size,
268 actual: nonce_size,
269 });
270 }
271
272 Ok(())
273 }
274
275 fn has_repeated_pattern(&self, data: &[u8]) -> bool {
277 if data.len() < 4 {
278 return false;
279 }
280
281 for pattern_len in 1..=data.len() / 2 {
283 if !data.len().is_multiple_of(pattern_len) {
284 continue;
285 }
286
287 let pattern = &data[0..pattern_len];
288 let mut is_repeated = true;
289
290 for chunk in data.chunks(pattern_len) {
291 if chunk != pattern {
292 is_repeated = false;
293 break;
294 }
295 }
296
297 if is_repeated {
298 return true;
299 }
300 }
301
302 false
303 }
304
305 fn has_sufficient_entropy(&self, data: &[u8]) -> bool {
307 if data.len() < 16 {
308 return false;
309 }
310
311 let mut byte_counts = [0u8; 256];
313 for &byte in data {
314 byte_counts[byte as usize] = byte_counts[byte as usize].saturating_add(1);
315 }
316
317 let unique_bytes = byte_counts.iter().filter(|&&count| count > 0).count();
318
319 unique_bytes >= data.len() / 2
321 }
322
323 fn has_suspicious_pattern(&self, data: &[u8]) -> bool {
325 if data.windows(7).any(|window| window == b"<script") {
327 return true;
328 }
329 if data.windows(7).any(|window| window == b"onload=") {
330 return true;
331 }
332 if data.windows(8).any(|window| window == b"onerror=") {
333 return true;
334 }
335 if data.windows(5).any(|window| window == b"eval(") {
336 return true;
337 }
338
339 false
340 }
341}
342
343impl Default for InputValidator {
344 fn default() -> Self {
345 Self::new()
346 }
347}
348
349#[cfg(feature = "std")]
351use std::sync::{
352 Arc,
353 RwLock,
354};
355
356#[cfg(feature = "std")]
357static GLOBAL_VALIDATOR: std::sync::OnceLock<Arc<RwLock<InputValidator>>> =
358 std::sync::OnceLock::new();
359#[cfg(not(feature = "std"))]
360static GLOBAL_VALIDATOR: once_cell::sync::Lazy<spin::Mutex<InputValidator>> =
361 once_cell::sync::Lazy::new(|| spin::Mutex::new(InputValidator::new()));
362
363pub fn get_input_validator() -> InputValidator {
365 #[cfg(feature = "std")]
366 {
367 GLOBAL_VALIDATOR
368 .get_or_init(|| Arc::new(RwLock::new(InputValidator::new())))
369 .read()
370 .map(|guard| (*guard).clone())
371 .unwrap_or_else(|_| InputValidator::new())
372 }
373 #[cfg(not(feature = "std"))]
374 {
375 GLOBAL_VALIDATOR.lock().clone()
376 }
377}
378
379pub fn set_input_validator(validator: InputValidator) {
381 #[cfg(feature = "std")]
382 {
383 if let Some(global_validator) = GLOBAL_VALIDATOR.get() {
384 if let Ok(mut global) = global_validator.write() {
385 *global = validator;
386 }
387 } else {
388 let _ = GLOBAL_VALIDATOR.set(Arc::new(RwLock::new(validator)));
389 }
390 }
391 #[cfg(not(feature = "std"))]
392 {
393 *GLOBAL_VALIDATOR.lock() = validator;
394 }
395}
396
397pub fn validate_key(key: &[u8]) -> Result<()> {
400 get_input_validator().validate_key(key)
401}
402
403pub fn validate_nonce(nonce: &[u8]) -> Result<()> {
405 get_input_validator().validate_nonce(nonce)
406}
407
408pub fn validate_plaintext(plaintext: &[u8]) -> Result<()> {
410 get_input_validator().validate_plaintext(plaintext)
411}
412
413pub fn validate_ciphertext(ciphertext: &[u8]) -> Result<()> {
415 get_input_validator().validate_ciphertext(ciphertext)
416}
417
418pub fn validate_associated_data(associated_data: &[u8]) -> Result<()> {
420 get_input_validator().validate_associated_data(associated_data)
421}
422
423pub fn validate_key_size(key_size: usize, expected_size: usize) -> Result<()> {
425 get_input_validator().validate_key_size(key_size, expected_size)
426}
427
428pub fn validate_nonce_size(nonce_size: usize, expected_size: usize) -> Result<()> {
430 get_input_validator().validate_nonce_size(nonce_size, expected_size)
431}
432
433#[cfg(test)]
434mod tests {
435 use super::*;
436
437 #[test]
438 fn test_validation_config_defaults() {
439 let config = ValidationConfig::default();
440 assert_eq!(config.max_key_size, 1024);
441 assert_eq!(config.max_nonce_size, 256);
442 assert_eq!(config.max_plaintext_size, 1024 * 1024);
443 assert!(config.strict);
444 assert!(config.validate_key_entropy);
445 }
446
447 #[test]
448 fn test_validation_config_strict() {
449 let config = ValidationConfig::strict();
450 assert_eq!(config.max_key_size, 512);
451 assert_eq!(config.max_nonce_size, 128);
452 assert_eq!(config.max_plaintext_size, 512 * 1024);
453 assert!(config.strict);
454 assert!(config.validate_key_entropy);
455 assert!(config.validate_nonce_uniqueness);
456 }
457
458 #[test]
459 fn test_validation_config_permissive() {
460 let config = ValidationConfig::permissive();
461 assert_eq!(config.max_key_size, 2048);
462 assert_eq!(config.max_nonce_size, 512);
463 assert_eq!(config.max_plaintext_size, 10 * 1024 * 1024);
464 assert!(!config.strict);
465 assert!(!config.validate_key_entropy);
466 assert!(!config.validate_nonce_uniqueness);
467 }
468
469 #[test]
470 fn test_input_validator_creation() {
471 let validator = InputValidator::new();
472 assert!(validator.config.strict);
473 }
474
475 #[test]
476 fn test_validate_key_empty() {
477 let validator = InputValidator::new();
478 assert!(validator.validate_key(&[]).is_err());
479 }
480
481 #[test]
482 fn test_validate_key_zero() {
483 let validator = InputValidator::new();
484 assert!(validator.validate_key(&[0, 0, 0, 0]).is_err());
485 }
486
487 #[test]
488 fn test_validate_key_all_ones() {
489 let validator = InputValidator::new();
490 assert!(validator.validate_key(&[0xFF, 0xFF, 0xFF, 0xFF]).is_err());
491 }
492
493 #[test]
494 fn test_validate_key_repeated_pattern() {
495 let validator = InputValidator::new();
496 assert!(validator.validate_key(&[1, 2, 1, 2, 1, 2]).is_err());
497 }
498
499 #[test]
500 fn test_validate_key_valid() {
501 let validator = InputValidator::new();
502 let key = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
503 assert!(validator.validate_key(&key).is_ok());
504 }
505
506 #[test]
507 fn test_validate_nonce_empty() {
508 let validator = InputValidator::new();
509 assert!(validator.validate_nonce(&[]).is_err());
510 }
511
512 #[test]
513 fn test_validate_nonce_zero() {
514 let validator = InputValidator::new();
515 assert!(validator.validate_nonce(&[0, 0, 0, 0]).is_err());
516 }
517
518 #[test]
519 fn test_validate_nonce_valid() {
520 let validator = InputValidator::new();
521 let nonce = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
522 assert!(validator.validate_nonce(&nonce).is_ok());
523 }
524
525 #[test]
526 fn test_validate_plaintext_valid() {
527 let validator = InputValidator::new();
528 let plaintext = b"Hello, World!";
529 assert!(validator.validate_plaintext(plaintext).is_ok());
530 }
531
532 #[test]
533 fn test_validate_ciphertext_empty() {
534 let validator = InputValidator::new();
535 assert!(validator.validate_ciphertext(&[]).is_err());
536 }
537
538 #[test]
539 fn test_validate_ciphertext_valid() {
540 let validator = InputValidator::new();
541 let ciphertext = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
542 assert!(validator.validate_ciphertext(&ciphertext).is_ok());
543 }
544
545 #[test]
546 fn test_validate_associated_data_valid() {
547 let validator = InputValidator::new();
548 let associated_data = b"metadata";
549 assert!(validator.validate_associated_data(associated_data).is_ok());
550 }
551
552 #[test]
553 fn test_validate_key_size() {
554 let validator = InputValidator::new();
555 assert!(validator.validate_key_size(32, 32).is_ok());
556 assert!(validator.validate_key_size(16, 32).is_err());
557 }
558
559 #[test]
560 fn test_validate_nonce_size() {
561 let validator = InputValidator::new();
562 assert!(validator.validate_nonce_size(16, 16).is_ok());
563 assert!(validator.validate_nonce_size(12, 16).is_err());
564 }
565
566 #[test]
567 fn test_has_repeated_pattern() {
568 let validator = InputValidator::new();
569 assert!(validator.has_repeated_pattern(&[1, 2, 1, 2, 1, 2]));
570 assert!(!validator.has_repeated_pattern(&[1, 2, 3, 4, 5, 6]));
571 }
572
573 #[test]
574 fn test_has_sufficient_entropy() {
575 let validator = InputValidator::new();
576 let high_entropy = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
578 assert!(validator.has_sufficient_entropy(&high_entropy));
579
580 let low_entropy = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1];
582 assert!(!validator.has_sufficient_entropy(&low_entropy));
583 }
584
585 #[test]
586 fn test_has_suspicious_pattern() {
587 let validator = InputValidator::new();
588 assert!(validator.has_suspicious_pattern(b"<script>alert('xss')</script>"));
589 assert!(!validator.has_suspicious_pattern(b"Hello, World!"));
590 }
591
592 #[test]
593 fn test_global_validation_functions() {
594 let key = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
595 let nonce = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
596 let plaintext = b"Hello, World!";
597 let ciphertext = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
598 let associated_data = b"metadata";
599
600 assert!(validate_key(&key).is_ok());
601 assert!(validate_nonce(&nonce).is_ok());
602 assert!(validate_plaintext(plaintext).is_ok());
603 assert!(validate_ciphertext(&ciphertext).is_ok());
604 assert!(validate_associated_data(associated_data).is_ok());
605 assert!(validate_key_size(32, 32).is_ok());
606 assert!(validate_nonce_size(16, 16).is_ok());
607 }
608}