hopr_crypto_random/
lib.rs

1//! This Rust crate contains implementation of common random number generation functions.
2//! All functions and types from this crate supply cryptographically secure random numbers.
3//!
4//! Instead of relying on external crates, all HOPR crates in this monorepo should
5//! exclusively rely on randomness functions only from this crate.
6
7use generic_array::{ArrayLength, GenericArray};
8use rand::CryptoRng;
9pub use rand::{Rng, RngCore};
10
11/// Maximum random integer that can be generated.
12/// This is the last positive 64-bit value in the two's complement representation.
13pub const MAX_RANDOM_INTEGER: u64 = 9007199254740991;
14
15/// Gets the default cryptographically secure random number generator.
16///
17/// **WARNING** On debug builds with the ` fixed_rng ` feature enabled during
18/// compilation, this function will return an RNG with a fixed seed, which is *NOT SECURE*!
19/// This is reserved for deterministic testing.
20#[cfg(all(debug_assertions, feature = "fixed_rng"))]
21#[inline]
22pub fn rng() -> impl RngCore + CryptoRng {
23    use rand::SeedableRng;
24    rand::rngs::StdRng::from_seed([
25        0x5f, 0x57, 0xce, 0x2a, 0x84, 0x14, 0x7e, 0x88, 0x43, 0x56, 0x44, 0x56, 0x7f, 0x90, 0x4f, 0xb2, 0x04, 0x6b,
26        0x18, 0x42, 0x75, 0x69, 0xbe, 0x53, 0xb2, 0x29, 0x78, 0xbd, 0xf3, 0x0a, 0xda, 0xba,
27    ])
28}
29
30/// Gets the default cryptographically secure random number generator.
31#[cfg(any(not(debug_assertions), not(feature = "fixed_rng")))]
32#[inline]
33pub fn rng() -> impl RngCore + CryptoRng {
34    rand::rngs::OsRng
35}
36
37/// Returns `true` if the build is using an **insecure** RNG with a fixed seed.
38///
39/// See also [`rng`].
40#[inline]
41pub const fn is_rng_fixed() -> bool {
42    cfg!(debug_assertions) && cfg!(feature = "fixed_rng")
43}
44
45/// Generates a random float uniformly distributed between 0 (inclusive) and 1 (exclusive).
46#[inline]
47pub fn random_float() -> f64 {
48    rng().r#gen()
49}
50
51/// Generates a random float uniformly distributed in the given range.
52#[inline]
53pub fn random_float_in_range(range: std::ops::Range<f64>) -> f64 {
54    rng().gen_range(range)
55}
56
57/// Generates a random unsigned integer which is at least `start` and optionally strictly less than `end`.
58/// If `end` is not given, the ` MAX_RANDOM_INTEGER ` value is used.
59/// The caller must make sure that 0 <= `start` < `end` <= `MAX_RANDOM_INTEGER`, otherwise the function will panic.
60pub fn random_integer(start: u64, end: Option<u64>) -> u64 {
61    let real_end = end.unwrap_or(MAX_RANDOM_INTEGER);
62
63    assert!(
64        real_end > start && real_end <= MAX_RANDOM_INTEGER,
65        "bounds must be 0 < {start} < {real_end} <= {MAX_RANDOM_INTEGER}"
66    );
67
68    let bound = real_end - start;
69    start + rng().gen_range(0..bound)
70}
71
72/// Fills the specific number of bytes starting from the given offset in the given buffer.
73#[inline]
74pub fn random_fill(buffer: &mut [u8]) {
75    rng().fill_bytes(buffer);
76}
77
78/// Allocates an array of the given size and fills it with random bytes
79pub fn random_bytes<const T: usize>() -> [u8; T] {
80    let mut ret = [0u8; T];
81    random_fill(&mut ret);
82    ret
83}
84
85/// Allocates `GenericArray` of the given size and fills it with random bytes
86pub fn random_array<L: ArrayLength>() -> GenericArray<u8, L> {
87    let mut ret = GenericArray::default();
88    random_fill(&mut ret);
89    ret
90}
91
92/// Trait for types that can be randomly generated.
93pub trait Randomizable {
94    /// Generates random value of this type using a cryptographically strong RNG.
95    fn random() -> Self;
96}
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101
102    #[test]
103    fn test_random_integer() {
104        assert!(random_integer(10, None) > 10);
105
106        let bounded = random_integer(10, Some(20));
107        assert!((10..20).contains(&bounded));
108    }
109
110    #[test]
111    fn test_random_float() {
112        let f = random_float();
113        assert!((0.0..1.0).contains(&f));
114    }
115
116    #[test]
117    fn test_random_fill() {
118        let mut buffer = [0u8; 10];
119        // 7 bytes with indices 2,3,4,5,6,7,8 will be filled with random bytes, the other stay zero
120        random_fill(&mut buffer[2..9]);
121        assert_eq!(0, buffer[0]);
122        assert_eq!(0, buffer[1]);
123        assert_eq!(0, buffer[9]);
124    }
125}