lib-q-core 0.0.5

Core types and traits for lib-Q post-quantum cryptography library
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
//! Centralized security validation for lib-Q
//!
//! This module provides the SecurityValidator that implements comprehensive
//! security validation for all cryptographic operations.

#[cfg(feature = "alloc")]
use alloc::string::ToString;

use super::{
    EntropyValidator,
    SecurityConstants,
    TimingValidator,
};
use crate::api::{
    Algorithm,
    AlgorithmCategory,
};
use crate::error::Result;

/// Centralized security validator for lib-Q
///
/// This validator provides comprehensive security validation for all cryptographic
/// operations, including algorithm validation, key validation, timing attack prevention,
/// and entropy validation.
#[cfg(feature = "alloc")]
#[derive(Clone)]
pub struct SecurityValidator {
    timing_validator: TimingValidator,
    entropy_validator: EntropyValidator,
    constants: SecurityConstants,
}

#[cfg(feature = "alloc")]
impl SecurityValidator {
    /// Create a new security validator
    ///
    /// # Returns
    ///
    /// A new instance of SecurityValidator with all validation components initialized.
    ///
    /// # Errors
    ///
    /// Returns an error if any validation component fails to initialize.
    pub fn new() -> Result<Self> {
        Ok(Self {
            timing_validator: TimingValidator::new()?,
            entropy_validator: EntropyValidator::new()?,
            constants: SecurityConstants::new(),
        })
    }

    /// Validate that an algorithm is appropriate for the given operation category
    ///
    /// # Arguments
    ///
    /// * `algorithm` - The algorithm to validate
    /// * `expected_category` - The expected algorithm category
    ///
    /// # Returns
    ///
    /// Returns `Ok(())` if the algorithm supports the category, or an error if it doesn't.
    pub fn validate_algorithm_category(
        &self,
        algorithm: Algorithm,
        expected_category: AlgorithmCategory,
    ) -> Result<()> {
        if !algorithm.supports_category(expected_category) {
            return Err(crate::error::Error::InvalidAlgorithm {
                algorithm: "Algorithm category mismatch",
            });
        }
        Ok(())
    }

    /// Validate key size for a given algorithm
    ///
    /// # Arguments
    ///
    /// * `algorithm` - The algorithm to validate against
    /// * `key_data` - The key data to validate
    /// * `is_secret` - Whether this is a secret key (affects expected size)
    ///
    /// # Returns
    ///
    /// Returns `Ok(())` if the key size is correct, or an error if it's not.
    pub fn validate_key_size(
        &self,
        algorithm: Algorithm,
        key_data: &[u8],
        is_secret: bool,
    ) -> Result<()> {
        let expected_size = self.constants.get_expected_key_size(algorithm, is_secret)?;

        if key_data.len() != expected_size {
            return Err(crate::error::Error::InvalidKeySize {
                expected: expected_size,
                actual: key_data.len(),
            });
        }

        Ok(())
    }

    fn ensure_non_trivial_key_bytes(key_data: &[u8]) -> Result<()> {
        if key_data.is_empty() {
            return Err(crate::error::Error::InvalidKeySize {
                expected: 1,
                actual: 0,
            });
        }

        if key_data.iter().all(|&b| b == 0) {
            return Err(crate::error::Error::InvalidKey {
                key_type: "key".to_string(),
                reason: "Key material cannot be all zeros".to_string(),
            });
        }

        if key_data.iter().all(|&b| b == 0xFF) {
            return Err(crate::error::Error::InvalidKey {
                key_type: "key".to_string(),
                reason: "Key material cannot be all ones".to_string(),
            });
        }

        Ok(())
    }

    /// Validate that key material is not all zeros (security check)
    ///
    /// # Arguments
    ///
    /// * `key_data` - The key data to validate
    ///
    /// # Returns
    ///
    /// Returns `Ok(())` if the key material is valid, or an error if it's not.
    pub fn validate_key_material(&self, key_data: &[u8]) -> Result<()> {
        Self::ensure_non_trivial_key_bytes(key_data)?;
        self.entropy_validator.validate_key_entropy(key_data)?;
        Ok(())
    }

    /// Validate public key for a given algorithm
    ///
    /// # Arguments
    ///
    /// * `algorithm` - The algorithm to validate against
    /// * `key_data` - The public key data to validate
    ///
    /// # Returns
    ///
    /// Returns `Ok(())` if the public key is valid, or an error if it's not.
    pub fn validate_public_key(&self, algorithm: Algorithm, key_data: &[u8]) -> Result<()> {
        self.validate_key_size(algorithm, key_data, false)?;
        self.validate_key_material(key_data)?;
        Ok(())
    }

