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