Skip to content

Commit 90d7b0a

Browse files
authored
Add RandomBits trait (#510)
Adds a trait for generating a random number of a given bit size.
1 parent 8746be8 commit 90d7b0a

File tree

7 files changed

+259
-42
lines changed

7 files changed

+259
-42
lines changed

benches/boxed_monty.rs

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use criterion::{
44
};
55
use crypto_bigint::{
66
modular::{BoxedMontyForm, BoxedMontyParams},
7-
BoxedUint, Odd, RandomMod,
7+
BoxedUint, Odd, RandomBits, RandomMod,
88
};
99
use num_bigint::BigUint;
1010
use rand_core::OsRng;
@@ -35,10 +35,14 @@ fn bench_montgomery_ops<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
3535
group.bench_function("multiplication, BoxedUint*BoxedUint", |b| {
3636
b.iter_batched(
3737
|| {
38-
let x =
39-
BoxedMontyForm::new(BoxedUint::random(&mut OsRng, UINT_BITS), params.clone());
40-
let y =
41-
BoxedMontyForm::new(BoxedUint::random(&mut OsRng, UINT_BITS), params.clone());
38+
let x = BoxedMontyForm::new(
39+
BoxedUint::random_bits(&mut OsRng, UINT_BITS),
40+
params.clone(),
41+
);
42+
let y = BoxedMontyForm::new(
43+
BoxedUint::random_bits(&mut OsRng, UINT_BITS),
44+
params.clone(),
45+
);
4246
(x, y)
4347
},
4448
|(x, y)| black_box(x * y),
@@ -50,8 +54,8 @@ fn bench_montgomery_ops<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
5054
group.bench_function("multiplication, BigUint*BigUint (num-bigint-dig)", |b| {
5155
b.iter_batched(
5256
|| {
53-
let x = to_biguint(&BoxedUint::random(&mut OsRng, UINT_BITS)) % &modulus;
54-
let y = to_biguint(&BoxedUint::random(&mut OsRng, UINT_BITS)) % &modulus;
57+
let x = to_biguint(&BoxedUint::random_bits(&mut OsRng, UINT_BITS)) % &modulus;
58+
let y = to_biguint(&BoxedUint::random_bits(&mut OsRng, UINT_BITS)) % &modulus;
5559
(x, y)
5660
},
5761
|(x, y)| x * y % &modulus,
@@ -64,9 +68,9 @@ fn bench_montgomery_ops<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
6468
group.bench_function("modpow, BoxedUint^BoxedUint", |b| {
6569
b.iter_batched(
6670
|| {
67-
let x = BoxedUint::random(&mut OsRng, UINT_BITS);
71+
let x = BoxedUint::random_bits(&mut OsRng, UINT_BITS);
6872
let x_m = BoxedMontyForm::new(x, params.clone());
69-
let p = BoxedUint::random(&mut OsRng, UINT_BITS)
73+
let p = BoxedUint::random_bits(&mut OsRng, UINT_BITS)
7074
| (BoxedUint::one_with_precision(UINT_BITS) << (UINT_BITS - 1));
7175
(x_m, p)
7276
},
@@ -78,10 +82,10 @@ fn bench_montgomery_ops<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
7882
group.bench_function("modpow, BigUint^BigUint (num-bigint-dig)", |b| {
7983
b.iter_batched(
8084
|| {
81-
let x = to_biguint(&BoxedUint::random(&mut OsRng, UINT_BITS));
85+
let x = to_biguint(&BoxedUint::random_bits(&mut OsRng, UINT_BITS));
8286
let x_m = x % &modulus;
8387
let p = to_biguint(
84-
&(BoxedUint::random(&mut OsRng, UINT_BITS)
88+
&(BoxedUint::random_bits(&mut OsRng, UINT_BITS)
8589
| (BoxedUint::one_with_precision(UINT_BITS) << (UINT_BITS - 1))),
8690
);
8791
(x_m, p)
@@ -112,7 +116,7 @@ fn bench_montgomery_conversion<M: Measurement>(group: &mut BenchmarkGroup<'_, M>
112116
let params = BoxedMontyParams::new(Odd::<BoxedUint>::random(&mut OsRng, UINT_BITS));
113117
group.bench_function("BoxedMontyForm::new", |b| {
114118
b.iter_batched(
115-
|| BoxedUint::random(&mut OsRng, UINT_BITS),
119+
|| BoxedUint::random_bits(&mut OsRng, UINT_BITS),
116120
|x| black_box(BoxedMontyForm::new(x, params.clone())),
117121
BatchSize::SmallInput,
118122
)
@@ -121,7 +125,12 @@ fn bench_montgomery_conversion<M: Measurement>(group: &mut BenchmarkGroup<'_, M>
121125
let params = BoxedMontyParams::new(Odd::<BoxedUint>::random(&mut OsRng, UINT_BITS));
122126
group.bench_function("BoxedMontyForm::retrieve", |b| {
123127
b.iter_batched(
124-
|| BoxedMontyForm::new(BoxedUint::random(&mut OsRng, UINT_BITS), params.clone()),
128+
|| {
129+
BoxedMontyForm::new(
130+
BoxedUint::random_bits(&mut OsRng, UINT_BITS),
131+
params.clone(),
132+
)
133+
},
125134
|x| black_box(x.retrieve()),
126135
BatchSize::SmallInput,
127136
)

benches/boxed_uint.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion};
2-
use crypto_bigint::BoxedUint;
2+
use crypto_bigint::{BoxedUint, RandomBits};
33
use rand_core::OsRng;
44

55
/// Size of `BoxedUint` to use in benchmark.
@@ -10,31 +10,31 @@ fn bench_shifts(c: &mut Criterion) {
1010

1111
group.bench_function("shl_vartime", |b| {
1212
b.iter_batched(
13-
|| BoxedUint::random(&mut OsRng, UINT_BITS),
13+
|| BoxedUint::random_bits(&mut OsRng, UINT_BITS),
1414
|x| black_box(x.shl_vartime(UINT_BITS / 2 + 10)),
1515
BatchSize::SmallInput,
1616
)
1717
});
1818

1919
group.bench_function("shl", |b| {
2020
b.iter_batched(
21-
|| BoxedUint::random(&mut OsRng, UINT_BITS),
21+
|| BoxedUint::random_bits(&mut OsRng, UINT_BITS),
2222
|x| x.overflowing_shl(UINT_BITS / 2 + 10),
2323
BatchSize::SmallInput,
2424
)
2525
});
2626

2727
group.bench_function("shr_vartime", |b| {
2828
b.iter_batched(
29-
|| BoxedUint::random(&mut OsRng, UINT_BITS),
29+
|| BoxedUint::random_bits(&mut OsRng, UINT_BITS),
3030
|x| black_box(x.shr_vartime(UINT_BITS / 2 + 10)),
3131
BatchSize::SmallInput,
3232
)
3333
});
3434

3535
group.bench_function("shr", |b| {
3636
b.iter_batched(
37-
|| BoxedUint::random(&mut OsRng, UINT_BITS),
37+
|| BoxedUint::random_bits(&mut OsRng, UINT_BITS),
3838
|x| x.overflowing_shr(UINT_BITS / 2 + 10),
3939
BatchSize::SmallInput,
4040
)

src/odd.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ use crate::BoxedUint;
1010
#[cfg(feature = "rand_core")]
1111
use {crate::Random, rand_core::CryptoRngCore};
1212

13+
#[cfg(all(feature = "alloc", feature = "rand_core"))]
14+
use crate::RandomBits;
15+
1316
/// Wrapper type for odd integers.
1417
///
1518
/// These are frequently used in cryptography, e.g. as a modulus.
@@ -143,8 +146,8 @@ impl<const LIMBS: usize> Random for Odd<Uint<LIMBS>> {
143146
#[cfg(all(feature = "alloc", feature = "rand_core"))]
144147
impl Odd<BoxedUint> {
145148
/// Generate a random `Odd<Uint<T>>`.
146-
pub fn random(rng: &mut impl CryptoRngCore, bits_precision: u32) -> Self {
147-
let mut ret = BoxedUint::random(rng, bits_precision);
149+
pub fn random(rng: &mut impl CryptoRngCore, bit_length: u32) -> Self {
150+
let mut ret = BoxedUint::random_bits(rng, bit_length);
148151
ret.limbs[0] |= Limb::ONE;
149152
Odd(ret)
150153
}

src/traits.rs

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ use subtle::{
2222
#[cfg(feature = "rand_core")]
2323
use rand_core::CryptoRngCore;
2424

25+
#[cfg(feature = "rand_core")]
26+
use core::fmt;
27+
2528
/// Integers whose representation takes a bounded amount of space.
2629
pub trait Bounded {
2730
/// Size of this integer in bits.
@@ -274,6 +277,104 @@ pub trait Random: Sized {
274277
fn random(rng: &mut impl CryptoRngCore) -> Self;
275278
}
276279

280+
/// Possible errors of the methods in [`RandomBits`] trait.
281+
#[cfg(feature = "rand_core")]
282+
#[derive(Debug)]
283+
pub enum RandomBitsError {
284+
/// An error of the internal RNG library.
285+
RandCore(rand_core::Error),
286+
/// The requested `bits_precision` does not match the size of the integer
287+
/// corresponding to the type (in the cases where this is set in compile time).
288+
BitsPrecisionMismatch {
289+
/// The requested precision.
290+
bits_precision: u32,
291+
/// The compile-time size of the integer.
292+
integer_bits: u32,
293+
},
294+
/// The requested `bit_length` is larger than `bits_precision`.
295+
BitLengthTooLarge {
296+
/// The requested bit length of the random number.
297+
bit_length: u32,
298+
/// The requested precision.
299+
bits_precision: u32,
300+
},
301+
}
302+
303+
#[cfg(feature = "rand_core")]
304+
impl fmt::Display for RandomBitsError {
305+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
306+
match self {
307+
Self::RandCore(err) => write!(f, "{}", err),
308+
Self::BitsPrecisionMismatch {
309+
bits_precision,
310+
integer_bits,
311+
} => write!(
312+
f,
313+
concat![
314+
"The requested `bits_precision` ({}) does not match ",
315+
"the size of the integer corresponding to the type ({})"
316+
],
317+
bits_precision, integer_bits
318+
),
319+
Self::BitLengthTooLarge {
320+
bit_length,
321+
bits_precision,
322+
} => write!(
323+
f,
324+
"The requested `bit_length` ({}) is larger than `bits_precision` ({}).",
325+
bit_length, bits_precision
326+
),
327+
}
328+
}
329+
}
330+
331+
#[cfg(feature = "std")]
332+
impl std::error::Error for RandomBitsError {}
333+
334+
/// Random bits generation support.
335+
#[cfg(feature = "rand_core")]
336+
pub trait RandomBits: Sized {
337+
/// Generate a cryptographically secure random value in range `[0, 2^bit_length)`.
338+
///
339+
/// A wrapper for [`RandomBits::try_random_bits`] that panics on error.
340+
fn random_bits(rng: &mut impl CryptoRngCore, bit_length: u32) -> Self {
341+
Self::try_random_bits(rng, bit_length).expect("try_random_bits() failed")
342+
}
343+
344+
/// Generate a cryptographically secure random value in range `[0, 2^bit_length)`.
345+
///
346+
/// This method is variable time wrt `bit_length`.
347+
fn try_random_bits(
348+
rng: &mut impl CryptoRngCore,
349+
bit_length: u32,
350+
) -> Result<Self, RandomBitsError>;
351+
352+
/// Generate a cryptographically secure random value in range `[0, 2^bit_length)`,
353+
/// returning an integer with the closest available size to `bits_precision`
354+
/// (if the implementing type supports runtime sizing).
355+
///
356+
/// A wrapper for [`RandomBits::try_random_bits_with_precision`] that panics on error.
357+
fn random_bits_with_precision(
358+
rng: &mut impl CryptoRngCore,
359+
bit_length: u32,
360+
bits_precision: u32,
361+
) -> Self {
362+
Self::try_random_bits_with_precision(rng, bit_length, bits_precision)
363+
.expect("try_random_bits_with_precision() failed")
364+
}
365+
366+
/// Generate a cryptographically secure random value in range `[0, 2^bit_length)`,
367+
/// returning an integer with the closest available size to `bits_precision`
368+
/// (if the implementing type supports runtime sizing).
369+
///
370+
/// This method is variable time wrt `bit_length`.
371+
fn try_random_bits_with_precision(
372+
rng: &mut impl CryptoRngCore,
373+
bit_length: u32,
374+
bits_precision: u32,
375+
) -> Result<Self, RandomBitsError>;
376+
}
377+
277378
/// Modular random number generation support.
278379
#[cfg(feature = "rand_core")]
279380
pub trait RandomMod: Sized + Zero {

src/uint/boxed/rand.rs

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,35 @@
11
//! Random number generator support.
22
33
use super::BoxedUint;
4-
use crate::{uint::rand::random_mod_core, Limb, NonZero, Random, RandomMod};
4+
use crate::{
5+
uint::rand::{random_bits_core, random_mod_core},
6+
NonZero, RandomBits, RandomBitsError, RandomMod,
7+
};
58
use rand_core::CryptoRngCore;
69

7-
impl BoxedUint {
8-
/// Generate a cryptographically secure random [`BoxedUint`].
9-
/// in range `[0, 2^bits_precision)`.
10-
pub fn random(rng: &mut impl CryptoRngCore, bits_precision: u32) -> Self {
11-
let mut ret = BoxedUint::zero_with_precision(bits_precision);
10+
impl RandomBits for BoxedUint {
11+
fn try_random_bits(
12+
rng: &mut impl CryptoRngCore,
13+
bit_length: u32,
14+
) -> Result<Self, RandomBitsError> {
15+
Self::try_random_bits_with_precision(rng, bit_length, bit_length)
16+
}
1217

13-
for limb in &mut *ret.limbs {
14-
*limb = Limb::random(rng)
18+
fn try_random_bits_with_precision(
19+
rng: &mut impl CryptoRngCore,
20+
bit_length: u32,
21+
bits_precision: u32,
22+
) -> Result<Self, RandomBitsError> {
23+
if bit_length > bits_precision {
24+
return Err(RandomBitsError::BitLengthTooLarge {
25+
bit_length,
26+
bits_precision,
27+
});
1528
}
1629

17-
// Since `bits_precision` will be rounded up on creation of `ret`,
18-
// we need to clear the high bits if the rounding occurred.
19-
ret.limbs[ret.limbs.len() - 1] =
20-
ret.limbs[ret.limbs.len() - 1] & (Limb::MAX >> (ret.bits_precision() - bits_precision));
21-
22-
ret
30+
let mut ret = BoxedUint::zero_with_precision(bits_precision);
31+
random_bits_core(rng, &mut ret.limbs, bit_length)?;
32+
Ok(ret)
2333
}
2434
}
2535

@@ -42,17 +52,17 @@ impl RandomMod for BoxedUint {
4252

4353
#[cfg(test)]
4454
mod tests {
45-
use crate::{BoxedUint, NonZero, RandomMod};
55+
use crate::{BoxedUint, NonZero, RandomBits, RandomMod};
4656
use rand_core::SeedableRng;
4757

4858
#[test]
4959
fn random() {
5060
let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(1);
5161

52-
let r = BoxedUint::random(&mut rng, 256);
62+
let r = BoxedUint::random_bits(&mut rng, 256);
5363
assert!(r.bits_precision() == 256);
5464

55-
let r = BoxedUint::random(&mut rng, 256 - 32 + 1);
65+
let r = BoxedUint::random_bits(&mut rng, 256 - 32 + 1);
5666
assert!(r.bits_precision() == 256);
5767
assert!(r < BoxedUint::one_with_precision(256) << (256 - 32 + 1));
5868
}

src/uint/boxed/sqrt.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ mod tests {
137137

138138
#[cfg(feature = "rand")]
139139
use {
140-
crate::CheckedMul,
140+
crate::{CheckedMul, RandomBits},
141141
rand_chacha::ChaChaRng,
142142
rand_core::{RngCore, SeedableRng},
143143
};
@@ -303,7 +303,7 @@ mod tests {
303303
}
304304

305305
for _ in 0..50 {
306-
let s = BoxedUint::random(&mut rng, 512);
306+
let s = BoxedUint::random_bits(&mut rng, 512);
307307
let mut s2 = BoxedUint::zero_with_precision(512);
308308
s2.limbs[..s.limbs.len()].copy_from_slice(&s.limbs);
309309
assert_eq!(s.square().sqrt(), s2);

0 commit comments

Comments
 (0)