Skip to content

Commit 2b18577

Browse files
committed
Implement BlockRngCore for ChaCha
1 parent 0a6ac86 commit 2b18577

File tree

1 file changed

+103
-88
lines changed

1 file changed

+103
-88
lines changed

src/prng/chacha.rs

Lines changed: 103 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@
1111
//! The ChaCha random number generator.
1212
1313
use core::fmt;
14-
use {RngCore, CryptoRng, SeedableRng};
15-
use {impls, le};
14+
use {RngCore, BlockRngCore, CryptoRng, SeedableRng, Error};
15+
use impls::BlockRng;
16+
use le;
1617

1718
const SEED_WORDS: usize = 8; // 8 words for the 256-bit key
1819
const STATE_WORDS: usize = 16;
@@ -62,45 +63,45 @@ const STATE_WORDS: usize = 16;
6263
/// http://cr.yp.to/papers.html#xsalsa)
6364
///
6465
/// [`set_rounds`]: #method.set_counter
65-
#[derive(Clone)]
66-
pub struct ChaChaRng {
67-
buffer: [u32; STATE_WORDS], // Internal buffer of output
68-
state: [u32; STATE_WORDS], // Initial state
69-
index: usize, // Index into state
70-
rounds: usize,
71-
}
66+
#[derive(Clone, Debug)]
67+
pub struct ChaChaRng(BlockRng<ChaChaCore>);
7268

73-
// Custom Debug implementation that does not expose the internal state
74-
impl fmt::Debug for ChaChaRng {
75-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
76-
write!(f, "ChaChaRng {{}}")
69+
impl RngCore for ChaChaRng {
70+
#[inline]
71+
fn next_u32(&mut self) -> u32 {
72+
self.0.next_u32()
7773
}
78-
}
7974

80-
macro_rules! quarter_round{
81-
($a: expr, $b: expr, $c: expr, $d: expr) => {{
82-
$a = $a.wrapping_add($b); $d ^= $a; $d = $d.rotate_left(16);
83-
$c = $c.wrapping_add($d); $b ^= $c; $b = $b.rotate_left(12);
84-
$a = $a.wrapping_add($b); $d ^= $a; $d = $d.rotate_left( 8);
85-
$c = $c.wrapping_add($d); $b ^= $c; $b = $b.rotate_left( 7);
86-
}}
75+
#[inline]
76+
fn next_u64(&mut self) -> u64 {
77+
self.0.next_u64()
78+
}
79+
80+
#[inline]
81+
fn fill_bytes(&mut self, dest: &mut [u8]) {
82+
self.0.fill_bytes(dest)
83+
}
84+
85+
#[inline]
86+
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
87+
self.0.try_fill_bytes(dest)
88+
}
8789
}
8890

