• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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