// Copyright 2021 Brian Smith. // Portions Copyright (c) 2014, Google Inc. // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice appear in all copies. // // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION // OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ // Adapted from the public domain, estream code by D. Bernstein. // Adapted from the BoringSSL crypto/chacha/chacha.c. use super::{Counter, Key, BLOCK_LEN}; use crate::polyfill::ChunksFixedMut; use core::ops::RangeFrom; pub(super) fn ChaCha20_ctr32( key: &Key, counter: Counter, in_out: &mut [u8], src: RangeFrom, ) { const SIGMA: [u32; 4] = [ u32::from_le_bytes(*b"expa"), u32::from_le_bytes(*b"nd 3"), u32::from_le_bytes(*b"2-by"), u32::from_le_bytes(*b"te k"), ]; let key = key.words_less_safe(); let counter = counter.into_words_less_safe(); let mut state = [ SIGMA[0], SIGMA[1], SIGMA[2], SIGMA[3], key[0], key[1], key[2], key[3], key[4], key[5], key[6], key[7], counter[0], counter[1], counter[2], counter[3], ]; let mut in_out_len = in_out.len().checked_sub(src.start).unwrap(); let mut input = in_out[src].as_ptr(); let mut output = in_out.as_mut_ptr(); let mut buf = [0u8; BLOCK_LEN]; while in_out_len > 0 { chacha_core(&mut buf, &state); state[12] += 1; let todo = core::cmp::min(BLOCK_LEN, in_out_len); for (i, &b) in buf[..todo].iter().enumerate() { let input = unsafe { *input.add(i) }; let b = input ^ b; unsafe { *output.add(i) = b }; } in_out_len -= todo; input = unsafe { input.add(todo) }; output = unsafe { output.add(todo) }; } } // Performs 20 rounds of ChaCha on `input`, storing the result in `output`. #[inline(always)] fn chacha_core(output: &mut [u8; BLOCK_LEN], input: &State) { let mut x = *input; for _ in (0..20).step_by(2) { quarterround(&mut x, 0, 4, 8, 12); quarterround(&mut x, 1, 5, 9, 13); quarterround(&mut x, 2, 6, 10, 14); quarterround(&mut x, 3, 7, 11, 15); quarterround(&mut x, 0, 5, 10, 15); quarterround(&mut x, 1, 6, 11, 12); quarterround(&mut x, 2, 7, 8, 13); quarterround(&mut x, 3, 4, 9, 14); } for (x, input) in x.iter_mut().zip(input.iter()) { *x = x.wrapping_add(*input); } for (output, &x) in ChunksFixedMut::<[u8; 4]>::chunks_fixed_mut(output).zip(x.iter()) { *output = u32::to_le_bytes(x) } } #[inline(always)] fn quarterround(x: &mut State, a: usize, b: usize, c: usize, d: usize) { #[inline(always)] fn step(x: &mut State, a: usize, b: usize, c: usize, rotation: u32) { x[a] = x[a].wrapping_add(x[b]); x[c] = (x[c] ^ x[a]).rotate_left(rotation); } step(x, a, b, d, 16); step(x, c, d, b, 12); step(x, a, b, d, 8); step(x, c, d, b, 7); } type State = [u32; BLOCK_LEN / 4];