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: Array64 = 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}
652