Coverage Report

Created: 2025-10-10 07:11

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