Skip to main content

sntrup_sys/
lib.rs

1//! Rust FFI bindings over the extracted, deduplicated SUPERCOP Streamlined
2//! NTRU Prime sources in `vendor/`. Every module wraps one parameter set,
3//! all sharing one compiled copy of `vendor/common/` (see
4//! `vendor/NOTICE.md` for why the split between shared and per-parameter-
5//! set code is drawn where it is). Each enabled parameter set gets its own
6//! module (gated by the matching Cargo feature), exposing `keypair()`,
7//! `encapsulate(pk)`, and `decapsulate(c, sk)`.
8//!
9//! # Randomness
10//!
11//! The vendored C code calls a single external `randombytes` C function for
12//! all key generation and encapsulation randomness. This crate implements
13//! it here using `getrandom` (the OS CSPRNG), satisfying the one external
14//! symbol every vendored directory expects (see vendor/NOTICE.md).
15
16/// # Safety
17///
18/// `buf` must be valid for writes of `buf_len` bytes and not aliased by any
19/// other live reference for the duration of this call. The vendored C code
20/// upholds this by construction (it always passes a real buffer of exactly
21/// `buf_len` bytes) but Rust can't verify that across the FFI boundary, so
22/// the contract is on the caller, hence `unsafe fn`.
23#[unsafe(no_mangle)]
24pub unsafe extern "C" fn randombytes(buf: *mut u8, buf_len: u64) {
25    let slice = unsafe { std::slice::from_raw_parts_mut(buf, buf_len as usize) };
26    getrandom::fill(slice).expect("OS randomness source failed");
27}
28
29#[cfg(feature = "sntrup653")]
30pub mod sntrup653 {
31    use std::os::raw::c_int;
32
33    pub const PUBLIC_KEY_BYTES: usize = 994;
34    pub const SECRET_KEY_BYTES: usize = 1518;
35    pub const CIPHERTEXT_BYTES: usize = 897;
36    pub const SHARED_SECRET_BYTES: usize = 32;
37
38    unsafe extern "C" {
39        fn sntrup653_ref_crypto_kem_keypair(pk: *mut u8, sk: *mut u8) -> c_int;
40        fn sntrup653_ref_crypto_kem_enc(c: *mut u8, k: *mut u8, pk: *const u8) -> c_int;
41        fn sntrup653_ref_crypto_kem_dec(k: *mut u8, c: *const u8, sk: *const u8) -> c_int;
42    }
43
44    /// Generate a fresh keypair. Returns `(public_key, secret_key)`.
45    pub fn keypair() -> (Vec<u8>, Vec<u8>) {
46        let mut pk = vec![0u8; PUBLIC_KEY_BYTES];
47        let mut sk = vec![0u8; SECRET_KEY_BYTES];
48        let rc = unsafe { sntrup653_ref_crypto_kem_keypair(pk.as_mut_ptr(), sk.as_mut_ptr()) };
49        assert_eq!(rc, 0, "sntrup653_ref_crypto_kem_keypair failed");
50        (pk, sk)
51    }
52
53    /// Encapsulate against `pk`. Returns `(ciphertext, shared_secret)`.
54    pub fn encapsulate(pk: &[u8]) -> (Vec<u8>, Vec<u8>) {
55        assert_eq!(pk.len(), PUBLIC_KEY_BYTES, "invalid public key length");
56        let mut c = vec![0u8; CIPHERTEXT_BYTES];
57        let mut ss = vec![0u8; SHARED_SECRET_BYTES];
58        let rc =
59            unsafe { sntrup653_ref_crypto_kem_enc(c.as_mut_ptr(), ss.as_mut_ptr(), pk.as_ptr()) };
60        assert_eq!(rc, 0, "sntrup653_ref_crypto_kem_enc failed");
61        (c, ss)
62    }
63
64    /// Decapsulate `c` using `sk`. Returns the shared secret.
65    ///
66    /// Per the Streamlined NTRU Prime KEM spec this always returns *some*
67    /// 32-byte value, even for an invalid/malformed ciphertext (implicit
68    /// rejection) -- it does not signal failure via the return value, by
69    /// design, to avoid a decryption-failure oracle.
70    pub fn decapsulate(c: &[u8], sk: &[u8]) -> Vec<u8> {
71        assert_eq!(c.len(), CIPHERTEXT_BYTES, "invalid ciphertext length");
72        assert_eq!(sk.len(), SECRET_KEY_BYTES, "invalid secret key length");
73        let mut ss = vec![0u8; SHARED_SECRET_BYTES];
74        unsafe { sntrup653_ref_crypto_kem_dec(ss.as_mut_ptr(), c.as_ptr(), sk.as_ptr()) };
75        ss
76    }
77}
78
79#[cfg(feature = "sntrup761")]
80pub mod sntrup761 {
81    use std::os::raw::c_int;
82
83    pub const PUBLIC_KEY_BYTES: usize = 1158;
84    pub const SECRET_KEY_BYTES: usize = 1763;
85    pub const CIPHERTEXT_BYTES: usize = 1039;
86    pub const SHARED_SECRET_BYTES: usize = 32;
87
88    unsafe extern "C" {
89        fn sntrup761_ref_crypto_kem_keypair(pk: *mut u8, sk: *mut u8) -> c_int;
90        fn sntrup761_ref_crypto_kem_enc(c: *mut u8, k: *mut u8, pk: *const u8) -> c_int;
91        fn sntrup761_ref_crypto_kem_dec(k: *mut u8, c: *const u8, sk: *const u8) -> c_int;
92    }
93
94    /// Generate a fresh keypair. Returns `(public_key, secret_key)`.
95    pub fn keypair() -> (Vec<u8>, Vec<u8>) {
96        let mut pk = vec![0u8; PUBLIC_KEY_BYTES];
97        let mut sk = vec![0u8; SECRET_KEY_BYTES];
98        let rc = unsafe { sntrup761_ref_crypto_kem_keypair(pk.as_mut_ptr(), sk.as_mut_ptr()) };
99        assert_eq!(rc, 0, "sntrup761_ref_crypto_kem_keypair failed");
100        (pk, sk)
101    }
102
103    /// Encapsulate against `pk`. Returns `(ciphertext, shared_secret)`.
104    pub fn encapsulate(pk: &[u8]) -> (Vec<u8>, Vec<u8>) {
105        assert_eq!(pk.len(), PUBLIC_KEY_BYTES, "invalid public key length");
106        let mut c = vec![0u8; CIPHERTEXT_BYTES];
107        let mut ss = vec![0u8; SHARED_SECRET_BYTES];
108        let rc =
109            unsafe { sntrup761_ref_crypto_kem_enc(c.as_mut_ptr(), ss.as_mut_ptr(), pk.as_ptr()) };
110        assert_eq!(rc, 0, "sntrup761_ref_crypto_kem_enc failed");
111        (c, ss)
112    }
113
114    /// Decapsulate `c` using `sk`. Returns the shared secret.
115    ///
116    /// Per the Streamlined NTRU Prime KEM spec this always returns *some*
117    /// 32-byte value, even for an invalid/malformed ciphertext (implicit
118    /// rejection) -- it does not signal failure via the return value, by
119    /// design, to avoid a decryption-failure oracle.
120    pub fn decapsulate(c: &[u8], sk: &[u8]) -> Vec<u8> {
121        assert_eq!(c.len(), CIPHERTEXT_BYTES, "invalid ciphertext length");
122        assert_eq!(sk.len(), SECRET_KEY_BYTES, "invalid secret key length");
123        let mut ss = vec![0u8; SHARED_SECRET_BYTES];
124        unsafe { sntrup761_ref_crypto_kem_dec(ss.as_mut_ptr(), c.as_ptr(), sk.as_ptr()) };
125        ss
126    }
127}
128
129#[cfg(feature = "sntrup857")]
130pub mod sntrup857 {
131    use std::os::raw::c_int;
132
133    pub const PUBLIC_KEY_BYTES: usize = 1322;
134    pub const SECRET_KEY_BYTES: usize = 1999;
135    pub const CIPHERTEXT_BYTES: usize = 1184;
136    pub const SHARED_SECRET_BYTES: usize = 32;
137
138    unsafe extern "C" {
139        fn sntrup857_ref_crypto_kem_keypair(pk: *mut u8, sk: *mut u8) -> c_int;
140        fn sntrup857_ref_crypto_kem_enc(c: *mut u8, k: *mut u8, pk: *const u8) -> c_int;
141        fn sntrup857_ref_crypto_kem_dec(k: *mut u8, c: *const u8, sk: *const u8) -> c_int;
142    }
143
144    /// Generate a fresh keypair. Returns `(public_key, secret_key)`.
145    pub fn keypair() -> (Vec<u8>, Vec<u8>) {
146        let mut pk = vec![0u8; PUBLIC_KEY_BYTES];
147        let mut sk = vec![0u8; SECRET_KEY_BYTES];
148        let rc = unsafe { sntrup857_ref_crypto_kem_keypair(pk.as_mut_ptr(), sk.as_mut_ptr()) };
149        assert_eq!(rc, 0, "sntrup857_ref_crypto_kem_keypair failed");
150        (pk, sk)
151    }
152
153    /// Encapsulate against `pk`. Returns `(ciphertext, shared_secret)`.
154    pub fn encapsulate(pk: &[u8]) -> (Vec<u8>, Vec<u8>) {
155        assert_eq!(pk.len(), PUBLIC_KEY_BYTES, "invalid public key length");
156        let mut c = vec![0u8; CIPHERTEXT_BYTES];
157        let mut ss = vec![0u8; SHARED_SECRET_BYTES];
158        let rc =
159            unsafe { sntrup857_ref_crypto_kem_enc(c.as_mut_ptr(), ss.as_mut_ptr(), pk.as_ptr()) };
160        assert_eq!(rc, 0, "sntrup857_ref_crypto_kem_enc failed");
161        (c, ss)
162    }
163
164    /// Decapsulate `c` using `sk`. Returns the shared secret.
165    ///
166    /// Per the Streamlined NTRU Prime KEM spec this always returns *some*
167    /// 32-byte value, even for an invalid/malformed ciphertext (implicit
168    /// rejection) -- it does not signal failure via the return value, by
169    /// design, to avoid a decryption-failure oracle.
170    pub fn decapsulate(c: &[u8], sk: &[u8]) -> Vec<u8> {
171        assert_eq!(c.len(), CIPHERTEXT_BYTES, "invalid ciphertext length");
172        assert_eq!(sk.len(), SECRET_KEY_BYTES, "invalid secret key length");
173        let mut ss = vec![0u8; SHARED_SECRET_BYTES];
174        unsafe { sntrup857_ref_crypto_kem_dec(ss.as_mut_ptr(), c.as_ptr(), sk.as_ptr()) };
175        ss
176    }
177}
178
179#[cfg(feature = "sntrup953")]
180pub mod sntrup953 {
181    use std::os::raw::c_int;
182
183    pub const PUBLIC_KEY_BYTES: usize = 1505;
184    pub const SECRET_KEY_BYTES: usize = 2254;
185    pub const CIPHERTEXT_BYTES: usize = 1349;
186    pub const SHARED_SECRET_BYTES: usize = 32;
187
188    unsafe extern "C" {
189        fn sntrup953_ref_crypto_kem_keypair(pk: *mut u8, sk: *mut u8) -> c_int;
190        fn sntrup953_ref_crypto_kem_enc(c: *mut u8, k: *mut u8, pk: *const u8) -> c_int;
191        fn sntrup953_ref_crypto_kem_dec(k: *mut u8, c: *const u8, sk: *const u8) -> c_int;
192    }
193
194    /// Generate a fresh keypair. Returns `(public_key, secret_key)`.
195    pub fn keypair() -> (Vec<u8>, Vec<u8>) {
196        let mut pk = vec![0u8; PUBLIC_KEY_BYTES];
197        let mut sk = vec![0u8; SECRET_KEY_BYTES];
198        let rc = unsafe { sntrup953_ref_crypto_kem_keypair(pk.as_mut_ptr(), sk.as_mut_ptr()) };
199        assert_eq!(rc, 0, "sntrup953_ref_crypto_kem_keypair failed");
200        (pk, sk)
201    }
202
203    /// Encapsulate against `pk`. Returns `(ciphertext, shared_secret)`.
204    pub fn encapsulate(pk: &[u8]) -> (Vec<u8>, Vec<u8>) {
205        assert_eq!(pk.len(), PUBLIC_KEY_BYTES, "invalid public key length");
206        let mut c = vec![0u8; CIPHERTEXT_BYTES];
207        let mut ss = vec![0u8; SHARED_SECRET_BYTES];
208        let rc =
209            unsafe { sntrup953_ref_crypto_kem_enc(c.as_mut_ptr(), ss.as_mut_ptr(), pk.as_ptr()) };
210        assert_eq!(rc, 0, "sntrup953_ref_crypto_kem_enc failed");
211        (c, ss)
212    }
213
214    /// Decapsulate `c` using `sk`. Returns the shared secret.
215    ///
216    /// Per the Streamlined NTRU Prime KEM spec this always returns *some*
217    /// 32-byte value, even for an invalid/malformed ciphertext (implicit
218    /// rejection) -- it does not signal failure via the return value, by
219    /// design, to avoid a decryption-failure oracle.
220    pub fn decapsulate(c: &[u8], sk: &[u8]) -> Vec<u8> {
221        assert_eq!(c.len(), CIPHERTEXT_BYTES, "invalid ciphertext length");
222        assert_eq!(sk.len(), SECRET_KEY_BYTES, "invalid secret key length");
223        let mut ss = vec![0u8; SHARED_SECRET_BYTES];
224        unsafe { sntrup953_ref_crypto_kem_dec(ss.as_mut_ptr(), c.as_ptr(), sk.as_ptr()) };
225        ss
226    }
227}
228
229#[cfg(feature = "sntrup1013")]
230pub mod sntrup1013 {
231    use std::os::raw::c_int;
232
233    pub const PUBLIC_KEY_BYTES: usize = 1623;
234    pub const SECRET_KEY_BYTES: usize = 2417;
235    pub const CIPHERTEXT_BYTES: usize = 1455;
236    pub const SHARED_SECRET_BYTES: usize = 32;
237
238    unsafe extern "C" {
239        fn sntrup1013_ref_crypto_kem_keypair(pk: *mut u8, sk: *mut u8) -> c_int;
240        fn sntrup1013_ref_crypto_kem_enc(c: *mut u8, k: *mut u8, pk: *const u8) -> c_int;
241        fn sntrup1013_ref_crypto_kem_dec(k: *mut u8, c: *const u8, sk: *const u8) -> c_int;
242    }
243
244    /// Generate a fresh keypair. Returns `(public_key, secret_key)`.
245    pub fn keypair() -> (Vec<u8>, Vec<u8>) {
246        let mut pk = vec![0u8; PUBLIC_KEY_BYTES];
247        let mut sk = vec![0u8; SECRET_KEY_BYTES];
248        let rc = unsafe { sntrup1013_ref_crypto_kem_keypair(pk.as_mut_ptr(), sk.as_mut_ptr()) };
249        assert_eq!(rc, 0, "sntrup1013_ref_crypto_kem_keypair failed");
250        (pk, sk)
251    }
252
253    /// Encapsulate against `pk`. Returns `(ciphertext, shared_secret)`.
254    pub fn encapsulate(pk: &[u8]) -> (Vec<u8>, Vec<u8>) {
255        assert_eq!(pk.len(), PUBLIC_KEY_BYTES, "invalid public key length");
256        let mut c = vec![0u8; CIPHERTEXT_BYTES];
257        let mut ss = vec![0u8; SHARED_SECRET_BYTES];
258        let rc =
259            unsafe { sntrup1013_ref_crypto_kem_enc(c.as_mut_ptr(), ss.as_mut_ptr(), pk.as_ptr()) };
260        assert_eq!(rc, 0, "sntrup1013_ref_crypto_kem_enc failed");
261        (c, ss)
262    }
263
264    /// Decapsulate `c` using `sk`. Returns the shared secret.
265    ///
266    /// Per the Streamlined NTRU Prime KEM spec this always returns *some*
267    /// 32-byte value, even for an invalid/malformed ciphertext (implicit
268    /// rejection) -- it does not signal failure via the return value, by
269    /// design, to avoid a decryption-failure oracle.
270    pub fn decapsulate(c: &[u8], sk: &[u8]) -> Vec<u8> {
271        assert_eq!(c.len(), CIPHERTEXT_BYTES, "invalid ciphertext length");
272        assert_eq!(sk.len(), SECRET_KEY_BYTES, "invalid secret key length");
273        let mut ss = vec![0u8; SHARED_SECRET_BYTES];
274        unsafe { sntrup1013_ref_crypto_kem_dec(ss.as_mut_ptr(), c.as_ptr(), sk.as_ptr()) };
275        ss
276    }
277}
278
279#[cfg(feature = "sntrup1277")]
280pub mod sntrup1277 {
281    use std::os::raw::c_int;
282
283    pub const PUBLIC_KEY_BYTES: usize = 2067;
284    pub const SECRET_KEY_BYTES: usize = 3059;
285    pub const CIPHERTEXT_BYTES: usize = 1847;
286    pub const SHARED_SECRET_BYTES: usize = 32;
287
288    unsafe extern "C" {
289        fn sntrup1277_ref_crypto_kem_keypair(pk: *mut u8, sk: *mut u8) -> c_int;
290        fn sntrup1277_ref_crypto_kem_enc(c: *mut u8, k: *mut u8, pk: *const u8) -> c_int;
291        fn sntrup1277_ref_crypto_kem_dec(k: *mut u8, c: *const u8, sk: *const u8) -> c_int;
292    }
293
294    /// Generate a fresh keypair. Returns `(public_key, secret_key)`.
295    pub fn keypair() -> (Vec<u8>, Vec<u8>) {
296        let mut pk = vec![0u8; PUBLIC_KEY_BYTES];
297        let mut sk = vec![0u8; SECRET_KEY_BYTES];
298        let rc = unsafe { sntrup1277_ref_crypto_kem_keypair(pk.as_mut_ptr(), sk.as_mut_ptr()) };
299        assert_eq!(rc, 0, "sntrup1277_ref_crypto_kem_keypair failed");
300        (pk, sk)
301    }
302
303    /// Encapsulate against `pk`. Returns `(ciphertext, shared_secret)`.
304    pub fn encapsulate(pk: &[u8]) -> (Vec<u8>, Vec<u8>) {
305        assert_eq!(pk.len(), PUBLIC_KEY_BYTES, "invalid public key length");
306        let mut c = vec![0u8; CIPHERTEXT_BYTES];
307        let mut ss = vec![0u8; SHARED_SECRET_BYTES];
308        let rc =
309            unsafe { sntrup1277_ref_crypto_kem_enc(c.as_mut_ptr(), ss.as_mut_ptr(), pk.as_ptr()) };
310        assert_eq!(rc, 0, "sntrup1277_ref_crypto_kem_enc failed");
311        (c, ss)
312    }
313
314    /// Decapsulate `c` using `sk`. Returns the shared secret.
315    ///
316    /// Per the Streamlined NTRU Prime KEM spec this always returns *some*
317    /// 32-byte value, even for an invalid/malformed ciphertext (implicit
318    /// rejection) -- it does not signal failure via the return value, by
319    /// design, to avoid a decryption-failure oracle.
320    pub fn decapsulate(c: &[u8], sk: &[u8]) -> Vec<u8> {
321        assert_eq!(c.len(), CIPHERTEXT_BYTES, "invalid ciphertext length");
322        assert_eq!(sk.len(), SECRET_KEY_BYTES, "invalid secret key length");
323        let mut ss = vec![0u8; SHARED_SECRET_BYTES];
324        unsafe { sntrup1277_ref_crypto_kem_dec(ss.as_mut_ptr(), c.as_ptr(), sk.as_ptr()) };
325        ss
326    }
327}