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