Skip to main content

bouncycastle_mldsa/
lib.rs

1//! This crate implements the Module Lattice Digital Signature Algorithm (ML-DSA) as per FIPS 204.
2//!
3//! # Usage
4//!
5//! This crate has been designed to serve a wide range of use cases, from people dabbling in
6//! cryptography for the first time, to cryptographic protocol designers who need access to the internal and advanced
7//! functionality of the ML-DSA algorithm, to embedded systems developers who want access to memory
8//! and performance optimized functions.
9//!
10//! This page gives examples of simple usage for generating keys and signatures, and verifying signatures.
11//!
12//! More examples on advanced usage can be found on the [`mldsa`] and [`hash_mldsa`] pages.
13//!
14//! ## Generating Keys
15//!
16//! ```rust
17//! use bouncycastle_mldsa::{MLDSA65, MLDSATrait};
18//!
19//! let (pk, sk) = MLDSA65::keygen().unwrap();
20//! ```
21//! That's it. That will use the library's default OS-backend RNG.
22//!
23//! Commonly with the ML-DSA algorithm, a 32-byte seed is used as the private key, and expanded into
24//! a full private key as needed. This is offered through the library's [`KeyMaterialTrait`] object:
25//!
26//! ```rust
27//! use bouncycastle_core::key_material::{KeyMaterial256, KeyType, KeyMaterialTrait};
28//! use bouncycastle_mldsa::{MLDSA65, MLDSATrait};
29//! use bouncycastle_hex as hex;
30//!
31//! let seed = KeyMaterial256::from_bytes_as_type(
32//!     &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(),
33//!     KeyType::Seed,
34//! ).unwrap();
35//!
36//! let (pk, sk) = MLDSA65::keygen_from_seed(&seed).unwrap();
37//! ```
38//!
39//! See [`MLDSATrait`] and [`MLDSATrait::sign_mu_deterministic_from_seed`] for an API flow that uses a merged
40//! keygen-and-sign function to provide improved speed and memory performance compared with making
41//! separate calls to [`MLDSATrait::keygen_from_seed`] followed by [`Signer::sign`].
42//!
43//! ## Generating and Verifying Signatures
44//!
45//! ```rust
46//! use bouncycastle_mldsa::{MLDSA65, MLDSATrait};
47//! use bouncycastle_core::traits::{Signer, SignatureVerifier};
48//! use bouncycastle_core::errors::SignatureError;
49//!
50//! let msg = b"The quick brown fox";
51//!
52//! let (pk, sk) = MLDSA65::keygen().unwrap();
53//!
54//! let sig = MLDSA65::sign(&sk, msg, None).unwrap();
55//! // This is the signature value that you can save to a file or whatever you need.
56//!
57//! match MLDSA65::verify(&pk, msg, None, &sig) {
58//!     Ok(()) => println!("Signature is valid!"),
59//!     Err(SignatureError::SignatureVerificationFailed) => println!("Signature is invalid!"),
60//!     Err(e) => panic!("Something else went wrong: {:?}", e),
61//! }
62//! ```
63//!
64//! And that's the basic usage! There are lots more bells-and-whistles in the form of exposed algorithm
65//! parameters, streaming APIs and other goodies that you can find by poking around this documentation.
66//!
67//! # Memory Footprint
68//!
69//! The following table lists the size of the on-disk bytes encoding and the in-memory struct size of the
70//! standard key objects:
71//!
72//! | Key Object | PK size on disk | PK size in memory | SK Size on disk | SK size in memory |
73//! |------------|-----------------|-------------------|-----------------|-------------------|
74//! | ML-DSA-44  | 1312            | 1312 (4128)       | 2560            | 12464             |
75//! | ML-DSA-65  | 1952            | 1952 (6176)       | 4032            | 17584             |
76//! | ML-DSA-87  | 2592            | 2592 (8224)       | 4896            | 23728             |
77//!
78//! The following table lists the size of the on-disk bytes encoding and the in-memory struct size of the
79//! expanded key objects that pre-expand the public matrix A for faster repeated verify() operations:
80//!
81//! | Key Object          | PK size on disk | PK size in memory | SK Size on disk | SK size in memory |
82//! |---------------------|-----------------|-------------------|-----------------|-------------------|
83//! | ML-DSA-44_expanded  | 1312            | 20512             | 2560            | 28848             |
84//! | ML-DSA-65_expanded  | 1952            | 36896             | 4032            | 48304             |
85//! | ML-DSA-87_expanded  | 2592            | 65568             | 4896            | 81072             |
86//!
87//! All values are in bytes. The "in memory" sizes are measured by rust's `std::mem::size_of`.
88//! Values in parentheses are the usual sizes in our un-optimized implementation in the \[bouncycastle_mldsa] crate.
89//!
90//!
91//! # 🚨 Security 🚨
92//!
93//! This crate intends to expose only APIs that are secure to use.
94//! There are, however, a few exceptions that are worth mentioning.
95//!
96//! If using a [`MLDSA::keygen_from_seed`], then it is your responsibility to ensure that the seed is
97//! cryptographically random and unpredictable at a security strength that matches the MLDSA parameter set.
98//!
99//! ML-DSA and HashML-DSA take several parameters: `seed`, `mu`, `ph`, `ctx`, and `rnd`.
100//! They fall into two groups with very different failure modes.
101//!
102//!
103//! `seed` and `rnd`, however, are secret/entropy inputs and must be handled with care:
104//!
105//! - `seed` *is* the private key, i.e. the entire key is derived from it. It must be generated
106//!   with a strong cryptographically secure PRNG, it must be kept secret, and it must never reused.
107//!   A low-entropy, predictable, or disclosed seed yields a full key compromise,
108//!   not merely an unverifiable signature.
109//!
110//! - `rnd` is the signing randomizer. ML-DSA is designed to be nonce-misuse-resistant, i.e. the
111//!   signing mask is derived from a secret key value together with `rnd` and `mu`, so
112//!   reusing `rnd`, or using the all-zero "deterministic" mode, does NOT
113//!   leak the private key (unlike ECDSA). Deterministic signing is FIPS-approved and safe. The randomized
114//!   mode exists to add resistance to fault and side-channel attacks, so `rnd`
115//!   should come from a good RNG when that threat model applies.
116//!
117//! `mu`, `ph`, and `ctx` are binding values that the verifier must reproduce. This means that getting
118//! them wrong does not compromise security, it just yields a signature the intended
119//! verifier won't accept (a correctness/interoperability failure). 
120//! One caveat: `ctx` can still be security-relevant at the protocol level (domain separation, replay and
121//! cross-protocol binding), so choosing it incorrectly can weaken those properties.
122
123
124#![no_std]
125#![forbid(unsafe_code)]
126#![forbid(missing_docs)]
127// These are because the code is matching variable names exactly against FIPS 204, for example both 'K' and 'k',
128// or 'A' and 'a' are used and have specific meanings.
129// But need to tell the rust linter to not care.
130#![allow(non_snake_case)]
131#![allow(non_upper_case_globals)]
132// so that private traits can be used to hide internal stuff that needs to be generic within the
133// MLDSA implementation, but should not get accessed from outside, such as FIPS-internal functions.
134#![allow(private_bounds)]
135#![allow(private_interfaces)]
136// Used in HashMLDSA for oid: &'static [u8] params.
137// #![allow(incomplete_features)] // needed because currently unsized_const_params is experimental
138// #![feature(adt_const_params)]
139// #![feature(unsized_const_params)]
140
141// imports needed just for docs
142#[allow(unused_imports)]
143use bouncycastle_core::key_material::KeyMaterialTrait;
144#[allow(unused_imports)]
145use bouncycastle_core::traits::{SignatureVerifier, Signer};
146
147mod aux_functions;
148pub mod hash_mldsa;
149mod matrix;
150pub mod mldsa;
151mod mldsa_keys;
152mod polynomial;
153
154/*** Exported types ***/
155pub use hash_mldsa::{HashMLDSA44_with_SHA256, HashMLDSA65_with_SHA256, HashMLDSA87_with_SHA256};
156pub use hash_mldsa::{HashMLDSA44_with_SHA512, HashMLDSA65_with_SHA512, HashMLDSA87_with_SHA512};
157pub use mldsa::MuBuilder;
158pub use mldsa::{MLDSA, MLDSA44, MLDSA65, MLDSA87, MLDSATrait};
159pub use mldsa_keys::{MLDSA44PrivateKey, MLDSA65PrivateKey, MLDSA87PrivateKey, MLDSAPrivateKey};
160pub use mldsa_keys::{
161    MLDSA44PrivateKeyExpanded, MLDSA65PrivateKeyExpanded, MLDSA87PrivateKeyExpanded,
162    MLDSAPrivateKeyExpanded,
163};
164pub use mldsa_keys::{MLDSA44PublicKey, MLDSA65PublicKey, MLDSA87PublicKey, MLDSAPublicKey};
165pub use mldsa_keys::{
166    MLDSA44PublicKeyExpanded, MLDSA65PublicKeyExpanded, MLDSA87PublicKeyExpanded,
167    MLDSAPublicKeyExpanded,
168};
169pub use mldsa_keys::{MLDSAPrivateKeyTrait, MLDSAPublicKeyTrait};
170
171/*** Exported constants ***/
172pub use mldsa::ML_DSA_44_NAME;
173pub use mldsa::ML_DSA_65_NAME;
174pub use mldsa::ML_DSA_87_NAME;
175
176pub use hash_mldsa::HASH_ML_DSA_44_with_SHA256_NAME;
177pub use hash_mldsa::HASH_ML_DSA_65_WITH_SHA256_NAME;
178pub use hash_mldsa::HASH_ML_DSA_87_with_SHA256_NAME;
179
180pub use hash_mldsa::HASH_ML_DSA_44_with_SHA512_NAME;
181pub use hash_mldsa::HASH_ML_DSA_65_WITH_SHA512_NAME;
182pub use hash_mldsa::HASH_ML_DSA_87_WITH_SHA512_NAME;
183
184pub use mldsa::{MLDSA_MU_LEN, MLDSA_RND_LEN, MLDSA_SEED_LEN, MLDSA_TR_LEN};
185pub use mldsa::{MLDSA44_PK_LEN, MLDSA44_SIG_LEN, MLDSA44_SK_LEN};
186pub use mldsa::{MLDSA65_PK_LEN, MLDSA65_SIG_LEN, MLDSA65_SK_LEN};
187pub use mldsa::{MLDSA87_PK_LEN, MLDSA87_SIG_LEN, MLDSA87_SK_LEN};
188
189pub use mldsa::SUSPENDED_MU_BUILDER_STATE_LEN;
190
191pub use matrix::Matrix;
192
193// re-export just so it's visible to unit tests
194pub use polynomial::Polynomial;