#![allow(non_snake_case)]
use super::field::GF448;
use super::ed448::{Point, Scalar};
pub fn x448(point: &[u8; 56], scalar: &[u8; 56]) -> [u8; 56] {
let mut s = *scalar;
s[0] &= 252;
s[55] |= 128;
let x1 = GF448::decode_reduce(point);
let mut x2 = GF448::ONE;
let mut z2 = GF448::ZERO;
let mut x3 = x1;
let mut z3 = GF448::ONE;
let mut swap = 0u32;
for t in (0..448).rev() {
let kt = (((s[t >> 3] >> (t & 7)) & 1) as u32).wrapping_neg();
swap ^= kt;
GF448::cswap(&mut x2, &mut x3, swap);
GF448::cswap(&mut z2, &mut z3, swap);
swap = kt;
let A = x2 + z2;
let B = x2 - z2;
let AA = A.square();
let BB = B.square();
let C = x3 + z3;
let D = x3 - z3;
let E = AA - BB;
let DA = D * A;
let CB = C * B;
x3 = (DA + CB).square();
z3 = x1 * (DA - CB).square();
x2 = AA * BB;
z2 = E * (AA + E.mul_small(39081));
}
GF448::cswap(&mut x2, &mut x3, swap);
GF448::cswap(&mut z2, &mut z3, swap);
(x2 / z2).encode()
}
pub fn x448_base(scalar: &[u8; 56]) -> [u8; 56] {
let mut sb = *scalar;
sb[0] &= 252;
sb[55] |= 128;
let s = Scalar::decode_reduce(&sb[..]);
let P = Point::mulgen(&s);
let u = P.to_montgomery_u();
u.encode()
}
#[cfg(test)]
mod tests {
use super::{x448, x448_base};
use sha2::{Sha512, Digest};
#[test]
fn x448_mc() {
let mut k = [0u8; 56];
k[0] = 5;
let mut u = k;
let mut ref1 = [0u8; 56];
hex::decode_to_slice("3f482c8a9f19b01e6c46ee9711d9dc14fd4bf67af30765c2ae2b846a4d23a8cd0db897086239492caf350b51f833868b9bc2b3bca9cf4113", &mut ref1[..]).unwrap();
let mut ref1000 = [0u8; 56];
hex::decode_to_slice("aa3b4749d55b9daf1e5b00288826c467274ce3ebbdd5c17b975e09d4af6c67cf10d087202db88286e2b79fceea3ec353ef54faa26e219f38", &mut ref1000[..]).unwrap();
for i in 0..1000 {
let old_k = k;
k = x448(&u, &k);
u = old_k;
if i == 0 {
assert!(k == ref1);
}
}
assert!(k == ref1000);
}
#[test]
fn x448_basepoint() {
let mut sh = Sha512::new();
let mut b = [0u8; 56];
b[0] = 5;
for i in 0..20 {
sh.update(&(i as u64).to_le_bytes());
let v = sh.finalize_reset();
let mut k = [0u8; 56];
k[..].copy_from_slice(&v[..56]);
assert!(x448(&b, &k) == x448_base(&k));
}
}
}