Skip to content

Commit 4120882

Browse files
authored
Merge pull request #235 from pitdicker/entropy_source
Add EntropySource wrapper
2 parents 9184bb2 + 1cff629 commit 4120882

File tree

3 files changed

+149
-70
lines changed

3 files changed

+149
-70
lines changed

src/jitter.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -742,7 +742,12 @@ impl Rng for JitterRng {
742742
}
743743

744744
fn fill_bytes(&mut self, dest: &mut [u8]) {
745-
impls::fill_bytes_via_u64(self, dest)
745+
// Fill using `next_u32`. This is faster for filling small slices (four
746+
// bytes or less), while the overhead is negligible.
747+
//
748+
// This is done especially for wrappers that implement `next_u32`
749+
// themselves via `fill_bytes`.
750+
impls::fill_bytes_via_u32(self, dest)
746751
}
747752
}
748753

src/lib.rs

+129-31
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ use prng::Isaac64Rng as IsaacWordRng;
288288

289289
use distributions::{Range, IndependentSample};
290290
use distributions::range::SampleRange;
291-
#[cfg(feature="std")] use reseeding::{ReseedingRng, ReseedWithNew};
291+
#[cfg(feature="std")] use reseeding::ReseedingRng;
292292

293293
// public modules
294294
pub mod distributions;
@@ -844,29 +844,7 @@ pub trait NewRng: SeedableRng {
844844
#[cfg(feature="std")]
845845
impl<R: SeedableRng> NewRng for R {
846846
fn new() -> Result<Self, Error> {
847-
// Note: error handling would be easier with try/catch blocks
848-
fn new_os<T: SeedableRng>() -> Result<T, Error> {
849-
let mut r = OsRng::new()?;
850-
T::from_rng(&mut r)
851-
}
852-
853-
fn new_jitter<T: SeedableRng>() -> Result<T, Error> {
854-
let mut r = JitterRng::new()?;
855-
T::from_rng(&mut r)
856-
}
857-
858-
trace!("Seeding new RNG");
859-
new_os().or_else(|e1| {
860-
warn!("OsRng failed [falling back to JitterRng]: {:?}", e1);
861-
new_jitter().map_err(|_e2| {
862-
warn!("JitterRng failed: {:?}", _e2);
863-
// TODO: can we somehow return both error sources?
864-
Error::with_cause(
865-
ErrorKind::Unavailable,
866-
"seeding a new RNG failed: both OS and Jitter entropy sources failed",
867-
e1)
868-
})
869-
})
847+
R::from_rng(EntropyRng::new())
870848
}
871849
}
872850

@@ -964,20 +942,19 @@ pub fn weak_rng() -> XorShiftRng {
964942
#[cfg(feature="std")]
965943
#[derive(Clone, Debug)]
966944
pub struct ThreadRng {
967-
rng: Rc<RefCell<ReseedingRng<StdRng, ReseedWithNew>>>,
945+
rng: Rc<RefCell<ReseedingRng<StdRng, EntropyRng>>>,
968946
}
969947

970948
#[cfg(feature="std")]
971949
thread_local!(
972-
static THREAD_RNG_KEY: Rc<RefCell<ReseedingRng<StdRng, ReseedWithNew>>> = {
950+
static THREAD_RNG_KEY: Rc<RefCell<ReseedingRng<StdRng, EntropyRng>>> = {
973951
const THREAD_RNG_RESEED_THRESHOLD: u64 = 32_768;
974-
let r = match StdRng::new() {
975-
Ok(r) => r,
976-
Err(e) => panic!("could not initialize thread_rng: {:?}", e)
977-
};
952+
let mut entropy_source = EntropyRng::new();
953+
let r = StdRng::from_rng(&mut entropy_source)
954+
.expect("could not initialize thread_rng");
978955
let rng = ReseedingRng::new(r,
979956
THREAD_RNG_RESEED_THRESHOLD,
980-
ReseedWithNew);
957+
entropy_source);
981958
Rc::new(RefCell::new(rng))
982959
}
983960
);
@@ -1018,6 +995,127 @@ impl Rng for ThreadRng {
1018995
}
1019996
}
1020997

998+
/// An RNG provided specifically for seeding PRNG's.
999+
///
1000+
/// `EntropyRng` uses the interface for random numbers provided by the operating
1001+
/// system ([`OsRng`]). If that returns an error, it will fall back to the
1002+
/// [`JitterRng`] entropy collector. Every time it will then check if `OsRng`
1003+
/// is still not available, and switch back if possible.
1004+
///
1005+
/// [`OsRng`]: os/struct.OsRng.html
1006+
/// [`JitterRng`]: jitter/struct.JitterRng.html
1007+
#[cfg(feature="std")]
1008+
#[derive(Debug)]
1009+
pub struct EntropyRng {
1010+
rng: EntropySource,
1011+
}
1012+
1013+
#[cfg(feature="std")]
1014+
#[derive(Debug)]
1015+
enum EntropySource {
1016+
Os(OsRng),
1017+
Jitter(JitterRng),
1018+
None,
1019+
}
1020+
1021+
#[cfg(feature="std")]
1022+
impl EntropyRng {
1023+
/// Create a new `EntropyRng`.
1024+
///
1025+
/// This method will do no system calls or other initialization routines,
1026+
/// those are done on first use. This is done to make `new` infallible,
1027+
/// and `try_fill_bytes` the only place to report errors.
1028+
pub fn new() -> Self {
1029+
EntropyRng { rng: EntropySource::None }
1030+
}
1031+
}
1032+
1033+
#[cfg(feature="std")]
1034+
impl Rng for EntropyRng {
1035+
fn next_u32(&mut self) -> u32 {
1036+
impls::next_u32_via_fill(self)
1037+
}
1038+
1039+
fn next_u64(&mut self) -> u64 {
1040+
impls::next_u64_via_fill(self)
1041+
}
1042+
1043+
fn fill_bytes(&mut self, dest: &mut [u8]) {
1044+
self.try_fill_bytes(dest).unwrap();
1045+
}
1046+
1047+
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
1048+
fn try_os_new(dest: &mut [u8]) -> Result<OsRng, Error>
1049+
{
1050+
let mut rng = OsRng::new()?;
1051+
rng.try_fill_bytes(dest)?;
1052+
Ok(rng)
1053+
}
1054+
1055+
fn try_jitter_new(dest: &mut [u8]) -> Result<JitterRng, Error>
1056+
{
1057+
let mut rng = JitterRng::new()?;
1058+
rng.try_fill_bytes(dest)?;
1059+
Ok(rng)
1060+
}
1061+
1062+
let mut switch_rng = None;
1063+
match self.rng {
1064+
EntropySource::None => {
1065+
let os_rng_result = try_os_new(dest);
1066+
match os_rng_result {
1067+
Ok(os_rng) => {
1068+
switch_rng = Some(EntropySource::Os(os_rng));
1069+
}
1070+
Err(os_rng_error) => {
1071+
warn!("EntropyRng: OsRng failed [falling back to JitterRng]: {}",
1072+
os_rng_error);
1073+
match try_jitter_new(dest) {
1074+
Ok(jitter_rng) => {
1075+
switch_rng = Some(EntropySource::Jitter(jitter_rng));
1076+
}
1077+
Err(_jitter_error) => {
1078+
warn!("EntropyRng: JitterRng failed: {}",
1079+
_jitter_error);
1080+
return Err(os_rng_error);
1081+
}
1082+
}
1083+
}
1084+
}
1085+
}
1086+
EntropySource::Os(ref mut rng) => {
1087+
let os_rng_result = rng.try_fill_bytes(dest);
1088+
if let Err(os_rng_error) = os_rng_result {
1089+
warn!("EntropyRng: OsRng failed [falling back to JitterRng]: {}",
1090+
os_rng_error);
1091+
match try_jitter_new(dest) {
1092+
Ok(jitter_rng) => {
1093+
switch_rng = Some(EntropySource::Jitter(jitter_rng));
1094+
}
1095+
Err(_jitter_error) => {
1096+
warn!("EntropyRng: JitterRng failed: {}",
1097+
_jitter_error);
1098+
return Err(os_rng_error);
1099+
}
1100+
}
1101+
}
1102+
}
1103+
EntropySource::Jitter(ref mut rng) => {
1104+
if let Ok(os_rng) = try_os_new(dest) {
1105+
info!("EntropyRng: OsRng available [switching back from JitterRng]");
1106+
switch_rng = Some(EntropySource::Os(os_rng));
1107+
} else {
1108+
return rng.try_fill_bytes(dest); // use JitterRng
1109+
}
1110+
}
1111+
}
1112+
if let Some(rng) = switch_rng {
1113+
self.rng = rng;
1114+
}
1115+
Ok(())
1116+
}
1117+
}
1118+
10211119
/// Generates a random value using the thread-local random number generator.
10221120
///
10231121
/// `random()` can generate various types of random things, and so may require

