Skip to main content

lib_q_cb_kem/
lib.rs

1//! lib-Q Classical McEliece KEM - Post-quantum Key Encapsulation Mechanism
2//!
3//! This crate provides a pure Rust implementation of the Classical McEliece KEM
4//! following the lib-Q architecture with proper security validation and provider pattern integration.
5//!
6//! ## Architecture
7//!
8//! This implementation follows the lib-Q provider pattern:
9//! - **Provider Pattern**: Implements `KemOperations` trait for integration with lib-q-core
10//! - **Security Validation**: Comprehensive input validation and security checks
11//! - **Algorithm Support**: Full support for NIST-approved Classical McEliece variants
12//! - **Memory Safety**: Automatic zeroization of sensitive data
13//! - **no_std Support**: Works in constrained environments
14//!
15//! ## Supported Algorithms
16//!
17//! - **Classical McEliece**: All NIST-approved variants (348864, 460896, 6688128, 6960119, 8192128)
18//! - **Hash Functions**: SHA3 (SHAKE256) support
19//!
20//! ## Feature Support
21//!
22//! All KEM algorithms support:
23//! - **no_std**: Works in constrained environments with external randomness
24//! - **WASM**: JavaScript-compatible bindings for web environments
25//! - **Security validation**: Comprehensive input validation and security checks
26//! - **Memory safety**: Automatic zeroization of sensitive data
27//! - **Hash function**: SHA3 (SHAKE256) hash function
28//!
29//! ## Usage
30//!
31//! ### With libQ Integration
32//! ```rust,ignore
33//! use lib_q_core::{Algorithm, KemContext, create_kem_context};
34//! use lib_q_cb_kem::LibQCbKemProvider;
35//!
36//! fn main() -> Result<(), Box<dyn std::error::Error>> {
37//!     // Create KEM context with Classical McEliece provider
38//!     let mut ctx = create_kem_context();
39//!     ctx.set_provider(Box::new(LibQCbKemProvider::new()?));
40//!
41//!     // Generate keypair (requires std feature for automatic randomness)
42//!     let keypair = ctx.generate_keypair(Algorithm::CbKem348864, None)?;
43//!
44//!     // Encapsulate shared secret
45//!     let (ciphertext, shared_secret) = ctx.encapsulate(Algorithm::CbKem348864, &keypair.public_key, None)?;
46//!
47//!     // Decapsulate shared secret
48//!     let decapsulated_secret = ctx.decapsulate(Algorithm::CbKem348864, &keypair.secret_key, &ciphertext)?;
49//!     assert_eq!(shared_secret, decapsulated_secret);
50//!     Ok(())
51//! }
52//! ```
53//!
54//! ### Direct Usage (no_std compatible)
55//! ```rust,ignore
56//! use lib_q_cb_kem::{keypair, encapsulate, decapsulate, LibQRng};
57//!
58//! fn main() -> Result<(), Box<dyn std::error::Error>> {
59//!     // Create deterministic RNG for testing (use hardware RNG in production)
60//!     let mut rng = LibQRng::new_deterministic(0x0102030405060708);
61//!
62//!     // Generate keypair
63//!     let (public_key, secret_key) = keypair(&mut rng);
64//!
65//!     // Encapsulate shared secret
66//!     let (ciphertext, shared_secret) = encapsulate(&public_key, &mut rng);
67//!
68//!     // Decapsulate shared secret
69//!     let decapsulated_secret = decapsulate(&secret_key, &ciphertext);
70//!     assert_eq!(shared_secret.as_ref(), decapsulated_secret.as_ref());
71//!     Ok(())
72//! }
73//! ```
74
75#![no_std]
76#![forbid(unsafe_code)]
77#![cfg_attr(docsrs, feature(doc_cfg))]
78// Reference-style loops and explicit formulas match upstream crypto code; keep Clippy from blocking CI.
79#![allow(clippy::collapsible_if)]
80#![allow(clippy::identity_op)]
81#![allow(clippy::manual_div_ceil)]
82#![allow(clippy::manual_memcpy)]
83#![allow(clippy::needless_range_loop)]
84#![allow(clippy::unnecessary_map_or)]
85#![allow(clippy::unnecessary_mut_passed)]
86
87mod api;
88mod benes;
89mod bm;
90mod controlbits;
91mod crypto_hash;
92mod decrypt;
93mod encrypt;
94mod gf;
95mod int32_sort;
96mod libq_provider;
97mod operations;
98mod params;
99mod pk_gen;
100mod root;
101mod sk_gen;
102mod synd;
103mod test_utils;
104mod transpose;
105mod uint64_sort;
106mod util;
107
108#[cfg(feature = "nist-aes-rng")]
109#[cfg_attr(docsrs, doc(cfg(feature = "nist-aes-rng")))]
110mod nist_aes_rng;
111
112use core::fmt::Debug;
113
114use rand_core::{
115    CryptoRng,
116    Rng,
117};
118
119#[cfg(feature = "alloc")]
120extern crate alloc;
121#[cfg(feature = "alloc")]
122use alloc::boxed::Box;
123
124pub use api::{
125    CRYPTO_BYTES,
126    CRYPTO_CIPHERTEXTBYTES,
127    CRYPTO_PRIMITIVE,
128    CRYPTO_PUBLICKEYBYTES,
129    CRYPTO_SECRETKEYBYTES,
130};
131// Re-export unified RNG from lib-q-random
132pub use lib_q_random::ClassicalMcElieceRng as LibQRng;
133// Re-export libQ provider
134#[cfg(feature = "alloc")]
135pub use libq_provider::LibQCbKemProvider;
136#[cfg(feature = "nist-aes-rng")]
137#[cfg_attr(docsrs, doc(cfg(feature = "nist-aes-rng")))]
138pub use nist_aes_rng::{
139    AesState,
140    MAX_BYTES_PER_REQUEST,
141    NistDrbgError,
142    RESEED_INTERVAL,
143    SEEDLEN,
144};
145
146mod macros {
147    /// This macro(A, B, C, T) allows to get “&A[B..B+C]” of type “&[T]” as type “&[T; C]”.
148    /// The default type T is u8 and “mut A” instead of “A” returns a mutable reference.
149    macro_rules! sub {
150        ($var:expr, $offset:expr, $len:expr) => {{
151            <&[u8; $len]>::try_from(&$var[$offset..($offset + $len)])
152                .expect("slice has the correct length")
153        }};
154        (mut $var:expr, $offset:expr, $len:expr) => {{
155            <&mut [u8; $len]>::try_from(&mut $var[$offset..($offset + $len)])
156                .expect("slice has the correct length")
157        }};
158        ($var:expr, $offset:expr, $len:expr, $t:ty) => {{
159            <&[$t; $len]>::try_from(&$var[$offset..($offset + $len)])
160                .expect("slice has the correct length")
161        }};
162        (mut $var:expr, $offset:expr, $len:expr, $t:ty) => {{
163            <&mut [$t; $len]>::try_from(&mut $var[$offset..($offset + $len)])
164                .expect("slice has the correct length")
165        }};
166    }
167
168    pub(crate) use sub;
169}
170
171#[derive(Debug)]
172enum KeyBufferMut<'a, const SIZE: usize> {
173    Borrowed(&'a mut [u8; SIZE]),
174    #[cfg(feature = "alloc")]
175    Owned(Box<[u8; SIZE]>),
176}
177
178impl<const SIZE: usize> KeyBufferMut<'_, SIZE> {
179    #[cfg(feature = "alloc")]
180    fn to_owned(&self) -> KeyBufferMut<'static, SIZE> {
181        let mut new_buffer = util::alloc_boxed_array::<SIZE>();
182        new_buffer.copy_from_slice(self.as_ref());
183        KeyBufferMut::Owned(new_buffer)
184    }
185}
186
187impl<const SIZE: usize> AsRef<[u8; SIZE]> for KeyBufferMut<'_, SIZE> {
188    fn as_ref(&self) -> &[u8; SIZE] {
189        match &self {
190            KeyBufferMut::Borrowed(buf) => buf,
191            #[cfg(feature = "alloc")]
192            KeyBufferMut::Owned(buf) => buf.as_ref(),
193        }
194    }
195}
196
197impl<const SIZE: usize> AsMut<[u8; SIZE]> for KeyBufferMut<'_, SIZE> {
198    fn as_mut(&mut self) -> &mut [u8; SIZE] {
199        match self {
200            KeyBufferMut::Borrowed(buf) => buf,
201            #[cfg(feature = "alloc")]
202            KeyBufferMut::Owned(buf) => buf.as_mut(),
203        }
204    }
205}
206
207#[cfg(feature = "zeroize")]
208impl<const SIZE: usize> zeroize::Zeroize for KeyBufferMut<'_, SIZE> {
209    fn zeroize(&mut self) {
210        match self {
211            KeyBufferMut::Borrowed(buf) => buf.zeroize(),
212            #[cfg(feature = "alloc")]
213            KeyBufferMut::Owned(buf) => buf.zeroize(),
214        }
215    }
216}
217
218/// A Classic McEliece public key. These are very large compared to keys
219/// in most other cryptographic algorithms.
220#[derive(Debug)]
221#[must_use]
222pub struct PublicKey<'a>(KeyBufferMut<'a, CRYPTO_PUBLICKEYBYTES>);
223
224impl PublicKey<'_> {
225    /// Copies the key to the heap and makes it `'static`.
226    #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
227    #[cfg(feature = "alloc")]
228    pub fn to_owned(&self) -> PublicKey<'static> {
229        PublicKey(self.0.to_owned())
230    }
231
232    pub fn as_array(&self) -> &[u8; CRYPTO_PUBLICKEYBYTES] {
233        self.0.as_ref()
234    }
235}
236
237impl AsRef<[u8]> for PublicKey<'_> {
238    fn as_ref(&self) -> &[u8] {
239        self.0.as_ref()
240    }
241}
242
243impl<'a> From<&'a mut [u8; CRYPTO_PUBLICKEYBYTES]> for PublicKey<'a> {
244    fn from(data: &'a mut [u8; CRYPTO_PUBLICKEYBYTES]) -> Self {
245        Self(KeyBufferMut::Borrowed(data))
246    }
247}
248
249#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
250#[cfg(feature = "alloc")]
251impl From<Box<[u8; CRYPTO_PUBLICKEYBYTES]>> for PublicKey<'static> {
252    fn from(data: Box<[u8; CRYPTO_PUBLICKEYBYTES]>) -> Self {
253        Self(KeyBufferMut::Owned(data))
254    }
255}
256
257#[cfg(feature = "zeroize")]
258impl zeroize::Zeroize for PublicKey<'_> {
259    fn zeroize(&mut self) {
260        self.0.zeroize();
261    }
262}
263
264#[cfg(feature = "zeroize")]
265impl zeroize::ZeroizeOnDrop for PublicKey<'_> {}
266
267impl Drop for PublicKey<'_> {
268    fn drop(&mut self) {
269        #[cfg(feature = "zeroize")]
270        {
271            use zeroize::Zeroize;
272            self.zeroize();
273        }
274    }
275}
276
277/// A Classic McEliece secret key.
278///
279/// Should be kept on the device where it's generated. Used to decapsulate the [`SharedSecret`]
280/// from the [`Ciphertext`] received from the encapsulator.
281#[must_use]
282pub struct SecretKey<'a>(KeyBufferMut<'a, CRYPTO_SECRETKEYBYTES>);
283
284impl SecretKey<'_> {
285    /// Copies the key to the heap and makes it `'static`.
286    #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
287    #[cfg(feature = "alloc")]
288    pub fn to_owned(&self) -> SecretKey<'static> {
289        SecretKey(self.0.to_owned())
290    }
291
292    /// Returns the secret key as an array of bytes.
293    ///
294    /// Please note that depending on your threat model, moving the data out of the
295    /// `SecretKey` can be bad for security. The `SecretKey` type is designed to keep the
296    /// backing data in a single location in memory and zeroing it out when it goes out
297    /// of scope.
298    pub fn as_array(&self) -> &[u8; CRYPTO_SECRETKEYBYTES] {
299        self.0.as_ref()
300    }
301}
302
303impl Debug for SecretKey<'_> {
304    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
305        f.debug_tuple("SecretKey").field(&"-- redacted --").finish()
306    }
307}
308
309impl AsRef<[u8]> for SecretKey<'_> {
310    fn as_ref(&self) -> &[u8] {
311        self.0.as_ref()
312    }
313}
314
315impl<'a> From<&'a mut [u8; CRYPTO_SECRETKEYBYTES]> for SecretKey<'a> {
316    /// Represents a mutable byte array of the correct size as a `SecretKey`.
317    /// Please note that the array will be zeroed on drop.
318    fn from(data: &'a mut [u8; CRYPTO_SECRETKEYBYTES]) -> Self {
319        Self(KeyBufferMut::Borrowed(data))
320    }
321}
322
323#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
324#[cfg(feature = "alloc")]
325impl From<Box<[u8; CRYPTO_SECRETKEYBYTES]>> for SecretKey<'static> {
326    fn from(data: Box<[u8; CRYPTO_SECRETKEYBYTES]>) -> Self {
327        Self(KeyBufferMut::Owned(data))
328    }
329}
330
331#[cfg(feature = "zeroize")]
332impl zeroize::Zeroize for SecretKey<'_> {
333    fn zeroize(&mut self) {
334        self.0.zeroize();
335    }
336}
337
338#[cfg(feature = "zeroize")]
339impl zeroize::ZeroizeOnDrop for SecretKey<'_> {}
340
341impl Drop for SecretKey<'_> {
342    fn drop(&mut self) {
343        #[cfg(feature = "zeroize")]
344        {
345            use zeroize::Zeroize;
346            self.zeroize();
347        }
348    }
349}
350
351/// The ciphertext computed by the encapsulator.
352#[derive(Debug)]
353#[must_use]
354pub struct Ciphertext([u8; CRYPTO_CIPHERTEXTBYTES]);
355
356impl Ciphertext {
357    pub fn as_array(&self) -> &[u8; CRYPTO_CIPHERTEXTBYTES] {
358        &self.0
359    }
360}
361
362impl AsRef<[u8]> for Ciphertext {
363    fn as_ref(&self) -> &[u8] {
364        self.0.as_ref()
365    }
366}
367
368impl From<[u8; CRYPTO_CIPHERTEXTBYTES]> for Ciphertext {
369    fn from(data: [u8; CRYPTO_CIPHERTEXTBYTES]) -> Self {
370        Self(data)
371    }
372}
373
374#[cfg(feature = "zeroize")]
375impl zeroize::Zeroize for Ciphertext {
376    fn zeroize(&mut self) {
377        self.0.zeroize();
378    }
379}
380
381#[cfg(feature = "zeroize")]
382impl zeroize::ZeroizeOnDrop for Ciphertext {}
383
384impl Drop for Ciphertext {
385    fn drop(&mut self) {
386        #[cfg(feature = "zeroize")]
387        {
388            use zeroize::Zeroize;
389            self.zeroize();
390        }
391    }
392}
393
394/// The shared secret computed by the KEM. Returned from both the
395/// encapsulator and decapsulator.
396#[must_use]
397pub struct SharedSecret<'a>(KeyBufferMut<'a, CRYPTO_BYTES>);
398
399impl SharedSecret<'_> {
400    /// Copies the secret to the heap and makes it `'static`.
401    #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
402    #[cfg(feature = "alloc")]
403    pub fn to_owned(&self) -> SharedSecret<'static> {
404        SharedSecret(self.0.to_owned())
405    }
406
407    pub fn as_array(&self) -> &[u8; CRYPTO_BYTES] {
408        self.0.as_ref()
409    }
410}
411
412impl Debug for SharedSecret<'_> {
413    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
414        f.debug_tuple("SharedSecret")
415            .field(&"-- redacted --")
416            .finish()
417    }
418}
419
420impl AsRef<[u8]> for SharedSecret<'_> {
421    fn as_ref(&self) -> &[u8] {
422        self.0.as_ref()
423    }
424}
425
426#[cfg(feature = "zeroize")]
427impl zeroize::Zeroize for SharedSecret<'_> {
428    fn zeroize(&mut self) {
429        self.0.zeroize();
430    }
431}
432
433#[cfg(feature = "zeroize")]
434impl zeroize::ZeroizeOnDrop for SharedSecret<'_> {}
435
436impl Drop for SharedSecret<'_> {
437    fn drop(&mut self) {
438        #[cfg(feature = "zeroize")]
439        {
440            use zeroize::Zeroize;
441            self.zeroize();
442        }
443    }
444}
445
446/// KEM Keypair generation.
447///
448/// Generate a public and secret key.
449/// The public key is meant to be shared with any party,
450/// but access to the secret key must be limited to the generating party.
451pub fn keypair<'public, 'secret, R: CryptoRng + Rng>(
452    public_key_buf: &'public mut [u8; CRYPTO_PUBLICKEYBYTES],
453    secret_key_buf: &'secret mut [u8; CRYPTO_SECRETKEYBYTES],
454    rng: &mut R,
455) -> (PublicKey<'public>, SecretKey<'secret>) {
456    operations::crypto_kem_keypair(public_key_buf, secret_key_buf, rng);
457
458    (
459        PublicKey(KeyBufferMut::Borrowed(public_key_buf)),
460        SecretKey(KeyBufferMut::Borrowed(secret_key_buf)),
461    )
462}
463
464/// Convenient wrapper around [`keypair`] that stores the public and private keys on the heap
465/// and returns them with the ``'static`` lifetime.
466#[cfg(feature = "alloc")]
467#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
468pub fn keypair_boxed<R: CryptoRng + Rng>(rng: &mut R) -> (PublicKey<'static>, SecretKey<'static>) {
469    let mut public_key_buf = util::alloc_boxed_array::<CRYPTO_PUBLICKEYBYTES>();
470    let mut secret_key_buf = util::alloc_boxed_array::<CRYPTO_SECRETKEYBYTES>();
471
472    operations::crypto_kem_keypair(&mut public_key_buf, &mut secret_key_buf, rng);
473
474    (
475        PublicKey(KeyBufferMut::Owned(public_key_buf)),
476        SecretKey(KeyBufferMut::Owned(secret_key_buf)),
477    )
478}
479
480/// KEM Encapsulation.
481///
482/// Given a public key `public_key`, compute a shared key.
483/// The returned ciphertext should be sent back to the entity holding
484/// the secret key corresponding to public key given here, so they can compute
485/// the same shared key.
486pub fn encapsulate<'shared_secret, R: CryptoRng + Rng>(
487    public_key: &PublicKey<'_>,
488    shared_secret_buf: &'shared_secret mut [u8; CRYPTO_BYTES],
489    rng: &mut R,
490) -> (Ciphertext, SharedSecret<'shared_secret>) {
491    let mut shared_secret_buf = KeyBufferMut::Borrowed(shared_secret_buf);
492    let mut ciphertext_buf = [0u8; CRYPTO_CIPHERTEXTBYTES];
493
494    operations::crypto_kem_enc(
495        &mut ciphertext_buf,
496        shared_secret_buf.as_mut(),
497        public_key.0.as_ref(),
498        rng,
499    );
500
501    (Ciphertext(ciphertext_buf), SharedSecret(shared_secret_buf))
502}
503
504/// Convenient wrapper around [`encapsulate`] that stores the shared secret on the heap
505/// and returns it with the ``'static`` lifetime.
506#[cfg(feature = "alloc")]
507#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
508pub fn encapsulate_boxed<R: CryptoRng + Rng>(
509    public_key: &PublicKey<'_>,
510    rng: &mut R,
511) -> (Ciphertext, SharedSecret<'static>) {
512    let mut shared_secret_buf = KeyBufferMut::Owned(Box::new([0u8; CRYPTO_BYTES]));
513    let mut ciphertext_buf = [0u8; CRYPTO_CIPHERTEXTBYTES];
514
515    operations::crypto_kem_enc(
516        &mut ciphertext_buf,
517        shared_secret_buf.as_mut(),
518        public_key.0.as_ref(),
519        rng,
520    );
521
522    (Ciphertext(ciphertext_buf), SharedSecret(shared_secret_buf))
523}
524
525/// KEM Decapsulation.
526///
527/// Given a secret key `secret_key` and a ciphertext `ciphertext`,
528/// determine the shared key negotiated by both parties.
529pub fn decapsulate<'shared_secret>(
530    ciphertext: &Ciphertext,
531    secret_key: &SecretKey,
532    shared_secret_buf: &'shared_secret mut [u8; CRYPTO_BYTES],
533) -> SharedSecret<'shared_secret> {
534    let mut shared_secret_buf = KeyBufferMut::Borrowed(shared_secret_buf);
535
536    operations::crypto_kem_dec(
537        shared_secret_buf.as_mut(),
538        ciphertext.as_array(),
539        secret_key.as_array(),
540    );
541
542    SharedSecret(shared_secret_buf)
543}
544
545/// Convenient wrapper around [`decapsulate`] that stores the shared secret on the heap
546/// and returns it with the ``'static`` lifetime.
547#[cfg(feature = "alloc")]
548#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
549pub fn decapsulate_boxed(ciphertext: &Ciphertext, secret_key: &SecretKey) -> SharedSecret<'static> {
550    let mut shared_secret_buf = KeyBufferMut::Owned(Box::new([0u8; CRYPTO_BYTES]));
551
552    operations::crypto_kem_dec(
553        shared_secret_buf.as_mut(),
554        ciphertext.as_array(),
555        secret_key.as_array(),
556    );
557
558    SharedSecret(shared_secret_buf)
559}
560
561#[cfg(feature = "wasm")]
562mod wasm;
563
564// Tests may use `std` - no extern crate needed in Rust 2018+