1#![no_std]
4
5#[cfg(test)]
6mod tests;
7
8pub type ChaCha8Block = [u32; 16];
10
11#[inline(always)]
13#[cfg_attr(feature = "no-panic", no_panic::no_panic)]
14pub fn block_to_bytes(block: &ChaCha8Block) -> [u8; 64] {
15 unsafe { block.as_ptr().cast::<[u8; 64]>().read() }
17}
18
19#[inline(always)]
21#[cfg_attr(feature = "no-panic", no_panic::no_panic)]
22pub fn bytes_to_block(bytes: &[u8; 64]) -> ChaCha8Block {
23 unsafe { bytes.as_ptr().cast::<ChaCha8Block>().read_unaligned() }
25}
26
27#[derive(Debug, Copy, Clone)]
29pub struct ChaCha8State {
30 data: ChaCha8Block,
31}
32
33impl ChaCha8State {
34 const ROUNDS: usize = 8;
35
36 #[inline]
38 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
39 pub fn init(key: &[u8; 32], nonce: &[u8; 12]) -> Self {
40 let mut data = [0u32; 16];
41 data[0] = 0x61707865;
42 data[1] = 0x3320646e;
43 data[2] = 0x79622d32;
44 data[3] = 0x6b206574;
45
46 for (i, &chunk) in key.as_chunks::<4>().0.iter().enumerate() {
47 data[4 + i] = u32::from_le_bytes(chunk);
48 }
49
50 for (i, &chunk) in nonce.as_chunks::<4>().0.iter().enumerate() {
53 data[13 + i] = u32::from_le_bytes(chunk);
54 }
55
56 Self { data }
57 }
58
59 #[inline(always)]
61 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
62 pub fn to_repr(self) -> ChaCha8Block {
63 self.data
64 }
65
66 #[inline(always)]
68 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
69 pub fn from_repr(data: ChaCha8Block) -> Self {
70 Self { data }
71 }
72
73 #[inline(always)]
77 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
78 pub fn compute_block(mut self, counter: u32) -> ChaCha8Block {
79 self.data[12] = counter;
80 let initial = self.data;
83
84 for _ in 0..Self::ROUNDS / 2 {
85 self.quarter_round(0, 4, 8, 12);
86 self.quarter_round(1, 5, 9, 13);
87 self.quarter_round(2, 6, 10, 14);
88 self.quarter_round(3, 7, 11, 15);
89
90 self.quarter_round(0, 5, 10, 15);
91 self.quarter_round(1, 6, 11, 12);
92 self.quarter_round(2, 7, 8, 13);
93 self.quarter_round(3, 4, 9, 14);
94 }
95
96 #[allow(clippy::needless_range_loop)]
99 for i in 0..16 {
103 self.data[i] = self.data[i].wrapping_add(initial[i]);
104 }
105
106 self.data
107 }
108
109 #[inline(always)]
110 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
111 fn quarter_round(&mut self, a: usize, b: usize, c: usize, d: usize) {
112 self.data[a] = self.data[a].wrapping_add(self.data[b]);
113 self.data[d] ^= self.data[a];
114 self.data[d] = self.data[d].rotate_left(16);
115
116 self.data[c] = self.data[c].wrapping_add(self.data[d]);
117 self.data[b] ^= self.data[c];
118 self.data[b] = self.data[b].rotate_left(12);
119
120 self.data[a] = self.data[a].wrapping_add(self.data[b]);
121 self.data[d] ^= self.data[a];
122 self.data[d] = self.data[d].rotate_left(8);
123
124 self.data[c] = self.data[c].wrapping_add(self.data[d]);
125 self.data[b] ^= self.data[c];
126 self.data[b] = self.data[b].rotate_left(7);
127 }
128}