classic_mceliece_rust/
lib.rs

1#![doc = include_str!("../README.md")]
2#![no_std]
3#![forbid(unsafe_code)]
4#![cfg_attr(docsrs, feature(doc_cfg))]
5
6mod api;
7mod benes;
8mod bm;
9mod controlbits;
10mod crypto_hash;
11mod decrypt;
12mod encrypt;
13mod gf;
14mod int32_sort;
15mod nist_aes_rng;
16mod operations;
17mod params;
18mod pk_gen;
19mod root;
20mod sk_gen;
21mod synd;
22mod test_katkem;
23mod test_kem;
24mod test_utils;
25mod transpose;
26mod uint64_sort;
27mod util;
28
29use core::fmt::Debug;
30use rand::{CryptoRng, RngCore};
31
32#[cfg(feature = "alloc")]
33extern crate alloc;
34#[cfg(feature = "alloc")]
35use alloc::boxed::Box;
36
37#[cfg(feature = "kem")]
38pub use kem_api::ClassicMcEliece;
39
40pub use api::{
41    CRYPTO_BYTES, CRYPTO_CIPHERTEXTBYTES, CRYPTO_PRIMITIVE, CRYPTO_PUBLICKEYBYTES,
42    CRYPTO_SECRETKEYBYTES,
43};
44
45mod macros {
46    /// This macro(A, B, C, T) allows to get “&A[B..B+C]” of type “&[T]” as type “&[T; C]”.
47    /// The default type T is u8 and “mut A” instead of “A” returns a mutable reference.
48    macro_rules! sub {
49        ($var:expr, $offset:expr, $len:expr) => {{
50            <&[u8; $len]>::try_from(&$var[$offset..($offset + $len)])
51                .expect("slice has the correct length")
52        }};
53        (mut $var:expr, $offset:expr, $len:expr) => {{
54            <&mut [u8; $len]>::try_from(&mut $var[$offset..($offset + $len)])
55                .expect("slice has the correct length")
56        }};
57        ($var:expr, $offset:expr, $len:expr, $t:ty) => {{
58            <&[$t; $len]>::try_from(&$var[$offset..($offset + $len)])
59                .expect("slice has the correct length")
60        }};
61        (mut $var:expr, $offset:expr, $len:expr, $t:ty) => {{
62            <&mut [$t; $len]>::try_from(&mut $var[$offset..($offset + $len)])
63                .expect("slice has the correct length")
64        }};
65    }
66
67    pub(crate) use sub;
68}
69
70#[derive(Debug)]
71enum KeyBufferMut<'a, const SIZE: usize> {
72    Borrowed(&'a mut [u8; SIZE]),
73    #[cfg(feature = "alloc")]
74    Owned(Box<[u8; SIZE]>),
75}
76
77impl<const SIZE: usize> KeyBufferMut<'_, SIZE> {
78    #[cfg(feature = "alloc")]
79    fn to_owned(&self) -> KeyBufferMut<'static, SIZE> {
80        let mut new_buffer = util::alloc_boxed_array::<SIZE>();
81        new_buffer.copy_from_slice(self.as_ref());
82        KeyBufferMut::Owned(new_buffer)
83    }
84}
85
86impl<const SIZE: usize> AsRef<[u8; SIZE]> for KeyBufferMut<'_, SIZE> {
87    fn as_ref(&self) -> &[u8; SIZE] {
88        match &self {
89            KeyBufferMut::Borrowed(buf) => buf,
90            #[cfg(feature = "alloc")]
91            KeyBufferMut::Owned(buf) => buf.as_ref(),
92        }
93    }
94}
95
96impl<const SIZE: usize> AsMut<[u8; SIZE]> for KeyBufferMut<'_, SIZE> {
97    fn as_mut(&mut self) -> &mut [u8; SIZE] {
98        match self {
99            KeyBufferMut::Borrowed(buf) => buf,
100            #[cfg(feature = "alloc")]
101            KeyBufferMut::Owned(buf) => buf.as_mut(),
102        }
103    }
104}
105
106#[cfg(feature = "zeroize")]
107impl<const SIZE: usize> zeroize::Zeroize for KeyBufferMut<'_, SIZE> {
108    fn zeroize(&mut self) {
109        match self {
110            KeyBufferMut::Borrowed(buf) => buf.zeroize(),
111            #[cfg(feature = "alloc")]
112            KeyBufferMut::Owned(buf) => buf.zeroize(),
113        }
114    }
115}
116
117/// A Classic McEliece public key. These are very large compared to keys
118/// in most other cryptographic algorithms.
119#[derive(Debug)]
120#[must_use]
121pub struct PublicKey<'a>(KeyBufferMut<'a, CRYPTO_PUBLICKEYBYTES>);
122
123impl PublicKey<'_> {
124    /// Copies the key to the heap and makes it `'static`.
125    #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
126    #[cfg(feature = "alloc")]
127    pub fn to_owned(&self) -> PublicKey<'static> {
128        PublicKey(self.0.to_owned())
129    }
130
131    pub fn as_array(&self) -> &[u8; CRYPTO_PUBLICKEYBYTES] {
132        self.0.as_ref()
133    }
134}
135
136impl AsRef<[u8]> for PublicKey<'_> {
137    fn as_ref(&self) -> &[u8] {
138        self.0.as_ref()
139    }
140}
141
142impl<'a> From<&'a mut [u8; CRYPTO_PUBLICKEYBYTES]> for PublicKey<'a> {
143    fn from(data: &'a mut [u8; CRYPTO_PUBLICKEYBYTES]) -> Self {
144        Self(KeyBufferMut::Borrowed(data))
145    }
146}
147
148#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
149#[cfg(feature = "alloc")]
150impl From<Box<[u8; CRYPTO_PUBLICKEYBYTES]>> for PublicKey<'static> {
151    fn from(data: Box<[u8; CRYPTO_PUBLICKEYBYTES]>) -> Self {
152        Self(KeyBufferMut::Owned(data))
153    }
154}
155
156#[cfg(feature = "zeroize")]
157impl zeroize::Zeroize for PublicKey<'_> {
158    fn zeroize(&mut self) {
159        self.0.zeroize();
160    }
161}
162
163#[cfg(feature = "zeroize")]
164impl zeroize::ZeroizeOnDrop for PublicKey<'_> {}
165
166impl Drop for PublicKey<'_> {
167    fn drop(&mut self) {
168        #[cfg(feature = "zeroize")]
169        {
170            use zeroize::Zeroize;
171            self.zeroize();
172        }
173    }
174}
175
176/// A Classic McEliece secret key.
177///
178/// Should be kept on the device where it's generated. Used to decapsulate the [`SharedSecret`]
179/// from the [`Ciphertext`] received from the encapsulator.
180#[must_use]
181pub struct SecretKey<'a>(KeyBufferMut<'a, CRYPTO_SECRETKEYBYTES>);
182
183impl SecretKey<'_> {
184    /// Copies the key to the heap and makes it `'static`.
185    #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
186    #[cfg(feature = "alloc")]
187    pub fn to_owned(&self) -> SecretKey<'static> {
188        SecretKey(self.0.to_owned())
189    }
190
191    /// Returns the secret key as an array of bytes.
192    ///
193    /// Please note that depending on your threat model, moving the data out of the
194    /// `SecretKey` can be bad for security. The `SecretKey` type is designed to keep the
195    /// backing data in a single location in memory and zeroing it out when it goes out
196    /// of scope.
197    pub fn as_array(&self) -> &[u8; CRYPTO_SECRETKEYBYTES] {
198        self.0.as_ref()
199    }
200}
201
202impl Debug for SecretKey<'_> {
203    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
204        f.debug_tuple("SecretKey").field(&"-- redacted --").finish()
205    }
206}
207
208impl AsRef<[u8]> for SecretKey<'_> {
209    fn as_ref(&self) -> &[u8] {
210        self.0.as_ref()
211    }
212}
213
214impl<'a> From<&'a mut [u8; CRYPTO_SECRETKEYBYTES]> for SecretKey<'a> {
215    /// Represents a mutable byte array of the correct size as a `SecretKey`.
216    /// Please note that the array will be zeroed on drop.
217    fn from(data: &'a mut [u8; CRYPTO_SECRETKEYBYTES]) -> Self {
218        Self(KeyBufferMut::Borrowed(data))
219    }
220}
221
222#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
223#[cfg(feature = "alloc")]
224impl From<Box<[u8; CRYPTO_SECRETKEYBYTES]>> for SecretKey<'static> {
225    fn from(data: Box<[u8; CRYPTO_SECRETKEYBYTES]>) -> Self {
226        Self(KeyBufferMut::Owned(data))
227    }
228}
229
230#[cfg(feature = "zeroize")]
231impl zeroize::Zeroize for SecretKey<'_> {
232    fn zeroize(&mut self) {
233        self.0.zeroize();
234    }
235}
236
237#[cfg(feature = "zeroize")]
238impl zeroize::ZeroizeOnDrop for SecretKey<'_> {}
239
240impl Drop for SecretKey<'_> {
241    fn drop(&mut self) {
242        #[cfg(feature = "zeroize")]
243        {
244            use zeroize::Zeroize;
245            self.zeroize();
246        }
247    }
248}
249
250/// The ciphertext computed by the encapsulator.
251#[derive(Debug)]
252#[must_use]
253pub struct Ciphertext([u8; CRYPTO_CIPHERTEXTBYTES]);
254
255impl Ciphertext {
256    pub fn as_array(&self) -> &[u8; CRYPTO_CIPHERTEXTBYTES] {
257        &self.0
258    }
259}
260
261impl AsRef<[u8]> for Ciphertext {
262    fn as_ref(&self) -> &[u8] {
263        self.0.as_ref()
264    }
265}
266
267impl From<[u8; CRYPTO_CIPHERTEXTBYTES]> for Ciphertext {
268    fn from(data: [u8; CRYPTO_CIPHERTEXTBYTES]) -> Self {
269        Self(data)
270    }
271}
272
273/// The shared secret computed by the KEM. Returned from both the
274/// encapsulator and decapsulator.
275#[must_use]
276pub struct SharedSecret<'a>(KeyBufferMut<'a, CRYPTO_BYTES>);
277
278impl SharedSecret<'_> {
279    /// Copies the secret to the heap and makes it `'static`.
280    #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
281    #[cfg(feature = "alloc")]
282    pub fn to_owned(&self) -> SharedSecret<'static> {
283        SharedSecret(self.0.to_owned())
284    }
285
286    pub fn as_array(&self) -> &[u8; CRYPTO_BYTES] {
287        self.0.as_ref()
288    }
289}
290
291impl Debug for SharedSecret<'_> {
292    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
293        f.debug_tuple("SharedSecret")
294            .field(&"-- redacted --")
295            .finish()
296    }
297}
298
299impl AsRef<[u8]> for SharedSecret<'_> {
300    fn as_ref(&self) -> &[u8] {
301        self.0.as_ref()
302    }
303}
304
305#[cfg(feature = "zeroize")]
306impl zeroize::Zeroize for SharedSecret<'_> {
307    fn zeroize(&mut self) {
308        self.0.zeroize();
309    }
310}
311
312#[cfg(feature = "zeroize")]
313impl zeroize::ZeroizeOnDrop for SharedSecret<'_> {}
314
315impl Drop for SharedSecret<'_> {
316    fn drop(&mut self) {
317        #[cfg(feature = "zeroize")]
318        {
319            use zeroize::Zeroize;
320            self.zeroize();
321        }
322    }
323}
324
325/// KEM Keypair generation.
326///
327/// Generate a public and secret key.
328/// The public key is meant to be shared with any party,
329/// but access to the secret key must be limited to the generating party.
330pub fn keypair<'public, 'secret, R: CryptoRng + RngCore>(
331    public_key_buf: &'public mut [u8; CRYPTO_PUBLICKEYBYTES],
332    secret_key_buf: &'secret mut [u8; CRYPTO_SECRETKEYBYTES],
333    rng: &mut R,
334) -> (PublicKey<'public>, SecretKey<'secret>) {
335    operations::crypto_kem_keypair(public_key_buf, secret_key_buf, rng);
336
337    (
338        PublicKey(KeyBufferMut::Borrowed(public_key_buf)),
339        SecretKey(KeyBufferMut::Borrowed(secret_key_buf)),
340    )
341}
342
343/// Convenient wrapper around [`keypair`] that stores the public and private keys on the heap
344/// and returns them with the ``'static`` lifetime.
345#[cfg(feature = "alloc")]
346#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
347pub fn keypair_boxed<R: CryptoRng + RngCore>(
348    rng: &mut R,
349) -> (PublicKey<'static>, SecretKey<'static>) {
350    let mut public_key_buf = util::alloc_boxed_array::<CRYPTO_PUBLICKEYBYTES>();
351    let mut secret_key_buf = util::alloc_boxed_array::<CRYPTO_SECRETKEYBYTES>();
352
353    operations::crypto_kem_keypair(&mut public_key_buf, &mut secret_key_buf, rng);
354
355    (
356        PublicKey(KeyBufferMut::Owned(public_key_buf)),
357        SecretKey(KeyBufferMut::Owned(secret_key_buf)),
358    )
359}
360
361/// KEM Encapsulation.
362///
363/// Given a public key `public_key`, compute a shared key.
364/// The returned ciphertext should be sent back to the entity holding
365/// the secret key corresponding to public key given here, so they can compute
366/// the same shared key.
367pub fn encapsulate<'shared_secret, R: CryptoRng + RngCore>(
368    public_key: &PublicKey<'_>,
369    shared_secret_buf: &'shared_secret mut [u8; CRYPTO_BYTES],
370    rng: &mut R,
371) -> (Ciphertext, SharedSecret<'shared_secret>) {
372    let mut shared_secret_buf = KeyBufferMut::Borrowed(shared_secret_buf);
373    let mut ciphertext_buf = [0u8; CRYPTO_CIPHERTEXTBYTES];
374
375    operations::crypto_kem_enc(
376        &mut ciphertext_buf,
377        shared_secret_buf.as_mut(),
378        public_key.0.as_ref(),
379        rng,
380    );
381
382    (Ciphertext(ciphertext_buf), SharedSecret(shared_secret_buf))
383}
384
385/// Convenient wrapper around [`encapsulate`] that stores the shared secret on the heap
386/// and returns it with the ``'static`` lifetime.
387#[cfg(feature = "alloc")]
388#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
389pub fn encapsulate_boxed<R: CryptoRng + RngCore>(
390    public_key: &PublicKey<'_>,
391    rng: &mut R,
392) -> (Ciphertext, SharedSecret<'static>) {
393    let mut shared_secret_buf = KeyBufferMut::Owned(Box::new([0u8; CRYPTO_BYTES]));
394    let mut ciphertext_buf = [0u8; CRYPTO_CIPHERTEXTBYTES];
395
396    operations::crypto_kem_enc(
397        &mut ciphertext_buf,
398        shared_secret_buf.as_mut(),
399        public_key.0.as_ref(),
400        rng,
401    );
402
403    (Ciphertext(ciphertext_buf), SharedSecret(shared_secret_buf))
404}
405
406/// KEM Decapsulation.
407///
408/// Given a secret key `secret_key` and a ciphertext `ciphertext`,
409/// determine the shared key negotiated by both parties.
410pub fn decapsulate<'shared_secret>(
411    ciphertext: &Ciphertext,
412    secret_key: &SecretKey,
413    shared_secret_buf: &'shared_secret mut [u8; CRYPTO_BYTES],
414) -> SharedSecret<'shared_secret> {
415    let mut shared_secret_buf = KeyBufferMut::Borrowed(shared_secret_buf);
416
417    operations::crypto_kem_dec(
418        shared_secret_buf.as_mut(),
419        ciphertext.as_array(),
420        secret_key.as_array(),
421    );
422
423    SharedSecret(shared_secret_buf)
424}
425
426/// Convenient wrapper around [`decapsulate`] that stores the shared secret on the heap
427/// and returns it with the ``'static`` lifetime.
428#[cfg(feature = "alloc")]
429#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
430pub fn decapsulate_boxed(ciphertext: &Ciphertext, secret_key: &SecretKey) -> SharedSecret<'static> {
431    let mut shared_secret_buf = KeyBufferMut::Owned(Box::new([0u8; CRYPTO_BYTES]));
432
433    operations::crypto_kem_dec(
434        shared_secret_buf.as_mut(),
435        ciphertext.as_array(),
436        secret_key.as_array(),
437    );
438
439    SharedSecret(shared_secret_buf)
440}
441
442#[cfg(feature = "kem")]
443mod kem_api {
444    use kem::generic_array::{typenum, GenericArray};
445    use kem::{Decapsulator, EncappedKey, Encapsulator, SharedSecret};
446    use rand::{CryptoRng, RngCore};
447
448    use crate::{Ciphertext, PublicKey, SecretKey};
449    use crate::{CRYPTO_BYTES, CRYPTO_CIPHERTEXTBYTES};
450
451    /// A struct for encapsulating a shared key using Classic McEliece.
452    #[derive(Debug)]
453    #[cfg_attr(docsrs, doc(cfg(feature = "kem")))]
454    pub struct ClassicMcEliece;
455
456    impl Encapsulator<Ciphertext> for ClassicMcEliece {
457        fn try_encap<R: CryptoRng + RngCore>(
458            &self,
459            csprng: &mut R,
460            recip_pubkey: &<Ciphertext as EncappedKey>::RecipientPublicKey,
461        ) -> Result<(Ciphertext, SharedSecret<Ciphertext>), kem::Error> {
462            let mut ciphertext_buf = [0u8; CRYPTO_CIPHERTEXTBYTES];
463            let mut shared_secret = GenericArray::<_, _>::default();
464
465            let shared_secret_buf: &mut [u8; CRYPTO_BYTES] = shared_secret
466                .as_mut_slice()
467                .try_into()
468                .expect("GenericArray should be CRYPTO_BYTES long");
469
470            crate::operations::crypto_kem_enc(
471                &mut ciphertext_buf,
472                shared_secret_buf,
473                recip_pubkey.0.as_ref(),
474                csprng,
475            );
476            Ok((
477                Ciphertext(ciphertext_buf),
478                SharedSecret::<Ciphertext>::new(shared_secret),
479            ))
480        }
481    }
482
483    #[cfg_attr(docsrs, doc(cfg(feature = "kem")))]
484    impl EncappedKey for Ciphertext {
485        type EncappedKeySize = crate::api::CryptoCiphertextBytesTypenum;
486
487        type SharedSecretSize = typenum::U32;
488
489        type SenderPublicKey = ();
490
491        type RecipientPublicKey = PublicKey<'static>;
492
493        fn from_bytes(bytes: &GenericArray<u8, Self::EncappedKeySize>) -> Result<Self, kem::Error> {
494            let mut data = [0u8; CRYPTO_CIPHERTEXTBYTES];
495            data.copy_from_slice(bytes.as_slice());
496            Ok(Ciphertext(data))
497        }
498    }
499
500    #[cfg_attr(docsrs, doc(cfg(feature = "kem")))]
501    impl<'sk> Decapsulator<Ciphertext> for SecretKey<'sk> {
502        fn try_decap(
503            &self,
504            ciphertext: &Ciphertext,
505        ) -> Result<SharedSecret<Ciphertext>, kem::Error> {
506            let mut shared_secret = GenericArray::<_, _>::default();
507
508            let shared_secret_buf: &mut [u8; CRYPTO_BYTES] = shared_secret
509                .as_mut_slice()
510                .try_into()
511                .expect("GenericArray should be CRYPTO_BYTES long");
512
513            crate::operations::crypto_kem_dec(
514                shared_secret_buf,
515                ciphertext.as_array(),
516                self.as_array(),
517            );
518            Ok(SharedSecret::<Ciphertext>::new(shared_secret))
519        }
520    }
521}
522
523// Tests may use `std`
524#[cfg(test)]
525#[macro_use]
526extern crate std;