use core::fmt;
use fp::field_ops::{FieldOps, FieldRandom};
use crate::curve_ops::Curve;
use crate::point_montgomery::KummerPoint;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MontgomeryCurve<F: FieldOps> {
pub a: F,
pub b: F,
}
impl<F> fmt::Display for MontgomeryCurve<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,
"MontgomeryCurve {{\n By^2 = x(x^2 + Ax + 1)\n A = {}\n B = {}\n}}",
self.a, self.b
)
} else {
write!(
f,
"By^2 = x(x^2 + Ax + 1) over (A={}, B={})",
self.a, self.b
)
}
} else {
if f.alternate() {
write!(
f,
"MontgomeryCurve {{\n y^2 + xy = x(x^2 + Ax + B^2)\n A = {}\n B = {}\n}}",
self.a, self.b
)
} else {
write!(
f,
"y^2 + xy = x(x^2 + Ax + B^2) over (A={}, B={})",
self.a, self.b
)
}
}
}
}
impl<F: FieldOps + Copy> MontgomeryCurve<F> {
pub fn new(a: F, b: F) -> Self {
assert!(Self::is_smooth(&a, &b));
Self { a, b }
}
pub fn is_smooth(a: &F, b: &F) -> bool {
if F::characteristic()[0] != 2 {
let two = F::one().double();
!bool::from(b.is_zero()) && *a != two && *a != two.negate()
} else {
!bool::from(b.is_zero())
}
}
pub fn a_invariants(&self) -> [F; 2] {
[self.a.clone(), self.b.clone()]
}
pub fn a24(&self) -> F {
assert_ne!(F::characteristic()[0], 2);
let two = <F as FieldOps>::double(&F::one());
let four = <F as FieldOps>::double(&two);
let four_inv = <F as FieldOps>::invert(&four).unwrap();
(self.a + two) * four_inv
}
}
impl<F: FieldOps + Copy + FieldRandom> MontgomeryCurve<F> {
pub fn random_point(
&self,
rng: &mut (impl rand::CryptoRng + rand::Rng),
) -> KummerPoint<F> {
loop {
let x = F::random(rng);
let p = KummerPoint::from_x(x);
if self.is_on_curve(&p) {
return p;
}
}
}
}
impl<F: FieldOps + Copy+ FieldRandom> Curve for MontgomeryCurve<F> {
type BaseField = F;
type Point = KummerPoint<F>;
fn is_on_curve(&self, point: &Self::Point) -> bool {
if point.is_identity() {
true
} else {
let z_inv = <F as FieldOps>::invert(&point.z);
if bool::from(z_inv.is_none()) {
return true;
}
let x = point.x * z_inv.unwrap();
if F::characteristic()[0] != 2 {
let xsq = <F as FieldOps>::square(&x);
let xcubed = x * xsq;
let axsq = self.a * xsq;
let binv = <F as FieldOps>::invert(&self.b).unwrap();
let sum = xcubed + axsq + x;
<F as FieldOps>::legendre(&(sum * binv)) > -1i8
} else {
if bool::from(x.is_zero()) {
true
} else {
let bsq = <F as FieldOps>::square(&self.b);
let xinv = x.invert().unwrap();
let rhs = x + self.a + bsq * xinv;
bool::from(rhs.trace().is_zero())
}
}
}
}
fn random_point(&self, rng: &mut (impl rand::CryptoRng + rand::Rng)) -> Self::Point {
MontgomeryCurve::random_point(self, rng)
}
fn j_invariant(&self) -> F {
if F::characteristic()[0] != 2 {
let two = <F as FieldOps>::double(&F::one());
let three = two + F::one();
let four = <F as FieldOps>::double(&two);
let sixteen = <F as FieldOps>::square(&four);
let twofivesix = <F as FieldOps>::square(&sixteen);
let asq = <F as FieldOps>::square(&self.a);
let asq_min_three = asq - three;
let asq_min_three_cubed = asq_min_three * <F as FieldOps>::square(&asq_min_three);
let asq_min_four_inv = <F as FieldOps>::invert(&(asq - four))
.into_option()
.expect("a should be different from 2, -2.");
twofivesix * asq_min_three_cubed * asq_min_four_inv
} else {
assert!(!bool::from(self.b.is_zero()));
let bsq = <F as FieldOps>::square(&self.b);
let bfourth = <F as FieldOps>::square(&bsq);
<F as FieldOps>::invert(&bfourth).unwrap()
}
}
fn a_invariants(&self) -> Vec<Self::BaseField> {
MontgomeryCurve::a_invariants(self).to_vec()
}
}