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