Skip to main content

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