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::RngContext;
42 use crate::sync::Mutex;
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 }
97
98 impl<R: RngCore> CryptoRng for FakeCryptoRng<R> {}
99
100 impl<R: SeedableRng> SeedableRng for FakeCryptoRng<R> {
101 type Seed = R::Seed;
102
103 fn from_seed(seed: Self::Seed) -> Self {
104 Self(Arc::new(Mutex::new(R::from_seed(seed))))
105 }
106 }
107
108 impl<R: RngCore> RngContext for FakeCryptoRng<R> {
109 type Rng<'a>
110 = &'a mut Self
111 where
112 Self: 'a;
113
114 fn rng(&mut self) -> Self::Rng<'_> {
115 self
116 }
117 }
118
119 /// Create a new deterministic RNG from a seed.
120 pub fn new_rng(mut seed: u128) -> XorShiftRng {
121 if seed == 0 {
122 // XorShiftRng can't take 0 seeds
123 seed = 1;
124 }
125 XorShiftRng::from_seed(seed.to_ne_bytes())
126 }
127
128 /// Invokes a function multiple times with different RNG seeds.
129 pub fn run_with_many_seeds<F: FnMut(u128)>(mut f: F) {
130 // Arbitrary seed.
131 let mut rng = new_rng(0x0fe50fae6c37593d71944697f1245847);
132 for _ in 0..64 {
133 f(rng.random());
134 }
135 }
136}