use super::{AffinePoint, CURVE_EQUATION_B_SINGLE, FieldElement};
use core::{
iter::Sum,
ops::{Add, AddAssign, Neg, Sub, SubAssign},
};
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
#[rustfmt::skip]
const ENDOMORPHISM_BETA: FieldElement = FieldElement::from_bytes_unchecked(&[
0x7a, 0xe9, 0x6a, 0x2b, 0x65, 0x7c, 0x07, 0x10,
0x6e, 0x64, 0x47, 0x9e, 0xac, 0x34, 0x34, 0xe9,
0x9c, 0xf0, 0x49, 0x75, 0x12, 0xf5, 0x89, 0x95,
0xc1, 0x39, 0x6c, 0x28, 0x71, 0x95, 0x01, 0xee,
]);
#[derive(Clone, Copy, Debug)]
pub struct ProjectivePoint {
pub x: FieldElement,
pub y: FieldElement,
pub z: FieldElement,
}
impl ProjectivePoint {
pub const IDENTITY: Self = Self {
x: FieldElement::ZERO,
y: FieldElement::ONE,
z: FieldElement::ZERO,
};
pub const GENERATOR: Self = Self {
x: AffinePoint::GENERATOR.x,
y: AffinePoint::GENERATOR.y,
z: FieldElement::ONE,
};
#[deprecated(since = "0.10.2", note = "use `ProjectivePoint::IDENTITY` instead")]
pub const fn identity() -> ProjectivePoint {
Self::IDENTITY
}
#[deprecated(since = "0.10.2", note = "use `ProjectivePoint::GENERATOR` instead")]
pub fn generator() -> ProjectivePoint {
Self::GENERATOR
}
pub fn to_affine(&self) -> AffinePoint {
self.z
.invert()
.map(|zinv| AffinePoint::new(self.x * &zinv, self.y * &zinv))
.unwrap_or_else(|| AffinePoint::IDENTITY)
}
fn neg(&self) -> ProjectivePoint {
ProjectivePoint {
x: self.x,
y: self.y.negate(1).normalize_weak(),
z: self.z,
}
}
fn add(&self, other: &ProjectivePoint) -> ProjectivePoint {
let xx = self.x * &other.x;
let yy = self.y * &other.y;
let zz = self.z * &other.z;
let n_xx_yy = (xx + &yy).negate(2);
let n_yy_zz = (yy + &zz).negate(2);
let n_xx_zz = (xx + &zz).negate(2);
let xy_pairs = ((self.x + &self.y) * &(other.x + &other.y)) + &n_xx_yy;
let yz_pairs = ((self.y + &self.z) * &(other.y + &other.z)) + &n_yy_zz;
let xz_pairs = ((self.x + &self.z) * &(other.x + &other.z)) + &n_xx_zz;
let bzz = zz.mul_single(CURVE_EQUATION_B_SINGLE);
let bzz3 = (bzz.double() + &bzz).normalize_weak();
let yy_m_bzz3 = yy + &bzz3.negate(1);
let yy_p_bzz3 = yy + &bzz3;
let byz = &yz_pairs
.mul_single(CURVE_EQUATION_B_SINGLE)
.normalize_weak();
let byz3 = (byz.double() + byz).normalize_weak();
let xx3 = xx.double() + &xx;
let bxx9 = (xx3.double() + &xx3)
.normalize_weak()
.mul_single(CURVE_EQUATION_B_SINGLE)
.normalize_weak();
let new_x = ((xy_pairs * &yy_m_bzz3) + &(byz3 * &xz_pairs).negate(1)).normalize_weak(); let new_y = ((yy_p_bzz3 * &yy_m_bzz3) + &(bxx9 * &xz_pairs)).normalize_weak();
let new_z = ((yz_pairs * &yy_p_bzz3) + &(xx3 * &xy_pairs)).normalize_weak();
ProjectivePoint {
x: new_x,
y: new_y,
z: new_z,
}
}
fn add_mixed(&self, other: &AffinePoint) -> ProjectivePoint {
let xx = self.x * &other.x;
let yy = self.y * &other.y;
let xy_pairs = ((self.x + &self.y) * &(other.x + &other.y)) + &(xx + &yy).negate(2);
let yz_pairs = (other.y * &self.z) + &self.y;
let xz_pairs = (other.x * &self.z) + &self.x;
let bzz = &self.z.mul_single(CURVE_EQUATION_B_SINGLE);
let bzz3 = (bzz.double() + bzz).normalize_weak();
let yy_m_bzz3 = yy + &bzz3.negate(1);
let yy_p_bzz3 = yy + &bzz3;
let byz = &yz_pairs
.mul_single(CURVE_EQUATION_B_SINGLE)
.normalize_weak();
let byz3 = (byz.double() + byz).normalize_weak();
let xx3 = xx.double() + &xx;
let bxx9 = &(xx3.double() + &xx3)
.normalize_weak()
.mul_single(CURVE_EQUATION_B_SINGLE)
.normalize_weak();
let mut ret = ProjectivePoint {
x: ((xy_pairs * &yy_m_bzz3) + &(byz3 * &xz_pairs).negate(1)).normalize_weak(),
y: ((yy_p_bzz3 * &yy_m_bzz3) + &(bxx9 * &xz_pairs)).normalize_weak(),
z: ((yz_pairs * &yy_p_bzz3) + &(xx3 * &xy_pairs)).normalize_weak(),
};
ret.conditional_assign(self, other.is_identity());
ret
}
#[inline]
pub fn double(&self) -> ProjectivePoint {
let yy = self.y.square();
let zz = self.z.square();
let xy2 = (self.x * &self.y).double();
let bzz = &zz.mul_single(CURVE_EQUATION_B_SINGLE);
let bzz3 = (bzz.double() + bzz).normalize_weak();
let bzz9 = (bzz3.double() + &bzz3).normalize_weak();
let yy_m_bzz9 = yy + &bzz9.negate(1);
let yy_p_bzz3 = yy + &bzz3;
let yy_zz = yy * &zz;
let yy_zz8 = yy_zz.double().double().double();
let t = (yy_zz8.double() + &yy_zz8)
.normalize_weak()
.mul_single(CURVE_EQUATION_B_SINGLE);
ProjectivePoint {
x: xy2 * &yy_m_bzz9,
y: ((yy_m_bzz9 * &yy_p_bzz3) + &t).normalize_weak(),
z: ((yy * &self.y) * &self.z)
.double()
.double()
.double()
.normalize_weak(),
}
}
fn sub(&self, other: &ProjectivePoint) -> ProjectivePoint {
self.add(&other.neg())
}
fn sub_mixed(&self, other: &AffinePoint) -> ProjectivePoint {
self.add_mixed(&other.neg())
}
pub fn endomorphism(&self) -> Self {
Self {
x: self.x * &ENDOMORPHISM_BETA,
y: self.y,
z: self.z,
}
}
pub fn eq_affine(&self, other: &AffinePoint) -> Choice {
let both_identity = self.is_identity() & other.is_identity();
let rhs_identity = other.is_identity();
let rhs_x = &other.x * &self.z;
let x_eq = rhs_x.negate(1).add(&self.x).normalizes_to_zero();
let rhs_y = &other.y * &self.z;
let y_eq = rhs_y.negate(1).add(&self.y).normalizes_to_zero();
both_identity | (!rhs_identity & x_eq & y_eq)
}
}
impl From<AffinePoint> for ProjectivePoint {
fn from(p: AffinePoint) -> Self {
let projective = ProjectivePoint {
x: p.x,
y: p.y,
z: FieldElement::ONE,
};
Self::conditional_select(&projective, &Self::IDENTITY, p.is_identity())
}
}
impl From<&AffinePoint> for ProjectivePoint {
fn from(p: &AffinePoint) -> Self {
Self::from(*p)
}
}
impl From<ProjectivePoint> for AffinePoint {
fn from(p: ProjectivePoint) -> AffinePoint {
p.to_affine()
}
}
impl From<&ProjectivePoint> for AffinePoint {
fn from(p: &ProjectivePoint) -> AffinePoint {
p.to_affine()
}
}
impl ConditionallySelectable for ProjectivePoint {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
ProjectivePoint {
x: FieldElement::conditional_select(&a.x, &b.x, choice),
y: FieldElement::conditional_select(&a.y, &b.y, choice),
z: FieldElement::conditional_select(&a.z, &b.z, choice),
}
}
}
impl ConstantTimeEq for ProjectivePoint {
fn ct_eq(&self, other: &Self) -> Choice {
let lhs_x = self.x * &other.z;
let rhs_x = other.x * &self.z;
let x_eq = rhs_x.negate(1).add(&lhs_x).normalizes_to_zero();
let lhs_y = self.y * &other.z;
let rhs_y = other.y * &self.z;
let y_eq = rhs_y.negate(1).add(&lhs_y).normalizes_to_zero();
x_eq & y_eq
}
}
impl PartialEq for ProjectivePoint {
fn eq(&self, other: &Self) -> bool {
self.ct_eq(other).into()
}
}
impl PartialEq<AffinePoint> for ProjectivePoint {
fn eq(&self, other: &AffinePoint) -> bool {
self.eq_affine(other).into()
}
}
impl PartialEq<ProjectivePoint> for AffinePoint {
fn eq(&self, other: &ProjectivePoint) -> bool {
other.eq_affine(self).into()
}
}
impl Eq for ProjectivePoint {}
impl ProjectivePoint {
pub fn is_identity(&self) -> Choice {
self.z.normalizes_to_zero()
}
}
impl Default for ProjectivePoint {
fn default() -> Self {
Self::IDENTITY
}
}
impl Add<&ProjectivePoint> for &ProjectivePoint {
type Output = ProjectivePoint;
fn add(self, other: &ProjectivePoint) -> ProjectivePoint {
ProjectivePoint::add(self, other)
}
}
impl Add<ProjectivePoint> for ProjectivePoint {
type Output = ProjectivePoint;
fn add(self, other: ProjectivePoint) -> ProjectivePoint {
ProjectivePoint::add(&self, &other)
}
}
impl Add<&ProjectivePoint> for ProjectivePoint {
type Output = ProjectivePoint;
fn add(self, other: &ProjectivePoint) -> ProjectivePoint {
ProjectivePoint::add(&self, other)
}
}
impl AddAssign<ProjectivePoint> for ProjectivePoint {
fn add_assign(&mut self, rhs: ProjectivePoint) {
*self = ProjectivePoint::add(self, &rhs);
}
}
impl AddAssign<&ProjectivePoint> for ProjectivePoint {
fn add_assign(&mut self, rhs: &ProjectivePoint) {
*self = ProjectivePoint::add(self, rhs);
}
}
impl Add<AffinePoint> for ProjectivePoint {
type Output = ProjectivePoint;
fn add(self, other: AffinePoint) -> ProjectivePoint {
ProjectivePoint::add_mixed(&self, &other)
}
}
impl Add<&AffinePoint> for &ProjectivePoint {
type Output = ProjectivePoint;
fn add(self, other: &AffinePoint) -> ProjectivePoint {
ProjectivePoint::add_mixed(self, other)
}
}
impl Add<&AffinePoint> for ProjectivePoint {
type Output = ProjectivePoint;
fn add(self, other: &AffinePoint) -> ProjectivePoint {
ProjectivePoint::add_mixed(&self, other)
}
}
impl AddAssign<AffinePoint> for ProjectivePoint {
fn add_assign(&mut self, rhs: AffinePoint) {
*self = ProjectivePoint::add_mixed(self, &rhs);
}
}
impl AddAssign<&AffinePoint> for ProjectivePoint {
fn add_assign(&mut self, rhs: &AffinePoint) {
*self = ProjectivePoint::add_mixed(self, rhs);
}
}
impl Sum for ProjectivePoint {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(ProjectivePoint::IDENTITY, |a, b| a + b)
}
}
impl<'a> Sum<&'a ProjectivePoint> for ProjectivePoint {
fn sum<I: Iterator<Item = &'a ProjectivePoint>>(iter: I) -> Self {
iter.cloned().sum()
}
}
impl Sub<ProjectivePoint> for ProjectivePoint {
type Output = ProjectivePoint;
fn sub(self, other: ProjectivePoint) -> ProjectivePoint {
ProjectivePoint::sub(&self, &other)
}
}
impl Sub<&ProjectivePoint> for &ProjectivePoint {
type Output = ProjectivePoint;
fn sub(self, other: &ProjectivePoint) -> ProjectivePoint {
ProjectivePoint::sub(self, other)
}
}
impl Sub<&ProjectivePoint> for ProjectivePoint {
type Output = ProjectivePoint;
fn sub(self, other: &ProjectivePoint) -> ProjectivePoint {
ProjectivePoint::sub(&self, other)
}
}
impl SubAssign<ProjectivePoint> for ProjectivePoint {
fn sub_assign(&mut self, rhs: ProjectivePoint) {
*self = ProjectivePoint::sub(self, &rhs);
}
}
impl SubAssign<&ProjectivePoint> for ProjectivePoint {
fn sub_assign(&mut self, rhs: &ProjectivePoint) {
*self = ProjectivePoint::sub(self, rhs);
}
}
impl Sub<AffinePoint> for ProjectivePoint {
type Output = ProjectivePoint;
fn sub(self, other: AffinePoint) -> ProjectivePoint {
ProjectivePoint::sub_mixed(&self, &other)
}
}
impl Sub<&AffinePoint> for &ProjectivePoint {
type Output = ProjectivePoint;
fn sub(self, other: &AffinePoint) -> ProjectivePoint {
ProjectivePoint::sub_mixed(self, other)
}
}
impl Sub<&AffinePoint> for ProjectivePoint {
type Output = ProjectivePoint;
fn sub(self, other: &AffinePoint) -> ProjectivePoint {
ProjectivePoint::sub_mixed(&self, other)
}
}
impl SubAssign<AffinePoint> for ProjectivePoint {
fn sub_assign(&mut self, rhs: AffinePoint) {
*self = ProjectivePoint::sub_mixed(self, &rhs);
}
}
impl SubAssign<&AffinePoint> for ProjectivePoint {
fn sub_assign(&mut self, rhs: &AffinePoint) {
*self = ProjectivePoint::sub_mixed(self, rhs);
}
}
impl Neg for ProjectivePoint {
type Output = ProjectivePoint;
fn neg(self) -> ProjectivePoint {
ProjectivePoint::neg(&self)
}
}
impl<'a> Neg for &'a ProjectivePoint {
type Output = ProjectivePoint;
fn neg(self) -> ProjectivePoint {
ProjectivePoint::neg(self)
}
}
impl AsRef<ProjectivePoint> for ProjectivePoint {
fn as_ref(&self) -> &ProjectivePoint {
self
}
}