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