Skip to content

Commit c9d35fc

Browse files
committed
Rewrite EntropyRng
1 parent 2a18a4d commit c9d35fc

File tree

1 file changed

+189
-70
lines changed

1 file changed

+189
-70
lines changed

src/rngs/entropy.rs

Lines changed: 189 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@
1010

1111
//! Entropy generator, or wrapper around external generators
1212
13-
use rand_core::{RngCore, CryptoRng, Error, impls};
14-
use rngs::{OsRng, JitterRng};
13+
use rand_core::{RngCore, CryptoRng, Error, ErrorKind, impls};
14+
#[allow(unused)]
15+
use rngs;
1516

1617
/// An interface returning random data from external source(s), provided
1718
/// specifically for securely seeding algorithmic generators (PRNGs).
@@ -46,13 +47,14 @@ use rngs::{OsRng, JitterRng};
4647
/// [`try_fill_bytes`]: ../trait.RngCore.html#method.tymethod.try_fill_bytes
4748
#[derive(Debug)]
4849
pub struct EntropyRng {
49-
rng: EntropySource,
50+
source: Source,
5051
}
5152

5253
#[derive(Debug)]
53-
enum EntropySource {
54-
Os(OsRng),
55-
Jitter(JitterRng),
54+
enum Source {
55+
Os(Os),
56+
Custom(Custom),
57+
Jitter(Jitter),
5658
None,
5759
}
5860

@@ -63,7 +65,7 @@ impl EntropyRng {
6365
/// those are done on first use. This is done to make `new` infallible,
6466
/// and `try_fill_bytes` the only place to report errors.
6567
pub fn new() -> Self {
66-
EntropyRng { rng: EntropySource::None }
68+
EntropyRng { source: Source::None }
6769
}
6870
}
6971

@@ -88,82 +90,199 @@ impl RngCore for EntropyRng {
8890
}
8991

9092
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
91-
fn try_os_new(dest: &mut [u8]) -> Result<OsRng, Error>
92-
{
93-
let mut rng = OsRng::new()?;
94-
rng.try_fill_bytes(dest)?;
95-
Ok(rng)
96-
}
93+
let mut reported_error = None;
9794

98-
fn try_jitter_new(dest: &mut [u8]) -> Result<JitterRng, Error>
99-
{
100-
let mut rng = JitterRng::new()?;
101-
rng.try_fill_bytes(dest)?;
102-
Ok(rng)
95+
if let Source::Os(ref mut os_rng) = self.source {
96+
match os_rng.fill(dest) {
97+
Ok(()) => return Ok(()),
98+
Err(err) => {
99+
warn!("EntropyRng: OsRng failed \
100+
[trying other entropy sources]: {}", err);
101+
reported_error = Some(err);
102+
},
103+
}
104+
} else if Os::is_supported() {
105+
match Os::new_and_fill(dest) {
106+
Ok(os_rng) => {
107+
debug!("EntropyRng: using OsRng");
108+
self.source = Source::Os(os_rng);
109+
return Ok(());
110+
},
111+
Err(err) => { reported_error = reported_error.or(Some(err)) },
112+
}
103113
}
104114

105-
let mut switch_rng = None;
106-
match self.rng {
107-
EntropySource::None => {
108-
let os_rng_result = try_os_new(dest);
109-
match os_rng_result {
110-
Ok(os_rng) => {
111-
debug!("EntropyRng: using OsRng");
112-
switch_rng = Some(EntropySource::Os(os_rng));
113-
}
114-
Err(os_rng_error) => {
115-
warn!("EntropyRng: OsRng failed [falling back to JitterRng]: {}",
116-
os_rng_error);
117-
match try_jitter_new(dest) {
118-
Ok(jitter_rng) => {
119-
debug!("EntropyRng: using JitterRng");
120-
switch_rng = Some(EntropySource::Jitter(jitter_rng));
121-
}
122-
Err(_jitter_error) => {
123-
warn!("EntropyRng: JitterRng failed: {}",
124-
_jitter_error);
125-
return Err(os_rng_error);
126-
}
127-
}
128-
}
129-
}
115+
if let Source::Custom(ref mut rng) = self.source {
116+
match rng.fill(dest) {
117+
Ok(()) => return Ok(()),
118+
Err(err) => {
119+
warn!("EntropyRng: custom entropy source failed \
120+
[trying other entropy sources]: {}", err);
121+
reported_error = Some(err);
122+
},
130123
}
131-
EntropySource::Os(ref mut rng) => {
132-
let os_rng_result = rng.try_fill_bytes(dest);
133-
if let Err(os_rng_error) = os_rng_result {
134-
warn!("EntropyRng: OsRng failed [falling back to JitterRng]: {}",
135-
os_rng_error);
136-
match try_jitter_new(dest) {
137-
Ok(jitter_rng) => {
138-
debug!("EntropyRng: using JitterRng");
139-
switch_rng = Some(EntropySource::Jitter(jitter_rng));
140-
}
141-
Err(_jitter_error) => {
142-
warn!("EntropyRng: JitterRng failed: {}",
143-
_jitter_error);
144-
return Err(os_rng_error);
145-
}
146-
}
147-
}
124+
} else if Custom::is_supported() {
125+
match Custom::new_and_fill(dest) {
126+
Ok(custom) => {
127+
debug!("EntropyRng: using custom entropy source");
128+
self.source = Source::Custom(custom);
129+
return Ok(());
130+
},
131+
Err(err) => { reported_error = reported_error.or(Some(err)) },
148132
}
149-
EntropySource::Jitter(ref mut rng) => {
150-
if let Ok(os_rng) = try_os_new(dest) {
151-
debug!("EntropyRng: using OsRng");
152-
switch_rng = Some(EntropySource::Os(os_rng));
153-
} else {
154-
return rng.try_fill_bytes(dest); // use JitterRng
155-
}
133+
}
134+
135+
if let Source::Jitter(ref mut jitter_rng) = self.source {
136+
match jitter_rng.fill(dest) {
137+
Ok(()) => return Ok(()),
138+
Err(err) => {
139+
warn!("EntropyRng: JitterRng failed: {}", err);
140+
reported_error = Some(err);
141+
},
142+
}
143+
} else if Jitter::is_supported() {
144+
match Jitter::new_and_fill(dest) {
145+
Ok(jitter_rng) => {
146+
debug!("EntropyRng: using JitterRng");
147+
self.source = Source::Jitter(jitter_rng);
148+
return Ok(());
149+
},
150+
Err(err) => { reported_error = reported_error.or(Some(err)) },
156151
}
157152
}
158-
if let Some(rng) = switch_rng {
159-
self.rng = rng;
153+
154+
if let Some(err) = reported_error {
155+
Err(Error::with_cause(ErrorKind::Unavailable,
156+
"All entropy sources failed",
157+
err))
158+
} else {
159+
Err(Error::new(ErrorKind::Unavailable,
160+
"No entropy sources available"))
160161
}
161-
Ok(())
162162
}
163163
}
164164

165165
impl CryptoRng for EntropyRng {}
166166

167+
168+
169+
trait EntropySource {
170+
fn new_and_fill(dest: &mut [u8]) -> Result<Self, Error>
171+
where Self: Sized;
172+
173+
fn fill(&mut self, dest: &mut [u8]) -> Result<(), Error>;
174+
175+
fn is_supported() -> bool { true }
176+
}
177+
178+
#[allow(unused)]
179+
#[derive(Clone, Debug)]
180+
struct NoSource;
181+
182+
#[allow(unused)]
183+
impl EntropySource for NoSource {
184+
fn new_and_fill(dest: &mut [u8]) -> Result<Self, Error> {
185+
Err(Error::new(ErrorKind::Unavailable, "Source not supported"))
186+
}
187+
188+
fn fill(&mut self, dest: &mut [u8]) -> Result<(), Error> {
189+
unreachable!()
190+
}
191+
192+
fn is_supported() -> bool { false }
193+
}
194+
195+
196+
#[cfg(all(feature="std",
197+
any(target_os = "linux", target_os = "android",
198+
target_os = "netbsd",
199+
target_os = "dragonfly",
200+
target_os = "haiku",
201+
target_os = "emscripten",
202+
target_os = "solaris",
203+
target_os = "cloudabi",
204+
target_os = "macos", target_os = "ios",
205+
target_os = "freebsd",
206+
target_os = "openbsd", target_os = "bitrig",
207+
target_os = "redox",
208+
target_os = "fuchsia",
209+
windows,
210+
all(target_arch = "wasm32", feature = "stdweb")
211+
)))]
212+
#[derive(Clone, Debug)]
213+
pub struct Os(rngs::OsRng);
214+
215+
#[cfg(all(feature="std",
216+
any(target_os = "linux", target_os = "android",
217+
target_os = "netbsd",
218+
target_os = "dragonfly",
219+
target_os = "haiku",
220+
target_os = "emscripten",
221+
target_os = "solaris",
222+
target_os = "cloudabi",
223+
target_os = "macos", target_os = "ios",
224+
target_os = "freebsd",
225+
target_os = "openbsd", target_os = "bitrig",
226+
target_os = "redox",
227+
target_os = "fuchsia",
228+
windows,
229+
all(target_arch = "wasm32", feature = "stdweb")
230+
)))]
231+
impl EntropySource for Os {
232+
fn new_and_fill(dest: &mut [u8]) -> Result<Self, Error> {
233+
let mut rng = rngs::OsRng::new()?;
234+
rng.try_fill_bytes(dest)?;
235+
Ok(Os(rng))
236+
}
237+
238+
fn fill(&mut self, dest: &mut [u8]) -> Result<(), Error> {
239+
self.0.try_fill_bytes(dest)
240+
}
241+
}
242+
243+
#[cfg(not(all(feature="std",
244+
any(target_os = "linux", target_os = "android",
245+
target_os = "netbsd",
246+
target_os = "dragonfly",
247+
target_os = "haiku",
248+
target_os = "emscripten",
249+
target_os = "solaris",
250+
target_os = "cloudabi",
251+
target_os = "macos", target_os = "ios",
252+
target_os = "freebsd",
253+
target_os = "openbsd", target_os = "bitrig",
254+
target_os = "redox",
255+
target_os = "fuchsia",
256+
windows,
257+
all(target_arch = "wasm32", feature = "stdweb")
258+
))))]
259+
type Os = NoSource;
260+
261+
262+
type Custom = NoSource;
263+
264+
265+
#[cfg(not(target_arch = "wasm32"))]
266+
#[derive(Clone, Debug)]
267+
pub struct Jitter(rngs::JitterRng);
268+
269+
#[cfg(not(target_arch = "wasm32"))]
270+
impl EntropySource for Jitter {
271+
fn new_and_fill(dest: &mut [u8]) -> Result<Self, Error> {
272+
let mut rng = rngs::JitterRng::new()?;
273+
rng.try_fill_bytes(dest)?;
274+
Ok(Jitter(rng))
275+
}
276+
277+
fn fill(&mut self, dest: &mut [u8]) -> Result<(), Error> {
278+
self.0.try_fill_bytes(dest)
279+
}
280+
}
281+
282+
#[cfg(target_arch = "wasm32")]
283+
type Jitter = NoSource;
284+
285+
167286
#[cfg(test)]
168287
mod test {
169288
use super::*;

0 commit comments

Comments
 (0)