Skip to content
Open
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
16 changes: 13 additions & 3 deletions chacha20/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ pub use chacha::{ChaCha8, ChaCha12, ChaCha20, Key, KeyIvInit};
#[cfg(feature = "rng")]
pub use rand_core;
#[cfg(feature = "rng")]
pub use rng::{ChaCha8Core, ChaCha8Rng, ChaCha12Core, ChaCha12Rng, ChaCha20Core, ChaCha20Rng};
pub use rng::{ChaCha8Rng, ChaCha12Rng, ChaCha20Rng};

#[cfg(feature = "legacy")]
pub use legacy::{ChaCha20Legacy, LegacyNonce};
Expand Down Expand Up @@ -265,6 +265,16 @@ impl<R: Rounds, V: Variant> ChaChaCore<R, V> {
_pd: PhantomData,
}
}

#[inline(always)]
fn get_block_pos(&self) -> V::Counter {
V::get_block_pos(&self.state[12..])
}

#[inline(always)]
fn set_block_pos(&mut self, pos: V::Counter) {
V::set_block_pos(&mut self.state[12..], pos);
}
}

#[cfg(feature = "cipher")]
Expand All @@ -273,12 +283,12 @@ impl<R: Rounds, V: Variant> StreamCipherSeekCore for ChaChaCore<R, V> {

#[inline(always)]
fn get_block_pos(&self) -> Self::Counter {
V::get_block_pos(&self.state[12..])
self.get_block_pos()
}

#[inline(always)]
fn set_block_pos(&mut self, pos: Self::Counter) {
V::set_block_pos(&mut self.state[12..], pos);
self.set_block_pos(pos)
}
}

Expand Down
117 changes: 36 additions & 81 deletions chacha20/src/rng.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,33 +77,32 @@ impl Debug for Seed {
}
}

/// A wrapper around 64 bits of data that can be constructed from any of the
/// following:
/// A wrapper for `stream_id` (64-bits).
///
/// Can be constructed from any of the following:
/// * `u64`
/// * `[u32; 2]`
/// * `[u8; 8]`
///
/// The arrays should be in little endian order. You should not need to use
/// this directly, as the methods in this crate that use this type call
/// `.into()` for you, so you only need to supply any of the above types.
pub struct U32x2([u32; Self::LEN]);
/// The arrays should be in little endian order.
pub struct StreamId([u32; Self::LEN]);

