s2n_quic_core/
random.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use core::ops::RangeInclusive;
5
6/// A generator of random data. The two methods provide the same functionality for
7/// different use cases. One for "public" randomly generated data that may appear
8/// in the clear, and one for "private" data that should remain secret. This approach
9/// lessens the risk of potential predictability weaknesses in random number generation
10/// algorithms from leaking information across contexts.
11pub trait Generator: 'static + Send {
12    /// Fills `dest` with unpredictable bits that may be
13    /// sent over the wire and viewable in the clear.
14    fn public_random_fill(&mut self, dest: &mut [u8]);
15
16    /// Fills `dest` with unpredictable bits that will only be
17    /// used internally within the endpoint, remaining secret.
18    fn private_random_fill(&mut self, dest: &mut [u8]);
19}
20
21/// Generates a random usize within the given inclusive range
22///
23/// NOTE: This will have slight bias towards the lower end of the range. Usages that
24/// require uniform sampling should implement rejection sampling or other methodologies
25/// and not copy this implementation.
26pub(crate) fn gen_range_biased<R: Generator + ?Sized>(
27    random_generator: &mut R,
28    range: RangeInclusive<usize>,
29) -> usize {
30    if range.start() == range.end() {
31        return *range.start();
32    }
33
34    let mut dest = [0; core::mem::size_of::<usize>()];
35    random_generator.public_random_fill(&mut dest);
36    let result = usize::from_le_bytes(dest);
37
38    let max_variance = (range.end() - range.start()).saturating_add(1);
39    range.start() + result % max_variance
40}
41
42#[cfg(any(test, feature = "testing"))]
43pub mod testing {
44    use crate::random;
45
46    #[derive(Debug, Default)]
47    pub struct Generator(pub u8);
48
49    impl random::Generator for Generator {
50        fn public_random_fill(&mut self, dest: &mut [u8]) {
51            let seed = self.0;
52
53            for (i, elem) in dest.iter_mut().enumerate() {
54                *elem = seed ^ i as u8;
55            }
56
57            self.0 = self.0.wrapping_add(1)
58        }
59
60        fn private_random_fill(&mut self, dest: &mut [u8]) {
61            let seed = u8::MAX - self.0;
62
63            for (i, elem) in dest.iter_mut().enumerate() {
64                *elem = seed ^ i as u8;
65            }
66
67            self.0 = self.0.wrapping_add(1)
68        }
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use crate::random;
75
76    #[test]
77    #[cfg_attr(miri, ignore)] // This test is too expensive for miri to complete in a reasonable amount of time
78    #[cfg_attr(kani, kani::proof, kani::unwind(10), kani::solver(kissat))]
79    fn gen_range_biased_test() {
80        bolero::check!()
81            .with_type()
82            .cloned()
83            .for_each(|(seed, mut min, mut max)| {
84                if min > max {
85                    core::mem::swap(&mut min, &mut max);
86                }
87                let mut generator = random::testing::Generator(seed);
88                let result = random::gen_range_biased(&mut generator, min..=max);
89                assert!(result >= min);
90                assert!(result <= max);
91            });
92    }
93}