Skip to content

Commit 17b3d4a

Browse files
committed
ChaCha: support reading counter and setting nonce
1 parent 628a952 commit 17b3d4a

File tree

1 file changed

+89
-44
lines changed

1 file changed

+89
-44
lines changed

src/prng/chacha.rs

+89-44
Original file line numberDiff line numberDiff line change
@@ -129,35 +129,92 @@ impl ChaChaRng {
129129
ChaChaRng::from_seed([0; SEED_WORDS*4])
130130
}
131131

132-
/// Sets the internal 128-bit ChaCha counter to a user-provided value. This
133-
/// permits jumping arbitrarily ahead (or backwards) in the pseudorandom
134-
/// stream.
135-
///
136-
/// The 128 bits used for the counter overlap with the nonce and smaller
137-
/// counter of ChaCha when used as a stream cipher. It is in theory possible
138-
/// to use `set_counter` to obtain the conventional ChaCha pseudorandom
139-
/// stream associated with a particular nonce. This is not a supported use
140-
/// of the RNG, because a nonce set that way is not treated as a constant
141-
/// value but still as part of the counter, besides endian issues.
142-
///
143-
/// # Examples
144-
///
145-
/// ```rust
146-
/// use rand::{ChaChaRng, RngCore, SeedableRng};
147-
///
148-
/// // Note: Use `NewRng` or `ChaChaRng::from_rng()` outside of testing.
149-
/// let mut rng1 = ChaChaRng::from_seed([0; 32]);
150-
/// let mut rng2 = rng1.clone();
151-
///
152-
/// // Skip to round 20. Because every round generates 16 `u32` values, this
153-
/// // actually means skipping 320 values.
154-
/// for _ in 0..(20*16) { rng1.next_u32(); }
155-
/// rng2.set_counter(20, 0);
156-
/// assert_eq!(rng1.next_u32(), rng2.next_u32());
157-
/// ```
158-
pub fn set_counter(&mut self, counter_low: u64, counter_high: u64) {
159-
self.0.inner_mut().set_counter(counter_low, counter_high);
160-
self.0.reset(); // force recomputation on next use
132+
/// Get the low 64-bits of the counter.
133+
///
134+
/// Note that the low 32-bits of the counter are sufficient to produce
135+
/// 256 GiB of data and 64-bits are enough to produce 1 ZiB of data
136+
/// (2<sup>70</sup> bytes), thus 64-bits are sufficient unless one also
137+
/// wants to read a nonce.
138+
///
139+
/// If only the low 32-bits of the counter are wanted, simply cast:
140+
/// `get_counter() as u32`.
141+
pub fn get_counter(&self) -> u64 {
142+
let core = self.0.inner();
143+
core.state[12] as u64 + ((core.state[13] as u64) << 32)
144+
}
145+
146+
/// Get the full 128-bit counter.
147+
///
148+
/// The upper half of this counter is likely to be zero unless a nonce has
149+
/// been set, therefore `get_counter()` will usually suffice.
150+
#[cfg(feature="i128_support")]
151+
pub fn get_counter_128(&self) -> u128 {
152+
let core = self.0.inner();
153+
let low = core.state[12] as u64
154+
+ ((core.state[13] as u64) << 32);
155+
let high = core.state[14] as u64
156+
+ ((core.state[15] as u64) << 32);
157+
low as u128 + ((high as u128) << 64)
158+
}
159+
160+
/// Set the counter.
161+
///
162+
/// Note that this sets the full 128-bit counter by setting the upper
163+
/// 64-bits to zero. If setting a nonce, do so *after* setting the counter.
164+
pub fn set_counter(&mut self, counter: u64) {
165+
let core = self.0.inner_mut();
166+
core.state[12] = counter as u32;
167+
core.state[13] = (counter >> 32) as u32;
168+
core.state[14] = 0;
169+
core.state[15] = 0;
170+
}
171+
172+
/// Set the full counter.
173+
#[cfg(feature="i128_support")]
174+
pub fn set_counter_128(&mut self, counter: u128) {
175+
let core = self.0.inner_mut();
176+
core.state[12] = counter as u32;
177+
core.state[13] = (counter >> 32) as u32;
178+
core.state[14] = (counter >> 64) as u32;
179+
core.state[15] = (counter >> 96) as u32;
180+
}
181+
182+
/// Set a 64-bit nonce.
183+
///
184+
/// The original ChaCha cipher takes a 64-bit counter and 64-bit nonce.
185+
/// Output can be replicated by setting the counter (if non-zero) then
186+
/// calling this function with the nonce.
187+
///
188+
/// It is possible to achieve the same result with a single call to
189+
/// `set_counter_128`, but in that case be careful of Endianness when
190+
/// converting the nonce.
191+
pub fn set_nonce_64(&mut self, nonce: [u8; 8]) {
192+
let mut nonce_le = [0u32; 2];
193+
le::read_u32_into(&nonce, &mut nonce_le);
194+
let core = self.0.inner_mut();
195+
core.state[14] = nonce_le[0];
196+
core.state[15] = nonce_le[1];
197+
}
198+
199+
/// Set a 96-bit nonce.
200+
///
201+
/// The IETF ChaCha cipher standard takes a 32-bit counter and 96-bit nonce.
202+
/// Output can be replicated by setting the counter (if non-zero) then
203+
/// calling this function with the nonce, though note that after 256 GiB of
204+
/// output the low 32-bits of the nonce will be incremented and this RNG
205+
/// will continue to produce output, instead of terminating as the IETF
206+
/// cipher would.
207+
///
208+
/// It is possible to achieve the same result with a single call to
209+
/// `set_counter_128`, but in that case be careful of Endianness when
210+
/// converting the nonce.
211+
pub fn set_nonce_96(&mut self, nonce: [u8; 12]) {
212+
let mut nonce_le = [0u32; 3];
213+
le::read_u32_into(&nonce, &mut nonce_le);
214+
let core = self.0.inner_mut();
215+
core.state[13] = nonce_le[0];
216+
core.state[14] = nonce_le[1];
217+
core.state[15] = nonce_le[2];
161218
}
162219

163220
/// Sets the number of rounds to run the ChaCha core algorithm per block to
@@ -256,16 +313,6 @@ impl BlockRngCore for ChaChaCore {
256313
}
257314

258315
impl ChaChaCore {
259-
/// Sets the internal 128-bit ChaCha counter to a user-provided value. This
260-
/// permits jumping arbitrarily ahead (or backwards) in the pseudorandom
261-
/// stream.
262-
pub fn set_counter(&mut self, counter_low: u64, counter_high: u64) {
263-
self.state[12] = counter_low as u32;
264-
self.state[13] = (counter_low >> 32) as u32;
265-
self.state[14] = counter_high as u32;
266-
self.state[15] = (counter_high >> 32) as u32;
267-
}
268-
269316
/// Sets the number of rounds to run the ChaCha core algorithm per block to
270317
/// generate.
271318
pub fn set_rounds(&mut self, rounds: usize) {
@@ -377,7 +424,7 @@ mod test {
377424

378425
// Test block 2 by using `set_counter`
379426
let mut rng2 = ChaChaRng::from_seed(seed);
380-
rng2.set_counter(2, 0);
427+
rng2.set_counter(2);
381428
for i in results.iter_mut() { *i = rng2.next_u32(); }
382429
assert_eq!(results, expected);
383430
}
@@ -417,14 +464,12 @@ mod test {
417464
}
418465

419466
#[test]
420-
fn test_chacha_set_counter() {
467+
fn test_chacha_nonce() {
421468
// Test vector 5 from
422469
// https://tools.ietf.org/html/draft-nir-cfrg-chacha20-poly1305-04
423-
// Although we do not support setting a nonce, we try it here anyway so
424-
// we can use this test vector.
425470
let seed = [0u8; 32];
426471
let mut rng = ChaChaRng::from_seed(seed);
427-
rng.set_counter(0, 2u64 << 56);
472+
rng.set_nonce_96([0,0,0,0, 0,0,0,0, 0,0,0,2]);
428473

429474
let mut results = [0u32; 16];
430475
for i in results.iter_mut() { *i = rng.next_u32(); }

0 commit comments

Comments
 (0)