Coverage Report

Created: 2025-07-23 07:04

/rust/registry/src/index.crates.io-6f17d22bba15001f/rand_chacha-0.9.0/src/chacha.rs
Line
Count
Source (jump to first uncovered line)
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
use crate::guts::ChaCha;
12
use core::fmt;
13
use rand_core::block::{BlockRng, BlockRngCore, CryptoBlockRng};
14
use rand_core::{CryptoRng, RngCore, SeedableRng};
15
16
#[cfg(feature = "serde")]
17
use serde::{Deserialize, Deserializer, Serialize, Serializer};
18
19
// NB. this must remain consistent with some currently hard-coded numbers in this module
20
const BUF_BLOCKS: u8 = 4;
21
// number of 32-bit words per ChaCha block (fixed by algorithm definition)
22
const BLOCK_WORDS: u8 = 16;
23
24
#[repr(transparent)]
25
pub struct Array64<T>([T; 64]);
26
impl<T> Default for Array64<T>
27
where
28
    T: Default,
29
{
30
    #[rustfmt::skip]
31
0
    fn default() -> Self {
32
0
        Self([
33
0
            T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(),
34
0
            T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(),
35
0
            T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(),
36
0
            T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(),
37
0
            T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(),
38
0
            T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(),
39
0
            T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(),
40
0
            T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(),
41
0
        ])
42
0
    }
43
}
44
impl<T> AsRef<[T]> for Array64<T> {
45
0
    fn as_ref(&self) -> &[T] {
46
0
        &self.0
47
0
    }
48
}
49
impl<T> AsMut<[T]> for Array64<T> {
50
0
    fn as_mut(&mut self) -> &mut [T] {
51
0
        &mut self.0
52
0
    }
53
}
54
impl<T> Clone for Array64<T>
55
where
56
    T: Copy + Default,
57
{
58
0
    fn clone(&self) -> Self {
59
0
        let mut new = Self::default();
60
0
        new.0.copy_from_slice(&self.0);
61
0
        new
62
0
    }
63
}
64
impl<T> fmt::Debug for Array64<T> {
65
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
66
0
        write!(f, "Array64 {{}}")
67
0
    }
68
}
69
70
macro_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
0
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
81
0
                write!(f, "ChaChaXCore {{}}")
82
0
            }
Unexecuted instantiation: <rand_chacha::chacha::ChaCha20Core as core::fmt::Debug>::fmt
Unexecuted instantiation: <rand_chacha::chacha::ChaCha12Core as core::fmt::Debug>::fmt
Unexecuted instantiation: <rand_chacha::chacha::ChaCha8Core as core::fmt::Debug>::fmt
83
        }
84
85
        impl BlockRngCore for $ChaChaXCore {
86
            type Item = u32;
87
            type Results = Array64<u32>;
88
89
            #[inline]
90
0
            fn generate(&mut self, r: &mut Self::Results) {
91
0
                self.state.refill4($rounds, &mut r.0);
92
0
            }
Unexecuted instantiation: <rand_chacha::chacha::ChaCha12Core as rand_core::block::BlockRngCore>::generate
Unexecuted instantiation: <rand_chacha::chacha::ChaCha20Core as rand_core::block::BlockRngCore>::generate
Unexecuted instantiation: <rand_chacha::chacha::ChaCha12Core as rand_core::block::BlockRngCore>::generate
Unexecuted instantiation: <rand_chacha::chacha::ChaCha8Core as rand_core::block::BlockRngCore>::generate
93
        }
94
95
        impl SeedableRng for $ChaChaXCore {
96
            type Seed = [u8; 32];
97
98
            #[inline]
99
0
            fn from_seed(seed: Self::Seed) -> Self {
100
0
                $ChaChaXCore {
101
0
                    state: ChaCha::new(&seed, &[0u8; 8]),
102
0
                }
103
0
            }
Unexecuted instantiation: <rand_chacha::chacha::ChaCha12Core as rand_core::SeedableRng>::from_seed
Unexecuted instantiation: <rand_chacha::chacha::ChaCha20Core as rand_core::SeedableRng>::from_seed
Unexecuted instantiation: <rand_chacha::chacha::ChaCha12Core as rand_core::SeedableRng>::from_seed
Unexecuted instantiation: <rand_chacha::chacha::ChaCha8Core as rand_core::SeedableRng>::from_seed
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
0
            fn from_seed(seed: Self::Seed) -> Self {
156
0
                let core = $ChaChaXCore::from_seed(seed);
157
0
                Self {
158
0
                    rng: BlockRng::new(core),
159
0
                }
160
0
            }
Unexecuted instantiation: <rand_chacha::chacha::ChaCha20Rng as rand_core::SeedableRng>::from_seed
Unexecuted instantiation: <rand_chacha::chacha::ChaCha12Rng as rand_core::SeedableRng>::from_seed
Unexecuted instantiation: <rand_chacha::chacha::ChaCha8Rng as rand_core::SeedableRng>::from_seed
161
        }
