Skip to content

Commit

Permalink
Add InfallibleRng marker trait
Browse files Browse the repository at this point in the history
  • Loading branch information
newpavlov committed Mar 18, 2024
1 parent b45e892 commit f2d07f3
Show file tree
Hide file tree
Showing 10 changed files with 111 additions and 66 deletions.
82 changes: 49 additions & 33 deletions rand_chacha/src/chacha.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@
use self::core::fmt;
use crate::guts::ChaCha;
use rand_core::block::{BlockRng, BlockRngCore, CryptoBlockRng};
use rand_core::{CryptoRng, Error, RngCore, SeedableRng};
use rand_core::{CryptoRng, Error, InfallibleRng, RngCore, SeedableRng};

#[cfg(feature = "serde1")] use serde::{Serialize, Deserialize, Serializer, Deserializer};
#[cfg(feature = "serde1")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};

// NB. this must remain consistent with some currently hard-coded numbers in this module
const BUF_BLOCKS: u8 = 4;
Expand Down Expand Up @@ -85,6 +86,7 @@ macro_rules! chacha_impl {
impl BlockRngCore for $ChaChaXCore {
type Item = u32;
type Results = Array64<u32>;

#[inline]
fn generate(&mut self, r: &mut Self::Results) {
self.state.refill4($rounds, &mut r.0);
Expand All @@ -93,9 +95,12 @@ macro_rules! chacha_impl {

impl SeedableRng for $ChaChaXCore {
type Seed = [u8; 32];

#[inline]
fn from_seed(seed: Self::Seed) -> Self {
$ChaChaXCore { state: ChaCha::new(&seed, &[0u8; 8]) }
$ChaChaXCore {
state: ChaCha::new(&seed, &[0u8; 8]),
}
}
}

Expand Down Expand Up @@ -146,6 +151,7 @@ macro_rules! chacha_impl {

impl SeedableRng for $ChaChaXRng {
type Seed = [u8; 32];

#[inline]
fn from_seed(seed: Self::Seed) -> Self {
let core = $ChaChaXCore::from_seed(seed);
Expand All @@ -160,14 +166,17 @@ macro_rules! chacha_impl {
fn next_u32(&mut self) -> u32 {
self.rng.next_u32()
}

#[inline]
fn next_u64(&mut self) -> u64 {
self.rng.next_u64()
}

#[inline]
fn fill_bytes(&mut self, bytes: &mut [u8]) {
self.rng.fill_bytes(bytes)
}

#[inline]
fn try_fill_bytes(&mut self, bytes: &mut [u8]) -> Result<(), Error> {
self.rng.try_fill_bytes(bytes)
Expand Down Expand Up @@ -209,11 +218,9 @@ macro_rules! chacha_impl {
#[inline]
pub fn set_word_pos(&mut self, word_offset: u128) {
let block = (word_offset / u128::from(BLOCK_WORDS)) as u64;
self.rng.core.state.set_block_pos(block);
self.rng
.core
.state
.set_block_pos(block);
self.rng.generate_and_set((word_offset % u128::from(BLOCK_WORDS)) as usize);
.generate_and_set((word_offset % u128::from(BLOCK_WORDS)) as usize);
}

/// Set the stream number.
Expand All @@ -229,10 +236,7 @@ macro_rules! chacha_impl {
/// indirectly via `set_word_pos`), but this is not directly supported.
#[inline]
pub fn set_stream(&mut self, stream: u64) {
self.rng
.core
.state
.set_nonce(stream);
self.rng.core.state.set_nonce(stream);
if self.rng.index() != 64 {
let wp = self.get_word_pos();
self.set_word_pos(wp);
Expand All @@ -242,24 +246,20 @@ macro_rules! chacha_impl {
/// Get the stream number.
#[inline]
pub fn get_stream(&self) -> u64 {
self.rng
.core
.state
.get_nonce()
self.rng.core.state.get_nonce()
}

/// Get the seed.
#[inline]
pub fn get_seed(&self) -> [u8; 32] {
self.rng
.core
.state
.get_seed()
self.rng.core.state.get_seed()
}
}

impl CryptoRng for $ChaChaXRng {}

impl InfallibleRng for $ChaChaXRng {}

impl From<$ChaChaXCore> for $ChaChaXRng {
fn from(core: $ChaChaXCore) -> Self {
$ChaChaXRng {
Expand All @@ -286,22 +286,20 @@ macro_rules! chacha_impl {
}
#[cfg(feature = "serde1")]
impl<'de> Deserialize<'de> for $ChaChaXRng {
fn deserialize<D>(d: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
fn deserialize<D>(d: D) -> Result<Self, D::Error>
where D: Deserializer<'de> {
$abst::$ChaChaXRng::deserialize(d).map(|x| Self::from(&x))
}
}

mod $abst {
#[cfg(feature = "serde1")] use serde::{Serialize, Deserialize};
#[cfg(feature = "serde1")] use serde::{Deserialize, Serialize};

// The abstract state of a ChaCha stream, independent of implementation choices. The
// comparison and serialization of this object is considered a semver-covered part of
// the API.
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(
feature = "serde1",
derive(Serialize, Deserialize),
)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub(crate) struct $ChaChaXRng {
seed: [u8; 32],
stream: u64,
Expand Down Expand Up @@ -331,27 +329,45 @@ macro_rules! chacha_impl {
}
}
}
}
};
}

chacha_impl!(ChaCha20Core, ChaCha20Rng, 10, "ChaCha with 20 rounds", abstract20);
chacha_impl!(ChaCha12Core, ChaCha12Rng, 6, "ChaCha with 12 rounds", abstract12);
chacha_impl!(ChaCha8Core, ChaCha8Rng, 4, "ChaCha with 8 rounds", abstract8);
chacha_impl!(
ChaCha20Core,
ChaCha20Rng,
10,
"ChaCha with 20 rounds",
abstract20
);
chacha_impl!(
ChaCha12Core,
ChaCha12Rng,
6,
"ChaCha with 12 rounds",
abstract12
);
chacha_impl!(
ChaCha8Core,
ChaCha8Rng,
4,
"ChaCha with 8 rounds",
abstract8
);

#[cfg(test)]
mod test {
use rand_core::{RngCore, SeedableRng};

#[cfg(feature = "serde1")] use super::{ChaCha20Rng, ChaCha12Rng, ChaCha8Rng};
#[cfg(feature = "serde1")] use super::{ChaCha12Rng, ChaCha20Rng, ChaCha8Rng};

type ChaChaRng = super::ChaCha20Rng;

#[cfg(feature = "serde1")]
#[test]
fn test_chacha_serde_roundtrip() {
let seed = [
1, 0, 52, 0, 0, 0, 0, 0, 1, 0, 10, 0, 22, 32, 0, 0, 2, 0, 55, 49, 0, 11, 0, 0, 3, 0, 0, 0, 0,
0, 2, 92,
1, 0, 52, 0, 0, 0, 0, 0, 1, 0, 10, 0, 22, 32, 0, 0, 2, 0, 55, 49, 0, 11, 0, 0, 3, 0, 0,
0, 0, 0, 2, 92,
];
let mut rng1 = ChaCha20Rng::from_seed(seed);
let mut rng2 = ChaCha12Rng::from_seed(seed);
Expand Down Expand Up @@ -598,7 +614,7 @@ mod test {

#[test]
fn test_chacha_word_pos_wrap_exact() {
use super::{BUF_BLOCKS, BLOCK_WORDS};
use super::{BLOCK_WORDS, BUF_BLOCKS};
let mut rng = ChaChaRng::from_seed(Default::default());
// refilling the buffer in set_word_pos will wrap the block counter to 0
let last_block = (1 << 68) - u128::from(BUF_BLOCKS * BLOCK_WORDS);
Expand Down
8 changes: 8 additions & 0 deletions rand_core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,14 @@ pub trait RngCore {
/// [`BlockRngCore`]: block::BlockRngCore
pub trait CryptoRng: RngCore {}

/// A marker trait used to indicate that an [`RngCore`] implementation is
/// supposed to never return errors from the [`RngCore::try_fill_bytes`] method
/// and never panic on unwrapping [`Error`] while calling other methods.
///
/// This trait is usually implemented by PRNGs, as opposed to OS, hardware,
/// and periodically-reseeded RNGs, which may fail with IO errors.
pub trait InfallibleRng: RngCore {}

/// A random number generator that can be explicitly seeded.
///
/// This trait encapsulates the low-level functionality common to all
Expand Down
5 changes: 4 additions & 1 deletion rand_pcg/src/pcg128.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
const MULTIPLIER: u128 = 0x2360_ED05_1FC6_5DA4_4385_DF64_9FCC_F645;

use core::fmt;
use rand_core::{impls, le, Error, RngCore, SeedableRng};
use rand_core::{impls, le, Error, InfallibleRng, RngCore, SeedableRng};
#[cfg(feature = "serde1")] use serde::{Deserialize, Serialize};

/// A PCG random number generator (XSL RR 128/64 (LCG) variant).
Expand Down Expand Up @@ -159,6 +159,7 @@ impl RngCore for Lcg128Xsl64 {
}
}

impl InfallibleRng for Lcg128Xsl64 {}

/// A PCG random number generator (XSL 128/64 (MCG) variant).
///
Expand Down Expand Up @@ -280,3 +281,5 @@ fn output_xsl_rr(state: u128) -> u64 {
let xsl = ((state >> XSHIFT) as u64) ^ (state as u64);
xsl.rotate_right(rot)
}

impl InfallibleRng for Mcg128Xsl64 {}
4 changes: 3 additions & 1 deletion rand_pcg/src/pcg128cm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
const MULTIPLIER: u64 = 15750249268501108917;

use core::fmt;
use rand_core::{impls, le, Error, RngCore, SeedableRng};
use rand_core::{impls, le, Error, InfallibleRng, RngCore, SeedableRng};
#[cfg(feature = "serde1")] use serde::{Deserialize, Serialize};

/// A PCG random number generator (CM DXSM 128/64 (LCG) variant).
Expand Down Expand Up @@ -165,6 +165,8 @@ impl RngCore for Lcg128CmDxsm64 {
}
}

impl InfallibleRng for Lcg128CmDxsm64 {}

#[inline(always)]
fn output_dxsm(state: u128) -> u64 {
// See https://github.com/imneme/pcg-cpp/blob/ffd522e7188bef30a00c74dc7eb9de5faff90092/include/pcg_random.hpp#L1016
Expand Down
4 changes: 3 additions & 1 deletion rand_pcg/src/pcg64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
//! PCG random number generators

use core::fmt;
use rand_core::{impls, le, Error, RngCore, SeedableRng};
use rand_core::{impls, le, Error, InfallibleRng, RngCore, SeedableRng};
#[cfg(feature = "serde1")] use serde::{Deserialize, Serialize};

// This is the default multiplier used by PCG for 64-bit state.
Expand Down Expand Up @@ -167,3 +167,5 @@ impl RngCore for Lcg64Xsh32 {
Ok(())
}
}

impl InfallibleRng for Lcg64Xsh32 {}
16 changes: 7 additions & 9 deletions src/rngs/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@

//! Mock random number generator

use rand_core::{impls, Error, RngCore};
use rand_core::{impls, Error, InfallibleRng, RngCore};

#[cfg(feature = "serde1")]
use serde::{Serialize, Deserialize};
#[cfg(feature = "serde1")] use serde::{Deserialize, Serialize};

/// A mock generator yielding very predictable output
///
Expand Down Expand Up @@ -81,10 +80,11 @@ impl RngCore for StepRng {
}
}

impl InfallibleRng for StepRng {}

#[cfg(test)]
mod tests {
#[cfg(any(feature = "alloc", feature = "serde1"))]
use super::StepRng;
#[cfg(any(feature = "alloc", feature = "serde1"))] use super::StepRng;

#[test]
#[cfg(feature = "serde1")]
Expand All @@ -94,18 +94,16 @@ mod tests {
bincode::deserialize(&bincode::serialize(&some_rng).unwrap()).unwrap();
assert_eq!(some_rng.v, de_some_rng.v);
assert_eq!(some_rng.a, de_some_rng.a);

}

#[test]
#[cfg(feature = "alloc")]
fn test_bool() {
use crate::{Rng, distributions::Standard};
use crate::{distributions::Standard, Rng};

// If this result ever changes, update doc on StepRng!
let rng = StepRng::new(0, 1 << 31);
let result: alloc::vec::Vec<bool> =
rng.sample_iter(Standard).take(6).collect();
let result: alloc::vec::Vec<bool> = rng.sample_iter(Standard).take(6).collect();
assert_eq!(&result, &[false, true, false, true, false, true]);
}
}
4 changes: 3 additions & 1 deletion src/rngs/small.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

//! A small fast RNG

use rand_core::{Error, RngCore, SeedableRng};
use rand_core::{Error, InfallibleRng, RngCore, SeedableRng};

#[cfg(target_pointer_width = "64")]
type Rng = super::xoshiro256plusplus::Xoshiro256PlusPlus;
Expand Down Expand Up @@ -64,6 +64,8 @@ impl RngCore for SmallRng {
}
}

impl InfallibleRng for SmallRng {}

impl SmallRng {
/// Construct an instance seeded from another `Rng`
///
Expand Down
8 changes: 5 additions & 3 deletions src/rngs/std.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@

use crate::{CryptoRng, Error, RngCore, SeedableRng};

#[cfg(feature = "getrandom")]
pub(crate) use rand_chacha::ChaCha12Core as Core;
#[cfg(feature = "getrandom")] pub(crate) use rand_chacha::ChaCha12Core as Core;

use rand_chacha::ChaCha12Rng as Rng;
use rand_core::InfallibleRng;

/// The standard RNG. The PRNG algorithm in `StdRng` is chosen to be efficient
/// on the current platform, to be statistically strong and unpredictable
/// (meaning a cryptographically secure PRNG).
///
/// The current algorithm used is the ChaCha block cipher with 12 rounds. Please
/// see this relevant [rand issue] for the discussion. This may change as new
/// see this relevant [rand issue] for the discussion. This may change as new
/// evidence of cipher security and performance becomes available.
///
/// The algorithm is deterministic but should not be considered reproducible
Expand Down Expand Up @@ -73,6 +73,8 @@ impl SeedableRng for StdRng {

impl CryptoRng for StdRng {}

impl InfallibleRng for StdRng {}


#[cfg(test)]
mod test {
Expand Down

0 comments on commit f2d07f3

Please sign in to comment.