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

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

8
.idea/.gitignore generated vendored Normal file
View file

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

11
.idea/diffie-hellman.iml generated Normal file
View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="EMPTY_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

8
.idea/modules.xml generated Normal file
View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/diffie-hellman.iml" filepath="$PROJECT_DIR$/.idea/diffie-hellman.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

80
Cargo.lock generated Normal file
View file

@ -0,0 +1,80 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "autocfg"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "cfg-if"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
[[package]]
name = "crypto-bigint"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96272c2ff28b807e09250b180ad1fb7889a3258f7455759b5c3c58b719467130"
dependencies = [
"num-traits",
"rand_core",
"subtle",
]
[[package]]
name = "diffie-hellman"
version = "0.1.0"
dependencies = [
"crypto-bigint",
]
[[package]]
name = "getrandom"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "libc"
version = "0.2.176"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174"
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"

7
Cargo.toml Normal file
View file

@ -0,0 +1,7 @@
[package]
name = "diffie-hellman"
version = "0.1.0"
edition = "2024"
[dependencies]
crypto-bigint = { version = "0.6.1", features = ["rand"] }

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)
}