Skip to content

Commit a996cf7

Browse files
committed
Add EntropySource
1 parent 8b36d3b commit a996cf7

File tree

1 file changed

+100
-0
lines changed

1 file changed

+100
-0
lines changed

src/lib.rs

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1006,6 +1006,106 @@ impl Rng for ThreadRng {
10061006
}
10071007
}
10081008

1009+
/// An RNG provided specifically for seeding PRNG's.
1010+
///
1011+
/// `EntropySource` uses the interface for random numbers provided by the
1012+
/// operating system (`OsRng`). If that returns an error, it will fall back to
1013+
/// the `JitterRng` entropy collector. Occasionally it will then check if
1014+
/// `OsRng` is still not available, and switch back if possible.
1015+
#[cfg(feature="std")]
1016+
#[derive(Debug)]
1017+
pub struct EntropySource {
1018+
rng: EntropySourceInner,
1019+
counter: u32,
1020+
}
1021+
1022+
#[cfg(feature="std")]
1023+
#[derive(Debug)]
1024+
enum EntropySourceInner {
1025+
Os(OsRng),
1026+
Jitter(JitterRng),
1027+
}
1028+
1029+
#[cfg(feature="std")]
1030+
impl EntropySource {
1031+
pub fn new() -> Result<Self, Error> {
1032+
match OsRng::new() {
1033+
Ok(r) =>
1034+
Ok(EntropySource { rng: EntropySourceInner::Os(r),
1035+
counter: 0u32 }),
1036+
Err(e1) => {
1037+
match JitterRng::new() {
1038+
Ok(r) =>
1039+
Ok(EntropySource { rng: EntropySourceInner::Jitter(r),
1040+
counter: 0 }),
1041+
Err(_) =>
1042+
Err(Error::with_cause(
1043+
ErrorKind::Unavailable,
1044+
"Both OS and Jitter entropy sources are unavailable",
1045+
e1))
1046+
}
1047+
}
1048+
}
1049+
}
1050+
}
1051+
1052+
#[cfg(feature="std")]
1053+
impl Rng for EntropySource {
1054+
fn next_u32(&mut self) -> u32 {
1055+
impls::next_u32_via_fill(self)
1056+
}
1057+
1058+
fn next_u64(&mut self) -> u64 {
1059+
impls::next_u64_via_fill(self)
1060+
}
1061+
1062+
fn fill_bytes(&mut self, dest: &mut [u8]) {
1063+
self.try_fill_bytes(dest).unwrap();
1064+
}
1065+
1066+
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
1067+
let mut switch_rng = None;
1068+
let mut result;
1069+
match self.rng {
1070+
EntropySourceInner::Os(ref mut rng) => {
1071+
result = rng.try_fill_bytes(dest);
1072+
if result.is_err() {
1073+
// Fall back to JitterRng.
1074+
let mut rng2_result = JitterRng::new();
1075+
if let Ok(mut rng2) = rng2_result {
1076+
result = rng2.try_fill_bytes(dest); // Can't fail
1077+
switch_rng = Some(EntropySourceInner::Jitter(rng2));
1078+
}
1079+
}
1080+
}
1081+
EntropySourceInner::Jitter(ref mut rng) => {
1082+
if self.counter < 8 {
1083+
result = rng.try_fill_bytes(dest); // use JitterRng
1084+
self.counter = (self.counter + 1) % 8;
1085+
} else {
1086+
// Try if OsRng is still unavailable
1087+
let os_rng_result = OsRng::new();
1088+
if let Ok(mut os_rng) = os_rng_result {
1089+
result = os_rng.try_fill_bytes(dest);
1090+
if result.is_ok() {
1091+
switch_rng = Some(EntropySourceInner::Os(os_rng));
1092+
} else {
1093+
result = rng.try_fill_bytes(dest); // use JitterRng
1094+
}
1095+
} else {
1096+
result = rng.try_fill_bytes(dest); // use JitterRng
1097+
}
1098+
}
1099+
}
1100+
}
1101+
if let Some(rng) = switch_rng {
1102+
self.rng = rng;
1103+
self.counter = 0;
1104+
}
1105+
result
1106+
}
1107+
}
1108+
10091109
/// Generates a random value using the thread-local random number generator.
10101110
///
10111111
/// `random()` can generate various types of random things, and so may require

0 commit comments

Comments
 (0)