use core::fmt;
use crate::curve_weierstrass::WeierstrassCurve;
use crate::point_ops::PointOps;
use fp::field_ops::FieldOps;
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
#[derive(Debug, Clone, Copy)]
pub struct AffinePoint<F: FieldOps> {
pub x: F,
pub y: F,
pub infinity: bool,
}
impl<F: FieldOps> PartialEq for AffinePoint<F>
where
F: FieldOps + ConstantTimeEq,
{
fn eq(&self, other: &Self) -> bool {
match (self.infinity, other.infinity) {
(true, true) => true,
(true, false) => false,
(false, true) => false,
(false, false) => self.x == other.x && self.y == other.y,
}
}
}
impl<F> fmt::Display for AffinePoint<F>
where
F: FieldOps + fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.infinity {
if f.alternate() {
write!(f, "AffinePoint {{ O }}")
} else {
write!(f, "O")
}
} else if f.alternate() {
write!(f, "AffinePoint {{ x = {}, y = {} }}", self.x, self.y)
} else {
write!(f, "({}, {})", self.x, self.y)
}
}
}
impl<F: FieldOps> Eq for AffinePoint<F>
where
F: FieldOps + ConstantTimeEq
{ }
impl<F: FieldOps> AffinePoint<F> {
pub fn new(x: F, y: F) -> Self {
Self {
x,
y,
infinity: false,
}
}
pub fn identity() -> Self {
Self {
x: F::zero(),
y: F::zero(),
infinity: true,
}
}
pub fn is_identity(&self) -> bool {
self.infinity
}
}
impl<F> ConditionallySelectable for AffinePoint<F>
where
F: FieldOps + Copy,
{
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
let ai = a.infinity as u8;
let bi = b.infinity as u8;
let infinity = u8::conditional_select(&ai, &bi, choice) != 0;
Self {
x: F::conditional_select(&a.x, &b.x, choice),
y: F::conditional_select(&a.y, &b.y, choice),
infinity,
}
}
fn conditional_assign(&mut self, other: &Self, choice: Choice) {
F::conditional_assign(&mut self.x, &other.x, choice);
F::conditional_assign(&mut self.y, &other.y, choice);
let mut inf = self.infinity as u8;
let other_inf = other.infinity as u8;
inf.conditional_assign(&other_inf, choice);
self.infinity = inf != 0;
}
fn conditional_swap(a: &mut Self, b: &mut Self, choice: Choice) {
F::conditional_swap(&mut a.x, &mut b.x, choice);
F::conditional_swap(&mut a.y, &mut b.y, choice);
let mut ai = a.infinity as u8;
let mut bi = b.infinity as u8;
u8::conditional_swap(&mut ai, &mut bi, choice);
a.infinity = ai != 0;
b.infinity = bi != 0;
}
}
impl<F> ConstantTimeEq for AffinePoint<F>
where
F: FieldOps + Copy + ConstantTimeEq,
{
fn ct_eq(&self, other: &Self) -> Choice {
let self_inf = Choice::from(self.infinity as u8);
let other_inf = Choice::from(other.infinity as u8);
let both_inf = self_inf & other_inf;
let both_finite = !self_inf & !other_inf;
let coords_eq = self.x.ct_eq(&other.x) & self.y.ct_eq(&other.y);
both_inf | (both_finite & coords_eq)
}
fn ct_ne(&self, other: &Self) -> Choice {
!self.ct_eq(other)
}
}
impl<F: FieldOps> AffinePoint<F> {
pub fn negate(&self, curve: &WeierstrassCurve<F>) -> Self {
if self.infinity {
return Self::identity();
}
let neg_y = -self.y - curve.a1 * self.x - curve.a3;
Self::new(self.x.clone(), neg_y)
}
pub fn double(&self, curve: &WeierstrassCurve<F>) -> Self {
if self.infinity {
return Self::identity();
}
let denom = <F as FieldOps>::double(&self.y) + curve.a1 * self.x + curve.a3;
let denom_inv = match denom.invert().into_option() {
Some(inv) => inv,
None => return Self::identity(),
};
let numer = {
let x1_sq = <F as FieldOps>::square(&self.x);
let three_x1_sq = x1_sq + <F as FieldOps>::double(&x1_sq);
let two_a2_x1 = <F as FieldOps>::double(&(curve.a2 * self.x));
let a1y1 = curve.a1 * self.y;
three_x1_sq + two_a2_x1 + curve.a4 - a1y1
};
let lambda = numer * denom_inv;
let x3 = {
let lam_sq = <F as FieldOps>::square(&lambda);
let a1_lam = curve.a1 * lambda;
let two_x1 = <F as FieldOps>::double(&self.x);
lam_sq + a1_lam - curve.a2 - two_x1
};
let y3 = {
let dx = self.x - x3;
let lam_dx = lambda * dx;
let a1x3 = curve.a1 * x3;
lam_dx - self.y - a1x3 - curve.a3
};
Self::new(x3, y3)
}
pub fn add(&self, other: &Self, curve: &WeierstrassCurve<F>) -> Self {
if self.infinity {
return other.clone();
}
if other.infinity {
return self.clone();
}
if self.x == other.x {
if self.y == other.y {
return self.double(curve);
}
return Self::identity();
}
let dx = other.x - self.x;
let dy = other.y - self.y;
let dx_inv = dx
.invert()
.into_option()
.expect("dx must be invertible (x₁ ≠ x₂)");
let lambda = dy * dx_inv;
let x3 = {
let lam_sq = <F as FieldOps>::square(&lambda);
let a1_lam = curve.a1 * lambda;
lam_sq + a1_lam - curve.a2 - self.x - other.x
};
let y3 = {
let dx3 = self.x - x3;
let lam_dx3 = lambda * dx3;
let a1x3 = curve.a1 * x3;
lam_dx3 - self.y - a1x3 - curve.a3
};
Self::new(x3, y3)
}
pub fn scalar_mul(&self, k: &[u64], curve: &<AffinePoint<F> as PointOps>::Curve) -> Self {
let mut r0 = Self::identity();
let mut r1 = self.clone();
for &limb in k.iter().rev() {
for bit in (0..64).rev() {
let choice = Choice::from(((limb >> bit) & 1) as u8);
Self::conditional_swap(&mut r0, &mut r1, choice);
let sum = r0.add(&r1, curve);
let dbl = r0.double(curve);
r1 = sum;
r0 = dbl;
Self::conditional_swap(&mut r0, &mut r1, choice);
}
}
r0
}
}
impl<F> PointOps for AffinePoint<F>
where
F: FieldOps,
{
type BaseField = F;
type Curve = WeierstrassCurve<F>;
fn identity(_curve: &Self::Curve) -> Self {
AffinePoint::<F>::identity()
}
fn is_identity(&self) -> bool {
self.infinity
}
fn negate(&self, curve: &Self::Curve) -> Self {
AffinePoint::<F>::negate(self, curve)
}
fn scalar_mul(&self, k: &[u64], curve: &Self::Curve) -> Self {
AffinePoint::<F>::scalar_mul(self, k, curve)
}
}
impl<F> crate::point_ops::PointAdd for AffinePoint<F>
where
F: FieldOps,
{
fn add(&self, other: &Self, curve: &Self::Curve) -> Self {
AffinePoint::<F>::add(self, other, curve)
}
}