Skip to content
Draft
Show file tree
Hide file tree
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
8 changes: 6 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ features = ["small_rng", "serde"]
# Meta-features:
default = ["std", "std_rng", "os_rng", "small_rng", "thread_rng"]
nightly = [] # some additions requiring nightly Rust
serde = ["dep:serde", "rand_core/serde"]
serde = ["dep:serde"]

# Option (enabled by default): without "std" rand uses libcore; this option
# enables functionality expected to be available on a standard platform.
Expand Down Expand Up @@ -76,7 +76,8 @@ exclude = ["benches", "distr_test"]
rand_core = { version = "0.10.0-rc-2", default-features = false }
log = { version = "0.4.4", optional = true }
serde = { version = "1.0.103", features = ["derive"], optional = true }
chacha20 = { version = "=0.10.0-rc.5", default-features = false, features = ["rng"], optional = true }
# chacha20 = { version = "=0.10.0-rc.5", default-features = false, features = ["rng"], optional = true }
chacha20 = { path = "rand_chacha", optional = true, package = "rand_chacha" }
getrandom = { version = "0.3.0", optional = true }

[dev-dependencies]
Expand All @@ -85,3 +86,6 @@ rand_pcg = { path = "rand_pcg", version = "0.10.0-rc.1" }
bincode = "1.2.1"
rayon = "1.7"
serde_json = "1.0.140"

[patch.crates-io]
rand_core = { git = "https://github.com/rust-random/rand_core", branch = "block_buffer" }
4 changes: 3 additions & 1 deletion benches/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ rand = { path = "..", features = ["small_rng", "nightly"] }
rand_pcg = { path = "../rand_pcg" }
rand_chacha = { path = "../rand_chacha" }
criterion = "0.5"
criterion-cycles-per-byte = "0.6"

[[bench]]
name = "array"
Expand Down Expand Up @@ -56,3 +55,6 @@ harness = false
[[bench]]
name = "weighted"
harness = false

[patch.crates-io]
rand_core = { git = "https://github.com/rust-random/rand_core", branch = "block_buffer" }
4 changes: 2 additions & 2 deletions benches/benches/generators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use rand::prelude::*;
use rand::rngs::OsRng;
use rand::rngs::ReseedingRng;
use rand_chacha::rand_core::UnwrapErr;
use rand_chacha::{ChaCha8Rng, ChaCha12Rng, ChaCha20Core, ChaCha20Rng};
use rand_chacha::{ChaCha8Rng, ChaCha12Rng, ChaCha20Rng};
use rand_pcg::{Pcg32, Pcg64, Pcg64Dxsm, Pcg64Mcg};

