From c7a7c8178b58932bd30085b33759bb57c6bcda15 Mon Sep 17 00:00:00 2001 From: Arthur Gautier Date: Fri, 14 Feb 2025 21:47:16 -0800 Subject: [PATCH 1/3] provide a `RandomMod::try_random_mod` method --- Cargo.lock | 3 +-- Cargo.toml | 4 ++++ src/limb/rand.rs | 11 +++++++---- src/traits.rs | 19 ++++++++++++++++++- src/uint/boxed/rand.rs | 11 ++++++++++- src/uint/rand.rs | 29 ++++++++++++++++++++--------- 6 files changed, 60 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3054ca0b..094401d7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -565,8 +565,7 @@ dependencies = [ [[package]] name = "rand_core" version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a88e0da7a2c97baa202165137c158d0a2e824ac465d13d81046727b34cb247d3" +source = "git+https://github.com/rust-random/rand.git#775b05be1b8a4fdef17c6601cd223551fbf67edc" dependencies = [ "getrandom 0.3.1", "zerocopy 0.8.14", diff --git a/Cargo.toml b/Cargo.toml index 6968add4..292b7f1d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -81,3 +81,7 @@ harness = false [[bench]] name = "int" harness = false + +[patch.crates-io] +# https://github.com/rust-random/rand/pull/1593 +rand_core = { git = "https://github.com/rust-random/rand.git" } diff --git a/src/limb/rand.rs b/src/limb/rand.rs index f606df01..2a5ba16c 100644 --- a/src/limb/rand.rs +++ b/src/limb/rand.rs @@ -2,7 +2,7 @@ use super::Limb; use crate::{Encoding, NonZero, Random, RandomMod}; -use rand_core::RngCore; +use rand_core::{RngCore, TryRngCore}; use subtle::ConstantTimeLess; impl Random for Limb { @@ -18,7 +18,10 @@ impl Random for Limb { } impl RandomMod for Limb { - fn random_mod(rng: &mut R, modulus: &NonZero) -> Self { + fn try_random_mod( + rng: &mut R, + modulus: &NonZero, + ) -> Result { let mut bytes = ::Repr::default(); let n_bits = modulus.bits() as usize; @@ -26,12 +29,12 @@ impl RandomMod for Limb { let mask = 0xffu8 >> (8 * n_bytes - n_bits); loop { - rng.fill_bytes(&mut bytes[..n_bytes]); + rng.try_fill_bytes(&mut bytes[..n_bytes])?; bytes[n_bytes - 1] &= mask; let n = Limb::from_le_bytes(bytes); if n.ct_lt(modulus).into() { - return n; + return Ok(n); } } } diff --git a/src/traits.rs b/src/traits.rs index e3dd4f39..86079823 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -419,7 +419,24 @@ pub trait RandomMod: Sized + Zero { /// example, it implements `CryptoRng`), then this is guaranteed not to /// leak anything about the output value aside from it being less than /// `modulus`. - fn random_mod(rng: &mut R, modulus: &NonZero) -> Self; + fn random_mod(rng: &mut R, modulus: &NonZero) -> Self { + let Ok(out) = Self::try_random_mod(rng, modulus); + out + } + + /// Generate a random number which is less than a given `modulus`. + /// + /// This uses rejection sampling. + /// + /// As a result, it runs in variable time that depends in part on + /// `modulus`. If the generator `rng` is cryptographically secure (for + /// example, it implements `CryptoRng`), then this is guaranteed not to + /// leak anything about the output value aside from it being less than + /// `modulus`. + fn try_random_mod( + rng: &mut R, + modulus: &NonZero, + ) -> Result; } /// Compute `self + rhs mod p`. diff --git a/src/uint/boxed/rand.rs b/src/uint/boxed/rand.rs index cf01d0ae..6243b78e 100644 --- a/src/uint/boxed/rand.rs +++ b/src/uint/boxed/rand.rs @@ -36,9 +36,18 @@ impl RandomBits for BoxedUint { impl RandomMod for BoxedUint { fn random_mod(rng: &mut R, modulus: &NonZero) -> Self { let mut n = BoxedUint::zero_with_precision(modulus.bits_precision()); - random_mod_core(rng, &mut n, modulus, modulus.bits()); + let Ok(()) = random_mod_core(rng, &mut n, modulus, modulus.bits()); n } + + fn try_random_mod( + rng: &mut R, + modulus: &NonZero, + ) -> Result { + let mut n = BoxedUint::zero_with_precision(modulus.bits_precision()); + random_mod_core(rng, &mut n, modulus, modulus.bits())?; + Ok(n) + } } #[cfg(test)] diff --git a/src/uint/rand.rs b/src/uint/rand.rs index 2e4e60b1..98d954ba 100644 --- a/src/uint/rand.rs +++ b/src/uint/rand.rs @@ -83,35 +83,45 @@ impl RandomBits for Uint { impl RandomMod for Uint { fn random_mod(rng: &mut R, modulus: &NonZero) -> Self { let mut n = Self::ZERO; - random_mod_core(rng, &mut n, modulus, modulus.bits_vartime()); + let Ok(()) = random_mod_core(rng, &mut n, modulus, modulus.bits_vartime()); n } + + fn try_random_mod( + rng: &mut R, + modulus: &NonZero, + ) -> Result { + let mut n = Self::ZERO; + random_mod_core(rng, &mut n, modulus, modulus.bits_vartime())?; + Ok(n) + } } /// Generic implementation of `random_mod` which can be shared with `BoxedUint`. // TODO(tarcieri): obtain `n_bits` via a trait like `Integer` -pub(super) fn random_mod_core( +pub(super) fn random_mod_core( rng: &mut R, n: &mut T, modulus: &NonZero, n_bits: u32, -) where +) -> Result<(), R::Error> +where T: AsMut<[Limb]> + AsRef<[Limb]> + ConstantTimeLess + Zero, { #[cfg(target_pointer_width = "64")] - let mut next_word = || rng.next_u64(); + let mut next_word = || rng.try_next_u64(); #[cfg(target_pointer_width = "32")] - let mut next_word = || rng.next_u32(); + let mut next_word = || rng.try_next_u32(); let n_limbs = n_bits.div_ceil(Limb::BITS) as usize; let hi_word_modulus = modulus.as_ref().as_ref()[n_limbs - 1].0; let mask = !0 >> hi_word_modulus.leading_zeros(); - let mut hi_word = next_word() & mask; + let mut hi_word = next_word()? & mask; loop { while hi_word > hi_word_modulus { - hi_word = next_word() & mask; + hi_word = next_word()? & mask; } // Set high limb n.as_mut()[n_limbs - 1] = Limb::from_le_bytes(hi_word.to_le_bytes()); @@ -120,15 +130,16 @@ pub(super) fn random_mod_core( // Need to deserialize from little-endian to make sure that two 32-bit limbs // deserialized sequentially are equal to one 64-bit limb produced from the same // byte stream. - n.as_mut()[i] = Limb::from_le_bytes(next_word().to_le_bytes()); + n.as_mut()[i] = Limb::from_le_bytes(next_word()?.to_le_bytes()); } // If the high limb is equal to the modulus' high limb, it's still possible // that the full uint is too big so we check and repeat if it is. if n.ct_lt(modulus).into() { break; } - hi_word = next_word() & mask; + hi_word = next_word()? & mask; } + Ok(()) } #[cfg(test)] From 8c1c6c75d4224becb1bb11ecd1557d4c82fbcdfb Mon Sep 17 00:00:00 2001 From: Arthur Gautier Date: Wed, 19 Feb 2025 14:11:00 -0800 Subject: [PATCH 2/3] provide a `Random::try_random` method --- src/int/rand.rs | 6 +++--- src/limb/rand.rs | 15 +++++++-------- src/modular/const_monty_form.rs | 9 ++++++--- src/non_zero.rs | 8 ++++---- src/odd.rs | 10 +++++----- src/traits.rs | 10 +++++++++- src/uint/rand.rs | 6 +++--- src/wrapping.rs | 6 +++--- 8 files changed, 40 insertions(+), 30 deletions(-) diff --git a/src/int/rand.rs b/src/int/rand.rs index 94f02e61..07a194b7 100644 --- a/src/int/rand.rs +++ b/src/int/rand.rs @@ -1,6 +1,6 @@ //! Random number generator support -use rand_core::{RngCore, TryRngCore}; +use rand_core::TryRngCore; use crate::{Int, Random, RandomBits, RandomBitsError}; @@ -8,8 +8,8 @@ use super::Uint; impl Random for Int { /// Generate a cryptographically secure random [`Int`]. - fn random(rng: &mut R) -> Self { - Self(Uint::random(rng)) + fn try_random(rng: &mut R) -> Result { + Ok(Self(Uint::try_random(rng)?)) } } diff --git a/src/limb/rand.rs b/src/limb/rand.rs index 2a5ba16c..22fd4218 100644 --- a/src/limb/rand.rs +++ b/src/limb/rand.rs @@ -2,18 +2,17 @@ use super::Limb; use crate::{Encoding, NonZero, Random, RandomMod}; -use rand_core::{RngCore, TryRngCore}; +use rand_core::TryRngCore; use subtle::ConstantTimeLess; impl Random for Limb { - #[cfg(target_pointer_width = "32")] - fn random(rng: &mut R) -> Self { - Self(rng.next_u32()) - } + fn try_random(rng: &mut R) -> Result { + #[cfg(target_pointer_width = "32")] + let val = rng.try_next_u32()?; + #[cfg(target_pointer_width = "64")] + let val = rng.try_next_u64()?; - #[cfg(target_pointer_width = "64")] - fn random(rng: &mut R) -> Self { - Self(rng.next_u64()) + Ok(Self(val)) } } diff --git a/src/modular/const_monty_form.rs b/src/modular/const_monty_form.rs index 8ff8dd80..cfd2511c 100644 --- a/src/modular/const_monty_form.rs +++ b/src/modular/const_monty_form.rs @@ -15,7 +15,7 @@ use core::{fmt::Debug, marker::PhantomData}; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; #[cfg(feature = "rand_core")] -use crate::{Random, RandomMod, rand_core::RngCore}; +use crate::{Random, RandomMod, rand_core::TryRngCore}; #[cfg(feature = "serde")] use { @@ -204,8 +204,11 @@ where MOD: ConstMontyParams, { #[inline] - fn random(rng: &mut R) -> Self { - Self::new(&Uint::random_mod(rng, MOD::MODULUS.as_nz_ref())) + fn try_random(rng: &mut R) -> Result { + Ok(Self::new(&Uint::try_random_mod( + rng, + MOD::MODULUS.as_nz_ref(), + )?)) } } diff --git a/src/non_zero.rs b/src/non_zero.rs index 741c66e2..e76b5b97 100644 --- a/src/non_zero.rs +++ b/src/non_zero.rs @@ -12,7 +12,7 @@ use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; use crate::{ArrayEncoding, ByteArray}; #[cfg(feature = "rand_core")] -use {crate::Random, rand_core::RngCore}; +use {crate::Random, rand_core::TryRngCore}; #[cfg(feature = "serde")] use serdect::serde::{ @@ -246,10 +246,10 @@ where /// As a result, it runs in variable time. If the generator `rng` is /// cryptographically secure (for example, it implements `CryptoRng`), /// then this is guaranteed not to leak anything about the output value. - fn random(mut rng: &mut R) -> Self { + fn try_random(rng: &mut R) -> Result { loop { - if let Some(result) = Self::new(T::random(&mut rng)).into() { - break result; + if let Some(result) = Self::new(T::try_random(rng)?).into() { + break Ok(result); } } } diff --git a/src/odd.rs b/src/odd.rs index 4f076211..b442a632 100644 --- a/src/odd.rs +++ b/src/odd.rs @@ -8,10 +8,10 @@ use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; use crate::BoxedUint; #[cfg(feature = "rand_core")] -use {crate::Random, rand_core::RngCore}; +use crate::{Random, rand_core::TryRngCore}; #[cfg(all(feature = "alloc", feature = "rand_core"))] -use {crate::RandomBits, rand_core::TryRngCore}; +use crate::RandomBits; #[cfg(feature = "serde")] use crate::Zero; @@ -153,10 +153,10 @@ impl PartialOrd> for BoxedUint { #[cfg(feature = "rand_core")] impl Random for Odd> { /// Generate a random `Odd>`. - fn random(rng: &mut R) -> Self { - let mut ret = Uint::random(rng); + fn try_random(rng: &mut R) -> Result { + let mut ret = Uint::try_random(rng)?; ret.limbs[0] |= Limb::ONE; - Odd(ret) + Ok(Odd(ret)) } } diff --git a/src/traits.rs b/src/traits.rs index 86079823..962b6f52 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -299,7 +299,15 @@ pub trait Random: Sized { /// Generate a random value. /// /// If `rng` is a CSRNG, the generation is cryptographically secure as well. - fn random(rng: &mut R) -> Self; + fn random(rng: &mut R) -> Self { + let Ok(out) = Self::try_random(rng); + out + } + + /// Generate a random value. + /// + /// If `rng` is a CSRNG, the generation is cryptographically secure as well. + fn try_random(rng: &mut R) -> Result; } /// Possible errors of the methods in [`RandomBits`] trait. diff --git a/src/uint/rand.rs b/src/uint/rand.rs index 98d954ba..b37021c1 100644 --- a/src/uint/rand.rs +++ b/src/uint/rand.rs @@ -6,14 +6,14 @@ use rand_core::{RngCore, TryRngCore}; use subtle::ConstantTimeLess; impl Random for Uint { - fn random(mut rng: &mut R) -> Self { + fn try_random(rng: &mut R) -> Result { let mut limbs = [Limb::ZERO; LIMBS]; for limb in &mut limbs { - *limb = Limb::random(&mut rng) + *limb = Limb::try_random(rng)? } - limbs.into() + Ok(limbs.into()) } } diff --git a/src/wrapping.rs b/src/wrapping.rs index 414eaa22..3b2b969d 100644 --- a/src/wrapping.rs +++ b/src/wrapping.rs @@ -8,7 +8,7 @@ use core::{ use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; #[cfg(feature = "rand_core")] -use {crate::Random, rand_core::RngCore}; +use {crate::Random, rand_core::TryRngCore}; #[cfg(feature = "serde")] use serdect::serde::{Deserialize, Deserializer, Serialize, Serializer}; @@ -259,8 +259,8 @@ impl fmt::UpperHex for Wrapping { #[cfg(feature = "rand_core")] impl Random for Wrapping { - fn random(rng: &mut R) -> Self { - Wrapping(Random::random(rng)) + fn try_random(rng: &mut R) -> Result { + Ok(Wrapping(Random::try_random(rng)?)) } } From 8167e9cfd2994e78862de6161864510bfbea33a8 Mon Sep 17 00:00:00 2001 From: Artyom Pavlov Date: Sat, 22 Feb 2025 19:46:36 +0300 Subject: [PATCH 3/3] Remove `rand_core` patch --- Cargo.toml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 292b7f1d..d386180e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ subtle = { version = "2.6", default-features = false } der = { version = "0.8.0-rc.1", optional = true, default-features = false } hybrid-array = { version = "0.2", optional = true } num-traits = { version = "0.2.19", default-features = false } -rand_core = { version = "0.9", optional = true, default-features = false } +rand_core = { version = "0.9.2", optional = true, default-features = false } rlp = { version = "0.6", optional = true, default-features = false } serdect = { version = "0.3", optional = true, default-features = false } zeroize = { version = "1", optional = true, default-features = false } @@ -81,7 +81,3 @@ harness = false [[bench]] name = "int" harness = false - -[patch.crates-io] -# https://github.com/rust-random/rand/pull/1593 -rand_core = { git = "https://github.com/rust-random/rand.git" }