use std::cmp::min;
pub fn poly1305_init(r: &mut[u32], s: &mut[u32], u: &mut[u32], key: &[u8]) {
r[0] = and!(shr!(read32_le!(&key[ 0..]), 0), 0x03FFFFFF);
r[1] = and!(shr!(read32_le!(&key[ 3..]), 2), 0x03FFFF03);
r[2] = and!(shr!(read32_le!(&key[ 6..]), 4), 0x03FFC0FF);
r[3] = and!(shr!(read32_le!(&key[ 9..]), 6), 0x03F03FFF);
r[4] = and!(shr!(read32_le!(&key[12..]), 8), 0x000FFFFF);
s[0] = read32_le!(&key[16..]);
s[1] = read32_le!(&key[20..]);
s[2] = read32_le!(&key[24..]);
s[3] = read32_le!(&key[28..]);
u[0] = 0;
u[1] = mul!(r[1], 5);
u[2] = mul!(r[2], 5);
u[3] = mul!(r[3], 5);
u[4] = mul!(r[4], 5);
}
pub fn poly1305_update(a: &mut[u32], r: &[u32], u: &[u32], mut data: &[u8], is_last: bool) {
let mut buf = vec![0; 16];
let mut w = vec![0; 5];
while !data.is_empty() {
let buf_len = min(data.len(), buf.len());
if buf_len < 16 {
buf.copy_from_slice(&[0; 16]);
if is_last { buf[buf_len] = 0x01 }
}
buf[..buf_len].copy_from_slice(&data[..buf_len]);
a[0] = add!(a[0], and!(shr!(read32_le!(&buf[ 0..]), 0), 0x03FFFFFF));
a[1] = add!(a[1], and!(shr!(read32_le!(&buf[ 3..]), 2), 0x03FFFFFF));
a[2] = add!(a[2], and!(shr!(read32_le!(&buf[ 6..]), 4), 0x03FFFFFF));
a[3] = add!(a[3], and!(shr!(read32_le!(&buf[ 9..]), 6), 0x03FFFFFF));
a[4] = match buf_len < 16 && is_last {
true => add!(a[4], or!(shr!(read32_le!(&buf[12..]), 8), 0x00000000)),
false => add!(a[4], or!(shr!(read32_le!(&buf[12..]), 8), 0x01000000))
};
macro_rules! m {
($a:expr, $b:expr) => ({ mul!($a as u64, $b as u64) })
}
w[0] = add!(m!(a[0], r[0]), m!(a[1], u[4]), m!(a[2], u[3]), m!(a[3], u[2]), m!(a[4], u[1]));
w[1] = add!(m!(a[0], r[1]), m!(a[1], r[0]), m!(a[2], u[4]), m!(a[3], u[3]), m!(a[4], u[2]));
w[2] = add!(m!(a[0], r[2]), m!(a[1], r[1]), m!(a[2], r[0]), m!(a[3], u[4]), m!(a[4], u[3]));
w[3] = add!(m!(a[0], r[3]), m!(a[1], r[2]), m!(a[2], r[1]), m!(a[3], r[0]), m!(a[4], u[4]));
w[4] = add!(m!(a[0], r[4]), m!(a[1], r[3]), m!(a[2], r[2]), m!(a[3], r[1]), m!(a[4], r[0]));
let mut c;
c = shr!(w[0], 26); a[0] = and!(w[0] as u32, 0x3FFFFFF); w[1] = add!(w[1], c);
c = shr!(w[1], 26); a[1] = and!(w[1] as u32, 0x3FFFFFF); w[2] = add!(w[2], c);
c = shr!(w[2], 26); a[2] = and!(w[2] as u32, 0x3FFFFFF); w[3] = add!(w[3], c);
c = shr!(w[3], 26); a[3] = and!(w[3] as u32, 0x3FFFFFF); w[4] = add!(w[4], c);
c = shr!(w[4], 26); a[4] = and!(w[4] as u32, 0x3FFFFFF);
a[0] = add!(a[0], mul!(c as u32, 5));
a[1] = add!(a[1], shr!(a[0], 26));
a[0] = and!(a[0], 0x3FFFFFF);
data = &data[buf_len..]
}
}
pub fn poly1305_finish(tag: &mut[u8], a: &mut[u32], s: &[u32]) {
let mut c;
c = shr!(a[1], 26); a[1] = and!(a[1], 0x3ffffff); a[2] = add!(a[2], c);
c = shr!(a[2], 26); a[2] = and!(a[2], 0x3ffffff); a[3] = add!(a[3], c);
c = shr!(a[3], 26); a[3] = and!(a[3], 0x3ffffff); a[4] = add!(a[4], c);
c = shr!(a[4], 26); a[4] = and!(a[4], 0x3ffffff); a[0] = add!(a[0], mul!(c, 5));
c = shr!(a[0], 26); a[0] = and!(a[0], 0x3ffffff); a[1] = add!(a[1], c);
let mut mux = gt!(a[0], 0x03FFFFFAu32);
for i in 1..5 { mux = and!(mux, eq!(a[i], 0x03FFFFFF)) }
c = 5;
for i in 0..5 {
let mut t = add!(a[i], c);
c = shr!(t, 26);
t = and!(t, 0x03FFFFFF);
a[i] = mux_bool!(mux, t, a[i]);
}
let mut word;
word = add!(a[0] as u64, shl!(a[1] as u64, 26), s[0] as u64);
write32_le!(word as u32 => &mut tag[0..]);
word = add!(shr!(word, 32), shl!(a[2] as u64, 20), s[1] as u64);
write32_le!(word as u32 => &mut tag[4..]);
word = add!(shr!(word, 32), shl!(a[3] as u64, 14), s[2] as u64);
write32_le!(word as u32 => &mut tag[8..]);
word = add!(shr!(word, 32) as u32, shl!(a[4], 8), s[3]) as u64;
write32_le!(word as u32 => &mut tag[12..]);
}