Skip to main content

plat/
rand.rs

1// rand -- the operating system's cryptographic random number generator
2//
3//   fill_random(&mut buf)        BCryptGenRandom (system-preferred RNG) / getrandom(2)
4//   random_bytes::<N>() -> [u8; N]          random_u64()
5//
6// There is no fallback: when the system RNG fails the process stops (a key made from weak
7// randomness is worse than no key).
8
9#[cfg(windows)]
10fn os_fill(buf: &mut [u8]) -> bool {
11    #[link(name = "bcrypt", kind = "raw-dylib")]
12    unsafe extern "system" {
13        fn BCryptGenRandom(alg: usize, buf: *mut u8, len: u32, flags: u32) -> i32;
14    }
15    const BCRYPT_USE_SYSTEM_PREFERRED_RNG: u32 = 2;
16    for chunk in buf.chunks_mut(1 << 30) {
17        // SAFETY: the buffer is valid for chunk.len() bytes.
18        let st = unsafe { BCryptGenRandom(0, chunk.as_mut_ptr(), chunk.len() as u32, BCRYPT_USE_SYSTEM_PREFERRED_RNG) };
19        if st != 0 {
20            return false;
21        }
22    }
23    true
24}
25
26#[cfg(target_os = "linux")]
27fn os_fill(buf: &mut [u8]) -> bool {
28    const EINTR: isize = 4;
29    let mut done = 0;
30    while done < buf.len() {
31        let want = (buf.len() - done).min(1 << 25);
32        // SAFETY: writes at most `want` bytes into the rest of the buffer.
33        let r = unsafe { rt::os::syscall3(rt::os::SYS_GETRANDOM, buf[done..].as_mut_ptr() as usize, want, 0) };
34        if r == -EINTR {
35            continue;
36        }
37        if r <= 0 {
38            return false;
39        }
40        done += r as usize;
41    }
42    true
43}
44
45/// fill a buffer with cryptographically secure random bytes (stops the process on failure)
46pub fn fill_random(buf: &mut [u8]) {
47    if !os_fill(buf) {
48        crate::write_stderr(b"fatal: the system random number generator failed\n");
49        crate::exit(70);
50    }
51}
52
53/// N random bytes
54pub fn random_bytes<const N: usize>() -> [u8; N] {
55    let mut b = [0u8; N];
56    fill_random(&mut b);
57    b
58}
59
60/// a random 64-bit number
61pub fn random_u64() -> u64 {
62    u64::from_le_bytes(random_bytes::<8>())
63}