Skip to main content

primitives/random/
mod.rs

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