1 // Copyright 2019 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 use std::fs::File;
6 use std::io::{self, Read};
7
8 /// A simple prng based on a Linear congruential generator
9 /// https://en.wikipedia.org/wiki/Linear_congruential_generator
10 pub struct SimpleRng {
11 seed: u64,
12 }
13
14 impl SimpleRng {
15 /// Create a new SimpleRng
new(seed: u64) -> SimpleRng16 pub fn new(seed: u64) -> SimpleRng {
17 SimpleRng { seed }
18 }
19
20 /// Generate random u64
rng(&mut self) -> u6421 pub fn rng(&mut self) -> u64 {
22 // a simple Linear congruential generator
23 let a: u64 = 6364136223846793005;
24 let c: u64 = 1442695040888963407;
25 self.seed = a.wrapping_mul(self.seed).wrapping_add(c);
26 self.seed
27 }
28 }
29
30 // Uniformly samples the ASCII alphanumeric characters given a random variable. If an `Err` is
31 // passed in, the error is returned as `Some(Err(...))`. If the the random variable can not be used
32 // to uniformly sample, `None` is returned.
uniform_sample_ascii_alphanumeric( b: Result<u8, std::io::Error>, ) -> Option<Result<char, std::io::Error>>33 fn uniform_sample_ascii_alphanumeric(
34 b: Result<u8, std::io::Error>,
35 ) -> Option<Result<char, std::io::Error>> {
36 const ASCII_CHARS: &[u8] = b"\
37 ABCDEFGHIJKLMNOPQRSTUVWXYZ\
38 abcdefghijklmnopqrstuvwxyz\
39 0123456789";
40 let char_index = match b {
41 Ok(c) => c as usize,
42 Err(e) => return Some(Err(e)),
43 };
44 // Throw away numbers that would cause sampling bias.
45 if char_index >= ASCII_CHARS.len() * 4 {
46 None
47 } else {
48 Some(Ok(ASCII_CHARS[char_index % ASCII_CHARS.len()] as char))
49 }
50 }
51
52 /// Samples `/dev/urandom` and generates a random ASCII string of length `len`
urandom_str(len: usize) -> io::Result<String>53 pub fn urandom_str(len: usize) -> io::Result<String> {
54 File::open("/dev/urandom")?
55 .bytes()
56 .filter_map(uniform_sample_ascii_alphanumeric)
57 .take(len)
58 .collect::<io::Result<String>>()
59 }
60
61 #[cfg(test)]
62 mod tests {
63 use super::*;
64
65 #[test]
check_100()66 fn check_100() {
67 for i in 0..100 {
68 let s = urandom_str(i).unwrap();
69 assert_eq!(s.len(), i);
70 assert!(s.is_ascii());
71 assert!(!s.contains(' '));
72 assert!(!s.contains(|c: char| !c.is_ascii_alphanumeric()));
73 }
74 }
75 }
76