impl U32x2 {
/// Amount of raw bytes backing a `U32x2` instance.
impl StreamId {
/// Amount of raw bytes backing a `StreamId` instance.
const BYTES: usize = size_of::<Self>();

/// The length of the array contained within `U32x2`.
/// The length of the array contained within `StreamId`.
const LEN: usize = 2;
}

impl From<[u32; Self::LEN]> for U32x2 {
impl From<[u32; Self::LEN]> for StreamId {
#[inline]
fn from(value: [u32; Self::LEN]) -> Self {
Self(value)
}
}

impl From<[u8; Self::BYTES]> for U32x2 {
impl From<[u8; Self::BYTES]> for StreamId {
#[inline]
fn from(value: [u8; Self::BYTES]) -> Self {
let mut result = Self(Default::default());
Expand All @@ -118,34 +117,14 @@ impl From<[u8; Self::BYTES]> for U32x2 {
}
}

impl From<u64> for U32x2 {
impl From<u64> for StreamId {
#[inline]
fn from(value: u64) -> Self {
let result: [u8; Self::BYTES] = value.to_le_bytes()[..Self::BYTES].try_into().unwrap();
result.into()
}
}

/// A wrapper for `stream_id`.
///
/// Can be constructed from any of the following:
/// * `u64`
/// * `[u32; 2]`
/// * `[u8; 8]`
///
/// The arrays should be in little endian order.
pub type StreamId = U32x2;

/// A wrapper for `block_pos`.
///
/// Can be constructed from any of the following:
/// * `u64`
/// * `[u32; 2]`
/// * `[u8; 8]`
///
/// The arrays should be in little endian order.
pub type BlockPos = U32x2;

const BUFFER_SIZE: usize = 64;

// NB. this must remain consistent with some currently hard-coded numbers in this module
Expand Down Expand Up @@ -195,7 +174,7 @@ impl<R: Rounds, V: Variant> ChaChaCore<R, V> {
}

macro_rules! impl_chacha_rng {
($ChaChaXRng:ident, $ChaChaXCore:ident, $rounds:ident, $abst:ident) => {
($ChaChaXRng:ident, $rounds:ident, $abst:ident) => {
/// A cryptographically secure random number generator that uses the ChaCha algorithm.
///
/// ChaCha is a stream cipher designed by Daniel J. Bernstein[^1], that we use as an RNG. It is
Expand Down Expand Up @@ -225,8 +204,9 @@ macro_rules! impl_chacha_rng {
/// seed seed seed seed
/// counter counter stream_id stream_id
/// ```
/// This implementation uses an output buffer of sixteen `u32` words, and uses
/// [`BlockRng`] to implement the [`Rng`] methods.
/// This implementation uses an output buffer of sixteen `u32` words, using
/// [`rand_core::block::BlockRng`] over [`ChaChaCore`] to implement
/// [`rand_core::Rng`].
///
/// # Example for `ChaCha20Rng`
///
Expand Down Expand Up @@ -266,19 +246,15 @@ macro_rules! impl_chacha_rng {
///
/// [^2]: [eSTREAM: the ECRYPT Stream Cipher Project](http://www.ecrypt.eu.org/stream/)
pub struct $ChaChaXRng {
/// The ChaChaCore struct
pub core: BlockRng<$ChaChaXCore>,
core: BlockRng<ChaChaCore<$rounds, Legacy>>,
}

/// The ChaCha core random number generator
pub struct $ChaChaXCore(ChaChaCore<$rounds, Legacy>);

impl SeedableRng for $ChaChaXCore {
impl SeedableRng for ChaChaCore<$rounds, Legacy> {
type Seed = Seed;

#[inline]
fn from_seed(seed: Self::Seed) -> Self {
Self(ChaChaCore::<$rounds, Legacy>::new(seed.as_ref(), &[0u8; 8]))
ChaChaCore::<$rounds, Legacy>::new(seed.as_ref(), &[0u8; 8])
}
}
impl SeedableRng for $ChaChaXRng {
Expand All @@ -287,7 +263,7 @@ macro_rules! impl_chacha_rng {
#[inline]
fn from_seed(seed: Self::Seed) -> Self {
Self {
core: BlockRng::new($ChaChaXCore::from_seed(seed.into())),
core: BlockRng::new(ChaChaCore::<$rounds, Legacy>::from_seed(seed.into())),
}
}
}
Expand All @@ -308,12 +284,9 @@ macro_rules! impl_chacha_rng {
Ok(())
}
}
impl CryptoGenerator for $ChaChaXCore {}
impl CryptoGenerator for ChaChaCore<$rounds, Legacy> {}
Copy link
Member

Choose a reason for hiding this comment

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

If the field will be private this impl can be removed.

Copy link
Contributor Author

@dhardy dhardy Jan 28, 2026

Choose a reason for hiding this comment

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

The point being that we don't need trait CryptoGenerator at all. The trait is (I believe) only used by ReseedingRng, so lets start with that. (Another PR will be needed to update to the new rand_core soon anyway.)

impl TryCryptoRng for $ChaChaXRng {}

#[cfg(feature = "zeroize")]
impl ZeroizeOnDrop for $ChaChaXCore {}

#[cfg(feature = "zeroize")]
impl ZeroizeOnDrop for $ChaChaXRng {}

Expand All @@ -336,8 +309,8 @@ macro_rules! impl_chacha_rng {
/// byte-offset.
#[inline]
pub fn get_word_pos(&self) -> u128 {
let mut block_counter = (u64::from(self.core.core.0.state[13]) << 32)
| u64::from(self.core.core.0.state[12]);
let mut block_counter = (u64::from(self.core.core.state[13]) << 32)
| u64::from(self.core.core.state[12]);
if self.core.word_offset() != 0 {
block_counter = block_counter.wrapping_sub(BUF_BLOCKS as u64);
}
Expand All @@ -360,8 +333,8 @@ macro_rules! impl_chacha_rng {
let index = (word_offset % BLOCK_WORDS as u128) as usize;
let counter = word_offset / BLOCK_WORDS as u128;
//self.set_block_pos(counter as u64);
self.core.core.0.state[12] = counter as u32;
self.core.core.0.state[13] = (counter >> 32) as u32;
self.core.core.state[12] = counter as u32;
self.core.core.state[13] = (counter >> 32) as u32;
self.core.reset_and_skip(index);
}

Expand All @@ -371,28 +344,18 @@ macro_rules! impl_chacha_rng {
/// together.
///
/// The word pos will be equal to `block_pos * 16 words per block`.
///
/// This method takes any of the following:
/// * `u64`
/// * `[u32; 2]`
/// * `[u8; 8]`
///
/// Note: the arrays should be in little endian order.
#[inline]
#[allow(unused)]
pub fn set_block_pos<B: Into<BlockPos>>(&mut self, block_pos: B) {
pub fn set_block_pos>(&mut self, block_pos: u64) {
self.core.reset_and_skip(0);
let block_pos = block_pos.into().0;
self.core.core.0.state[12] = block_pos[0];
self.core.core.0.state[13] = block_pos[1]
self.core.set_block_pos(block_pos);
}

/// Get the block pos.
#[inline]
#[allow(unused)]
pub fn get_block_pos(&self) -> u64 {
let counter =
self.core.core.0.state[12] as u64 | ((self.core.core.0.state[13] as u64) << 32);
let counter = self.core.get_block_pos();
if self.core.word_offset() != 0 {
counter - BUF_BLOCKS as u64 + self.core.word_offset() as u64 / 16
} else {
Expand Down Expand Up @@ -440,15 +403,15 @@ macro_rules! impl_chacha_rng {
#[inline]
pub fn set_stream<S: Into<StreamId>>(&mut self, stream: S) {
let stream: StreamId = stream.into();
self.core.core.0.state[14..].copy_from_slice(&stream.0);
self.core.core.state[14..].copy_from_slice(&stream.0);
self.set_block_pos(0);
}

/// Get the stream number.
#[inline]
pub fn get_stream(&self) -> u64 {
let mut result = [0u8; 8];
for (i, &big) in self.core.core.0.state[14..BLOCK_WORDS as usize]
for (i, &big) in self.core.core.state[14..BLOCK_WORDS as usize]
.iter()
.enumerate()
{
Expand All @@ -465,7 +428,7 @@ macro_rules! impl_chacha_rng {
#[inline]
pub fn get_seed(&self) -> [u8; 32] {
let mut result = [0u8; 32];
for (i, &big) in self.core.core.0.state[4..12].iter().enumerate() {
for (i, &big) in self.core.core.state[4..12].iter().enumerate() {
let index = i * 4;
result[index + 0] = big as u8;
result[index + 1] = (big >> 8) as u8;
Expand All @@ -486,14 +449,6 @@ macro_rules! impl_chacha_rng {

impl Eq for $ChaChaXRng {}

impl From<$ChaChaXCore> for $ChaChaXRng {
fn from(core: $ChaChaXCore) -> Self {
$ChaChaXRng {
core: BlockRng::new(core),
}
}
}

mod $abst {
// 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
Expand Down Expand Up @@ -529,12 +484,12 @@ macro_rules! impl_chacha_rng {
}
}

impl Generator for $ChaChaXCore {
impl Generator for ChaChaCore<$rounds, Legacy> {
type Output = [u32; BUFFER_SIZE];

#[inline]
fn generate(&mut self, r: &mut Self::Output) {
self.0.generate(r);
self.generate(r);
}

#[cfg(feature = "zeroize")]
Expand All @@ -545,11 +500,11 @@ macro_rules! impl_chacha_rng {
};
}

impl_chacha_rng!(ChaCha8Rng, ChaCha8Core, R8, abst8);
impl_chacha_rng!(ChaCha8Rng, R8, abst8);

impl_chacha_rng!(ChaCha12Rng, ChaCha12Core, R12, abst12);
impl_chacha_rng!(ChaCha12Rng, R12, abst12);

impl_chacha_rng!(ChaCha20Rng, ChaCha20Core, R20, abst20);
impl_chacha_rng!(ChaCha20Rng, R20, abst20);

#[cfg(test)]
pub(crate) mod tests {
Expand Down Expand Up @@ -920,7 +875,7 @@ pub(crate) mod tests {
#[test]
fn test_chacha_word_pos_zero() {
let mut rng = ChaChaRng::from_seed(Default::default());
assert_eq!(rng.core.core.0.state[12], 0);
assert_eq!(rng.core.core.state[12], 0);
assert_eq!(rng.core.word_offset(), 0);
assert_eq!(rng.get_word_pos(), 0);
rng.set_word_pos(0);
Expand Down
Loading