rand_chacha/
chacha.rs

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}