pqcrypto_mldsa/
mldsa44.rs

1//! ml-dsa-44
2//!
3//! These bindings use the clean version from [PQClean][pqc]
4//!
5//! # Example
6//! ```
7//! // if using pqcrypto-mldsa
8//! use pqcrypto_mldsa::mldsa44::*;
9//! // or if using the pqcrypto crate:
10//! // use pqcrypto::sign::mldsa44::*;
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
32use paste::paste;
33
34#[cfg(feature = "std")]
35use std::fmt;
36
37macro_rules! simple_struct {
38    ($type: ident, $size: expr) => {
39        #[derive(Clone, Copy)]
40        #[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
41        pub struct $type(
42            #[cfg_attr(feature = "serialization", serde(with = "BigArray"))] [u8; $size],
43        );
44
45        impl $type {
46            /// Generates an uninitialized object
47            ///
48            /// Used to pass to ``ffi`` interfaces.
49            ///
50            /// Internal use only!
51            fn new() -> Self {
52                $type([0u8; $size])
53            }
54        }
55
56        impl primitive::$type for $type {
57            /// Get this object as a byte slice
58            #[inline]
59            fn as_bytes(&self) -> &[u8] {
60                &self.0
61            }
62
63            /// Construct this object from a byte slice
64            fn from_bytes(bytes: &[u8]) -> Result<Self> {
65                if bytes.len() != $size {
66                    Err(Error::BadLength {
67                        name: stringify!($type),
68                        actual: bytes.len(),
69                        expected: $size,
70                    })
71                } else {
72                    let mut array = [0u8; $size];
73                    array.copy_from_slice(bytes);
74                    Ok($type(array))
75                }
76            }
77        }
78
79        impl PartialEq for $type {
80            /// By no means constant time comparison
81            fn eq(&self, other: &Self) -> bool {
82                self.0
83                    .iter()
84                    .zip(other.0.iter())
85                    .try_for_each(|(a, b)| if a == b { Ok(()) } else { Err(()) })
86                    .is_ok()
87            }
88        }
89
90        #[cfg(feature = "std")]
91        impl fmt::Debug for $type {
92            /// Add a debug implementation that won't leak private values
93            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94                write!(f, "{} ({} bytes)", stringify!($type), self.0.len())
95            }
96        }
97    };
98}
99
100simple_struct!(PublicKey, ffi::PQCLEAN_MLDSA44_CLEAN_CRYPTO_PUBLICKEYBYTES);
101simple_struct!(SecretKey, ffi::PQCLEAN_MLDSA44_CLEAN_CRYPTO_SECRETKEYBYTES);
102
103#[derive(Clone, Copy)]
104#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
105pub struct DetachedSignature(
106    #[cfg_attr(feature = "serialization", serde(with = "BigArray"))]
107    [u8; ffi::PQCLEAN_MLDSA44_CLEAN_CRYPTO_BYTES],
108    usize,
109);
110
111// for internal use
112impl DetachedSignature {
113    fn new() -> Self {
114        DetachedSignature([0u8; ffi::PQCLEAN_MLDSA44_CLEAN_CRYPTO_BYTES], 0)
115    }
116}
117
118impl primitive::DetachedSignature for DetachedSignature {
119    /// Get this object as a byte slice
120    #[inline]
121    fn as_bytes(&self) -> &[u8] {
122        &self.0[..self.1]
123    }
124
125    #[inline]
126    fn from_bytes(bytes: &[u8]) -> Result<Self> {
127        let actual = bytes.len();
128        let expected = ffi::PQCLEAN_MLDSA44_CLEAN_CRYPTO_BYTES;
129        if actual > expected {
130            return Err(Error::BadLength {
131                name: "DetachedSignature",
132                actual,
133                expected,
134            });
135        }
136        let mut array = [0u8; ffi::PQCLEAN_MLDSA44_CLEAN_CRYPTO_BYTES];
137        array[..bytes.len()].copy_from_slice(bytes);
138        Ok(DetachedSignature(array, actual))
139    }
140}
141
142#[derive(Clone)]
143#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
144pub struct SignedMessage(Vec<u8>);
145impl primitive::SignedMessage for SignedMessage {
146    /// Get this object as a byte slice
147    #[inline]
148    fn as_bytes(&self) -> &[u8] {
149        self.0.as_slice()
150    }
151
152    /// Construct this object from a byte slice
153    #[inline]
154    fn from_bytes(bytes: &[u8]) -> Result<Self> {
155        Ok(SignedMessage(bytes.to_vec()))
156    }
157}
158
159impl SignedMessage {
160    pub fn len(&self) -> usize {
161        self.0.len()
162    }
163}
164
165/// Get the number of bytes for a public key
166pub const fn public_key_bytes() -> usize {
167    ffi::PQCLEAN_MLDSA44_CLEAN_CRYPTO_PUBLICKEYBYTES
168}
169
170/// Get the number of bytes for a secret key
171pub const fn secret_key_bytes() -> usize {
172    ffi::PQCLEAN_MLDSA44_CLEAN_CRYPTO_SECRETKEYBYTES
173}
174
175/// Get the number of bytes that a signature occupies
176pub const fn signature_bytes() -> usize {
177    ffi::PQCLEAN_MLDSA44_CLEAN_CRYPTO_BYTES
178}
179
180macro_rules! gen_keypair {
181    ($variant:ident) => {{
182        let mut pk = PublicKey::new();
183        let mut sk = SecretKey::new();
184        assert_eq!(
185            unsafe { ffi::$variant(pk.0.as_mut_ptr(), sk.0.as_mut_ptr()) },
186            0
187        );
188        (pk, sk)
189    }};
190}
191
192/// Generate a ml-dsa-44 keypair
193pub fn keypair() -> (PublicKey, SecretKey) {
194    #[cfg(all(enable_x86_avx2, feature = "avx2"))]
195    {
196        if std::is_x86_feature_detected!("avx2") {
197            return gen_keypair!(PQCLEAN_MLDSA44_AVX2_crypto_sign_keypair);
198        }
199    }
200    #[cfg(all(enable_aarch64_neon, feature = "neon"))]
201    {
202        // always use AArch64 code, when target is detected as all AArch64 targets have NEON
203        // support, and std::is_aarch64_feature_detected!("neon") works only with Rust nightly at
204        // the moment
205        if true {
206            return gen_keypair!(PQCLEAN_MLDSA44_AARCH64_crypto_sign_keypair);
207        }
208    }
209    gen_keypair!(PQCLEAN_MLDSA44_CLEAN_crypto_sign_keypair)
210}
211
212macro_rules! gen_signature {
213    ($variant:ident, $msg:ident, $sk:ident) => {{
214        let max_len = $msg.len() + signature_bytes();
215        let mut signed_msg = Vec::with_capacity(max_len);
216        let mut smlen: usize = 0;
217        unsafe {
218            paste! {
219                ffi::[<$variant _ctx>](
220                signed_msg.as_mut_ptr(),
221                &mut smlen as *mut usize,
222                $msg.as_ptr(),
223                $msg.len(),
224                core::ptr::null(), 0,
225                $sk.0.as_ptr(),
226            );
227            }
228            debug_assert!(smlen <= max_len, "exceeded vector capacity");
229            signed_msg.set_len(smlen);
230        }
231        SignedMessage(signed_msg)
232    }};
233}
234
235macro_rules! gen_signature_ctx {
236    ($variant:ident, $msg:ident, $ctx:ident, $sk:ident) => {{
237        let max_len = $msg.len() + signature_bytes();
238        let mut signed_msg = Vec::with_capacity(max_len);
239        let mut smlen: usize = 0;
240        unsafe {
241            ffi::$variant(
242                signed_msg.as_mut_ptr(),
243                &mut smlen as *mut usize,
244                $msg.as_ptr(),
245                $msg.len(),
246                $ctx.as_ptr(),
247                $ctx.len(),
248                $sk.0.as_ptr(),
249            );
250            debug_assert!(smlen <= max_len, "exceeded vector capacity");
251            signed_msg.set_len(smlen);
252        }
253        SignedMessage(signed_msg)
254    }};
255}
256
257/// Sign the message and return the signed message.
258pub fn sign(msg: &[u8], sk: &SecretKey) -> SignedMessage {
259    #[cfg(all(enable_x86_avx2, feature = "avx2"))]
260    {
261        if std::is_x86_feature_detected!("avx2") {
262            return gen_signature!(PQCLEAN_MLDSA44_AVX2_crypto_sign, msg, sk);
263        }
264    }
265    #[cfg(all(enable_aarch64_neon, feature = "neon"))]
266    {
267        if true {
268            return gen_signature!(PQCLEAN_MLDSA44_AARCH64_crypto_sign, msg, sk);
269        }
270    }
271    gen_signature!(PQCLEAN_MLDSA44_CLEAN_crypto_sign, msg, sk)
272}
273
274/// Sign the message for the provided context and return the signed message.
275pub fn sign_ctx(msg: &[u8], ctx: &[u8], sk: &SecretKey) -> SignedMessage {
276    #[cfg(all(enable_x86_avx2, feature = "avx2"))]
277    {
278        if std::is_x86_feature_detected!("avx2") {
279            return gen_signature_ctx!(PQCLEAN_MLDSA44_AVX2_crypto_sign_ctx, msg, ctx, sk);
280        }
281    }
282    #[cfg(all(enable_aarch64_neon, feature = "neon"))]
283    {
284        if true {
285            return gen_signature_ctx!(PQCLEAN_MLDSA44_AARCH64_crypto_sign_ctx, msg, ctx, sk);
286        }
287    }
288    gen_signature_ctx!(PQCLEAN_MLDSA44_CLEAN_crypto_sign_ctx, msg, ctx, sk)
289}
290
291macro_rules! open_signed {
292    ($variant:ident, $sm:ident, $pk:ident) => {{
293        let mut m: Vec<u8> = Vec::with_capacity($sm.len());
294        let mut mlen: usize = 0;
295        match unsafe {
296            paste! { ffi:: [<$variant _ctx>] (
297                m.as_mut_ptr(),
298                &mut mlen as *mut usize,
299                $sm.0.as_ptr(),
300                $sm.len(),
301                core::ptr::null() as *const u8, 0,
302                $pk.0.as_ptr(),
303            )}
304        } {
305            0 => {
306                unsafe { m.set_len(mlen) };
307                Ok(m)
308            }
309            -1 => Err(primitive::VerificationError::InvalidSignature),
310            _ => Err(primitive::VerificationError::UnknownVerificationError),
311        }
312    }};
313}
314
315macro_rules! open_signed_ctx {
316    ($variant:ident, $sm:ident, $ctx: ident, $pk:ident) => {{
317        let mut m: Vec<u8> = Vec::with_capacity($sm.len());
318        let mut mlen: usize = 0;
319        match unsafe {
320            ffi::$variant(
321                m.as_mut_ptr(),
322                &mut mlen as *mut usize,
323                $sm.0.as_ptr(),
324                $sm.len(),
325                $ctx.as_ptr(),
326                $ctx.len(),
327                $pk.0.as_ptr(),
328            )
329        } {
330            0 => {
331                unsafe { m.set_len(mlen) };
332                Ok(m)
333            }
334            -1 => Err(primitive::VerificationError::InvalidSignature),
335            _ => Err(primitive::VerificationError::UnknownVerificationError),
336        }
337    }};
338}
339
340/// Open the signed message and if verification succeeds return the message
341pub fn open(
342    sm: &SignedMessage,
343    pk: &PublicKey,
344) -> core::result::Result<Vec<u8>, primitive::VerificationError> {
345    #[cfg(all(enable_x86_avx2, feature = "avx2"))]
346    {
347        if std::is_x86_feature_detected!("avx2") {
348            return open_signed!(PQCLEAN_MLDSA44_AVX2_crypto_sign_open, sm, pk);
349        }
350    }
351    #[cfg(all(enable_aarch64_neon, feature = "neon"))]
352    {
353        if true {
354            return open_signed!(PQCLEAN_MLDSA44_AARCH64_crypto_sign_open, sm, pk);
355        }
356    }
357    open_signed!(PQCLEAN_MLDSA44_CLEAN_crypto_sign_open, sm, pk)
358}
359
360/// Open the signed message and if verification succeeds return the message
361pub fn open_ctx(
362    sm: &SignedMessage,
363    ctx: &[u8],
364    pk: &PublicKey,
365) -> core::result::Result<Vec<u8>, primitive::VerificationError> {
366    #[cfg(all(enable_x86_avx2, feature = "avx2"))]
367    {
368        if std::is_x86_feature_detected!("avx2") {
369            return open_signed_ctx!(PQCLEAN_MLDSA44_AVX2_crypto_sign_open_ctx, sm, ctx, pk);
370        }
371    }
372    #[cfg(all(enable_aarch64_neon, feature = "neon"))]
373    {
374        if true {
375            return open_signed_ctx!(PQCLEAN_MLDSA44_AARCH64_crypto_sign_open_ctx, sm, ctx, pk);
376        }
377    }
378    open_signed_ctx!(PQCLEAN_MLDSA44_CLEAN_crypto_sign_open_ctx, sm, ctx, pk)
379}
380
381macro_rules! detached_signature {
382    ($variant:ident, $msg:ident, $sk:ident) => {{
383        let mut sig = DetachedSignature::new();
384        unsafe {
385            paste! {
386                ffi:: [<$variant _ctx >](
387                    sig.0.as_mut_ptr(),
388                    &mut sig.1 as *mut usize,
389                    $msg.as_ptr(),
390                    $msg.len(),
391                    core::ptr::null(), 0,
392                    $sk.0.as_ptr(),
393                );
394            }
395        }
396        sig
397    }};
398}
399
400macro_rules! detached_signature_ctx {
401    ($variant:ident, $msg:ident, $ctx:ident, $sk:ident) => {{
402        let mut sig = DetachedSignature::new();
403        unsafe {
404            ffi::$variant(
405                sig.0.as_mut_ptr(),
406                &mut sig.1 as *mut usize,
407                $msg.as_ptr(),
408                $msg.len(),
409                $ctx.as_ptr(),
410                $ctx.len(),
411                $sk.0.as_ptr(),
412            );
413        }
414        sig
415    }};
416}
417
418/// Create a detached signature on the message
419pub fn detached_sign(msg: &[u8], sk: &SecretKey) -> DetachedSignature {
420    #[cfg(all(enable_x86_avx2, feature = "avx2"))]
421    {
422        if std::is_x86_feature_detected!("avx2") {
423            return detached_signature!(PQCLEAN_MLDSA44_AVX2_crypto_sign_signature, msg, sk);
424        }
425    }
426    #[cfg(all(enable_aarch64_neon, feature = "neon"))]
427    {
428        if true {
429            return detached_signature!(PQCLEAN_MLDSA44_AARCH64_crypto_sign_signature, msg, sk);
430        }
431    }
432    detached_signature!(PQCLEAN_MLDSA44_CLEAN_crypto_sign_signature, msg, sk)
433}
434
435/// Create a detached signature on the message
436pub fn detached_sign_ctx(msg: &[u8], ctx: &[u8], sk: &SecretKey) -> DetachedSignature {
437    #[cfg(all(enable_x86_avx2, feature = "avx2"))]
438    {
439        if std::is_x86_feature_detected!("avx2") {
440            return detached_signature_ctx!(
441                PQCLEAN_MLDSA44_AVX2_crypto_sign_signature_ctx,
442                msg,
443                ctx,
444                sk
445            );
446        }
447    }
448    #[cfg(all(enable_aarch64_neon, feature = "neon"))]
449    {
450        if true {
451            return detached_signature_ctx!(
452                PQCLEAN_MLDSA44_AARCH64_crypto_sign_signature_ctx,
453                msg,
454                ctx,
455                sk
456            );
457        }
458    }
459    detached_signature_ctx!(
460        PQCLEAN_MLDSA44_CLEAN_crypto_sign_signature_ctx,
461        msg,
462        ctx,
463        sk
464    )
465}
466
467macro_rules! verify_detached_sig {
468    ($variant:ident, $sig:ident, $msg:ident, $pk:ident) => {{
469        let res = unsafe {
470            paste! {
471                ffi:: [<$variant _ctx >](
472                    $sig.0.as_ptr(),
473                    $sig.1,
474                    $msg.as_ptr(),
475                    $msg.len(),
476                    core::ptr::null(), 0,
477                    $pk.0.as_ptr(),
478                )
479            }
480        };
481        match res {
482            0 => Ok(()),
483            -1 => Err(primitive::VerificationError::InvalidSignature),
484            _ => Err(primitive::VerificationError::UnknownVerificationError),
485        }
486    }};
487}
488
489macro_rules! verify_detached_sig_ctx {
490    ($variant:ident, $sig:ident, $msg:ident, $ctx:ident, $pk:ident) => {{
491        let res = unsafe {
492            ffi::$variant(
493                $sig.0.as_ptr(),
494                $sig.1,
495                $msg.as_ptr(),
496                $msg.len(),
497                $ctx.as_ptr(),
498                $ctx.len(),
499                $pk.0.as_ptr(),
500            )
501        };
502        match res {
503            0 => Ok(()),
504            -1 => Err(primitive::VerificationError::InvalidSignature),
505            _ => Err(primitive::VerificationError::UnknownVerificationError),
506        }
507    }};
508}
509
510/// Verify the detached signature
511pub fn verify_detached_signature(
512    sig: &DetachedSignature,
513    msg: &[u8],
514    pk: &PublicKey,
515) -> core::result::Result<(), primitive::VerificationError> {
516    #[cfg(all(enable_x86_avx2, feature = "avx2"))]
517    {
518        if std::is_x86_feature_detected!("avx2") {
519            return verify_detached_sig!(PQCLEAN_MLDSA44_AVX2_crypto_sign_verify, sig, msg, pk);
520        }
521    }
522    #[cfg(all(enable_aarch64_neon, feature = "neon"))]
523    {
524        if true {
525            return verify_detached_sig!(PQCLEAN_MLDSA44_AARCH64_crypto_sign_verify, sig, msg, pk);
526        }
527    }
528    verify_detached_sig!(PQCLEAN_MLDSA44_CLEAN_crypto_sign_verify, sig, msg, pk)
529}
530
531/// Verify the detached signature
532pub fn verify_detached_signature_ctx(
533    sig: &DetachedSignature,
534    msg: &[u8],
535    ctx: &[u8],
536    pk: &PublicKey,
537) -> core::result::Result<(), primitive::VerificationError> {
538    #[cfg(all(enable_x86_avx2, feature = "avx2"))]
539    {
540        if std::is_x86_feature_detected!("avx2") {
541            return verify_detached_sig_ctx!(
542                PQCLEAN_MLDSA44_AVX2_crypto_sign_verify_ctx,
543                sig,
544                msg,
545                ctx,
546                pk
547            );
548        }
549    }
550    #[cfg(all(enable_aarch64_neon, feature = "neon"))]
551    {
552        if true {
553            return verify_detached_sig_ctx!(
554                PQCLEAN_MLDSA44_AARCH64_crypto_sign_verify_ctx,
555                sig,
556                msg,
557                ctx,
558                pk
559            );
560        }
561    }
562    verify_detached_sig_ctx!(
563        PQCLEAN_MLDSA44_CLEAN_crypto_sign_verify_ctx,
564        sig,
565        msg,
566        ctx,
567        pk
568    )
569}
570
571#[cfg(test)]
572mod test {
573    use super::*;
574    use rand::prelude::*;
575
576    #[test]
577    pub fn test_sign() {
578        let mut rng = rand::rng();
579        let len: u16 = rng.random();
580
581        let message = (0..len).map(|_| rng.gen::<u8>()).collect::<Vec<_>>();
582        let (pk, sk) = keypair();
583        let sm = sign(&message, &sk);
584        let verifiedmsg = open(&sm, &pk).unwrap();
585        assert!(verifiedmsg == message);
586    }
587
588    #[test]
589    pub fn test_sign_detached() {
590        let mut rng = rand::rng();
591        let len: u16 = rng.random();
592        let message = (0..len).map(|_| rng.gen::<u8>()).collect::<Vec<_>>();
593
594        let (pk, sk) = keypair();
595        let sig = detached_sign(&message, &sk);
596        assert!(verify_detached_signature(&sig, &message, &pk).is_ok());
597        assert!(!verify_detached_signature(&sig, &message[..message.len() - 1], &pk).is_ok());
598    }
599
600    #[test]
601    pub fn test_sign_ctx() {
602        let mut rng = rand::rng();
603        let len: u16 = rng.random();
604        let ctx = (0..10).map(|_| rng.gen::<u8>()).collect::<Vec<_>>();
605
606        let message = (0..len).map(|_| rng.gen::<u8>()).collect::<Vec<_>>();
607        let (pk, sk) = keypair();
608        let sm = sign_ctx(&message, &ctx, &sk);
609        let verifiedmsg = open_ctx(&sm, &ctx, &pk).unwrap();
610        assert!(verifiedmsg == message);
611        assert!(open(&sm, &pk).is_err());
612    }
613
614    #[test]
615    pub fn test_sign_detached_ctx() {
616        let mut rng = rand::rng();
617        let len: u16 = rng.random();
618        let message = (0..len).map(|_| rng.gen::<u8>()).collect::<Vec<_>>();
619        let ctx = (0..10).map(|_| rng.gen::<u8>()).collect::<Vec<_>>();
620
621        let (pk, sk) = keypair();
622        let sig = detached_sign_ctx(&message, &ctx, &sk);
623        assert!(verify_detached_signature_ctx(&sig, &message, &ctx, &pk).is_ok());
624        assert!(
625            !verify_detached_signature_ctx(&sig, &message[..message.len() - 1], &ctx, &pk).is_ok()
626        );
627        assert!(!verify_detached_signature_ctx(
628            &sig,
629            &message[..message.len()],
630            &ctx[..ctx.len() - 1],
631            &pk
632        )
633        .is_ok());
634        assert!(!verify_detached_signature(&sig, &message[..message.len() - 1], &pk).is_ok());
635    }
636}