src/reseeding.rs

+14-38
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,7 @@
1111
//! A wrapper around another RNG that reseeds it after it
1212
//! generates a certain number of random bytes.
1313
14-
use {Rng, Error};
15-
#[cfg(feature="std")]
16-
use NewRng;
14+
use {Rng, SeedableRng, Error};
1715

1816
/// A wrapper around any RNG which reseeds the underlying RNG after it
1917
/// has generated a certain number of random bytes.
@@ -23,10 +21,10 @@ pub struct ReseedingRng<R, Rsdr> {
2321
generation_threshold: u64,
2422
bytes_generated: u64,
2523
/// Controls the behaviour when reseeding the RNG.
26-
pub reseeder: Rsdr,
24+
reseeder: Rsdr,
2725
}
2826

29-
impl<R: Rng, Rsdr: Reseeder<R>> ReseedingRng<R, Rsdr> {
27+
impl<R: Rng+SeedableRng, Rsdr: Rng> ReseedingRng<R, Rsdr> {
3028
/// Create a new `ReseedingRng` with the given parameters.
3129
///
3230
/// # Arguments
@@ -48,14 +46,14 @@ impl<R: Rng, Rsdr: Reseeder<R>> ReseedingRng<R, Rsdr> {
4846
pub fn reseed_if_necessary(&mut self) {
4947
if self.bytes_generated >= self.generation_threshold {
5048
trace!("Reseeding RNG after {} bytes", self.bytes_generated);
51-
self.reseeder.reseed(&mut self.rng).unwrap();
49+
R::from_rng(&mut self.reseeder).map(|result| self.rng = result).unwrap();
5250
self.bytes_generated = 0;
5351
}
5452
}
5553
}
5654

5755

58-
impl<R: Rng, Rsdr: Reseeder<R>> Rng for ReseedingRng<R, Rsdr> {
56+
impl<R: Rng+SeedableRng, Rsdr: Rng> Rng for ReseedingRng<R, Rsdr> {
5957
fn next_u32(&mut self) -> u32 {
6058
self.reseed_if_necessary();
6159
self.bytes_generated += 4;
@@ -81,35 +79,10 @@ impl<R: Rng, Rsdr: Reseeder<R>> Rng for ReseedingRng<R, Rsdr> {
8179
}
8280
}
8381

84-
/// Something that can be used to reseed an RNG via `ReseedingRng`.
85-
///
86-
/// Note that implementations should support `Clone` only if reseeding is
87-
/// deterministic (no external entropy source). This is so that a `ReseedingRng`
88-
/// only supports `Clone` if fully deterministic.
89-
pub trait Reseeder<R: ?Sized> {
90-
/// Reseed the given RNG.
91-
///
92-
/// On error, this should just forward the source error; errors are handled
93-
/// by the caller.
94-
fn reseed(&mut self, rng: &mut R) -> Result<(), Error>;
95-
}
96-
97-
/// Reseed an RNG using `NewRng` to replace the current instance.
98-
#[cfg(feature="std")]
99-
#[derive(Debug)]
100-
pub struct ReseedWithNew;
101-
102-
#[cfg(feature="std")]
103-
impl<R: Rng + NewRng> Reseeder<R> for ReseedWithNew {
104-
fn reseed(&mut self, rng: &mut R) -> Result<(), Error> {
105-
R::new().map(|result| *rng = result)
106-
}
107-
}
108-
10982
#[cfg(test)]
11083
mod test {
11184
use {impls, le};
112-
use super::{ReseedingRng, Reseeder};
85+
use super::{ReseedingRng};
11386
use {SeedableRng, Rng, Error};
11487

11588
struct Counter {
@@ -140,17 +113,20 @@ mod test {
140113
}
141114

142115
#[derive(Debug, Clone)]
143-
struct ReseedCounter;
144-
impl Reseeder<Counter> for ReseedCounter {
145-
fn reseed(&mut self, rng: &mut Counter) -> Result<(), Error> {
146-
*rng = Counter { i: 0 };
116+
struct ResetCounter;
117+
impl Rng for ResetCounter {
118+
fn next_u32(&mut self) -> u32 { unimplemented!() }
119+
fn next_u64(&mut self) -> u64 { unimplemented!() }
120+
fn fill_bytes(&mut self, _dest: &mut [u8]) { unimplemented!() }
121+
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
122+
for i in dest.iter_mut() { *i = 0; }
147123
Ok(())
148124
}
149125
}
150126

151127
#[test]
152128
fn test_reseeding() {
153-
let mut rs = ReseedingRng::new(Counter {i:0}, 400, ReseedCounter);
129+
let mut rs = ReseedingRng::new(Counter {i:0}, 400, ResetCounter);
154130

155131
let mut i = 0;
156132
for _ in 0..1000 {

0 commit comments

Comments
 (0)