use crate::constants::TWISTED_D;
use crate::curve::edwards::ExtendedPoint as EdwardsExtendedPoint;
use crate::curve::twedwards::affine::AffinePoint;
use crate::curve::twedwards::extensible::ExtensiblePoint;
use crate::field::FieldElement;
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
#[derive(Copy, Clone, Debug)]
pub struct ExtendedPoint {
pub(crate) X: FieldElement,
pub(crate) Y: FieldElement,
pub(crate) Z: FieldElement,
pub(crate) T: FieldElement,
}
impl ConstantTimeEq for ExtendedPoint {
fn ct_eq(&self, other: &Self) -> Choice {
let XZ = self.X * other.Z;
let ZX = self.Z * other.X;
let YZ = self.Y * other.Z;
let ZY = self.Z * other.Y;
(XZ.ct_eq(&ZX)) & (YZ.ct_eq(&ZY))
}
}
impl ConditionallySelectable for ExtendedPoint {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
ExtendedPoint {
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),
T: FieldElement::conditional_select(&a.T, &b.T, choice),
}
}
}
impl PartialEq for ExtendedPoint {
fn eq(&self, other: &ExtendedPoint) -> bool {
self.ct_eq(other).into()
}
}
impl Eq for ExtendedPoint {}
impl Default for ExtendedPoint {
fn default() -> ExtendedPoint {
ExtendedPoint::identity()
}
}
impl ExtendedPoint {
pub fn identity() -> ExtendedPoint {
ExtendedPoint {
X: FieldElement::zero(),
Y: FieldElement::one(),
Z: FieldElement::one(),
T: FieldElement::zero(),
}
}
pub const fn generator() -> ExtendedPoint {
crate::constants::TWISTED_EDWARDS_BASE_POINT
}
pub(crate) fn double(&self) -> ExtendedPoint {
self.to_extensible().double().to_extended()
}
pub(crate) fn add(&self, other: &ExtendedPoint) -> ExtendedPoint {
self.to_extensible().add_extended(other).to_extended()
}
pub fn to_extensible(&self) -> ExtensiblePoint {
ExtensiblePoint {
X: self.X,
Y: self.Y,
Z: self.Z,
T1: self.T,
T2: FieldElement::one(),
}
}
pub(crate) fn to_affine(&self) -> AffinePoint {
let INV_Z = self.Z.invert();
let mut x = self.X * INV_Z;
x.strong_reduce();
let mut y = self.Y * INV_Z;
y.strong_reduce();
AffinePoint { x, y }
}
fn edwards_isogeny(&self, a: FieldElement) -> EdwardsExtendedPoint {
let affine = self.to_affine();
let x = affine.x;
let y = affine.y;
let xy = x * y;
let x_numerator = xy + xy;
let x_denom = y.square() - (a * x.square());
let new_x = x_numerator * x_denom.invert();
let y_numerator = y.square() + (a * x.square());
let y_denom = (FieldElement::one() + FieldElement::one()) - y.square() - (a * x.square());
let new_y = y_numerator * y_denom.invert();
EdwardsExtendedPoint {
X: new_x,
Y: new_y,
Z: FieldElement::one(),
T: new_x * new_y,
}
}
pub fn to_untwisted(&self) -> EdwardsExtendedPoint {
self.edwards_isogeny(FieldElement::minus_one())
}
pub(crate) fn is_on_curve(&self) -> bool {
let XY = self.X * self.Y;
let ZT = self.Z * self.T;
let YY = self.Y.square();
let XX = self.X.square();
let ZZ = self.Z.square();
let TT = self.T.square();
let lhs = YY - XX;
let rhs = ZZ + TT * TWISTED_D;
(XY == ZT) && (lhs == rhs)
}
pub fn negate(&self) -> ExtendedPoint {
ExtendedPoint {
X: self.X.negate(),
Y: self.Y,
Z: self.Z,
T: self.T.negate(),
}
}
pub fn torque(&self) -> ExtendedPoint {
ExtendedPoint {
X: self.X.negate(),
Y: self.Y.negate(),
Z: self.Z,
T: self.T,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::constants::{GOLDILOCKS_BASE_POINT, TWISTED_EDWARDS_BASE_POINT};
use hex::decode as hex_decode;
fn slice_to_fixed_array(b: &[u8]) -> [u8; 56] {
let mut a: [u8; 56] = [0; 56];
a.copy_from_slice(&b);
a
}
fn hex_to_field(data: &str) -> FieldElement {
let mut bytes = hex_decode(data).unwrap();
bytes.reverse();
FieldElement::from_bytes(&slice_to_fixed_array(&bytes))
}
#[test]
fn test_isogeny() {
let x = hex_to_field("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa955555555555555555555555555555555555555555555555555555555");
let y = hex_to_field("ae05e9634ad7048db359d6205086c2b0036ed7a035884dd7b7e36d728ad8c4b80d6565833a2a3098bbbcb2bed1cda06bdaeafbcdea9386ed");
let a = AffinePoint { x, y }.to_extended();
let twist_a = a.to_untwisted().to_twisted();
assert!(twist_a == a.double().double())
}
#[test]
fn test_is_on_curve() {
assert!(TWISTED_EDWARDS_BASE_POINT.is_on_curve());
let invalid_point = ExtendedPoint {
X: GOLDILOCKS_BASE_POINT.X,
Y: GOLDILOCKS_BASE_POINT.Y,
Z: GOLDILOCKS_BASE_POINT.Z,
T: GOLDILOCKS_BASE_POINT.T,
};
assert!(!invalid_point.is_on_curve());
}
#[test]
fn test_point_add() {
let a = TWISTED_EDWARDS_BASE_POINT;
let b = a.double();
let c_1 = a.to_extensible().add_extended(&b).to_extended();
let c_2 = b.to_extensible().add_extended(&a).to_extended();
assert!(c_1 == c_2);
let c = c_1.to_extensible().add_extended(&ExtendedPoint::identity());
assert!(c.to_extended() == c_1);
}
#[test]
fn test_point_sub() {
let a = TWISTED_EDWARDS_BASE_POINT;
let b = a.double();
let c_1 = a.to_extensible().sub_extended(&b).to_extended();
let c_2 = b.negate().to_extensible().add_extended(&a).to_extended();
assert!(c_1 == c_2);
}
#[test]
fn test_negate() {
let a = TWISTED_EDWARDS_BASE_POINT;
let neg_a = a.negate();
assert!(a.to_extensible().add_extended(&neg_a) == ExtensiblePoint::identity());
}
}