use libcrux_hacl_rs::streaming_types::error_code;
use libcrux_poly1305::hacl::mac_poly1305::{self, state_t};
use super::cpu::HaveNeon;
use super::poly1305_arm::Poly1305Neon;
pub(crate) enum Poly1305 {
Neon(Poly1305Neon),
Portable(Hacl),
}
impl Poly1305 {
pub(crate) fn new(key: &[u8; 32]) -> Self {
match HaveNeon::check() {
Some(token) => Self::Neon(Poly1305Neon::new(key, token)),
None => Self::Portable(Hacl::new(key)),
}
}
pub(crate) fn add_bytes(&mut self, bytes: &[u8]) {
match self {
Self::Neon(p) => p.add_bytes(bytes),
Self::Portable(p) => p.add_bytes(bytes),
}
}
pub(crate) fn finish(self) -> [u8; 16] {
match self {
Self::Neon(p) => p.finish(),
Self::Portable(p) => p.finish(),
}
}
}
pub(crate) struct Hacl([state_t; 1]);
impl Hacl {
fn new(key: &[u8; 32]) -> Self {
let mut state = [state_t {
block_state: [0; 25],
buf: [0; 16],
total_len: 0,
p_key: [0; 32],
}];
mac_poly1305::reset(&mut state, key);
Self(state)
}
fn add_bytes(&mut self, bytes: &[u8]) {
let result = mac_poly1305::update(&mut self.0, bytes, bytes.len() as u32);
assert!(result == error_code::Success, "Poly1305 input fits 32 bits");
}
fn finish(self) -> [u8; 16] {
let mut tag = [0u8; 16];
mac_poly1305::digest(&self.0, &mut tag);
tag
}
}
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use super::*;
use crate::low::ct;
#[test]
fn neon_matches_portable() {
let Some(token) = HaveNeon::check() else {
return;
};
let key: [u8; 32] = core::array::from_fn(|i| 0xa5 ^ (i as u8).wrapping_mul(29));
let msg: Vec<u8> = (0..1100u32)
.map(|i| (i.wrapping_mul(0x9e3779b9) >> 24) as u8)
.collect();
ct::secret_slice(&key);
ct::secret_slice(&msg);
for len in (0..=200).chain([255, 256, 257, 1023, 1024, 1025, 1100]) {
let msg = &msg[..len];
let mut want = Hacl::new(&key);
want.add_bytes(msg);
let want = ct::into_public(want.finish());
for piece in [len.max(1), 1, 7, 16, 31, 32, 33, 65] {
let mut got = Poly1305Neon::new(&key, token);
for chunk in msg.chunks(piece) {
got.add_bytes(chunk);
}
assert_eq!(
ct::into_public(got.finish()),
want,
"{len} bytes in pieces of {piece}"
);
}
}
let ones = [0xff; 32];
let msg = [0xff; 1100];
for len in [16, 17, 32, 64, 1100] {
let mut want = Hacl::new(&ones);
want.add_bytes(&msg[..len]);
let mut got = Poly1305Neon::new(&ones, token);
got.add_bytes(&msg[..len]);
assert_eq!(
ct::into_public(got.finish()),
ct::into_public(want.finish()),
"{len} bytes of ones"
);
}
}
}