Files
wolfssl/wrapper/rust/wolfssl-wolfcrypt/src/dilithium.rs
T
Josh Holtrop 54cb1d8d38 Rust wrapper: ensure memory safety for C RNG struct
- store pointer to WC_RNG instead of full struct
- enforce RNG is not dropped before consumer structs

The C library stores a pointer via the set_rng() methods on a few
structs (e.g. RSA). This change holds a reference (or instance) of RNG
within the consumer structs to ensure it is kept alive if set_rng (or
now set_shared_rng) is used.
2026-05-18 12:27:44 -07:00

1318 lines
45 KiB
Rust

/*
* Copyright (C) 2006-2026 wolfSSL Inc.
*
* This file is part of wolfSSL.
*
* wolfSSL is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfSSL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
/*!
This module provides a Rust wrapper for the wolfCrypt library's ML-DSA
(Dilithium) post-quantum digital signature functionality.
The primary component is the [`Dilithium`] struct, which manages the lifecycle
of a wolfSSL `dilithium_key` object. It ensures proper initialization and
deallocation.
Three security parameter sets are supported, selected via
[`Dilithium::set_level()`]:
| Constant | Level | NIST PQC Level |
|-----------------|-------|----------------|
| [`Dilithium::LEVEL_44`] | 2 | 2 (ML-DSA-44) |
| [`Dilithium::LEVEL_65`] | 3 | 3 (ML-DSA-65) |
| [`Dilithium::LEVEL_87`] | 5 | 5 (ML-DSA-87) |
# Examples
```rust
#[cfg(all(dilithium, dilithium_make_key, dilithium_sign, dilithium_verify, random))]
{
use wolfssl_wolfcrypt::random::RNG;
use wolfssl_wolfcrypt::dilithium::Dilithium;
let mut rng = RNG::new().expect("RNG creation failed");
let mut key = Dilithium::generate(Dilithium::LEVEL_44, &mut rng)
.expect("Key generation failed");
let message = b"Hello, ML-DSA!";
let mut sig = vec![0u8; key.sig_size().expect("sig_size failed")];
let sig_len = key.sign_msg(message, &mut sig, &mut rng)
.expect("Signing failed");
let valid = key.verify_msg(&sig[..sig_len], message)
.expect("Verification failed");
assert!(valid);
}
```
*/
#![cfg(dilithium)]
use crate::sys;
#[cfg(all(random, any(dilithium_make_key, dilithium_sign)))]
use crate::random::RNG;
use core::mem::MaybeUninit;
/// Rust wrapper for a wolfSSL `dilithium_key` object.
///
/// Manages the lifecycle of the underlying key, including initialization and
/// deallocation via the [`Drop`] trait.
///
/// An instance is created with [`Dilithium::generate()`],
/// [`Dilithium::generate_from_seed()`], or [`Dilithium::new()`].
pub struct Dilithium {
ws_key: sys::dilithium_key,
}
impl Dilithium {
/// ML-DSA-44 security parameter set (NIST Level 2).
pub const LEVEL_44: u8 = sys::WC_ML_DSA_44 as u8;
/// ML-DSA-65 security parameter set (NIST Level 3).
pub const LEVEL_65: u8 = sys::WC_ML_DSA_65 as u8;
/// ML-DSA-87 security parameter set (NIST Level 5).
pub const LEVEL_87: u8 = sys::WC_ML_DSA_87 as u8;
/// Required size in bytes of the seed passed to
/// [`Dilithium::generate_from_seed()`] (`DILITHIUM_SEED_SZ`).
pub const DILITHIUM_SEED_SZ: usize = sys::DILITHIUM_SEED_SZ as usize;
/// Required size in bytes of the seed passed to signing-with-seed
/// functions such as [`Dilithium::sign_msg_with_seed()`]
/// (`DILITHIUM_RND_SZ`).
pub const SIGN_SEED_SIZE: usize = sys::DILITHIUM_RND_SZ as usize;
/// Private (secret) key size in bytes for ML-DSA-44.
#[cfg(dilithium_level2)]
pub const LEVEL2_KEY_SIZE: usize = sys::DILITHIUM_LEVEL2_KEY_SIZE as usize;
/// Signature size in bytes for ML-DSA-44.
#[cfg(dilithium_level2)]
pub const LEVEL2_SIG_SIZE: usize = sys::DILITHIUM_LEVEL2_SIG_SIZE as usize;
/// Public key size in bytes for ML-DSA-44.
#[cfg(dilithium_level2)]
pub const LEVEL2_PUB_KEY_SIZE: usize = sys::DILITHIUM_LEVEL2_PUB_KEY_SIZE as usize;
/// Combined private-plus-public key size in bytes for ML-DSA-44.
#[cfg(dilithium_level2)]
pub const LEVEL2_PRV_KEY_SIZE: usize =
sys::DILITHIUM_LEVEL2_PUB_KEY_SIZE as usize + sys::DILITHIUM_LEVEL2_KEY_SIZE as usize;
/// Private (secret) key size in bytes for ML-DSA-65.
#[cfg(dilithium_level3)]
pub const LEVEL3_KEY_SIZE: usize = sys::DILITHIUM_LEVEL3_KEY_SIZE as usize;
/// Signature size in bytes for ML-DSA-65.
#[cfg(dilithium_level3)]
pub const LEVEL3_SIG_SIZE: usize = sys::DILITHIUM_LEVEL3_SIG_SIZE as usize;
/// Public key size in bytes for ML-DSA-65.
#[cfg(dilithium_level3)]
pub const LEVEL3_PUB_KEY_SIZE: usize = sys::DILITHIUM_LEVEL3_PUB_KEY_SIZE as usize;
/// Combined private-plus-public key size in bytes for ML-DSA-65.
#[cfg(dilithium_level3)]
pub const LEVEL3_PRV_KEY_SIZE: usize =
sys::DILITHIUM_LEVEL3_PUB_KEY_SIZE as usize + sys::DILITHIUM_LEVEL3_KEY_SIZE as usize;
/// Private (secret) key size in bytes for ML-DSA-87.
#[cfg(dilithium_level5)]
pub const LEVEL5_KEY_SIZE: usize = sys::DILITHIUM_LEVEL5_KEY_SIZE as usize;
/// Signature size in bytes for ML-DSA-87.
#[cfg(dilithium_level5)]
pub const LEVEL5_SIG_SIZE: usize = sys::DILITHIUM_LEVEL5_SIG_SIZE as usize;
/// Public key size in bytes for ML-DSA-87.
#[cfg(dilithium_level5)]
pub const LEVEL5_PUB_KEY_SIZE: usize = sys::DILITHIUM_LEVEL5_PUB_KEY_SIZE as usize;
/// Combined private-plus-public key size in bytes for ML-DSA-87.
#[cfg(dilithium_level5)]
pub const LEVEL5_PRV_KEY_SIZE: usize =
sys::DILITHIUM_LEVEL5_PUB_KEY_SIZE as usize + sys::DILITHIUM_LEVEL5_KEY_SIZE as usize;
/// Generate a new Dilithium key pair using a random number generator.
///
/// # Parameters
///
/// * `level`: Security parameter set. One of [`Dilithium::LEVEL_44`],
/// [`Dilithium::LEVEL_65`], or [`Dilithium::LEVEL_87`].
/// * `rng`: `RNG` instance to use for random number generation.
///
/// # Returns
///
/// Returns either Ok(Dilithium) containing the key instance or Err(e)
/// containing the wolfSSL library error code value.
///
/// # Example
///
/// ```rust
/// #[cfg(all(dilithium, dilithium_make_key, random))]
/// {
/// use wolfssl_wolfcrypt::random::RNG;
/// use wolfssl_wolfcrypt::dilithium::Dilithium;
/// let mut rng = RNG::new().expect("Error creating RNG");
/// let key = Dilithium::generate(Dilithium::LEVEL_44, &mut rng)
/// .expect("Error with generate()");
/// }
/// ```
#[cfg(all(dilithium_make_key, random))]
pub fn generate(level: u8, rng: &RNG) -> Result<Self, i32> {
Self::generate_ex(level, rng, None, None)
}
/// Generate a new Dilithium key pair with optional heap hint and device ID.
///
/// # Parameters
///
/// * `level`: Security parameter set. One of [`Dilithium::LEVEL_44`],
/// [`Dilithium::LEVEL_65`], or [`Dilithium::LEVEL_87`].
/// * `rng`: `RNG` instance to use for random number generation.
/// * `heap`: Optional heap hint.
/// * `dev_id`: Optional device ID for crypto callbacks or async hardware.
///
/// # Returns
///
/// Returns either Ok(Dilithium) containing the key instance or Err(e)
/// containing the wolfSSL library error code value.
///
/// # Example
///
/// ```rust
/// #[cfg(all(dilithium, dilithium_make_key, random))]
/// {
/// use wolfssl_wolfcrypt::random::RNG;
/// use wolfssl_wolfcrypt::dilithium::Dilithium;
/// let mut rng = RNG::new().expect("Error creating RNG");
/// let key = Dilithium::generate_ex(Dilithium::LEVEL_44, &mut rng, None, None)
/// .expect("Error with generate_ex()");
/// }
/// ```
#[cfg(all(dilithium_make_key, random))]
pub fn generate_ex(
level: u8,
rng: &RNG,
heap: Option<*mut core::ffi::c_void>,
dev_id: Option<i32>,
) -> Result<Self, i32> {
let mut key = Self::new_ex(heap, dev_id)?;
let rc = unsafe { sys::wc_dilithium_set_level(&mut key.ws_key, level) };
if rc != 0 {
return Err(rc);
}
let rc = unsafe { sys::wc_dilithium_make_key(&mut key.ws_key, rng.wc_rng) };
if rc != 0 {
return Err(rc);
}
Ok(key)
}
/// Generate a Dilithium key pair from a fixed seed.
///
/// Produces the same key pair for a given `(level, seed)` pair, enabling
/// deterministic key generation.
///
/// # Parameters
///
/// * `level`: Security parameter set. One of [`Dilithium::LEVEL_44`],
/// [`Dilithium::LEVEL_65`], or [`Dilithium::LEVEL_87`].
/// * `seed`: Seed bytes. Must be `DILITHIUM_SEED_SZ` (32) bytes.
///
/// # Returns
///
/// Returns either Ok(Dilithium) containing the key instance or Err(e)
/// containing the wolfSSL library error code value.
///
/// # Example
///
/// ```rust
/// #[cfg(all(dilithium, dilithium_make_key_from_seed))]
/// {
/// use wolfssl_wolfcrypt::dilithium::Dilithium;
/// let seed = [0x42u8; 32];
/// let key = Dilithium::generate_from_seed(Dilithium::LEVEL_44, &seed)
/// .expect("Error with generate_from_seed()");
/// }
/// ```
#[cfg(dilithium_make_key_from_seed)]
pub fn generate_from_seed(level: u8, seed: &[u8]) -> Result<Self, i32> {
Self::generate_from_seed_ex(level, seed, None, None)
}
/// Generate a Dilithium key pair from a fixed seed with optional heap hint
/// and device ID.
///
/// # Parameters
///
/// * `level`: Security parameter set. One of [`Dilithium::LEVEL_44`],
/// [`Dilithium::LEVEL_65`], or [`Dilithium::LEVEL_87`].
/// * `seed`: Seed bytes. Must be `DILITHIUM_SEED_SZ` (32) bytes.
/// * `heap`: Optional heap hint.
/// * `dev_id`: Optional device ID for crypto callbacks or async hardware.
///
/// # Returns
///
/// Returns either Ok(Dilithium) containing the key instance or Err(e)
/// containing the wolfSSL library error code value.
///
/// # Example
///
/// ```rust
/// #[cfg(all(dilithium, dilithium_make_key_from_seed))]
/// {
/// use wolfssl_wolfcrypt::dilithium::Dilithium;
/// let seed = [0x42u8; 32];
/// let key = Dilithium::generate_from_seed_ex(Dilithium::LEVEL_44, &seed, None, None)
/// .expect("Error with generate_from_seed_ex()");
/// }
/// ```
#[cfg(dilithium_make_key_from_seed)]
pub fn generate_from_seed_ex(
level: u8,
seed: &[u8],
heap: Option<*mut core::ffi::c_void>,
dev_id: Option<i32>,
) -> Result<Self, i32> {
if seed.len() != Self::DILITHIUM_SEED_SZ {
return Err(sys::wolfCrypt_ErrorCodes_BUFFER_E);
}
let mut key = Self::new_ex(heap, dev_id)?;
let rc = unsafe { sys::wc_dilithium_set_level(&mut key.ws_key, level) };
if rc != 0 {
return Err(rc);
}
let rc = unsafe {
sys::wc_dilithium_make_key_from_seed(&mut key.ws_key, seed.as_ptr())
};
if rc != 0 {
return Err(rc);
}
Ok(key)
}
/// Create and initialize a new Dilithium key instance without a key.
///
/// The security level and key material can be set afterwards using
/// [`Dilithium::set_level()`] and one of the import functions.
///
/// # Returns
///
/// Returns either Ok(Dilithium) containing the key instance or Err(e)
/// containing the wolfSSL library error code value.
///
/// # Example
///
/// ```rust
/// #[cfg(dilithium)]
/// {
/// use wolfssl_wolfcrypt::dilithium::Dilithium;
/// let key = Dilithium::new().expect("Error with new()");
/// }
/// ```
pub fn new() -> Result<Self, i32> {
Self::new_ex(None, None)
}
/// Create and initialize a new Dilithium key instance with optional heap
/// hint and device ID.
///
/// # Parameters
///
/// * `heap`: Optional heap hint.
/// * `dev_id`: Optional device ID for crypto callbacks or async hardware.
///
/// # Returns
///
/// Returns either Ok(Dilithium) containing the key instance or Err(e)
/// containing the wolfSSL library error code value.
///
/// # Example
///
/// ```rust
/// #[cfg(dilithium)]
/// {
/// use wolfssl_wolfcrypt::dilithium::Dilithium;
/// let key = Dilithium::new_ex(None, None).expect("Error with new_ex()");
/// }
/// ```
pub fn new_ex(
heap: Option<*mut core::ffi::c_void>,
dev_id: Option<i32>,
) -> Result<Self, i32> {
let mut ws_key: MaybeUninit<sys::dilithium_key> = MaybeUninit::uninit();
let heap = match heap {
Some(h) => h,
None => core::ptr::null_mut(),
};
let dev_id = match dev_id {
Some(id) => id,
None => sys::INVALID_DEVID,
};
let rc = unsafe { sys::wc_dilithium_init_ex(ws_key.as_mut_ptr(), heap, dev_id) };
if rc != 0 {
return Err(rc);
}
let ws_key = unsafe { ws_key.assume_init() };
Ok(Dilithium { ws_key })
}
/// Set the security parameter level for this key.
///
/// Must be called before generating or importing key material. Use one of
/// the level constants: [`Dilithium::LEVEL_44`], [`Dilithium::LEVEL_65`],
/// or [`Dilithium::LEVEL_87`].
///
/// # Parameters
///
/// * `level`: Security level (2 = ML-DSA-44, 3 = ML-DSA-65, 5 = ML-DSA-87).
///
/// # Returns
///
/// Returns either Ok(()) on success or Err(e) containing the wolfSSL
/// library error code value.
///
/// # Example
///
/// ```rust
/// #[cfg(dilithium)]
/// {
/// use wolfssl_wolfcrypt::dilithium::Dilithium;
/// let mut key = Dilithium::new().expect("Error with new()");
/// key.set_level(Dilithium::LEVEL_65).expect("Error with set_level()");
/// }
/// ```
pub fn set_level(&mut self, level: u8) -> Result<(), i32> {
let rc = unsafe { sys::wc_dilithium_set_level(&mut self.ws_key, level) };
if rc != 0 {
return Err(rc);
}
Ok(())
}
/// Get the security parameter level of this key.
///
/// # Returns
///
/// Returns either Ok(level) containing the current security level or
/// Err(e) containing the wolfSSL library error code value.
///
/// # Example
///
/// ```rust
/// #[cfg(dilithium)]
/// {
/// use wolfssl_wolfcrypt::dilithium::Dilithium;
/// let mut key = Dilithium::new().expect("Error with new()");
/// key.set_level(Dilithium::LEVEL_87).expect("Error with set_level()");
/// let level = key.get_level().expect("Error with get_level()");
/// assert_eq!(level, Dilithium::LEVEL_87);
/// }
/// ```
pub fn get_level(&mut self) -> Result<u8, i32> {
let mut level = 0u8;
let rc = unsafe { sys::wc_dilithium_get_level(&mut self.ws_key, &mut level) };
if rc != 0 {
return Err(rc);
}
Ok(level)
}
/// Get the private (secret) key size in bytes for the current level.
///
/// # Returns
///
/// Returns either Ok(size) containing the private key size or Err(e)
/// containing the wolfSSL library error code value.
///
/// # Example
///
/// ```rust
/// #[cfg(all(dilithium, dilithium_make_key, random))]
/// {
/// use wolfssl_wolfcrypt::random::RNG;
/// use wolfssl_wolfcrypt::dilithium::Dilithium;
/// let mut rng = RNG::new().expect("Error creating RNG");
/// let mut key = Dilithium::generate(Dilithium::LEVEL_44, &mut rng)
/// .expect("Error with generate()");
/// let sz = key.size().expect("Error with size()");
/// assert_eq!(sz, Dilithium::LEVEL2_KEY_SIZE);
/// }
/// ```
pub fn size(&mut self) -> Result<usize, i32> {
let rc = unsafe { sys::wc_dilithium_size(&mut self.ws_key) };
if rc < 0 {
return Err(rc);
}
Ok(rc as usize)
}
/// Get the combined private-plus-public key size in bytes for the current
/// level.
///
/// # Returns
///
/// Returns either Ok(size) or Err(e) containing the wolfSSL library error
/// code value.
///
/// # Example
///
/// ```rust
/// #[cfg(all(dilithium, dilithium_make_key, random))]
/// {
/// use wolfssl_wolfcrypt::random::RNG;
/// use wolfssl_wolfcrypt::dilithium::Dilithium;
/// let mut rng = RNG::new().expect("Error creating RNG");
/// let mut key = Dilithium::generate(Dilithium::LEVEL_44, &mut rng)
/// .expect("Error with generate()");
/// let sz = key.priv_size().expect("Error with priv_size()");
/// assert_eq!(sz, Dilithium::LEVEL2_PRV_KEY_SIZE);
/// }
/// ```
pub fn priv_size(&mut self) -> Result<usize, i32> {
let rc = unsafe { sys::wc_dilithium_priv_size(&mut self.ws_key) };
if rc < 0 {
return Err(rc);
}
Ok(rc as usize)
}
/// Get the public key size in bytes for the current level.
///
/// # Returns
///
/// Returns either Ok(size) or Err(e) containing the wolfSSL library error
/// code value.
///
/// # Example
///
/// ```rust
/// #[cfg(all(dilithium, dilithium_make_key, random))]
/// {
/// use wolfssl_wolfcrypt::random::RNG;
/// use wolfssl_wolfcrypt::dilithium::Dilithium;
/// let mut rng = RNG::new().expect("Error creating RNG");
/// let mut key = Dilithium::generate(Dilithium::LEVEL_44, &mut rng)
/// .expect("Error with generate()");
/// let sz = key.pub_size().expect("Error with pub_size()");
/// assert_eq!(sz, Dilithium::LEVEL2_PUB_KEY_SIZE);
/// }
/// ```
pub fn pub_size(&mut self) -> Result<usize, i32> {
let rc = unsafe { sys::wc_dilithium_pub_size(&mut self.ws_key) };
if rc < 0 {
return Err(rc);
}
Ok(rc as usize)
}
/// Get the signature size in bytes for the current level.
///
/// # Returns
///
/// Returns either Ok(size) or Err(e) containing the wolfSSL library error
/// code value.
///
/// # Example
///
/// ```rust
/// #[cfg(all(dilithium, dilithium_make_key, random))]
/// {
/// use wolfssl_wolfcrypt::random::RNG;
/// use wolfssl_wolfcrypt::dilithium::Dilithium;
/// let mut rng = RNG::new().expect("Error creating RNG");
/// let mut key = Dilithium::generate(Dilithium::LEVEL_44, &mut rng)
/// .expect("Error with generate()");
/// let sz = key.sig_size().expect("Error with sig_size()");
/// assert_eq!(sz, Dilithium::LEVEL2_SIG_SIZE);
/// }
/// ```
pub fn sig_size(&mut self) -> Result<usize, i32> {
let rc = unsafe { sys::wc_dilithium_sig_size(&mut self.ws_key) };
if rc < 0 {
return Err(rc);
}
Ok(rc as usize)
}
/// Check that the key pair is valid (public key matches private key).
///
/// # Returns
///
/// Returns either Ok(()) on success or Err(e) containing the wolfSSL
/// library error code value.
///
/// # Example
///
/// ```rust
/// #[cfg(all(dilithium, dilithium_make_key, dilithium_check_key, random))]
/// {
/// use wolfssl_wolfcrypt::random::RNG;
/// use wolfssl_wolfcrypt::dilithium::Dilithium;
/// let mut rng = RNG::new().expect("Error creating RNG");
/// let mut key = Dilithium::generate(Dilithium::LEVEL_44, &mut rng)
/// .expect("Error with generate()");
/// key.check_key().expect("Error with check_key()");
/// }
/// ```
#[cfg(dilithium_check_key)]
pub fn check_key(&mut self) -> Result<(), i32> {
let rc = unsafe { sys::wc_dilithium_check_key(&mut self.ws_key) };
if rc != 0 {
return Err(rc);
}
Ok(())
}
/// Import a public key from a raw byte buffer.
///
/// # Parameters
///
/// * `public`: Input buffer containing the raw public key bytes.
///
/// # Returns
///
/// Returns either Ok(()) on success or Err(e) containing the wolfSSL
/// library error code value.
///
/// # Example
///
/// ```rust
/// #[cfg(all(dilithium, dilithium_make_key, dilithium_import, dilithium_export, random))]
/// {
/// use wolfssl_wolfcrypt::random::RNG;
/// use wolfssl_wolfcrypt::dilithium::Dilithium;
/// let mut rng = RNG::new().expect("Error creating RNG");
/// let mut key = Dilithium::generate(Dilithium::LEVEL_44, &mut rng)
/// .expect("Error with generate()");
/// let mut pub_buf = vec![0u8; key.pub_size().unwrap()];
/// key.export_public(&mut pub_buf).expect("Error with export_public()");
/// let mut key2 = Dilithium::new().expect("Error with new()");
/// key2.set_level(Dilithium::LEVEL_44).expect("Error with set_level()");
/// key2.import_public(&pub_buf).expect("Error with import_public()");
/// }
/// ```
#[cfg(dilithium_import)]
pub fn import_public(&mut self, public: &[u8]) -> Result<(), i32> {
let public_size = crate::buffer_len_to_u32(public.len())?;
let rc = unsafe {
sys::wc_dilithium_import_public(public.as_ptr(), public_size, &mut self.ws_key)
};
if rc != 0 {
return Err(rc);
}
Ok(())
}
/// Import a private (secret) key from a raw byte buffer.
///
/// The buffer should contain the raw private key bytes only
/// (size = `LEVEL*_KEY_SIZE`).
///
/// # Parameters
///
/// * `private`: Input buffer containing the raw private key bytes.
///
/// # Returns
///
/// Returns either Ok(()) on success or Err(e) containing the wolfSSL
/// library error code value.
///
/// # Example
///
/// ```rust
/// #[cfg(all(dilithium, dilithium_make_key, dilithium_import, dilithium_export, random))]
/// {
/// use wolfssl_wolfcrypt::random::RNG;
/// use wolfssl_wolfcrypt::dilithium::Dilithium;
/// let mut rng = RNG::new().expect("Error creating RNG");
/// let mut key = Dilithium::generate(Dilithium::LEVEL_44, &mut rng)
/// .expect("Error with generate()");
/// let mut priv_buf = vec![0u8; key.size().unwrap()];
/// key.export_private(&mut priv_buf).expect("Error with export_private()");
/// let mut key2 = Dilithium::new().expect("Error with new()");
/// key2.set_level(Dilithium::LEVEL_44).expect("Error with set_level()");
/// key2.import_private(&priv_buf).expect("Error with import_private()");
/// }
/// ```
#[cfg(dilithium_import)]
pub fn import_private(&mut self, private: &[u8]) -> Result<(), i32> {
let private_size = crate::buffer_len_to_u32(private.len())?;
let rc = unsafe {
sys::wc_dilithium_import_private(private.as_ptr(), private_size, &mut self.ws_key)
};
if rc != 0 {
return Err(rc);
}
Ok(())
}
/// Import both private and public key material from raw byte buffers.
///
/// # Parameters
///
/// * `private`: Input buffer containing the raw private key bytes.
/// * `public`: Input buffer containing the raw public key bytes.
///
/// # Returns
///
/// Returns either Ok(()) on success or Err(e) containing the wolfSSL
/// library error code value.
///
/// # Example
///
/// ```rust
/// #[cfg(all(dilithium, dilithium_make_key, dilithium_import, dilithium_export, random))]
/// {
/// use wolfssl_wolfcrypt::random::RNG;
/// use wolfssl_wolfcrypt::dilithium::Dilithium;
/// let mut rng = RNG::new().expect("Error creating RNG");
/// let mut key = Dilithium::generate(Dilithium::LEVEL_44, &mut rng)
/// .expect("Error with generate()");
/// let mut priv_buf = vec![0u8; key.size().unwrap()];
/// let mut pub_buf = vec![0u8; key.pub_size().unwrap()];
/// key.export_key(&mut priv_buf, &mut pub_buf).expect("Error with export_key()");
/// let mut key2 = Dilithium::new().expect("Error with new()");
/// key2.set_level(Dilithium::LEVEL_44).expect("Error with set_level()");
/// key2.import_key(&priv_buf, &pub_buf).expect("Error with import_key()");
/// }
/// ```
#[cfg(dilithium_import)]
pub fn import_key(&mut self, private: &[u8], public: &[u8]) -> Result<(), i32> {
let private_size = crate::buffer_len_to_u32(private.len())?;
let public_size = crate::buffer_len_to_u32(public.len())?;
let rc = unsafe {
sys::wc_dilithium_import_key(
private.as_ptr(), private_size,
public.as_ptr(), public_size,
&mut self.ws_key,
)
};
if rc != 0 {
return Err(rc);
}
Ok(())
}
/// Export the public key to a raw byte buffer.
///
/// # Parameters
///
/// * `public`: Output buffer to receive the public key. Must be at least
/// `pub_size()` bytes.
///
/// # Returns
///
/// Returns either Ok(size) containing the number of bytes written or Err(e)
/// containing the wolfSSL library error code value.
///
/// # Example
///
/// ```rust
/// #[cfg(all(dilithium, dilithium_make_key, dilithium_export, random))]
/// {
/// use wolfssl_wolfcrypt::random::RNG;
/// use wolfssl_wolfcrypt::dilithium::Dilithium;
/// let mut rng = RNG::new().expect("Error creating RNG");
/// let mut key = Dilithium::generate(Dilithium::LEVEL_44, &mut rng)
/// .expect("Error with generate()");
/// let mut pub_buf = vec![0u8; key.pub_size().unwrap()];
/// let written = key.export_public(&mut pub_buf).expect("Error with export_public()");
/// assert_eq!(written, Dilithium::LEVEL2_PUB_KEY_SIZE);
/// }
/// ```
#[cfg(dilithium_export)]
pub fn export_public(&mut self, public: &mut [u8]) -> Result<usize, i32> {
let mut public_size = crate::buffer_len_to_u32(public.len())?;
let rc = unsafe {
sys::wc_dilithium_export_public(&mut self.ws_key, public.as_mut_ptr(), &mut public_size)
};
if rc != 0 {
return Err(rc);
}
Ok(public_size as usize)
}
/// Export the private (secret) key to a raw byte buffer.
///
/// # Parameters
///
/// * `private`: Output buffer to receive the private key. Must be at
/// least `size()` bytes.
///
/// # Returns
///
/// Returns either Ok(size) containing the number of bytes written or Err(e)
/// containing the wolfSSL library error code value.
///
/// # Example
///
/// ```rust
/// #[cfg(all(dilithium, dilithium_make_key, dilithium_export, random))]
/// {
/// use wolfssl_wolfcrypt::random::RNG;
/// use wolfssl_wolfcrypt::dilithium::Dilithium;
/// let mut rng = RNG::new().expect("Error creating RNG");
/// let mut key = Dilithium::generate(Dilithium::LEVEL_44, &mut rng)
/// .expect("Error with generate()");
/// let mut priv_buf = vec![0u8; key.size().unwrap()];
/// let written = key.export_private(&mut priv_buf).expect("Error with export_private()");
/// assert_eq!(written, Dilithium::LEVEL2_KEY_SIZE);
/// }
/// ```
#[cfg(dilithium_export)]
pub fn export_private(&mut self, private: &mut [u8]) -> Result<usize, i32> {
let mut private_size = crate::buffer_len_to_u32(private.len())?;
let rc = unsafe {
sys::wc_dilithium_export_private(
&mut self.ws_key, private.as_mut_ptr(), &mut private_size,
)
};
if rc != 0 {
return Err(rc);
}
Ok(private_size as usize)
}
/// Export both private and public key material to separate raw byte
/// buffers.
///
/// # Parameters
///
/// * `private`: Output buffer for the private key. Must be at least
/// `size()` bytes.
/// * `public`: Output buffer for the public key. Must be at least
/// `pub_size()` bytes.
///
/// # Returns
///
/// Returns either Ok(()) on success or Err(e) containing the wolfSSL
/// library error code value.
///
/// # Example
///
/// ```rust
/// #[cfg(all(dilithium, dilithium_make_key, dilithium_export, random))]
/// {
/// use wolfssl_wolfcrypt::random::RNG;
/// use wolfssl_wolfcrypt::dilithium::Dilithium;
/// let mut rng = RNG::new().expect("Error creating RNG");
/// let mut key = Dilithium::generate(Dilithium::LEVEL_44, &mut rng)
/// .expect("Error with generate()");
/// let mut priv_buf = vec![0u8; key.size().unwrap()];
/// let mut pub_buf = vec![0u8; key.pub_size().unwrap()];
/// key.export_key(&mut priv_buf, &mut pub_buf).expect("Error with export_key()");
/// }
/// ```
#[cfg(dilithium_export)]
pub fn export_key(&mut self, private: &mut [u8], public: &mut [u8]) -> Result<(), i32> {
let mut private_size = crate::buffer_len_to_u32(private.len())?;
let mut public_size = crate::buffer_len_to_u32(public.len())?;
let rc = unsafe {
sys::wc_dilithium_export_key(
&mut self.ws_key,
private.as_mut_ptr(), &mut private_size,
public.as_mut_ptr(), &mut public_size,
)
};
if rc != 0 {
return Err(rc);
}
Ok(())
}
/// Sign a message and write the signature to `sig`.
///
/// # Parameters
///
/// * `msg`: Message to sign.
/// * `sig`: Output buffer to hold the signature. Must be at least
/// `sig_size()` bytes.
/// * `rng`: RNG instance for hedged signing. For deterministic signing,
/// use [`Dilithium::sign_msg_with_seed()`] instead.
///
/// # Returns
///
/// Returns either Ok(size) containing the number of bytes written to `sig`
/// on success or Err(e) containing the wolfSSL library error code value.
///
/// # Example
///
/// ```rust
/// #[cfg(all(dilithium, dilithium_make_key, dilithium_sign, random))]
/// {
/// use wolfssl_wolfcrypt::random::RNG;
/// use wolfssl_wolfcrypt::dilithium::Dilithium;
/// let mut rng = RNG::new().expect("Error creating RNG");
/// let mut key = Dilithium::generate(Dilithium::LEVEL_44, &mut rng)
/// .expect("Error with generate()");
/// let message = b"Hello, ML-DSA!";
/// let mut sig = vec![0u8; key.sig_size().unwrap()];
/// let sig_len = key.sign_msg(message, &mut sig, &mut rng)
/// .expect("Error with sign_msg()");
/// assert_eq!(sig_len, Dilithium::LEVEL2_SIG_SIZE);
/// }
/// ```
#[cfg(all(dilithium_sign, random))]
pub fn sign_msg(
&mut self,
msg: &[u8],
sig: &mut [u8],
rng: &RNG,
) -> Result<usize, i32> {
let msg_len = crate::buffer_len_to_u32(msg.len())?;
let mut sig_len = crate::buffer_len_to_u32(sig.len())?;
let rc = unsafe {
sys::wc_dilithium_sign_ctx_msg(
core::ptr::null(), 0,
msg.as_ptr(), msg_len,
sig.as_mut_ptr(), &mut sig_len,
&mut self.ws_key,
rng.wc_rng,
)
};
if rc != 0 {
return Err(rc);
}
Ok(sig_len as usize)
}
/// Sign a message with a context string and write the signature to `sig`.
///
/// # Parameters
///
/// * `ctx`: Context string (at most 255 bytes).
/// * `msg`: Message to sign.
/// * `sig`: Output buffer to hold the signature. Must be at least
/// `sig_size()` bytes.
/// * `rng`: RNG instance for hedged signing. For deterministic signing,
/// use [`Dilithium::sign_ctx_msg_with_seed()`] instead.
///
/// # Returns
///
/// Returns either Ok(size) containing the number of bytes written to `sig`
/// on success or Err(e) containing the wolfSSL library error code value.
///
/// # Example
///
/// ```rust
/// #[cfg(all(dilithium, dilithium_make_key, dilithium_sign, random))]
/// {
/// use wolfssl_wolfcrypt::random::RNG;
/// use wolfssl_wolfcrypt::dilithium::Dilithium;
/// let mut rng = RNG::new().expect("Error creating RNG");
/// let mut key = Dilithium::generate(Dilithium::LEVEL_44, &mut rng)
/// .expect("Error with generate()");
/// let message = b"Hello, ML-DSA!";
/// let ctx = b"my context";
/// let mut sig = vec![0u8; key.sig_size().unwrap()];
/// key.sign_ctx_msg(ctx, message, &mut sig, &mut rng)
/// .expect("Error with sign_ctx_msg()");
/// }
/// ```
#[cfg(all(dilithium_sign, random))]
pub fn sign_ctx_msg(
&mut self,
ctx: &[u8],
msg: &[u8],
sig: &mut [u8],
rng: &RNG,
) -> Result<usize, i32> {
if ctx.len() > 255 {
return Err(sys::wolfCrypt_ErrorCodes_BUFFER_E);
}
let ctx_len = ctx.len() as u8;
let msg_len = crate::buffer_len_to_u32(msg.len())?;
let mut sig_len = crate::buffer_len_to_u32(sig.len())?;
let rc = unsafe {
sys::wc_dilithium_sign_ctx_msg(
ctx.as_ptr(), ctx_len,
msg.as_ptr(), msg_len,
sig.as_mut_ptr(), &mut sig_len,
&mut self.ws_key,
rng.wc_rng,
)
};
if rc != 0 {
return Err(rc);
}
Ok(sig_len as usize)
}
/// Sign a pre-hashed message with a context string.
///
/// This is the HashML-DSA variant: the message is supplied as a hash
/// digest along with the hash algorithm identifier.
///
/// # Parameters
///
/// * `ctx`: Context string (at most 255 bytes).
/// * `hash_alg`: Hash algorithm identifier (e.g. `WC_HASH_TYPE_SHA256`).
/// * `hash`: Hash digest of the message to sign.
/// * `sig`: Output buffer to hold the signature. Must be at least
/// `sig_size()` bytes.
/// * `rng`: RNG instance for hedged signing. For deterministic signing,
/// use [`Dilithium::sign_ctx_hash_with_seed()`] instead.
///
/// # Returns
///
/// Returns either Ok(size) containing the number of bytes written to `sig`
/// on success or Err(e) containing the wolfSSL library error code value.
#[cfg(all(dilithium_sign, random))]
pub fn sign_ctx_hash(
&mut self,
ctx: &[u8],
hash_alg: i32,
hash: &[u8],
sig: &mut [u8],
rng: &RNG,
) -> Result<usize, i32> {
if ctx.len() > 255 {
return Err(sys::wolfCrypt_ErrorCodes_BUFFER_E);
}
let ctx_len = ctx.len() as u8;
let hash_len = crate::buffer_len_to_u32(hash.len())?;
let mut sig_len = crate::buffer_len_to_u32(sig.len())?;
let rc = unsafe {
sys::wc_dilithium_sign_ctx_hash(
ctx.as_ptr(), ctx_len,
hash_alg,
hash.as_ptr(), hash_len,
sig.as_mut_ptr(), &mut sig_len,
&mut self.ws_key,
rng.wc_rng,
)
};
if rc != 0 {
return Err(rc);
}
Ok(sig_len as usize)
}
/// Sign a message using a fixed random seed instead of an RNG.
///
/// Produces a deterministic signature for a given `(key, msg, seed)`
/// triple.
///
/// # Parameters
///
/// * `msg`: Message to sign.
/// * `sig`: Output buffer to hold the signature.
/// * `seed`: Random seed bytes (`DILITHIUM_RND_SZ` = 32 bytes).
///
/// # Returns
///
/// Returns either Ok(size) containing the number of bytes written to `sig`
/// on success or Err(e) containing the wolfSSL library error code value.
///
/// # Example
///
/// ```rust
/// #[cfg(all(dilithium, dilithium_make_key_from_seed, dilithium_sign_with_seed))]
/// {
/// use wolfssl_wolfcrypt::dilithium::Dilithium;
/// let key_seed = [0x42u8; 32];
/// let mut key = Dilithium::generate_from_seed(Dilithium::LEVEL_44, &key_seed)
/// .expect("Error with generate_from_seed()");
/// let message = b"Hello, ML-DSA!";
/// let sign_seed = [0x55u8; 32];
/// let mut sig = vec![0u8; key.sig_size().unwrap()];
/// key.sign_msg_with_seed(message, &mut sig, &sign_seed)
/// .expect("Error with sign_msg_with_seed()");
/// }
/// ```
#[cfg(dilithium_sign_with_seed)]
pub fn sign_msg_with_seed(
&mut self,
msg: &[u8],
sig: &mut [u8],
seed: &[u8],
) -> Result<usize, i32> {
if seed.len() != sys::DILITHIUM_RND_SZ as usize {
return Err(sys::wolfCrypt_ErrorCodes_BUFFER_E);
}
let msg_len = crate::buffer_len_to_u32(msg.len())?;
let mut sig_len = crate::buffer_len_to_u32(sig.len())?;
let rc = unsafe {
sys::wc_dilithium_sign_ctx_msg_with_seed(
core::ptr::null(), 0,
msg.as_ptr(), msg_len,
sig.as_mut_ptr(), &mut sig_len,
&mut self.ws_key,
seed.as_ptr(),
)
};
if rc != 0 {
return Err(rc);
}
Ok(sig_len as usize)
}
/// Sign a message with a context string using a fixed random seed.
///
/// # Parameters
///
/// * `ctx`: Context string (at most 255 bytes).
/// * `msg`: Message to sign.
/// * `sig`: Output buffer to hold the signature.
/// * `seed`: Random seed bytes (`DILITHIUM_RND_SZ` = 32 bytes).
///
/// # Returns
///
/// Returns either Ok(size) containing the number of bytes written to `sig`
/// on success or Err(e) containing the wolfSSL library error code value.
#[cfg(dilithium_sign_with_seed)]
pub fn sign_ctx_msg_with_seed(
&mut self,
ctx: &[u8],
msg: &[u8],
sig: &mut [u8],
seed: &[u8],
) -> Result<usize, i32> {
if ctx.len() > 255 {
return Err(sys::wolfCrypt_ErrorCodes_BUFFER_E);
}
if seed.len() != sys::DILITHIUM_RND_SZ as usize {
return Err(sys::wolfCrypt_ErrorCodes_BUFFER_E);
}
let ctx_len = ctx.len() as u8;
let msg_len = crate::buffer_len_to_u32(msg.len())?;
let mut sig_len = crate::buffer_len_to_u32(sig.len())?;
let rc = unsafe {
sys::wc_dilithium_sign_ctx_msg_with_seed(
ctx.as_ptr(), ctx_len,
msg.as_ptr(), msg_len,
sig.as_mut_ptr(), &mut sig_len,
&mut self.ws_key,
seed.as_ptr(),
)
};
if rc != 0 {
return Err(rc);
}
Ok(sig_len as usize)
}
/// Sign a pre-hashed message with a context string using a fixed random
/// seed.
///
/// # Parameters
///
/// * `ctx`: Context string (at most 255 bytes).
/// * `hash_alg`: Hash algorithm identifier (e.g. `WC_HASH_TYPE_SHA256`).
/// * `hash`: Hash digest of the message to sign.
/// * `sig`: Output buffer to hold the signature.
/// * `seed`: Random seed bytes (`DILITHIUM_RND_SZ` = 32 bytes).
///
/// # Returns
///
/// Returns either Ok(size) containing the number of bytes written to `sig`
/// on success or Err(e) containing the wolfSSL library error code value.
#[cfg(dilithium_sign_with_seed)]
pub fn sign_ctx_hash_with_seed(
&mut self,
ctx: &[u8],
hash_alg: i32,
hash: &[u8],
sig: &mut [u8],
seed: &[u8],
) -> Result<usize, i32> {
if ctx.len() > 255 {
return Err(sys::wolfCrypt_ErrorCodes_BUFFER_E);
}
if seed.len() != sys::DILITHIUM_RND_SZ as usize {
return Err(sys::wolfCrypt_ErrorCodes_BUFFER_E);
}
let ctx_len = ctx.len() as u8;
let hash_len = crate::buffer_len_to_u32(hash.len())?;
let mut sig_len = crate::buffer_len_to_u32(sig.len())?;
let rc = unsafe {
sys::wc_dilithium_sign_ctx_hash_with_seed(
ctx.as_ptr(), ctx_len,
hash_alg,
hash.as_ptr(), hash_len,
sig.as_mut_ptr(), &mut sig_len,
&mut self.ws_key,
seed.as_ptr(),
)
};
if rc != 0 {
return Err(rc);
}
Ok(sig_len as usize)
}
/// Verify a message signature.
///
/// # Parameters
///
/// * `sig`: Signature to verify.
/// * `msg`: Message the signature was created over.
///
/// # Returns
///
/// Returns either Ok(true) if the signature is valid, Ok(false) if it is
/// invalid, or Err(e) containing the wolfSSL library error code value.
///
/// # Example
///
/// ```rust
/// #[cfg(all(dilithium, dilithium_make_key, dilithium_sign, dilithium_verify, random))]
/// {
/// use wolfssl_wolfcrypt::random::RNG;
/// use wolfssl_wolfcrypt::dilithium::Dilithium;
/// let mut rng = RNG::new().expect("Error creating RNG");
/// let mut key = Dilithium::generate(Dilithium::LEVEL_44, &mut rng)
/// .expect("Error with generate()");
/// let message = b"Hello, ML-DSA!";
/// let mut sig = vec![0u8; key.sig_size().unwrap()];
/// let sig_len = key.sign_msg(message, &mut sig, &mut rng)
/// .expect("Error with sign_msg()");
/// let valid = key.verify_msg(&sig[..sig_len], message)
/// .expect("Error with verify_msg()");
/// assert!(valid);
/// }
/// ```
#[cfg(dilithium_verify)]
pub fn verify_msg(&mut self, sig: &[u8], msg: &[u8]) -> Result<bool, i32> {
let sig_len = crate::buffer_len_to_u32(sig.len())?;
let msg_len = crate::buffer_len_to_u32(msg.len())?;
let mut res = 0i32;
let rc = unsafe {
sys::wc_dilithium_verify_ctx_msg(
sig.as_ptr(), sig_len,
core::ptr::null(), 0,
msg.as_ptr(), msg_len,
&mut res,
&mut self.ws_key,
)
};
if rc != 0 {
return Err(rc);
}
Ok(res == 1)
}
/// Verify a message signature with a context string.
///
/// # Parameters
///
/// * `sig`: Signature to verify.
/// * `ctx`: Context string used when signing.
/// * `msg`: Message the signature was created over.
///
/// # Returns
///
/// Returns either Ok(true) if the signature is valid, Ok(false) if it is
/// invalid, or Err(e) containing the wolfSSL library error code value.
///
/// # Example
///
/// ```rust
/// #[cfg(all(dilithium, dilithium_make_key, dilithium_sign, dilithium_verify, random))]
/// {
/// use wolfssl_wolfcrypt::random::RNG;
/// use wolfssl_wolfcrypt::dilithium::Dilithium;
/// let mut rng = RNG::new().expect("Error creating RNG");
/// let mut key = Dilithium::generate(Dilithium::LEVEL_44, &mut rng)
/// .expect("Error with generate()");
/// let message = b"Hello, ML-DSA!";
/// let ctx = b"my context";
/// let mut sig = vec![0u8; key.sig_size().unwrap()];
/// let sig_len = key.sign_ctx_msg(ctx, message, &mut sig, &mut rng)
/// .expect("Error with sign_ctx_msg()");
/// let valid = key.verify_ctx_msg(&sig[..sig_len], ctx, message)
/// .expect("Error with verify_ctx_msg()");
/// assert!(valid);
/// }
/// ```
#[cfg(dilithium_verify)]
pub fn verify_ctx_msg(&mut self, sig: &[u8], ctx: &[u8], msg: &[u8]) -> Result<bool, i32> {
if ctx.len() > 255 {
return Err(sys::wolfCrypt_ErrorCodes_BUFFER_E);
}
let sig_len = crate::buffer_len_to_u32(sig.len())?;
let ctx_len = ctx.len() as u8;
let msg_len = crate::buffer_len_to_u32(msg.len())?;
let mut res = 0i32;
let rc = unsafe {
sys::wc_dilithium_verify_ctx_msg(
sig.as_ptr(), sig_len,
ctx.as_ptr(), ctx_len,
msg.as_ptr(), msg_len,
&mut res,
&mut self.ws_key,
)
};
if rc != 0 {
return Err(rc);
}
Ok(res == 1)
}
/// Verify a pre-hashed message signature with a context string.
///
/// This is the HashML-DSA variant: the message is supplied as a hash
/// digest along with the hash algorithm identifier.
///
/// # Parameters
///
/// * `sig`: Signature to verify.
/// * `ctx`: Context string used when signing.
/// * `hash_alg`: Hash algorithm identifier (e.g. `WC_HASH_TYPE_SHA256`).
/// * `hash`: Hash digest of the message to verify.
///
/// # Returns
///
/// Returns either Ok(true) if the signature is valid, Ok(false) if it is
/// invalid, or Err(e) containing the wolfSSL library error code value.
#[cfg(dilithium_verify)]
pub fn verify_ctx_hash(
&mut self,
sig: &[u8],
ctx: &[u8],
hash_alg: i32,
hash: &[u8],
) -> Result<bool, i32> {
if ctx.len() > 255 {
return Err(sys::wolfCrypt_ErrorCodes_BUFFER_E);
}
let sig_len = crate::buffer_len_to_u32(sig.len())?;
let ctx_len = ctx.len() as u8;
let hash_len = crate::buffer_len_to_u32(hash.len())?;
let mut res = 0i32;
let rc = unsafe {
sys::wc_dilithium_verify_ctx_hash(
sig.as_ptr(), sig_len,
ctx.as_ptr(), ctx_len,
hash_alg,
hash.as_ptr(), hash_len,
&mut res,
&mut self.ws_key,
)
};
if rc != 0 {
return Err(rc);
}
Ok(res == 1)
}
}
impl Dilithium {
fn zeroize(&mut self) {
unsafe { crate::zeroize_raw(&mut self.ws_key); }
}
}
impl Drop for Dilithium {
/// Safely free the underlying wolfSSL Dilithium key context.
///
/// This calls `wc_dilithium_free()`. The Rust Drop trait guarantees this
/// is called when the `Dilithium` struct goes out of scope.
fn drop(&mut self) {
unsafe { sys::wc_dilithium_free(&mut self.ws_key); }
self.zeroize();
}
}