Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 76 additions & 12 deletions src/rngs/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,61 @@ impl ReseedingCore {
}
}

/// The [`ThreadRng`] internal
///
/// This type is the actual pseudo-random number generator that powers [`ThreadRng`]. The same
/// Security design criteria and consideration as those of [`ThreadRng`] apply, whereas it allows
/// users to utilize the same reseeding generator where `Send` or `Sync` is required.
pub struct ThreadRngCore {
inner: BlockRng<ReseedingCore>,
}

/// Debug implementation does not leak internal state
impl fmt::Debug for ThreadRngCore {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "ThreadRngCore {{ .. }}")
}
}

impl ThreadRngCore {
/// Initialize the generator using [`SysRng`]
pub fn new() -> Result<Self, SysError> {
Core::try_from_rng(&mut SysRng).map(|result| Self {
inner: BlockRng::new(ReseedingCore { inner: result }),
})
}

/// Immediately reseed the generator
///
/// This discards any remaining random data in the cache.
pub fn reseed(&mut self) -> Result<(), SysError> {
self.inner.reset_and_skip(0);
self.inner.core.reseed()
}
}

impl TryRng for ThreadRngCore {
type Error = Infallible;

#[inline(always)]
fn try_next_u32(&mut self) -> Result<u32, Infallible> {
Ok(self.inner.next_word())
}

#[inline(always)]
fn try_next_u64(&mut self) -> Result<u64, Infallible> {
Ok(self.inner.next_u64_from_u32())
}

#[inline(always)]
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Infallible> {
self.inner.fill_bytes(dest);
Ok(())
}
}

impl TryCryptoRng for ThreadRngCore {}

/// A reference to the thread-local generator
///
/// This type is a reference to a lazily-initialized thread-local generator.
Expand All @@ -89,7 +144,7 @@ impl ReseedingCore {
///
/// - Automatic seeding via [`SysRng`] and after every 64 kB of output.
/// Limitation: there is no automatic reseeding on process fork (see [below](#fork)).
/// - A rigorusly analyzed, unpredictable (cryptographic) pseudo-random generator
/// - A rigorously analyzed, unpredictable (cryptographic) pseudo-random generator
/// (see [the book on security](https://rust-random.github.io/book/guide-rngs.html#security)).
/// The currently selected algorithm is ChaCha (12-rounds).
/// See also [`StdRng`] documentation.
Expand Down Expand Up @@ -127,7 +182,7 @@ impl ReseedingCore {
#[derive(Clone)]
pub struct ThreadRng {
// Rc is explicitly !Send and !Sync
rng: Rc<UnsafeCell<BlockRng<ReseedingCore>>>,
rng: Rc<UnsafeCell<ThreadRngCore>>,
}

impl ThreadRng {
Expand All @@ -138,8 +193,7 @@ impl ThreadRng {
// SAFETY: We must make sure to stop using `rng` before anyone else
// creates another mutable reference
let rng = unsafe { &mut *self.rng.get() };
rng.reset_and_skip(0);
rng.core.reseed()
rng.reseed()
}
}

Expand All @@ -153,12 +207,11 @@ impl fmt::Debug for ThreadRng {
thread_local!(
// We require Rc<..> to avoid premature freeing when ThreadRng is used
// within thread-local destructors. See #968.
static THREAD_RNG_KEY: Rc<UnsafeCell<BlockRng<ReseedingCore>>> = {
Rc::new(UnsafeCell::new(BlockRng::new(ReseedingCore {
inner: Core::try_from_rng(&mut SysRng).unwrap_or_else(|err| {
static THREAD_RNG_KEY: Rc<UnsafeCell<ThreadRngCore>> = {
Rc::new(UnsafeCell::new(ThreadRngCore::new().unwrap_or_else(|err| {
panic!("could not initialize ThreadRng: {}", err)
}),
})))
))
}
);

Expand Down Expand Up @@ -209,24 +262,23 @@ impl TryRng for ThreadRng {
// SAFETY: We must make sure to stop using `rng` before anyone else
// creates another mutable reference
let rng = unsafe { &mut *self.rng.get() };
Ok(rng.next_word())
rng.try_next_u32()
}

#[inline(always)]
fn try_next_u64(&mut self) -> Result<u64, Infallible> {
// SAFETY: We must make sure to stop using `rng` before anyone else
// creates another mutable reference
let rng = unsafe { &mut *self.rng.get() };
Ok(rng.next_u64_from_u32())
rng.try_next_u64()
}

#[inline(always)]
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Infallible> {
// SAFETY: We must make sure to stop using `rng` before anyone else
// creates another mutable reference
let rng = unsafe { &mut *self.rng.get() };
rng.fill_bytes(dest);
Ok(())
rng.try_fill_bytes(dest)
}
}

Expand All @@ -242,10 +294,22 @@ mod test {
assert_eq!(r.random_range(0..1), 0);
}

#[test]
fn test_thread_rng_core() {
use crate::RngExt;
let mut r = super::ThreadRngCore::new().unwrap();
r.random::<i32>();
assert_eq!(r.random_range(0..1), 0);
}

#[test]
fn test_debug_output() {
// We don't care about the exact output here, but it must not include
// private CSPRNG state or the cache stored by BlockRng!
assert_eq!(std::format!("{:?}", crate::rng()), "ThreadRng { .. }");
assert_eq!(
std::format!("{:?}", super::ThreadRngCore::new().unwrap()),
"ThreadRngCore { .. }"
);
}
}