Skip to content

Commit bcef014

Browse files
authored
Revert "Write random_mod in terms of new random_bits" (#1060)
This reverts commit ba4f0e0 (#1026) For whatever reason this breaks the `dsa` test suite. I'm in the middle of a major refactoring in `crypto-bigint` and this took quite a bit of bisecting to figure out on top of all of that, so I don't have time to investigate why. cc @mrdomino
1 parent 34a24b7 commit bcef014

File tree

5 files changed

+90
-100
lines changed

5 files changed

+90
-100
lines changed

src/traits.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -639,6 +639,7 @@ pub trait Encoding: Sized {
639639
/// Byte array representation.
640640
type Repr: AsRef<[u8]>
641641
+ AsMut<[u8]>
642+
+ Copy
642643
+ Clone
643644
+ Sized
644645
+ for<'a> TryFrom<&'a [u8], Error: core::error::Error>;

src/uint/boxed/encoding.rs

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Const-friendly decoding operations for [`BoxedUint`].
22
33
use super::BoxedUint;
4-
use crate::{CtEq, CtOption, DecodeError, Encoding, Limb, Word, uint::encoding};
4+
use crate::{CtEq, CtOption, DecodeError, Limb, Word, uint::encoding};
55
use alloc::{boxed::Box, string::String, vec::Vec};
66

77
#[cfg(feature = "serde")]
@@ -245,28 +245,6 @@ impl BoxedUint {
245245
}
246246
}
247247

248-
impl Encoding for BoxedUint {
249-
type Repr = Box<[u8]>;
250-
251-
fn to_be_bytes(&self) -> Self::Repr {
252-
BoxedUint::to_be_bytes(self)
253-
}
254-
255-
fn to_le_bytes(&self) -> Self::Repr {
256-
BoxedUint::to_le_bytes(self)
257-
}
258-
259-
fn from_be_bytes(bytes: Self::Repr) -> Self {
260-
BoxedUint::from_be_slice(&bytes, (bytes.len() * 8).try_into().expect("overflow"))
261-
.expect("decode error")
262-
}
263-
264-
fn from_le_bytes(bytes: Self::Repr) -> Self {
265-
BoxedUint::from_le_slice(&bytes, (bytes.len() * 8).try_into().expect("overflow"))
266-
.expect("decode error")
267-
}
268-
}
269-
270248
/// Decoder target producing a Vec<Limb>
271249
#[derive(Default)]
272250
struct VecDecodeByLimb {

src/uint/boxed/rand.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use super::BoxedUint;
44
use crate::{
55
NonZero, RandomBits, RandomBitsError, RandomMod,
6-
uint::rand::{random_bits_core, random_mod_vartime_core},
6+
uint::rand::{random_bits_core, random_mod_core},
77
};
88
use rand_core::{RngCore, TryRngCore};
99

@@ -28,15 +28,15 @@ impl RandomBits for BoxedUint {
2828
}
2929

3030
let mut ret = BoxedUint::zero_with_precision(bits_precision);
31-
random_bits_core(rng, &mut ret, bit_length).map_err(RandomBitsError::RandCore)?;
31+
random_bits_core(rng, &mut ret.limbs, bit_length)?;
3232
Ok(ret)
3333
}
3434
}
3535

3636
impl RandomMod for BoxedUint {
3737
fn random_mod_vartime<R: RngCore + ?Sized>(rng: &mut R, modulus: &NonZero<Self>) -> Self {
3838
let mut n = BoxedUint::zero_with_precision(modulus.bits_precision());
39-
let Ok(()) = random_mod_vartime_core(rng, &mut n, modulus, modulus.bits());
39+
let Ok(()) = random_mod_core(rng, &mut n, modulus, modulus.bits());
4040
n
4141
}
4242

@@ -45,7 +45,7 @@ impl RandomMod for BoxedUint {
4545
modulus: &NonZero<Self>,
4646
) -> Result<Self, R::Error> {
4747
let mut n = BoxedUint::zero_with_precision(modulus.bits_precision());
48-
random_mod_vartime_core(rng, &mut n, modulus, modulus.bits())?;
48+
random_mod_core(rng, &mut n, modulus, modulus.bits())?;
4949
Ok(n)
5050
}
5151
}

src/uint/rand.rs

Lines changed: 83 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Random number generator support
22
3-
use super::Uint;
4-
use crate::{CtLt, Encoding, Limb, NonZero, Random, RandomBits, RandomBitsError, RandomMod};
3+
use super::{Uint, Word};
4+
use crate::{CtLt, Encoding, Limb, NonZero, Random, RandomBits, RandomBitsError, RandomMod, Zero};
55
use rand_core::{RngCore, TryRngCore};
66

77
impl<const LIMBS: usize> Random for Uint<LIMBS> {
@@ -25,26 +25,45 @@ impl<const LIMBS: usize> Random for Uint<LIMBS> {
2525
/// `rng.fill_bytes(&mut bytes[..i]); rng.fill_bytes(&mut bytes[i..])` constructs the same `bytes`,
2626
/// as long as `i` is a multiple of `X`.
2727
/// Note that the `TryRngCore` trait does _not_ require this behaviour from `rng`.
28-
pub(crate) fn random_bits_core<T, R: TryRngCore + ?Sized>(
28+
pub(crate) fn random_bits_core<R: TryRngCore + ?Sized>(
2929
rng: &mut R,
30-
x: &mut T,
31-
n_bits: u32,
32-
) -> Result<(), R::Error>
33-
where
34-
T: Encoding,
35-
{
36-
if n_bits == 0 {
30+
zeroed_limbs: &mut [Limb],
31+
bit_length: u32,
32+
) -> Result<(), RandomBitsError<R::Error>> {
33+
if bit_length == 0 {
3734
return Ok(());
3835
}
3936

40-
let n_bytes = n_bits.div_ceil(u8::BITS) as usize;
41-
let hi_mask = u8::MAX >> ((u8::BITS - (n_bits % u8::BITS)) % u8::BITS);
37+
let buffer: Word = 0;
38+
let mut buffer = buffer.to_be_bytes();
4239

43-
let mut buffer = x.to_le_bytes();
44-
let slice = buffer.as_mut();
45-
rng.try_fill_bytes(&mut slice[..n_bytes])?;
46-
slice[n_bytes - 1] &= hi_mask;
47-
*x = T::from_le_bytes(buffer);
40+
let nonzero_limbs = bit_length.div_ceil(Limb::BITS) as usize;
41+
let partial_limb = bit_length % Limb::BITS;
42+
let mask = Word::MAX >> ((Word::BITS - partial_limb) % Word::BITS);
43+
44+
for i in 0..nonzero_limbs - 1 {
45+
rng.try_fill_bytes(&mut buffer)
46+
.map_err(RandomBitsError::RandCore)?;
47+
zeroed_limbs[i] = Limb(Word::from_le_bytes(buffer));
48+
}
49+
50+
// This algorithm should sample the same number of random bytes, regardless of the pointer width
51+
// of the target platform. To this end, special attention has to be paid to the case where
52+
// bit_length - 1 < 32 mod 64. Bit strings of that size can be represented using `2X+1` 32-bit
53+
// words or `X+1` 64-bit words. Note that 64*(X+1) - 32*(2X+1) = 32. Hence, if we sample full
54+
// words only, a 64-bit platform will sample 32 bits more than a 32-bit platform. We prevent
55+
// this by forcing both platforms to only sample 4 bytes for the last word in this case.
56+
let slice = if partial_limb > 0 && partial_limb <= 32 {
57+
// Note: we do not have to zeroize the second half of the buffer, as the mask will take
58+
// care of this in the end.
59+
&mut buffer[0..4]
60+
} else {
61+
buffer.as_mut_slice()
62+
};
63+
64+
rng.try_fill_bytes(slice)
65+
.map_err(RandomBitsError::RandCore)?;
66+
zeroed_limbs[nonzero_limbs - 1] = Limb(Word::from_le_bytes(buffer) & mask);
4867

4968
Ok(())
5069
}
@@ -74,46 +93,72 @@ impl<const LIMBS: usize> RandomBits for Uint<LIMBS> {
7493
bits_precision,
7594
});
7695
}
77-
let mut x = Self::ZERO;
78-
random_bits_core(rng, &mut x, bit_length).map_err(RandomBitsError::RandCore)?;
79-
Ok(x)
96+
let mut limbs = [Limb::ZERO; LIMBS];
97+
random_bits_core(rng, &mut limbs, bit_length)?;
98+
Ok(Self::from(limbs))
8099
}
81100
}
82101

83102
impl<const LIMBS: usize> RandomMod for Uint<LIMBS> {
84-
fn random_mod_vartime<R: RngCore + ?Sized>(rng: &mut R, modulus: &NonZero<Self>) -> Self {
85-
let mut x = Self::ZERO;
86-
let Ok(()) = random_mod_vartime_core(rng, &mut x, modulus, modulus.bits_vartime());
87-
x
103+
fn random_mod<R: RngCore + ?Sized>(rng: &mut R, modulus: &NonZero<Self>) -> Self {
104+
let mut n = Self::ZERO;
105+
let Ok(()) = random_mod_core(rng, &mut n, modulus, modulus.bits_vartime());
106+
n
88107
}
89108

90109
fn try_random_mod_vartime<R: TryRngCore + ?Sized>(
91110
rng: &mut R,
92111
modulus: &NonZero<Self>,
93112
) -> Result<Self, R::Error> {
94-
let mut x = Self::ZERO;
95-
random_mod_vartime_core(rng, &mut x, modulus, modulus.bits_vartime())?;
96-
Ok(x)
113+
let mut n = Self::ZERO;
114+
random_mod_core(rng, &mut n, modulus, modulus.bits_vartime())?;
115+
Ok(n)
97116
}
98117
}
99118

100-
/// Generic implementation of `random_mod_vartime` which can be shared with `BoxedUint`.
119+
/// Generic implementation of `random_mod` which can be shared with `BoxedUint`.
101120
// TODO(tarcieri): obtain `n_bits` via a trait like `Integer`
102-
pub(super) fn random_mod_vartime_core<T, R: TryRngCore + ?Sized>(
121+
pub(super) fn random_mod_core<T, R: TryRngCore + ?Sized>(
103122
rng: &mut R,
104-
x: &mut T,
123+
n: &mut T,
105124
modulus: &NonZero<T>,
106125
n_bits: u32,
107126
) -> Result<(), R::Error>
108127
where
109-
T: Encoding + CtLt,
128+
T: AsMut<[Limb]> + AsRef<[Limb]> + CtLt + Zero,
110129
{
130+
#[cfg(target_pointer_width = "64")]
131+
let mut next_word = || rng.try_next_u64();
132+
#[cfg(target_pointer_width = "32")]
133+
let mut next_word = || rng.try_next_u32();
134+
135+
let n_limbs = n_bits.div_ceil(Limb::BITS) as usize;
136+
137+
let hi_word_modulus = modulus.as_ref().as_ref()[n_limbs - 1].0;
138+
let mask = !0 >> hi_word_modulus.leading_zeros();
139+
let mut hi_word = next_word()? & mask;
140+
111141
loop {
112-
random_bits_core(rng, x, n_bits)?;
113-
if x.ct_lt(modulus).into() {
114-
return Ok(());
142+
while hi_word > hi_word_modulus {
143+
hi_word = next_word()? & mask;
144+
}
145+
// Set high limb
146+
n.as_mut()[n_limbs - 1] = Limb::from_le_bytes(hi_word.to_le_bytes());
147+
// Set low limbs
148+
for i in 0..n_limbs - 1 {
149+
// Need to deserialize from little-endian to make sure that two 32-bit limbs
150+
// deserialized sequentially are equal to one 64-bit limb produced from the same
151+
// byte stream.
152+
n.as_mut()[i] = Limb::from_le_bytes(next_word()?.to_le_bytes());
153+
}
154+
// If the high limb is equal to the modulus' high limb, it's still possible
155+
// that the full uint is too big so we check and repeat if it is.
156+
if n.ct_lt(modulus).into() {
157+
break;
115158
}
159+
hi_word = next_word()? & mask;
116160
}
161+
Ok(())
117162
}
118163

119164
#[cfg(test)]
@@ -223,7 +268,7 @@ mod tests {
223268

224269
let bit_length = 989;
225270
let mut val = U1024::ZERO;
226-
random_bits_core(&mut rng, &mut val, bit_length).expect("safe");
271+
random_bits_core(&mut rng, val.as_mut_limbs(), bit_length).expect("safe");
227272

228273
assert_eq!(
229274
val,
@@ -242,41 +287,6 @@ mod tests {
242287
);
243288
}
244289

245-
/// Make sure random_mod_vartime output is consistent across platforms
246-
#[test]
247-
fn random_mod_vartime_platform_independence() {
248-
let mut rng = get_four_sequential_rng();
249-
250-
let modulus = NonZero::new(U256::from_u32(8192)).unwrap();
251-
let mut vals = [U256::ZERO; 5];
252-
for val in &mut vals {
253-
*val = U256::random_mod_vartime(&mut rng, &modulus);
254-
}
255-
let expected = [55, 3378, 2172, 1657, 5323];
256-
for (want, got) in expected.into_iter().zip(vals.into_iter()) {
257-
// assert_eq!(got.as_words()[0], want);
258-
assert_eq!(got, U256::from_u32(want));
259-
}
260-
261-
let modulus =
262-
NonZero::new(U256::ZERO.wrapping_sub(&U256::from_u64(rng.next_u64()))).unwrap();
263-
let val = U256::random_mod_vartime(&mut rng, &modulus);
264-
assert_eq!(
265-
val,
266-
U256::from_be_hex("E17653A37F1BCC44277FA208E6B31E08CDC4A23A7E88E660EF781C7DD2D368BA")
267-
);
268-
269-
let mut state = [0u8; 16];
270-
rng.fill_bytes(&mut state);
271-
272-
assert_eq!(
273-
state,
274-
[
275-
105, 47, 30, 235, 242, 2, 67, 197, 163, 64, 75, 125, 34, 120, 40, 134,
276-
],
277-
);
278-
}
279-
280290
/// Test that random bytes are sampled consecutively.
281291
#[test]
282292
fn random_bits_4_bytes_sequential() {
@@ -287,8 +297,9 @@ mod tests {
287297
let mut rng = get_four_sequential_rng();
288298
let mut first = U1024::ZERO;
289299
let mut second = U1024::ZERO;
290-
random_bits_core(&mut rng, &mut first, bit_length).expect("safe");
291-
random_bits_core(&mut rng, &mut second, U1024::BITS - bit_length).expect("safe");
300+
random_bits_core(&mut rng, first.as_mut_limbs(), bit_length).expect("safe");
301+
random_bits_core(&mut rng, second.as_mut_limbs(), U1024::BITS - bit_length)
302+
.expect("safe");
292303
assert_eq!(second.shl(bit_length).bitor(&first), RANDOM_OUTPUT);
293304
}
294305
}

tests/monty_form.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ where
5252
T: Unsigned + Bounded + Encoding,
5353
<T as Unsigned>::Monty: Invert<Output = CtOption<T::Monty>>,
5454
{
55-
let r = T::from_be_bytes(bytes.clone());
55+
let r = T::from_be_bytes(bytes);
5656
let rm = <T as Unsigned>::Monty::new(r.clone(), monty_params);
5757
let rm_inv = rm.invert();
5858
prop_assume!(bool::from(rm_inv.is_some()), "r={:?} is not invertible", r);

0 commit comments

Comments
 (0)