/*
 * 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();
    }
}
