Skip to content

Commit 400a8fe

Browse files
committed
Add EntropySource
1 parent 45f87af commit 400a8fe

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
@@ -1015,6 +1015,106 @@ impl Rng for ThreadRng {
10151015
}
10161016
}
10171017

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

0 commit comments

Comments
 (0)