chacha20/backends/
soft.rs

1//! Portable implementation which does not rely on architecture-specific
2//! intrinsics.
3
4use crate::{Block, ChaChaCore, Unsigned, STATE_WORDS};
5use cipher::{
6    consts::{U1, U64},
7    BlockSizeUser, ParBlocksSizeUser, StreamBackend,
8};
9
10pub(crate) struct Backend<'a, R: Unsigned>(pub(crate) &'a mut ChaChaCore<R>);
11
12impl<'a, R: Unsigned> BlockSizeUser for Backend<'a, R> {
13    type BlockSize = U64;
14}
15
16impl<'a, R: Unsigned> ParBlocksSizeUser for Backend<'a, R> {
17    type ParBlocksSize = U1;
18}
19
20impl<'a, R: Unsigned> StreamBackend for Backend<'a, R> {
21    #[inline(always)]
22    fn gen_ks_block(&mut self, block: &mut Block) {
23        let res = run_rounds::<R>(&self.0.state);
24        self.0.state[12] = self.0.state[12].wrapping_add(1);
25
26        for (chunk, val) in block.chunks_exact_mut(4).zip(res.iter()) {
27            chunk.copy_from_slice(&val.to_le_bytes());
28        }
29    }
30}
31
32#[inline(always)]
33fn run_rounds<R: Unsigned>(state: &[u32; STATE_WORDS]) -> [u32; STATE_WORDS] {
34    let mut res = *state;
35
36    for _ in 0..R::USIZE {
37        // column rounds
38        quarter_round(0, 4, 8, 12, &mut res);
39        quarter_round(1, 5, 9, 13, &mut res);
40        quarter_round(2, 6, 10, 14, &mut res);
41        quarter_round(3, 7, 11, 15, &mut res);
42
43        // diagonal rounds
44        quarter_round(0, 5, 10, 15, &mut res);
45        quarter_round(1, 6, 11, 12, &mut res);
46        quarter_round(2, 7, 8, 13, &mut res);
47        quarter_round(3, 4, 9, 14, &mut res);
48    }
49
50    for (s1, s0) in res.iter_mut().zip(state.iter()) {
51        *s1 = s1.wrapping_add(*s0);
52    }
53    res
54}
55
56/// The ChaCha20 quarter round function
57fn quarter_round(a: usize, b: usize, c: usize, d: usize, state: &mut [u32; STATE_WORDS]) {
58    state[a] = state[a].wrapping_add(state[b]);
59    state[d] ^= state[a];
60    state[d] = state[d].rotate_left(16);
61
62    state[c] = state[c].wrapping_add(state[d]);
63    state[b] ^= state[c];
64    state[b] = state[b].rotate_left(12);
65
66    state[a] = state[a].wrapping_add(state[b]);
67    state[d] ^= state[a];
68    state[d] = state[d].rotate_left(8);
69
70    state[c] = state[c].wrapping_add(state[d]);
71    state[b] ^= state[c];
72    state[b] = state[b].rotate_left(7);
73}