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