use std::io::{self, BufWriter, ErrorKind, Write};
use cryptography::{
Camellia128, Cast128, Grasshopper, Rabbit, Salsa20, Seed as SeedCipher, Serpent128, Sm4,
Snow3g, Twofish128, Zuc128,
};
use entropy::rng::{
AesCtr, BlockCtrRng, BsdRandCompat, BsdRandom, ChaCha20Rng, ConstantRng, CounterRng,
CryptoCtrDrbg, DualEcDrbg, HashDrbg, HmacDrbg, Jsf64, Lcg32, LcgVariant, LinuxLibcRandom,
Mt19937, OsRng, Pcg32, Pcg64, Rand48, Rng, Sfc64, SpongeBob, Squidward, StreamRng,
SystemVRand, WindowsDotNetRandom, WindowsMsvcRand, WindowsVb6Rnd, WyRand, Xoroshiro128,
Xorshift32, Xorshift64, Xoshiro256,
};
use entropy::seed::{IV16, IV8, K16, K32};
pub const NAMES: &[&str] = &[
"osrng",
"mt19937",
"xorshift32",
"xorshift64",
"sysv_rand",
"rand48",
"bsd_random",
"linux_glibc_random",
"bsd_rand_compat",
"windows_msvc_rand",
"windows_vb6_rnd",
"windows_dotnet_random",
"ansi_c_lcg",
"lcg_minstd",
"borland_lcg",
"msvc_lcg",
"aes_ctr",
"camellia_ctr",
"twofish_ctr",
"serpent_ctr",
"sm4_ctr",
"grasshopper_ctr",
"cast128_ctr",
"seed_ctr",
"rabbit",
"salsa20",
"snow3g",
"zuc128",
"spongebob",
"squidward",
"pcg32",
"pcg64",
"xoshiro256",
"xoroshiro128",
"wyrand",
"sfc64",
"jsf64",
"chacha20",
"hmac_drbg",
"hash_drbg",
"crypto_ctr_drbg",
"dual_ec_p256",
"constant",
"counter",
];
fn dump<R: Rng>(mut rng: R, n: u64) -> io::Result<()> {
let stdout = io::stdout();
let mut out = BufWriter::with_capacity(1 << 20, stdout.lock());
for _ in 0..n {
out.write_all(&rng.next_u32().to_le_bytes())?;
}
out.flush()
}
fn dispatch(name: &str, n: u64) -> Result<io::Result<()>, ()> {
let r = match name {
"osrng" => dump(OsRng::new(), n),
"mt19937" => dump(Mt19937::new(19650218), n),
"xorshift64" => dump(Xorshift64::new(1), n),
"xorshift32" => dump(Xorshift32::new(1), n),
"sysv_rand" => dump(SystemVRand::new(1), n),
"rand48" => dump(Rand48::new(1), n),
"bsd_random" => dump(BsdRandom::new(1), n),
"linux_glibc_random" => dump(LinuxLibcRandom::new(1), n),
"bsd_rand_compat" => dump(BsdRandCompat::new(1), n),
"windows_msvc_rand" => dump(WindowsMsvcRand::new(1), n),
"windows_vb6_rnd" => dump(WindowsVb6Rnd::new(1), n),
"windows_dotnet_random" => dump(WindowsDotNetRandom::new(1), n),
"ansi_c_lcg" => dump(Lcg32::ansi_c(), n),
"lcg_minstd" => dump(Lcg32::minstd(), n),
"borland_lcg" => dump(Lcg32::new(LcgVariant::Borland, 1), n),
"msvc_lcg" => dump(Lcg32::new(LcgVariant::Msvc, 1), n),
"aes_ctr" => dump(AesCtr::with_nist_key(), n),
"camellia_ctr" => dump(BlockCtrRng::new(Camellia128::new(&K16), 0), n),
"twofish_ctr" => dump(BlockCtrRng::new(Twofish128::new(&K16), 0), n),
"serpent_ctr" => dump(BlockCtrRng::new(Serpent128::new(&K16), 0), n),
"sm4_ctr" => dump(BlockCtrRng::new(Sm4::new(&K16), 0), n),
"grasshopper_ctr" => dump(BlockCtrRng::new(Grasshopper::new(&K32), 0), n),
"cast128_ctr" => dump(BlockCtrRng::new(Cast128::new(&K16), 0), n),
"seed_ctr" => dump(BlockCtrRng::new(SeedCipher::new(&K16), 0), n),
"rabbit" => dump(StreamRng::new(Rabbit::new(&K16, &IV8)), n),
"salsa20" => dump(StreamRng::new(Salsa20::new(&K32, &IV8)), n),
"snow3g" => dump(StreamRng::new(Snow3g::new(&K16, &IV16)), n),
"zuc128" => dump(StreamRng::new(Zuc128::new(&K16, &IV16)), n),
"spongebob" => dump(SpongeBob::with_test_seed(), n),
"squidward" => dump(Squidward::with_test_seed(), n),
"pcg32" => dump(Pcg32::new(42, 54), n),
"pcg64" => dump(Pcg64::new(1, 1), n),
"xoshiro256" => dump(Xoshiro256::new(1, 2, 3, 4), n),
"xoroshiro128" => dump(Xoroshiro128::new(1, 2), n),
"wyrand" => dump(WyRand::new(42), n),
"sfc64" => dump(Sfc64::new(1, 2, 3), n),
"jsf64" => dump(Jsf64::new(0xdead_beef), n),
"chacha20" => dump(ChaCha20Rng::from_os_rng(), n),
"hmac_drbg" => dump(HmacDrbg::from_os_rng(), n),
"hash_drbg" => dump(HashDrbg::from_os_rng(), n),
"crypto_ctr_drbg" => dump(CryptoCtrDrbg::with_test_seed(), n),
"dual_ec_p256" => dump(DualEcDrbg::p256(b"entropy-r-report"), n),
"constant" => dump(ConstantRng::new(0xDEAD_DEAD), n),
"counter" => dump(CounterRng::new(0), n),
_ => return Err(()),
};
Ok(r)
}
fn print_usage(stream: &mut dyn Write) {
let _ = writeln!(
stream,
"usage: dump_rng <name> <count>\n dump_rng --list\n dump_rng --help",
);
}
fn main() {
let argv: Vec<String> = std::env::args().skip(1).collect();
if argv.iter().any(|a| a == "--help" || a == "-h") {
print_usage(&mut io::stdout());
return;
}
if argv.iter().any(|a| a == "--list") {
let stdout = io::stdout();
let mut out = stdout.lock();
for n in NAMES {
let _ = writeln!(out, "{n}");
}
return;
}
if argv.len() != 2 {
print_usage(&mut io::stderr());
std::process::exit(2);
}
let name = argv[0].to_ascii_lowercase();
let count: u64 = match argv[1].parse() {
Ok(c) => c,
Err(_) => {
eprintln!("dump_rng: count must be a non-negative integer, got {:?}", argv[1]);
std::process::exit(2);
}
};
match dispatch(&name, count) {
Ok(Ok(())) => {}
Ok(Err(e)) if e.kind() == ErrorKind::BrokenPipe => {
}
Ok(Err(e)) => {
eprintln!("dump_rng: write error: {e}");
std::process::exit(1);
}
Err(()) => {
eprintln!("dump_rng: unknown RNG: {name}");
eprintln!("hint: dump_rng --list");
std::process::exit(2);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn names_are_unique_and_sorted_meaningfully() {
let mut sorted = NAMES.to_vec();
sorted.sort();
sorted.dedup();
assert_eq!(sorted.len(), NAMES.len(), "duplicate name in NAMES");
}
#[test]
fn every_name_is_dispatchable_with_zero_words() {
for &n in NAMES {
match dispatch(n, 0) {
Ok(Ok(())) => {}
Ok(Err(e)) => panic!("dispatch({n}, 0) failed I/O: {e}"),
Err(()) => panic!("dispatch({n}, 0) returned UnknownName"),
}
}
}
#[test]
fn dispatch_rejects_unknown_name() {
assert!(dispatch("not_a_real_rng", 0).is_err());
}
}