chacha20/backends/
soft.rs

1//! Portable implementation which does not rely on architecture-specific intrinsics.
2
3#![allow(clippy::cast_possible_truncation)]
4
5use crate::{ChaChaCore, Rounds, STATE_WORDS, Variant, quarter_round};
6
7#[cfg(feature = "cipher")]
8use crate::chacha::Block;
9#[cfg(feature = "cipher")]
10use cipher::{
11    BlockSizeUser, ParBlocksSizeUser, StreamCipherBackend,
12    consts::{U1, U64},
13};
14
15#[cfg(feature = "rng")]
16use crate::rng::BLOCK_WORDS;
17
18pub(crate) struct Backend<'a, R: Rounds, V: Variant>(pub(crate) &'a mut ChaChaCore<R, V>);
19
20#[cfg(feature = "cipher")]
21impl<R: Rounds, V: Variant> BlockSizeUser for Backend<'_, R, V> {
22    type BlockSize = U64;
23}
24
25#[cfg(feature = "cipher")]
26impl<R: Rounds, V: Variant> ParBlocksSizeUser for Backend<'_, R, V> {
27    type ParBlocksSize = U1;
28}
29
30#[cfg(feature = "cipher")]
31impl<R: Rounds, V: Variant> StreamCipherBackend for Backend<'_, R, V> {
32    #[inline(always)]
33    fn gen_ks_block(&mut self, block: &mut Block) {
34        let res = run_rounds::<R>(&self.0.state);
35        let mut ctr = (u64::from(self.0.state[13]) << 32) | u64::from(self.0.state[12]);
36        ctr = ctr.wrapping_add(1);
37        self.0.state[12] = ctr as u32;
38        if size_of::<V::Counter>() == 8 {
39            self.0.state[13] = (ctr >> 32) as u32;
40        }
41
42        for (chunk, val) in block.chunks_exact_mut(4).zip(res.iter()) {
43            chunk.copy_from_slice(&val.to_le_bytes());
44        }
45    }
46}
47
48#[cfg(feature = "rng")]
49impl<R: Rounds, V: Variant> Backend<'_, R, V> {
50    #[inline(always)]
51    pub(crate) fn gen_ks_blocks(&mut self, buffer: &mut [u32; 64]) {
52        for block in 0..4 {
53            let res = run_rounds::<R>(&self.0.state);
54            let mut ctr = (u64::from(self.0.state[13]) << 32) | u64::from(self.0.state[12]);
55            ctr = ctr.wrapping_add(1);
56            self.0.state[12] = ctr as u32;
57            self.0.state[13] = (ctr >> 32) as u32;
58
59            buffer[block * BLOCK_WORDS as usize..(block + 1) * BLOCK_WORDS as usize]
60                .copy_from_slice(&res);
61        }
62    }
63}
64
65#[inline(always)]
66fn run_rounds<R: Rounds>(state: &[u32; STATE_WORDS]) -> [u32; STATE_WORDS] {
67    let mut res = *state;
68
69    for _ in 0..R::COUNT {
70        // column rounds
71        quarter_round(0, 4, 8, 12, &mut res);
72        quarter_round(1, 5, 9, 13, &mut res);
73        quarter_round(2, 6, 10, 14, &mut res);
74        quarter_round(3, 7, 11, 15, &mut res);
75
76        // diagonal rounds
77        quarter_round(0, 5, 10, 15, &mut res);
78        quarter_round(1, 6, 11, 12, &mut res);
79        quarter_round(2, 7, 8, 13, &mut res);
80        quarter_round(3, 4, 9, 14, &mut res);
81    }
82
83    for (s1, s0) in res.iter_mut().zip(state.iter()) {
84        *s1 = s1.wrapping_add(*s0);
85    }
86    res
87}