1 // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! The ChaCha random number generator.
12
13 use std::num::Wrapping as w;
14 use {Rng, SeedableRng, Rand, w32};
15
16 const KEY_WORDS : usize = 8; // 8 words for the 256-bit key
17 const STATE_WORDS : usize = 16;
18 const CHACHA_ROUNDS: u32 = 20; // Cryptographically secure from 8 upwards as of this writing
19
20 /// A random number generator that uses the ChaCha20 algorithm [1].
21 ///
22 /// The ChaCha algorithm is widely accepted as suitable for
23 /// cryptographic purposes, but this implementation has not been
24 /// verified as such. Prefer a generator like `OsRng` that defers to
25 /// the operating system for cases that need high security.
26 ///
27 /// [1]: D. J. Bernstein, [*ChaCha, a variant of
28 /// Salsa20*](http://cr.yp.to/chacha.html)
29 #[derive(Copy, Clone, Debug)]
30 pub struct ChaChaRng {
31 buffer: [w32; STATE_WORDS], // Internal buffer of output
32 state: [w32; STATE_WORDS], // Initial state
33 index: usize, // Index into state
34 }
35
36 static EMPTY: ChaChaRng = ChaChaRng {
37 buffer: [w(0); STATE_WORDS],
38 state: [w(0); STATE_WORDS],
39 index: STATE_WORDS
40 };
41
42
43 macro_rules! quarter_round{
44 ($a: expr, $b: expr, $c: expr, $d: expr) => {{
45 $a = $a + $b; $d = $d ^ $a; $d = w($d.0.rotate_left(16));
46 $c = $c + $d; $b = $b ^ $c; $b = w($b.0.rotate_left(12));
47 $a = $a + $b; $d = $d ^ $a; $d = w($d.0.rotate_left( 8));
48 $c = $c + $d; $b = $b ^ $c; $b = w($b.0.rotate_left( 7));
49 }}
50 }
51
52 macro_rules! double_round{
53 ($x: expr) => {{
54 // Column round
55 quarter_round!($x[ 0], $x[ 4], $x[ 8], $x[12]);
56 quarter_round!($x[ 1], $x[ 5], $x[ 9], $x[13]);
57 quarter_round!($x[ 2], $x[ 6], $x[10], $x[14]);
58 quarter_round!($x[ 3], $x[ 7], $x[11], $x[15]);
59 // Diagonal round
60 quarter_round!($x[ 0], $x[ 5], $x[10], $x[15]);
61 quarter_round!($x[ 1], $x[ 6], $x[11], $x[12]);
62 quarter_round!($x[ 2], $x[ 7], $x[ 8], $x[13]);
63 quarter_round!($x[ 3], $x[ 4], $x[ 9], $x[14]);
64 }}
65 }
66
67 #[inline]
core(output: &mut [w32; STATE_WORDS], input: &[w32; STATE_WORDS])68 fn core(output: &mut [w32; STATE_WORDS], input: &[w32; STATE_WORDS]) {
69 *output = *input;
70
71 for _ in 0..CHACHA_ROUNDS / 2 {
72 double_round!(output);
73 }
74
75 for i in 0..STATE_WORDS {
76 output[i] = output[i] + input[i];
77 }
78 }
79
80 impl ChaChaRng {
81
82 /// Create an ChaCha random number generator using the default
83 /// fixed key of 8 zero words.
84 ///
85 /// # Examples
86 ///
87 /// ```rust
88 /// use rand::{Rng, ChaChaRng};
89 ///
90 /// let mut ra = ChaChaRng::new_unseeded();
91 /// println!("{:?}", ra.next_u32());
92 /// println!("{:?}", ra.next_u32());
93 /// ```
94 ///
95 /// Since this equivalent to a RNG with a fixed seed, repeated executions
96 /// of an unseeded RNG will produce the same result. This code sample will
97 /// consistently produce:
98 ///
99 /// - 2917185654
100 /// - 2419978656
new_unseeded() -> ChaChaRng101 pub fn new_unseeded() -> ChaChaRng {
102 let mut rng = EMPTY;
103 rng.init(&[0; KEY_WORDS]);
104 rng
105 }
106
107 /// Sets the internal 128-bit ChaCha counter to
108 /// a user-provided value. This permits jumping
109 /// arbitrarily ahead (or backwards) in the pseudorandom stream.
110 ///
111 /// Since the nonce words are used to extend the counter to 128 bits,
112 /// users wishing to obtain the conventional ChaCha pseudorandom stream
113 /// associated with a particular nonce can call this function with
114 /// arguments `0, desired_nonce`.
115 ///
116 /// # Examples
117 ///
118 /// ```rust
119 /// use rand::{Rng, ChaChaRng};
120 ///
121 /// let mut ra = ChaChaRng::new_unseeded();
122 /// ra.set_counter(0u64, 1234567890u64);
123 /// println!("{:?}", ra.next_u32());
124 /// println!("{:?}", ra.next_u32());
125 /// ```
set_counter(&mut self, counter_low: u64, counter_high: u64)126 pub fn set_counter(&mut self, counter_low: u64, counter_high: u64) {
127 self.state[12] = w((counter_low >> 0) as u32);
128 self.state[13] = w((counter_low >> 32) as u32);
129 self.state[14] = w((counter_high >> 0) as u32);
130 self.state[15] = w((counter_high >> 32) as u32);
131 self.index = STATE_WORDS; // force recomputation
132 }
133
134 /// Initializes `self.state` with the appropriate key and constants
135 ///
136 /// We deviate slightly from the ChaCha specification regarding
137 /// the nonce, which is used to extend the counter to 128 bits.
138 /// This is provably as strong as the original cipher, though,
139 /// since any distinguishing attack on our variant also works
140 /// against ChaCha with a chosen-nonce. See the XSalsa20 [1]
141 /// security proof for a more involved example of this.
142 ///
143 /// The modified word layout is:
144 /// ```text
145 /// constant constant constant constant
146 /// key key key key
147 /// key key key key
148 /// counter counter counter counter
149 /// ```
150 /// [1]: Daniel J. Bernstein. [*Extending the Salsa20
151 /// nonce.*](http://cr.yp.to/papers.html#xsalsa)
init(&mut self, key: &[u32; KEY_WORDS])152 fn init(&mut self, key: &[u32; KEY_WORDS]) {
153 self.state[0] = w(0x61707865);
154 self.state[1] = w(0x3320646E);
155 self.state[2] = w(0x79622D32);
156 self.state[3] = w(0x6B206574);
157
158 for i in 0..KEY_WORDS {
159 self.state[4+i] = w(key[i]);
160 }
161
162 self.state[12] = w(0);
163 self.state[13] = w(0);
164 self.state[14] = w(0);
165 self.state[15] = w(0);
166
167 self.index = STATE_WORDS;
168 }
169
170 /// Refill the internal output buffer (`self.buffer`)
update(&mut self)171 fn update(&mut self) {
172 core(&mut self.buffer, &self.state);
173 self.index = 0;
174 // update 128-bit counter
175 self.state[12] = self.state[12] + w(1);
176 if self.state[12] != w(0) { return };
177 self.state[13] = self.state[13] + w(1);
178 if self.state[13] != w(0) { return };
179 self.state[14] = self.state[14] + w(1);
180 if self.state[14] != w(0) { return };
181 self.state[15] = self.state[15] + w(1);
182 }
183 }
184
185 impl Rng for ChaChaRng {
186 #[inline]
next_u32(&mut self) -> u32187 fn next_u32(&mut self) -> u32 {
188 if self.index == STATE_WORDS {
189 self.update();
190 }
191
192 let value = self.buffer[self.index % STATE_WORDS];
193 self.index += 1;
194 value.0
195 }
196 }
197
198 impl<'a> SeedableRng<&'a [u32]> for ChaChaRng {
199
reseed(&mut self, seed: &'a [u32])200 fn reseed(&mut self, seed: &'a [u32]) {
201 // reset state
202 self.init(&[0u32; KEY_WORDS]);
203 // set key in place
204 let key = &mut self.state[4 .. 4+KEY_WORDS];
205 for (k, s) in key.iter_mut().zip(seed.iter()) {
206 *k = w(*s);
207 }
208 }
209
210 /// Create a ChaCha generator from a seed,
211 /// obtained from a variable-length u32 array.
212 /// Only up to 8 words are used; if less than 8
213 /// words are used, the remaining are set to zero.
from_seed(seed: &'a [u32]) -> ChaChaRng214 fn from_seed(seed: &'a [u32]) -> ChaChaRng {
215 let mut rng = EMPTY;
216 rng.reseed(seed);
217 rng
218 }
219 }
220
221 impl Rand for ChaChaRng {
rand<R: Rng>(other: &mut R) -> ChaChaRng222 fn rand<R: Rng>(other: &mut R) -> ChaChaRng {
223 let mut key : [u32; KEY_WORDS] = [0; KEY_WORDS];
224 for word in key.iter_mut() {
225 *word = other.gen();
226 }
227 SeedableRng::from_seed(&key[..])
228 }
229 }
230
231
232 #[cfg(test)]
233 mod test {
234 use {Rng, SeedableRng};
235 use super::ChaChaRng;
236
237 #[test]
test_rng_rand_seeded()238 fn test_rng_rand_seeded() {
239 let s = ::test::rng().gen_iter::<u32>().take(8).collect::<Vec<u32>>();
240 let mut ra: ChaChaRng = SeedableRng::from_seed(&s[..]);
241 let mut rb: ChaChaRng = SeedableRng::from_seed(&s[..]);
242 assert!(::test::iter_eq(ra.gen_ascii_chars().take(100),
243 rb.gen_ascii_chars().take(100)));
244 }
245
246 #[test]
test_rng_seeded()247 fn test_rng_seeded() {
248 let seed : &[_] = &[0,1,2,3,4,5,6,7];
249 let mut ra: ChaChaRng = SeedableRng::from_seed(seed);
250 let mut rb: ChaChaRng = SeedableRng::from_seed(seed);
251 assert!(::test::iter_eq(ra.gen_ascii_chars().take(100),
252 rb.gen_ascii_chars().take(100)));
253 }
254
255 #[test]
test_rng_reseed()256 fn test_rng_reseed() {
257 let s = ::test::rng().gen_iter::<u32>().take(8).collect::<Vec<u32>>();
258 let mut r: ChaChaRng = SeedableRng::from_seed(&s[..]);
259 let string1: String = r.gen_ascii_chars().take(100).collect();
260
261 r.reseed(&s);
262
263 let string2: String = r.gen_ascii_chars().take(100).collect();
264 assert_eq!(string1, string2);
265 }
266
267 #[test]
test_rng_true_values()268 fn test_rng_true_values() {
269 // Test vectors 1 and 2 from
270 // http://tools.ietf.org/html/draft-nir-cfrg-chacha20-poly1305-04
271 let seed : &[_] = &[0u32; 8];
272 let mut ra: ChaChaRng = SeedableRng::from_seed(seed);
273
274 let v = (0..16).map(|_| ra.next_u32()).collect::<Vec<_>>();
275 assert_eq!(v,
276 vec!(0xade0b876, 0x903df1a0, 0xe56a5d40, 0x28bd8653,
277 0xb819d2bd, 0x1aed8da0, 0xccef36a8, 0xc70d778b,
278 0x7c5941da, 0x8d485751, 0x3fe02477, 0x374ad8b8,
279 0xf4b8436a, 0x1ca11815, 0x69b687c3, 0x8665eeb2));
280
281 let v = (0..16).map(|_| ra.next_u32()).collect::<Vec<_>>();
282 assert_eq!(v,
283 vec!(0xbee7079f, 0x7a385155, 0x7c97ba98, 0x0d082d73,
284 0xa0290fcb, 0x6965e348, 0x3e53c612, 0xed7aee32,
285 0x7621b729, 0x434ee69c, 0xb03371d5, 0xd539d874,
286 0x281fed31, 0x45fb0a51, 0x1f0ae1ac, 0x6f4d794b));
287
288
289 let seed : &[_] = &[0,1,2,3,4,5,6,7];
290 let mut ra: ChaChaRng = SeedableRng::from_seed(seed);
291
292 // Store the 17*i-th 32-bit word,
293 // i.e., the i-th word of the i-th 16-word block
294 let mut v : Vec<u32> = Vec::new();
295 for _ in 0..16 {
296 v.push(ra.next_u32());
297 for _ in 0..16 {
298 ra.next_u32();
299 }
300 }
301
302 assert_eq!(v,
303 vec!(0xf225c81a, 0x6ab1be57, 0x04d42951, 0x70858036,
304 0x49884684, 0x64efec72, 0x4be2d186, 0x3615b384,
305 0x11cfa18e, 0xd3c50049, 0x75c775f6, 0x434c6530,
306 0x2c5bad8f, 0x898881dc, 0x5f1c86d9, 0xc1f8e7f4));
307 }
308
309 #[test]
test_rng_clone()310 fn test_rng_clone() {
311 let seed : &[_] = &[0u32; 8];
312 let mut rng: ChaChaRng = SeedableRng::from_seed(seed);
313 let mut clone = rng.clone();
314 for _ in 0..16 {
315 assert_eq!(rng.next_u64(), clone.next_u64());
316 }
317 }
318 }
319