From ef32e28146dc904fc33e7b773402aa129310df49 Mon Sep 17 00:00:00 2001 From: Bogdan Opanchuk Date: Wed, 8 Nov 2023 16:02:46 -0800 Subject: [PATCH] Make `Uint::random_mod()` work identically on 32- and 64-bit targets (#285) --- src/uint/rand.rs | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/uint/rand.rs b/src/uint/rand.rs index c5f730b8..80af3bb1 100644 --- a/src/uint/rand.rs +++ b/src/uint/rand.rs @@ -1,7 +1,7 @@ //! Random number generator support use super::Uint; -use crate::{Limb, NonZero, Random, RandomMod}; +use crate::{Encoding, Limb, NonZero, Random, RandomMod}; use rand_core::CryptoRngCore; use subtle::ConstantTimeLess; @@ -30,18 +30,29 @@ impl RandomMod for Uint { /// issue so long as the underlying random number generator is truly a /// CSRNG, where previous outputs are unrelated to subsequent /// outputs and do not reveal information about the RNG's internal state. - fn random_mod(mut rng: &mut impl CryptoRngCore, modulus: &NonZero) -> Self { + fn random_mod(rng: &mut impl CryptoRngCore, modulus: &NonZero) -> Self { let mut n = Self::ZERO; let n_bits = modulus.as_ref().bits_vartime(); + let n_bytes = (n_bits + 7) / 8; let n_limbs = (n_bits + Limb::BITS - 1) / Limb::BITS; - let mask = Limb::MAX >> (Limb::BITS * n_limbs - n_bits); + let hi_bytes = n_bytes - (n_limbs - 1) * Limb::BYTES; + + let mut bytes = Limb::ZERO.to_le_bytes(); loop { - for i in 0..n_limbs { - n.limbs[i] = Limb::random(&mut rng); + for i in 0..n_limbs - 1 { + rng.fill_bytes(bytes.as_mut()); + // 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.limbs[i] = Limb::from_le_bytes(bytes); } - n.limbs[n_limbs - 1] = n.limbs[n_limbs - 1] & mask; + + // Generate the high limb which may need to only be filled partially. + bytes.as_mut().fill(0); + rng.fill_bytes(&mut (bytes.as_mut()[0..hi_bytes])); + n.limbs[n_limbs - 1] = Limb::from_le_bytes(bytes); if n.ct_lt(modulus).into() { return n;