chacha/
lib.rs

1// Copyright 2016 Peter Reid. See the COPYRIGHT file at the top-level
2// directory of this distribution.
3//
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7// option. This file may not be copied, modified, or distributed
8// except according to those terms.
9
10//! ChaCha is a family of 256-bit stream ciphers. This crate includes five
11//! members of the family:
12//!
13//!
14//! |              | nonce length | stream length  | key length | rounds
15//! |--------------|--------------|----------------|------------|----------
16//! | ChaCha20     | 8            | 2<sup>70</sup> | 32         | 20
17//! | IETF         | 16           | 2<sup>38</sup> | 32         | 20
18//! | XChaCha20    | 24           | 2<sup>70</sup> | 32         | 20
19//! | ChaCha12     | 8            | 2<sup>70</sup> | 32         | 12
20//! | ChaCha8      | 8            | 2<sup>70</sup> | 32         | 8
21//! _(Lengths are given in bytes.)_
22//!
23//! ChaCha12 and ChaCha8 trade off the security margin in favor of performance.
24//! The IETF implementation increases the nonce length, making randomly generating
25//! the same nonce twice less likely, at the cost of making the stream shorter.
26//! XChaCha20 increases the nonce length even further while maintaining the stream
27//! length at the cost of a slightly more expensive initialization step.
28//!
29//! ChaCha benefits greatly from SIMD instructions, which currently requires Rust's
30//! nightly build. Compile with the feature `nightly` enabled for maximum performance.
31//!
32//! ChaCha was designed by Daniel J. Bernstein in 2008 as a slightly modified version
33//! of his Salsa family of ciphers. Salsa20 has been
34//! [analyzed](http://www.ecrypt.eu.org/stream/salsa20pf.html) as part of
35//! the [eSTREAM project](https://en.wikipedia.org/wiki/ESTREAM) and has not had
36//! any practical attack found. That cryptanalysis would generally apply to ChaCha20 as well.
37//! The ChaCha round function is used in the BLAKE hash function, which was
38//! analyzed as part of the
39//! [SHA-3 competition](https://en.wikipedia.org/wiki/NIST_hash_function_competition),
40//! again without finding a practical attack. The IETF's
41//! [RFC 7539](https://tools.ietf.org/html/rfc7539) standardizes a member
42//! of the ChaCha family.
43
44#![no_std]
45#![cfg_attr(feature="nightly", feature(repr_simd))]
46#![cfg_attr(feature="nightly", feature(test))]
47
48extern crate keystream;
49
50#[cfg(all(test, feature="bench"))]
51extern crate test;
52
53pub use keystream::{KeyStream, SeekableKeyStream};
54pub use keystream::Error;
55use core::cmp::min;
56use core::convert::TryInto;
57
58/// A ChaCha keystream.
59///
60/// After being initialized with a `key` and `nonce`, a `ChaCha` instance
61/// will generate a long stream of bytes that is indistinguishable from
62/// random for anyone not knowing the key and nonce.
63///
64/// # Examples
65///
66/// ```
67/// use chacha::{ChaCha, KeyStream};
68///
69/// let secret_key = [
70///     0x29, 0xfa, 0x35, 0x60, 0x88, 0x45, 0xc6, 0xf9, 
71///     0xd8, 0xfe, 0x65, 0xe3, 0x22, 0x0e, 0x5b, 0x05, 
72///     0x03, 0x4a, 0xa0, 0x9f, 0x9e, 0x27, 0xad, 0x0f, 
73///     0x6c, 0x90, 0xa5, 0x73, 0xa8, 0x10, 0xe4, 0x94, 
74/// ];
75/// let nonce = [0u8; 8];
76/// let mut stream = ChaCha::new_chacha20(&secret_key, &nonce);
77///
78/// let mut buffer = *b"abcdef";
79/// stream.xor_read(&mut buffer[..]).expect("hit end of stream far too soon");
80/// let expected_ciphertext = [0xde, 0x87, 0xa5, 0xbe, 0x1d, 0x77];
81/// assert_eq!(buffer, expected_ciphertext);
82/// ```
83///
84#[derive(Clone)]
85pub struct ChaCha {
86    input: [u32; 16],
87    output: [u8; 64],
88    offset: u8,
89    rounds: u8,
90    large_block_counter: bool,
91}
92
93impl ChaCha {
94    /// Create a ChaCha stream conforming to the IETF's
95    /// [RFC 7539](https://tools.ietf.org/html/rfc7539).
96    /// The stream takes a 12-byte nonce and has a length of
97    /// 2<sup>38</sup> bytes, or 256 GiB.
98    pub fn new_ietf(key: &[u8; 32], nonce: &[u8; 12]) -> ChaCha {
99        ChaCha {
100            input: [
101                0x61707865, 0x3320646e, 0x79622d32, 0x6b206574,
102                u32::from_le_bytes(key[ 0.. 4].try_into().unwrap()),
103                u32::from_le_bytes(key[ 4.. 8].try_into().unwrap()),
104                u32::from_le_bytes(key[ 8..12].try_into().unwrap()),
105                u32::from_le_bytes(key[12..16].try_into().unwrap()),
106                u32::from_le_bytes(key[16..20].try_into().unwrap()),
107                u32::from_le_bytes(key[20..24].try_into().unwrap()),
108                u32::from_le_bytes(key[24..28].try_into().unwrap()),
109                u32::from_le_bytes(key[28..32].try_into().unwrap()),
110                0, // block counter
111                u32::from_le_bytes(nonce[ 0.. 4].try_into().unwrap()),
112                u32::from_le_bytes(nonce[ 4.. 8].try_into().unwrap()),
113                u32::from_le_bytes(nonce[ 8..12].try_into().unwrap()),
114            ],
115            output: [0; 64],
116            offset: 255,
117            large_block_counter: false,
118            rounds: 20,
119        }
120    }
121
122    /// Create a ChaCha stream with an 8-byte nonce and has a length of
123    /// 2<sup>70</sup> bytes. This is compatible with libsodium's ChaCha20
124    /// implementation and Daniel Bernstein's original
125    /// [specification](https://cr.yp.to/chacha/chacha-20080128.pdf).
126    pub fn new_chacha20(key: &[u8; 32], nonce: &[u8; 8]) -> ChaCha {
127        ChaCha {
128            input: [
129                0x61707865, 0x3320646e, 0x79622d32, 0x6b206574,
130                u32::from_le_bytes(key[ 0.. 4].try_into().unwrap()),
131                u32::from_le_bytes(key[ 4.. 8].try_into().unwrap()),
132                u32::from_le_bytes(key[ 8..12].try_into().unwrap()),
133                u32::from_le_bytes(key[12..16].try_into().unwrap()),
134                u32::from_le_bytes(key[16..20].try_into().unwrap()),
135                u32::from_le_bytes(key[20..24].try_into().unwrap()),
136                u32::from_le_bytes(key[24..28].try_into().unwrap()),
137                u32::from_le_bytes(key[28..32].try_into().unwrap()),
138                0, // block counter
139                0,
140                u32::from_le_bytes(nonce[ 0.. 4].try_into().unwrap()),
141                u32::from_le_bytes(nonce[ 4.. 8].try_into().unwrap()),
142            ],
143            output: [0; 64],
144            offset: 255,
145            large_block_counter: true,
146            rounds: 20,
147        }
148    }
149
150    /// Create a ChaCha stream with an 8-byte nonce and has a length of
151    /// 2<sup>70</sup> bytes. This is compatible with libsodium's ChaCha12
152    /// implementation. ChaCha12 decreases security margin relative to
153    /// ChaCha20 in favor of speed.
154    pub fn new_chacha12(key: &[u8; 32], nonce: &[u8; 8]) -> ChaCha {
155        let mut st = ChaCha::new_chacha20(key, nonce);
156        st.rounds = 12;
157        st
158    }
159
160    /// Create a ChaCha stream with an 8-byte nonce and has a length of
161    /// 2<sup>70</sup> bytes. This is compatible with libsodium's ChaCha12
162    /// implementation. ChaCha8 decreases security margin relative to
163    /// ChaCha20 in favor of speed.
164    pub fn new_chacha8(key: &[u8; 32], nonce: &[u8; 8]) -> ChaCha {
165        let mut st = ChaCha::new_chacha20(key, nonce);
166        st.rounds = 8;
167        st
168    }
169
170    /// Create a ChaCha stream with a 24-byte nonce and a length of
171    /// 2<sup>70</sup> bytes. This stream's initialization relates
172    /// to ChaCha20 in the same way that that
173    /// [XSalsa20](https://cr.yp.to/snuffle/xsalsa-20110204.pdf)
174    /// relates to Salsa20.
175    pub fn new_xchacha20(key: &[u8; 32], nonce: &[u8; 24]) -> ChaCha {
176        let mut st = [
177            0x61707865, 0x3320646e, 0x79622d32, 0x6b206574,
178            u32::from_le_bytes(key[ 0.. 4].try_into().unwrap()),
179            u32::from_le_bytes(key[ 4.. 8].try_into().unwrap()),
180            u32::from_le_bytes(key[ 8..12].try_into().unwrap()),
181            u32::from_le_bytes(key[12..16].try_into().unwrap()),
182            u32::from_le_bytes(key[16..20].try_into().unwrap()),
183            u32::from_le_bytes(key[20..24].try_into().unwrap()),
184            u32::from_le_bytes(key[24..28].try_into().unwrap()),
185            u32::from_le_bytes(key[28..32].try_into().unwrap()),
186            u32::from_le_bytes(nonce[ 0.. 4].try_into().unwrap()),
187            u32::from_le_bytes(nonce[ 4.. 8].try_into().unwrap()),
188            u32::from_le_bytes(nonce[ 8..12].try_into().unwrap()),
189            u32::from_le_bytes(nonce[12..16].try_into().unwrap()),
190        ];
191        permute_general(20, &mut st, false, None);
192
193        ChaCha {
194            input: [
195                0x61707865, 0x3320646e, 0x79622d32, 0x6b206574,
196                st[ 0], st[ 1], st[ 2], st[ 3],
197                st[12], st[13], st[14], st[15],
198                0, 0,
199                u32::from_le_bytes(nonce[16..20].try_into().unwrap()),
200                u32::from_le_bytes(nonce[20..24].try_into().unwrap()),
201            ],
202            output: [0; 64],
203            offset: 255,
204            large_block_counter: true,
205            rounds: 20,
206        }
207    }
208}
209
210#[cfg_attr(feature="nightly", repr(simd))]
211#[derive(Copy, Clone)]
212struct Row(u32, u32, u32, u32);
213
214impl Row {
215    fn add(self, x: Row) -> Row {
216        Row(
217            self.0.wrapping_add(x.0),
218            self.1.wrapping_add(x.1),
219            self.2.wrapping_add(x.2),
220            self.3.wrapping_add(x.3)
221        )
222    }
223
224    fn xor(self, x: Row) -> Row {
225        Row(self.0^x.0, self.1^x.1, self.2^x.2, self.3^x.3)
226    }
227
228    fn or(self, x: Row) -> Row {
229        Row(self.0|x.0, self.1|x.1, self.2|x.2, self.3|x.3)
230    }
231
232    fn shift_left(self, bit_distance: usize) -> Row {
233        Row(self.0<<bit_distance, self.1<<bit_distance, self.2<<bit_distance, self.3<<bit_distance)
234    }
235
236    fn shift_right(self, bit_distance: usize) -> Row {
237        Row(self.0>>bit_distance, self.1>>bit_distance, self.2>>bit_distance, self.3>>bit_distance)
238    }
239
240    fn roll_left(self, bit_distance: usize) -> Row {
241        let lefted = self.shift_left(bit_distance);
242        let righted = self.shift_right(32 - bit_distance);
243        lefted.or(righted)
244    }
245    
246    fn shuffle_left_1(self) -> Row {
247        Row(self.1, self.2, self.3, self.0)
248    }
249    
250    fn shuffle_left_2(self) -> Row {
251        Row(self.2, self.3, self.0, self.1)
252    }
253    
254    fn shuffle_left_3(self) -> Row {
255        Row(self.3, self.0, self.1, self.2)
256    }
257    
258}
259
260// Inlining this causes the loop to unroll, which makes the disassembly hard
261// to read.
262#[inline(always)]
263fn permute_general(mut rounds: u8, xs: &mut [u32; 16], do_add: bool, bs: Option<&mut [u8; 64]>) {
264    let mut a = Row(xs[ 0], xs[ 1], xs[ 2], xs[ 3]);
265    let mut b = Row(xs[ 4], xs[ 5], xs[ 6], xs[ 7]);
266    let mut c = Row(xs[ 8], xs[ 9], xs[10], xs[11]);
267    let mut d = Row(xs[12], xs[13], xs[14], xs[15]);
268
269    loop {
270        rounds = rounds.wrapping_sub(1);
271
272        a = a.add(b); d = a.xor(d); d = d.roll_left(16);
273        c = c.add(d); b = b.xor(c); b = b.roll_left(12);
274        a = a.add(b); d = a.xor(d); d = d.roll_left( 8);
275        c = c.add(d); b = b.xor(c); b = b.roll_left( 7);
276
277        // Without this branch, making each iterate a double-round,
278        // the compiler gets confused and does not use SSE instructions.
279        if rounds%2==1 {
280            // We are coming up on an odd round.
281            // We will want to act on diagonals instead of columns, so
282            // rearrange our rows accordingly.
283            b = b.shuffle_left_1();
284            c = c.shuffle_left_2();
285            d = d.shuffle_left_3();
286        } else {
287            // We are coming up on an even round.
288            // Undo our rearrangement into diagonals so we can act on
289            // columns again.
290            b = b.shuffle_left_3();
291            c = c.shuffle_left_2();
292            d = d.shuffle_left_1();
293            if rounds==0 {
294                break;
295            }
296        }
297    }
298    if do_add {
299        a = a.add(Row(xs[ 0], xs[ 1], xs[ 2], xs[ 3]));
300        b = b.add(Row(xs[ 4], xs[ 5], xs[ 6], xs[ 7]));
301        c = c.add(Row(xs[ 8], xs[ 9], xs[10], xs[11]));
302        d = d.add(Row(xs[12], xs[13], xs[14], xs[15]));
303    }
304
305    if let Some(bs) = bs {
306        bs[ 0.. 4].copy_from_slice(&a.0.to_le_bytes());
307        bs[ 4.. 8].copy_from_slice(&a.1.to_le_bytes());
308        bs[ 8..12].copy_from_slice(&a.2.to_le_bytes());
309        bs[12..16].copy_from_slice(&a.3.to_le_bytes());
310        bs[16..20].copy_from_slice(&b.0.to_le_bytes());
311        bs[20..24].copy_from_slice(&b.1.to_le_bytes());
312        bs[24..28].copy_from_slice(&b.2.to_le_bytes());
313        bs[28..32].copy_from_slice(&b.3.to_le_bytes());
314        bs[32..36].copy_from_slice(&c.0.to_le_bytes());
315        bs[36..40].copy_from_slice(&c.1.to_le_bytes());
316        bs[40..44].copy_from_slice(&c.2.to_le_bytes());
317        bs[44..48].copy_from_slice(&c.3.to_le_bytes());
318        bs[48..52].copy_from_slice(&d.0.to_le_bytes());
319        bs[52..56].copy_from_slice(&d.1.to_le_bytes());
320        bs[56..60].copy_from_slice(&d.2.to_le_bytes());
321        bs[60..64].copy_from_slice(&d.3.to_le_bytes());
322    } else {
323        xs[ 0] = a.0; xs[ 1] = a.1; xs[ 2] = a.2; xs[ 3] = a.3;
324        xs[ 4] = b.0; xs[ 5] = b.1; xs[ 6] = b.2; xs[ 7] = b.3;
325        xs[ 8] = c.0; xs[ 9] = c.1; xs[10] = c.2; xs[11] = c.3;
326        xs[12] = d.0; xs[13] = d.1; xs[14] = d.2; xs[15] = d.3;
327    }
328}
329
330/// Apply the ChaCha core function. Note that this is reversible.
331pub fn permute(rounds: u8, xs: &mut [u32; 16]) {
332    permute_general(rounds, xs, false, None)
333}
334
335/// Apply the ChaCha core function and add the result to the input.
336/// This is what maps ChaCha streams' input blocks to output blocks.
337pub fn permute_and_add(rounds: u8, xs: &mut [u32; 16]) {
338    permute_general(rounds, xs, true, None)
339}
340
341
342impl ChaCha {
343    fn increment_counter(&mut self) -> Result<(), Error> {
344        if self.input[12] != 0 {
345            // This is the common case, where we just increment the counter.
346
347            let (incremented_low, overflow) = self.input[12].overflowing_add(1);
348
349            self.input[12] = incremented_low;
350            self.input[13] = self.input[13].wrapping_add((overflow & self.large_block_counter) as u32);
351        } else {
352            // The low block counter overflowed OR we are just starting.
353            // We detect the "just starting" case by setting `offset` to 255.
354            // (During other parts of operation, `offset` does not exceed 64.
355            if self.offset == 255 {
356                self.input[12] = 1;
357                self.offset = 64;
358            } else if self.input[13]==0 || !self.large_block_counter {
359                // Our counter wrapped around!
360                return Err(Error::EndReached);
361            } else {
362                self.input[12] = 1;
363            }
364        }
365
366        Ok( () )
367    }
368}
369
370impl KeyStream for ChaCha {
371    fn xor_read(&mut self, dest: &mut [u8]) -> Result<(), Error> {
372        let dest = if self.offset < 64 {
373            let from_existing = min(dest.len(), 64 - self.offset as usize);
374            for (dest_byte, output_byte) in dest.iter_mut().zip(self.output[self.offset as usize..].iter()) {
375                *dest_byte = *dest_byte ^ *output_byte;
376            }
377            self.offset += from_existing as u8;
378            &mut dest[from_existing..]
379        } else {
380            dest
381        };
382
383        for dest_chunk in dest.chunks_mut(64) {
384            let mut output_buf = self.input;
385            permute_general(self.rounds, &mut output_buf, true, None);
386            self.increment_counter()?;
387            if dest_chunk.len() == 64 {
388                for idx in 0..16 {
389                    let word = u32::from_le_bytes(dest_chunk[idx*4..idx*4+4].try_into().unwrap()) ^ output_buf[idx];
390                    dest_chunk[idx*4..idx*4+4].copy_from_slice(&word.to_le_bytes());
391                }
392            } else {
393                for idx in 0..16 {
394                    self.output[idx*4..idx*4+4].copy_from_slice(&output_buf[idx].to_le_bytes());
395                }
396                for (dest_byte, output_byte) in dest_chunk.iter_mut().zip(self.output.iter()) {
397                    *dest_byte = *dest_byte ^ output_byte;
398                }
399                self.offset = dest_chunk.len() as u8;
400            }
401        }
402
403        Ok( () )
404    }
405}
406
407impl SeekableKeyStream for ChaCha {
408    fn seek_to(&mut self, byte_offset: u64) -> Result<(), Error> {
409        // With one block counter word, we can go past the end of the stream with a u64.
410        if self.large_block_counter {
411            self.input[12] = (byte_offset >> 6) as u32;
412            self.input[13] = (byte_offset >> 38) as u32;
413        } else {
414            if byte_offset>=64*0x1_0000_0000 {
415                // Set an overflow state.
416                self.input[12] = 0;
417                self.offset = 64;
418                return Err(Error::EndReached);
419            } else {
420                self.input[12] = (byte_offset >> 6) as u32;
421            }
422        }
423
424        self.offset = (byte_offset & 0x3f) as u8;
425        permute_general(self.rounds, &mut self.input, true, Some(&mut self.output));
426
427        let (incremented_low, overflow) = self.input[12].overflowing_add(1);
428        self.input[12] = incremented_low;
429        self.input[13] = self.input[13].wrapping_add(if overflow {
430            if self.large_block_counter { 1 } else { 0 }
431        } else { 0 });
432
433        Ok( () )
434    }
435}
436
437
438/// Runs the self-test for the chacha20 block function.
439#[cold]
440pub fn selftest() {
441    let key = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
442               0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
443               0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
444               0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f];
445    let nonce = [0x00, 0x00, 0x00, 0x09,
446                 0x00, 0x00, 0x00, 0x4a,
447                 0x00, 0x00, 0x00, 0x00];
448    let expected = [0x10, 0xf1, 0xe7, 0xe4, 0xd1, 0x3b, 0x59, 0x15,
449                    0x50, 0x0f, 0xdd, 0x1f, 0xa3, 0x20, 0x71, 0xc4,
450                    0xc7, 0xd1, 0xf4, 0xc7, 0x33, 0xc0, 0x68, 0x03,
451                    0x04, 0x22, 0xaa, 0x9a, 0xc3, 0xd4, 0x6c, 0x4e,
452                    0xd2, 0x82, 0x64, 0x46, 0x07, 0x9f, 0xaa, 0x09,
453                    0x14, 0xc2, 0xd7, 0x05, 0xd9, 0x8b, 0x02, 0xa2,
454                    0xb5, 0x12, 0x9c, 0xd1, 0xde, 0x16, 0x4e, 0xb9,
455                    0xcb, 0xd0, 0x83, 0xe8, 0xa2, 0x50, 0x3c, 0x4e];
456
457    let mut result = [0u8; 64];
458    let mut state = ChaCha::new_ietf(&key, &nonce);
459    state.seek_to(64).unwrap();
460    state.xor_read(&mut result).unwrap();
461    assert_eq!(result, expected);
462}
463
464
465#[cfg(test)]
466mod tests {
467use super::*;
468
469#[test]
470fn do_selftest() {
471    selftest();
472}
473
474#[test]
475fn rfc_7539_permute_20() {
476    let mut xs = [
477        0x61707865, 0x3320646e, 0x79622d32, 0x6b206574,
478        0x03020100, 0x07060504, 0x0b0a0908, 0x0f0e0d0c,
479        0x13121110, 0x17161514, 0x1b1a1918, 0x1f1e1d1c,
480        0x00000001, 0x09000000, 0x4a000000, 0x00000000,
481    ];
482
483    permute(20, &mut xs);
484
485    assert_eq!(xs, [
486        0x837778ab, 0xe238d763, 0xa67ae21e, 0x5950bb2f,
487        0xc4f2d0c7, 0xfc62bb2f, 0x8fa018fc, 0x3f5ec7b7,
488        0x335271c2, 0xf29489f3, 0xeabda8fc, 0x82e46ebd,
489        0xd19c12b4, 0xb04e16de, 0x9e83d0cb, 0x4e3c50a2,
490    ]);
491}
492
493#[test]
494fn rfc_7539_permute_and_add_20() {
495    let mut xs = [
496        0x61707865, 0x3320646e, 0x79622d32, 0x6b206574,
497        0x03020100, 0x07060504, 0x0b0a0908, 0x0f0e0d0c,
498        0x13121110, 0x17161514, 0x1b1a1918, 0x1f1e1d1c,
499        0x00000001, 0x09000000, 0x4a000000, 0x00000000,
500    ];
501
502    permute_and_add(20, &mut xs);
503
504    assert_eq!(xs, [
505       0xe4e7f110, 0x15593bd1, 0x1fdd0f50, 0xc47120a3,
506       0xc7f4d1c7, 0x0368c033, 0x9aaa2204, 0x4e6cd4c3,
507       0x466482d2, 0x09aa9f07, 0x05d7c214, 0xa2028bd9,
508       0xd19c12b5, 0xb94e16de, 0xe883d0cb, 0x4e3c50a2,
509    ]);
510}
511
512#[test]
513fn rfc_7539_case_1() {
514    let mut st = ChaCha::new_ietf(
515        &[
516            0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
517            0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
518            0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,
519            0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f
520        ], &[
521            0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x4a,
522            0x00,0x00,0x00,0x00
523        ]
524    );
525
526    let mut buf = [0u8; 128];
527    st.xor_read(&mut buf).unwrap();
528    assert_eq!(buf[64..].to_vec(), [
529        0x10, 0xf1, 0xe7, 0xe4, 0xd1, 0x3b, 0x59, 0x15, 0x50, 0x0f, 0xdd, 0x1f, 0xa3, 0x20, 0x71, 0xc4,
530        0xc7, 0xd1, 0xf4, 0xc7, 0x33, 0xc0, 0x68, 0x03, 0x04, 0x22, 0xaa, 0x9a, 0xc3, 0xd4, 0x6c, 0x4e,
531        0xd2, 0x82, 0x64, 0x46, 0x07, 0x9f, 0xaa, 0x09, 0x14, 0xc2, 0xd7, 0x05, 0xd9, 0x8b, 0x02, 0xa2,
532        0xb5, 0x12, 0x9c, 0xd1, 0xde, 0x16, 0x4e, 0xb9, 0xcb, 0xd0, 0x83, 0xe8, 0xa2, 0x50, 0x3c, 0x4e,
533    ].to_vec());
534}
535
536#[test]
537fn rfc_7539_case_2() {
538    let mut st = ChaCha::new_ietf(
539        &[
540            0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
541            0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
542            0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
543            0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
544        ], &[
545            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a,
546            0x00, 0x00, 0x00, 0x00
547        ]
548    );
549
550    let plaintext = b"Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.";
551    let mut buf = [0u8; 178];
552    for (dest, src) in buf[64..].iter_mut().zip(plaintext.iter()) {
553        *dest = *src;
554    }
555    st.xor_read(&mut buf[..]).unwrap();
556
557    assert_eq!(buf[64..].to_vec(), [
558        0x6e, 0x2e, 0x35, 0x9a, 0x25, 0x68, 0xf9, 0x80, 0x41, 0xba, 0x07, 0x28, 0xdd, 0x0d, 0x69, 0x81,
559        0xe9, 0x7e, 0x7a, 0xec, 0x1d, 0x43, 0x60, 0xc2, 0x0a, 0x27, 0xaf, 0xcc, 0xfd, 0x9f, 0xae, 0x0b,
560        0xf9, 0x1b, 0x65, 0xc5, 0x52, 0x47, 0x33, 0xab, 0x8f, 0x59, 0x3d, 0xab, 0xcd, 0x62, 0xb3, 0x57,
561        0x16, 0x39, 0xd6, 0x24, 0xe6, 0x51, 0x52, 0xab, 0x8f, 0x53, 0x0c, 0x35, 0x9f, 0x08, 0x61, 0xd8,
562        0x07, 0xca, 0x0d, 0xbf, 0x50, 0x0d, 0x6a, 0x61, 0x56, 0xa3, 0x8e, 0x08, 0x8a, 0x22, 0xb6, 0x5e,
563        0x52, 0xbc, 0x51, 0x4d, 0x16, 0xcc, 0xf8, 0x06, 0x81, 0x8c, 0xe9, 0x1a, 0xb7, 0x79, 0x37, 0x36,
564        0x5a, 0xf9, 0x0b, 0xbf, 0x74, 0xa3, 0x5b, 0xe6, 0xb4, 0x0b, 0x8e, 0xed, 0xf2, 0x78, 0x5e, 0x42,
565        0x87, 0x4d,
566    ].to_vec());
567}
568
569#[test]
570fn rfc_7539_case_2_chunked() {
571    let mut st = ChaCha::new_ietf(
572        &[
573            0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
574            0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
575            0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
576            0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
577        ], &[
578            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a,
579            0x00, 0x00, 0x00, 0x00
580        ]
581    );
582
583    let plaintext = b"Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.";
584    let mut buf = [0u8; 178];
585    for (dest, src) in buf[64..].iter_mut().zip(plaintext.iter()) {
586        *dest = *src;
587    }
588    st.xor_read(&mut buf[..40]).unwrap();
589    st.xor_read(&mut buf[40..78]).unwrap();
590    st.xor_read(&mut buf[78..79]).unwrap();
591    st.xor_read(&mut buf[79..128]).unwrap();
592    st.xor_read(&mut buf[128..]).unwrap();
593
594    assert_eq!(buf[64..].to_vec(), [
595        0x6e, 0x2e, 0x35, 0x9a, 0x25, 0x68, 0xf9, 0x80, 0x41, 0xba, 0x07, 0x28, 0xdd, 0x0d, 0x69, 0x81,
596        0xe9, 0x7e, 0x7a, 0xec, 0x1d, 0x43, 0x60, 0xc2, 0x0a, 0x27, 0xaf, 0xcc, 0xfd, 0x9f, 0xae, 0x0b,
597        0xf9, 0x1b, 0x65, 0xc5, 0x52, 0x47, 0x33, 0xab, 0x8f, 0x59, 0x3d, 0xab, 0xcd, 0x62, 0xb3, 0x57,
598        0x16, 0x39, 0xd6, 0x24, 0xe6, 0x51, 0x52, 0xab, 0x8f, 0x53, 0x0c, 0x35, 0x9f, 0x08, 0x61, 0xd8,
599        0x07, 0xca, 0x0d, 0xbf, 0x50, 0x0d, 0x6a, 0x61, 0x56, 0xa3, 0x8e, 0x08, 0x8a, 0x22, 0xb6, 0x5e,
600        0x52, 0xbc, 0x51, 0x4d, 0x16, 0xcc, 0xf8, 0x06, 0x81, 0x8c, 0xe9, 0x1a, 0xb7, 0x79, 0x37, 0x36,
601        0x5a, 0xf9, 0x0b, 0xbf, 0x74, 0xa3, 0x5b, 0xe6, 0xb4, 0x0b, 0x8e, 0xed, 0xf2, 0x78, 0x5e, 0x42,
602        0x87, 0x4d,
603    ].to_vec());
604}
605
606#[test]
607fn xchacha20_case_1() {
608    let mut stream = ChaCha::new_xchacha20(
609        &[
610            0x82, 0xf4, 0x11, 0xa0, 0x74, 0xf6, 0x56, 0xc6,
611            0x6e, 0x7d, 0xbd, 0xdb, 0x0a, 0x2c, 0x1b, 0x22,
612            0x76, 0x0b, 0x9b, 0x21, 0x05, 0xf4, 0xff, 0xdb,
613            0xb1, 0xd4, 0xb1, 0xe8, 0x24, 0xe2, 0x1d, 0xef,
614        ],
615        &[
616            0x3b, 0x07, 0xca, 0x6e, 0x72, 0x9e, 0xb4, 0x4a,
617            0x51, 0x0b, 0x7a, 0x1b, 0xe5, 0x18, 0x47, 0x83,
618            0x8a, 0x80, 0x4f, 0x8b, 0x10, 0x6b, 0x38, 0xbd,
619        ]
620    );
621
622    let mut xs = [0u8; 100];
623    stream.xor_read(&mut xs).unwrap();
624
625    assert_eq!(xs.to_vec(), [
626        0x20, 0x18, 0x63, 0x97, 0x0b, 0x8e, 0x08, 0x1f, 0x41, 0x22,
627        0xad, 0xdf, 0xdf, 0x32, 0xf6, 0xc0, 0x3e, 0x48, 0xd9, 0xbc,
628        0x4e, 0x34, 0xa5, 0x96, 0x54, 0xf4, 0x92, 0x48, 0xb9, 0xbe,
629        0x59, 0xd3, 0xea, 0xa1, 0x06, 0xac, 0x33, 0x76, 0xe7, 0xe7,
630        0xd9, 0xd1, 0x25, 0x1f, 0x2c, 0xbf, 0x61, 0xef, 0x27, 0x00,
631        0x0f, 0x3d, 0x19, 0xaf, 0xb7, 0x6b, 0x9c, 0x24, 0x71, 0x51,
632        0xe7, 0xbc, 0x26, 0x46, 0x75, 0x83, 0xf5, 0x20, 0x51, 0x8e,
633        0xcc, 0xd2, 0x05, 0x5c, 0xcd, 0x6c, 0xc8, 0xa1, 0x95, 0x95,
634        0x3d, 0x82, 0xa1, 0x0c, 0x20, 0x65, 0x91, 0x67, 0x78, 0xdb,
635        0x35, 0xda, 0x2b, 0xe4, 0x44, 0x15, 0xd2, 0xf5, 0xef, 0xb0,
636    ].to_vec());
637}
638
639#[test]
640fn chacha12_case_1() {
641    let mut stream = ChaCha::new_chacha12(
642        &[
643            0x27, 0xfc, 0x12, 0x0b, 0x01, 0x3b, 0x82, 0x9f,
644            0x1f, 0xae, 0xef, 0xd1, 0xab, 0x41, 0x7e, 0x86,
645            0x62, 0xf4, 0x3e, 0x0d, 0x73, 0xf9, 0x8d, 0xe8,
646            0x66, 0xe3, 0x46, 0x35, 0x31, 0x80, 0xfd, 0xb7,
647        ],
648        &[
649            0xdb, 0x4b, 0x4a, 0x41, 0xd8, 0xdf, 0x18, 0xaa
650        ]
651    );
652
653    let mut xs = [0u8; 100];
654    stream.xor_read(&mut xs).unwrap();
655
656    assert_eq!(xs.to_vec(), [
657        0x5f, 0x3c, 0x8c, 0x19, 0x0a, 0x78, 0xab, 0x7f,
658        0xe8, 0x08, 0xca, 0xe9, 0xcb, 0xcb, 0x0a, 0x98,
659        0x37, 0xc8, 0x93, 0x49, 0x2d, 0x96, 0x3a, 0x1c,
660        0x2e, 0xda, 0x6c, 0x15, 0x58, 0xb0, 0x2c, 0x83,
661        0xfc, 0x02, 0xa4, 0x4c, 0xbb, 0xb7, 0xe6, 0x20,
662        0x4d, 0x51, 0xd1, 0xc2, 0x43, 0x0e, 0x9c, 0x0b,
663        0x58, 0xf2, 0x93, 0x7b, 0xf5, 0x93, 0x84, 0x0c,
664        0x85, 0x0b, 0xda, 0x90, 0x51, 0xa1, 0xf0, 0x51,
665        0xdd, 0xf0, 0x9d, 0x2a, 0x03, 0xeb, 0xf0, 0x9f,
666        0x01, 0xbd, 0xba, 0x9d, 0xa0, 0xb6, 0xda, 0x79,
667        0x1b, 0x2e, 0x64, 0x56, 0x41, 0x04, 0x7d, 0x11,
668        0xeb, 0xf8, 0x50, 0x87, 0xd4, 0xde, 0x5c, 0x01,
669        0x5f, 0xdd, 0xd0, 0x44,
670    ].to_vec());
671}
672
673
674#[test]
675fn chacha8_case_1() {
676    let mut stream = ChaCha::new_chacha8(
677        &[
678            0x64, 0x1a, 0xea, 0xeb, 0x08, 0x03, 0x6b, 0x61,
679            0x7a, 0x42, 0xcf, 0x14, 0xe8, 0xc5, 0xd2, 0xd1,
680            0x15, 0xf8, 0xd7, 0xcb, 0x6e, 0xa5, 0xe2, 0x8b,
681            0x9b, 0xfa, 0xf8, 0x3e, 0x03, 0x84, 0x26, 0xa7,
682        ],
683        &[
684            0xa1, 0x4a, 0x11, 0x68, 0x27, 0x1d, 0x45, 0x9b,
685        ]
686    );
687
688    let mut xs = [0u8; 100];
689    stream.xor_read(&mut xs).unwrap();
690
691    assert_eq!(xs.to_vec(), [
692        0x17, 0x21, 0xc0, 0x44, 0xa8, 0xa6, 0x45, 0x35,
693        0x22, 0xdd, 0xdb, 0x31, 0x43, 0xd0, 0xbe, 0x35,
694        0x12, 0x63, 0x3c, 0xa3, 0xc7, 0x9b, 0xf8, 0xcc,
695        0xc3, 0x59, 0x4c, 0xb2, 0xc2, 0xf3, 0x10, 0xf7,
696        0xbd, 0x54, 0x4f, 0x55, 0xce, 0x0d, 0xb3, 0x81,
697        0x23, 0x41, 0x2d, 0x6c, 0x45, 0x20, 0x7d, 0x5c,
698        0xf9, 0xaf, 0x0c, 0x6c, 0x68, 0x0c, 0xce, 0x1f,
699        0x7e, 0x43, 0x38, 0x8d, 0x1b, 0x03, 0x46, 0xb7,
700        0x13, 0x3c, 0x59, 0xfd, 0x6a, 0xf4, 0xa5, 0xa5,
701        0x68, 0xaa, 0x33, 0x4c, 0xcd, 0xc3, 0x8a, 0xf5,
702        0xac, 0xe2, 0x01, 0xdf, 0x84, 0xd0, 0xa3, 0xca,
703        0x22, 0x54, 0x94, 0xca, 0x62, 0x09, 0x34, 0x5f,
704        0xcf, 0x30, 0x13, 0x2e,
705    ].to_vec());
706}
707
708#[test]
709fn chacha20_case_1() {
710    let mut stream = ChaCha::new_chacha20(
711        &[
712            0xfa, 0x44, 0x47, 0x8c, 0x59, 0xca, 0x70, 0x53,
713            0x8e, 0x35, 0x49, 0x09, 0x6c, 0xe8, 0xb5, 0x23,
714            0x23, 0x2c, 0x50, 0xd9, 0xe8, 0xe8, 0xd1, 0x0c,
715            0x20, 0x3e, 0xf6, 0xc8, 0xd0, 0x70, 0x98, 0xa5
716        ],
717        &[
718            0x8d, 0x3a, 0x0d, 0x6d, 0x78, 0x27, 0xc0, 0x07
719        ]
720    );
721
722    let offset = 274877906800u64;
723    assert!((offset>>38) != ((offset+240)>>38)); // This will overflow the small word of the counter
724
725    stream.seek_to(offset).unwrap();
726
727    let mut xs = [0u8; 256];
728    stream.xor_read(&mut xs).unwrap();
729
730    assert_eq!(xs.to_vec(), [
731        0x15, 0x46, 0xa5, 0x47, 0xff, 0x77, 0xc5, 0xc9,
732        0x64, 0xe4, 0x4f, 0xd0, 0x39, 0xe9, 0x13, 0xc6,
733        0x39, 0x5c, 0x8f, 0x19, 0xd4, 0x3e, 0xfa, 0xa8,
734        0x80, 0x75, 0x0f, 0x66, 0x87, 0xb4, 0xe6, 0xe2,
735        0xd8, 0xf4, 0x2f, 0x63, 0x54, 0x6d, 0xa2, 0xd1,
736        0x33, 0xb5, 0xaa, 0x2f, 0x1e, 0xf3, 0xf2, 0x18,
737        0xb6, 0xc7, 0x29, 0x43, 0x08, 0x9e, 0x40, 0x12,
738        0x21, 0x0c, 0x2c, 0xbe, 0xd0, 0xe8, 0xe9, 0x34,
739        0x98, 0xa6, 0x82, 0x5f, 0xc8, 0xff, 0x7a, 0x50,
740        0x4f, 0x26, 0xdb, 0x33, 0xb6, 0xcb, 0xe3, 0x62,
741        0x99, 0x43, 0x62, 0x44, 0xc9, 0xb2, 0xef, 0xf8,
742        0x83, 0x02, 0xc5, 0x59, 0x33, 0x91, 0x1b, 0x7d,
743        0x5d, 0xea, 0x75, 0xf2, 0xb6, 0xd4, 0x76, 0x1b,
744        0xa4, 0x4b, 0xb6, 0xf8, 0x14, 0xc9, 0x87, 0x9d,
745        0x2b, 0xa2, 0xac, 0x8b, 0x17, 0x8f, 0xa1, 0x10,
746        0x4a, 0x36, 0x86, 0x94, 0x87, 0x23, 0x39, 0x73,
747        0x8f, 0xfb, 0x96, 0x0e, 0x33, 0xdb, 0x39, 0xef,
748        0xb8, 0xea, 0xef, 0x88, 0x5b, 0x91, 0x0e, 0xea,
749        0x07, 0x8e, 0x7a, 0x1f, 0xeb, 0x3f, 0x81, 0x85,
750        0xda, 0xfd, 0x14, 0x55, 0xb7, 0x04, 0xd7, 0x6d,
751        0xa3, 0xa0, 0xce, 0x47, 0x60, 0x74, 0x18, 0x41,
752        0x21, 0x7b, 0xba, 0x1e, 0x4e, 0xce, 0x76, 0x0e,
753        0xaf, 0x68, 0x61, 0x71, 0x33, 0x43, 0x1f, 0xeb,
754        0x80, 0x6c, 0x06, 0x11, 0x73, 0xaf, 0x6b, 0x8b,
755        0x2a, 0x23, 0xbe, 0x90, 0xc5, 0xd1, 0x45, 0xcc,
756        0x25, 0x8e, 0x3c, 0x11, 0x9a, 0xab, 0x28, 0x00,
757        0xf0, 0xc7, 0xbc, 0x19, 0x59, 0xda, 0xe7, 0x54,
758        0x81, 0x71, 0x2c, 0xab, 0x73, 0x1b, 0x7d, 0xfd,
759        0x78, 0x3f, 0xa3, 0xa2, 0x28, 0xf9, 0x96, 0x8a,
760        0xae, 0xa6, 0x8f, 0x36, 0xa9, 0x2f, 0x43, 0xc9,
761        0xb5, 0x23, 0x33, 0x7a, 0x55, 0xb9, 0x7b, 0xca,
762        0xf5, 0xf5, 0x77, 0x44, 0x47, 0xbf, 0x41, 0xe8,
763    ].to_vec());
764}
765
766#[test]
767fn seek_off_end() {
768    let mut st = ChaCha::new_ietf(&[0xff; 32], &[0; 12]);
769
770    assert_eq!(st.seek_to(0x40_0000_0000), Err(Error::EndReached));
771    assert_eq!(st.xor_read(&mut [0u8; 1]), Err(Error::EndReached));
772
773    assert_eq!(st.seek_to(1), Ok(()));
774    assert!(st.xor_read(&mut [0u8; 1]).is_ok());
775}
776
777#[test]
778fn read_last_bytes() {
779    let mut st = ChaCha::new_ietf(&[0xff; 32], &[0; 12]);
780
781    st.seek_to(0x40_0000_0000 - 10).expect("should be able to seek to near the end");
782    st.xor_read(&mut [0u8; 10]).expect("should be able to read last 10 bytes");
783    assert!(st.xor_read(&mut [0u8; 1]).is_err());
784    assert!(st.xor_read(&mut [0u8; 10]).is_err());
785
786    st.seek_to(0x40_0000_0000 - 10).unwrap();
787    assert!(st.xor_read(&mut [0u8; 11]).is_err());
788}
789
790#[test]
791fn seek_consistency() {
792    let mut st = ChaCha::new_ietf(&[0x50; 32], &[0x44; 12]);
793
794    let mut continuous = [0u8; 1000];
795    st.xor_read(&mut continuous).unwrap();
796
797    let mut chunks = [0u8; 1000];
798
799    st.seek_to(128).unwrap();
800    st.xor_read(&mut chunks[128..300]).unwrap();
801
802    st.seek_to(0).unwrap();
803    st.xor_read(&mut chunks[0..10]).unwrap();
804
805    st.seek_to(300).unwrap();
806    st.xor_read(&mut chunks[300..533]).unwrap();
807
808    st.seek_to(533).unwrap();
809    st.xor_read(&mut chunks[533..]).unwrap();
810
811    st.seek_to(10).unwrap();
812    st.xor_read(&mut chunks[10..128]).unwrap();
813
814    assert_eq!(continuous.to_vec(), chunks.to_vec());
815
816    // Make sure we don't affect a nonce word when we hit the end with the small block counter
817    assert!(st.seek_to(0x40_0000_0000).is_err());
818    let mut small = [0u8; 100];
819    st.seek_to(0).unwrap();
820    st.xor_read(&mut small).unwrap();
821    assert_eq!(small.to_vec(), continuous[..100].to_vec());
822}
823
824} // mod tests
825
826
827#[cfg(all(test, feature="bench"))]
828mod bench {
829    use super::{ChaCha, KeyStream};
830    use test::Bencher;
831
832    #[bench]
833    pub fn chacha20(bh: &mut Bencher) {
834        let mut stream = ChaCha::new_chacha20(&[0; 32], &[0; 8]);
835        let mut buf = [0u8; 1024];
836        bh.bytes = buf.len() as u64;
837        bh.iter(|| {
838            let _ = stream.xor_read(&mut buf);
839        });
840    }
841}