#![no_std]
#[cfg(test)]
mod tests;
pub type ChaCha8Block = [u32; 16];
#[inline(always)]
#[cfg_attr(feature = "no-panic", no_panic::no_panic)]
pub fn block_to_bytes(block: &ChaCha8Block) -> [u8; 64] {
unsafe { block.as_ptr().cast::<[u8; 64]>().read() }
}
#[inline(always)]
#[cfg_attr(feature = "no-panic", no_panic::no_panic)]
pub fn bytes_to_block(bytes: &[u8; 64]) -> ChaCha8Block {
unsafe { bytes.as_ptr().cast::<ChaCha8Block>().read_unaligned() }
}
#[derive(Debug, Copy, Clone)]
pub struct ChaCha8State {
data: ChaCha8Block,
}
impl ChaCha8State {
const ROUNDS: usize = 8;
#[inline]
#[cfg_attr(feature = "no-panic", no_panic::no_panic)]
pub fn init(key: &[u8; 32], nonce: &[u8; 12]) -> Self {
let mut data = [0u32; 16];
data[0] = 0x61707865;
data[1] = 0x3320646e;
data[2] = 0x79622d32;
data[3] = 0x6b206574;
for (i, &chunk) in key.as_chunks::<4>().0.iter().enumerate() {
data[4 + i] = u32::from_le_bytes(chunk);
}
for (i, &chunk) in nonce.as_chunks::<4>().0.iter().enumerate() {
data[13 + i] = u32::from_le_bytes(chunk);
}
Self { data }
}
#[inline(always)]
#[cfg_attr(feature = "no-panic", no_panic::no_panic)]
pub fn to_repr(self) -> ChaCha8Block {
self.data
}
#[inline(always)]
#[cfg_attr(feature = "no-panic", no_panic::no_panic)]
pub fn from_repr(data: ChaCha8Block) -> Self {
Self { data }
}
#[inline(always)]
#[cfg_attr(feature = "no-panic", no_panic::no_panic)]
pub fn compute_block(mut self, counter: u32) -> ChaCha8Block {
self.data[12] = counter;
let initial = self.data;
for _ in 0..Self::ROUNDS / 2 {
self.quarter_round(0, 4, 8, 12);
self.quarter_round(1, 5, 9, 13);
self.quarter_round(2, 6, 10, 14);
self.quarter_round(3, 7, 11, 15);
self.quarter_round(0, 5, 10, 15);
self.quarter_round(1, 6, 11, 12);
self.quarter_round(2, 7, 8, 13);
self.quarter_round(3, 4, 9, 14);
}
#[expect(clippy::needless_range_loop)]
for i in 0..16 {
self.data[i] = self.data[i].wrapping_add(initial[i]);
}
self.data
}
#[inline(always)]
#[cfg_attr(feature = "no-panic", no_panic::no_panic)]
fn quarter_round(&mut self, a: usize, b: usize, c: usize, d: usize) {
self.data[a] = self.data[a].wrapping_add(self.data[b]);
self.data[d] ^= self.data[a];
self.data[d] = self.data[d].rotate_left(16);
self.data[c] = self.data[c].wrapping_add(self.data[d]);
self.data[b] ^= self.data[c];
self.data[b] = self.data[b].rotate_left(12);
self.data[a] = self.data[a].wrapping_add(self.data[b]);
self.data[d] ^= self.data[a];
self.data[d] = self.data[d].rotate_left(8);
self.data[c] = self.data[c].wrapping_add(self.data[d]);
self.data[b] ^= self.data[c];
self.data[b] = self.data[b].rotate_left(7);
}
}