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}