1 // Copyright 2021 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 //! Rust implementation of functionality parallel to libchrome's base/rand_util.h.
6
7 use std::{thread::sleep, time::Duration};
8
9 use libc::{c_uint, c_void};
10
11 use super::{errno_result, Result};
12 use crate::handle_eintr_errno;
13
14 /// How long to wait before calling getrandom again if it does not return
15 /// enough bytes.
16 const POLL_INTERVAL: Duration = Duration::from_millis(50);
17
18 /// Represents whether or not the random bytes are pulled from the source of
19 /// /dev/random or /dev/urandom.
20 #[derive(Debug, Clone, Eq, PartialEq)]
21 pub enum Source {
22 // This is the default and uses the same source as /dev/urandom.
23 Pseudorandom,
24 // This uses the same source as /dev/random and may be.
25 Random,
26 }
27
28 impl Default for Source {
default() -> Self29 fn default() -> Self {
30 Source::Pseudorandom
31 }
32 }
33
34 impl Source {
to_getrandom_flags(&self) -> c_uint35 fn to_getrandom_flags(&self) -> c_uint {
36 match self {
37 Source::Random => libc::GRND_RANDOM,
38 Source::Pseudorandom => 0,
39 }
40 }
41 }
42
43 /// Fills `output` completely with random bytes from the specified `source`.
rand_bytes(mut output: &mut [u8], source: Source) -> Result<()>44 pub fn rand_bytes(mut output: &mut [u8], source: Source) -> Result<()> {
45 if output.is_empty() {
46 return Ok(());
47 }
48
49 loop {
50 // Safe because output is mutable and the writes are limited by output.len().
51 let bytes = handle_eintr_errno!(unsafe {
52 libc::getrandom(
53 output.as_mut_ptr() as *mut c_void,
54 output.len(),
55 source.to_getrandom_flags(),
56 )
57 });
58
59 if bytes < 0 {
60 return errno_result();
61 }
62 if bytes as usize == output.len() {
63 return Ok(());
64 }
65
66 // Wait for more entropy and try again for the remaining bytes.
67 sleep(POLL_INTERVAL);
68 output = &mut output[bytes as usize..];
69 }
70 }
71
72 /// Allocates a vector of length `len` filled with random bytes from the
73 /// specified `source`.
rand_vec(len: usize, source: Source) -> Result<Vec<u8>>74 pub fn rand_vec(len: usize, source: Source) -> Result<Vec<u8>> {
75 let mut rand = Vec::with_capacity(len);
76 if len == 0 {
77 return Ok(rand);
78 }
79
80 // Safe because rand will either be initialized by getrandom or dropped.
81 unsafe { rand.set_len(len) };
82 rand_bytes(rand.as_mut_slice(), source)?;
83 Ok(rand)
84 }
85
86 #[cfg(test)]
87 mod tests {
88 use super::*;
89
90 const TEST_SIZE: usize = 64;
91
92 // ANDROID: b/227794338
93 // #[test]
94 // fn randbytes_success() {
95 // let mut rand = vec![0u8; TEST_SIZE];
96 // rand_bytes(&mut rand, Source::Pseudorandom).unwrap();
97 // assert_ne!(&rand, &[0u8; TEST_SIZE]);
98 // }
99
100 // #[test]
101 // fn randvec_success() {
102 // let rand = rand_vec(TEST_SIZE, Source::Pseudorandom).unwrap();
103 // assert_eq!(rand.len(), TEST_SIZE);
104 // assert_ne!(&rand, &[0u8; TEST_SIZE]);
105 // }
106
107 // #[test]
108 // fn sourcerandom_success() {
109 // let rand = rand_vec(TEST_SIZE, Source::Random).unwrap();
110 // assert_ne!(&rand, &[0u8; TEST_SIZE]);
111 // }
112 }
113