criterion_group!(
Expand Down Expand Up @@ -198,7 +198,7 @@ pub fn reseeding_bytes(c: &mut Criterion) {
fn bench(g: &mut BenchmarkGroup<WallTime>, thresh: u64) {
let name = format!("chacha20_{thresh}k");
g.bench_function(name.as_str(), |b| {
let mut rng = ReseedingRng::<ChaCha20Core, _>::new(thresh * 1024, OsRng).unwrap();
let mut rng = ReseedingRng::<ChaCha20Rng, _>::new(thresh * 1024, OsRng).unwrap();
let mut buf = [0u8; 1024 * 1024];
b.iter(|| {
rng.fill_bytes(&mut buf);
Expand Down
128 changes: 46 additions & 82 deletions rand_chacha/src/chacha.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@

use crate::guts::ChaCha;
use core::fmt;
use rand_core::block::{BlockRng, BlockRngCore, CryptoBlockRng};
use rand_core::{CryptoRng, RngCore, SeedableRng};
use rand_core::{CryptoRng, RngCore, SeedableRng, le};

#[cfg(feature = "serde")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};
Expand All @@ -21,52 +20,6 @@ const BUF_BLOCKS: u8 = 4;
// number of 32-bit words per ChaCha block (fixed by algorithm definition)
const BLOCK_WORDS: u8 = 16;

#[repr(transparent)]
pub struct Array64<T>([T; 64]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was needed because of the bound Results: Default and [_; 64] not implementing this.

It's a nice but ultimately unimportant improvement of the new utility fns.

impl<T> Default for Array64<T>
where
T: Default,
{
#[rustfmt::skip]
fn default() -> Self {
Self([
T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(),
T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(),
T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(),
T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(),
T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(),
T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(),
T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(),
T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(),
])
}
}
impl<T> AsRef<[T]> for Array64<T> {
fn as_ref(&self) -> &[T] {
&self.0
}
}
impl<T> AsMut<[T]> for Array64<T> {
fn as_mut(&mut self) -> &mut [T] {
&mut self.0
}
}
impl<T> Clone for Array64<T>
where
T: Copy + Default,
{
fn clone(&self) -> Self {
let mut new = Self::default();
new.0.copy_from_slice(&self.0);
new
}
}
impl<T> fmt::Debug for Array64<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Array64 {{}}")
}
}

macro_rules! chacha_impl {
($ChaChaXCore:ident, $ChaChaXRng:ident, $rounds:expr, $doc:expr, $abst:ident,) => {
#[doc=$doc]
Expand All @@ -82,28 +35,19 @@ 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);
}
}

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

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

impl CryptoBlockRng for $ChaChaXCore {}
#[inline]
fn next_block(&mut self, r: &mut [u32; 64]) {
self.state.refill4($rounds, r);
}
}

/// A cryptographically secure random number generator that uses the ChaCha algorithm.
///
Expand Down Expand Up @@ -136,7 +80,7 @@ macro_rules! chacha_impl {
/// ```
///
/// This implementation uses an output buffer of sixteen `u32` words, and uses
/// [`BlockRng`] to implement the [`RngCore`] methods.
/// them to implement the [`RngCore`] methods.
///
/// [^1]: D. J. Bernstein, [*ChaCha, a variant of Salsa20*](
/// https://cr.yp.to/chacha.html)
Expand All @@ -145,35 +89,55 @@ macro_rules! chacha_impl {
/// http://www.ecrypt.eu.org/stream/)
#[derive(Clone, Debug)]
pub struct $ChaChaXRng {
rng: BlockRng<$ChaChaXCore>,
core: $ChaChaXCore,
buffer: [u32; 64],
}

impl $ChaChaXRng {
fn buffer_index(&self) -> u32 {
self.buffer[0]
}

fn generate_and_set(&mut self, index: usize) {
assert!(index < self.buffer.len());
self.buffer[0] = if index != 0 {
self.core.next_block(&mut self.buffer);
index as u32
} else {
self.buffer.len() as u32
}
}
}
Comment on lines +96 to 110
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These fns are needed to support get/set word-pos fns. They were provided by BlockRng.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These feel like low-level details. They very much depend on using buffer[0] as an index, which isn't something properly documented anywhere.

We could fix this with a Buffer type, but this is undesirable. We could perhaps fix this with a Buffer trait, though it wouldn't be well documented.


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

#[inline]
fn from_seed(seed: Self::Seed) -> Self {
let core = $ChaChaXCore::from_seed(seed);
Self {
rng: BlockRng::new(core),
core: $ChaChaXCore::from_seed(seed),
buffer: le::new_buffer(),
}
}
}

impl RngCore for $ChaChaXRng {
#[inline]
fn next_u32(&mut self) -> u32 {
self.rng.next_u32()
let Self { core, buffer } = self;
le::next_word_via_gen_block(buffer, |block| core.next_block(block))
}

#[inline]
fn next_u64(&mut self) -> u64 {
self.rng.next_u64()
let Self { core, buffer } = self;
le::next_u64_via_gen_block(buffer, |block| core.next_block(block))
}

#[inline]
fn fill_bytes(&mut self, bytes: &mut [u8]) {
self.rng.fill_bytes(bytes)
fn fill_bytes(&mut self, dst: &mut [u8]) {
let Self { core, buffer } = self;
le::fill_bytes_via_gen_block(dst, buffer, |block| core.next_block(block));
}
Comment on lines 116 to 141
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This stuff all requires marginally lower-level implementations; not very significant.

}

Expand All @@ -190,11 +154,11 @@ macro_rules! chacha_impl {
#[inline]
pub fn get_word_pos(&self) -> u128 {
let buf_start_block = {
let buf_end_block = self.rng.core.state.get_block_pos();
let buf_end_block = self.core.state.get_block_pos();
u64::wrapping_sub(buf_end_block, BUF_BLOCKS.into())
};
let (buf_offset_blocks, block_offset_words) = {
let buf_offset_words = self.rng.index() as u64;
let buf_offset_words = self.buffer_index() as u64;
let blocks_part = buf_offset_words / u64::from(BLOCK_WORDS);
let words_part = buf_offset_words % u64::from(BLOCK_WORDS);
(blocks_part, words_part)
Expand All @@ -212,9 +176,8 @@ 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
.generate_and_set((word_offset % u128::from(BLOCK_WORDS)) as usize);
self.core.state.set_block_pos(block);
self.generate_and_set((word_offset % u128::from(BLOCK_WORDS)) as usize);
}

/// Set the stream number.
Expand All @@ -230,8 +193,8 @@ 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);
if self.rng.index() != 64 {
self.core.state.set_nonce(stream);
if self.buffer_index() != 64 {
let wp = self.get_word_pos();
self.set_word_pos(wp);
}
Expand All @@ -240,13 +203,13 @@ macro_rules! chacha_impl {
/// Get the stream number.
#[inline]
pub fn get_stream(&self) -> u64 {
self.rng.core.state.get_nonce()
self.core.state.get_nonce()
}

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

Expand All @@ -255,7 +218,8 @@ macro_rules! chacha_impl {
impl From<$ChaChaXCore> for $ChaChaXRng {
fn from(core: $ChaChaXCore) -> Self {
$ChaChaXRng {
rng: BlockRng::new(core),
core,
buffer: le::new_buffer(),
}
}
}
Expand Down
6 changes: 2 additions & 4 deletions rand_chacha/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@
//!
//! ## Generators
//!
//! This crate provides 8-, 12- and 20-round variants of generators via a "core"
//! implementation (of [`BlockRngCore`]), each with an associated "RNG" type
//! (implementing [`RngCore`]).
//! This crate provides 8-, 12- and 20-round variants of generators
//! implementing [`RngCore`].
//!
//! These generators are all deterministic and portable (see [Reproducibility]
//! in the book), with testing against reference vectors.
Expand Down Expand Up @@ -73,7 +72,6 @@
//! [Seeding RNGs]: https://rust-random.github.io/book/guide-seeding.html
//! [Security]: https://rust-random.github.io/book/guide-rngs.html#security
//! [Random Values]: https://rust-random.github.io/book/guide-values.html
//! [`BlockRngCore`]: rand_core::block::BlockRngCore
//! [`RngCore`]: rand_core::RngCore
//! [`SeedableRng`]: rand_core::SeedableRng
//! [`OsRng`]: https://docs.rs/rand/latest/rand/rngs/struct.OsRng.html
Expand Down
16 changes: 7 additions & 9 deletions rand_pcg/src/pcg128.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,9 @@ impl SeedableRng for Lcg128Xsl64 {
/// We use a single 255-bit seed to initialise the state and select a stream.
/// One `seed` bit (lowest bit of `seed[8]`) is ignored.
fn from_seed(seed: Self::Seed) -> Self {
let mut seed_u64 = [0u64; 4];
le::read_u64_into(&seed, &mut seed_u64);
let state = u128::from(seed_u64[0]) | (u128::from(seed_u64[1]) << 64);
let incr = u128::from(seed_u64[2]) | (u128::from(seed_u64[3]) << 64);
let seed: [u64; 4] = le::read_words(&seed);
let state = u128::from(seed[0]) | (u128::from(seed[1]) << 64);
let incr = u128::from(seed[2]) | (u128::from(seed[3]) << 64);

// The increment must be odd, hence we discard one bit:
Lcg128Xsl64::from_state_incr(state, incr | 1)
Expand All @@ -150,7 +149,7 @@ impl RngCore for Lcg128Xsl64 {

#[inline]
fn fill_bytes(&mut self, dest: &mut [u8]) {
le::fill_bytes_via_next(self, dest)
le::fill_bytes_via_next_word(dest, || self.next_u64())
}
}

Expand Down Expand Up @@ -232,9 +231,8 @@ impl SeedableRng for Mcg128Xsl64 {

fn from_seed(seed: Self::Seed) -> Self {
// Read as if a little-endian u128 value:
let mut seed_u64 = [0u64; 2];
le::read_u64_into(&seed, &mut seed_u64);
let state = u128::from(seed_u64[0]) | (u128::from(seed_u64[1]) << 64);
let seed: [u64; 2] = le::read_words(&seed);
let state = u128::from(seed[0]) | (u128::from(seed[1]) << 64);
Mcg128Xsl64::new(state)
}
}
Expand All @@ -253,7 +251,7 @@ impl RngCore for Mcg128Xsl64 {

#[inline]
fn fill_bytes(&mut self, dest: &mut [u8]) {
le::fill_bytes_via_next(self, dest)
le::fill_bytes_via_next_word(dest, || self.next_u64())
}
}

Expand Down
9 changes: 4 additions & 5 deletions rand_pcg/src/pcg128cm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,9 @@ impl SeedableRng for Lcg128CmDxsm64 {
/// We use a single 255-bit seed to initialise the state and select a stream.
/// One `seed` bit (lowest bit of `seed[8]`) is ignored.
fn from_seed(seed: Self::Seed) -> Self {
let mut seed_u64 = [0u64; 4];
le::read_u64_into(&seed, &mut seed_u64);
let state = u128::from(seed_u64[0]) | (u128::from(seed_u64[1]) << 64);
let incr = u128::from(seed_u64[2]) | (u128::from(seed_u64[3]) << 64);
let seed: [u64; 4] = le::read_words(&seed);
let state = u128::from(seed[0]) | (u128::from(seed[1]) << 64);
let incr = u128::from(seed[2]) | (u128::from(seed[3]) << 64);

// The increment must be odd, hence we discard one bit:
Self::from_state_incr(state, incr | 1)
Expand All @@ -156,7 +155,7 @@ impl RngCore for Lcg128CmDxsm64 {

#[inline]
fn fill_bytes(&mut self, dest: &mut [u8]) {
le::fill_bytes_via_next(self, dest)
le::fill_bytes_via_next_word(dest, || self.next_u64())
}
}

Expand Down
Loading