89-
macro_rules! double_round{
90-
($x: expr) => {{
91-
// Column round
92-
quarter_round!($x[ 0], $x[ 4], $x[ 8], $x[12]);
93-
quarter_round!($x[ 1], $x[ 5], $x[ 9], $x[13]);
94-
quarter_round!($x[ 2], $x[ 6], $x[10], $x[14]);
95-
quarter_round!($x[ 3], $x[ 7], $x[11], $x[15]);
96-
// Diagonal round
97-
quarter_round!($x[ 0], $x[ 5], $x[10], $x[15]);
98-
quarter_round!($x[ 1], $x[ 6], $x[11], $x[12]);
99-
quarter_round!($x[ 2], $x[ 7], $x[ 8], $x[13]);
100-
quarter_round!($x[ 3], $x[ 4], $x[ 9], $x[14]);
101-
}}
91+
impl SeedableRng for ChaChaRng {
92+
type Seed = <ChaChaCore as SeedableRng>::Seed;
93+
94+
fn from_seed(seed: Self::Seed) -> Self {
95+
ChaChaRng(BlockRng::<ChaChaCore>::from_seed(seed))
96+
}
97+
98+
fn from_rng<R: RngCore>(rng: &mut R) -> Result<Self, Error> {
99+
BlockRng::<ChaChaCore>::from_rng(rng).map(|rng| ChaChaRng(rng))
100+
}
102101
}
103102

103+
impl CryptoRng for ChaChaRng {}
104+
104105
impl ChaChaRng {
105106
/// Create an ChaCha random number generator using the default
106107
/// fixed key of 8 zero words.
@@ -153,11 +154,8 @@ impl ChaChaRng {
153154
/// assert_eq!(rng1.next_u32(), rng2.next_u32());
154155
/// ```
155156
pub fn set_counter(&mut self, counter_low: u64, counter_high: u64) {
156-
self.state[12] = counter_low as u32;
157-
self.state[13] = (counter_low >> 32) as u32;
158-
self.state[14] = counter_high as u32;
159-
self.state[15] = (counter_high >> 32) as u32;
160-
self.index = STATE_WORDS; // force recomputation on next use
157+
self.0.core.set_counter(counter_low, counter_high);
158+
self.0.index = STATE_WORDS; // force recomputation on next use
161159
}
162160

163161
/// Sets the number of rounds to run the ChaCha core algorithm per block to
@@ -180,13 +178,52 @@ impl ChaChaRng {
180178
/// assert_eq!(rng.next_u32(), 0x2fef003e);
181179
/// ```
182180
pub fn set_rounds(&mut self, rounds: usize) {
183-
assert!([4usize, 8, 12, 16, 20].iter().any(|x| *x == rounds));
184-
self.rounds = rounds;
185-
self.index = STATE_WORDS; // force recomputation on next use
181+
self.0.core.set_rounds(rounds);
182+
self.0.index = STATE_WORDS; // force recomputation on next use
186183
}
184+
}
187185

188-
/// Refill the internal output buffer (`self.buffer`)
189-
fn update(&mut self) {
186+
#[derive(Clone)]
187+
pub struct ChaChaCore {
188+
state: [u32; STATE_WORDS],
189+
rounds: usize,
190+
}
191+
192+
// Custom Debug implementation that does not expose the internal state
193+
impl fmt::Debug for ChaChaCore {
194+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
195+
write!(f, "ChaChaCore {{}}")
196+
}
197+
}
198+
199+
macro_rules! quarter_round{
200+
($a: expr, $b: expr, $c: expr, $d: expr) => {{
201+
$a = $a.wrapping_add($b); $d ^= $a; $d = $d.rotate_left(16);
202+
$c = $c.wrapping_add($d); $b ^= $c; $b = $b.rotate_left(12);
203+
$a = $a.wrapping_add($b); $d ^= $a; $d = $d.rotate_left( 8);
204+
$c = $c.wrapping_add($d); $b ^= $c; $b = $b.rotate_left( 7);
205+
}}
206+
}
207+
208+
macro_rules! double_round{
209+
($x: expr) => {{
210+
// Column round
211+
quarter_round!($x[ 0], $x[ 4], $x[ 8], $x[12]);
212+
quarter_round!($x[ 1], $x[ 5], $x[ 9], $x[13]);
213+
quarter_round!($x[ 2], $x[ 6], $x[10], $x[14]);
214+
quarter_round!($x[ 3], $x[ 7], $x[11], $x[15]);
215+
// Diagonal round
216+
quarter_round!($x[ 0], $x[ 5], $x[10], $x[15]);
217+
quarter_round!($x[ 1], $x[ 6], $x[11], $x[12]);
218+
quarter_round!($x[ 2], $x[ 7], $x[ 8], $x[13]);
219+
quarter_round!($x[ 3], $x[ 4], $x[ 9], $x[14]);
220+
}}
221+
}
222+
223+
impl BlockRngCore<u32> for ChaChaCore {
224+
type Results = [u32; STATE_WORDS];
225+
226+
fn generate(&mut self, results: &mut Self::Results) -> Result<(), Error> {
190227
// For some reason extracting this part into a separate function
191228
// improves performance by 50%.
192229
fn core(results: &mut [u32; STATE_WORDS],
@@ -202,71 +239,49 @@ impl ChaChaRng {
202239
}
203240
}
204241

205-
core(&mut self.buffer, &self.state, self.rounds);
206-
self.index = 0;
242+
core(results, &self.state, self.rounds);
243+
207244
// update 128-bit counter
208245
self.state[12] = self.state[12].wrapping_add(1);
209-
if self.state[12] != 0 { return };
246+
if self.state[12] != 0 { return Ok(()) };
210247
self.state[13] = self.state[13].wrapping_add(1);
211-
if self.state[13] != 0 { return };
248+
if self.state[13] != 0 { return Ok(()) };
212249
self.state[14] = self.state[14].wrapping_add(1);
213-
if self.state[14] != 0 { return };
250+
if self.state[14] != 0 { return Ok(()) };
214251
self.state[15] = self.state[15].wrapping_add(1);
252+
Ok(())
215253
}
216254
}
217255

218-
impl RngCore for ChaChaRng {
219-
#[inline]
220-
fn next_u32(&mut self) -> u32 {
221-
// Using a local variable for `index`, and checking the size avoids a
222-
// bounds check later on.
223-
let mut index = self.index as usize;
224-
if index >= STATE_WORDS {
225-
self.update();
226-
index = 0;
227-
}
228-
229-
let value = self.buffer[index];
230-
self.index += 1;
231-
value
232-
}
233-
234-
fn next_u64(&mut self) -> u64 {
235-
impls::next_u64_via_u32(self)
256+
impl ChaChaCore {
257+
/// Sets the internal 128-bit ChaCha counter to a user-provided value. This
258+
/// permits jumping arbitrarily ahead (or backwards) in the pseudorandom
259+
/// stream.
260+
pub fn set_counter(&mut self, counter_low: u64, counter_high: u64) {
261+
self.state[12] = counter_low as u32;
262+
self.state[13] = (counter_low >> 32) as u32;
263+
self.state[14] = counter_high as u32;
264+
self.state[15] = (counter_high >> 32) as u32;
236265
}
237266

238-
239-
fn fill_bytes(&mut self, dest: &mut [u8]) {
240-
let mut read_len = 0;
241-
while read_len < dest.len() {
242-
if self.index >= self.buffer.len() {
243-
self.update();
244-
}
245-
246-
let (consumed_u32, filled_u8) =
247-
impls::fill_via_u32_chunks(&self.buffer[self.index..],
248-
&mut dest[read_len..]);
249-
250-
self.index += consumed_u32;
251-
read_len += filled_u8;
252-
}
267+
/// Sets the number of rounds to run the ChaCha core algorithm per block to
268+
/// generate.
269+
pub fn set_rounds(&mut self, rounds: usize) {
270+
assert!([4usize, 8, 12, 16, 20].iter().any(|x| *x == rounds));
271+
self.rounds = rounds;
253272
}
254273
}
255274

256-
impl CryptoRng for ChaChaRng {}
257-
258-
impl SeedableRng for ChaChaRng {
275+
impl SeedableRng for ChaChaCore {
259276
type Seed = [u8; SEED_WORDS*4];
260277
fn from_seed(seed: Self::Seed) -> Self {
261278
let mut seed_le = [0u32; SEED_WORDS];
262279
le::read_u32_into(&seed, &mut seed_le);
263-
ChaChaRng {
264-
buffer: [0; STATE_WORDS],
280+
Self {
265281
state: [0x61707865, 0x3320646E, 0x79622D32, 0x6B206574, // constants
266282
seed_le[0], seed_le[1], seed_le[2], seed_le[3], // seed
267283
seed_le[4], seed_le[5], seed_le[6], seed_le[7], // seed
268284
0, 0, 0, 0], // counter
269-
index: STATE_WORDS, // generate on first use
270285
rounds: 20,
271286
}
272287
}

0 commit comments

Comments
 (0)