use std::io::Read;
pub fn bytes(length: usize) -> Vec<u8> {
let mut buffer = vec![0u8; length];
if let Ok(mut source) = std::fs::File::open("/dev/urandom")
&& source.read_exact(&mut buffer).is_ok()
{
return buffer;
}
panic!(
"could not read /dev/urandom, and there is no safe substitute. Every secret this \
framework makes — session ids, CSRF tokens, API tokens, password salts, encryption \
nonces, APP_KEY — comes from here, so it stops rather than issue one that can be \
guessed. Check the file-descriptor limit and that /dev is mounted."
);
}
pub fn hex(length: usize) -> String {
let mut out = String::with_capacity(length * 2);
for byte in bytes(length) {
out.push(char::from_digit((byte >> 4) as u32, 16).unwrap_or('0'));
out.push(char::from_digit((byte & 0x0f) as u32, 16).unwrap_or('0'));
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn returns_the_requested_number_of_bytes() {
assert_eq!(bytes(32).len(), 32);
assert_eq!(bytes(0).len(), 0);
}
#[test]
fn hex_output_is_twice_as_long_and_lowercase_hexadecimal() {
let value = hex(32);
assert_eq!(value.len(), 64);
assert!(value.chars().all(|c| c.is_ascii_hexdigit() && !c.is_ascii_uppercase()));
}
#[test]
fn two_draws_of_the_same_length_differ() {
assert_ne!(hex(32), hex(32));
}
#[test]
fn there_is_no_substitute_for_the_operating_systems_randomness() {
let source = include_str!("random.rs");
let code: String = source
.split("#[cfg(test)]")
.next()
.expect("there is code above the tests")
.lines()
.filter(|line| !line.trim_start().starts_with("//"))
.collect::<Vec<_>>()
.join("\n");
for banned in ["xorshift", "SystemTime::now", "Box::into_raw", "fn fallback"] {
assert!(
!code.contains(banned),
"`{banned}` is back in the entropy path. Every secret the framework issues comes \
from this function; a clock- or address-seeded value here is a guessable \
session id, and the failure is silent."
);
}
}
}