// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: BSD-3-Clause //! Miscellaneous functions related to getting (pseudo) random numbers and //! strings. //! //! NOTE! This should not be used when you do need __real__ random numbers such //! as for encryption but will probably be suitable when you want locally //! unique ID's that will not be shared over the network. use std::ffi::OsString; use std::str; /// Gets an ever increasing u64 (at least for this process). /// /// The number retrieved will be based upon the time of the last reboot (x86_64) /// and something undefined for other architectures. pub fn timestamp_cycles() -> u64 { #[cfg(target_arch = "x86_64")] // SAFETY: Safe because there's nothing that can go wrong with this call. unsafe { std::arch::x86_64::_rdtsc() } #[cfg(not(target_arch = "x86_64"))] { const MONOTONIC_CLOCK_MULTPIPLIER: u64 = 1_000_000_000; let mut ts = libc::timespec { tv_sec: 0, tv_nsec: 0, }; // SAFETY: We initialized the parameters correctly and we trust the function. unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut ts); } (ts.tv_sec as u64) * MONOTONIC_CLOCK_MULTPIPLIER + (ts.tv_nsec as u64) } } /// Generate pseudo random u32 numbers based on the current timestamp. pub fn xor_pseudo_rng_u32() -> u32 { let mut t: u32 = timestamp_cycles() as u32; // Taken from https://en.wikipedia.org/wiki/Xorshift t ^= t << 13; t ^= t >> 17; t ^ (t << 5) } // This will get an array of numbers that can safely be converted to strings // because they will be in the range [a-zA-Z0-9]. The return vector could be any // size between 0 and 4. fn xor_pseudo_rng_u8_alphanumerics(rand_fn: &dyn Fn() -> u32) -> Vec { rand_fn() .to_ne_bytes() .to_vec() .drain(..) .filter(|val| { (48..=57).contains(val) || (65..=90).contains(val) || (97..=122).contains(val) }) .collect() } fn xor_pseudo_rng_u8_bytes(rand_fn: &dyn Fn() -> u32) -> Vec { rand_fn().to_ne_bytes().to_vec() } fn rand_alphanumerics_impl(rand_fn: &dyn Fn() -> u32, len: usize) -> OsString { let mut buf = OsString::new(); let mut done = 0; loop { for n in xor_pseudo_rng_u8_alphanumerics(rand_fn) { done += 1; buf.push(str::from_utf8(&[n]).unwrap_or("_")); if done >= len { return buf; } } } } fn rand_bytes_impl(rand_fn: &dyn Fn() -> u32, len: usize) -> Vec { let mut buf: Vec> = Vec::new(); let mut num = if len % 4 == 0 { len / 4 } else { len / 4 + 1 }; while num > 0 { buf.push(xor_pseudo_rng_u8_bytes(rand_fn)); num -= 1; } buf.into_iter().flatten().take(len).collect() } /// Gets a pseudo random OsString of length `len` with characters in the /// range [a-zA-Z0-9]. pub fn rand_alphanumerics(len: usize) -> OsString { rand_alphanumerics_impl(&xor_pseudo_rng_u32, len) } /// Get a pseudo random vector of `len` bytes. pub fn rand_bytes(len: usize) -> Vec { rand_bytes_impl(&xor_pseudo_rng_u32, len) } #[cfg(test)] mod tests { use super::*; #[test] fn test_timestamp_cycles() { for _ in 0..1000 { assert!(timestamp_cycles() < timestamp_cycles()); } } #[test] fn test_xor_pseudo_rng_u32() { for _ in 0..1000 { assert_ne!(xor_pseudo_rng_u32(), xor_pseudo_rng_u32()); } } #[test] fn test_xor_pseudo_rng_u8_alphas() { let i = 3612982; // 55 (shifted 16 places), 33 (shifted 8 places), 54... // The 33 will be discarded as it is not a valid letter // (upper or lower) or number. let s = xor_pseudo_rng_u8_alphanumerics(&|| i); if cfg!(target_endian = "big") { assert_eq!(vec![55, 54], s); } else { assert_eq!(vec![54, 55], s); } } #[test] fn test_rand_alphanumerics_impl() { let s = rand_alphanumerics_impl(&|| 14134, 5); if cfg!(target_endian = "big") { assert_eq!("76767", s); } else { assert_eq!("67676", s); } } #[test] fn test_rand_alphanumerics() { let s = rand_alphanumerics(5); assert_eq!(5, s.len()); } #[test] fn test_xor_pseudo_rng_u8_bytes() { let i = 3612982; // 55 (shifted 16 places), 33 (shifted 8 places), 54... // The 33 will be discarded as it is not a valid letter // (upper or lower) or number. let s = xor_pseudo_rng_u8_bytes(&|| i); if cfg!(target_endian = "big") { assert_eq!(vec![0, 55, 33, 54], s); } else { assert_eq!(vec![54, 33, 55, 0], s); } } #[test] fn test_rand_bytes_impl() { let s = rand_bytes_impl(&|| 1234567, 4); if cfg!(target_endian = "big") { assert_eq!(vec![0, 18, 214, 135], s); } else { assert_eq!(vec![135, 214, 18, 0], s); } } #[test] fn test_rand_bytes() { for i in 0..8 { assert_eq!(i, rand_bytes(i).len()); } } }