use num_bigint::BigUint;
#[derive(PartialEq, Clone, Debug)]
pub enum Point {
Coor(BigUint, BigUint),
Identity,
}
#[derive(PartialEq, Clone, Debug)]
pub struct EllipticCurve {
a: BigUint,
b: BigUint,
p: BigUint,
}
impl EllipticCurve {
pub fn add(&self, c: &Point, d: &Point) -> Point {
assert!(self.is_on_curve(c), "Point is not in curve");
assert!(self.is_on_curve(d), "Point is not in curve");
assert!(*c != *d, "Points should not be the same");
match (c, d) {
(Point::Identity, _) => d.clone(),
(_, Point::Identity) => c.clone(),
(Point::Coor(x1, y1), Point::Coor(x2, y2)) => {
let y1plusy2 = FiniteField::add(&y1, &y2, &self.p);
if x1 == x2 && y1plusy2 == BigUint::from(0u32) {
return Point::Identity;
}
let numerator = FiniteField::subtract(y2, y1, &self.p);
let denominator = FiniteField::subtract(x2, x1, &self.p);
let s = FiniteField::divide(&numerator, &denominator, &self.p);
let (x3, y3) = self.compute_x3_y3(&x1, &y1, &x2, &s);
Point::Coor(x3, y3)
}
}
}
pub fn double(&self, c: &Point) -> Point {
assert!(self.is_on_curve(c), "Point is not in curve");
match c {
Point::Identity => Point::Identity,
Point::Coor(x1, y1) => {
let numerator = x1.modpow(&BigUint::from(2u32), &self.p);
let numerator = FiniteField::mult(&BigUint::from(3u32), &numerator, &self.p);
let numerator = FiniteField::add(&self.a, &numerator, &self.p);
let denominator = FiniteField::mult(&BigUint::from(2u32), y1, &self.p);
let s = FiniteField::divide(&numerator, &denominator, &self.p);
let (x3, y3) = self.compute_x3_y3(&x1, &y1, &x1, &s);
Point::Coor(x3, y3)
}
}
}
fn compute_x3_y3(
&self,
x1: &BigUint,
y1: &BigUint,
x2: &BigUint,
s: &BigUint,
) -> (BigUint, BigUint) {
let s2 = s.modpow(&BigUint::from(2u32), &self.p);
let s2minusx1 = FiniteField::subtract(&s2, x1, &self.p);
let x3 = FiniteField::subtract(&s2minusx1, x2, &self.p);
let x1minusx3 = FiniteField::subtract(x1, &x3, &self.p);
let sx1minusx3 = FiniteField::mult(&s, &x1minusx3, &self.p);
let y3 = FiniteField::subtract(&sx1minusx3, &y1, &self.p);
(x3, y3)
}
pub fn scalar_mul(_c: &Point, _d: &BigUint) -> Point {
todo!()
}
pub fn is_on_curve(&self, c: &Point) -> bool {
match c {
Point::Coor(x, y) => {
let y2 = y.modpow(&BigUint::from(2u32), &self.p);
let x3 = x.modpow(&BigUint::from(3u32), &self.p);
let ax = FiniteField::mult(&self.a, x, &self.p);
let x3plusax = FiniteField::add(&x3, &ax, &self.p);
y2 == FiniteField::add(&x3plusax, &self.b, &self.p)
}
Point::Identity => true,
}
}
}
pub struct FiniteField {}
impl FiniteField {
pub fn add(c: &BigUint, d: &BigUint, p: &BigUint) -> BigUint {
let r = c + d;
r.modpow(&BigUint::from(1u32), p)
}
pub fn mult(c: &BigUint, d: &BigUint, p: &BigUint) -> BigUint {
let r = c * d;
r.modpow(&BigUint::from(1u32), p)
}
pub fn inv_addition(c: &BigUint, p: &BigUint) -> BigUint {
assert!(c < p, "c >= p");
p - c
}
pub fn subtract(c: &BigUint, d: &BigUint, p: &BigUint) -> BigUint {
let d_inv = FiniteField::inv_addition(d, p);
FiniteField::add(c, &d_inv, p)
}
pub fn inv_multiplication(c: &BigUint, p: &BigUint) -> BigUint {
c.modpow(&(p - BigUint::from(2u32)), p)
}
pub fn divide(c: &BigUint, d: &BigUint, p: &BigUint) -> BigUint {
let d_inv = FiniteField::inv_multiplication(d, p);
FiniteField::mult(c, &d_inv, p)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_add_1() {
let c = BigUint::from(4u32);
let d = BigUint::from(10u32);
let p = BigUint::from(11u32);
let r = FiniteField::add(&c, &d, &p);
assert_eq!(r, BigUint::from(3u32));
}
#[test]
fn test_add_2() {
let c = BigUint::from(4u32);
let d = BigUint::from(10u32);
let p = BigUint::from(31u32);
let r = FiniteField::add(&c, &d, &p);
assert_eq!(r, BigUint::from(14u32));
}
#[test]
fn test_mul_1() {
let c = BigUint::from(4u32);
let d = BigUint::from(10u32);
let p = BigUint::from(11u32);
let r = FiniteField::mult(&c, &d, &p);
assert_eq!(r, BigUint::from(7u32));
}
#[test]
fn test_mul_2() {
let c = BigUint::from(4u32);
let d = BigUint::from(10u32);
let p = BigUint::from(51u32);
let r = FiniteField::mult(&c, &d, &p);
assert_eq!(r, BigUint::from(40u32));
}
#[test]
fn test_inv_addition_1() {
let c = BigUint::from(4u32);
let p = BigUint::from(51u32);
let r = FiniteField::inv_addition(&c, &p);
assert_eq!(r, BigUint::from(47u32));
}
#[test]
#[should_panic]
fn test_inv_addition_2() {
let c = BigUint::from(52u32);
let p = BigUint::from(51u32);
FiniteField::inv_addition(&c, &p);
}
#[test]
fn test_inv_addition_identity() {
let c = BigUint::from(4u32);
let p = BigUint::from(51u32);
let c_inv = FiniteField::inv_addition(&c, &p);
assert_eq!(c_inv, BigUint::from(47u32));
assert_eq!(FiniteField::add(&c, &c_inv, &p), BigUint::from(0u32));
}
#[test]
fn test_substract() {
let c = BigUint::from(4u32);
let p = BigUint::from(51u32);
assert_eq!(FiniteField::subtract(&c, &c, &p), BigUint::from(0u32));
}
#[test]
fn test_inv_multiplication_identity() {
let c = BigUint::from(4u32);
let p = BigUint::from(11u32);
let c_inv = FiniteField::inv_multiplication(&c, &p);
assert_eq!(c_inv, BigUint::from(3u32));
assert_eq!(FiniteField::mult(&c, &c_inv, &p), BigUint::from(1u32));
}
#[test]
fn test_divide() {
let c = BigUint::from(4u32);
let p = BigUint::from(11u32);
assert_eq!(FiniteField::divide(&c, &c, &p), BigUint::from(1u32));
}
#[test]
fn test_ec_point_addition() {
let ec = EllipticCurve {
a: BigUint::from(2u32),
b: BigUint::from(2u32),
p: BigUint::from(17u32),
};
let p1 = Point::Coor(BigUint::from(6u32), BigUint::from(3u32));
let p2 = Point::Coor(BigUint::from(5u32), BigUint::from(1u32));
let pr = Point::Coor(BigUint::from(10u32), BigUint::from(6u32));
let res = ec.add(&p1, &p2);
assert_eq!(res, pr);
let res = ec.add(&p2, &p1);
assert_eq!(res, pr);
}
#[test]
fn test_ec_point_addition_identity() {
let ec = EllipticCurve {
a: BigUint::from(2u32),
b: BigUint::from(2u32),
p: BigUint::from(17u32),
};
let p1 = Point::Coor(BigUint::from(6u32), BigUint::from(3u32));
let p2 = Point::Identity;
let pr = p1.clone();
let res = ec.add(&p1, &p2);
assert_eq!(res, pr);
let res = ec.add(&p2, &p1);
assert_eq!(res, pr);
}
#[test]
fn test_ec_point_addition_reflected_in_x() {
let ec = EllipticCurve {
a: BigUint::from(2u32),
b: BigUint::from(2u32),
p: BigUint::from(17u32),
};
let p1 = Point::Coor(BigUint::from(5u32), BigUint::from(16u32));
let p2 = Point::Coor(BigUint::from(5u32), BigUint::from(1u32));
let pr = Point::Identity;
let res = ec.add(&p1, &p2);
assert_eq!(res, pr);
let res = ec.add(&p2, &p1);
assert_eq!(res, pr);
}
#[test]
fn test_ec_point_doubling() {
let ec = EllipticCurve {
a: BigUint::from(2u32),
b: BigUint::from(2u32),
p: BigUint::from(17u32),
};
let p1 = Point::Coor(BigUint::from(5u32), BigUint::from(1u32));
let pr = Point::Coor(BigUint::from(6u32), BigUint::from(3u32));
let res = ec.double(&p1);
assert_eq!(res, pr);
}
#[test]
fn test_ec_point_doubling_identity() {
let ec = EllipticCurve {
a: BigUint::from(2u32),
b: BigUint::from(2u32),
p: BigUint::from(17u32),
};
let p1 = Point::Identity;
let pr = Point::Identity;
let res = ec.double(&p1);
assert_eq!(res, pr);
}
}