Skip to content

Commit 62d6cf1

Browse files
committed
provide a try_random_mod method
1 parent 534c294 commit 62d6cf1

File tree

6 files changed

+77
-17
lines changed

6 files changed

+77
-17
lines changed

Cargo.lock

Lines changed: 4 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ subtle = { version = "2.6", default-features = false }
2323
der = { version = "0.8.0-rc.1", optional = true, default-features = false }
2424
hybrid-array = { version = "0.2", optional = true }
2525
num-traits = { version = "0.2.19", default-features = false }
26-
rand_core = { version = "0.9", optional = true, default-features = false }
26+
rand_core = { version = "0.9.1", optional = true, default-features = false }
2727
rlp = { version = "0.6", optional = true, default-features = false }
2828
serdect = { version = "0.3", optional = true, default-features = false }
2929
zeroize = { version = "1", optional = true, default-features = false }
@@ -81,3 +81,7 @@ harness = false
8181
[[bench]]
8282
name = "int"
8383
harness = false
84+
85+
[patch.crates-io]
86+
# https://github.com/rust-random/rand/pull/1593
87+
rand_core = { git = "https://github.com/fjarri/rand.git", branch = "sized" }

src/limb/rand.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use super::Limb;
44
use crate::{Encoding, NonZero, Random, RandomMod};
5-
use rand_core::RngCore;
5+
use rand_core::{RngCore, TryRngCore};
66
use subtle::ConstantTimeLess;
77

88
impl Random for Limb {
@@ -35,4 +35,25 @@ impl RandomMod for Limb {
3535
}
3636
}
3737
}
38+
39+
fn try_random_mod<R: TryRngCore + ?Sized>(
40+
rng: &mut R,
41+
modulus: &NonZero<Self>,
42+
) -> Result<Self, R::Error> {
43+
let mut bytes = <Self as Encoding>::Repr::default();
44+
45+
let n_bits = modulus.bits() as usize;
46+
let n_bytes = (n_bits + 7) / 8;
47+
let mask = 0xffu8 >> (8 * n_bytes - n_bits);
48+
49+
loop {
50+
rng.try_fill_bytes(&mut bytes[..n_bytes])?;
51+
bytes[n_bytes - 1] &= mask;
52+
53+
let n = Limb::from_le_bytes(bytes);
54+
if n.ct_lt(modulus).into() {
55+
return Ok(n);
56+
}
57+
}
58+
}
3859
}

src/traits.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,20 @@ pub trait RandomMod: Sized + Zero {
420420
/// leak anything about the output value aside from it being less than
421421
/// `modulus`.
422422
fn random_mod<R: RngCore + ?Sized>(rng: &mut R, modulus: &NonZero<Self>) -> Self;
423+
424+
/// Generate a random number which is less than a given `modulus`.
425+
///
426+
/// This uses rejection sampling.
427+
///
428+
/// As a result, it runs in variable time that depends in part on
429+
/// `modulus`. If the generator `rng` is cryptographically secure (for
430+
/// example, it implements `CryptoRng`), then this is guaranteed not to
431+
/// leak anything about the output value aside from it being less than
432+
/// `modulus`.
433+
fn try_random_mod<R: TryRngCore + ?Sized>(
434+
rng: &mut R,
435+
modulus: &NonZero<Self>,
436+
) -> Result<Self, R::Error>;
423437
}
424438

425439
/// Compute `self + rhs mod p`.