162
163
        impl RngCore for $ChaChaXRng {
164
            #[inline]
165
0
            fn next_u32(&mut self) -> u32 {
166
0
                self.rng.next_u32()
167
0
            }
Unexecuted instantiation: <rand_chacha::chacha::ChaCha20Rng as rand_core::RngCore>::next_u32
Unexecuted instantiation: <rand_chacha::chacha::ChaCha12Rng as rand_core::RngCore>::next_u32
Unexecuted instantiation: <rand_chacha::chacha::ChaCha8Rng as rand_core::RngCore>::next_u32
168
169
            #[inline]
170
0
            fn next_u64(&mut self) -> u64 {
171
0
                self.rng.next_u64()
172
0
            }
Unexecuted instantiation: <rand_chacha::chacha::ChaCha20Rng as rand_core::RngCore>::next_u64
Unexecuted instantiation: <rand_chacha::chacha::ChaCha12Rng as rand_core::RngCore>::next_u64
Unexecuted instantiation: <rand_chacha::chacha::ChaCha8Rng as rand_core::RngCore>::next_u64
173
174
            #[inline]
175
0
            fn fill_bytes(&mut self, bytes: &mut [u8]) {
176
0
                self.rng.fill_bytes(bytes)
177
0
            }
Unexecuted instantiation: <rand_chacha::chacha::ChaCha20Rng as rand_core::RngCore>::fill_bytes
Unexecuted instantiation: <rand_chacha::chacha::ChaCha12Rng as rand_core::RngCore>::fill_bytes
Unexecuted instantiation: <rand_chacha::chacha::ChaCha8Rng as rand_core::RngCore>::fill_bytes
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
0
            pub fn get_word_pos(&self) -> u128 {
192
0
                let buf_start_block = {
193
0
                    let buf_end_block = self.rng.core.state.get_block_pos();
194
0
                    u64::wrapping_sub(buf_end_block, BUF_BLOCKS.into())
195
0
                };
196
0
                let (buf_offset_blocks, block_offset_words) = {
197
0
                    let buf_offset_words = self.rng.index() as u64;
198
0
                    let blocks_part = buf_offset_words / u64::from(BLOCK_WORDS);
199
0
                    let words_part = buf_offset_words % u64::from(BLOCK_WORDS);
200
0
                    (blocks_part, words_part)
201
0
                };
202
0
                let pos_block = u64::wrapping_add(buf_start_block, buf_offset_blocks);
203
0
                let pos_block_words = u128::from(pos_block) * u128::from(BLOCK_WORDS);
204
0
                pos_block_words + u128::from(block_offset_words)
205
0
            }
Unexecuted instantiation: <rand_chacha::chacha::ChaCha20Rng>::get_word_pos
Unexecuted instantiation: <rand_chacha::chacha::ChaCha12Rng>::get_word_pos
Unexecuted instantiation: <rand_chacha::chacha::ChaCha8Rng>::get_word_pos
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
0
            pub fn set_word_pos(&mut self, word_offset: u128) {
214
0
                let block = (word_offset / u128::from(BLOCK_WORDS)) as u64;
215
0
                self.rng.core.state.set_block_pos(block);
216
0
                self.rng
217
0
                    .generate_and_set((word_offset % u128::from(BLOCK_WORDS)) as usize);
218
0
            }
Unexecuted instantiation: <rand_chacha::chacha::ChaCha20Rng>::set_word_pos
Unexecuted instantiation: <rand_chacha::chacha::ChaCha12Rng>::set_word_pos
Unexecuted instantiation: <rand_chacha::chacha::ChaCha8Rng>::set_word_pos
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
0
            pub fn set_stream(&mut self, stream: u64) {
233
0
                self.rng.core.state.set_nonce(stream);
234
0
                if self.rng.index() != 64 {
235
0
                    let wp = self.get_word_pos();
236
0
                    self.set_word_pos(wp);
237
0
                }
238
0
            }
Unexecuted instantiation: <rand_chacha::chacha::ChaCha20Rng>::set_stream
Unexecuted instantiation: <rand_chacha::chacha::ChaCha12Rng>::set_stream
Unexecuted instantiation: <rand_chacha::chacha::ChaCha8Rng>::set_stream
239
240
            /// Get the stream number.
241
            #[inline]
242
0
            pub fn get_stream(&self) -> u64 {
243
0
                self.rng.core.state.get_nonce()
244
0
            }
Unexecuted instantiation: <rand_chacha::chacha::ChaCha20Rng>::get_stream
Unexecuted instantiation: <rand_chacha::chacha::ChaCha12Rng>::get_stream
Unexecuted instantiation: <rand_chacha::chacha::ChaCha8Rng>::get_stream
245
246
            /// Get the seed.
247
            #[inline]
248
0
            pub fn get_seed(&self) -> [u8; 32] {
249
0
                self.rng.core.state.get_seed()
250
0
            }
Unexecuted instantiation: <rand_chacha::chacha::ChaCha20Rng>::get_seed
Unexecuted instantiation: <rand_chacha::chacha::ChaCha12Rng>::get_seed
Unexecuted instantiation: <rand_chacha::chacha::ChaCha8Rng>::get_seed
251
        }