    /// Validate secret key for a given algorithm
    ///
    /// # Arguments
    ///
    /// * `algorithm` - The algorithm to validate against
    /// * `key_data` - The secret key data to validate
    ///
    /// # Returns
    ///
    /// Returns `Ok(())` if the secret key is valid, or an error if it's not.
    pub fn validate_secret_key(&self, algorithm: Algorithm, key_data: &[u8]) -> Result<()> {
        self.validate_key_size(algorithm, key_data, true)?;
        Self::ensure_non_trivial_key_bytes(key_data)?;

        self.entropy_validator.validate_key_entropy(key_data)?;

        Ok(())
    }

    /// Validate AEAD plaintext, ciphertext, or associated data size for one operation.
    pub fn validate_aead_message(&self, message: &[u8]) -> Result<()> {
        if message.len() > self.constants.max_aead_message_size() {
            return Err(crate::error::Error::InvalidMessageSize {
                max: self.constants.max_aead_message_size(),
                actual: message.len(),
            });
        }
        Ok(())
    }

    /// Validate hash absorb input length.
    pub fn validate_hash_input(&self, data: &[u8]) -> Result<()> {
        if data.len() > self.constants.max_hash_message_size() {
            return Err(crate::error::Error::InvalidMessageSize {
                max: self.constants.max_hash_message_size(),
                actual: data.len(),
            });
        }
        Ok(())
    }

    /// Validate signature message preimage length (same policy as [`Self::validate_hash_input`]).
    pub fn validate_signature_message(&self, message: &[u8]) -> Result<()> {
        self.validate_hash_input(message)
    }

    /// Immutable access to configured security constants.
    pub fn security_constants(&self) -> &SecurityConstants {
        &self.constants
    }

    /// Mutable access to security constants (message size ceilings, nonce size, …).
    pub fn security_constants_mut(&mut self) -> &mut SecurityConstants {
        &mut self.constants
    }

    /// Validate nonce size and uniqueness for AEAD operations
    ///
    /// # Arguments
    ///
    /// * `nonce` - The nonce to validate
    ///
    /// # Returns
    ///
    /// Returns `Ok(())` if the nonce is valid, or an error if it's not.
    pub fn validate_nonce(&self, nonce: &[u8]) -> Result<()> {
        if nonce.len() != self.constants.standard_nonce_size() {
            return Err(crate::error::Error::InvalidNonceSize {
                expected: self.constants.standard_nonce_size(),
                actual: nonce.len(),
            });
        }

        // Check for zero nonce
        if nonce.iter().all(|&b| b == 0) {
            return Err(crate::error::Error::InvalidKey {
                key_type: "nonce".to_string(),
                reason: "Nonce cannot be all zeros".to_string(),
            });
        }

        Ok(())
    }

    /// Validate ciphertext size for a given algorithm
    ///
    /// # Arguments
    ///
    /// * `algorithm` - The algorithm to validate against
    /// * `ciphertext` - The ciphertext to validate
    ///
    /// # Returns
    ///
    /// Returns `Ok(())` if the ciphertext size is valid, or an error if it's not.
    pub fn validate_ciphertext(&self, algorithm: Algorithm, ciphertext: &[u8]) -> Result<()> {
        if ciphertext.is_empty() {
            return Err(crate::error::Error::InvalidCiphertextSize {
                expected: 1,
                actual: 0,
            });
        }

        let expected_size = self.constants.get_expected_ciphertext_size(algorithm)?;
        if ciphertext.len() != expected_size {
            return Err(crate::error::Error::InvalidCiphertextSize {
                expected: expected_size,
                actual: ciphertext.len(),
            });
        }

        Ok(())
    }

    /// Validate signature size for a given algorithm
    ///
    /// # Arguments
    ///
    /// * `algorithm` - The algorithm to validate against
    /// * `signature` - The signature to validate
    ///
    /// # Returns
    ///
    /// Returns `Ok(())` if the signature size is valid, or an error if it's not.
    pub fn validate_signature(&self, algorithm: Algorithm, signature: &[u8]) -> Result<()> {
        if signature.is_empty() {
            return Err(crate::error::Error::InvalidSignatureSize {
                expected: 1,
                actual: 0,
            });
        }

        let expected_size = self.constants.get_expected_signature_size(algorithm)?;
        if signature.len() != expected_size {
            return Err(crate::error::Error::InvalidSignatureSize {
                expected: expected_size,
                actual: signature.len(),
            });
        }

        Ok(())
    }

    /// Validate randomness for cryptographic operations
    ///
    /// # Arguments
    ///
    /// * `randomness` - The randomness to validate
    ///
    /// # Returns
    ///
    /// Returns `Ok(())` if the randomness is valid, or an error if it's not.
    pub fn validate_randomness(&self, randomness: &[u8]) -> Result<()> {
        if randomness.len() < self.constants.min_randomness_size() {
            return Err(crate::error::Error::InvalidKeySize {
                expected: self.constants.min_randomness_size(),
                actual: randomness.len(),
            });
        }

        self.validate_key_material(randomness)?;
        Ok(())
    }

