use curve25519_dalek::montgomery::MontgomeryPoint;
use super::cpu::HaveNeon;
#[cfg(any(curve25519_dalek_bits = "64", curve25519_dalek_backend = "fiat"))]
compile_error!(
"rind runs curve25519-dalek's serial 32-bit backend on 32-bit ARM: \
remove the curve25519_dalek_bits and curve25519_dalek_backend cfgs"
);
unsafe extern "C" {
#[link_name = aws_lc_symbol!("x25519_NEON")]
fn x25519_neon(out: *mut [u8; 32], scalar: *const [u8; 32], point: *const [u8; 32]);
}
core::arch::global_asm!(
".code 32",
include_str!("boringssl/x25519-asm-arm.s"),
".fpu vfpv3",
x25519_NEON = sym x25519_neon,
);
pub(crate) fn curve25519_x25519(res: &mut [u64; 4], scalar: &[u64; 4], point: &[u64; 4]) {
let (scalar, point) = (to_bytes(scalar), to_bytes(point));
let out = match HaveNeon::check() {
Some(neon) => ladder_neon(neon, &scalar, &point),
None => MontgomeryPoint(point).mul_clamped(scalar).0,
};
*res = from_bytes(&out);
}
pub(crate) fn curve25519_x25519base(res: &mut [u64; 4], scalar: &[u64; 4]) {
*res = from_bytes(&MontgomeryPoint::mul_base_clamped(to_bytes(scalar)).0);
}
fn ladder_neon(_: HaveNeon, scalar: &[u8; 32], point: &[u8; 32]) -> [u8; 32] {
let mut out = [0u8; 32];
unsafe { x25519_neon(&mut out, scalar, point) };
out
}
fn to_bytes(words: &[u64; 4]) -> [u8; 32] {
let mut r = [0u8; 32];
for (chunk, word) in r.as_chunks_mut::<8>().0.iter_mut().zip(words) {
*chunk = word.to_le_bytes();
}
r
}
fn from_bytes(bytes: &[u8; 32]) -> [u64; 4] {
let mut r = [0u64; 4];
for (word, chunk) in r.iter_mut().zip(bytes.as_chunks::<8>().0) {
*word = u64::from_le_bytes(*chunk);
}
r
}
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use super::*;
use crate::low;
#[test]
fn neon_matches_dalek() {
let Some(neon) = HaveNeon::check() else {
return;
};
let mut points = vec![[0u8; 32], [0xff; 32]];
for low in [0xec, 0xed, 0xee] {
let mut p = [0xff; 32];
p[0] = low;
p[31] = 0x7f;
points.push(p);
}
let mut basepoint = [0u8; 32];
basepoint[0] = 9;
points.push(basepoint);
let mut x = 0x9e3779b9u32;
for _ in 0..8 {
points.push(core::array::from_fn(|_| {
x = x.wrapping_mul(1664525).wrapping_add(1013904223);
(x >> 24) as u8
}));
}
for (i, point) in points.iter().enumerate() {
let scalar: [u8; 32] = core::array::from_fn(|j| (i * 37 + j * 101) as u8 ^ 0xa5);
let scalar = low::ct::into_secret(scalar);
let want = low::ct::into_public(MontgomeryPoint(*point).mul_clamped(scalar).0);
let got = low::ct::into_public(ladder_neon(neon, &scalar, point));
assert_eq!(got, want, "point {i}");
}
}
}