1 // Copyright 2021 Brian Smith.
2 // Portions Copyright (c) 2014, Google Inc.
3 //
4 // Permission to use, copy, modify, and/or distribute this software for any
5 // purpose with or without fee is hereby granted, provided that the above
6 // copyright notice and this permission notice appear in all copies.
7 //
8 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
11 // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
13 // OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14 // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
15 // Adapted from the public domain, estream code by D. Bernstein.
16 // Adapted from the BoringSSL crypto/chacha/chacha.c.
17 
18 use super::{Counter, Key, BLOCK_LEN};
19 use crate::polyfill::ChunksFixedMut;
20 use core::ops::RangeFrom;
21 
ChaCha20_ctr32( key: &Key, counter: Counter, in_out: &mut [u8], src: RangeFrom<usize>, )22 pub(super) fn ChaCha20_ctr32(
23     key: &Key,
24     counter: Counter,
25     in_out: &mut [u8],
26     src: RangeFrom<usize>,
27 ) {
28     const SIGMA: [u32; 4] = [
29         u32::from_le_bytes(*b"expa"),
30         u32::from_le_bytes(*b"nd 3"),
31         u32::from_le_bytes(*b"2-by"),
32         u32::from_le_bytes(*b"te k"),
33     ];
34 
35     let key = key.words_less_safe();
36     let counter = counter.into_words_less_safe();
37 
38     let mut state = [
39         SIGMA[0], SIGMA[1], SIGMA[2], SIGMA[3], key[0], key[1], key[2], key[3], key[4], key[5],
40         key[6], key[7], counter[0], counter[1], counter[2], counter[3],
41     ];
42 
43     let mut in_out_len = in_out.len().checked_sub(src.start).unwrap();
44     let mut input = in_out[src].as_ptr();
45     let mut output = in_out.as_mut_ptr();
46 
47     let mut buf = [0u8; BLOCK_LEN];
48     while in_out_len > 0 {
49         chacha_core(&mut buf, &state);
50         state[12] += 1;
51 
52         let todo = core::cmp::min(BLOCK_LEN, in_out_len);
53         for (i, &b) in buf[..todo].iter().enumerate() {
54             let input = unsafe { *input.add(i) };
55             let b = input ^ b;
56             unsafe { *output.add(i) = b };
57         }
58 
59         in_out_len -= todo;
60         input = unsafe { input.add(todo) };
61         output = unsafe { output.add(todo) };
62     }
63 }
64 
65 // Performs 20 rounds of ChaCha on `input`, storing the result in `output`.
66 #[inline(always)]
chacha_core(output: &mut [u8; BLOCK_LEN], input: &State)67 fn chacha_core(output: &mut [u8; BLOCK_LEN], input: &State) {
68     let mut x = *input;
69 
70     for _ in (0..20).step_by(2) {
71         quarterround(&mut x, 0, 4, 8, 12);
72         quarterround(&mut x, 1, 5, 9, 13);
73         quarterround(&mut x, 2, 6, 10, 14);
74         quarterround(&mut x, 3, 7, 11, 15);
75         quarterround(&mut x, 0, 5, 10, 15);
76         quarterround(&mut x, 1, 6, 11, 12);
77         quarterround(&mut x, 2, 7, 8, 13);
78         quarterround(&mut x, 3, 4, 9, 14);
79     }
80 
81     for (x, input) in x.iter_mut().zip(input.iter()) {
82         *x = x.wrapping_add(*input);
83     }
84 
85     for (output, &x) in ChunksFixedMut::<[u8; 4]>::chunks_fixed_mut(output).zip(x.iter()) {
86         *output = u32::to_le_bytes(x)
87     }
88 }
89 
90 #[inline(always)]
quarterround(x: &mut State, a: usize, b: usize, c: usize, d: usize)91 fn quarterround(x: &mut State, a: usize, b: usize, c: usize, d: usize) {
92     #[inline(always)]
93     fn step(x: &mut State, a: usize, b: usize, c: usize, rotation: u32) {
94         x[a] = x[a].wrapping_add(x[b]);
95         x[c] = (x[c] ^ x[a]).rotate_left(rotation);
96     }
97     step(x, a, b, d, 16);
98     step(x, c, d, b, 12);
99     step(x, a, b, d, 8);
100     step(x, c, d, b, 7);
101 }
102 
103 type State = [u32; BLOCK_LEN / 4];
104