252
253
        impl CryptoRng for $ChaChaXRng {}
254
255
        impl From<$ChaChaXCore> for $ChaChaXRng {
256
0
            fn from(core: $ChaChaXCore) -> Self {
257
0
                $ChaChaXRng {
258
0
                    rng: BlockRng::new(core),
259
0
                }
260
0
            }
Unexecuted instantiation: <rand_chacha::chacha::ChaCha20Rng as core::convert::From<rand_chacha::chacha::ChaCha20Core>>::from
Unexecuted instantiation: <rand_chacha::chacha::ChaCha12Rng as core::convert::From<rand_chacha::chacha::ChaCha12Core>>::from
Unexecuted instantiation: <rand_chacha::chacha::ChaCha8Rng as core::convert::From<rand_chacha::chacha::ChaCha8Core>>::from
261
        }
262
263
        impl PartialEq<$ChaChaXRng> for $ChaChaXRng {
264
0
            fn eq(&self, rhs: &$ChaChaXRng) -> bool {
265
0
                let a: $abst::$ChaChaXRng = self.into();
266
0
                let b: $abst::$ChaChaXRng = rhs.into();
267
0
                a == b
268
0
            }
Unexecuted instantiation: <rand_chacha::chacha::ChaCha20Rng as core::cmp::PartialEq>::eq
Unexecuted instantiation: <rand_chacha::chacha::ChaCha12Rng as core::cmp::PartialEq>::eq
Unexecuted instantiation: <rand_chacha::chacha::ChaCha8Rng as core::cmp::PartialEq>::eq
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
0
                fn from(r: &super::$ChaChaXRng) -> Self {
310
0
                    Self {
311
0
                        seed: r.get_seed(),
312
0
                        stream: r.get_stream(),
313
0
                        word_pos: r.get_word_pos(),
314
0
                    }
315
0
                }
Unexecuted instantiation: <rand_chacha::chacha::abstract20::ChaCha20Rng as core::convert::From<&rand_chacha::chacha::ChaCha20Rng>>::from
Unexecuted instantiation: <rand_chacha::chacha::abstract12::ChaCha12Rng as core::convert::From<&rand_chacha::chacha::ChaCha12Rng>>::from
Unexecuted instantiation: <rand_chacha::chacha::abstract8::ChaCha8Rng as core::convert::From<&rand_chacha::chacha::ChaCha8Rng>>::from
316
            }
317
318
            impl From<&$ChaChaXRng> for super::$ChaChaXRng {
319
                // Construct one of the possible concrete RNGs realizing an abstract state.
320
0
                fn from(a: &$ChaChaXRng) -> Self {
321
                    use rand_core::SeedableRng;
322
0
                    let mut r = Self::from_seed(a.seed);
323
0
                    r.set_stream(a.stream);
324
0
                    r.set_word_pos(a.word_pos);
325
0
                    r
326
0
                }
Unexecuted instantiation: <rand_chacha::chacha::ChaCha20Rng as core::convert::From<&rand_chacha::chacha::abstract20::ChaCha20Rng>>::from
Unexecuted instantiation: <rand_chacha::chacha::ChaCha12Rng as core::convert::From<&rand_chacha::chacha::abstract12::ChaCha12Rng>>::from
Unexecuted instantiation: <rand_chacha::chacha::ChaCha8Rng as core::convert::From<&rand_chacha::chacha::abstract8::ChaCha8Rng>>::from
327
            }
328
        }
329
    };
330
}
331
332
chacha_impl!(
333
    ChaCha20Core,
334
    ChaCha20Rng,
335
    10,
336
    "ChaCha with 20 rounds",
337
    abstract20,
338
);
339
chacha_impl!(
340
    ChaCha12Core,
341
    ChaCha12Rng,
342
    6,
343
    "ChaCha with 12 rounds",
344
    abstract12,
345
);
346
chacha_impl!(
347
    ChaCha8Core,
348
    ChaCha8Rng,
349
    4,
350
    "ChaCha with 8 rounds",
351
    abstract8,
352
);
353
354
#[cfg(test)]
355
mod 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
}