use crate::finite_fields::FiniteField;
use num_bigint::BigUint;
#[derive(PartialEq, Clone, Debug)]
pub enum Point {
Coor(BigUint, BigUint),
Identity,
}
#[derive(PartialEq, Debug)]
pub enum EllipticCurveError {
InvalidPoint(Point),
InvalidScalar(BigUint),
}
#[derive(PartialEq, Clone, Debug)]
pub struct EllipticCurve {
pub a: BigUint,
pub b: BigUint,
pub p: BigUint,
}
impl EllipticCurve {
pub fn add(&self, a: &Point, b: &Point) -> Result<Point, EllipticCurveError> {
if !self.is_on_curve(a) {
return Err(EllipticCurveError::InvalidPoint(a.clone()));
}
if !self.is_on_curve(b) {
return Err(EllipticCurveError::InvalidPoint(b.clone()));
}
if *a == *b {
return self.double(a);
}
match (a, b) {
(Point::Identity, _) => Ok(b.clone()),
(_, Point::Identity) => Ok(a.clone()),
(Point::Coor(x1, y1), Point::Coor(x2, y2)) => {
let y1plusy2 = FiniteField::add(y1, y2, &self.p).unwrap();
if x1 == x2 && y1plusy2 == BigUint::from(0u32) {
return Ok(Point::Identity);
}
let numerator = FiniteField::subtract(y2, y1, &self.p).unwrap();
let denominator = FiniteField::subtract(x2, x1, &self.p).unwrap();
let s = FiniteField::divide(&numerator, &denominator, &self.p).unwrap();
let (x3, y3) = self.compute_x3_y3(x1, y1, x2, &s);
Ok(Point::Coor(x3, y3))
}
}
}
pub fn double(&self, a: &Point) -> Result<Point, EllipticCurveError> {
if !self.is_on_curve(a) {
return Err(EllipticCurveError::InvalidPoint(a.clone()));
}
match a {
Point::Identity => Ok(Point::Identity),
Point::Coor(x1, y1) => {
if *y1 == BigUint::from(0u32) {
return Ok(Point::Identity);
}
let numerator = x1.modpow(&BigUint::from(2u32), &self.p);
let numerator =
FiniteField::mult(&BigUint::from(3u32), &numerator, &self.p).unwrap();
let numerator = FiniteField::add(&self.a, &numerator, &self.p).unwrap();
let denominator = FiniteField::mult(&BigUint::from(2u32), y1, &self.p).unwrap();
let s = FiniteField::divide(&numerator, &denominator, &self.p).unwrap();
let (x3, y3) = self.compute_x3_y3(x1, y1, x1, &s);
Ok(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 x3 = FiniteField::subtract(&s2, x1, &self.p).unwrap();
let x3 = FiniteField::subtract(&x3, x2, &self.p).unwrap();
let y3 = FiniteField::subtract(x1, &x3, &self.p).unwrap();
let y3 = FiniteField::mult(s, &y3, &self.p).unwrap();
let y3 = FiniteField::subtract(&y3, y1, &self.p).unwrap();
(x3, y3)
}
pub fn scalar_mul(&self, a: &Point, d: &BigUint) -> Result<Point, EllipticCurveError> {
if *d == BigUint::from(0u32) {
return Err(EllipticCurveError::InvalidScalar(d.clone()));
}
let mut t = a.clone();
for i in (0..(d.bits() - 1)).rev() {
t = self.double(&t)?;
if d.bit(i) {
t = self.add(&t, a)?;
}
}
Ok(t)
}
pub fn is_on_curve(&self, a: &Point) -> bool {
match a {
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).unwrap();
let x3plusax = FiniteField::add(&x3, &ax, &self.p).unwrap();
y2 == FiniteField::add(&x3plusax, &self.b, &self.p).unwrap()
}
Point::Identity => true,
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_point_in_curve() {
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 p3 = Point::Coor(BigUint::from(10u32), BigUint::from(6u32));
assert!(ec.is_on_curve(&p1));
assert!(ec.is_on_curve(&p2));
assert!(ec.is_on_curve(&p3));
let p4 = Point::Coor(BigUint::from(4u32), BigUint::from(1u32));
let p5 = Point::Coor(BigUint::from(1u32), BigUint::from(1u32));
let p6 = Point::Coor(BigUint::from(0u32), BigUint::from(1u32));
assert!(!ec.is_on_curve(&p4));
assert!(!ec.is_on_curve(&p5));
assert!(!ec.is_on_curve(&p6));
}
#[test]
fn test_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 = Ok(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);
let p1 = Point::Coor(BigUint::from(5u32), BigUint::from(1u32));
let p2 = Point::Coor(BigUint::from(5u32), BigUint::from(1u32));
let pr = Ok(Point::Coor(BigUint::from(6u32), BigUint::from(3u32)));
let res = ec.add(&p1, &p2);
assert_eq!(res, pr);
let p1 = Point::Coor(BigUint::from(10u32), BigUint::from(6u32));
let p2 = Point::Coor(BigUint::from(5u32), BigUint::from(1u32));
let pr = Ok(Point::Coor(BigUint::from(3u32), BigUint::from(1u32)));
let res = ec.add(&p1, &p2);
assert_eq!(res, pr);
let p1 = Point::Coor(BigUint::from(16u32), BigUint::from(13u32));
let p2 = Point::Coor(BigUint::from(5u32), BigUint::from(1u32));
let pr = Ok(Point::Coor(BigUint::from(0u32), BigUint::from(6u32)));
let res = ec.add(&p1, &p2);
assert_eq!(res, pr);
let p1 = Point::Coor(BigUint::from(6u32), BigUint::from(3u32));
let res = ec.add(&p1, &Point::Identity);
assert_eq!(res, Ok(p1.clone()));
let res = ec.add(&Point::Identity, &p1);
assert_eq!(res, Ok(p1.clone()));
let res = ec.add(&Point::Identity, &Point::Identity);
assert_eq!(res, Ok(Point::Identity));
let p1 = Point::Coor(BigUint::from(5u32), BigUint::from(16u32));
let p2 = Point::Coor(BigUint::from(5u32), BigUint::from(1u32));
let res = ec.add(&p1, &p2);
assert_eq!(res, Ok(Point::Identity));
let res = ec.add(&p2, &p1);
assert_eq!(res, Ok(Point::Identity));
}
#[test]
fn test_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 = Ok(Point::Coor(BigUint::from(6u32), BigUint::from(3u32)));
let res = ec.double(&p1);
assert_eq!(res, pr);
let res = ec.double(&Point::Identity);
assert_eq!(res, Ok(Point::Identity));
}
#[test]
fn test_scalar_multiplication() {
let ec = EllipticCurve {
a: BigUint::from(2u32),
b: BigUint::from(2u32),
p: BigUint::from(17u32),
};
let a = Point::Coor(BigUint::from(5u32), BigUint::from(1u32));
let pr = Ok(Point::Coor(BigUint::from(6u32), BigUint::from(3u32)));
let res = ec.scalar_mul(&a, &BigUint::from(2u32));
assert_eq!(res, pr);
let pr = Ok(Point::Coor(BigUint::from(7u32), BigUint::from(11u32)));
let res = ec.scalar_mul(&a, &BigUint::from(10u32));
assert_eq!(res, pr);
let pr = Ok(Point::Coor(BigUint::from(3u32), BigUint::from(16u32)));
let res = ec.scalar_mul(&a, &BigUint::from(15u32));
assert_eq!(res, pr);
let pr = Ok(Point::Coor(BigUint::from(10u32), BigUint::from(11u32)));
let res = ec.scalar_mul(&a, &BigUint::from(16u32));
assert_eq!(res, pr);
let pr = Ok(Point::Coor(BigUint::from(6u32), BigUint::from(14u32)));
let res = ec.scalar_mul(&a, &BigUint::from(17u32));
assert_eq!(res, pr);
let pr = Ok(Point::Coor(BigUint::from(5u32), BigUint::from(16u32)));
let res = ec.scalar_mul(&a, &BigUint::from(18u32));
assert_eq!(res, pr);
let pr = Ok(Point::Identity);
let res = ec.scalar_mul(&a, &BigUint::from(19u32));
assert_eq!(res, pr);
let p1 = Point::Coor(BigUint::from(10u32), BigUint::from(6u32));
let pr = Ok(Point::Coor(BigUint::from(16u32), BigUint::from(13u32)));
let res = ec.double(&p1);
assert_eq!(res, pr);
}
#[test]
fn test_ec_secp256k1() {
let p = BigUint::parse_bytes(
b"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F",
16,
)
.expect("could not convert p");
let n = BigUint::parse_bytes(
b"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141",
16,
)
.expect("could not convert n");
let gx = BigUint::parse_bytes(
b"79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798",
16,
)
.expect("could not convert gx");
let gy = BigUint::parse_bytes(
b"483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8",
16,
)
.expect("could not convert gy");
let ec = EllipticCurve {
a: BigUint::from(0u32),
b: BigUint::from(7u32),
p,
};
let g = Point::Coor(gx, gy);
let res = ec.scalar_mul(&g, &n); assert_eq!(res, Ok(Point::Identity));
let p = ec.scalar_mul(&g, &BigUint::from(1201u32)).unwrap();
let res = ec.scalar_mul(&p, &n); assert_eq!(res, Ok(Point::Identity));
}
}