#![allow(non_snake_case)]
use super::field::GF25519;
use super::ed25519::{Point, Scalar};
pub fn x25519(point: &[u8; 32], scalar: &[u8; 32]) -> [u8; 32] {
let mut s = *scalar;
s[0] &= 248;
s[31] &= 127;
s[31] |= 64;
let mut u = *point;
u[31] &= 127;
let x1 = GF25519::decode_reduce(&u[..]);
let mut x2 = GF25519::ONE;
let mut z2 = GF25519::ZERO;
let mut x3 = x1;
let mut z3 = GF25519::ONE;
let mut swap = 0u32;
for t in (0..255).rev() {
let kt = (((s[t >> 3] >> (t & 7)) & 1) as u32).wrapping_neg();
swap ^= kt;
GF25519::cswap(&mut x2, &mut x3, swap);
GF25519::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(121665));
}
GF25519::cswap(&mut x2, &mut x3, swap);
GF25519::cswap(&mut z2, &mut z3, swap);
(x2 / z2).encode()
}
pub fn x25519_base(scalar: &[u8; 32]) -> [u8; 32] {
let mut sb = *scalar;
sb[0] &= 248;
sb[31] &= 127;
sb[31] |= 64;
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::{x25519, x25519_base};
use sha2::{Sha256, Digest};
#[test]
fn x25519_mc() {
let mut k = [0u8; 32];
k[0] = 9;
let mut u = k;
let mut ref1 = [0u8; 32];
hex::decode_to_slice("422c8e7a6227d7bca1350b3e2bb7279f7897b87bb6854b783c60e80311ae3079", &mut ref1[..]).unwrap();
let mut ref1000 = [0u8; 32];
hex::decode_to_slice("684cf59ba83309552800ef566f2f4d3c1c3887c49360e3875f2eb94d99532c51", &mut ref1000[..]).unwrap();
for i in 0..1000 {
let old_k = k;
k = x25519(&u, &k);
u = old_k;
if i == 0 {
assert!(k == ref1);
}
}
assert!(k == ref1000);
}
#[test]
fn x25519_basepoint() {
let mut sh = Sha256::new();
let mut b = [0u8; 32];
b[0] = 9;
for i in 0..20 {
sh.update(&(i as u64).to_le_bytes());
let v = sh.finalize_reset();
let mut k = [0u8; 32];
k[..].copy_from_slice(&v);
assert!(x25519(&b, &k) == x25519_base(&k));
}
}
}