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