use core::fmt;
use fp::field_ops::{FieldOps, FieldRandom};
use crate::curve_ops::Curve;
use crate::point_edwards::EdwardsPoint;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EdwardsCurve<F: FieldOps> {
pub d1: F,
pub d2: F,
}
impl<F> fmt::Display for EdwardsCurve<F>
where
F: FieldOps + fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if F::characteristic()[0] != 2 {
if f.alternate() {
write!(
f,
"EdwardsCurve {{\n x^2 + y^2 = 1 + d x^2 y^2\n d = {}\n}}",
self.d2
)
} else {
write!(f, "x^2 + y^2 = 1 + ({})x^2y^2", self.d2)
}
} else {
if f.alternate() {
write!(
f,
"EdwardsCurve {{\n d1(x+y) + d2(x^2+y^2) = xy + xy(x+y) + x^2y^2\n d1 = {}\n d2 = {}\n}}",
self.d1, self.d2
)
} else {
write!(
f,
"{}(x+y) + {}(x^2+y^2) = xy + xy(x+y) + x^2y^2",
self.d1, self.d2
)
}
}
}
}
impl<F: FieldOps + FieldRandom> EdwardsCurve<F> {
pub fn new(d: F) -> Self {
assert!(F::characteristic()[0] != 2, "use new_binary() for char 2");
assert!(d != F::zero(), "d must be nonzero");
assert!(d != F::one(), "d must not be 1");
Self {
d1: F::zero(),
d2: d,
}
}
pub fn new_binary(d1: F, d2: F) -> Self {
assert_eq!(F::characteristic()[0], 2, "use new() for odd char");
assert!(d1 != F::zero(), "d1 must be nonzero");
let d1_sq = <F as FieldOps>::square(&d1);
assert!(d2 != d1_sq + d1, "d2 must differ from d1² + d1");
Self { d1, d2 }
}
pub fn d(&self) -> F {
self.d2
}
pub fn contains(&self, x: &F, y: &F) -> bool {
if F::characteristic()[0] != 2 {
let x2 = <F as FieldOps>::square(x);
let y2 = <F as FieldOps>::square(y);
x2 + y2 == F::one() + self.d2 * x2 * y2
} else {
let x2 = <F as FieldOps>::square(x);
let y2 = <F as FieldOps>::square(y);
let xy = *x * *y;
let xpy = *x + *y;
self.d1 * xpy + self.d2 * (x2 + y2) == xy + xy * xpy + x2 * y2
}
}
pub fn random_point(&self, rng: &mut (impl rand::CryptoRng + rand::Rng)) -> EdwardsPoint<F> {
assert!(
F::characteristic()[0] != 2,
"random_point currently implemented only for odd characteristic"
);
loop {
let x = F::random(rng);
let x2 = <F as FieldOps>::square(&x);
let denom = F::one() - self.d2 * x2;
if bool::from(denom.is_zero()) {
continue;
}
let rhs = (F::one() - x2) * denom.invert().unwrap();
if let Some(y) = rhs.sqrt().into_option() {
let p = EdwardsPoint::new(x, y);
debug_assert!(self.is_on_curve(&p));
return p;
}
}
}
}
impl<F: FieldOps + FieldRandom> Curve for EdwardsCurve<F> {
type BaseField = F;
type Point = EdwardsPoint<F>;
fn is_on_curve(&self, point: &Self::Point) -> bool {
self.contains(&point.x, &point.y)
}
fn random_point(&self, rng: &mut (impl rand::CryptoRng + rand::Rng)) -> Self::Point {
EdwardsCurve::random_point(self, rng)
}
fn j_invariant(&self) -> F {
if F::characteristic()[0] != 2 {
let d = self.d2;
let d2 = <F as FieldOps>::square(&d);
let two = <F as FieldOps>::double(&F::one());
let four = <F as FieldOps>::double(&two);
let eight = <F as FieldOps>::double(&four);
let fourteen = eight + four + two;
let sixteen = <F as FieldOps>::double(&eight);
let inner = F::one() + fourteen * d + d2;
let inner_cubed = inner * <F as FieldOps>::square(&inner);
let numer = sixteen * inner_cubed;
let one_minus_d = F::one() - d;
let omd2 = <F as FieldOps>::square(&one_minus_d);
let omd4 = <F as FieldOps>::square(&omd2);
let denom = d * omd4;
numer
* denom
.invert()
.into_option()
.expect("d(1-d)^4 must be invertible")
} else {
let d1_sq = <F as FieldOps>::square(&self.d1);
let d1_4 = <F as FieldOps>::square(&d1_sq);
let d2_sq = <F as FieldOps>::square(&self.d2);
let denom = d1_4 * (d1_4 + d1_sq + d2_sq);
denom
.invert()
.into_option()
.expect("j-invariant denominator must be invertible")
}
}
fn a_invariants(&self) -> Vec<Self::BaseField> {
if F::characteristic()[0] != 2 {
vec![self.d2]
} else {
vec![self.d1, self.d2]
}
}
}