use super::cpu::HaveNeon;
unsafe extern "C" {
#[link_name = aws_lc_symbol!("ChaCha20_ctr32_nohw")]
fn ChaCha20_ctr32_nohw(
out: *mut u8,
input: *const u8,
len: usize,
key: *const u32,
counter_nonce: *const u32,
);
#[link_name = aws_lc_symbol!("ChaCha20_ctr32_neon")]
fn ChaCha20_ctr32_neon(
out: *mut u8,
input: *const u8,
len: usize,
key: *const u32,
counter_nonce: *const u32,
);
}
core::arch::global_asm!(
".text",
".fpu neon",
".code 32",
include_str!("aws_lc/chacha-armv4.s"),
".fpu vfpv3",
ChaCha20_ctr32_nohw = sym ChaCha20_ctr32_nohw,
ChaCha20_ctr32_neon = sym ChaCha20_ctr32_neon,
);
const NEON_MIN_LEN: usize = 192;
pub(super) fn chacha20_ctr32(state: &[u32; 16], buffer: &mut [u8], neon: Option<HaveNeon>) {
let len = buffer.len();
if len == 0 {
return;
}
let ptr = buffer.as_mut_ptr();
let key = state[4..12].as_ptr();
let counter_nonce = state[12..].as_ptr();
match neon {
Some(_) if len >= NEON_MIN_LEN => unsafe {
ChaCha20_ctr32_neon(ptr, ptr, len, key, counter_nonce)
},
_ => unsafe { ChaCha20_ctr32_nohw(ptr, ptr, len, key, counter_nonce) },
}
}
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use super::*;
use crate::low::ct;
#[test]
fn neon_matches_nohw() {
if HaveNeon::check().is_none() {
return;
}
let key: [u32; 8] = core::array::from_fn(|i| 0x0101_0101u32.wrapping_mul(i as u32 + 1));
let input: Vec<u8> = (0..1100u32)
.map(|i| (i.wrapping_mul(0x9e3779b9) >> 24) as u8)
.collect();
ct::secret_slice(&key);
ct::secret_slice(&input);
for counter in [0, 1, u32::MAX - 16] {
let counter_nonce = [counter, 0x4a00_0000, 0x0900_0000, 0x0700_0000];
for len in (1..=320).chain([511, 512, 513, 1023, 1024, 1025, 1088]) {
let mut want = input[..len].to_vec();
let mut got = want.clone();
unsafe {
ChaCha20_ctr32_nohw(
want.as_mut_ptr(),
want.as_ptr(),
len,
key.as_ptr(),
counter_nonce.as_ptr(),
);
ChaCha20_ctr32_neon(
got.as_mut_ptr(),
got.as_ptr(),
len,
key.as_ptr(),
counter_nonce.as_ptr(),
);
}
ct::public_slice(&want);
ct::public_slice(&got);
assert_eq!(got, want, "counter {counter}, {len} bytes");
}
}
}
}