1
0
mirror of https://github.com/wolfSSL/wolfssl.git synced 2026-10-24 04:09:07 +03:00

Rust wrapper: mldsa: use cfg gates for level constants

Fixes F-12237.
This commit is contained in:
Josh Holtrop
2026-09-30 14:47:11 -04:00
parent fce5e879d5
commit dba52cf55b
4 changed files with 106 additions and 53 deletions

View File

@@ -15,6 +15,10 @@ Breaking changes:
rand_core::UnwrapErr to keep the previous panic-on-failure behavior
- Ed25519 and Ed448 no longer implement the signature crate's Keypair trait;
use the new SigningKey types instead
- MlDsa::LEVEL_44, MlDsa::LEVEL_65 and MlDsa::LEVEL_87 are now gated on the
mldsa_level2, mldsa_level3 and mldsa_level5 cfgs, so selecting a parameter
set the library was not built with is a compile error instead of a runtime
NOT_COMPILED_IN
New features:
@@ -31,11 +35,11 @@ Fixes and improvements:
shared reference. The wolfSSL context is now held in an UnsafeCell
- Fix a panic in Keypair::verifying_key() for Ed25519 and Ed448 keys with no
public key, such as after new() or import_private_only()
Fixes and improvements:
- Validate PBKDF2 and scrypt password hash output length against the PHC
minimum before running the KDF
- Fix detection of the built-in ML-DSA parameter sets: the mldsa_level2,
mldsa_level3 and mldsa_level5 cfgs probed the WC_MLDSA_NN_KEY_SIZE macros,
which wc_mldsa.h defines unconditionally, so all three were always enabled
## v2.2.0

View File