    /// Perform constant-time comparison of two byte slices
    ///
    /// # Arguments
    ///
    /// * `a` - First byte slice
    /// * `b` - Second byte slice
    ///
    /// # Returns
    ///
    /// Returns `true` if the slices are equal, `false` otherwise.
    /// The comparison is performed in constant time to prevent timing attacks.
    pub fn constant_time_compare(&self, a: &[u8], b: &[u8]) -> bool {
        self.timing_validator.constant_time_compare(a, b)
    }

    /// Get immutable access to the entropy validator
    ///
    /// This method provides access to the entropy validator for configuration
    /// and inspection purposes.
    pub fn entropy_validator(&self) -> &EntropyValidator {
        &self.entropy_validator
    }

    /// Get mutable access to the entropy validator
    ///
    /// This method provides mutable access to the entropy validator for
    /// configuration purposes. Use with caution in production environments.
    ///
    /// # Security Warning
    ///
    /// Disabling entropy validation reduces security. Only use this for
    /// testing scenarios with deterministic randomness.
    pub fn entropy_validator_mut(&mut self) -> &mut EntropyValidator {
        &mut self.entropy_validator
    }
}

#[cfg(test)]
#[cfg(feature = "alloc")]
mod tests {
    use super::*;

    #[test]
    fn test_security_validator_creation() {
        let validator = SecurityValidator::new();
        assert!(
            validator.is_ok(),
            "SecurityValidator should be created successfully"
        );
    }

    #[test]
    fn test_validate_algorithm_category() {
        let validator = SecurityValidator::new().unwrap();

        // Test correct category
        let result =
            validator.validate_algorithm_category(Algorithm::MlKem512, AlgorithmCategory::Kem);
        assert!(result.is_ok(), "Should accept correct algorithm category");

        // Test incorrect category
        let result = validator
            .validate_algorithm_category(Algorithm::MlKem512, AlgorithmCategory::Signature);
        assert!(
            result.is_err(),
            "Should reject incorrect algorithm category"
        );
    }

    #[test]
    fn test_validate_key_material() {
        let validator = SecurityValidator::new().unwrap();

        // Test valid key (16 bytes to meet minimum entropy requirements)
        let valid_key = vec![
            0x1A, 0x2B, 0x3C, 0x4D, 0x5E, 0x6F, 0x70, 0x81, 0x92, 0xA3, 0xB4, 0xC5, 0xD6, 0xE7,
            0xF8, 0x09,
        ];
        let result = validator.validate_key_material(&valid_key);
        assert!(result.is_ok(), "Should accept valid key material");

        // Test zero key
        let zero_key = vec![0u8; 8];
        let result = validator.validate_key_material(&zero_key);
        assert!(result.is_err(), "Should reject zero key");

        // Test all-ones key
        let ones_key = vec![0xFFu8; 8];
        let result = validator.validate_key_material(&ones_key);
        assert!(result.is_err(), "Should reject all-ones key");

        // Test empty key
        let empty_key = vec![];
        let result = validator.validate_key_material(&empty_key);
        assert!(result.is_err(), "Should reject empty key");
    }

    #[test]
    fn test_validate_aead_message() {
        let validator = SecurityValidator::new().unwrap();

        let valid_message = vec![1u8; 1000];
        assert!(validator.validate_aead_message(&valid_message).is_ok());

        let oversized_message = vec![1u8; 2 * 1024 * 1024];
        assert!(validator.validate_aead_message(&oversized_message).is_err());
    }

    #[test]
    fn test_validate_hash_input_default_unbounded() {
        let validator = SecurityValidator::new().unwrap();
        let large = vec![1u8; 2 * 1024 * 1024];
        assert!(validator.validate_hash_input(&large).is_ok());
    }

    #[test]
    fn test_validate_hash_input_when_capped() {
        let mut validator = SecurityValidator::new().unwrap();
        validator
            .security_constants_mut()
            .set_max_hash_message_size(1024);
        let oversized = vec![1u8; 2048];
        assert!(validator.validate_hash_input(&oversized).is_err());
    }

    #[test]
    fn test_constant_time_compare() {
        let validator = SecurityValidator::new().unwrap();

        // Test equal slices
        let a = vec![1, 2, 3, 4];
        let b = vec![1, 2, 3, 4];
        assert!(
            validator.constant_time_compare(&a, &b),
            "Should return true for equal slices"
        );

        // Test different slices
        let c = vec![1, 2, 3, 5];
        assert!(
            !validator.constant_time_compare(&a, &c),
            "Should return false for different slices"
        );

        // Test different length slices
        let d = vec![1, 2, 3];
        assert!(
            !validator.constant_time_compare(&a, &d),
            "Should return false for different length slices"
        );
    }
}