pqcrypto_dilithium/
dilithium3.rs

1//! dilithium3
2//!
3//! These bindings use the clean version from [PQClean][pqc]
4//!
5//! # Example
6//! ```
7//! // if using pqcrypto-dilithium
8//! use pqcrypto_dilithium::dilithium3::*;
9//! // or if using the pqcrypto crate:
10//! // use pqcrypto::sign::dilithium3::*;
11//! let message = vec![0, 1, 2, 3, 4, 5];
12//! let (pk, sk) = keypair();
13//! let sm = sign(&message, &sk);
14//! let verifiedmsg = open(&sm, &pk).unwrap();
15//! assert!(verifiedmsg == message);
16//! ```
17//!
18//! [pqc]: https://github.com/pqclean/pqclean/
19
20// This file is generated.
21
22#[cfg(feature = "serialization")]
23use serde::{Deserialize, Serialize};
24#[cfg(feature = "serialization")]
25use serde_big_array::BigArray;
26
27use crate::ffi;
28use alloc::vec::Vec;
29use pqcrypto_traits::sign as primitive;
30use pqcrypto_traits::{Error, Result};
31
32macro_rules! simple_struct {
33    ($type: ident, $size: expr) => {
34        #[derive(Clone, Copy)]
35        #[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
36        pub struct $type(
37            #[cfg_attr(feature = "serialization", serde(with = "BigArray"))] [u8; $size],
38        );
39
40        impl $type {
41            /// Generates an uninitialized object
42            ///
43            /// Used to pass to ``ffi`` interfaces.
44            ///
45            /// Internal use only!
46            fn new() -> Self {
47                $type([0u8; $size])
48            }
49        }
50
51        impl primitive::$type for $type {
52            /// Get this object as a byte slice
53            #[inline]
54            fn as_bytes(&self) -> &[u8] {
55                &self.0
56            }
57
58            /// Construct this object from a byte slice
59            fn from_bytes(bytes: &[u8]) -> Result<Self> {
60                if bytes.len() != $size {
61                    Err(Error::BadLength {
62                        name: stringify!($type),
63                        actual: bytes.len(),
64                        expected: $size,
65                    })
66                } else {
67                    let mut array = [0u8; $size];
68                    array.copy_from_slice(bytes);
69                    Ok($type(array))
70                }
71            }
72        }
73
74        impl PartialEq for $type {
75            /// By no means constant time comparison
76            fn eq(&self, other: &Self) -> bool {
77                self.0
78                    .iter()
79                    .zip(other.0.iter())
80                    .try_for_each(|(a, b)| if a == b { Ok(()) } else { Err(()) })
81                    .is_ok()
82            }
83        }
84    };
85}
86
87simple_struct!(
88    PublicKey,
89    ffi::PQCLEAN_DILITHIUM3_CLEAN_CRYPTO_PUBLICKEYBYTES
90);
91simple_struct!(
92    SecretKey,
93    ffi::PQCLEAN_DILITHIUM3_CLEAN_CRYPTO_SECRETKEYBYTES
94);
95
96#[derive(Clone, Copy)]
97#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
98pub struct DetachedSignature(
99    #[cfg_attr(feature = "serialization", serde(with = "BigArray"))]
100    [u8; ffi::PQCLEAN_DILITHIUM3_CLEAN_CRYPTO_BYTES],
101    usize,
102);
103
104// for internal use
105impl DetachedSignature {
106    fn new() -> Self {
107        DetachedSignature([0u8; ffi::PQCLEAN_DILITHIUM3_CLEAN_CRYPTO_BYTES], 0)
108    }
109}
110
111impl primitive::DetachedSignature for DetachedSignature {
112    /// Get this object as a byte slice
113    #[inline]
114    fn as_bytes(&self) -> &[u8] {
115        &self.0[..self.1]
116    }
117
118    #[inline]
119    fn from_bytes(bytes: &[u8]) -> Result<Self> {
120        let actual = bytes.len();
121        let expected = ffi::PQCLEAN_DILITHIUM3_CLEAN_CRYPTO_BYTES;
122        if actual > expected {
123            return Err(Error::BadLength {
124                name: "DetachedSignature",
125                actual,
126                expected,
127            });
128        }
129        let mut array = [0u8; ffi::PQCLEAN_DILITHIUM3_CLEAN_CRYPTO_BYTES];
130        array[..bytes.len()].copy_from_slice(bytes);
131        Ok(DetachedSignature(array, actual))
132    }
133}
134
135#[derive(Clone)]
136#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
137pub struct SignedMessage(Vec<u8>);
138impl primitive::SignedMessage for SignedMessage {
139    /// Get this object as a byte slice
140    #[inline]
141    fn as_bytes(&self) -> &[u8] {
142        self.0.as_slice()
143    }
144
145    /// Construct this object from a byte slice
146    #[inline]
147    fn from_bytes(bytes: &[u8]) -> Result<Self> {
148        Ok(SignedMessage(bytes.to_vec()))
149    }
150}
151
152impl SignedMessage {
153    pub fn len(&self) -> usize {
154        self.0.len()
155    }
156}
157
158/// Get the number of bytes for a public key
159pub const fn public_key_bytes() -> usize {
160    ffi::PQCLEAN_DILITHIUM3_CLEAN_CRYPTO_PUBLICKEYBYTES
161}
162
163/// Get the number of bytes for a secret key
164pub const fn secret_key_bytes() -> usize {
165    ffi::PQCLEAN_DILITHIUM3_CLEAN_CRYPTO_SECRETKEYBYTES
166}
167
168/// Get the number of bytes that a signature occupies
169pub const fn signature_bytes() -> usize {
170    ffi::PQCLEAN_DILITHIUM3_CLEAN_CRYPTO_BYTES
171}
172
173macro_rules! gen_keypair {
174    ($variant:ident) => {{
175        let mut pk = PublicKey::new();
176        let mut sk = SecretKey::new();
177        assert_eq!(
178            unsafe { ffi::$variant(pk.0.as_mut_ptr(), sk.0.as_mut_ptr()) },
179            0
180        );
181        (pk, sk)
182    }};
183}
184
185/// Generate a dilithium3 keypair
186pub fn keypair() -> (PublicKey, SecretKey) {
187    #[cfg(all(enable_x86_avx2, feature = "avx2"))]
188    {
189        if std::is_x86_feature_detected!("avx2") {
190            return gen_keypair!(PQCLEAN_DILITHIUM3_AVX2_crypto_sign_keypair);
191        }
192    }
193    #[cfg(all(enable_aarch64_neon, feature = "neon"))]
194    {
195        // always use AArch64 code, when target is detected as all AArch64 targets have NEON
196        // support, and std::is_aarch64_feature_detected!("neon") works only with Rust nightly at
197        // the moment
198        if true {
199            return gen_keypair!(PQCLEAN_DILITHIUM3_AARCH64_crypto_sign_keypair);
200        }
201    }
202    gen_keypair!(PQCLEAN_DILITHIUM3_CLEAN_crypto_sign_keypair)
203}
204
205macro_rules! gen_signature {
206    ($variant:ident, $msg:ident, $sk:ident) => {{
207        let max_len = $msg.len() + signature_bytes();
208        let mut signed_msg = Vec::with_capacity(max_len);
209        let mut smlen: usize = 0;
210        unsafe {
211            ffi::$variant(
212                signed_msg.as_mut_ptr(),
213                &mut smlen as *mut usize,
214                $msg.as_ptr(),
215                $msg.len(),
216                $sk.0.as_ptr(),
217            );
218            debug_assert!(smlen <= max_len, "exceeded vector capacity");
219            signed_msg.set_len(smlen);
220        }
221        SignedMessage(signed_msg)
222    }};
223}
224
225/// Sign the message and return the signed message.
226pub fn sign(msg: &[u8], sk: &SecretKey) -> SignedMessage {
227    #[cfg(all(enable_x86_avx2, feature = "avx2"))]
228    {
229        if std::is_x86_feature_detected!("avx2") {
230            return gen_signature!(PQCLEAN_DILITHIUM3_AVX2_crypto_sign, msg, sk);
231        }
232    }
233    #[cfg(all(enable_aarch64_neon, feature = "neon"))]
234    {
235        if true {
236            return gen_signature!(PQCLEAN_DILITHIUM3_AARCH64_crypto_sign, msg, sk);
237        }
238    }
239    gen_signature!(PQCLEAN_DILITHIUM3_CLEAN_crypto_sign, msg, sk)
240}
241
242macro_rules! open_signed {
243    ($variant:ident, $sm:ident, $pk:ident) => {{
244        let mut m: Vec<u8> = Vec::with_capacity($sm.len());
245        let mut mlen: usize = 0;
246        match unsafe {
247            ffi::$variant(
248                m.as_mut_ptr(),
249                &mut mlen as *mut usize,
250                $sm.0.as_ptr(),
251                $sm.len(),
252                $pk.0.as_ptr(),
253            )
254        } {
255            0 => {
256                unsafe { m.set_len(mlen) };
257                Ok(m)
258            }
259            -1 => Err(primitive::VerificationError::InvalidSignature),
260            _ => Err(primitive::VerificationError::UnknownVerificationError),
261        }
262    }};
263}
264
265/// Open the signed message and if verification succeeds return the message
266pub fn open(
267    sm: &SignedMessage,
268    pk: &PublicKey,
269) -> core::result::Result<Vec<u8>, primitive::VerificationError> {
270    #[cfg(all(enable_x86_avx2, feature = "avx2"))]
271    {
272        if std::is_x86_feature_detected!("avx2") {
273            return open_signed!(PQCLEAN_DILITHIUM3_AVX2_crypto_sign_open, sm, pk);
274        }
275    }
276    #[cfg(all(enable_aarch64_neon, feature = "neon"))]
277    {
278        if true {
279            return open_signed!(PQCLEAN_DILITHIUM3_AARCH64_crypto_sign_open, sm, pk);
280        }
281    }
282    open_signed!(PQCLEAN_DILITHIUM3_CLEAN_crypto_sign_open, sm, pk)
283}
284
285macro_rules! detached_signature {
286    ($variant:ident, $msg:ident, $sk:ident) => {{
287        let mut sig = DetachedSignature::new();
288        unsafe {
289            ffi::$variant(
290                sig.0.as_mut_ptr(),
291                &mut sig.1 as *mut usize,
292                $msg.as_ptr(),
293                $msg.len(),
294                $sk.0.as_ptr(),
295            );
296        }
297        sig
298    }};
299}
300
301/// Create a detached signature on the message
302pub fn detached_sign(msg: &[u8], sk: &SecretKey) -> DetachedSignature {
303    #[cfg(all(enable_x86_avx2, feature = "avx2"))]
304    {
305        if std::is_x86_feature_detected!("avx2") {
306            return detached_signature!(PQCLEAN_DILITHIUM3_AVX2_crypto_sign_signature, msg, sk);
307        }
308    }
309    #[cfg(all(enable_aarch64_neon, feature = "neon"))]
310    {
311        if true {
312            return detached_signature!(PQCLEAN_DILITHIUM3_AARCH64_crypto_sign_signature, msg, sk);
313        }
314    }
315    detached_signature!(PQCLEAN_DILITHIUM3_CLEAN_crypto_sign_signature, msg, sk)
316}
317
318macro_rules! verify_detached_sig {
319    ($variant:ident, $sig:ident, $msg:ident, $pk:ident) => {{
320        let res = unsafe {
321            ffi::$variant(
322                $sig.0.as_ptr(),
323                $sig.1,
324                $msg.as_ptr(),
325                $msg.len(),
326                $pk.0.as_ptr(),
327            )
328        };
329        match res {
330            0 => Ok(()),
331            -1 => Err(primitive::VerificationError::InvalidSignature),
332            _ => Err(primitive::VerificationError::UnknownVerificationError),
333        }
334    }};
335}
336
337/// Verify the detached signature
338pub fn verify_detached_signature(
339    sig: &DetachedSignature,
340    msg: &[u8],
341    pk: &PublicKey,
342) -> core::result::Result<(), primitive::VerificationError> {
343    #[cfg(all(enable_x86_avx2, feature = "avx2"))]
344    {
345        if std::is_x86_feature_detected!("avx2") {
346            return verify_detached_sig!(PQCLEAN_DILITHIUM3_AVX2_crypto_sign_verify, sig, msg, pk);
347        }
348    }
349    #[cfg(all(enable_aarch64_neon, feature = "neon"))]
350    {
351        if true {
352            return verify_detached_sig!(
353                PQCLEAN_DILITHIUM3_AARCH64_crypto_sign_verify,
354                sig,
355                msg,
356                pk
357            );
358        }
359    }
360    verify_detached_sig!(PQCLEAN_DILITHIUM3_CLEAN_crypto_sign_verify, sig, msg, pk)
361}
362
363#[cfg(test)]
364mod test {
365    use super::*;
366    use rand::prelude::*;
367
368    #[test]
369    pub fn test_sign() {
370        let mut rng = rand::thread_rng();
371        let len: u16 = rng.gen();
372
373        let message = (0..len).map(|_| rng.gen::<u8>()).collect::<Vec<_>>();
374        let (pk, sk) = keypair();
375        let sm = sign(&message, &sk);
376        let verifiedmsg = open(&sm, &pk).unwrap();
377        assert!(verifiedmsg == message);
378    }
379
380    #[test]
381    pub fn test_sign_detached() {
382        let mut rng = rand::thread_rng();
383        let len: u16 = rng.gen();
384        let message = (0..len).map(|_| rng.gen::<u8>()).collect::<Vec<_>>();
385
386        let (pk, sk) = keypair();
387        let sig = detached_sign(&message, &sk);
388        assert!(verify_detached_signature(&sig, &message, &pk).is_ok());
389        assert!(!verify_detached_signature(&sig, &message[..message.len() - 1], &pk).is_ok());
390    }
391}