1 // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 // SPDX-License-Identifier: BSD-3-Clause
3 
4 //! Miscellaneous functions related to getting (pseudo) random numbers and
5 //! strings.
6 //!
7 //! NOTE! This should not be used when you do need __real__ random numbers such
8 //! as for encryption but will probably be suitable when you want locally
9 //! unique ID's that will not be shared over the network.
10 
11 use std::ffi::OsString;
12 use std::str;
13 
14 /// Gets an ever increasing u64 (at least for this process).
15 ///
16 /// The number retrieved will be based upon the time of the last reboot (x86_64)
17 /// and something undefined for other architectures.
timestamp_cycles() -> u6418 pub fn timestamp_cycles() -> u64 {
19     #[cfg(target_arch = "x86_64")]
20     // SAFETY: Safe because there's nothing that can go wrong with this call.
21     unsafe {
22         std::arch::x86_64::_rdtsc()
23     }
24 
25     #[cfg(not(target_arch = "x86_64"))]
26     {
27         const MONOTONIC_CLOCK_MULTPIPLIER: u64 = 1_000_000_000;
28 
29         let mut ts = libc::timespec {
30             tv_sec: 0,
31             tv_nsec: 0,
32         };
33         // SAFETY: We initialized the parameters correctly and we trust the function.
34         unsafe {
35             libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut ts);
36         }
37         (ts.tv_sec as u64) * MONOTONIC_CLOCK_MULTPIPLIER + (ts.tv_nsec as u64)
38     }
39 }
40 
41 /// Generate pseudo random u32 numbers based on the current timestamp.
xor_pseudo_rng_u32() -> u3242 pub fn xor_pseudo_rng_u32() -> u32 {
43     let mut t: u32 = timestamp_cycles() as u32;
44     // Taken from https://en.wikipedia.org/wiki/Xorshift
45     t ^= t << 13;
46     t ^= t >> 17;
47     t ^ (t << 5)
48 }
49 
50 // This will get an array of numbers that can safely be converted to strings
51 // because they will be in the range [a-zA-Z0-9]. The return vector could be any
52 // size between 0 and 4.
xor_pseudo_rng_u8_alphanumerics(rand_fn: &dyn Fn() -> u32) -> Vec<u8>53 fn xor_pseudo_rng_u8_alphanumerics(rand_fn: &dyn Fn() -> u32) -> Vec<u8> {
54     rand_fn()
55         .to_ne_bytes()
56         .to_vec()
57         .drain(..)
58         .filter(|val| {
59             (48..=57).contains(val) || (65..=90).contains(val) || (97..=122).contains(val)
60         })
61         .collect()
62 }
63 
xor_pseudo_rng_u8_bytes(rand_fn: &dyn Fn() -> u32) -> Vec<u8>64 fn xor_pseudo_rng_u8_bytes(rand_fn: &dyn Fn() -> u32) -> Vec<u8> {
65     rand_fn().to_ne_bytes().to_vec()
66 }
67 
rand_alphanumerics_impl(rand_fn: &dyn Fn() -> u32, len: usize) -> OsString68 fn rand_alphanumerics_impl(rand_fn: &dyn Fn() -> u32, len: usize) -> OsString {
69     let mut buf = OsString::new();
70     let mut done = 0;
71     loop {
72         for n in xor_pseudo_rng_u8_alphanumerics(rand_fn) {
73             done += 1;
74             buf.push(str::from_utf8(&[n]).unwrap_or("_"));
75             if done >= len {
76                 return buf;
77             }
78         }
79     }
80 }
81 
rand_bytes_impl(rand_fn: &dyn Fn() -> u32, len: usize) -> Vec<u8>82 fn rand_bytes_impl(rand_fn: &dyn Fn() -> u32, len: usize) -> Vec<u8> {
83     let mut buf: Vec<Vec<u8>> = Vec::new();
84     let mut num = if len % 4 == 0 { len / 4 } else { len / 4 + 1 };
85     while num > 0 {
86         buf.push(xor_pseudo_rng_u8_bytes(rand_fn));
87         num -= 1;
88     }
89     buf.into_iter().flatten().take(len).collect()
90 }
91 
92 /// Gets a pseudo random OsString of length `len` with characters in the
93 /// range [a-zA-Z0-9].
rand_alphanumerics(len: usize) -> OsString94 pub fn rand_alphanumerics(len: usize) -> OsString {
95     rand_alphanumerics_impl(&xor_pseudo_rng_u32, len)
96 }
97 
98 /// Get a pseudo random vector of `len` bytes.
rand_bytes(len: usize) -> Vec<u8>99 pub fn rand_bytes(len: usize) -> Vec<u8> {
100     rand_bytes_impl(&xor_pseudo_rng_u32, len)
101 }
102 
103 #[cfg(test)]
104 mod tests {
105     use super::*;
106 
107     #[test]
test_timestamp_cycles()108     fn test_timestamp_cycles() {
109         for _ in 0..1000 {
110             assert!(timestamp_cycles() < timestamp_cycles());
111         }
112     }
113 
114     #[test]
test_xor_pseudo_rng_u32()115     fn test_xor_pseudo_rng_u32() {
116         for _ in 0..1000 {
117             assert_ne!(xor_pseudo_rng_u32(), xor_pseudo_rng_u32());
118         }
119     }
120 
121     #[test]
test_xor_pseudo_rng_u8_alphas()122     fn test_xor_pseudo_rng_u8_alphas() {
123         let i = 3612982; // 55 (shifted 16 places), 33 (shifted 8 places), 54...
124                          // The 33 will be discarded as it is not a valid letter
125                          // (upper or lower) or number.
126         let s = xor_pseudo_rng_u8_alphanumerics(&|| i);
127         if cfg!(target_endian = "big") {
128             assert_eq!(vec![55, 54], s);
129         } else {
130             assert_eq!(vec![54, 55], s);
131         }
132     }
133 
134     #[test]
test_rand_alphanumerics_impl()135     fn test_rand_alphanumerics_impl() {
136         let s = rand_alphanumerics_impl(&|| 14134, 5);
137         if cfg!(target_endian = "big") {
138             assert_eq!("76767", s);
139         } else {
140             assert_eq!("67676", s);
141         }
142     }
143 
144     #[test]
test_rand_alphanumerics()145     fn test_rand_alphanumerics() {
146         let s = rand_alphanumerics(5);
147         assert_eq!(5, s.len());
148     }
149 
150     #[test]
test_xor_pseudo_rng_u8_bytes()151     fn test_xor_pseudo_rng_u8_bytes() {
152         let i = 3612982; // 55 (shifted 16 places), 33 (shifted 8 places), 54...
153                          // The 33 will be discarded as it is not a valid letter
154                          // (upper or lower) or number.
155         let s = xor_pseudo_rng_u8_bytes(&|| i);
156         if cfg!(target_endian = "big") {
157             assert_eq!(vec![0, 55, 33, 54], s);
158         } else {
159             assert_eq!(vec![54, 33, 55, 0], s);
160         }
161     }
162 
163     #[test]
test_rand_bytes_impl()164     fn test_rand_bytes_impl() {
165         let s = rand_bytes_impl(&|| 1234567, 4);
166         if cfg!(target_endian = "big") {
167             assert_eq!(vec![0, 18, 214, 135], s);
168         } else {
169             assert_eq!(vec![135, 214, 18, 0], s);
170         }
171     }
172 
173     #[test]
test_rand_bytes()174     fn test_rand_bytes() {
175         for i in 0..8 {
176             assert_eq!(i, rand_bytes(i).len());
177         }
178     }
179 }
180