use super::cpu::HaveNeon;
use crate::low::{Zeroable, zeroise_value};
#[repr(C, align(16))]
#[derive(Clone, Copy)]
struct Fe1305x2 {
v: [u32; 12],
}
static ZERO: Fe1305x2 = Fe1305x2 { v: [0; 12] };
unsafe extern "C" {
#[link_name = aws_lc_symbol!("openssl_poly1305_neon2_addmulmod")]
fn openssl_poly1305_neon2_addmulmod(
r: *mut Fe1305x2,
x: *const Fe1305x2,
y: *const Fe1305x2,
c: *const Fe1305x2,
);
#[link_name = aws_lc_symbol!("openssl_poly1305_neon2_blocks")]
fn openssl_poly1305_neon2_blocks(
h: *mut Fe1305x2,
precomp: *const [Fe1305x2; 2],
input: *const u8,
len: usize,
) -> usize;
}
core::arch::global_asm!(
".text",
".fpu neon",
".code 32",
include_str!("aws_lc/poly1305_arm_asm.s"),
".fpu vfpv3",
openssl_poly1305_neon2_blocks = sym openssl_poly1305_neon2_blocks,
openssl_poly1305_neon2_addmulmod = sym openssl_poly1305_neon2_addmulmod,
);
fn addmulmod(r: *mut Fe1305x2, x: *const Fe1305x2, y: *const Fe1305x2, c: &Fe1305x2) {
unsafe { openssl_poly1305_neon2_addmulmod(r, x, y, c) }
}
fn freeze(r: &mut Fe1305x2) {
let mut x0 = r.v[0];
let mut x1 = r.v[2];
let mut x2 = r.v[4];
let mut x3 = r.v[6];
let mut x4 = r.v[8];
for _ in 0..3 {
x1 = x1.wrapping_add(x0 >> 26);
x0 &= 0x3ffffff;
x2 = x2.wrapping_add(x1 >> 26);
x1 &= 0x3ffffff;
x3 = x3.wrapping_add(x2 >> 26);
x2 &= 0x3ffffff;
x4 = x4.wrapping_add(x3 >> 26);
x3 &= 0x3ffffff;
x0 = x0.wrapping_add(5u32.wrapping_mul(x4 >> 26));
x4 &= 0x3ffffff;
}
let mut y0 = x0.wrapping_add(5);
let mut y1 = x1.wrapping_add(y0 >> 26);
y0 &= 0x3ffffff;
let mut y2 = x2.wrapping_add(y1 >> 26);
y1 &= 0x3ffffff;
let mut y3 = x3.wrapping_add(y2 >> 26);
y2 &= 0x3ffffff;
let mut y4 = x4.wrapping_add(y3 >> 26);
y3 &= 0x3ffffff;
let swap = 0u32.wrapping_sub(y4 >> 26);
y4 &= 0x3ffffff;
y0 ^= x0;
y1 ^= x1;
y2 ^= x2;
y3 ^= x3;
y4 ^= x4;
y0 &= swap;
y1 &= swap;
y2 &= swap;
y3 &= swap;
y4 &= swap;
y0 ^= x0;
y1 ^= x1;
y2 ^= x2;
y3 ^= x3;
y4 ^= x4;
r.v[0] = y0;
r.v[2] = y1;
r.v[4] = y2;
r.v[6] = y3;
r.v[8] = y4;
}
fn load32(t: &[u8]) -> u32 {
u32::from_le_bytes([t[0], t[1], t[2], t[3]])
}
fn fe1305x2_tobytearray(r: &mut [u8; 16], x: &Fe1305x2) {
let mut x0 = x.v[0];
let mut x1 = x.v[2];
let mut x2 = x.v[4];
let mut x3 = x.v[6];
let mut x4 = x.v[8];
x1 = x1.wrapping_add(x0 >> 26);
x0 &= 0x3ffffff;
x2 = x2.wrapping_add(x1 >> 26);
x1 &= 0x3ffffff;
x3 = x3.wrapping_add(x2 >> 26);
x2 &= 0x3ffffff;
x4 = x4.wrapping_add(x3 >> 26);
x3 &= 0x3ffffff;
r[0..4].copy_from_slice(&x0.wrapping_add(x1 << 26).to_le_bytes());
r[4..8].copy_from_slice(&(x1 >> 6).wrapping_add(x2 << 20).to_le_bytes());
r[8..12].copy_from_slice(&(x2 >> 12).wrapping_add(x3 << 14).to_le_bytes());
r[12..16].copy_from_slice(&(x3 >> 18).wrapping_add(x4 << 8).to_le_bytes());
}
fn fe1305x2_frombytearray(r: &mut Fe1305x2, x: &[u8]) {
let (first, second) = x.split_at(x.len().min(16));
load_lane(r, 0, first);
if second.is_empty() {
for i in [1, 3, 5, 7, 9] {
r.v[i] = 0;
}
} else {
load_lane(r, 1, second);
}
}
fn load_lane(r: &mut Fe1305x2, lane: usize, block: &[u8]) {
let mut t = [0u8; 17];
t[..block.len()].copy_from_slice(block);
t[block.len()] = 1;
r.v[lane] = 0x3ffffff & load32(&t[0..]);
r.v[lane + 2] = 0x3ffffff & (load32(&t[3..]) >> 2);
r.v[lane + 4] = 0x3ffffff & (load32(&t[6..]) >> 4);
r.v[lane + 6] = 0x3ffffff & (load32(&t[9..]) >> 6);
r.v[lane + 8] = load32(&t[13..]);
}
pub(crate) struct Poly1305Neon {
r: Fe1305x2,
h: Fe1305x2,
c: Fe1305x2,
precomp: [Fe1305x2; 2],
buf: [u8; 32],
buf_used: usize,
key: [u8; 16],
}
impl Zeroable for Poly1305Neon {}
impl Poly1305Neon {
pub(crate) fn new(key: &[u8; 32], _: HaveNeon) -> Self {
let mut r = Fe1305x2 { v: [0; 12] };
r.v[0] = 0x3ffffff & load32(&key[0..]);
r.v[2] = 0x3ffff03 & (load32(&key[3..]) >> 2);
r.v[4] = 0x3ffc0ff & (load32(&key[6..]) >> 4);
r.v[6] = 0x3f03fff & (load32(&key[9..]) >> 6);
r.v[8] = 0x00fffff & (load32(&key[12..]) >> 8);
for i in [0, 2, 4, 6, 8] {
r.v[i + 1] = r.v[i];
}
let mut st = Self {
r,
h: ZERO,
c: ZERO,
precomp: [ZERO; 2],
buf: [0; 32],
buf_used: 0,
key: core::array::from_fn(|i| key[16 + i]),
};
let precomp = st.precomp.as_mut_ptr();
addmulmod(precomp, &st.r, &st.r, &ZERO);
addmulmod(unsafe { precomp.add(1) }, precomp, precomp, &ZERO);
st
}
pub(crate) fn add_bytes(&mut self, mut input: &[u8]) {
if self.buf_used != 0 {
let todo = (self.buf.len() - self.buf_used).min(input.len());
let (head, rest) = input.split_at(todo);
self.buf[self.buf_used..self.buf_used + todo].copy_from_slice(head);
self.buf_used += todo;
input = rest;
if self.buf_used == self.buf.len() && !input.is_empty() {
let h: *mut Fe1305x2 = &mut self.h;
addmulmod(h, h, &self.precomp[0], &ZERO);
fe1305x2_frombytearray(&mut self.c, &self.buf);
for i in 0..10 {
self.h.v[i] = self.h.v[i].wrapping_add(self.c.v[i]);
}
self.buf_used = 0;
}
}
while input.len() > 32 {
let len = input.len().min(1 << 20);
let left = unsafe {
openssl_poly1305_neon2_blocks(&mut self.h, &self.precomp, input.as_ptr(), len)
};
input = &input[len - left..];
}
if !input.is_empty() {
self.buf[..input.len()].copy_from_slice(input);
self.buf_used = input.len();
}
}
pub(crate) fn finish(mut self) -> [u8; 16] {
let h: *mut Fe1305x2 = &mut self.h;
addmulmod(h, h, &self.precomp[0], &ZERO);
if self.buf_used > 16 {
fe1305x2_frombytearray(&mut self.c, &self.buf[..self.buf_used]);
for i in [1, 3, 5, 7, 9] {
self.precomp[0].v[i] = self.r.v[i];
}
addmulmod(h, h, &self.precomp[0], &self.c);
} else if self.buf_used > 0 {
fe1305x2_frombytearray(&mut self.c, &self.buf[..self.buf_used]);
self.r.v[1] = 1;
for i in [3, 5, 7, 9] {
self.r.v[i] = 0;
}
addmulmod(h, h, &self.r, &self.c);
}
for i in [0, 2, 4, 6, 8] {
self.h.v[i] = self.h.v[i].wrapping_add(self.h.v[i + 1]);
}
freeze(&mut self.h);
fe1305x2_frombytearray(&mut self.c, &self.key);
self.c.v[8] ^= 1 << 24;
for i in [0, 2, 4, 6, 8] {
self.h.v[i] = self.h.v[i].wrapping_add(self.c.v[i]);
}
let mut mac = [0u8; 16];
fe1305x2_tobytearray(&mut mac, &self.h);
mac
}
}
impl Drop for Poly1305Neon {
fn drop(&mut self) {
zeroise_value(self);
}
}