crypto/utils/
rand.rs

1// Copyright 2020 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4/// Fill the buffer `bs` with cryptographically strong entropy from the operating systems
5/// recommended entropy source
6pub fn fill(bs: &mut [u8]) -> crate::Result<()> {
7    getrandom::getrandom(bs).map_err(|e| crate::Error::SystemError {
8        call: "getrandom::getrandom",
9        raw_os_error: e.raw_os_error(),
10    })
11}
12
13/// Generate a cryptographically strong random value of a `Sized` type `T`
14///
15/// # Safety
16/// This function fills the memory of the returned type with random values, there are no guarantees
17/// that the type's invariants hold and so may lead to undefined behavior if used inappropriately.
18pub unsafe fn gen<T: Sized + Copy>() -> crate::Result<T> {
19    let mut t = core::mem::MaybeUninit::uninit();
20    fill(core::slice::from_raw_parts_mut(
21        t.as_mut_ptr() as *mut u8,
22        core::mem::size_of::<T>(),
23    ))?;
24    Ok(t.assume_init())
25}
26
27#[cfg(test)]
28mod tests {
29    use super::*;
30
31    #[test]
32    fn test_fill() {
33        for _ in 0..ITERATIONS {
34            for size in TEST_SIZES.iter() {
35                let mut buf = vec![0; *size];
36                fill(&mut buf).unwrap();
37                check_uniform_dist(&buf)
38            }
39        }
40    }
41    const TEST_SIZES: &[usize] = &[1024 * 1024, 4 * 1024 * 1024, (4 * 1024 * 1024) + 15];
42    const ITERATIONS: usize = 8;
43
44    fn check_uniform_dist(buf: &[u8]) {
45        let mut dist = vec![0f64; 256];
46        buf.iter().for_each(|b| dist[*b as usize] += 1.0);
47
48        let estimated_avg = (buf.len() as f64) / 256.0;
49        let (estimated_min, estimated_max) = (estimated_avg * 0.9, estimated_avg * 1.1);
50        dist.iter().for_each(|d| {
51            assert!(*d > estimated_min, "{} is not > {}", *d, estimated_min);
52            assert!(*d < estimated_max, "{} is not < {}", *d, estimated_max);
53        });
54    }
55
56    #[test]
57    #[should_panic]
58    fn verify_check_uniform_dist() {
59        check_uniform_dist(&[0; (4 * 1024 * 1024) + 15])
60    }
61}