@@ -678,9 +678,13 @@ fn scan_cfg() -> Result<()> {
check_cfg(&binding, "wc_MlDsaKey_ExportPubRaw", "mldsa_export_public");
check_cfg(&binding, "wc_MlDsaKey_ExportPrivRaw", "mldsa_export_private");
check_cfg(&binding, "wc_MlDsaKey_CheckKey", "mldsa_check_key");
check_cfg(&binding, "WC_MLDSA_44_KEY_SIZE", "mldsa_level2");
check_cfg(&binding, "WC_MLDSA_65_KEY_SIZE", "mldsa_level3");
check_cfg(&binding, "WC_MLDSA_87_KEY_SIZE", "mldsa_level5");
/* The WC_MLDSA_NN_* size macros are defined unconditionally by
* wc_mldsa.h, so their presence says nothing about which parameter sets
* were built in. The PARAMS_ML_DSA_NN_* macros are the ones guarded by
* WOLFSSL_NO_ML_DSA_NN, so probe those instead. */
check_cfg(&binding, "PARAMS_ML_DSA_44_K", "mldsa_level2");
check_cfg(&binding, "PARAMS_ML_DSA_65_K", "mldsa_level3");
check_cfg(&binding, "PARAMS_ML_DSA_87_K", "mldsa_level5");
/* mlkem / ML-KEM */
check_cfg(&binding, "wc_MlKemKey_New", "mlkem");

View File

@@ -29,16 +29,20 @@ deallocation.
Three security parameter sets are supported, selected via
[`MlDsa::set_level()`]:
| Constant | Level | NIST PQC Level |
|-----------------|-------|----------------|
| [`MlDsa::LEVEL_44`] | 2 | 2 (ML-DSA-44) |
| [`MlDsa::LEVEL_65`] | 3 | 3 (ML-DSA-65) |
| [`MlDsa::LEVEL_87`] | 5 | 5 (ML-DSA-87) |
| Constant | Level | NIST PQC Level | Build cfg |
|-----------------|-------|----------------|-----------|
| [`MlDsa::LEVEL_44`] | 2 | 2 (ML-DSA-44) | `mldsa_level2` |
| [`MlDsa::LEVEL_65`] | 3 | 3 (ML-DSA-65) | `mldsa_level3` |
| [`MlDsa::LEVEL_87`] | 5 | 5 (ML-DSA-87) | `mldsa_level5` |
A parameter set that the library was not built with has no level constant,
so selecting it is a compile-time error rather than a runtime
`NOT_COMPILED_IN`.
# Examples
```rust
#[cfg(all(mldsa, mldsa_make_key, mldsa_sign, mldsa_verify, random))]
#[cfg(all(mldsa, mldsa_level2, mldsa_make_key, mldsa_sign, mldsa_verify, random))]
{
use wolfssl_wolfcrypt::random::RNG;
use wolfssl_wolfcrypt::mldsa::MlDsa;
@@ -76,10 +80,22 @@ pub struct MlDsa {
impl MlDsa {
/// ML-DSA-44 security parameter set (NIST Level 2).
///
/// Only present when the library was built with ML-DSA-44 support
/// (i.e. without `WOLFSSL_NO_ML_DSA_44`).
#[cfg(mldsa_level2)]
pub const LEVEL_44: u8 = sys::WC_ML_DSA_44 as u8;
/// ML-DSA-65 security parameter set (NIST Level 3).
///
/// Only present when the library was built with ML-DSA-65 support
/// (i.e. without `WOLFSSL_NO_ML_DSA_65`).
#[cfg(mldsa_level3)]
pub const LEVEL_65: u8 = sys::WC_ML_DSA_65 as u8;
/// ML-DSA-87 security parameter set (NIST Level 5).
///
/// Only present when the library was built with ML-DSA-87 support
/// (i.e. without `WOLFSSL_NO_ML_DSA_87`).
#[cfg(mldsa_level5)]
pub const LEVEL_87: u8 = sys::WC_ML_DSA_87 as u8;
/// Required size in bytes of the seed passed to
@@ -149,7 +165,7 @@ impl MlDsa {
/// # Example
///
/// ```rust
/// #[cfg(all(mldsa, mldsa_make_key, random))]
/// #[cfg(all(mldsa, mldsa_level2, mldsa_make_key, random))]
/// {
/// use wolfssl_wolfcrypt::random::RNG;
/// use wolfssl_wolfcrypt::mldsa::MlDsa;
@@ -181,7 +197,7 @@ impl MlDsa {
/// # Example
///
/// ```rust
/// #[cfg(all(mldsa, mldsa_make_key, random))]
/// #[cfg(all(mldsa, mldsa_level2, mldsa_make_key, random))]
/// {
/// use wolfssl_wolfcrypt::random::RNG;
/// use wolfssl_wolfcrypt::mldsa::MlDsa;
@@ -228,7 +244,7 @@ impl MlDsa {
/// # Example
///
/// ```rust
/// #[cfg(all(mldsa, mldsa_make_key_from_seed))]
/// #[cfg(all(mldsa, mldsa_level2, mldsa_make_key_from_seed))]
/// {
/// use wolfssl_wolfcrypt::mldsa::MlDsa;
/// let seed = [0x42u8; 32];
@@ -260,7 +276,7 @@ impl MlDsa {
/// # Example
///
/// ```rust
/// #[cfg(all(mldsa, mldsa_make_key_from_seed))]
/// #[cfg(all(mldsa, mldsa_level2, mldsa_make_key_from_seed))]
/// {
/// use wolfssl_wolfcrypt::mldsa::MlDsa;
/// let seed = [0x42u8; 32];
@@ -376,7 +392,7 @@ impl MlDsa {
/// # Example
///
/// ```rust
/// #[cfg(mldsa)]
/// #[cfg(all(mldsa, mldsa_level3))]
/// {
/// use wolfssl_wolfcrypt::mldsa::MlDsa;
/// let mut key = MlDsa::new().expect("Error with new()");
@@ -401,7 +417,7 @@ impl MlDsa {
/// # Example
///
/// ```rust
/// #[cfg(mldsa)]
/// #[cfg(all(mldsa, mldsa_level5))]
/// {
/// use wolfssl_wolfcrypt::mldsa::MlDsa;
/// let mut key = MlDsa::new().expect("Error with new()");
@@ -429,7 +445,7 @@ impl MlDsa {
/// # Example
///
/// ```rust
/// #[cfg(all(mldsa, mldsa_make_key, mldsa_size, random))]
/// #[cfg(all(mldsa, mldsa_level2, mldsa_make_key, mldsa_size, random))]
/// {
/// use wolfssl_wolfcrypt::random::RNG;
/// use wolfssl_wolfcrypt::mldsa::MlDsa;
@@ -460,7 +476,7 @@ impl MlDsa {
/// # Example
///
/// ```rust
/// #[cfg(all(mldsa, mldsa_make_key, mldsa_priv_size, random))]
/// #[cfg(all(mldsa, mldsa_level2, mldsa_make_key, mldsa_priv_size, random))]
/// {
/// use wolfssl_wolfcrypt::random::RNG;
/// use wolfssl_wolfcrypt::mldsa::MlDsa;
@@ -490,7 +506,7 @@ impl MlDsa {
/// # Example
///
/// ```rust
/// #[cfg(all(mldsa, mldsa_make_key, mldsa_pub_size, random))]
/// #[cfg(all(mldsa, mldsa_level2, mldsa_make_key, mldsa_pub_size, random))]
/// {
/// use wolfssl_wolfcrypt::random::RNG;
/// use wolfssl_wolfcrypt::mldsa::MlDsa;
@@ -520,7 +536,7 @@ impl MlDsa {
/// # Example
///
/// ```rust
/// #[cfg(all(mldsa, mldsa_make_key, mldsa_sig_size, random))]
/// #[cfg(all(mldsa, mldsa_level2, mldsa_make_key, mldsa_sig_size, random))]
/// {
/// use wolfssl_wolfcrypt::random::RNG;
/// use wolfssl_wolfcrypt::mldsa::MlDsa;
@@ -550,7 +566,7 @@ impl MlDsa {
/// # Example
///
/// ```rust
/// #[cfg(all(mldsa, mldsa_make_key, mldsa_check_key, random))]
/// #[cfg(all(mldsa, mldsa_level2, mldsa_make_key, mldsa_check_key, random))]
/// {
/// use wolfssl_wolfcrypt::random::RNG;
/// use wolfssl_wolfcrypt::mldsa::MlDsa;
@@ -583,7 +599,7 @@ impl MlDsa {
/// # Example
///
/// ```rust
/// #[cfg(all(mldsa, mldsa_make_key, mldsa_pub_size, mldsa_export_public,
/// #[cfg(all(mldsa, mldsa_level2, mldsa_make_key, mldsa_pub_size, mldsa_export_public,
/// mldsa_import_public, random))]
/// {
/// use wolfssl_wolfcrypt::random::RNG;
@@ -627,7 +643,7 @@ impl MlDsa {
/// # Example
///
/// ```rust
/// #[cfg(all(mldsa, mldsa_make_key, mldsa_size, mldsa_export_private,
/// #[cfg(all(mldsa, mldsa_level2, mldsa_make_key, mldsa_size, mldsa_export_private,
/// mldsa_import_private, random))]
/// {
/// use wolfssl_wolfcrypt::random::RNG;
@@ -669,7 +685,7 @@ impl MlDsa {
/// # Example
///
/// ```rust
/// #[cfg(all(mldsa, mldsa_make_key, mldsa_size, mldsa_pub_size,
/// #[cfg(all(mldsa, mldsa_level2, mldsa_make_key, mldsa_size, mldsa_pub_size,
/// mldsa_export_private, mldsa_import_private,
/// mldsa_import_public, random))]
/// {
@@ -718,7 +734,8 @@ impl MlDsa {
/// # Example
///
/// ```rust
/// #[cfg(all(mldsa, mldsa_make_key, mldsa_pub_size, mldsa_export_public, random))]
/// #[cfg(all(mldsa, mldsa_level2, mldsa_make_key, mldsa_pub_size,
/// mldsa_export_public, random))]
/// {
/// use wolfssl_wolfcrypt::random::RNG;
/// use wolfssl_wolfcrypt::mldsa::MlDsa;
@@ -757,7 +774,8 @@ impl MlDsa {
/// # Example
///
/// ```rust
/// #[cfg(all(mldsa, mldsa_make_key, mldsa_size, mldsa_export_private, random))]
/// #[cfg(all(mldsa, mldsa_level2, mldsa_make_key, mldsa_size,
/// mldsa_export_private, random))]
/// {
/// use wolfssl_wolfcrypt::random::RNG;
/// use wolfssl_wolfcrypt::mldsa::MlDsa;
@@ -801,7 +819,7 @@ impl MlDsa {
/// # Example
///
/// ```rust
/// #[cfg(all(mldsa, mldsa_make_key, mldsa_size, mldsa_pub_size,
/// #[cfg(all(mldsa, mldsa_level2, mldsa_make_key, mldsa_size, mldsa_pub_size,
/// mldsa_export_private, mldsa_export_public, random))]
/// {
/// use wolfssl_wolfcrypt::random::RNG;
@@ -849,7 +867,7 @@ impl MlDsa {
/// # Example
///
/// ```rust
/// #[cfg(all(mldsa, mldsa_make_key, mldsa_sign, random))]
/// #[cfg(all(mldsa, mldsa_level2, mldsa_make_key, mldsa_sign, random))]
/// {
/// use wolfssl_wolfcrypt::random::RNG;
/// use wolfssl_wolfcrypt::mldsa::MlDsa;
@@ -906,7 +924,7 @@ impl MlDsa {
/// # Example
///
/// ```rust
/// #[cfg(all(mldsa, mldsa_make_key, mldsa_sign, random))]
/// #[cfg(all(mldsa, mldsa_level2, mldsa_make_key, mldsa_sign, random))]
/// {
/// use wolfssl_wolfcrypt::random::RNG;
/// use wolfssl_wolfcrypt::mldsa::MlDsa;
@@ -1018,7 +1036,7 @@ impl MlDsa {
/// # Example
///
/// ```rust
/// #[cfg(all(mldsa, mldsa_make_key_from_seed, mldsa_sign_with_seed))]
/// #[cfg(all(mldsa, mldsa_level2, mldsa_make_key_from_seed, mldsa_sign_with_seed))]
/// {
/// use wolfssl_wolfcrypt::mldsa::MlDsa;
/// let key_seed = [0x42u8; 32];
@@ -1167,7 +1185,7 @@ impl MlDsa {
/// # Example
///
/// ```rust
/// #[cfg(all(mldsa, mldsa_make_key, mldsa_sign, mldsa_verify, random))]
/// #[cfg(all(mldsa, mldsa_level2, mldsa_make_key, mldsa_sign, mldsa_verify, random))]
/// {
/// use wolfssl_wolfcrypt::random::RNG;
/// use wolfssl_wolfcrypt::mldsa::MlDsa;
@@ -1219,7 +1237,7 @@ impl MlDsa {
/// # Example
///
/// ```rust
/// #[cfg(all(mldsa, mldsa_make_key, mldsa_sign, mldsa_verify, random))]
/// #[cfg(all(mldsa, mldsa_level2, mldsa_make_key, mldsa_sign, mldsa_verify, random))]
/// {
/// use wolfssl_wolfcrypt::random::RNG;
/// use wolfssl_wolfcrypt::mldsa::MlDsa;

View File

@@ -30,8 +30,11 @@ use wolfssl_wolfcrypt::random::RNG;
/// the wolfCrypt API.
#[test]
fn test_level_constants() {
#[cfg(mldsa_level2)]
assert_eq!(MlDsa::LEVEL_44, 2);
#[cfg(mldsa_level3)]
assert_eq!(MlDsa::LEVEL_65, 3);
#[cfg(mldsa_level5)]
assert_eq!(MlDsa::LEVEL_87, 5);
}
@@ -42,18 +45,28 @@ fn test_new_and_level() {
let mut key = MlDsa::new().expect("Error with new()");
key.set_level(MlDsa::LEVEL_44).expect("Error with set_level()");
assert_eq!(key.get_level().expect("Error with get_level()"), MlDsa::LEVEL_44);
#[cfg(mldsa_level2)]
{
key.set_level(MlDsa::LEVEL_44).expect("Error with set_level()");
assert_eq!(key.get_level().expect("Error with get_level()"), MlDsa::LEVEL_44);
}
key.set_level(MlDsa::LEVEL_65).expect("Error with set_level()");
assert_eq!(key.get_level().expect("Error with get_level()"), MlDsa::LEVEL_65);
#[cfg(mldsa_level3)]
{
key.set_level(MlDsa::LEVEL_65).expect("Error with set_level()");
assert_eq!(key.get_level().expect("Error with get_level()"), MlDsa::LEVEL_65);
}
key.set_level(MlDsa::LEVEL_87).expect("Error with set_level()");
assert_eq!(key.get_level().expect("Error with get_level()"), MlDsa::LEVEL_87);
#[cfg(mldsa_level5)]
{
key.set_level(MlDsa::LEVEL_87).expect("Error with set_level()");
assert_eq!(key.get_level().expect("Error with get_level()"), MlDsa::LEVEL_87);
}
}
/// Verify that `new_ex()` accepts the optional heap and device ID parameters.
#[test]
#[cfg(mldsa_level2)]
fn test_new_ex() {
common::setup();
let mut key = MlDsa::new_ex(None, None).expect("Error with new_ex()");
@@ -111,7 +124,7 @@ fn test_sizes_level87() {
/// Verify that `check_key()` accepts a freshly generated ML-DSA-44 key pair.
#[test]
#[cfg(all(mldsa_make_key, mldsa_check_key))]
#[cfg(all(mldsa_make_key, mldsa_check_key, mldsa_level2))]
fn test_check_key_level44() {
common::setup();
let mut rng = RNG::new().expect("Error creating RNG");
@@ -122,7 +135,7 @@ fn test_check_key_level44() {
/// Verify that `check_key()` accepts a freshly generated ML-DSA-65 key pair.
#[test]
#[cfg(all(mldsa_make_key, mldsa_check_key))]
#[cfg(all(mldsa_make_key, mldsa_check_key, mldsa_level3))]
fn test_check_key_level65() {
common::setup();
let mut rng = RNG::new().expect("Error creating RNG");
@@ -133,7 +146,7 @@ fn test_check_key_level65() {
/// Verify that `check_key()` accepts a freshly generated ML-DSA-87 key pair.
#[test]
#[cfg(all(mldsa_make_key, mldsa_check_key))]
#[cfg(all(mldsa_make_key, mldsa_check_key, mldsa_level5))]
fn test_check_key_level87() {
common::setup();
let mut rng = RNG::new().expect("Error creating RNG");
@@ -147,7 +160,7 @@ fn test_check_key_level87() {
/// Also verifies that a tampered message or signature produces a
/// verification failure rather than an error.
#[test]
#[cfg(all(mldsa_make_key, mldsa_sign, mldsa_verify))]
#[cfg(all(mldsa_make_key, mldsa_sign, mldsa_verify, mldsa_level2))]
fn test_sign_verify_level44() {
common::setup();
let mut rng = RNG::new().expect("Error creating RNG");
@@ -171,7 +184,7 @@ fn test_sign_verify_level44() {
/// Sign and verify a message round-trip using ML-DSA-65.
#[test]
#[cfg(all(mldsa_make_key, mldsa_sign, mldsa_verify))]
#[cfg(all(mldsa_make_key, mldsa_sign, mldsa_verify, mldsa_level3))]
fn test_sign_verify_level65() {
common::setup();
let mut rng = RNG::new().expect("Error creating RNG");
@@ -190,7 +203,7 @@ fn test_sign_verify_level65() {
/// Sign and verify a message round-trip using ML-DSA-87.
#[test]
#[cfg(all(mldsa_make_key, mldsa_sign, mldsa_verify))]
#[cfg(all(mldsa_make_key, mldsa_sign, mldsa_verify, mldsa_level5))]
fn test_sign_verify_level87() {
common::setup();
let mut rng = RNG::new().expect("Error creating RNG");
@@ -211,7 +224,7 @@ fn test_sign_verify_level87() {
///
/// Also verifies that a mismatched context causes verification to fail.
#[test]
#[cfg(all(mldsa_make_key, mldsa_sign, mldsa_verify))]
#[cfg(all(mldsa_make_key, mldsa_sign, mldsa_verify, mldsa_level2))]
fn test_sign_ctx_verify_level44() {
common::setup();
let mut rng = RNG::new().expect("Error creating RNG");
@@ -242,7 +255,7 @@ fn test_sign_ctx_verify_level44() {
#[test]
#[cfg(all(mldsa_make_key, mldsa_size, mldsa_pub_size, mldsa_sig_size,
mldsa_import_public, mldsa_import_private, mldsa_export_public,
mldsa_export_private, mldsa_sign, mldsa_verify))]
mldsa_export_private, mldsa_sign, mldsa_verify, mldsa_level2))]
fn test_import_export_level44() {
common::setup();
let mut rng = RNG::new().expect("Error creating RNG");
@@ -299,7 +312,7 @@ fn test_import_export_level44() {
#[test]
#[cfg(all(mldsa_make_key, mldsa_size, mldsa_pub_size, mldsa_sig_size,
mldsa_import_public, mldsa_import_private, mldsa_export_public,
mldsa_export_private, mldsa_sign, mldsa_verify))]
mldsa_export_private, mldsa_sign, mldsa_verify, mldsa_level2))]
fn test_import_key_level44() {
common::setup();
let mut rng = RNG::new().expect("Error creating RNG");
@@ -331,7 +344,7 @@ fn test_import_key_level44() {
/// produces the same key pair on repeated calls.
#[test]
#[cfg(all(mldsa_make_key_from_seed, mldsa_size, mldsa_pub_size,
mldsa_export_public, mldsa_export_private))]
mldsa_export_public, mldsa_export_private, mldsa_level2))]
fn test_generate_from_seed_determinism() {
common::setup();
// MLDSA_SEED_SZ = 32 bytes
@@ -361,7 +374,8 @@ fn test_generate_from_seed_determinism() {
/// message, and signing seed always produce the same signature bytes, and
/// the signature verifies correctly.
#[test]
#[cfg(all(mldsa_make_key_from_seed, mldsa_sign_with_seed, mldsa_verify))]
#[cfg(all(mldsa_make_key_from_seed, mldsa_sign_with_seed, mldsa_verify,
mldsa_level2))]
fn test_sign_with_seed_determinism() {
common::setup();
// MLDSA_SEED_SZ = 32 bytes
@@ -393,7 +407,8 @@ fn test_sign_with_seed_determinism() {
/// Verify that `sign_ctx_msg_with_seed()` is deterministic and that the
/// produced signature verifies with `verify_ctx_msg()`.
#[test]
#[cfg(all(mldsa_make_key_from_seed, mldsa_sign_with_seed, mldsa_verify))]
#[cfg(all(mldsa_make_key_from_seed, mldsa_sign_with_seed, mldsa_verify,
mldsa_level2))]
fn test_sign_ctx_with_seed_determinism() {
common::setup();
let key_seed = [0x11u8; 32];
@@ -431,7 +446,19 @@ fn test_seed_sign_verify_all_levels() {
let sign_seed = [0xCDu8; 32];
let message = b"All-levels seed sign/verify test";
for level in [MlDsa::LEVEL_44, MlDsa::LEVEL_65, MlDsa::LEVEL_87] {
// Only the parameter sets the library was built with have a level
// constant, so build the list from the ones that are present. At
// least one is always present: wc_mldsa.h rejects a build with all
// three disabled.
let mut levels: Vec<u8> = Vec::new();
#[cfg(mldsa_level2)]
levels.push(MlDsa::LEVEL_44);
#[cfg(mldsa_level3)]
levels.push(MlDsa::LEVEL_65);
#[cfg(mldsa_level5)]
levels.push(MlDsa::LEVEL_87);
for level in levels {
let mut key = MlDsa::generate_from_seed(level, &key_seed)
.expect("Error with generate_from_seed()");
let sig_size = key.sig_size().expect("Error with sig_size()");