Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,65 @@ pub trait Random: Sized {
fn random(rng: &mut impl CryptoRngCore) -> Self;
}

/// Possible errors of [`RandomBits::try_random_bits`].
#[cfg(feature = "rand_core")]
#[derive(Debug)]
pub enum RandomBitsError {
/// An error of the internal RNG library.
RandCore(rand_core::Error),
/// The requested `bits_precision` does not match the size of the integer
/// corresponding to the type (in the cases where this is set in compile time).
BitsPrecisionMismatch {
/// The requested precision.
bits_precision: u32,
/// The compile-time size of the integer.
integer_bits: u32,
},
/// The requested `bit_length` is larger than `bits_precision`.
BitLengthTooLarge {
/// The requested bit length of the random number.
bit_length: u32,
/// The requested precision.
bits_precision: u32,
},
}

/// Random bits generation support.
#[cfg(feature = "rand_core")]
pub trait RandomBits: Sized {
/// Generate a cryptographically secure random value in range `[0, 2^bit_length)`.
fn random_bits(rng: &mut impl CryptoRngCore, bit_length: u32) -> Self {
Self::try_random_bits(rng, bit_length).expect("try_random_bits() failed")
}

/// Generate a cryptographically secure random value in range `[0, 2^bit_length)`.
fn try_random_bits(
rng: &mut impl CryptoRngCore,
bit_length: u32,
) -> Result<Self, RandomBitsError>;

/// Generate a cryptographically secure random value.
///
/// A wrapper for [`RandomBits::try_random_bits_with_precision`] that panics on error.
fn random_bits_with_precision(
rng: &mut impl CryptoRngCore,
bit_length: u32,
bits_precision: u32,
) -> Self {
Self::try_random_bits_with_precision(rng, bit_length, bits_precision)
.expect("try_random_bits_with_precision() failed")
}

/// Generate a cryptographically secure random value in range `[0, 2^bit_length)`,
/// returning an integer with the closest available size to `bits_precision`
/// (if the implementing type supports runtime sizing).
fn try_random_bits_with_precision(
rng: &mut impl CryptoRngCore,
bit_length: u32,
bits_precision: u32,
) -> Result<Self, RandomBitsError>;
}

