primitives/random/
mod.rs

1pub mod derive_rng;
2pub mod prf;
3pub mod prg;
4pub mod test_rng;
5
6pub use prg::{new_prg, prg, Prng};
7use rand::{distributions::Standard, prelude::Distribution};
8pub use rand_chacha::rand_core::CryptoRngCore;
9use subtle::Choice;
10pub use test_rng::{test_rng, TestRng};
11use typenum::Unsigned;
12
13use crate::{
14    algebra::field::FieldExtension,
15    constants::{CollisionResistanceBytes, Lambda},
16    errors::PrimitiveError,
17    random::derive_rng::DeriveRng,
18    utils::IntoExactSizeIterator,
19};
20
21/// The generic type for all seeds across our protocols.
22pub type Seed = [u8; CollisionResistanceBytes::USIZE];
23
24impl DeriveRng for Seed {}
25
26/// A trait for getting a random value for a type. Will be replaced by the `random` feature
27/// once it is included in std (https://github.com/rust-lang/rust/issues/130703).
28pub trait Random: Sized {
29    // Generates a random value.
30    fn random(rng: impl CryptoRngCore) -> Self;
31
32    // Generates a container with `size` random elements.
33    fn random_n<Container: FromIterator<Self>>(
34        mut rng: impl CryptoRngCore,
35        size: usize,
36    ) -> Container {
37        (0..size).map(|_| Self::random(&mut rng)).collect()
38    }
39}
40
41/// Generalise so that `rand::random()` can be used for any type that implements `Random`.
42impl<T> Random for T
43where
44    Standard: Distribution<T>,
45{
46    #[inline]
47    fn random(mut source: impl CryptoRngCore) -> Self {
48        Standard.sample(&mut source)
49    }
50}
51
52/// A trait for getting a random value for a type alongside some data.
53pub trait RandomWith<D: Clone>: Sized {
54    // Generates a random value given additional data.
55    fn random_with(rng: impl CryptoRngCore, data: D) -> Self;
56
57    // Generates a container with `size` random elements and some available data.
58    fn random_n_with<Container: FromIterator<Self>>(
59        mut rng: impl CryptoRngCore,
60        size: usize,
61        data: D,
62    ) -> Container {
63        (0..size)
64            .map(|_| Self::random_with(&mut rng, data.clone()))
65            .collect()
66    }
67
68    // Generates a container with `size` random elements and some available data.
69    fn random_n_with_each<Container: FromIterator<Self>>(
70        mut rng: impl CryptoRngCore,
71        all_data: impl IntoExactSizeIterator<Item = D>,
72    ) -> Container {
73        all_data
74            .into_iter()
75            .map(|data| Self::random_with(&mut rng, data))
76            .collect()
77    }
78}
79
80/// Generate random non-zero values.
81pub trait RandomNonZero: Sized {
82    // Generates a non-zero element.
83    fn random_non_zero(rng: impl CryptoRngCore) -> Result<Self, PrimitiveError>;
84
85    // Generates a container with `size` random non-zero elements.
86    fn random_n_non_zero<Container: FromIterator<Self>>(
87        rng: impl CryptoRngCore,
88        size: usize,
89    ) -> Result<Container, PrimitiveError>;
90}
91
92impl<T: FieldExtension> RandomNonZero for T {
93    /// Generates a random non-zero value.
94    ///
95    /// May error out if it cannot find a non-zero value after a certain number
96    /// of tries, defined so that:
97    ///
98    /// > `Prob(out == 0) <= 2^-(λ)` as long as `Prob(random()==0) <= 2^-(size_of::<Self>)`
99    ///
100    /// The default implementation repetitively calls `random()` (rejection sampling).
101    /// As such, it is not constant-time, but the side channel leakage should not impact security
102    /// as long as the rng is evaluated in constant time and produces uniformly random values.
103    ///
104    /// If needed, override with a constant-time implementation using `ConditionallySelectable` and
105    /// always running for a fixed number of iterations, potentially returning a zero value
106    /// (with overwhelmingly low probability).
107    #[inline]
108    fn random_non_zero(mut rng: impl CryptoRngCore) -> Result<Self, PrimitiveError> {
109        let n_tries = (Lambda::USIZE).div_ceil(std::mem::size_of::<Self>());
110        for _ in 0..n_tries {
111            let value = <Self as ff::Field>::random(&mut rng);
112            if !<Choice as Into<bool>>::into(value.is_zero()) {
113                return Ok(value);
114            }
115        }
116        Err(PrimitiveError::ZeroValueSampled(
117            std::any::type_name::<Self>().into(),
118        ))
119    }
120
121    // Generates a container with `size` random non-zero elements.
122    fn random_n_non_zero<Container: FromIterator<Self>>(
123        mut rng: impl CryptoRngCore,
124        size: usize,
125    ) -> Result<Container, PrimitiveError> {
126        (0..size).map(|_| Self::random_non_zero(&mut rng)).collect()
127    }
128}