rand_otp/
lib.rs

1#![allow(clippy::unreadable_literal)]
2
3pub struct Otp<R>(R);
4
5/** A "random number generator" which reads from a source.
6
7	This is useful for crypto with a one-time-pad or for situations where you want to control the sequence of generated numbers.
8
9	Integers read from this RNG are read little-endian from the underlying source.
10*/
11impl<R: std::io::Read> Otp<R> {
12	pub fn new(source: R) -> Self {
13		Self(source)
14	}
15}
16
17impl<R: std::io::Read> rand::RngCore for Otp<R> {
18	fn next_u32(&mut self) -> u32 {
19		let mut buf = [0; 4];
20		self.0.read_exact(&mut buf).unwrap();
21		u32::from_le_bytes(buf)
22	}
23
24	fn next_u64(&mut self) -> u64 {
25		let mut buf = [0; 8];
26		self.0.read_exact(&mut buf).unwrap();
27		u64::from_le_bytes(buf)
28	}
29
30	fn fill_bytes(&mut self, buf: &mut [u8]) {
31		self.0.read_exact(buf).unwrap();
32	}
33
34	fn try_fill_bytes(&mut self, buf: &mut [u8]) -> Result<(), rand::Error> {
35		self.0.read(buf)
36			.and_then(|len| match len {
37				0 => Err(std::io::ErrorKind::UnexpectedEof.into()),
38				_ => Ok(()),
39			})
40			.map_err(rand::Error::new)
41	}
42}
43
44#[test]
45fn test_basic() {
46	use rand::RngCore;
47
48	let buf = [
49		1, 2, 3, 4,
50		4, 3, 2, 1,
51		8, 9, 10, 11, 12, 13, 14, 15,
52		255,
53	];
54
55	let mut rng = Otp::new(&buf[..]);
56	assert_eq!(rng.next_u32(), 0x04030201);
57	assert_eq!(rng.next_u32(), 0x01020304);
58
59	assert_eq!(rng.next_u64(), 0x0F0E0D0C0B0A0908);
60
61	let mut out = [0; 1];
62	rng.fill_bytes(&mut out);
63	assert_eq!(out, [255]);
64
65	assert!(rng.try_fill_bytes(&mut out).is_err());
66}
67
68#[test]
69fn test_urandom() {
70	use rand::RngCore;
71
72	let f = std::fs::File::open("/dev/urandom").unwrap();
73
74	let mut rng = Otp::new(f);
75	rng.next_u32();
76	rng.next_u64();
77	rng.fill_bytes(&mut [0; 32]);
78	assert!(rng.try_fill_bytes(&mut [0; 32]).is_ok());
79}
80
81#[test]
82#[should_panic(expected = "UnexpectedEof")]
83fn test_eof() {
84	use rand::RngCore;
85
86	let buf = [
87		1, 2, 3, 4,
88	];
89
90	Otp::new(&buf[..]).next_u64();
91}