use super::field::*;
fn a24() -> FieldElement<4> {
let mut fe = FieldElement::<4>::ZERO;
fe.limbs[0] = 121_665;
fe
}
const BASE_U: [u8; 32] = {
let mut b = [0u8; 32];
b[0] = 9;
b
};
fn decode_scalar(scalar: &[u8; 32]) -> [u8; 32] {
let mut k = *scalar;
k[0] &= 248; k[31] &= 127; k[31] |= 64; k
}
fn decode_u(u: &[u8; 32]) -> FieldElement<4> {
let mut buf = *u;
buf[31] &= 0x7f;
FieldElement::<4>::from_bytes_le(&buf)
}
fn encode_u(fe: &FieldElement<4>) -> [u8; 32] {
let v = fe.to_bytes_le();
let mut out = [0u8; 32];
out.copy_from_slice(&v);
out
}
fn ct_swap_fe(a: &mut FieldElement<4>, b: &mut FieldElement<4>, swap: u64) {
let mask = 0u64.wrapping_sub(swap);
for i in 0..4 {
let t = mask & (a.limbs[i] ^ b.limbs[i]);
a.limbs[i] ^= t;
b.limbs[i] ^= t;
}
}
pub fn x25519(scalar: &[u8; 32], u: &[u8; 32]) -> [u8; 32] {
let k = decode_scalar(scalar);
let x1 = decode_u(u);
let a24 = a24();
let p = &CURVE25519_P;
let mut x_2 = FieldElement::<4>::one();
let mut z_2 = FieldElement::<4>::ZERO;
let mut x_3 = x1;
let mut z_3 = FieldElement::<4>::one();
let mut swap: u64 = 0;
for t in (0..=254).rev() {
let k_t = ((k[t >> 3] >> (t & 7)) & 1) as u64;
swap ^= k_t;
ct_swap_fe(&mut x_2, &mut x_3, swap);
ct_swap_fe(&mut z_2, &mut z_3, swap);
swap = k_t;
let a = field_add(&x_2, &z_2, p);
let aa = field_sqr(&a, p);
let b = field_sub(&x_2, &z_2, p);
let bb = field_sqr(&b, p);
let e = field_sub(&aa, &bb, p);
let c = field_add(&x_3, &z_3, p);
let d = field_sub(&x_3, &z_3, p);
let da = field_mul(&d, &a, p);
let cb = field_mul(&c, &b, p);
let da_plus_cb = field_add(&da, &cb, p);
x_3 = field_sqr(&da_plus_cb, p);
let da_minus_cb = field_sub(&da, &cb, p);
let da_minus_cb_sq = field_sqr(&da_minus_cb, p);
z_3 = field_mul(&x1, &da_minus_cb_sq, p);
x_2 = field_mul(&aa, &bb, p);
let a24_e = field_mul(&a24, &e, p);
let aa_plus_a24e = field_add(&aa, &a24_e, p);
z_2 = field_mul(&e, &aa_plus_a24e, p);
}
ct_swap_fe(&mut x_2, &mut x_3, swap);
ct_swap_fe(&mut z_2, &mut z_3, swap);
let z_inv = field_inv(&z_2, p);
let result = field_mul(&x_2, &z_inv, p);
encode_u(&result)
}
pub fn x25519_derive_public(sk: &[u8; 32]) -> [u8; 32] {
x25519(sk, &BASE_U)
}
pub fn x25519_ecdh(sk: &[u8; 32], peer_pk: &[u8; 32]) -> [u8; 32] {
x25519(sk, peer_pk)
}
#[cfg(test)]
mod tests {
use super::*;
fn hex32(h: &str) -> [u8; 32] {
assert_eq!(h.len(), 64);
let mut out = [0u8; 32];
for i in 0..32 {
out[i] = u8::from_str_radix(&h[2 * i..2 * i + 2], 16).unwrap();
}
out
}
#[test]
fn rfc7748_section_5_2_vector_1() {
let scalar = hex32("a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4");
let u = hex32("e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c");
let expected = hex32("c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552");
let got = x25519(&scalar, &u);
assert_eq!(got, expected);
}
#[test]
fn rfc7748_section_5_2_vector_2() {
let scalar = hex32("4b66e9d4d1b4673c5ad22691957d6af5c11b6421e0ea01d42ca4169e7918ba0d");
let u = hex32("e5210f12786811d3f4b7959d0538ae2c31dbe7106fc03c3efc4cd549c715a493");
let expected = hex32("95cbde9476e8907d7aade45cb4b873f88b595a68799fa152e6f8f7647aac7957");
let got = x25519(&scalar, &u);
assert_eq!(got, expected);
}
#[test]
fn rfc7748_section_6_1_alice_pk() {
let alice_sk = hex32("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a");
let expected = hex32("8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a");
assert_eq!(x25519_derive_public(&alice_sk), expected);
}
#[test]
fn rfc7748_section_6_1_bob_pk() {
let bob_sk = hex32("5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb");
let expected = hex32("de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f");
assert_eq!(x25519_derive_public(&bob_sk), expected);
}
#[test]
fn rfc7748_section_6_1_shared_secret() {
let alice_sk = hex32("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a");
let bob_sk = hex32("5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb");
let alice_pk = x25519_derive_public(&alice_sk);
let bob_pk = x25519_derive_public(&bob_sk);
let expected = hex32("4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742");
assert_eq!(x25519_ecdh(&alice_sk, &bob_pk), expected);
assert_eq!(x25519_ecdh(&bob_sk, &alice_pk), expected);
}
#[test]
fn x25519_roundtrip_custom_keys() {
let alice_sk = hex32("0101010101010101010101010101010101010101010101010101010101010101");
let bob_sk = hex32("0202020202020202020202020202020202020202020202020202020202020202");
let alice_pk = x25519_derive_public(&alice_sk);
let bob_pk = x25519_derive_public(&bob_sk);
let s_ab = x25519_ecdh(&alice_sk, &bob_pk);
let s_ba = x25519_ecdh(&bob_sk, &alice_pk);
assert_eq!(s_ab, s_ba);
assert!(s_ab.iter().any(|&b| b != 0));
}
#[test]
fn clamping_is_idempotent() {
let base = hex32("a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4");
let u = hex32("e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c");
let ref_out = x25519(&base, &u);
let mut dirty = base;
dirty[0] |= 0b0000_0111; dirty[31] |= 0b1000_0000; dirty[31] &= !0b0100_0000; let dirty_out = x25519(&dirty, &u);
assert_eq!(ref_out, dirty_out);
}
#[test]
fn u_high_bit_is_ignored() {
let scalar = hex32("a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4");
let u = hex32("e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c");
let ref_out = x25519(&scalar, &u);
let mut u_dirty = u;
u_dirty[31] |= 0x80;
let dirty_out = x25519(&scalar, &u_dirty);
assert_eq!(ref_out, dirty_out);
}
}