use core::fmt;
use subtle::{Choice, CtOption, ConditionallySelectable, ConstantTimeEq};
use crate::curve_montgomery::MontgomeryCurve;
use crate::point_ops::PointOps;
use fp::field_ops::{FieldOps};
#[derive(Debug, Clone, Copy)]
pub struct KummerPoint<F: FieldOps + Copy> {
pub x: F,
pub z: F,
}
impl<F> fmt::Display for KummerPoint<F>
where
F: FieldOps + Copy + fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_identity() {
if f.alternate() {
write!(f, "KummerPoint {{ O = (1:0) }}")
} else {
write!(f, "O")
}
} else if f.alternate() {
match self.to_x().into_option() {
Some(x_aff) => write!(
f,
"KummerPoint {{ X:Z = ({}:{}), x = {} }}",
self.x, self.z, x_aff
),
None => write!(
f,
"KummerPoint {{ X:Z = ({}:{}) }}",
self.x, self.z
),
}
} else {
write!(f, "({}:{})", self.x, self.z)
}
}
}
impl<F: FieldOps> PartialEq for KummerPoint<F>
where
F: FieldOps + ConstantTimeEq,
{
fn eq(&self, other: &Self) -> bool {
self.x * other.z == other.x * self.z
}
}
impl<F: FieldOps> Eq for KummerPoint<F> where F: FieldOps + ConstantTimeEq {}
impl<F: FieldOps> KummerPoint<F> {
pub fn new(x: F, z: F) -> Self {
assert!(!bool::from((x * z).is_zero()));
Self { x, z }
}
pub fn from_x(x: F) -> Self {
Self { x, z: F::one() }
}
pub fn identity() -> Self {
Self {
x: F::one(),
z: F::zero(),
}
}
pub fn is_identity(&self) -> bool {
bool::from(self.z.is_zero())
}
pub fn to_x(&self) -> CtOption<F> {
let z_inv = self.z.invert();
z_inv.map(|zinv| self.x * zinv)
}
}
impl<F> ConditionallySelectable for KummerPoint<F>
where
F: FieldOps + Copy,
{
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
Self {
x: F::conditional_select(&a.x, &b.x, choice),
z: F::conditional_select(&a.z, &b.z, choice),
}
}
fn conditional_assign(&mut self, other: &Self, choice: Choice) {
F::conditional_assign(&mut self.x, &other.x, choice);
F::conditional_assign(&mut self.z, &other.z, choice);
}
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.z, &mut b.z, choice);
}
}
impl<F> ConstantTimeEq for KummerPoint<F>
where
F: FieldOps + Copy + ConstantTimeEq,
{
fn ct_eq(&self, other: &Self) -> Choice {
let x1z2 = self.x * other.z;
let x2z1 = other.x * self.z;
x1z2.ct_eq(&x2z1)
}
fn ct_ne(&self, other: &Self) -> Choice {
!self.ct_eq(other)
}
}
impl<F: FieldOps + Copy> KummerPoint<F> {
pub fn xdouble(&self, curve: &MontgomeryCurve<F>) -> Self {
if F::characteristic()[0] != 2 {
let sumsq = <F as FieldOps>::square(&(self.x + self.z));
let diffsq = <F as FieldOps>::square(&(self.x - self.z));
let fourxz = sumsq - diffsq;
let a24 = curve.a24();
let new_z = fourxz * (diffsq + a24 * fourxz);
Self {
x: sumsq * diffsq,
z: new_z,
}
} else {
let temp_x = self.x + curve.b * self.z;
let new_x = <F as FieldOps>::square(&<F as FieldOps>::square(&temp_x));
let xz = self.x * self.z;
let new_z = <F as FieldOps>::square(&xz);
Self { x: new_x, z: new_z }
}
}
pub fn xadd(&self, other: &Self, diff: &Self) -> Self {
if F::characteristic()[0] != 2 {
let u = (self.x - self.z) * (other.x + other.z);
let v = (self.x + self.z) * (other.x - other.z);
let new_x = diff.z * <F as FieldOps>::square(&(u + v));
let new_z = diff.x * <F as FieldOps>::square(&(u - v));
Self { x: new_x, z: new_z }
} else {
let x1z2 = self.x * other.z;
let x2z1 = other.x * self.z;
let sum_squared = <F as FieldOps>::square(&(x1z2 + x2z1));
let new_x = diff.x * sum_squared + diff.z * x1z2 * x2z1;
let new_z = diff.z * sum_squared;
Self { x: new_x, z: new_z }
}
}
pub fn scalar_mul(&self, k: &[u64], curve: &MontgomeryCurve<F>) -> Self {
if self.is_identity() || k.is_empty() {
return Self::identity();
}
let mut r0 = Self::identity();
let mut r1 = *self;
let mut swap = Choice::from(0u8);
for &limb in k.iter().rev() {
for bit in (0..64).rev() {
let ki = Choice::from(((limb >> bit) & 1) as u8);
let do_swap = swap ^ ki;
Self::conditional_swap(&mut r0, &mut r1, do_swap);
swap = ki;
let r0_dbl = r0.xdouble(curve);
let r0_plus_r1 = r0.xadd(&r1, self);
r0 = r0_dbl;
r1 = r0_plus_r1;
}
}
Self::conditional_swap(&mut r0, &mut r1, swap);
r0
}
}
impl<F> PointOps for KummerPoint<F>
where
F: FieldOps + Copy,
{
type BaseField = F;
type Curve = MontgomeryCurve<F>;
fn identity(_curve: &Self::Curve) -> Self {
KummerPoint::<F>::identity()
}
fn is_identity(&self) -> bool {
KummerPoint::<F>::is_identity(self)
}
fn negate(&self, _curve: &Self::Curve) -> Self {
*self
}
fn scalar_mul(&self, k: &[u64], curve: &Self::Curve) -> Self {
KummerPoint::<F>::scalar_mul(self, k, curve)
}
}