Skip to main content

primitives/random/
mod.rs

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