src/uint/boxed/rand.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,19 @@ impl RandomBits for BoxedUint {
3636
impl RandomMod for BoxedUint {
3737
fn random_mod<R: RngCore + ?Sized>(rng: &mut R, modulus: &NonZero<Self>) -> Self {
3838
let mut n = BoxedUint::zero_with_precision(modulus.bits_precision());
39-
random_mod_core(rng, &mut n, modulus, modulus.bits());
39+
random_mod_core(rng, &mut n, modulus, modulus.bits())
40+
.expect("RngCore used as TryRngCore failed");
4041
n
4142
}
43+
44+
fn try_random_mod<R: TryRngCore + ?Sized>(
45+
rng: &mut R,
46+
modulus: &NonZero<Self>,
47+
) -> Result<Self, R::Error> {
48+
let mut n = BoxedUint::zero_with_precision(modulus.bits_precision());
49+
random_mod_core(rng, &mut n, modulus, modulus.bits())?;
50+
Ok(n)
51+
}
4252
}
4353

4454
#[cfg(test)]

src/uint/rand.rs

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -83,35 +83,46 @@ impl<const LIMBS: usize> RandomBits for Uint<LIMBS> {
8383
impl<const LIMBS: usize> RandomMod for Uint<LIMBS> {
8484
fn random_mod<R: RngCore + ?Sized>(rng: &mut R, modulus: &NonZero<Self>) -> Self {
8585
let mut n = Self::ZERO;
86-
random_mod_core(rng, &mut n, modulus, modulus.bits_vartime());
86+
random_mod_core(rng, &mut n, modulus, modulus.bits_vartime())
87+
.expect("RngCore used as TryRngCore failed");
8788
n
8889
}
90+
91+
fn try_random_mod<R: TryRngCore + ?Sized>(
92+
rng: &mut R,
93+
modulus: &NonZero<Self>,
94+
) -> Result<Self, R::Error> {
95+
let mut n = Self::ZERO;
96+
random_mod_core(rng, &mut n, modulus, modulus.bits_vartime())?;
97+
Ok(n)
98+
}
8999
}
90100

91101
/// Generic implementation of `random_mod` which can be shared with `BoxedUint`.
92102
// TODO(tarcieri): obtain `n_bits` via a trait like `Integer`
93-
pub(super) fn random_mod_core<T, R: RngCore + ?Sized>(
103+
pub(super) fn random_mod_core<T, R: TryRngCore + ?Sized>(
94104
rng: &mut R,
95105
n: &mut T,
96106
modulus: &NonZero<T>,
97107
n_bits: u32,
98-
) where
108+
) -> Result<(), R::Error>
109+
where
99110
T: AsMut<[Limb]> + AsRef<[Limb]> + ConstantTimeLess + Zero,
100111
{
101112
#[cfg(target_pointer_width = "64")]
102-
let mut next_word = || rng.next_u64();
113+
let mut next_word = || rng.try_next_u64();
103114
#[cfg(target_pointer_width = "32")]
104-
let mut next_word = || rng.next_u32();
115+
let mut next_word = || rng.try_next_u32();
105116

106117
let n_limbs = n_bits.div_ceil(Limb::BITS) as usize;
107118

108119
let hi_word_modulus = modulus.as_ref().as_ref()[n_limbs - 1].0;
109120
let mask = !0 >> hi_word_modulus.leading_zeros();
110-
let mut hi_word = next_word() & mask;
121+
let mut hi_word = next_word()? & mask;
111122

112123
loop {
113124
while hi_word > hi_word_modulus {
114-
hi_word = next_word() & mask;
125+
hi_word = next_word()? & mask;
115126
}
116127
// Set high limb
117128
n.as_mut()[n_limbs - 1] = Limb::from_le_bytes(hi_word.to_le_bytes());
@@ -120,15 +131,16 @@ pub(super) fn random_mod_core<T, R: RngCore + ?Sized>(
120131
// Need to deserialize from little-endian to make sure that two 32-bit limbs
121132
// deserialized sequentially are equal to one 64-bit limb produced from the same
122133
// byte stream.
123-
n.as_mut()[i] = Limb::from_le_bytes(next_word().to_le_bytes());
134+
n.as_mut()[i] = Limb::from_le_bytes(next_word()?.to_le_bytes());
124135
}
125136
// If the high limb is equal to the modulus' high limb, it's still possible
126137
// that the full uint is too big so we check and repeat if it is.
127138
if n.ct_lt(modulus).into() {
128139
break;
129140
}
130-
hi_word = next_word() & mask;
141+
hi_word = next_word()? & mask;
131142
}
143+
Ok(())
132144
}
133145

134146
#[cfg(test)]

0 commit comments

Comments
 (0)