Skip to main content

lib_q_cb_kem/
libq_provider.rs

1//! lib-Q Classical McEliece Provider Implementation
2//!
3//! This module provides the LibQCbKemProvider that implements the KemOperations
4//! trait and routes Classical McEliece KEM operations to the appropriate algorithm
5//! implementations with proper security validation.
6
7#[cfg(feature = "alloc")]
8extern crate alloc;
9#[cfg(feature = "alloc")]
10use alloc::{
11    string::ToString,
12    vec::Vec,
13};
14
15#[cfg(feature = "alloc")]
16use lib_q_core::api::{
17    Algorithm,
18    CryptoProvider,
19    KemOperations,
20};
21#[cfg(feature = "alloc")]
22use lib_q_core::error::{
23    Error,
24    Result,
25};
26#[cfg(feature = "alloc")]
27use lib_q_core::security::SecurityValidator;
28#[cfg(feature = "alloc")]
29use lib_q_core::traits::{
30    KemKeypair,
31    KemPublicKey,
32    KemSecretKey,
33};
34
35// Import Classical McEliece implementations
36#[cfg(feature = "alloc")]
37use crate::{
38    CRYPTO_BYTES,
39    CRYPTO_CIPHERTEXTBYTES,
40    CRYPTO_PUBLICKEYBYTES,
41    CRYPTO_SECRETKEYBYTES,
42    Ciphertext,
43    PublicKey,
44    SecretKey,
45    decapsulate,
46    encapsulate,
47    keypair,
48};
49
50/// lib-Q Classical McEliece KEM provider implementation
51///
52/// This provider implements KEM operations for Classical McEliece, including key generation,
53/// encapsulation, and decapsulation with proper security validation and algorithm routing.
54#[cfg(feature = "alloc")]
55#[derive(Clone)]
56pub struct LibQCbKemProvider {
57    security_validator: SecurityValidator,
58}
59
60#[cfg(feature = "alloc")]
61impl core::fmt::Debug for LibQCbKemProvider {
62    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
63        f.debug_struct("LibQCbKemProvider")
64            .field("security_validator", &"<SecurityValidator>")
65            .finish()
66    }
67}
68
69#[cfg(feature = "alloc")]
70impl LibQCbKemProvider {
71    /// Create a new Classical McEliece KEM provider
72    ///
73    /// # Returns
74    ///
75    /// A new instance of LibQCbKemProvider with security validation initialized.
76    ///
77    /// # Errors
78    ///
79    /// Returns an error if the security validator fails to initialize.
80    pub fn new() -> Result<Self> {
81        Ok(Self {
82            security_validator: SecurityValidator::new()?,
83        })
84    }
85
86    /// Get the security validator
87    pub fn security_validator(&self) -> &SecurityValidator {
88        &self.security_validator
89    }
90
91    /// Get mutable access to the security validator
92    ///
93    /// This method is provided for testing scenarios where entropy validation
94    /// needs to be disabled for deterministic testing. Use with caution in production.
95    pub fn security_validator_mut(&mut self) -> &mut SecurityValidator {
96        &mut self.security_validator
97    }
98}
99
100#[cfg(feature = "alloc")]
101impl KemOperations for LibQCbKemProvider {
102    fn generate_keypair(
103        &self,
104        algorithm: Algorithm,
105        randomness: Option<&[u8]>,
106    ) -> Result<KemKeypair> {
107        // Validate algorithm category
108        self.security_validator
109            .validate_algorithm_category(algorithm, lib_q_core::api::AlgorithmCategory::Kem)?;
110
111        // Validate randomness if provided
112        if let Some(rng) = randomness {
113            self.security_validator.validate_randomness(rng)?;
114        }
115
116        // Route to specific algorithm implementation
117        match algorithm {
118            // Classical McEliece algorithms
119            Algorithm::CbKem348864 => self.generate_cb_kem_keypair(
120                CRYPTO_PUBLICKEYBYTES,
121                CRYPTO_SECRETKEYBYTES,
122                randomness,
123            ),
124            Algorithm::CbKem460896 => self.generate_cb_kem_keypair(
125                CRYPTO_PUBLICKEYBYTES,
126                CRYPTO_SECRETKEYBYTES,
127                randomness,
128            ),
129            Algorithm::CbKem6688128 => self.generate_cb_kem_keypair(
130                CRYPTO_PUBLICKEYBYTES,
131                CRYPTO_SECRETKEYBYTES,
132                randomness,
133            ),
134            Algorithm::CbKem6960119 => self.generate_cb_kem_keypair(
135                CRYPTO_PUBLICKEYBYTES,
136                CRYPTO_SECRETKEYBYTES,
137                randomness,
138            ),
139            Algorithm::CbKem8192128 => self.generate_cb_kem_keypair(
140                CRYPTO_PUBLICKEYBYTES,
141                CRYPTO_SECRETKEYBYTES,
142                randomness,
143            ),
144
145            _ => Err(Error::InvalidAlgorithm {
146                algorithm: "Algorithm not supported for Classical McEliece KEM operations",
147            }),
148        }
149    }
150
151    fn encapsulate(
152        &self,
153        algorithm: Algorithm,
154        public_key: &KemPublicKey,
155        randomness: Option<&[u8]>,
156    ) -> Result<(Vec<u8>, Vec<u8>)> {
157        // Validate algorithm category
158        self.security_validator
159            .validate_algorithm_category(algorithm, lib_q_core::api::AlgorithmCategory::Kem)?;
160
161        // Validate public key size only (skip entropy validation for CB-KEM keys)
162        self.security_validator
163            .validate_key_size(algorithm, public_key.as_bytes(), false)?;
164
165        // Validate randomness if provided
166        if let Some(rng) = randomness {
167            self.security_validator.validate_randomness(rng)?;
168        }
169
170        // Route to specific algorithm implementation
171        match algorithm {
172            // Classical McEliece algorithms
173            Algorithm::CbKem348864 |
174            Algorithm::CbKem460896 |
175            Algorithm::CbKem6688128 |
176            Algorithm::CbKem6960119 |
177            Algorithm::CbKem8192128 => self.encapsulate_cb_kem(public_key, randomness),
178
179            _ => Err(Error::InvalidAlgorithm {
180                algorithm: "Algorithm not supported for Classical McEliece KEM operations",
181            }),
182        }
183    }
184
185    fn decapsulate(
186        &self,
187        algorithm: Algorithm,
188        secret_key: &KemSecretKey,
189        ciphertext: &[u8],
190    ) -> Result<Vec<u8>> {
191        // Validate algorithm category
192        self.security_validator
193            .validate_algorithm_category(algorithm, lib_q_core::api::AlgorithmCategory::Kem)?;
194
195        // Validate secret key size only (skip entropy validation for CB-KEM keys)
196        self.security_validator
197            .validate_key_size(algorithm, secret_key.as_bytes(), true)?;
198
199        // Validate ciphertext
200        self.security_validator
201            .validate_ciphertext(algorithm, ciphertext)?;
202
203        // Route to specific algorithm implementation
204        match algorithm {
205            // Classical McEliece algorithms
206            Algorithm::CbKem348864 |
207            Algorithm::CbKem460896 |
208            Algorithm::CbKem6688128 |
209            Algorithm::CbKem6960119 |
210            Algorithm::CbKem8192128 => self.decapsulate_cb_kem(secret_key, ciphertext),
211
212            _ => Err(Error::InvalidAlgorithm {
213                algorithm: "Algorithm not supported for Classical McEliece KEM operations",
214            }),
215        }
216    }
217
218    fn derive_public_key(
219        &self,
220        _algorithm: Algorithm,
221        _secret_key: &KemSecretKey,
222    ) -> Result<KemPublicKey> {
223        // Classical McEliece doesn't support public key derivation from secret key
224        // The public key is generated independently during keypair generation
225        Err(Error::UnsupportedOperation {
226            operation: "Classical McEliece does not support public key derivation from secret key"
227                .to_string(),
228        })
229    }
230}
231
232#[cfg(feature = "alloc")]
233impl LibQCbKemProvider {
234    /// Generate a Classical McEliece keypair
235    fn generate_cb_kem_keypair(
236        &self,
237        public_key_size: usize,
238        secret_key_size: usize,
239        randomness: Option<&[u8]>,
240    ) -> Result<KemKeypair> {
241        // Validate key sizes
242        if public_key_size != CRYPTO_PUBLICKEYBYTES {
243            return Err(Error::InvalidKeySize {
244                expected: CRYPTO_PUBLICKEYBYTES,
245                actual: public_key_size,
246            });
247        }
248        if secret_key_size != CRYPTO_SECRETKEYBYTES {
249            return Err(Error::InvalidKeySize {
250                expected: CRYPTO_SECRETKEYBYTES,
251                actual: secret_key_size,
252            });
253        }
254
255        // Create buffers for keys
256        let mut public_key_buf = [0u8; CRYPTO_PUBLICKEYBYTES];
257        let mut secret_key_buf = [0u8; CRYPTO_SECRETKEYBYTES];
258
259        // Generate keypair using Classical McEliece
260        let (public_key, secret_key) = if let Some(rng_bytes) = randomness {
261            // Use provided randomness with deterministic RNG
262            // Use local RNG implementation (non-blocking)
263            {
264                let mut rng = crate::LibQRng::new_deterministic_from_bytes(rng_bytes);
265                keypair(&mut public_key_buf, &mut secret_key_buf, &mut rng)
266            }
267        } else {
268            // Use secure system randomness with local RNG (non-blocking)
269            {
270                let mut rng = crate::LibQRng::new();
271                keypair(&mut public_key_buf, &mut secret_key_buf, &mut rng)
272            }
273        };
274
275        // Copy key material into KemKeypair-owned buffers; stack buffers are zeroed when
276        // `PublicKey` / `SecretKey` drop (zeroize feature).
277        Ok(KemKeypair::new(
278            Vec::from(public_key.as_array().as_slice()),
279            Vec::from(secret_key.as_array().as_slice()),
280        ))
281    }
282
283    /// Encapsulate using Classical McEliece
284    fn encapsulate_cb_kem(
285        &self,
286        public_key: &KemPublicKey,
287        randomness: Option<&[u8]>,
288    ) -> Result<(Vec<u8>, Vec<u8>)> {
289        // Validate public key size
290        if public_key.as_bytes().len() != CRYPTO_PUBLICKEYBYTES {
291            return Err(Error::InvalidKeySize {
292                expected: CRYPTO_PUBLICKEYBYTES,
293                actual: public_key.as_bytes().len(),
294            });
295        }
296
297        // Create public key from bytes
298        let mut public_key_buf = [0u8; CRYPTO_PUBLICKEYBYTES];
299        public_key_buf.copy_from_slice(public_key.as_bytes());
300        let public_key = PublicKey::from(&mut public_key_buf);
301
302        // Create shared secret buffer
303        let mut shared_secret_buf = [0u8; CRYPTO_BYTES];
304
305        // Encapsulate
306        let (ciphertext, shared_secret) = if let Some(rng_bytes) = randomness {
307            // Use provided randomness with deterministic RNG
308            // Use local RNG implementation (non-blocking)
309            {
310                let mut rng = crate::LibQRng::new_deterministic_from_bytes(rng_bytes);
311                encapsulate(&public_key, &mut shared_secret_buf, &mut rng)
312            }
313        } else {
314            // Use secure system randomness with local RNG (non-blocking)
315            {
316                let mut rng = crate::LibQRng::new();
317                encapsulate(&public_key, &mut shared_secret_buf, &mut rng)
318            }
319        };
320
321        Ok((
322            Vec::from(ciphertext.as_array().as_slice()),
323            Vec::from(shared_secret.as_array().as_slice()),
324        ))
325    }
326
327    /// Decapsulate using Classical McEliece
328    fn decapsulate_cb_kem(&self, secret_key: &KemSecretKey, ciphertext: &[u8]) -> Result<Vec<u8>> {
329        // Validate secret key size
330        if secret_key.as_bytes().len() != CRYPTO_SECRETKEYBYTES {
331            return Err(Error::InvalidKeySize {
332                expected: CRYPTO_SECRETKEYBYTES,
333                actual: secret_key.as_bytes().len(),
334            });
335        }
336
337        // Validate ciphertext size
338        if ciphertext.len() != CRYPTO_CIPHERTEXTBYTES {
339            return Err(Error::InvalidCiphertextSize {
340                expected: CRYPTO_CIPHERTEXTBYTES,
341                actual: ciphertext.len(),
342            });
343        }
344
345        // Create secret key from bytes
346        let mut secret_key_buf = [0u8; CRYPTO_SECRETKEYBYTES];
347        secret_key_buf.copy_from_slice(secret_key.as_bytes());
348        let secret_key = SecretKey::from(&mut secret_key_buf);
349
350        // Create ciphertext from bytes
351        let mut ciphertext_buf = [0u8; CRYPTO_CIPHERTEXTBYTES];
352        ciphertext_buf.copy_from_slice(ciphertext);
353        let ciphertext = Ciphertext::from(ciphertext_buf);
354
355        // Create shared secret buffer
356        let mut shared_secret_buf = [0u8; CRYPTO_BYTES];
357
358        // Decapsulate
359        let shared_secret = decapsulate(&ciphertext, &secret_key, &mut shared_secret_buf);
360
361        Ok(Vec::from(shared_secret.as_array().as_slice()))
362    }
363}
364
365#[cfg(feature = "alloc")]
366impl CryptoProvider for LibQCbKemProvider {
367    fn kem(&self) -> Option<&dyn KemOperations> {
368        Some(self)
369    }
370
371    fn signature(&self) -> Option<&dyn lib_q_core::api::SignatureOperations> {
372        None
373    }
374
375    fn hash(&self) -> Option<&dyn lib_q_core::api::HashOperations> {
376        None
377    }
378
379    fn aead(&self) -> Option<&dyn lib_q_core::api::AeadOperations> {
380        None
381    }
382}
383
384#[cfg(all(test, feature = "alloc"))]
385mod tests {
386    use super::*;
387
388    /// [`Algorithm`] that matches the single active `cbkem*` feature for this build (see `api.rs`).
389    fn compiled_cb_kem_algorithm() -> Algorithm {
390        #[cfg(any(feature = "cbkem348864", feature = "cbkem348864f"))]
391        {
392            Algorithm::CbKem348864
393        }
394        #[cfg(all(
395            not(any(feature = "cbkem348864", feature = "cbkem348864f")),
396            any(feature = "cbkem460896", feature = "cbkem460896f"),
397        ))]
398        {
399            Algorithm::CbKem460896
400        }
401        #[cfg(all(
402            not(any(
403                feature = "cbkem348864",
404                feature = "cbkem348864f",
405                feature = "cbkem460896",
406                feature = "cbkem460896f",
407            )),
408            any(feature = "cbkem6688128", feature = "cbkem6688128f"),
409        ))]
410        {
411            Algorithm::CbKem6688128
412        }
413        #[cfg(all(
414            not(any(
415                feature = "cbkem348864",
416                feature = "cbkem348864f",
417                feature = "cbkem460896",
418                feature = "cbkem460896f",
419                feature = "cbkem6688128",
420                feature = "cbkem6688128f",
421            )),
422            any(feature = "cbkem6960119", feature = "cbkem6960119f"),
423        ))]
424        {
425            Algorithm::CbKem6960119
426        }
427        #[cfg(all(
428            not(any(
429                feature = "cbkem348864",
430                feature = "cbkem348864f",
431                feature = "cbkem460896",
432                feature = "cbkem460896f",
433                feature = "cbkem6688128",
434                feature = "cbkem6688128f",
435                feature = "cbkem6960119",
436                feature = "cbkem6960119f",
437            )),
438            any(feature = "cbkem8192128", feature = "cbkem8192128f"),
439        ))]
440        {
441            Algorithm::CbKem8192128
442        }
443    }
444
445    #[test]
446    fn test_provider_creation() {
447        let provider = LibQCbKemProvider::new();
448        assert!(provider.is_ok(), "Provider should be created successfully");
449    }
450
451    #[test]
452    fn test_provider_security_validator() {
453        let provider = LibQCbKemProvider::new().unwrap();
454        let _validator = provider.security_validator();
455        // Security validator should be accessible
456    }
457
458    #[test]
459    fn test_provider_security_validator_mut_accessor() {
460        let mut provider = LibQCbKemProvider::new().unwrap();
461        let _validator_mut = provider.security_validator_mut();
462    }
463
464    #[test]
465    fn test_provider_unsupported_algorithm() {
466        let provider = LibQCbKemProvider::new().unwrap();
467        let result = provider.generate_keypair(Algorithm::Sha3_256, None);
468        assert!(
469            result.is_err(),
470            "Should return error for unsupported algorithm"
471        );
472
473        if let Err(Error::InvalidAlgorithm { .. }) = result {
474            // Expected error type
475        } else {
476            panic!("Expected InvalidAlgorithm error");
477        }
478    }
479
480    #[test]
481    fn test_provider_algorithm_routing() {
482        let provider = LibQCbKemProvider::new().unwrap();
483
484        // Must match the compiled variant: this crate is built for exactly one `cbkem*` feature.
485        let alg = compiled_cb_kem_algorithm();
486
487        provider
488            .security_validator()
489            .validate_algorithm_category(alg, lib_q_core::api::AlgorithmCategory::Kem)
490            .expect("compiled CB-KEM algorithm should be a KEM");
491
492        // This crate is `#![no_std]` (the `std` feature is only a cfg knob), so we cannot spawn
493        // a thread with a larger stack. Full `keypair()` for large-parameter variants overflows
494        // the default test thread stack in debug builds; exercise it only for the smallest build.
495        #[cfg(any(feature = "cbkem348864", feature = "cbkem348864f"))]
496        {
497            let result = provider.generate_keypair(alg, None);
498            match result {
499                Ok(_) => {}
500                Err(Error::NotImplemented { .. }) => {}
501                Err(Error::RandomGenerationFailed { .. }) => {}
502                Err(e) => panic!("Unexpected error type: {:?}", e),
503            }
504        }
505    }
506
507    #[test]
508    fn test_provider_full_kem_cycle() {
509        #[cfg(feature = "std")]
510        {
511            let provider = LibQCbKemProvider::new().unwrap();
512
513            let alg = compiled_cb_kem_algorithm();
514
515            // Full keygen+encap+decap is extremely expensive for larger parameter sets in debug
516            // and can exceed tarpaulin's per-test response window. Execute this end-to-end cycle
517            // only for the smallest variants; other variants are still validated by algorithm
518            // routing and lower-level known-answer/integration tests.
519            #[cfg(any(feature = "cbkem348864", feature = "cbkem348864f"))]
520            {
521                // Test full KEM cycle for the Classical McEliece variant in this build
522                let keypair = provider.generate_keypair(alg, None).unwrap();
523
524                // Test encapsulation
525                let (ciphertext, shared_secret1) = provider
526                    .encapsulate(alg, &keypair.public_key, None)
527                    .unwrap();
528
529                // Test decapsulation
530                let shared_secret2 = provider
531                    .decapsulate(alg, &keypair.secret_key, &ciphertext)
532                    .unwrap();
533
534                // Verify shared secrets match
535                assert_eq!(
536                    shared_secret1, shared_secret2,
537                    "Shared secrets should match"
538                );
539
540                // Verify sizes are correct
541                assert_eq!(
542                    ciphertext.len(),
543                    CRYPTO_CIPHERTEXTBYTES,
544                    "Classical McEliece ciphertext should be {} bytes",
545                    CRYPTO_CIPHERTEXTBYTES
546                );
547                assert_eq!(
548                    shared_secret1.len(),
549                    CRYPTO_BYTES,
550                    "Shared secret should be {} bytes",
551                    CRYPTO_BYTES
552                );
553            }
554
555            #[cfg(not(any(feature = "cbkem348864", feature = "cbkem348864f")))]
556            {
557                provider
558                    .security_validator()
559                    .validate_algorithm_category(alg, lib_q_core::api::AlgorithmCategory::Kem)
560                    .expect("compiled CB-KEM algorithm should be a KEM");
561            }
562        }
563    }
564
565    #[test]
566    fn test_provider_derive_public_key_is_unsupported() {
567        let provider = LibQCbKemProvider::new().unwrap();
568        let secret_key = KemSecretKey::new(Vec::new());
569        let result = provider.derive_public_key(compiled_cb_kem_algorithm(), &secret_key);
570        assert!(matches!(result, Err(Error::UnsupportedOperation { .. })));
571    }
572
573    #[test]
574    fn test_crypto_provider_trait_exposes_only_kem_operations() {
575        let provider = LibQCbKemProvider::new().unwrap();
576        let crypto_provider: &dyn CryptoProvider = &provider;
577        assert!(crypto_provider.kem().is_some());
578        assert!(crypto_provider.signature().is_none());
579        assert!(crypto_provider.hash().is_none());
580        assert!(crypto_provider.aead().is_none());
581    }
582}