Skip to main content

ml_dsa/
lib.rs

1#![no_std]
2#![doc = include_str!("../README.md")]
3#![doc(
4    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
5    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
6)]
7#![cfg_attr(docsrs, feature(doc_cfg))]
8#![allow(non_snake_case)] // Allow notation matching the spec
9#![allow(clippy::similar_names)] // Allow notation matching the spec
10#![allow(clippy::many_single_char_names)] // Allow notation matching the spec
11#![allow(clippy::clone_on_copy)] // Be explicit about moving data
12
13//! # Usage
14//!
15//! The following types provide the core functionality of this crate, and are all generic around the
16//! [`MlDsaParams`] trait which defines the security level and is one of [`MlDsa44`], [`MlDsa65`],
17//! or [`MlDsa87`]  (with `MlDsa65` recommended as providing the best balance of security and
18//! performance):
19//!
20//! - [`SigningKey`]: secret key capable of generating signatures. Implements the [`KeyInit`],
21//!   [`KeyExport`], [`Keypair`], and [`Signer`] traits, as well as [`Generate`] when the
22//!   `rand_core` feature of this crate is enabled.
23//! - [`VerifyingKey`]: public key associated with a given [`SigningKey`]. Implements the
24//!   [`KeyInit`], [`KeyExport`], and [`Verifier`] traits.
25//! - [`Signature`]: ML-DSA signature generated by a `SigningKey`, and verifiable by a
26//!   `VerifyingKey`. Implements the [`SignatureEncoding`] trait.
27//!
28#![cfg_attr(feature = "getrandom", doc = "```")]
29#![cfg_attr(not(feature = "getrandom"), doc = "```ignore")]
30//! # fn main() -> Result<(), signature::Error> {
31//! // NOTE: requires the `getrandom` feature is enabled
32//! use ml_dsa::{MlDsa65, Generate, Keypair, SigningKey, Signer, Verifier};
33//!
34//! let sk = SigningKey::<MlDsa65>::generate();
35//!
36//! let msg = b"Hello world";
37//! let sig = sk.sign(msg);
38//!
39//! sk.verifying_key().verify(msg, &sig)?;
40//! # Ok(()) }
41//! ```
42
43#[cfg(feature = "alloc")]
44extern crate alloc;
45
46#[cfg(feature = "pkcs8")]
47pub mod pkcs8;
48
49mod algebra;
50mod crypto;
51mod encode;
52mod hint;
53mod ntt;
54mod param;
55mod sampling;
56mod signing;
57mod verifying;
58
59pub use crate::{
60    param::{EncodedSignature, EncodedVerifyingKey, ExpandedSigningKeyBytes, MlDsaParams},
61    signing::{ExpandedSigningKey, SigningKey},
62    verifying::VerifyingKey,
63};
64pub use common::{self, KeyExport, KeyInit, KeySizeUser};
65pub use signature::{self, Error, Keypair, SignatureEncoding, Signer, Verifier};
66
67#[cfg(feature = "rand_core")]
68pub use common::Generate;
69
70use crate::{
71    algebra::{AlgebraExt, Vector},
72    crypto::H,
73    hint::Hint,
74    param::{ParameterSet, QMinus1},
75};
76use core::convert::{TryFrom, TryInto};
77use hybrid_array::{
78    Array,
79    sizes::{U1, U2, U4, U5, U6, U7, U8, U17, U19, U32, U48, U55, U64, U75, U80, U88},
80    typenum::{Diff, Length, Prod, Quot, Shleft},
81};
82use module_lattice::{MaybeBox, Truncate};
83use shake::Shake256;
84
85/// A 32-byte array, defined here for brevity because it is used several times
86pub type B32 = Array<u8, U32>;
87
88/// A 64-byte array, defined here for brevity because it is used several times
89pub(crate) type B64 = Array<u8, U64>;
90
91/// ML-DSA seeds are signing (private) keys, which are consistently 32-bytes across all security
92/// levels, and are the preferred serialization for representing such keys.
93pub type Seed = B32;
94
95/// An ML-DSA signature
96#[derive(Clone, Debug, PartialEq)]
97pub struct Signature<P: MlDsaParams> {
98    c_tilde: Array<u8, P::Lambda>,
99    z: MaybeBox<Vector<P::L>>,
100    h: Hint<P>,
101}
102
103impl<P: MlDsaParams> Signature<P> {
104    /// Encode the signature in a fixed-size byte array.
105    // Algorithm 26 sigEncode
106    pub fn encode(&self) -> EncodedSignature<P> {
107        let c_tilde = self.c_tilde.clone();
108        let z = P::encode_z(&self.z);
109        let h = self.h.bit_pack();
110        P::concat_sig(c_tilde, z, h)
111    }
112
113    /// Decode the signature from an appropriately sized byte array.
114    // Algorithm 27 sigDecode
115    pub fn decode(enc: &EncodedSignature<P>) -> Option<Self> {
116        let (c_tilde, z, h) = P::split_sig(enc);
117
118        let c_tilde = c_tilde.clone();
119        let z = MaybeBox::new(P::decode_z(z));
120        let h = Hint::bit_unpack(h)?;
121
122        if z.infinity_norm() >= P::GAMMA1_MINUS_BETA {
123            return None;
124        }
125
126        Some(Self { c_tilde, z, h })
127    }
128
129    /// Access the challenge hash `c̃`.
130    pub fn c_tilde(&self) -> &Array<u8, P::Lambda> {
131        &self.c_tilde
132    }
133}
134
135impl<'a, P: MlDsaParams> TryFrom<&'a [u8]> for Signature<P> {
136    type Error = Error;
137
138    fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
139        let enc = EncodedSignature::<P>::try_from(value).map_err(|_| Error::new())?;
140        Self::decode(&enc).ok_or(Error::new())
141    }
142}
143
144impl<P: MlDsaParams> TryInto<EncodedSignature<P>> for Signature<P> {
145    type Error = Error;
146
147    fn try_into(self) -> Result<EncodedSignature<P>, Self::Error> {
148        Ok(self.encode())
149    }
150}
151
152impl<P: MlDsaParams> SignatureEncoding for Signature<P> {
153    type Repr = EncodedSignature<P>;
154}
155
156impl<P: MlDsaParams> core::hash::Hash for Signature<P> {
157    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
158        self.encode().hash(state);
159    }
160}
161
162struct MuBuilder(H);
163
164impl MuBuilder {
165    fn new(tr: &[u8], ctx: &[u8]) -> Self {
166        let mut h = H::default();
167        h = h.absorb(tr);
168        h = h.absorb(&[0]);
169        h = h.absorb(&[Truncate::truncate(ctx.len())]);
170        h = h.absorb(ctx);
171
172        Self(h)
173    }
174
175    fn internal(tr: &[u8], Mp: &[&[u8]]) -> B64 {
176        let mut h = H::default().absorb(tr);
177
178        for m in Mp {
179            h = h.absorb(m);
180        }
181
182        h.squeeze_new()
183    }
184
185    fn message(mut self, M: &[&[u8]]) -> B64 {
186        for m in M {
187            self.0 = self.0.absorb(m);
188        }
189
190        self.0.squeeze_new()
191    }
192
193    fn finish(mut self) -> B64 {
194        self.0.squeeze_new()
195    }
196}
197
198impl AsMut<Shake256> for MuBuilder {
199    fn as_mut(&mut self) -> &mut Shake256 {
200        self.0.updatable()
201    }
202}
203
204/// `MlDsa44` is the parameter set for security category 2, providing the equivalent of 128-bit
205/// symmetric security.
206#[derive(Clone, Copy, Debug, Default, PartialEq)]
207pub struct MlDsa44;
208
209impl ParameterSet for MlDsa44 {
210    type K = U4;
211    type L = U4;
212    type Eta = U2;
213    type Gamma1 = Shleft<U1, U17>;
214    type Gamma2 = Quot<QMinus1, U88>;
215    type TwoGamma2 = Prod<U2, Self::Gamma2>;
216    type W1Bits = Length<Diff<Quot<U88, U2>, U1>>;
217    type Lambda = U32;
218    type Omega = U80;
219    const TAU: usize = 39;
220}
221
222/// `MlDsa65` is the parameter set for security category 3, providing the equivalent of 192-bit
223/// symmetric security, and is the recommended parameter set.
224///
225/// This set provides the best balance between performance and security.
226#[derive(Clone, Copy, Debug, Default, PartialEq)]
227pub struct MlDsa65;
228
229impl ParameterSet for MlDsa65 {
230    type K = U6;
231    type L = U5;
232    type Eta = U4;
233    type Gamma1 = Shleft<U1, U19>;
234    type Gamma2 = Quot<QMinus1, U32>;
235    type TwoGamma2 = Prod<U2, Self::Gamma2>;
236    type W1Bits = Length<Diff<Quot<U32, U2>, U1>>;
237    type Lambda = U48;
238    type Omega = U55;
239    const TAU: usize = 49;
240}
241
242/// `MlDsa87` is the parameter set for security category 5, providing the equivalent of 256-bit
243/// symmetric security.
244#[derive(Clone, Copy, Debug, Default, PartialEq)]
245pub struct MlDsa87;
246
247impl ParameterSet for MlDsa87 {
248    type K = U8;
249    type L = U7;
250    type Eta = U2;
251    type Gamma1 = Shleft<U1, U19>;
252    type Gamma2 = Quot<QMinus1, U32>;
253    type TwoGamma2 = Prod<U2, Self::Gamma2>;
254    type W1Bits = Length<Diff<Quot<U32, U2>, U1>>;
255    type Lambda = U64;
256    type Omega = U75;
257    const TAU: usize = 60;
258}
259
260#[cfg(test)]
261mod test {
262    use super::*;
263    use crate::param::*;
264    use hybrid_array::typenum::Unsigned;
265    use signature::{Keypair, digest::Update};
266
267    #[test]
268    fn output_sizes() {
269        //           priv pub  sig
270        // ML-DSA-44 2560 1312 2420
271        // ML-DSA-65 4032 1952 3309
272        // ML-DSA-87 4896 2592 4627
273        assert_eq!(SigningKeySize::<MlDsa44>::USIZE, 2560);
274        assert_eq!(VerifyingKeySize::<MlDsa44>::USIZE, 1312);
275        assert_eq!(SignatureSize::<MlDsa44>::USIZE, 2420);
276
277        assert_eq!(SigningKeySize::<MlDsa65>::USIZE, 4032);
278        assert_eq!(VerifyingKeySize::<MlDsa65>::USIZE, 1952);
279        assert_eq!(SignatureSize::<MlDsa65>::USIZE, 3309);
280
281        assert_eq!(SigningKeySize::<MlDsa87>::USIZE, 4896);
282        assert_eq!(VerifyingKeySize::<MlDsa87>::USIZE, 2592);
283        assert_eq!(SignatureSize::<MlDsa87>::USIZE, 4627);
284    }
285
286    fn encode_decode_round_trip_test<P>()
287    where
288        P: MlDsaParams + PartialEq,
289    {
290        let seed = Array::default();
291        let ssk = SigningKey::from_seed(&seed);
292        assert_eq!(ssk.to_seed(), seed);
293
294        let esk = ssk.expanded_key();
295        let vk = ssk.verifying_key();
296
297        let vk_bytes = vk.encode();
298        let vk2 = VerifyingKey::<P>::decode(&vk_bytes);
299        assert!(vk == vk2);
300
301        #[allow(deprecated)]
302        {
303            let sk_bytes = esk.to_expanded();
304            let sk2 = ExpandedSigningKey::<P>::from_expanded(&sk_bytes);
305            assert!(esk == &sk2);
306
307            let M = b"Hello world";
308            let rnd = Array([0u8; 32]);
309            let sig = esk.sign_internal(&[M], &rnd);
310            let sig_bytes = sig.encode();
311            let sig2 = Signature::<P>::decode(&sig_bytes).unwrap();
312            assert!(sig == sig2);
313        }
314    }
315
316    #[test]
317    fn encode_decode_round_trip() {
318        encode_decode_round_trip_test::<MlDsa44>();
319        encode_decode_round_trip_test::<MlDsa65>();
320        encode_decode_round_trip_test::<MlDsa87>();
321    }
322
323    fn public_from_private_test<P>()
324    where
325        P: MlDsaParams + PartialEq,
326    {
327        let ssk = SigningKey::<P>::from_seed(&Array::default());
328        let esk = ssk.expanded_key();
329        let vk = ssk.verifying_key();
330        let vk_derived = esk.verifying_key();
331
332        assert!(vk == vk_derived);
333    }
334
335    #[test]
336    fn public_from_private() {
337        public_from_private_test::<MlDsa44>();
338        public_from_private_test::<MlDsa65>();
339        public_from_private_test::<MlDsa87>();
340    }
341
342    fn sign_verify_round_trip_test<P>()
343    where
344        P: MlDsaParams,
345    {
346        let ssk = SigningKey::<P>::from_seed(&Array::default());
347        let esk = ssk.expanded_key();
348        let vk = ssk.verifying_key();
349
350        let M = b"Hello world";
351        let rnd = Array([0u8; 32]);
352        let sig = esk.sign_internal(&[M], &rnd);
353
354        assert!(vk.verify_internal(M, &sig));
355    }
356
357    #[test]
358    fn sign_verify_round_trip() {
359        sign_verify_round_trip_test::<MlDsa44>();
360        sign_verify_round_trip_test::<MlDsa65>();
361        sign_verify_round_trip_test::<MlDsa87>();
362    }
363
364    #[test]
365    fn sign_mu_verify_mu_round_trip() {
366        fn sign_mu_verify_mu<P>()
367        where
368            P: MlDsaParams,
369        {
370            let ssk = SigningKey::<P>::from_seed(&Array::default());
371            let esk = ssk.expanded_key();
372            let vk = ssk.verifying_key();
373
374            let M = b"Hello world";
375            let rnd = Array([0u8; 32]);
376            let mu = MuBuilder::internal(&esk.tr, &[M]);
377            let sig = esk.raw_sign_mu(&mu, &rnd);
378
379            assert!(vk.raw_verify_mu(&mu, &sig));
380        }
381        sign_mu_verify_mu::<MlDsa44>();
382        sign_mu_verify_mu::<MlDsa65>();
383        sign_mu_verify_mu::<MlDsa87>();
384    }
385
386    #[test]
387    fn sign_mu_verify_internal_round_trip() {
388        fn sign_mu_verify_internal<P>()
389        where
390            P: MlDsaParams,
391        {
392            let ssk = SigningKey::<P>::from_seed(&Array::default());
393            let esk = ssk.expanded_key();
394            let vk = ssk.verifying_key();
395
396            let M = b"Hello world";
397            let rnd = Array([0u8; 32]);
398            let mu = MuBuilder::internal(&esk.tr, &[M]);
399            let sig = esk.raw_sign_mu(&mu, &rnd);
400
401            assert!(vk.verify_internal(M, &sig));
402        }
403        sign_mu_verify_internal::<MlDsa44>();
404        sign_mu_verify_internal::<MlDsa65>();
405        sign_mu_verify_internal::<MlDsa87>();
406    }
407
408    #[test]
409    fn sign_internal_verify_mu_round_trip() {
410        fn sign_internal_verify_mu<P>()
411        where
412            P: MlDsaParams,
413        {
414            let ssk = SigningKey::<P>::from_seed(&Array::default());
415            let esk = ssk.expanded_key();
416            let vk = ssk.verifying_key();
417
418            let M = b"Hello world";
419            let rnd = Array([0u8; 32]);
420            let mu = MuBuilder::internal(&esk.tr, &[M]);
421            let sig = esk.sign_internal(&[M], &rnd);
422
423            assert!(vk.raw_verify_mu(&mu, &sig));
424        }
425        sign_internal_verify_mu::<MlDsa44>();
426        sign_internal_verify_mu::<MlDsa65>();
427        sign_internal_verify_mu::<MlDsa87>();
428    }
429
430    #[test]
431    fn from_seed_implementations_match() {
432        fn assert_from_seed_equality<P>()
433        where
434            P: MlDsaParams,
435        {
436            let seed = Seed::default();
437            let ssk = SigningKey::<P>::from_seed(&seed);
438            let sk1 = ExpandedSigningKey::<P>::from_seed(&seed);
439            assert_eq!(ssk.expanded_key(), &sk1);
440        }
441        assert_from_seed_equality::<MlDsa44>();
442        assert_from_seed_equality::<MlDsa65>();
443        assert_from_seed_equality::<MlDsa87>();
444    }
445
446    #[test]
447    fn to_seed_returns_correct_seed() {
448        fn test_to_seed<P: MlDsaParams>() {
449            let seed = Array([
450                1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
451                24, 25, 26, 27, 28, 29, 30, 31, 32,
452            ]);
453            let kp = SigningKey::<P>::from_seed(&seed);
454            assert_eq!(kp.to_seed(), seed);
455        }
456        test_to_seed::<MlDsa44>();
457        test_to_seed::<MlDsa65>();
458        test_to_seed::<MlDsa87>();
459    }
460
461    #[test]
462    fn verification_rejects_invalid_signature() {
463        fn test_invalid_sig<P: MlDsaParams>() {
464            let kp = SigningKey::<P>::from_seed(&Array::default());
465            let vk = kp.verifying_key();
466
467            let msg = b"Hello world";
468            let rnd = Array([0u8; 32]);
469            let mut sig = kp.expanded_key().sign_internal(&[msg], &rnd);
470            sig.c_tilde[0] ^= 0xFF;
471
472            assert!(!vk.verify_with_context(msg, &[], &sig));
473        }
474        test_invalid_sig::<MlDsa44>();
475        test_invalid_sig::<MlDsa65>();
476        test_invalid_sig::<MlDsa87>();
477    }
478
479    #[test]
480    fn verification_rejects_wrong_message() {
481        fn test_wrong_msg<P: MlDsaParams>() {
482            let kp = SigningKey::<P>::from_seed(&Array::default());
483            let vk = kp.verifying_key();
484
485            let msg1 = b"Hello world";
486            let msg2 = b"Wrong message";
487            let rnd = Array([0u8; 32]);
488            let sig = kp.expanded_key().sign_internal(&[msg1], &rnd);
489
490            assert!(!vk.verify_with_context(msg2, &[], &sig));
491        }
492        test_wrong_msg::<MlDsa44>();
493        test_wrong_msg::<MlDsa65>();
494        test_wrong_msg::<MlDsa87>();
495    }
496
497    #[test]
498    fn context_length_validation() {
499        fn test_ctx_length<P: MlDsaParams>() {
500            let ssk = SigningKey::<P>::from_seed(&Array::default());
501            let sk = ssk.expanded_key();
502            let vk = ssk.verifying_key();
503
504            let msg = b"Hello world";
505            let long_ctx = [0u8; 256];
506            let short_ctx = [0u8; 255];
507
508            assert!(sk.sign_deterministic(msg, &long_ctx).is_err());
509
510            let sig = sk.sign_deterministic(msg, &short_ctx).unwrap();
511            assert!(!vk.verify_with_context(msg, &long_ctx, &sig));
512            assert!(vk.verify_with_context(msg, &short_ctx, &sig));
513
514            assert!(
515                vk.compute_mu(
516                    |_| panic!("oversized context must be rejected before invoking the callback"),
517                    &long_ctx,
518                )
519                .is_err()
520            );
521
522            let mu = vk
523                .compute_mu(
524                    |h| {
525                        h.update(msg);
526                        Ok(())
527                    },
528                    &short_ctx,
529                )
530                .unwrap();
531            assert!(vk.verify_mu(&mu, &sig));
532            assert_eq!(sk.sign_mu_deterministic(&mu).encode(), sig.encode());
533        }
534        test_ctx_length::<MlDsa44>();
535        test_ctx_length::<MlDsa65>();
536        test_ctx_length::<MlDsa87>();
537    }
538
539    #[test]
540    fn derived_verifying_key_validates_signatures() {
541        fn test_derived_vk<P: MlDsaParams>() {
542            let seed = Array([42u8; 32]);
543            let ssk = SigningKey::<P>::from_seed(&seed);
544            let sk = ssk.expanded_key();
545            let derived_vk = sk.verifying_key();
546
547            let msg = b"Test message for derived key";
548            let rnd = Array([0u8; 32]);
549            let sig = sk.sign_internal(&[msg], &rnd);
550
551            assert!(derived_vk.verify_internal(msg, &sig));
552            assert_eq!(derived_vk.encode(), ssk.verifying_key().encode());
553        }
554        test_derived_vk::<MlDsa44>();
555        test_derived_vk::<MlDsa65>();
556        test_derived_vk::<MlDsa87>();
557    }
558
559    #[test]
560    #[cfg(feature = "alloc")]
561    fn debug_implementations() {
562        extern crate alloc;
563        use core::fmt::Write;
564
565        fn test_debug<P: MlDsaParams>() {
566            let kp = SigningKey::<P>::from_seed(&Array::default());
567
568            let mut kp_debug = alloc::string::String::new();
569            write!(&mut kp_debug, "{:?}", kp).unwrap();
570            assert!(kp_debug.contains("SigningKey"));
571
572            let mut sk_debug = alloc::string::String::new();
573            write!(&mut sk_debug, "{:?}", kp.expanded_key()).unwrap();
574            assert!(sk_debug.contains("ExpandedSigningKey"));
575        }
576        test_debug::<MlDsa44>();
577        test_debug::<MlDsa65>();
578        test_debug::<MlDsa87>();
579    }
580}