almost working

This commit is contained in:
Neemek 2025-10-08 07:41:51 +02:00
commit 14cfa2efb0
9 changed files with 390 additions and 0 deletions

250
src/lib.rs Normal file
View file

@ -0,0 +1,250 @@
use std::fmt::{Display, Formatter, Write};
use crypto_bigint::{Encoding, MulMod, NonZero, Odd, Random, U256};
use std::ops::Mul;
use crypto_bigint::rand_core::OsRng;
pub fn add(left: u64, right: u64) -> u64 {
left + right
}
/// By^2 = x^3 + Ax^2 + x
#[derive(Clone, Copy, Debug)]
pub struct MontgomeryCurve {
pub a: U256,
pub b: U256,
pub p: U256,
}
impl MontgomeryCurve {
pub const fn new(a: U256, b: U256, p: U256) -> MontgomeryCurve {
MontgomeryCurve { a, b, p }
}
pub fn y(&self, x: U256) -> U256 {
((x*x*x + self.a*x*x + x)/self.b).sqrt()
}
pub fn point(self, x: U256) -> MontgomeryPoint {
MontgomeryPoint{
x,
z: U256::ONE,
curve: self,
}
}
}
fn clamp_u256(x: U256) -> U256 {
let mut words: [u8; 32] = x.to_le_bytes();
// clamp value
words[0] &= 0b1111_1000;
words[31] &= 0b0111_1111;
words[31] |= 0b0100_0000;
U256::from_le_bytes(words)
}
#[derive(Clone, Copy, Debug)]
pub struct MontgomeryPoint {
x: U256,
z: U256,
curve: MontgomeryCurve,
}
impl MontgomeryPoint {
pub fn add(&self, rhs: MontgomeryPoint, neg: MontgomeryPoint) -> MontgomeryPoint {
let p = &NonZero::new(CURVE_25519.p).unwrap();
let mut v_0 = self.x + self.z; // 1: V_0 = X_P + Z_P
let mut v_1 = rhs.x.wrapping_sub(&rhs.z); // 2: V_1 = X_Q - Z_Q
v_1 = v_1.mul_mod(&v_0, p); // 3: V_1 = V_1 * V_0
v_0 = self.x.sub_mod(&self.z, p); // 4: V_0 = X_P - Z_P
let mut v_2 = rhs.x.add_mod(&rhs.z, p); // 5: V_2 = X_Q + Z_Q
v_2 = v_2.mul_mod(&v_0, p); // 6: V_2 = V_2 * V_0
let mut v_3 = v_1 + v_2; // 7: V_3 = V_1 + V_2
v_3 = v_3.mul_mod(&v_3, p); // 8: V_3 = V_3^2
let mut v_4 = v_1.sub_mod(&v_2, p); // 9: V_4 = V_1 - V_2
v_4 = v_4.mul_mod(&v_4, p); // 10:
let x = neg.z.mul_mod(&v_3, p); // 11: X_⨁ = Z_⊝ * V_3
let z = neg.x.mul_mod(&v_4, p); // 12: Z_⨁ = X_⊝ * V_4
MontgomeryPoint {
x,
z,
curve: self.curve,
}
}
pub fn double(&self) -> MontgomeryPoint {
let p = &NonZero::new(CURVE_25519.p).unwrap();
let mut v_1 = self.x.add_mod(&self.z, p);
v_1 = v_1.mul_mod(&v_1, p);
let mut v_2 = self.x.sub_mod(&self.z, p);
v_2 = v_2.mul_mod(&v_2, p);
let x_2p = v_1.mul_mod(&v_2, p);
v_1 = v_1.sub_mod(&v_2, p);
let mut v_3 = ((self.curve.a + U256::from_u8(2))/U256::from_u8(4)).mul_mod(&v_1, p);
v_3 = v_3.add_mod(&v_2, p);
let z_2p = v_1.mul_mod(&v_3, p);
MontgomeryPoint {
x: x_2p,
z: z_2p,
curve: self.curve,
}
}
pub fn get_x(&self) -> U256 {
let inv = self.z.inv_odd_mod(&Odd::new(CURVE_25519.p).unwrap());
self.x.mul_mod(&inv.unwrap(), &NonZero::new(self.curve.p).unwrap())
}
}
impl Display for MontgomeryPoint {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("({} : {})", self.x, self.z))
}
}
impl Mul<U256> for MontgomeryPoint {
type Output = MontgomeryPoint;
/// ~constant-time~ montgomery ladder
fn mul(self, rhs: U256) -> Self::Output {
let mut x_0 = self;
let mut x_1 = x_0.double();
for i in (0..rhs.bits() - 2).rev() {
if rhs.bit(i).into() {
x_0 = x_0.double();
x_1 = x_0.add(x_1, self);
} else {
x_0 = x_0.add(x_1, self);
x_1 = x_1.double();
}
}
x_0
}
}
/*
fn montgomery_ladder(x: U256, n: U256) -> U256 {
let mut x_1 = x;
let mut x_2 = U256::ONE;
let mut z_2 = U256::ZERO;
let mut x_3 = x;
let mut z_3 = U256::ONE;
let mut prevbit = 0u8;
for i in (0..(n.bits()-1)).rev() {
let bit = bool::from(n.bit(i)) as u8;
let b = bit ^ prevbit;
prevbit = bit;
// CSwap
match b & 1 == 0 {
true => {
swap(&mut x_2, &mut x_3);
swap(&mut z_2, &mut z_3);
}
false => {
swap(&mut x_2, &mut x_3);
swap(&mut x_2, &mut x_3);
}
}
//ladder_step(&mut x_2, &mut z_2, &mut x_3, &mut z_3, x_1);
}
U256::ZERO
}
*/
/// from the curve: \
/// $y^2 = x*(x^2 + 486662x + 1)$
/// => A = 486662, B = 1, p = 2^255 - 19
pub const CURVE_25519: MontgomeryCurve = MontgomeryCurve::new(
U256::from_u32(486662u32),
U256::ONE,
U256::ONE.shl(255).sub_mod(&U256::from_u8(19), &U256::MAX),
);
#[derive(Debug, Clone, Copy)]
pub struct Public(U256);
impl From<&Secret> for Public {
fn from(s: &Secret) -> Public {
let p = CURVE_25519.point(U256::from_u8(9));
let x = (p * clamp_u256(s.0)).get_x();
Public(x)
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct Secret(U256);
impl Secret {
pub fn random() -> Secret {
Secret::from(U256::random(&mut OsRng))
}
pub fn diffie_hellman(&self, public: Public) -> Secret {
let b = public.0; // public
let a = self.0; // private
let p = CURVE_25519.point(b);
let s = p * a;
Secret(s.get_x())
}
}
impl Display for Secret {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("{}", self.0))
}
}
impl From<U256> for Secret {
fn from(value: U256) -> Self {
Secret(clamp_u256(value))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn diffie_hellman() {
let a = Secret::random();
let b = Secret::random();
// Public keys
let pa = Public::from(&a);
let pb = Public::from(&b);
// Shared secret
let sa = a.diffie_hellman(pb);
let sb = b.diffie_hellman(pa);
assert_eq!(sa, sb);
}
}

19
src/main.rs Normal file
View file

@ -0,0 +1,19 @@
use crypto_bigint::rand_core::OsRng;
use crypto_bigint::U256;
use crypto_bigint::Random;
use diffie_hellman::{Public, Secret, CURVE_25519};
fn main() {
println!("{}", CURVE_25519.p);
let a = Secret::random();
let b = Secret::random();
let A = Public::from(&a);
let B = Public::from(&b);
let sa = a.diffie_hellman(B);
let sb = b.diffie_hellman(A);
println!("{} == {}", sa, sb)
}