Skip to main content

safe_oqs/
kem.rs

1//! KEM API
2//!
3//! See [`Kem`] for the main functionality.
4//! [`Algorithm`] lists the available algorithms.
5use alloc::vec::Vec;
6
7use core::ptr::NonNull;
8
9#[cfg(not(feature = "std"))]
10use cstr_core::CStr;
11#[cfg(feature = "std")]
12use std::ffi::CStr;
13
14#[cfg(feature = "serde")]
15use serde::{Deserialize, Serialize};
16
17use crate::ffi::kem as ffi;
18use crate::newtype_buffer;
19use crate::*;
20
21newtype_buffer!(PublicKey, PublicKeyRef);
22newtype_buffer!(SecretKey, SecretKeyRef);
23newtype_buffer!(Ciphertext, CiphertextRef);
24newtype_buffer!(SharedSecret, SharedSecretRef);
25
26macro_rules! implement_kems {
27    { $(($feat: literal) $kem: ident: $oqs_id: ident),* $(,)? } => (
28
29        /// Supported algorithms by OQS
30        ///
31        /// Note that this doesn't mean that they'll be available.
32        ///
33        /// Optional support for `serde` if that feature is enabled.
34        #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
35        #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
36        #[allow(missing_docs)]
37        pub enum Algorithm {
38            $(
39                $kem,
40            )*
41        }
42
43        fn algorithm_to_id(algorithm: Algorithm) -> *const libc::c_char {
44            let id: &[u8] = match algorithm {
45                $(
46                    Algorithm::$kem => &ffi::$oqs_id[..],
47                )*
48            };
49            id as *const _ as *const libc::c_char
50        }
51
52        $(
53            #[cfg(test)]
54            #[allow(non_snake_case)]
55            mod $kem {
56                use super::*;
57
58                #[test]
59                #[cfg(feature = $feat)]
60                fn test_encaps_decaps() -> Result<()> {
61                    crate::init();
62
63                    let alg = Algorithm::$kem;
64                    let kem = Kem::new(alg)?;
65                    let (pk, sk) = kem.keypair()?;
66                    let (ct, ss1) = kem.encapsulate(&pk)?;
67                    let ss2 = kem.decapsulate(&sk, &ct)?;
68                    assert_eq!(ss1, ss2, "shared secret not equal!");
69                    Ok(())
70                }
71
72                #[test]
73                fn test_enabled() {
74                    crate::init();
75                    if cfg!(feature = $feat) {
76                        assert!(Algorithm::$kem.is_enabled());
77                    } else {
78                        assert!(!Algorithm::$kem.is_enabled())
79                    }
80                }
81
82                #[test]
83                fn test_name() {
84                    let algo = Algorithm::$kem;
85                    // Just make sure the name impl does not panic or crash.
86                    let name = algo.name();
87                    #[cfg(feature = "std")]
88                    assert_eq!(name, algo.to_string());
89                    // ... And actually contains something.
90                    assert!(!name.is_empty());
91                }
92
93                #[test]
94                fn test_get_algorithm_back() {
95                    let algorithm = Algorithm::$kem;
96                    if algorithm.is_enabled() {
97                        let kem = Kem::new(algorithm).unwrap();
98                        assert_eq!(algorithm, kem.algorithm());
99                    }
100                }
101
102                #[test]
103                fn test_version() {
104                    if let Ok(kem) = Kem::new(Algorithm::$kem) {
105                        // Just make sure the version can be called without panic
106                        let version = kem.version();
107                        // ... And actually contains something.
108                        assert!(!version.is_empty());
109                    }
110                }
111            }
112        )*
113    )
114}
115
116implement_kems! {
117    ("bike") BikeL1: OQS_KEM_alg_bike_l1,
118    ("bike") BikeL3: OQS_KEM_alg_bike_l3,
119    ("bike") BikeL5: OQS_KEM_alg_bike_l5,
120    ("classic_mceliece") ClassicMcEliece348864: OQS_KEM_alg_classic_mceliece_348864,
121    ("classic_mceliece") ClassicMcEliece348864f: OQS_KEM_alg_classic_mceliece_348864f,
122    ("classic_mceliece") ClassicMcEliece460896: OQS_KEM_alg_classic_mceliece_460896,
123    ("classic_mceliece") ClassicMcEliece460896f: OQS_KEM_alg_classic_mceliece_460896f,
124    ("classic_mceliece") ClassicMcEliece6688128: OQS_KEM_alg_classic_mceliece_6688128,
125    ("classic_mceliece") ClassicMcEliece6688128f: OQS_KEM_alg_classic_mceliece_6688128f,
126    ("classic_mceliece") ClassicMcEliece6960119: OQS_KEM_alg_classic_mceliece_6960119,
127    ("classic_mceliece") ClassicMcEliece6960119f: OQS_KEM_alg_classic_mceliece_6960119f,
128    ("classic_mceliece") ClassicMcEliece8192128: OQS_KEM_alg_classic_mceliece_8192128,
129    ("classic_mceliece") ClassicMcEliece8192128f: OQS_KEM_alg_classic_mceliece_8192128f,
130    ("hqc") Hqc128: OQS_KEM_alg_hqc_128,
131    ("hqc") Hqc192: OQS_KEM_alg_hqc_192,
132    ("hqc") Hqc256: OQS_KEM_alg_hqc_256,
133    ("kyber") Kyber512: OQS_KEM_alg_kyber_512,
134    ("kyber") Kyber768: OQS_KEM_alg_kyber_768,
135    ("kyber") Kyber1024: OQS_KEM_alg_kyber_1024,
136    ("ntruprime") NtruPrimeSntrup761: OQS_KEM_alg_ntruprime_sntrup761,
137    ("frodokem") FrodoKem640Aes: OQS_KEM_alg_frodokem_640_aes,
138    ("frodokem") FrodoKem640Shake: OQS_KEM_alg_frodokem_640_shake,
139    ("frodokem") FrodoKem976Aes: OQS_KEM_alg_frodokem_976_aes,
140    ("frodokem") FrodoKem976Shake: OQS_KEM_alg_frodokem_976_shake,
141    ("frodokem") FrodoKem1344Aes: OQS_KEM_alg_frodokem_1344_aes,
142    ("frodokem") FrodoKem1344Shake: OQS_KEM_alg_frodokem_1344_shake,
143    ("ml_kem") MlKem512Ipd: OQS_KEM_alg_ml_kem_512_ipd,
144    ("ml_kem") MlKem768Ipd: OQS_KEM_alg_ml_kem_768_ipd,
145    ("ml_kem") MlKem1024Ipd: OQS_KEM_alg_ml_kem_1024_ipd,
146    ("ml_kem") MlKem512: OQS_KEM_alg_ml_kem_512,
147    ("ml_kem") MlKem768: OQS_KEM_alg_ml_kem_768,
148    ("ml_kem") MlKem1024: OQS_KEM_alg_ml_kem_1024,
149}
150
151impl Algorithm {
152    /// Returns true if this algorithm is enabled in the linked version
153    /// of liboqs
154    pub fn is_enabled(self) -> bool {
155        unsafe { ffi::OQS_KEM_alg_is_enabled(algorithm_to_id(self)) == 1 }
156    }
157
158    /// Provides a pointer to the id of the algorithm
159    ///
160    /// For use with the FFI api methods
161    pub fn to_id(self) -> *const libc::c_char {
162        algorithm_to_id(self)
163    }
164
165    /// Returns the algorithm's name as a static Rust string.
166    ///
167    /// This is the same as the `to_id`, but as a safe Rust string.
168    pub fn name(&self) -> &'static str {
169        // SAFETY: The id from ffi must be a proper null terminated C string
170        let id = unsafe { CStr::from_ptr(self.to_id()) };
171        id.to_str().expect("OQS algorithm names must be UTF-8")
172    }
173}
174
175#[cfg(feature = "std")]
176impl std::fmt::Display for Algorithm {
177    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
178        self.name().fmt(f)
179    }
180}
181
182/// KEM algorithm
183///
184/// # Example
185/// ```rust
186/// # if !cfg!(feature = "kyber") { return; }
187/// use safe_oqs;
188/// safe_oqs::init();
189/// let kem = safe_oqs::kem::Kem::new(safe_oqs::kem::Algorithm::Kyber512).unwrap();
190/// let (pk, sk) = kem.keypair().unwrap();
191/// let (ct, ss) = kem.encapsulate(&pk).unwrap();
192/// let ss2 = kem.decapsulate(&sk, &ct).unwrap();
193/// assert_eq!(ss, ss2);
194/// ```
195pub struct Kem {
196    algorithm: Algorithm,
197    kem: NonNull<ffi::OQS_KEM>,
198}
199
200unsafe impl Sync for Kem {}
201unsafe impl Send for Kem {}
202
203impl Drop for Kem {
204    fn drop(&mut self) {
205        unsafe { ffi::OQS_KEM_free(self.kem.as_ptr()) };
206    }
207}
208
209impl core::convert::TryFrom<Algorithm> for Kem {
210    type Error = crate::Error;
211    fn try_from(alg: Algorithm) -> Result<Kem> {
212        Kem::new(alg)
213    }
214}
215
216impl Kem {
217    /// Construct a new algorithm
218    pub fn new(algorithm: Algorithm) -> Result<Self> {
219        let kem = unsafe { ffi::OQS_KEM_new(algorithm_to_id(algorithm)) };
220        NonNull::new(kem).map_or_else(
221            || Err(Error::AlgorithmDisabled),
222            |kem| Ok(Self { algorithm, kem }),
223        )
224    }
225
226    /// Get the algorithm used by this `Kem`
227    pub fn algorithm(&self) -> Algorithm {
228        self.algorithm
229    }
230
231    /// Get the version of the implementation
232    pub fn version(&self) -> &'static str {
233        let kem = unsafe { self.kem.as_ref() };
234        // SAFETY: The alg_version from ffi must be a proper null terminated C string
235        let cstr = unsafe { CStr::from_ptr(kem.alg_version) };
236        cstr.to_str()
237            .expect("Algorithm version strings must be UTF-8")
238    }
239
240    /// Get the claimed nist level
241    pub fn claimed_nist_level(&self) -> u8 {
242        let kem = unsafe { self.kem.as_ref() };
243        kem.claimed_nist_level
244    }
245
246    /// Is the algorithm ind_cca secure
247    pub fn is_ind_cca(&self) -> bool {
248        let kem = unsafe { self.kem.as_ref() };
249        kem.ind_cca
250    }
251
252    /// Get the length of the public key
253    pub fn length_public_key(&self) -> usize {
254        let kem = unsafe { self.kem.as_ref() };
255        kem.length_public_key
256    }
257
258    /// Get the length of the secret key
259    pub fn length_secret_key(&self) -> usize {
260        let kem = unsafe { self.kem.as_ref() };
261        kem.length_secret_key
262    }
263
264    /// Get the length of the ciphertext
265    pub fn length_ciphertext(&self) -> usize {
266        let kem = unsafe { self.kem.as_ref() };
267        kem.length_ciphertext
268    }
269
270    /// Get the length of a shared secret
271    pub fn length_shared_secret(&self) -> usize {
272        let kem = unsafe { self.kem.as_ref() };
273        kem.length_shared_secret
274    }
275
276    /// Obtain a secret key objects from bytes
277    ///
278    /// Returns None if the secret key is not the correct length.
279    pub fn secret_key_from_bytes<'a>(&self, buf: &'a [u8]) -> Option<SecretKeyRef<'a>> {
280        if self.length_secret_key() != buf.len() {
281            None
282        } else {
283            Some(SecretKeyRef::new(buf))
284        }
285    }
286
287    /// Obtain a public key from bytes
288    ///
289    /// Returns None if the public key is not the correct length.
290    pub fn public_key_from_bytes<'a>(&self, buf: &'a [u8]) -> Option<PublicKeyRef<'a>> {
291        if self.length_public_key() != buf.len() {
292            None
293        } else {
294            Some(PublicKeyRef::new(buf))
295        }
296    }
297
298    /// Obtain a ciphertext from bytes
299    ///
300    /// Returns None if the ciphertext is not the correct length.
301    pub fn ciphertext_from_bytes<'a>(&self, buf: &'a [u8]) -> Option<CiphertextRef<'a>> {
302        if self.length_ciphertext() != buf.len() {
303            None
304        } else {
305            Some(CiphertextRef::new(buf))
306        }
307    }
308
309    /// Obtain a secret key from bytes
310    ///
311    /// Returns None if the shared secret is not the correct length.
312    pub fn shared_secret_from_bytes<'a>(&self, buf: &'a [u8]) -> Option<SharedSecretRef<'a>> {
313        if self.length_shared_secret() != buf.len() {
314            None
315        } else {
316            Some(SharedSecretRef::new(buf))
317        }
318    }
319
320    /// Generate a new keypair
321    pub fn keypair(&self) -> Result<(PublicKey, SecretKey)> {
322        let kem = unsafe { self.kem.as_ref() };
323        let func = kem.keypair.unwrap();
324        let mut pk = PublicKey {
325            bytes: Vec::with_capacity(kem.length_public_key),
326        };
327        let mut sk = SecretKey {
328            bytes: Vec::with_capacity(kem.length_secret_key),
329        };
330        let status = unsafe { func(pk.bytes.as_mut_ptr(), sk.bytes.as_mut_ptr()) };
331        status_to_result(status)?;
332        // update the lengths of the vecs
333        // this is safe to do, as we have initialised them now.
334        unsafe {
335            pk.bytes.set_len(kem.length_public_key);
336            sk.bytes.set_len(kem.length_secret_key);
337        }
338        Ok((pk, sk))
339    }
340
341    /// Encapsulate to the provided public key
342    pub fn encapsulate<'a, P: Into<PublicKeyRef<'a>>>(
343        &self,
344        pk: P,
345    ) -> Result<(Ciphertext, SharedSecret)> {
346        let pk = pk.into();
347        if pk.bytes.len() != self.length_public_key() {
348            return Err(Error::InvalidLength);
349        }
350        let kem = unsafe { self.kem.as_ref() };
351        let func = kem.encaps.unwrap();
352        let mut ct = Ciphertext {
353            bytes: Vec::with_capacity(kem.length_ciphertext),
354        };
355        let mut ss = SharedSecret {
356            bytes: Vec::with_capacity(kem.length_shared_secret),
357        };
358        // call encapsulate
359        let status = unsafe {
360            func(
361                ct.bytes.as_mut_ptr(),
362                ss.bytes.as_mut_ptr(),
363                pk.bytes.as_ptr(),
364            )
365        };
366        status_to_result(status)?;
367        // update the lengths of the vecs
368        // this is safe to do, as we have initialised them now.
369        unsafe {
370            ct.bytes.set_len(kem.length_ciphertext);
371            ss.bytes.set_len(kem.length_shared_secret);
372        }
373        Ok((ct, ss))
374    }
375
376    /// Decapsulate the provided ciphertext
377    pub fn decapsulate<'a, 'b, S: Into<SecretKeyRef<'a>>, C: Into<CiphertextRef<'b>>>(
378        &self,
379        sk: S,
380        ct: C,
381    ) -> Result<SharedSecret> {
382        let kem = unsafe { self.kem.as_ref() };
383        let sk = sk.into();
384        let ct = ct.into();
385        if sk.bytes.len() != self.length_secret_key() || ct.bytes.len() != self.length_ciphertext()
386        {
387            return Err(Error::InvalidLength);
388        }
389        let mut ss = SharedSecret {
390            bytes: Vec::with_capacity(kem.length_shared_secret),
391        };
392        let func = kem.decaps.unwrap();
393        // Call decapsulate
394        let status = unsafe { func(ss.bytes.as_mut_ptr(), ct.bytes.as_ptr(), sk.bytes.as_ptr()) };
395        status_to_result(status)?;
396        // update the lengths of the vecs
397        // this is safe to do, as we have initialised them now.
398        unsafe { ss.bytes.set_len(kem.length_shared_secret) };
399        Ok(ss)
400    }
401}