diff --git a/README.md b/README.md index ab351cd..69207e3 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,10 @@ This example demonstrates: Make sure to handle any potential errors or invalid inputs in your actual implementation. +## TODO + + + ## License This project is licensed under the MIT License. See the `LICENSE` file for more details. diff --git a/src/ec/edwards.rs b/src/ec/edwards.rs new file mode 100644 index 0000000..ced60d4 --- /dev/null +++ b/src/ec/edwards.rs @@ -0,0 +1,502 @@ +use crate::ec::EllipticCurve; +use crate::ff::FiniteField; +use crate::Point; +use log::debug; +use num_bigint::BigUint; +use num_traits::{One, Zero}; + +/// Represents a twisted Edwards curve in the form ax^2 + y^2 = 1 + dx^2y^2 (mod p) +#[derive(Debug, Clone, PartialEq)] +pub struct Edwards { + pub p: BigUint, + pub a: BigUint, + pub d: BigUint, + pub g: Point, +} + +impl Edwards { + pub fn add_2(&self, p1: &Point, p2: &Point) -> Point { + debug!("Adding points (add_2): {:?} and {:?}", p1, p2); + match (p1, p2) { + (Point::Identity, _) => p2.clone(), + (_, Point::Identity) => p1.clone(), + (Point::Coordinates(x1, y1), Point::Coordinates(x2, y2)) => { + // Check if the points are inverses of each other + if x1 == x2 && FiniteField::add(y1, y2, &self.p) == self.p { + debug!("Points are inverses, returning identity"); + return Point::Identity; + } + + let x1y2 = FiniteField::mul(x1, y2, &self.p); + let y1x2 = FiniteField::mul(y1, x2, &self.p); + let x1x2 = FiniteField::mul(x1, x2, &self.p); + let y1y2 = FiniteField::mul(y1, y2, &self.p); + + let dx1x2y1y2 = + FiniteField::mul(&self.d, &FiniteField::mul(&x1x2, &y1y2, &self.p), &self.p); + + let x3_num = FiniteField::add(&x1y2, &y1x2, &self.p); + let x3_den = FiniteField::add(&BigUint::one(), &dx1x2y1y2, &self.p); + let x3 = FiniteField::div(&x3_num, &x3_den, &self.p); + + let y3_num = + FiniteField::sub(&y1y2, &FiniteField::mul(&self.a, &x1x2, &self.p), &self.p); + let y3_den = FiniteField::sub(&BigUint::one(), &dx1x2y1y2, &self.p); + let y3 = FiniteField::div(&y3_num, &y3_den, &self.p); + + debug!("Affine result (add_2): ({:?}, {:?})", x3, y3); + + // Check if the result is the identity point + if x3.is_zero() && y3 == self.p.clone() - BigUint::one() { + debug!("Result is the identity point"); + Point::Identity + } else { + Point::Coordinates(x3, y3) + } + } + } + } +} + +impl EllipticCurve for Edwards { + /// Checks if a given point lies on the twisted Edwards curve. + fn is_on_curve(&self, point: &Point) -> bool { + let (x, y) = match point { + Point::Coordinates(x, y) => (x, y), + Point::Identity => return true, // The identity point is always on the curve + }; + + debug!("Curve parameters:"); + debug!("p: {}", self.p); + debug!("a: {}", self.a); + debug!("d: {}", self.d); + debug!("Point coordinates:"); + debug!("x: {}", x); + debug!("y: {}", y); + + // Calculate x^2 and y^2 + let x2 = FiniteField::mul(x, x, &self.p); + let y2 = FiniteField::mul(y, y, &self.p); + + // Left-hand side of the equation: ax^2 + y^2 mod p + let lhs = FiniteField::add(&FiniteField::mul(&self.a, &x2, &self.p), &y2, &self.p); + + // Calculate d*x^2*y^2 mod p + let dxy2 = FiniteField::mul(&self.d, &FiniteField::mul(&x2, &y2, &self.p), &self.p); + + // Right-hand side of the equation: 1 + d*x^2*y^2 mod p + let rhs = FiniteField::add(&BigUint::one(), &dxy2, &self.p); + + debug!("Equation components:"); + debug!("ax^2 mod p: {}", FiniteField::mul(&self.a, &x2, &self.p)); + debug!("y^2 mod p: {}", y2); + debug!("LHS (ax^2 + y^2 mod p): {}", lhs); + debug!("d*x^2*y^2 mod p: {}", dxy2); + debug!("RHS (1 + d*x^2*y^2 mod p): {}", rhs); + + let result = lhs == rhs; + debug!("Is on curve: {}", result); + + result + } + + fn add(&self, p1: &Point, p2: &Point) -> Point { + debug!("Adding points: {:?} and {:?}", p1, p2); + match (p1, p2) { + (Point::Identity, _) => { + debug!("First point is identity, returning second point"); + p2.clone() + } + (_, Point::Identity) => { + debug!("Second point is identity, returning first point"); + p1.clone() + } + (Point::Coordinates(x1, y1), Point::Coordinates(x2, y2)) => { + // Check if the points are inverses of each other + if x1 == x2 && FiniteField::add(y1, y2, &self.p) == self.p { + debug!("Points are inverses, returning identity"); + return Point::Identity; + } + + // Convert to projective coordinates + let (x1, y1, z1) = (x1.clone(), y1.clone(), BigUint::one()); + let (x2, y2, z2) = (x2.clone(), y2.clone(), BigUint::one()); + + // Compute A = Z1 * Z2 + let a = FiniteField::mul(&z1, &z2, &self.p); + debug!("A = Z1 * Z2 = {:?}", a); + + // Compute B = A^2 + let b = FiniteField::mul(&a, &a, &self.p); + debug!("B = A^2 = {:?}", b); + + // Compute C = X1 * X2 + let c = FiniteField::mul(&x1, &x2, &self.p); + debug!("C = X1 * X2 = {:?}", c); + + // Compute D = Y1 * Y2 + let d = FiniteField::mul(&y1, &y2, &self.p); + debug!("D = Y1 * Y2 = {:?}", d); + + // Compute E = d * C * D + let e = FiniteField::mul(&self.d, &FiniteField::mul(&c, &d, &self.p), &self.p); + debug!("E = d * C * D = {:?}", e); + + // Compute F = B - E + let f = FiniteField::sub(&b, &e, &self.p); + debug!("F = B - E = {:?}", f); + + // Compute G = B + E + let g = FiniteField::add(&b, &e, &self.p); + debug!("G = B + E = {:?}", g); + + // Compute X3 = A * F * ((X1 + Y1) * (X2 + Y2) - C - D) + let x3 = FiniteField::mul(&a, &f, &self.p); + let x3 = FiniteField::mul( + &x3, + &FiniteField::sub( + &FiniteField::mul( + &FiniteField::add(&x1, &y1, &self.p), + &FiniteField::add(&x2, &y2, &self.p), + &self.p, + ), + &FiniteField::add(&c, &d, &self.p), + &self.p, + ), + &self.p, + ); + debug!("X3 = {:?}", x3); + + // Compute Y3 = A * G * (D - a * C) + let y3 = FiniteField::mul(&a, &g, &self.p); + let y3 = FiniteField::mul( + &y3, + &FiniteField::sub(&d, &FiniteField::mul(&self.a, &c, &self.p), &self.p), + &self.p, + ); + debug!("Y3 = {:?}", y3); + + // Compute Z3 = F * G + let z3 = FiniteField::mul(&f, &g, &self.p); + debug!("Z3 = {:?}", z3); + + // Check if the result is the identity point + if z3.is_zero() { + debug!("Result is the identity point (Z3 = 0)"); + return Point::Identity; + } + + // Convert back to affine coordinates + let z3_inv = FiniteField::inv_mul(&z3, &self.p); + let x3_affine = FiniteField::mul(&x3, &z3_inv, &self.p); + let y3_affine = FiniteField::mul(&y3, &z3_inv, &self.p); + + debug!("Affine result: ({:?}, {:?})", x3_affine, y3_affine); + + // Check if the result is the identity point in affine coordinates + if x3_affine.is_zero() && y3_affine == (self.p.clone() - BigUint::one()) { + debug!("Result is the identity point in affine coordinates"); + Point::Identity + } else { + Point::Coordinates(x3_affine, y3_affine) + } + } + } + } + + fn double(&self, _p: &Point) -> Point { + // Implementation needed + unimplemented!() + } + + fn mul(&self, point: &Point, scalar: &BigUint) -> Point { + let mut result = Point::Identity; + let mut temp = point.clone(); + let mut n = scalar.clone(); + + debug!("Starting scalar multiplication"); + debug!("Point: {:?}, Scalar: {:?}", point, scalar); + + while !n.is_zero() { + if n.bit(0) { + debug!("Adding temp to result"); + result = self.add(&result, &temp); + } + debug!("Doubling temp"); + temp = self.add(&temp, &temp); + n >>= 1; + } + + debug!("Scalar multiplication result: {:?}", result); + result + } + + fn order(&self) -> &BigUint { + unimplemented!() + } + + fn base_point(&self) -> &Point { + &self.g + } + + fn field_modulus(&self) -> &BigUint { + &self.p + } +} + +#[cfg(test)] +mod tests { + use super::*; + use env_logger; + use num_bigint::BigUint; + use num_traits::Num; + + fn setup_curves() -> Vec<(Edwards, Point, &'static str)> { + vec![setup_curve25519(), setup_ed25519(), setup_simple_curve()] + } + + fn setup_curve25519() -> (Edwards, Point, &'static str) { + let p = BigUint::from_str_radix( + "57896044618658097711785492504343953926634992332820282019728792003956564819949", + 10, + ) + .unwrap(); + let a = BigUint::from_str_radix( + "57896044618658097711785492504343953926634992332820282019728792003956564819948", + 10, + ) + .unwrap(); // -1 mod p + let d = BigUint::from_str_radix( + "37095705934669439343138083508754565189542113879843219016388785533085940283555", + 10, + ) + .unwrap(); + let gx = BigUint::from_str_radix( + "15112221349535400772501151409588531511454012693041857206046113283949847762202", + 10, + ) + .unwrap(); + let gy = BigUint::from_str_radix( + "46316835694926478169428394003475163141307993866256225615783033603165251855960", + 10, + ) + .unwrap(); + let g = Point::Coordinates(gx, gy); + + let curve = Edwards { + p: p.clone(), + a, + d, + g: g.clone(), + }; + + (curve, g, "Curve25519") + } + + fn setup_ed25519() -> (Edwards, Point, &'static str) { + // Use the same parameters as Curve25519 + let (curve, g, _) = setup_curve25519(); + (curve, g, "Ed25519") + } + + fn setup_simple_curve() -> (Edwards, Point, &'static str) { + let p = BigUint::from(11u32); + let a = BigUint::from(1u32); + let d = BigUint::from(3u32); + let g = Point::Coordinates(BigUint::from(4u32), BigUint::from(4u32)); + + let curve = Edwards { + p: p.clone(), + a, + d, + g: g.clone(), + }; + + (curve, g, "Simple Curve") + } + + #[test] + fn test_is_on_curve() { + env_logger::init(); + + for (curve, g, curve_name) in setup_curves() { + assert!( + curve.is_on_curve(&g), + "{} generator point should be on the curve", + curve_name + ); + assert!( + curve.is_on_curve(&Point::Identity), + "Identity point should be on {}", + curve_name + ); + assert!( + !curve.is_on_curve(&Point::Coordinates( + BigUint::from(4u32), + BigUint::from(5u32) + )), + "Point (4, 5) should not be on {}", + curve_name + ); + } + } + + #[test] + fn test_point_addition() { + for (curve, g, curve_name) in setup_curves() { + for add_method in &[Edwards::add, Edwards::add_2] { + let method_name = if *add_method as usize == Edwards::add as usize { + "add" + } else { + "add_2" + }; + + // Test G + G + let g2 = add_method(&curve, &g, &g); + assert!( + curve.is_on_curve(&g2), + "{}: 2G should be on the curve ({})", + curve_name, + method_name + ); + + // Test G + 0 = G + let identity = Point::Identity; + assert_eq!( + add_method(&curve, &g, &identity), + g, + "{}: G + 0 should equal G ({})", + curve_name, + method_name + ); + + // Test 0 + G = G + assert_eq!( + add_method(&curve, &identity, &g), + g, + "{}: 0 + G should equal G ({})", + curve_name, + method_name + ); + + // Test G + (-G) = 0 + let neg_g = Point::Coordinates( + g.x().unwrap().clone(), + FiniteField::sub(&curve.p, g.y().unwrap(), &curve.p), + ); + debug!("{}: Testing G + (-G) = 0 ({})", curve_name, method_name); + debug!("G: {:?}", g); + debug!("(-G): {:?}", neg_g); + let result = add_method(&curve, &g, &neg_g); + debug!("Result of G + (-G): {:?}", result); + assert_eq!( + result, + Point::Identity, + "{}: G + (-G) should equal 0 ({})", + curve_name, + method_name + ); + + // Test associativity: (G + G) + G = G + (G + G) + let left = add_method(&curve, &add_method(&curve, &g, &g), &g); + let right = add_method(&curve, &g, &add_method(&curve, &g, &g)); + assert_eq!( + left, right, + "{}: Point addition should be associative ({})", + curve_name, method_name + ); + + // Test commutativity: G + 2G = 2G + G + let g2 = add_method(&curve, &g, &g); + assert_eq!( + add_method(&curve, &g, &g2), + add_method(&curve, &g2, &g), + "{}: Point addition should be commutative ({})", + curve_name, + method_name + ); + } + } + } + + #[test] + fn test_multiple_additions() { + for (curve, g, curve_name) in setup_curves() { + for add_method in &[Edwards::add, Edwards::add_2] { + let method_name = if *add_method as usize == Edwards::add as usize { + "add" + } else { + "add_2" + }; + + let mut result = g.clone(); + for i in 1..=5 { + result = add_method(&curve, &result, &g); + assert!( + curve.is_on_curve(&result), + "{}: {}G should be on the curve ({})", + curve_name, + i + 1, + method_name + ); + } + } + } + } + + #[test] + fn test_add_methods_equivalence() { + for (curve, g, curve_name) in setup_curves() { + let g2 = curve.add(&g, &g); + let g2_2 = curve.add_2(&g, &g); + assert_eq!( + g2, g2_2, + "{}: add and add_2 should produce the same result for G + G", + curve_name + ); + + let g3 = curve.add(&g2, &g); + let g3_2 = curve.add_2(&g2, &g); + assert_eq!( + g3, g3_2, + "{}: add and add_2 should produce the same result for 2G + G", + curve_name + ); + } + } + + #[test] + fn test_scalar_multiplication() { + for (curve, g, curve_name) in setup_curves() { + // Test 2 * G + let scalar = BigUint::from(2u32); + let result = curve.mul(&g, &scalar); + assert!( + curve.is_on_curve(&result), + "{}: 2G should be on the curve", + curve_name + ); + assert_eq!( + result, + curve.add(&g, &g), + "{}: 2 * G should equal G + G", + curve_name + ); + + // Test 3 * G + let scalar = BigUint::from(3u32); + let result = curve.mul(&g, &scalar); + assert!( + curve.is_on_curve(&result), + "{}: 3G should be on the curve", + curve_name + ); + assert_eq!( + result, + curve.add(&curve.add(&g, &g), &g), + "{}: 3 * G should equal G + G + G", + curve_name + ); + } + } +} diff --git a/src/ec/mod.rs b/src/ec/mod.rs index b00f9bb..c5c5a9e 100644 --- a/src/ec/mod.rs +++ b/src/ec/mod.rs @@ -4,6 +4,9 @@ use num_bigint::BigUint; pub mod weierstrass; pub use weierstrass::WeierstrassCurve; +pub mod edwards; +// pub use edwards::Edwards; + /// Base trait for all elliptic curves pub trait EllipticCurve { fn add(&self, p1: &Point, p2: &Point) -> Point; diff --git a/src/ff.rs b/src/ff.rs index 78218a8..1132107 100644 --- a/src/ff.rs +++ b/src/ff.rs @@ -37,6 +37,10 @@ impl FiniteField { t.0 % p } + pub fn div(a: &BigUint, b: &BigUint, p: &BigUint) -> BigUint { + Self::mul(a, &Self::inv_mul(b, p), p) + } + pub fn inv_add(a: &BigUint, p: &BigUint) -> BigUint { if a.is_zero() { BigUint::zero() diff --git a/src/lib.rs b/src/lib.rs index 3920d81..e48a2f9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,6 @@ +#[cfg(test)] +extern crate env_logger; + mod ec; mod ff; mod point; diff --git a/src/point.rs b/src/point.rs index 0e284f2..fb59290 100644 --- a/src/point.rs +++ b/src/point.rs @@ -5,4 +5,20 @@ use num_bigint::BigUint; pub enum Point { Coordinates(BigUint, BigUint), Identity, -} \ No newline at end of file +} + +impl Point { + pub fn x(&self) -> Option<&BigUint> { + match self { + Point::Coordinates(x, _) => Some(x), + Point::Identity => None, + } + } + + pub fn y(&self) -> Option<&BigUint> { + match self { + Point::Coordinates(_, y) => Some(y), + Point::Identity => None, + } + } +}