1// Copyright 2018 Developers of the Rand project.
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! The ChaCha random number generator.
10
11#[cfg(not(feature = "std"))] use core;
12#[cfg(feature = "std")] use std as core;
13
14use self::core::fmt;
15use crate::guts::ChaCha;
16use rand_core::block::{BlockRng, BlockRngCore};
17use rand_core::{CryptoRng, Error, RngCore, SeedableRng};
18
19#[cfg(feature = "serde1")] use serde::{Serialize, Deserialize, Serializer, Deserializer};
20
21// NB. this must remain consistent with some currently hard-coded numbers in this module
22const BUF_BLOCKS: u8 = 4;
23// number of 32-bit words per ChaCha block (fixed by algorithm definition)
24const BLOCK_WORDS: u8 = 16;
25
26#[repr(transparent)]
27pub struct Array64<T>([T; 64]);
28impl<T> Default for Array64<T>
29where T: Default
30{
31 #[rustfmt::skip]
32 fn default() -> Self {
33 Self([
34 T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(),
35 T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(),
36 T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(),
37 T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(),
38 T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(),
39 T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(),
40 T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(),
41 T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(),
42 ])
43 }
44}
45impl<T> AsRef<[T]> for Array64<T> {
46 fn as_ref(&self) -> &[T] {
47 &self.0
48 }
49}
50impl<T> AsMut<[T]> for Array64<T> {
51 fn as_mut(&mut self) -> &mut [T] {
52 &mut self.0
53 }
54}
55impl<T> Clone for Array64<T>
56where T: Copy + Default
57{
58 fn clone(&self) -> Self {
59 let mut new = Self::default();
60 new.0.copy_from_slice(&self.0);
61 new
62 }
63}
64impl<T> fmt::Debug for Array64<T> {
65 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
66 write!(f, "Array64 {{}}")
67 }
68}
69
70macro_rules! chacha_impl {
71 ($ChaChaXCore:ident, $ChaChaXRng:ident, $rounds:expr, $doc:expr, $abst:ident) => {
72 #[doc=$doc]
73 #[derive(Clone, PartialEq, Eq)]
74 pub struct $ChaChaXCore {
75 state: ChaCha,
76 }
77
78 // Custom Debug implementation that does not expose the internal state
79 impl fmt::Debug for $ChaChaXCore {
80 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
81 write!(f, "ChaChaXCore {{}}")
82 }
83 }
84
85 impl BlockRngCore for $ChaChaXCore {
86 type Item = u32;
87 type Results = Array64<u32>;
88 #[inline]
89 fn generate(&mut self, r: &mut Self::Results) {
90 // Fill slice of words by writing to equivalent slice of bytes, then fixing endianness.
91 self.state.refill4($rounds, unsafe {
92 &mut *(&mut *r as *mut Array64<u32> as *mut [u8; 256])
93 });
94 for x in r.as_mut() {
95 *x = x.to_le();
96 }
97 }
98 }
99
100 impl SeedableRng for $ChaChaXCore {
101 type Seed = [u8; 32];
102 #[inline]
103 fn from_seed(seed: Self::Seed) -> Self {
104 $ChaChaXCore { state: ChaCha::new(&seed, &[0u8; 8]) }
105 }
106 }
107
108 impl CryptoRng for $ChaChaXCore {}
109
110 /// A cryptographically secure random number generator that uses the ChaCha algorithm.
111 ///
112 /// ChaCha is a stream cipher designed by Daniel J. Bernstein[^1], that we use as an RNG. It is
113 /// an improved variant of the Salsa20 cipher family, which was selected as one of the "stream
114 /// ciphers suitable for widespread adoption" by eSTREAM[^2].
115 ///
116 /// ChaCha uses add-rotate-xor (ARX) operations as its basis. These are safe against timing
117 /// attacks, although that is mostly a concern for ciphers and not for RNGs. We provide a SIMD
118 /// implementation to support high throughput on a variety of common hardware platforms.
119 ///
120 /// With the ChaCha algorithm it is possible to choose the number of rounds the core algorithm
121 /// should run. The number of rounds is a tradeoff between performance and security, where 8
122 /// rounds is the minimum potentially secure configuration, and 20 rounds is widely used as a
123 /// conservative choice.
124 ///
125 /// We use a 64-bit counter and 64-bit stream identifier as in Bernstein's implementation[^1]
126 /// except that we use a stream identifier in place of a nonce. A 64-bit counter over 64-byte
127 /// (16 word) blocks allows 1 ZiB of output before cycling, and the stream identifier allows
128 /// 2<sup>64</sup> unique streams of output per seed. Both counter and stream are initialized
129 /// to zero but may be set via the `set_word_pos` and `set_stream` methods.
130 ///
131 /// The word layout is:
132 ///
133 /// ```text
134 /// constant constant constant constant
135 /// seed seed seed seed
136 /// seed seed seed seed
137 /// counter counter stream_id stream_id
138 /// ```
139 ///
140 /// This implementation uses an output buffer of sixteen `u32` words, and uses
141 /// [`BlockRng`] to implement the [`RngCore`] methods.
142 ///
143 /// [^1]: D. J. Bernstein, [*ChaCha, a variant of Salsa20*](
144 /// https://cr.yp.to/chacha.html)
145 ///
146 /// [^2]: [eSTREAM: the ECRYPT Stream Cipher Project](
147 /// http://www.ecrypt.eu.org/stream/)
148 #[derive(Clone, Debug)]
149 pub struct $ChaChaXRng {
150 rng: BlockRng<$ChaChaXCore>,
151 }
152
153 impl SeedableRng for $ChaChaXRng {
154 type Seed = [u8; 32];
155 #[inline]
156 fn from_seed(seed: Self::Seed) -> Self {
157 let core = $ChaChaXCore::from_seed(seed);
158 Self {
159 rng: BlockRng::new(core),
160 }
161 }
162 }
163
164 impl RngCore for $ChaChaXRng {
165 #[inline]
166 fn next_u32(&mut self) -> u32 {
167 self.rng.next_u32()
168 }
169 #[inline]
170 fn next_u64(&mut self) -> u64 {
171 self.rng.next_u64()
172 }
173 #[inline]
174 fn fill_bytes(&mut self, bytes: &mut [u8]) {
175 self.rng.fill_bytes(bytes)
176 }
177 #[inline]
178 fn try_fill_bytes(&mut self, bytes: &mut [u8]) -> Result<(), Error> {
179 self.rng.try_fill_bytes(bytes)
180 }
181 }
182
183 impl $ChaChaXRng {
184 // The buffer is a 4-block window, i.e. it is always at a block-aligned position in the
185 // stream but if the stream has been seeked it may not be self-aligned.
186
187 /// Get the offset from the start of the stream, in 32-bit words.
188 ///
189 /// Since the generated blocks are 16 words (2<sup>4</sup>) long and the
190 /// counter is 64-bits, the offset is a 68-bit number. Sub-word offsets are
191 /// not supported, hence the result can simply be multiplied by 4 to get a
192 /// byte-offset.
193 #[inline]
194 pub fn get_word_pos(&self) -> u128 {
195 let buf_start_block = {
196 let buf_end_block = self.rng.core.state.get_block_pos();
197 u64::wrapping_sub(buf_end_block, BUF_BLOCKS.into())
198 };
199 let (buf_offset_blocks, block_offset_words) = {
200 let buf_offset_words = self.rng.index() as u64;
201 let blocks_part = buf_offset_words / u64::from(BLOCK_WORDS);
202 let words_part = buf_offset_words % u64::from(BLOCK_WORDS);
203 (blocks_part, words_part)
204 };
205 let pos_block = u64::wrapping_add(buf_start_block, buf_offset_blocks);
206 let pos_block_words = u128::from(pos_block) * u128::from(BLOCK_WORDS);
207 pos_block_words + u128::from(block_offset_words)
208 }
209
210 /// Set the offset from the start of the stream, in 32-bit words.
211 ///
212 /// As with `get_word_pos`, we use a 68-bit number. Since the generator
213 /// simply cycles at the end of its period (1 ZiB), we ignore the upper
214 /// 60 bits.
215 #[inline]
216 pub fn set_word_pos(&mut self, word_offset: u128) {
217 let block = (word_offset / u128::from(BLOCK_WORDS)) as u64;
218 self.rng
219 .core
220 .state
221 .set_block_pos(block);
222 self.rng.generate_and_set((word_offset % u128::from(BLOCK_WORDS)) as usize);
223 }
224
225 /// Set the stream number.
226 ///
227 /// This is initialized to zero; 2<sup>64</sup> unique streams of output
228 /// are available per seed/key.
229 ///
230 /// Note that in order to reproduce ChaCha output with a specific 64-bit
231 /// nonce, one can convert that nonce to a `u64` in little-endian fashion
232 /// and pass to this function. In theory a 96-bit nonce can be used by
233 /// passing the last 64-bits to this function and using the first 32-bits as
234 /// the most significant half of the 64-bit counter (which may be set
235 /// indirectly via `set_word_pos`), but this is not directly supported.
236 #[inline]
237 pub fn set_stream(&mut self, stream: u64) {
238 self.rng
239 .core
240 .state
241 .set_nonce(stream);
242 if self.rng.index() != 64 {
243 let wp = self.get_word_pos();
244 self.set_word_pos(wp);
245 }
246 }
247
248 /// Get the stream number.
249 #[inline]
250 pub fn get_stream(&self) -> u64 {
251 self.rng
252 .core
253 .state
254 .get_nonce()
255 }
256
257 /// Get the seed.
258 #[inline]
259 pub fn get_seed(&self) -> [u8; 32] {
260 self.rng
261 .core
262 .state
263 .get_seed()
264 }
265 }
266
267 impl CryptoRng for $ChaChaXRng {}
268
269 impl From<$ChaChaXCore> for $ChaChaXRng {
270 fn from(core: $ChaChaXCore) -> Self {
271 $ChaChaXRng {
272 rng: BlockRng::new(core),
273 }
274 }
275 }
276
277 impl PartialEq<$ChaChaXRng> for $ChaChaXRng {
278 fn eq(&self, rhs: &$ChaChaXRng) -> bool {
279 let a: $abst::$ChaChaXRng = self.into();
280 let b: $abst::$ChaChaXRng = rhs.into();
281 a == b
282 }
283 }
284 impl Eq for $ChaChaXRng {}
285
286 #[cfg(feature = "serde1")]
287 impl Serialize for $ChaChaXRng {
288 fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
289 where S: Serializer {
290 $abst::$ChaChaXRng::from(self).serialize(s)
291 }
292 }
293 #[cfg(feature = "serde1")]
294 impl<'de> Deserialize<'de> for $ChaChaXRng {
295 fn deserialize<D>(d: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
296 $abst::$ChaChaXRng::deserialize(d).map(|x| Self::from(&x))
297 }
298 }
299
300 mod $abst {
301 #[cfg(feature = "serde1")] use serde::{Serialize, Deserialize};
302
303 // The abstract state of a ChaCha stream, independent of implementation choices. The
304 // comparison and serialization of this object is considered a semver-covered part of
305 // the API.
306 #[derive(Debug, PartialEq, Eq)]
307 #[cfg_attr(
308 feature = "serde1",
309 derive(Serialize, Deserialize),
310 )]
311 pub(crate) struct $ChaChaXRng {
312 seed: [u8; 32],
313 stream: u64,
314 word_pos: u128,
315 }
316
317 impl From<&super::$ChaChaXRng> for $ChaChaXRng {
318 // Forget all information about the input except what is necessary to determine the
319 // outputs of any sequence of pub API calls.
320 fn from(r: &super::$ChaChaXRng) -> Self {
321 Self {
322 seed: r.get_seed(),
323 stream: r.get_stream(),
324 word_pos: r.get_word_pos(),
325 }
326 }
327 }
328
329 impl From<&$ChaChaXRng> for super::$ChaChaXRng {
330 // Construct one of the possible concrete RNGs realizing an abstract state.
331 fn from(a: &$ChaChaXRng) -> Self {
332 use rand_core::SeedableRng;
333 let mut r = Self::from_seed(a.seed);
334 r.set_stream(a.stream);
335 r.set_word_pos(a.word_pos);
336 r
337 }
338 }
339 }
340 }
341}
342
343chacha_impl!(ChaCha20Core, ChaCha20Rng, 10, "ChaCha with 20 rounds", abstract20);
344chacha_impl!(ChaCha12Core, ChaCha12Rng, 6, "ChaCha with 12 rounds", abstract12);
345chacha_impl!(ChaCha8Core, ChaCha8Rng, 4, "ChaCha with 8 rounds", abstract8);
346
347#[cfg(test)]
348mod test {
349 use rand_core::{RngCore, SeedableRng};
350
351 #[cfg(feature = "serde1")] use super::{ChaCha20Rng, ChaCha12Rng, ChaCha8Rng};
352
353 type ChaChaRng = super::ChaCha20Rng;
354
355 #[cfg(feature = "serde1")]
356 #[test]
357 fn test_chacha_serde_roundtrip() {
358 let seed = [
359 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,
360 0, 2, 92,
361 ];
362 let mut rng1 = ChaCha20Rng::from_seed(seed);
363 let mut rng2 = ChaCha12Rng::from_seed(seed);
364 let mut rng3 = ChaCha8Rng::from_seed(seed);
365
366 let encoded1 = serde_json::to_string(&rng1).unwrap();
367 let encoded2 = serde_json::to_string(&rng2).unwrap();
368 let encoded3 = serde_json::to_string(&rng3).unwrap();
369
370 let mut decoded1: ChaCha20Rng = serde_json::from_str(&encoded1).unwrap();
371 let mut decoded2: ChaCha12Rng = serde_json::from_str(&encoded2).unwrap();
372 let mut decoded3: ChaCha8Rng = serde_json::from_str(&encoded3).unwrap();
373
374 assert_eq!(rng1, decoded1);
375 assert_eq!(rng2, decoded2);
376 assert_eq!(rng3, decoded3);
377
378 assert_eq!(rng1.next_u32(), decoded1.next_u32());
379 assert_eq!(rng2.next_u32(), decoded2.next_u32());
380 assert_eq!(rng3.next_u32(), decoded3.next_u32());
381 }
382
383 // This test validates that:
384 // 1. a hard-coded serialization demonstrating the format at time of initial release can still
385 // be deserialized to a ChaChaRng
386 // 2. re-serializing the resultant object produces exactly the original string
387 //
388 // Condition 2 is stronger than necessary: an equivalent serialization (e.g. with field order
389 // permuted, or whitespace differences) would also be admissible, but would fail this test.
390 // However testing for equivalence of serialized data is difficult, and there shouldn't be any
391 // reason we need to violate the stronger-than-needed condition, e.g. by changing the field
392 // definition order.
393 #[cfg(feature = "serde1")]
394 #[test]
395 fn test_chacha_serde_format_stability() {
396 let j = r#"{"seed":[4,8,15,16,23,42,4,8,15,16,23,42,4,8,15,16,23,42,4,8,15,16,23,42,4,8,15,16,23,42,4,8],"stream":27182818284,"word_pos":314159265359}"#;
397 let r: ChaChaRng = serde_json::from_str(&j).unwrap();
398 let j1 = serde_json::to_string(&r).unwrap();
399 assert_eq!(j, j1);
400 }
401
402 #[test]
403 fn test_chacha_construction() {
404 let seed = [
405 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0,
406 0, 0, 0,
407 ];
408 let mut rng1 = ChaChaRng::from_seed(seed);
409 assert_eq!(rng1.next_u32(), 137206642);
410
411 let mut rng2 = ChaChaRng::from_rng(rng1).unwrap();
412 assert_eq!(rng2.next_u32(), 1325750369);
413 }
414
415 #[test]
416 fn test_chacha_true_values_a() {
417 // Test vectors 1 and 2 from
418 // https://tools.ietf.org/html/draft-nir-cfrg-chacha20-poly1305-04
419 let seed = [0u8; 32];
420 let mut rng = ChaChaRng::from_seed(seed);
421
422 let mut results = [0u32; 16];
423 for i in results.iter_mut() {
424 *i = rng.next_u32();
425 }
426 let expected = [
427 0xade0b876, 0x903df1a0, 0xe56a5d40, 0x28bd8653, 0xb819d2bd, 0x1aed8da0, 0xccef36a8,
428 0xc70d778b, 0x7c5941da, 0x8d485751, 0x3fe02477, 0x374ad8b8, 0xf4b8436a, 0x1ca11815,
429 0x69b687c3, 0x8665eeb2,
430 ];
431 assert_eq!(results, expected);
432
433 for i in results.iter_mut() {
434 *i = rng.next_u32();
435 }
436 let expected = [
437 0xbee7079f, 0x7a385155, 0x7c97ba98, 0x0d082d73, 0xa0290fcb, 0x6965e348, 0x3e53c612,
438 0xed7aee32, 0x7621b729, 0x434ee69c, 0xb03371d5, 0xd539d874, 0x281fed31, 0x45fb0a51,
439 0x1f0ae1ac, 0x6f4d794b,
440 ];
441 assert_eq!(results, expected);
442 }
443
444 #[test]
445 fn test_chacha_true_values_b() {
446 // Test vector 3 from
447 // https://tools.ietf.org/html/draft-nir-cfrg-chacha20-poly1305-04
448 let seed = [
449 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
450 0, 0, 1,
451 ];
452 let mut rng = ChaChaRng::from_seed(seed);
453
454 // Skip block 0
455 for _ in 0..16 {
456 rng.next_u32();
457 }
458
459 let mut results = [0u32; 16];
460 for i in results.iter_mut() {
461 *i = rng.next_u32();
462 }
463 let expected = [
464 0x2452eb3a, 0x9249f8ec, 0x8d829d9b, 0xddd4ceb1, 0xe8252083, 0x60818b01, 0xf38422b8,
465 0x5aaa49c9, 0xbb00ca8e, 0xda3ba7b4, 0xc4b592d1, 0xfdf2732f, 0x4436274e, 0x2561b3c8,
466 0xebdd4aa6, 0xa0136c00,
467 ];
468 assert_eq!(results, expected);
469 }
470
471 #[test]
472 fn test_chacha_true_values_c() {
473 // Test vector 4 from
474 // https://tools.ietf.org/html/draft-nir-cfrg-chacha20-poly1305-04
475 let seed = [
476 0, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
477 0, 0, 0, 0,
478 ];
479 let expected = [
480 0xfb4dd572, 0x4bc42ef1, 0xdf922636, 0x327f1394, 0xa78dea8f, 0x5e269039, 0xa1bebbc1,
481 0xcaf09aae, 0xa25ab213, 0x48a6b46c, 0x1b9d9bcb, 0x092c5be6, 0x546ca624, 0x1bec45d5,
482 0x87f47473, 0x96f0992e,
483 ];
484 let expected_end = 3 * 16;
485 let mut results = [0u32; 16];
486
487 // Test block 2 by skipping block 0 and 1
488 let mut rng1 = ChaChaRng::from_seed(seed);
489 for _ in 0..32 {
490 rng1.next_u32();
491 }
492 for i in results.iter_mut() {
493 *i = rng1.next_u32();
494 }
495 assert_eq!(results, expected);
496 assert_eq!(rng1.get_word_pos(), expected_end);
497
498 // Test block 2 by using `set_word_pos`
499 let mut rng2 = ChaChaRng::from_seed(seed);
500 rng2.set_word_pos(2 * 16);
501 for i in results.iter_mut() {
502 *i = rng2.next_u32();
503 }
504 assert_eq!(results, expected);
505 assert_eq!(rng2.get_word_pos(), expected_end);
506
507 // Test skipping behaviour with other types
508 let mut buf = [0u8; 32];
509 rng2.fill_bytes(&mut buf[..]);
510 assert_eq!(rng2.get_word_pos(), expected_end + 8);
511 rng2.fill_bytes(&mut buf[0..25]);
512 assert_eq!(rng2.get_word_pos(), expected_end + 15);
513 rng2.next_u64();
514 assert_eq!(rng2.get_word_pos(), expected_end + 17);
515 rng2.next_u32();
516 rng2.next_u64();
517 assert_eq!(rng2.get_word_pos(), expected_end + 20);
518 rng2.fill_bytes(&mut buf[0..1]);
519 assert_eq!(rng2.get_word_pos(), expected_end + 21);
520 }
521
522 #[test]
523 fn test_chacha_multiple_blocks() {
524 let seed = [
525 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 6, 0, 0, 0, 7,
526 0, 0, 0,
527 ];
528 let mut rng = ChaChaRng::from_seed(seed);
529
530 // Store the 17*i-th 32-bit word,
531 // i.e., the i-th word of the i-th 16-word block
532 let mut results = [0u32; 16];
533 for i in results.iter_mut() {
534 *i = rng.next_u32();
535 for _ in 0..16 {
536 rng.next_u32();
537 }
538 }
539 let expected = [
540 0xf225c81a, 0x6ab1be57, 0x04d42951, 0x70858036, 0x49884684, 0x64efec72, 0x4be2d186,
541 0x3615b384, 0x11cfa18e, 0xd3c50049, 0x75c775f6, 0x434c6530, 0x2c5bad8f, 0x898881dc,
542 0x5f1c86d9, 0xc1f8e7f4,
543 ];
544 assert_eq!(results, expected);
545 }
546
547 #[test]
548 fn test_chacha_true_bytes() {
549 let seed = [0u8; 32];
550 let mut rng = ChaChaRng::from_seed(seed);
551 let mut results = [0u8; 32];
552 rng.fill_bytes(&mut results);
553 let expected = [
554 118, 184, 224, 173, 160, 241, 61, 144, 64, 93, 106, 229, 83, 134, 189, 40, 189, 210,
555 25, 184, 160, 141, 237, 26, 168, 54, 239, 204, 139, 119, 13, 199,
556 ];
557 assert_eq!(results, expected);
558 }
559
560 #[test]
561 fn test_chacha_nonce() {
562 // Test vector 5 from
563 // https://tools.ietf.org/html/draft-nir-cfrg-chacha20-poly1305-04
564 // Although we do not support setting a nonce, we try it here anyway so
565 // we can use this test vector.
566 let seed = [0u8; 32];
567 let mut rng = ChaChaRng::from_seed(seed);
568 // 96-bit nonce in LE order is: 0,0,0,0, 0,0,0,0, 0,0,0,2
569 rng.set_stream(2u64 << (24 + 32));
570
571 let mut results = [0u32; 16];
572 for i in results.iter_mut() {
573 *i = rng.next_u32();
574 }
575 let expected = [
576 0x374dc6c2, 0x3736d58c, 0xb904e24a, 0xcd3f93ef, 0x88228b1a, 0x96a4dfb3, 0x5b76ab72,
577 0xc727ee54, 0x0e0e978a, 0xf3145c95, 0x1b748ea8, 0xf786c297, 0x99c28f5f, 0x628314e8,
578 0x398a19fa, 0x6ded1b53,
579 ];
580 assert_eq!(results, expected);
581 }
582
583 #[test]
584 fn test_chacha_clone_streams() {
585 let seed = [
586 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 6, 0, 0, 0, 7,
587 0, 0, 0,
588 ];
589 let mut rng = ChaChaRng::from_seed(seed);
590 let mut clone = rng.clone();
591 for _ in 0..16 {
592 assert_eq!(rng.next_u64(), clone.next_u64());
593 }
594
595 rng.set_stream(51);
596 for _ in 0..7 {
597 assert!(rng.next_u32() != clone.next_u32());
598 }
599 clone.set_stream(51); // switch part way through block
600 for _ in 7..16 {
601 assert_eq!(rng.next_u32(), clone.next_u32());
602 }
603 }
604
605 #[test]
606 fn test_chacha_word_pos_wrap_exact() {
607 use super::{BUF_BLOCKS, BLOCK_WORDS};
608 let mut rng = ChaChaRng::from_seed(Default::default());
609 // refilling the buffer in set_word_pos will wrap the block counter to 0
610 let last_block = (1 << 68) - u128::from(BUF_BLOCKS * BLOCK_WORDS);
611 rng.set_word_pos(last_block);
612 assert_eq!(rng.get_word_pos(), last_block);
613 }
614
615 #[test]
616 fn test_chacha_word_pos_wrap_excess() {
617 use super::BLOCK_WORDS;
618 let mut rng = ChaChaRng::from_seed(Default::default());
619 // refilling the buffer in set_word_pos will wrap the block counter past 0
620 let last_block = (1 << 68) - u128::from(BLOCK_WORDS);
621 rng.set_word_pos(last_block);
622 assert_eq!(rng.get_word_pos(), last_block);
623 }
624
625 #[test]
626 fn test_chacha_word_pos_zero() {
627 let mut rng = ChaChaRng::from_seed(Default::default());
628 assert_eq!(rng.get_word_pos(), 0);
629 rng.set_word_pos(0);
630 assert_eq!(rng.get_word_pos(), 0);
631 }
632}
633