/// Modular random number generation support.
#[cfg(feature = "rand_core")]
pub trait RandomMod: Sized + Zero {
Expand Down
41 changes: 35 additions & 6 deletions src/uint/boxed/rand.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,57 @@
//! Random number generator support.

use super::BoxedUint;
use crate::{uint::rand::random_mod_core, Limb, NonZero, Random, RandomMod};
use crate::{
uint::rand::{random_bits_core, random_mod_core},
Limb, NonZero, Random, RandomBits, RandomBitsError, RandomMod,
};
use rand_core::CryptoRngCore;

impl BoxedUint {
/// Generate a cryptographically secure random [`BoxedUint`].
/// in range `[0, 2^bits_precision)`.
pub fn random(rng: &mut impl CryptoRngCore, bits_precision: u32) -> Self {
let mut ret = BoxedUint::zero_with_precision(bits_precision);
/// in range `[0, 2^bit_length)`, with the minimum precision that fits it.
pub fn random(rng: &mut impl CryptoRngCore, bit_length: u32) -> Self {
let mut ret = BoxedUint::zero_with_precision(bit_length);

for limb in &mut *ret.limbs {
*limb = Limb::random(rng)
}

// Since `bits_precision` will be rounded up on creation of `ret`,
// Since `bit_length` will be rounded up on creation of `ret`,
// we need to clear the high bits if the rounding occurred.
ret.limbs[ret.limbs.len() - 1] =
ret.limbs[ret.limbs.len() - 1] & (Limb::MAX >> (ret.bits_precision() - bits_precision));
ret.limbs[ret.limbs.len() - 1] & (Limb::MAX >> (ret.bits_precision() - bit_length));

ret
}
}

impl RandomBits for BoxedUint {
fn try_random_bits(
rng: &mut impl CryptoRngCore,
bit_length: u32,
) -> Result<Self, RandomBitsError> {
Self::try_random_bits_with_precision(rng, bit_length, bit_length)
}

fn try_random_bits_with_precision(
rng: &mut impl CryptoRngCore,
bit_length: u32,
bits_precision: u32,
) -> Result<Self, RandomBitsError> {
if bit_length > bits_precision {
return Err(RandomBitsError::BitLengthTooLarge {
bit_length,
bits_precision,
});
}

let mut ret = BoxedUint::zero_with_precision(bits_precision);
random_bits_core(rng, &mut ret.limbs, bit_length)?;
Ok(ret)
}
}

impl RandomMod for BoxedUint {
/// Generate a cryptographically secure random [`BoxedUint`] which is less than a given
/// `modulus`.
Expand Down
100 changes: 97 additions & 3 deletions src/uint/rand.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Random number generator support

use super::Uint;
use crate::{Encoding, Limb, NonZero, Random, RandomMod, Zero};
use super::{Uint, Word};
use crate::{Encoding, Limb, NonZero, Random, RandomBits, RandomBitsError, RandomMod, Zero};
use rand_core::CryptoRngCore;
use subtle::ConstantTimeLess;

Expand All @@ -18,6 +18,62 @@ impl<const LIMBS: usize> Random for Uint<LIMBS> {
}
}

pub(crate) fn random_bits_core(
rng: &mut impl CryptoRngCore,
limbs: &mut [Limb],
bit_length: u32,
) -> Result<(), RandomBitsError> {
let buffer: Word = 0;
let mut buffer = buffer.to_be_bytes();

let nonzero_limbs = ((bit_length + Limb::BITS - 1) / Limb::BITS) as usize;
let partial_limb = bit_length % Limb::BITS;
let mask = Word::MAX >> ((Word::BITS - partial_limb) % Word::BITS);

for i in 0..nonzero_limbs - 1 {
rng.try_fill_bytes(&mut buffer)
.map_err(RandomBitsError::RandCore)?;
limbs[i] = Limb(Word::from_be_bytes(buffer));
}

rng.try_fill_bytes(&mut buffer)
.map_err(RandomBitsError::RandCore)?;
limbs[nonzero_limbs - 1] = Limb(Word::from_be_bytes(buffer) & mask);

Ok(())
}

impl<const LIMBS: usize> RandomBits for Uint<LIMBS> {
fn try_random_bits(
rng: &mut impl CryptoRngCore,
bit_length: u32,
) -> Result<Self, RandomBitsError> {
Self::try_random_bits_with_precision(rng, bit_length, Self::BITS)
}

fn try_random_bits_with_precision(
rng: &mut impl CryptoRngCore,
bit_length: u32,
bits_precision: u32,
) -> Result<Self, RandomBitsError> {
if bits_precision != Self::BITS {
return Err(RandomBitsError::BitsPrecisionMismatch {
bits_precision,
integer_bits: Self::BITS,
});
}
if bit_length > Self::BITS {
return Err(RandomBitsError::BitLengthTooLarge {
bit_length,
bits_precision,
});
}
let mut limbs = [Limb::ZERO; LIMBS];
random_bits_core(rng, &mut limbs, bit_length)?;
Ok(Self::from(limbs))
}
}

impl<const LIMBS: usize> RandomMod for Uint<LIMBS> {
/// Generate a cryptographically secure random [`Uint`] which is less than
/// a given `modulus`.
Expand Down Expand Up @@ -75,7 +131,7 @@ pub(super) fn random_mod_core<T>(

#[cfg(test)]
mod tests {
use crate::{NonZero, RandomMod, U256};
use crate::{Limb, NonZero, RandomBits, RandomMod, U256};
use rand_core::SeedableRng;

#[test]
Expand All @@ -97,4 +153,42 @@ mod tests {
// Check that the value is in range
assert!(res < U256::from(0x10000000000000001u128));
}

#[test]
fn random_bits() {
let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(1);

let lower_bound = 16;

// Full length of the integer
let bit_length = U256::BITS;
for _ in 0..10 {
let res = U256::random_bits(&mut rng, bit_length);
assert!(res > (U256::ONE << (bit_length - lower_bound)));
}

// A multiple of limb size
let bit_length = U256::BITS - Limb::BITS;
for _ in 0..10 {
let res = U256::random_bits(&mut rng, bit_length);
assert!(res > (U256::ONE << (bit_length - lower_bound)));
assert!(res < (U256::ONE << bit_length));
}

// A multiple of 8
let bit_length = U256::BITS - Limb::BITS - 8;
for _ in 0..10 {
let res = U256::random_bits(&mut rng, bit_length);
assert!(res > (U256::ONE << (bit_length - lower_bound)));
assert!(res < (U256::ONE << bit_length));
}

// Not a multiple of 8
let bit_length = U256::BITS - Limb::BITS - 8 - 3;
for _ in 0..10 {
let res = U256::random_bits(&mut rng, bit_length);
assert!(res > (U256::ONE << (bit_length - lower_bound)));
assert!(res < (U256::ONE << bit_length));
}
}
}