Skip to main content

miden_crypto/rand/
mod.rs

1//! Pseudo-random element generation.
2
3use rand::Rng;
4
5use crate::{Felt, Word};
6
7mod coin;
8pub use coin::RandomCoin;
9
10// Test utilities for generating random data (used in tests and benchmarks)
11#[cfg(any(test, feature = "std"))]
12pub mod test_utils;
13
14// RANDOMNESS (ported from Winterfell's winter-utils)
15// ================================================================================================
16
17/// Defines how `Self` can be read from a sequence of random bytes.
18pub trait Randomizable: Sized {
19    /// Size of `Self` in bytes.
20    ///
21    /// This is used to determine how many bytes should be passed to the
22    /// [from_random_bytes()](Self::from_random_bytes) function.
23    const VALUE_SIZE: usize;
24
25    /// Returns `Self` if the set of bytes forms a valid value, otherwise returns None.
26    fn from_random_bytes(source: &[u8]) -> Option<Self>;
27}
28
29impl Randomizable for u128 {
30    const VALUE_SIZE: usize = 16;
31
32    fn from_random_bytes(source: &[u8]) -> Option<Self> {
33        let bytes = source.get(..Self::VALUE_SIZE)?.try_into().ok()?;
34        Some(u128::from_le_bytes(bytes))
35    }
36}
37
38impl Randomizable for u64 {
39    const VALUE_SIZE: usize = 8;
40
41    fn from_random_bytes(source: &[u8]) -> Option<Self> {
42        let bytes = source.get(..Self::VALUE_SIZE)?.try_into().ok()?;
43        Some(u64::from_le_bytes(bytes))
44    }
45}
46
47impl Randomizable for u32 {
48    const VALUE_SIZE: usize = 4;
49
50    fn from_random_bytes(source: &[u8]) -> Option<Self> {
51        let bytes = source.get(..Self::VALUE_SIZE)?.try_into().ok()?;
52        Some(u32::from_le_bytes(bytes))
53    }
54}
55
56impl Randomizable for u16 {
57    const VALUE_SIZE: usize = 2;
58
59    fn from_random_bytes(source: &[u8]) -> Option<Self> {
60        let bytes = source.get(..Self::VALUE_SIZE)?.try_into().ok()?;
61        Some(u16::from_le_bytes(bytes))
62    }
63}
64
65impl Randomizable for u8 {
66    const VALUE_SIZE: usize = 1;
67
68    fn from_random_bytes(source: &[u8]) -> Option<Self> {
69        source.first().copied()
70    }
71}
72
73impl Randomizable for Felt {
74    const VALUE_SIZE: usize = 8;
75
76    fn from_random_bytes(source: &[u8]) -> Option<Self> {
77        let bytes = source.get(..Self::VALUE_SIZE)?.try_into().ok()?;
78        let value = u64::from_le_bytes(bytes);
79        // Ensure the value is within the field modulus
80        if value < Felt::ORDER {
81            Some(Felt::new_unchecked(value))
82        } else {
83            None
84        }
85    }
86}
87
88impl Randomizable for Word {
89    const VALUE_SIZE: usize = Word::SERIALIZED_SIZE;
90
91    fn from_random_bytes(bytes: &[u8]) -> Option<Self> {
92        let bytes_array: [u8; 32] = bytes.get(..Self::VALUE_SIZE)?.try_into().ok()?;
93        Self::try_from(bytes_array).ok()
94    }
95}
96
97impl<const N: usize> Randomizable for [u8; N] {
98    const VALUE_SIZE: usize = N;
99
100    fn from_random_bytes(source: &[u8]) -> Option<Self> {
101        source.get(..N)?.try_into().ok()
102    }
103}
104
105/// Pseudo-random element generator.
106///
107/// An instance can be used to draw, uniformly at random, base field elements as well as [Word]s.
108pub trait FeltRng: Rng {
109    /// Draw, uniformly at random, a base field element.
110    fn draw_element(&mut self) -> Felt;
111
112    /// Draw, uniformly at random, a [Word].
113    fn draw_word(&mut self) -> Word;
114}
115
116// RANDOM VALUE GENERATION FOR TESTING
117// ================================================================================================
118
119/// Generates a random field element for testing purposes.
120///
121/// This function is only available with the `std` feature.
122#[cfg(feature = "std")]
123pub fn random_felt() -> Felt {
124    use rand::RngExt;
125    let mut rng = rand::rng();
126    // We use the `Felt::new` constructor to do rejection sampling here. It should effectively
127    // never repeat, but nevertheless gives us the correct distribution.
128    loop {
129        if let Ok(felt) = Felt::new(rng.random::<u64>()) {
130            return felt;
131        }
132    }
133}
134
135/// Generates a random word (4 field elements) for testing purposes.
136///
137/// This function is only available with the `std` feature.
138#[cfg(feature = "std")]
139pub fn random_word() -> Word {
140    Word::new([random_felt(), random_felt(), random_felt(), random_felt()])
141}
142
143#[cfg(test)]
144mod tests {
145    use super::Randomizable;
146    use crate::{Felt, Word};
147
148    #[test]
149    fn randomizable_short_inputs_return_none() {
150        assert!(u128::from_random_bytes(&[0; 15]).is_none());
151        assert!(u64::from_random_bytes(&[0; 7]).is_none());
152        assert!(u32::from_random_bytes(&[0; 3]).is_none());
153        assert!(u16::from_random_bytes(&[0; 1]).is_none());
154        assert!(u8::from_random_bytes(&[]).is_none());
155        assert!(Felt::from_random_bytes(&[0; 7]).is_none());
156        assert!(Word::from_random_bytes(&[0; 31]).is_none());
157        assert!(<[u8; 4]>::from_random_bytes(&[0; 3]).is_none());
158    }
159}