From ecced8df0c96458752adf646cf62d78bde3137b3 Mon Sep 17 00:00:00 2001 From: Nikola Pajkovsky Date: Fri, 26 Jun 2026 10:46:31 +0200 Subject: [PATCH 1/4] Remove DEFAULT_HASH_NAME constants and eliminate unwrap() in HashFactory defaults MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the three pub DEFAULT_*_HASH_NAME constants with direct enum variant construction in Default, default_128_bit(), and default_256_bit(). The constants were only used internally to drive Self::new().unwrap() calls — removing them makes the defaults infallible at compile time and reduces the public surface area of the factory. Signed-off-by: Nikola Pajkovsky --- crypto/factory/src/hash_factory.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/crypto/factory/src/hash_factory.rs b/crypto/factory/src/hash_factory.rs index 9d12117..052f4b5 100644 --- a/crypto/factory/src/hash_factory.rs +++ b/crypto/factory/src/hash_factory.rs @@ -35,11 +35,6 @@ use bouncycastle_sha2::{SHA224_NAME, SHA256_NAME, SHA384_NAME, SHA512_NAME}; use bouncycastle_sha3 as sha3; use bouncycastle_sha3::{SHA3_224_NAME, SHA3_256_NAME, SHA3_384_NAME, SHA3_512_NAME}; -/*** Defaults ***/ -pub const DEFAULT_HASH_NAME: &str = SHA3_256_NAME; -pub const DEFAULT_128BIT_HASH_NAME: &str = SHA3_256_NAME; -pub const DEFAULT_256BIT_HASH_NAME: &str = SHA3_512_NAME; - /// All members must impl Hash. /// Note: no SHAKE because SHAKE is not NIST approved as a hash function. See FIPS 202 section A.2. pub enum HashFactory { @@ -55,16 +50,16 @@ pub enum HashFactory { impl Default for HashFactory { fn default() -> HashFactory { - Self::new(DEFAULT_HASH_NAME).unwrap() + Self::SHA3_256(sha3::SHA3_256::new()) } } impl AlgorithmFactory for HashFactory { fn default_128_bit() -> HashFactory { - Self::new(DEFAULT_128BIT_HASH_NAME).unwrap() + Self::SHA3_256(sha3::SHA3_256::new()) } fn default_256_bit() -> HashFactory { - Self::new(DEFAULT_256BIT_HASH_NAME).unwrap() + Self::SHA3_512(sha3::SHA3_512::new()) } fn new(alg_name: &str) -> Result { From 909161760c1014a5b6fe7a8b274de4dc037843be Mon Sep 17 00:00:00 2001 From: Nikola Pajkovsky Date: Fri, 26 Jun 2026 10:50:24 +0200 Subject: [PATCH 2/4] Remove DEFAULT_KDF_NAME constants and eliminate unwrap() in KDFFactory defaults MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Same pattern as the HashFactory cleanup: replace the three pub DEFAULT_*_KDF_NAME constants with direct enum variant construction in Default, default_128_bit(), and default_256_bit(). The constants were only used internally to drive unwrap() calls on Self::new() — removing them makes the defaults infallible at compile time and shrinks the public API surface. Signed-off-by: Nikola Pajkovsky --- crypto/factory/src/kdf_factory.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/crypto/factory/src/kdf_factory.rs b/crypto/factory/src/kdf_factory.rs index f3deffb..50aa274 100644 --- a/crypto/factory/src/kdf_factory.rs +++ b/crypto/factory/src/kdf_factory.rs @@ -58,11 +58,6 @@ use bouncycastle_sha3::{ SHA3_224_NAME, SHA3_256_NAME, SHA3_384_NAME, SHA3_512_NAME, SHAKE128_NAME, SHAKE256_NAME, }; -/*** Defaults ***/ -pub const DEFAULT_KDF_NAME: &str = HKDF_SHA512_NAME; -pub const DEFAULT_128BIT_KDF_NAME: &str = HKDF_SHA256_NAME; -pub const DEFAULT_256BIT_KDF_NAME: &str = HKDF_SHA512_NAME; - // All members must impl KDF. pub enum KDFFactory { #[allow(non_camel_case_types)] @@ -79,17 +74,17 @@ pub enum KDFFactory { impl Default for KDFFactory { fn default() -> Self { - KDFFactory::new(DEFAULT_KDF_NAME).unwrap() + Self::HKDF_SHA512(hkdf::HKDF_SHA512::new()) } } impl AlgorithmFactory for KDFFactory { fn default_128_bit() -> Self { - KDFFactory::new(DEFAULT_128BIT_KDF_NAME).unwrap() + Self::HKDF_SHA256(hkdf::HKDF_SHA256::new()) } fn default_256_bit() -> Self { - KDFFactory::new(DEFAULT_256BIT_KDF_NAME).unwrap() + Self::HKDF_SHA512(hkdf::HKDF_SHA512::new()) } fn new(alg_name: &str) -> Result { From bda139f586e1620ec0396c6f4659ece724071bff Mon Sep 17 00:00:00 2001 From: Nikola Pajkovsky Date: Fri, 26 Jun 2026 10:55:38 +0200 Subject: [PATCH 3/4] Remove DEFAULT_KDF_NAME constants and eliminate unwrap() in KDFFactory defaults MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace pub DEFAULT_KDF_NAME, DEFAULT_128BIT_KDF_NAME, and DEFAULT_256BIT_KDF_NAME with direct enum variant construction in Default, default_128_bit(), and default_256_bit(). The constants existed solely to feed Self::new().unwrap() — constructing the variants directly makes the defaults infallible at compile time and removes three unnecessary items from the public API. Signed-off-by: Nikola Pajkovsky --- crypto/factory/src/rng_factory.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/crypto/factory/src/rng_factory.rs b/crypto/factory/src/rng_factory.rs index 9f1b8e0..1431c90 100644 --- a/crypto/factory/src/rng_factory.rs +++ b/crypto/factory/src/rng_factory.rs @@ -50,11 +50,6 @@ use bouncycastle_core::traits::{RNG, SecurityStrength}; use bouncycastle_rng as rng; use bouncycastle_rng::{HASH_DRBG_SHA256_NAME, HASH_DRBG_SHA512_NAME}; -/*** Defaults ***/ -pub const DEFAULT_DRBG_NAME: &str = HASH_DRBG_SHA512_NAME; -pub const DEFAULT_128BIT_DRBG_NAME: &str = HASH_DRBG_SHA256_NAME; -pub const DEFAULT_256BIT_DRBG_NAME: &str = HASH_DRBG_SHA512_NAME; - /// All members must impl RNG. pub enum RNGFactory { #[allow(non_camel_case_types)] @@ -65,16 +60,16 @@ pub enum RNGFactory { impl Default for RNGFactory { fn default() -> Self { - Self::new(DEFAULT_DRBG_NAME).unwrap() + Self::HashDRBG_SHA512(rng::HashDRBG_SHA512::new_from_os()) } } impl AlgorithmFactory for RNGFactory { fn default_128_bit() -> Self { - Self::new(DEFAULT_128BIT_DRBG_NAME).unwrap() + Self::HashDRBG_SHA256(rng::HashDRBG_SHA256::new_from_os()) } fn default_256_bit() -> Self { - Self::new(DEFAULT_256BIT_DRBG_NAME).unwrap() + Self::HashDRBG_SHA512(rng::HashDRBG_SHA512::new_from_os()) } fn new(alg_name: &str) -> Result { From 09b9f5319f5da94345976936905adc72a960cd8c Mon Sep 17 00:00:00 2001 From: Nikola Pajkovsky Date: Fri, 26 Jun 2026 11:54:50 +0200 Subject: [PATCH 4/4] Eliminate unwrap() and assert!() in ML-DSA signature verification Replace manual length checks followed by unwrap() with try_into().map_err() to coerce the signature slice to a fixed-size array in one step. Replace assert!() + unwrap() on the streaming verifier's optional public key with ok_or(), returning a proper SignatureError::GenericError instead of panicking. Use .then_some().ok_or() to convert the boolean verify result into a Result without an if/else branch. Signed-off-by: Nikola Pajkovsky --- crypto/mldsa/src/mldsa.rs | 54 ++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 32 deletions(-) diff --git a/crypto/mldsa/src/mldsa.rs b/crypto/mldsa/src/mldsa.rs index ca41c81..680c9ae 100644 --- a/crypto/mldsa/src/mldsa.rs +++ b/crypto/mldsa/src/mldsa.rs @@ -1536,15 +1536,12 @@ impl< sig: &[u8], ) -> Result<(), SignatureError> { let mu = MuBuilder::compute_mu(&pk.compute_tr(), msg, ctx)?; - - if sig.len() != SIG_LEN { - return Err(SignatureError::LengthError("Signature value is not the correct length.")); - } - if Self::verify_mu_internal(&pk.pk, &pk.A_hat(), &mu, &sig[..SIG_LEN].try_into().unwrap()) { - Ok(()) - } else { - Err(SignatureError::SignatureVerificationFailed) - } + let sig: &[u8; SIG_LEN] = sig.try_into().map_err(|_| { + SignatureError::LengthError("Signature value is not the correct length.") + })?; + Self::verify_mu_internal(&pk.pk, &pk.A_hat(), &mu, sig) + .then_some(()) + .ok_or(SignatureError::SignatureVerificationFailed) } /// Algorithm 8 ML-DSA.Verify_internal(𝑝𝑘, 𝑀′, 𝜎) @@ -2062,15 +2059,12 @@ impl< { fn verify(pk: &PK, msg: &[u8], ctx: Option<&[u8]>, sig: &[u8]) -> Result<(), SignatureError> { let mu = MuBuilder::compute_mu(&pk.compute_tr(), msg, ctx)?; - - if sig.len() != SIG_LEN { - return Err(SignatureError::LengthError("Signature value is not the correct length.")); - } - if Self::verify_mu_internal(pk, &pk.A_hat(), &mu, &sig.try_into().unwrap()) { - Ok(()) - } else { - Err(SignatureError::SignatureVerificationFailed) - } + let sig: &[u8; SIG_LEN] = sig.try_into().map_err(|_| { + SignatureError::LengthError("Signature value is not the correct length.") + })?; + Self::verify_mu_internal(pk, &pk.A_hat(), &mu, sig) + .then_some(()) + .ok_or(SignatureError::SignatureVerificationFailed) } fn verify_init(pk: &PK, ctx: Option<&[u8]>) -> Result { @@ -2091,20 +2085,16 @@ impl< fn verify_final(self, sig: &[u8]) -> Result<(), SignatureError> { let mu = self.mu_builder.do_final(); - assert!( - self.pk.is_some(), - "Somehow you managed to construct a streaming verifier without a public key, impressive!" - ); - let pk: &PK = &self.pk.unwrap(); - - if sig.len() != SIG_LEN { - return Err(SignatureError::LengthError("Signature value is not the correct length.")); - } - if Self::verify_mu_internal(pk, &pk.A_hat(), &mu, &sig[..SIG_LEN].try_into().unwrap()) { - Ok(()) - } else { - Err(SignatureError::SignatureVerificationFailed) - } + let pk: &PK = self + .pk + .as_ref() + .ok_or(SignatureError::GenericError("No public key set on streaming verifier."))?; + let sig: &[u8; SIG_LEN] = sig.try_into().map_err(|_| { + SignatureError::LengthError("Signature value is not the correct length.") + })?; + Self::verify_mu_internal(pk, &pk.A_hat(), &mu, sig) + .then_some(()) + .ok_or(SignatureError::SignatureVerificationFailed) } }