Skip to content

Commit 7183dbc

Browse files
committed
Allow a custom entropy source to be set at run-time
1 parent bc33123 commit 7183dbc

File tree

2 files changed

+140
-3
lines changed

2 files changed

+140
-3
lines changed

src/rngs/entropy.rs

+139-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010

1111
//! Entropy generator, or wrapper around external generators
1212
13+
use core::fmt;
14+
use std::rc::Rc;
15+
use std::sync::{Once, Mutex, MutexGuard, ONCE_INIT};
16+
1317
use rand_core::{RngCore, CryptoRng, Error, ErrorKind, impls};
1418
#[allow(unused)]
1519
use rngs;
@@ -30,6 +34,8 @@ use rngs;
3034
/// jitter); for better performance it is common to seed a local PRNG from
3135
/// external entropy then primarily use the local PRNG ([`thread_rng`] is
3236
/// provided as a convenient, local, automatically-seeded CSPRNG).
37+
///
38+
/// `EntropyRng` instances are explicitly not `Send` or `Sync`.
3339
///
3440
/// # Panics
3541
///
@@ -48,6 +54,7 @@ use rngs;
4854
#[derive(Debug)]
4955
pub struct EntropyRng {
5056
source: Source,
57+
_dummy: Rc<()> // enforce !Send
5158
}
5259

5360
#[derive(Debug)]
@@ -65,7 +72,7 @@ impl EntropyRng {
6572
/// those are done on first use. This is done to make `new` infallible,
6673
/// and `try_fill_bytes` the only place to report errors.
6774
pub fn new() -> Self {
68-
EntropyRng { source: Source::None }
75+
EntropyRng { source: Source::None, _dummy: Rc::new(()) }
6976
}
7077
}
7178

@@ -241,8 +248,121 @@ impl EntropySource for Os {
241248
))))]
242249
type Os = NoSource;
243250

251+
// TODO: impl !Send. Currently only possible on nightly; instead we use a dummy field in `EntropyRng`.
252+
#[derive(Clone)]
253+
struct Custom {
254+
source: &'static CustomEntropySource,
255+
param: u64,
256+
}
257+
258+
impl fmt::Debug for Custom {
259+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
260+
write!(f, "Custom {{ ... }}")
261+
}
262+
}
263+
264+
#[test]
265+
fn test_size() {
266+
use core::mem::size_of;
267+
println!("Size of Custom: {}", size_of::<Custom>());
268+
assert!(size_of::<Custom>() <= size_of::<rngs::JitterRng>());
269+
}
270+
271+
/// Properties of a custom entropy source.
272+
pub trait CustomEntropySource {
273+
/// Name of this source
274+
fn name(&self) -> &'static str;
275+
276+
/// Is this source available?
277+
///
278+
/// The default implementation returns `true`.
279+
fn is_available(&self) -> bool { true }
280+
281+
/// Prepare the entropy source for use.
282+
///
283+
/// This is always called before `fill` on each thread. This may be called
284+
/// multiple times.
285+
///
286+
/// A `u64` parameter may be returned, which is passed to `fill` when
287+
/// called, and is considered `!Send` (i.e. is never passed to a different
288+
/// thread).
289+
fn init(&self) -> Result<u64, Error>;
290+
291+
/// Fill `dest` with random data from the entropy source.
292+
///
293+
/// The `u64` parameter from `init` is passed.
294+
fn fill(&self, param: &mut u64, dest: &mut [u8]) -> Result<(), Error>;
295+
}
296+
297+
struct CustomNoSource;
298+
impl CustomEntropySource for CustomNoSource {
299+
fn name(&self) -> &'static str {
300+
"no source"
301+
}
302+
303+
fn is_available(&self) -> bool { false }
304+
305+
fn init(&self) -> Result<u64, Error> {
306+
unreachable!()
307+
}
244308

245-
type Custom = NoSource;
309+
fn fill(&self, _: &mut u64, _: &mut [u8]) -> Result<(), Error> {
310+
unreachable!()
311+
}
312+
}
313+
314+
// TODO: remove outer Option when `Mutex::new(&...)` is a constant expression
315+
static mut CUSTOM_SOURCE: Option<Mutex<&CustomEntropySource>> = None;
316+
static CUSTOM_SOURCE_ONCE: Once = ONCE_INIT;
317+
318+
fn access_custom_entropy() -> MutexGuard<'static, &'static CustomEntropySource> {
319+
CUSTOM_SOURCE_ONCE.call_once(|| {
320+
unsafe { CUSTOM_SOURCE = Some(Mutex::new(&CustomNoSource)) }
321+
});
322+
let mutex = unsafe { CUSTOM_SOURCE.as_ref().unwrap() };
323+
mutex.lock().unwrap()
324+
}
325+
326+
fn get_custom_entropy() -> &'static CustomEntropySource {
327+
*access_custom_entropy()
328+
}
329+
330+
/// Specify a custom entropy source.
331+
///
332+
/// This must be a static reference to an object implementing the
333+
/// `CustomEntropySource` trait.
334+
pub fn set_custom_entropy(source: &'static CustomEntropySource) {
335+
let mut guard = access_custom_entropy();
336+
*guard = source;
337+
}
338+
339+
impl EntropySource for Custom {
340+
fn name() -> &'static str {
341+
get_custom_entropy().name()
342+
}
343+
344+
/// Is this source available?
345+
///
346+
/// The default implementation returns `true`.
347+
fn is_available() -> bool {
348+
get_custom_entropy().is_available()
349+
}
350+
351+
/// Create an instance
352+
fn new() -> Result<Self, Error> where Self: Sized {
353+
let source = get_custom_entropy();
354+
let param = source.init()?;
355+
Ok(Custom {
356+
source,
357+
param,
358+
})
359+
}
360+
361+
/// Fill `dest` with random data from the entropy source
362+
fn fill(&mut self, dest: &mut [u8]) -> Result<(), Error> {
363+
self.source.fill(&mut self.param, dest)
364+
}
365+
}
246366

247367

248368
#[cfg(not(target_arch = "wasm32"))]
@@ -276,4 +396,21 @@ mod test {
276396
let n = (rng.next_u32() ^ rng.next_u32()).count_ones();
277397
assert!(n >= 2); // p(failure) approx 1e-7
278398
}
399+
400+
#[test]
401+
fn test_custom_entropy() {
402+
struct FakeEntropy;
403+
impl CustomEntropySource for FakeEntropy {
404+
fn name(&self) -> &'static str { "fake entropy" }
405+
fn init(&self) -> Result<u64, Error> { Ok(0) }
406+
fn fill(&self, _: &mut u64, dest: &mut [u8]) -> Result<(), Error> {
407+
for x in dest { *x = 0 }
408+
Ok(())
409+
}
410+
}
411+
set_custom_entropy(&FakeEntropy);
412+
let mut entropy = EntropyRng::new();
413+
// we can't properly test this because we can't disable `OsRng`
414+
assert!(entropy.next_u64() != 1);
415+
}
279416
}

src/rngs/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ mod std;
177177

178178

179179
pub use self::jitter::{JitterRng, TimerError};
180-
#[cfg(feature="std")] pub use self::entropy::EntropyRng;
180+
#[cfg(feature="std")] pub use self::entropy::{EntropyRng, CustomEntropySource, set_custom_entropy};
181181

182182
pub use self::small::SmallRng;
183183
pub use self::std::StdRng;

0 commit comments

Comments
 (0)