Skip to main content

lib_q_aead/security/
validation.rs

1//! Input validation and sanitization
2//!
3//! This module provides comprehensive input validation functions to ensure
4//! that all inputs to cryptographic operations are safe and valid.
5
6use lib_q_core::{
7    Error,
8    Result,
9};
10
11/// Input validation configuration
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub struct ValidationConfig {
14    /// Maximum key size in bytes
15    pub max_key_size: usize,
16    /// Maximum nonce size in bytes
17    pub max_nonce_size: usize,
18    /// Maximum plaintext size in bytes
19    pub max_plaintext_size: usize,
20    /// Maximum ciphertext size in bytes
21    pub max_ciphertext_size: usize,
22    /// Maximum associated data size in bytes
23    pub max_associated_data_size: usize,
24    /// Enable strict validation (reject potentially dangerous inputs)
25    pub strict: bool,
26    /// Enable entropy validation for keys
27    pub validate_key_entropy: bool,
28    /// Enable nonce uniqueness validation
29    pub validate_nonce_uniqueness: bool,
30}
31
32impl Default for ValidationConfig {
33    fn default() -> Self {
34        Self {
35            max_key_size: 1024,                      // 1KB
36            max_nonce_size: 256,                     // 256 bytes
37            max_plaintext_size: 1024 * 1024,         // 1MB
38            max_ciphertext_size: 1024 * 1024 + 1024, // 1MB + overhead
39            max_associated_data_size: 1024 * 1024,   // 1MB
40            strict: true,
41            validate_key_entropy: true,
42            validate_nonce_uniqueness: false, // Disabled by default as it requires state
43        }
44    }
45}
46
47impl ValidationConfig {
48    /// Create a strict validation configuration
49    pub fn strict() -> Self {
50        Self {
51            max_key_size: 512,
52            max_nonce_size: 128,
53            max_plaintext_size: 512 * 1024, // 512KB
54            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    /// Create a permissive validation configuration
63    pub fn permissive() -> Self {
64        Self {
65            max_key_size: 2048,
66            max_nonce_size: 512,
67            max_plaintext_size: 10 * 1024 * 1024, // 10MB
68            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/// Input validator
78#[derive(Clone)]
79pub struct InputValidator {
80    config: ValidationConfig,
81}
82
83impl InputValidator {
84    /// Create a new input validator with default configuration
85    pub fn new() -> Self {
86        Self {
87            config: ValidationConfig::default(),
88        }
89    }
90
91    /// Create a new input validator with custom configuration
92    pub fn with_config(config: ValidationConfig) -> Self {
93        Self { config }
94    }
95
96    /// Validate a key
97    pub fn validate_key(&self, key: &[u8]) -> Result<()> {
98        // Check key size
99        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        // Check for zero key
114        if key.iter().all(|&b| b == 0) {
115            return Err(Error::InvalidKeyFormat);
116        }
117
118        // Check for all-ones key
119        if key.iter().all(|&b| b == 0xFF) {
120            return Err(Error::InvalidKeyFormat);
121        }
122
123        // Check for repeated patterns
124        if self.config.strict && self.has_repeated_pattern(key) {
125            return Err(Error::InvalidKeyFormat);
126        }
127
128        // Validate entropy if enabled
129        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    /// Validate a nonce
140    pub fn validate_nonce(&self, nonce: &[u8]) -> Result<()> {
141        // Check nonce size
142        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        // Check for zero nonce
157        if nonce.iter().all(|&b| b == 0) {
158            return Err(Error::InvalidNonceSize {
159                expected: 1,
160                actual: 0,
161            });
162        }
163
164        // Check for all-ones nonce
165        if nonce.iter().all(|&b| b == 0xFF) {
166            return Err(Error::InvalidNonceSize {
167                expected: 1,
168                actual: 0,
169            });
170        }
171
172        // Check for repeated patterns
173        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    /// Validate plaintext
184    pub fn validate_plaintext(&self, plaintext: &[u8]) -> Result<()> {
185        // Check plaintext size
186        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        // Check for suspicious patterns in strict mode
194        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    /// Validate ciphertext
205    pub fn validate_ciphertext(&self, ciphertext: &[u8]) -> Result<()> {
206        // Check ciphertext size
207        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    /// Validate associated data
225    pub fn validate_associated_data(&self, associated_data: &[u8]) -> Result<()> {
226        // Check associated data size
227        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    /// Validate key size for a specific algorithm
238    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    /// Validate nonce size for a specific algorithm
257    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    /// Check if data has repeated patterns
276    fn has_repeated_pattern(&self, data: &[u8]) -> bool {
277        if data.len() < 4 {
278            return false;
279        }
280
281        // Check for simple repeated patterns
282        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    /// Check if data has sufficient entropy
306    fn has_sufficient_entropy(&self, data: &[u8]) -> bool {
307        if data.len() < 16 {
308            return false;
309        }
310
311        // Simple entropy check: count unique bytes
312        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        // Require at least 50% unique bytes for good entropy
320        unique_bytes >= data.len() / 2
321    }
322
323    /// Check if data has suspicious patterns
324    fn has_suspicious_pattern(&self, data: &[u8]) -> bool {
325        // Check for common attack patterns individually
326        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/// Global input validator with thread-safe access
350#[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
363/// Get the global input validator
364pub 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
379/// Set the global input validator
380pub 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
397/// Convenience functions for global validation
398/// Validate a key using the global validator
399pub fn validate_key(key: &[u8]) -> Result<()> {
400    get_input_validator().validate_key(key)
401}
402
403/// Validate a nonce using the global validator
404pub fn validate_nonce(nonce: &[u8]) -> Result<()> {
405    get_input_validator().validate_nonce(nonce)
406}
407
408/// Validate plaintext using the global validator
409pub fn validate_plaintext(plaintext: &[u8]) -> Result<()> {
410    get_input_validator().validate_plaintext(plaintext)
411}
412
413/// Validate ciphertext using the global validator
414pub fn validate_ciphertext(ciphertext: &[u8]) -> Result<()> {
415    get_input_validator().validate_ciphertext(ciphertext)
416}
417
418/// Validate associated data using the global validator
419pub fn validate_associated_data(associated_data: &[u8]) -> Result<()> {
420    get_input_validator().validate_associated_data(associated_data)
421}
422
423/// Validate key size using the global validator
424pub fn validate_key_size(key_size: usize, expected_size: usize) -> Result<()> {
425    get_input_validator().validate_key_size(key_size, expected_size)
426}
427
428/// Validate nonce size using the global validator
429pub 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        // High entropy data
577        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        // Low entropy data
581        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}