1use rand::{CryptoRng, Rng, RngCore, SeedableRng as _};
5
6pub fn new_uuid_v4() -> uuid::Uuid {
8 let mut random_bytes = uuid::Bytes::default();
9 forest_rng().fill(&mut random_bytes);
10 uuid::Builder::from_random_bytes(random_bytes).into_uuid()
11}
12
13#[cfg(test)]
16pub(crate) fn random_cid() -> cid::Cid {
17 use crate::utils::multihash::prelude::*;
18 let mut digest = [0u8; 32];
19 forest_rng().fill(&mut digest);
20 cid::Cid::new_v1(
21 fvm_ipld_encoding::DAG_CBOR,
22 MultihashCode::Blake2b256.digest(&digest),
23 )
24}
25
26pub fn forest_rng() -> impl Rng + CryptoRng {
30 forest_rng_internal(ForestRngMode::ThreadRng)
31}
32
33pub fn forest_os_rng() -> impl Rng + CryptoRng {
37 forest_rng_internal(ForestRngMode::OsRng)
38}
39
40pub const FIXED_RNG_SEED_ENV: &str = "FOREST_TEST_RNG_FIXED_SEED";
41
42enum ForestRngMode {
43 ThreadRng,
44 OsRng,
45}
46
47fn forest_rng_internal(mode: ForestRngMode) -> impl Rng + CryptoRng {
48 const ENV: &str = FIXED_RNG_SEED_ENV;
49 if let Ok(v) = std::env::var(ENV) {
50 if let Ok(seed) = v.parse() {
51 #[cfg(not(test))]
52 tracing::warn!("[security] using test RNG with fixed seed {seed} set by {ENV}");
53 return Either::Left(rand_chacha::ChaChaRng::seed_from_u64(seed));
54 } else {
55 tracing::warn!("invalid u64 seed set by {ENV}: {v}. Falling back to the default RNG.");
56 }
57 }
58 match mode {
59 #[allow(clippy::disallowed_methods)]
60 ForestRngMode::ThreadRng => Either::Right(Either::Left(rand::thread_rng())),
61 #[allow(clippy::disallowed_types)]
62 ForestRngMode::OsRng => Either::Right(Either::Right(rand::rngs::OsRng)),
63 }
64}
65
66enum Either<A, B> {
67 Left(A),
68 Right(B),
69}
70
71impl<A, B> RngCore for Either<A, B>
72where
73 A: RngCore,
74 B: RngCore,
75{
76 fn next_u32(&mut self) -> u32 {
77 match self {
78 Self::Left(i) => i.next_u32(),
79 Self::Right(i) => i.next_u32(),
80 }
81 }
82
83 fn next_u64(&mut self) -> u64 {
84 match self {
85 Self::Left(i) => i.next_u64(),
86 Self::Right(i) => i.next_u64(),
87 }
88 }
89
90 fn fill_bytes(&mut self, dst: &mut [u8]) {
91 match self {
92 Self::Left(i) => i.fill_bytes(dst),
93 Self::Right(i) => i.fill_bytes(dst),
94 }
95 }
96
97 fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> {
98 match self {
99 Self::Left(i) => i.try_fill_bytes(dest),
100 Self::Right(i) => i.try_fill_bytes(dest),
101 }
102 }
103}
104
105impl<A, B> CryptoRng for Either<A, B>
106where
107 A: RngCore,
108 B: RngCore,
109{
110}
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115 use serial_test::serial;
116
117 #[test]
118 #[serial]
119 fn test_fixed_seed_env() {
120 unsafe { std::env::set_var(FIXED_RNG_SEED_ENV, "0") };
121
122 let mut a = [0; 1024];
123 let mut b = [0; 1024];
124
125 forest_rng().fill(&mut a);
126 forest_rng().fill(&mut b);
127 assert_eq!(a, b);
128
129 forest_os_rng().fill(&mut a);
130 forest_os_rng().fill(&mut b);
131 assert_eq!(a, b);
132
133 unsafe { std::env::remove_var(FIXED_RNG_SEED_ENV) };
134 }
135
136 #[test]
137 #[serial]
138 fn test_thread_rng() {
139 unsafe { std::env::remove_var(FIXED_RNG_SEED_ENV) };
140 let mut a = [0; 1024];
141 forest_rng().fill(&mut a);
142 let mut b = [0; 1024];
143 forest_rng().fill(&mut b);
144 assert_ne!(a, b);
145 }
146
147 #[test]
148 #[serial]
149 fn test_os_rng() {
150 unsafe { std::env::remove_var(FIXED_RNG_SEED_ENV) };
151 let mut a = [0; 1024];
152 forest_os_rng().fill(&mut a);
153 let mut b = [0; 1024];
154 forest_os_rng().fill(&mut b);
155 assert_ne!(a, b);
156 }
157}