netstack3_base/rng.rs
1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Base types and traits for generating random numbers in netstack.
6//!
7//! NOTE:
8//! - Code in netstack3 is required to only obtain random values through an
9//! [`RngContext`]. This allows a deterministic RNG to be provided when useful
10//! (for example, in tests).
11//! - The CSPRNG requirement exists so that random values produced within the
12//! network stack are not predictable by outside observers. This helps prevent
13//! certain kinds of fingerprinting and denial of service attacks.
14
15use rand::{CryptoRng, RngCore};
16
17/// A context that provides a random number generator (RNG).
18pub trait RngContext {
19 // NB: If the CSPRNG requirement becomes a performance problem,
20 // introduce a second, non-cryptographically secure, RNG.
21
22 /// The random number generator (RNG) provided by this `RngContext`.
23 ///
24 /// The provided RNG must be cryptographically secure, and users may rely on
25 /// that property for their correctness and security.
26 type Rng<'a>: RngCore + CryptoRng
27 where
28 Self: 'a;
29
30 /// Gets the random number generator (RNG).
31 fn rng(&mut self) -> Self::Rng<'_>;
32}
33
34#[cfg(any(test, feature = "testutils"))]
35pub(crate) mod testutil {
36 use alloc::sync::Arc;
37
38 use rand::{CryptoRng, Rng as _, RngCore, SeedableRng};
39 use rand_xorshift::XorShiftRng;
40
41 use crate::sync::Mutex;
42 use crate::RngContext;
43
44 /// A wrapper which implements `RngCore` and `CryptoRng` for any `RngCore`.
45 ///
46 /// This is used to satisfy [`RngContext`]'s requirement that the
47 /// associated `Rng` type implements `CryptoRng`.
48 ///
49 /// # Security
50 ///
51 /// This is obviously insecure. Don't use it except in testing!
52 #[derive(Clone, Debug)]
53 pub struct FakeCryptoRng<R = XorShiftRng>(Arc<Mutex<R>>);
54
55 impl Default for FakeCryptoRng<XorShiftRng> {
56 fn default() -> FakeCryptoRng<XorShiftRng> {
57 FakeCryptoRng::new_xorshift(12957992561116578403)
58 }
59 }
60
61 impl FakeCryptoRng<XorShiftRng> {
62 /// Creates a new [`FakeCryptoRng<XorShiftRng>`] from a seed.
63 pub fn new_xorshift(seed: u128) -> FakeCryptoRng<XorShiftRng> {
64 Self(Arc::new(Mutex::new(new_rng(seed))))
65 }
66
67 /// Returns a deep clone of this RNG, copying the current RNG state.
68 pub fn deep_clone(&self) -> Self {
69 Self(Arc::new(Mutex::new(self.0.lock().clone())))
70 }
71
72 /// Creates `iterations` fake RNGs.
73 ///
74 /// `with_fake_rngs` will create `iterations` different
75 /// [`FakeCryptoRng`]s and call the function `f` for each one of them.
76 ///
77 /// This function can be used for tests that weed out weirdness that can
78 /// happen with certain random number sequences.
79 pub fn with_fake_rngs<F: Fn(Self)>(iterations: u128, f: F) {
80 for seed in 0..iterations {
81 f(FakeCryptoRng::new_xorshift(seed))
82 }
83 }
84 }
85
86 impl<R: RngCore> RngCore for FakeCryptoRng<R> {
87 fn next_u32(&mut self) -> u32 {
88 self.0.lock().next_u32()
89 }
90 fn next_u64(&mut self) -> u64 {
91 self.0.lock().next_u64()
92 }
93 fn fill_bytes(&mut self, dest: &mut [u8]) {
94 self.0.lock().fill_bytes(dest)
95 }
96 fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> {
97 self.0.lock().try_fill_bytes(dest)
98 }
99 }
100
101 impl<R: RngCore> CryptoRng for FakeCryptoRng<R> {}
102
103 impl<R: SeedableRng> SeedableRng for FakeCryptoRng<R> {
104 type Seed = R::Seed;
105
106 fn from_seed(seed: Self::Seed) -> Self {
107 Self(Arc::new(Mutex::new(R::from_seed(seed))))
108 }
109 }
110
111 impl<R: RngCore> RngContext for FakeCryptoRng<R> {
112 type Rng<'a>
113 = &'a mut Self
114 where
115 Self: 'a;
116
117 fn rng(&mut self) -> Self::Rng<'_> {
118 self
119 }
120 }
121
122 /// Create a new deterministic RNG from a seed.
123 pub fn new_rng(mut seed: u128) -> XorShiftRng {
124 if seed == 0 {
125 // XorShiftRng can't take 0 seeds
126 seed = 1;
127 }
128 XorShiftRng::from_seed(seed.to_ne_bytes())
129 }
130
131 /// Invokes a function multiple times with different RNG seeds.
132 pub fn run_with_many_seeds<F: FnMut(u128)>(mut f: F) {
133 // Arbitrary seed.
134 let mut rng = new_rng(0x0fe50fae6c37593d71944697f1245847);
135 for _ in 0..64 {
136 f(rng.gen());
137 }
138 }
139}