#[inline]
fn quarter_round(st: &mut [u32], a: u32, b: u32, c: u32, d: u32) {
let sta: u32 = st[a as usize];
let stb: u32 = st[b as usize];
let std: u32 = st[d as usize];
let sta1: u32 = sta.wrapping_add(stb);
let std1: u32 = std ^ sta1;
let std2: u32 = std1.wrapping_shl(16u32) | std1.wrapping_shr(16u32);
st[a as usize] = sta1;
st[d as usize] = std2;
let sta0: u32 = st[c as usize];
let stb0: u32 = st[d as usize];
let std0: u32 = st[b as usize];
let sta10: u32 = sta0.wrapping_add(stb0);
let std10: u32 = std0 ^ sta10;
let std20: u32 = std10.wrapping_shl(12u32) | std10.wrapping_shr(20u32);
st[c as usize] = sta10;
st[b as usize] = std20;
let sta2: u32 = st[a as usize];
let stb1: u32 = st[b as usize];
let std3: u32 = st[d as usize];
let sta11: u32 = sta2.wrapping_add(stb1);
let std11: u32 = std3 ^ sta11;
let std21: u32 = std11.wrapping_shl(8u32) | std11.wrapping_shr(24u32);
st[a as usize] = sta11;
st[d as usize] = std21;
let sta3: u32 = st[c as usize];
let stb2: u32 = st[d as usize];
let std4: u32 = st[b as usize];
let sta12: u32 = sta3.wrapping_add(stb2);
let std12: u32 = std4 ^ sta12;
let std22: u32 = std12.wrapping_shl(7u32) | std12.wrapping_shr(25u32);
st[c as usize] = sta12;
st[b as usize] = std22
}
#[inline]
fn double_round(st: &mut [u32]) {
quarter_round(st, 0u32, 4u32, 8u32, 12u32);
quarter_round(st, 1u32, 5u32, 9u32, 13u32);
quarter_round(st, 2u32, 6u32, 10u32, 14u32);
quarter_round(st, 3u32, 7u32, 11u32, 15u32);
quarter_round(st, 0u32, 5u32, 10u32, 15u32);
quarter_round(st, 1u32, 6u32, 11u32, 12u32);
quarter_round(st, 2u32, 7u32, 8u32, 13u32);
quarter_round(st, 3u32, 4u32, 9u32, 14u32)
}
#[inline]
fn rounds(st: &mut [u32]) {
double_round(st);
double_round(st);
double_round(st);
double_round(st);
double_round(st);
double_round(st);
double_round(st);
double_round(st);
double_round(st);
double_round(st)
}
pub(crate) fn hchacha20(key: &[u8; 32], nonce: &[u8; 16]) -> [u8; 32] {
let mut st = [0u32; 16];
let input = [b"expand 32-byte k", &key[..16], &key[16..], nonce];
for (s, b) in st
.iter_mut()
.zip(input.iter().flat_map(|b| b.as_chunks::<4>().0))
{
*s = u32::from_le_bytes(*b);
}
rounds(&mut st);
let mut out = [0u8; 32];
for (o, w) in out
.as_chunks_mut::<4>()
.0
.iter_mut()
.zip(st[..4].iter().chain(&st[12..]))
{
*o = w.to_le_bytes();
}
out
}
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use super::*;
#[test]
fn hchacha_test_vectors() {
let key = [
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
0x1c, 0x1d, 0x1e, 0x1f,
];
let nonce = [
0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x31, 0x41,
0x59, 0x27, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10,
];
assert_eq!(
hchacha20(&key, nonce[..16].try_into().unwrap()),
[
0x82, 0x41, 0x3b, 0x42, 0x27, 0xb2, 0x7b, 0xfe, 0xd3, 0x0e, 0x42, 0x50, 0x8a, 0x87,
0x7d, 0x73, 0xa0, 0xf9, 0xe4, 0xd5, 0x8a, 0x74, 0xa8, 0x53, 0xc1, 0x2e, 0xc4, 0x13,
0x26, 0xd3, 0xec, 0xdc,
]
);
}
}