use super::{FieldElement, Scalar};
use crate::internal::subtle::{Choice, ConstantTimeEq, CtOption};
const G_X: [u64; 4] = [
0x79BE_667E_F9DC_BBAC,
0x55A0_6295_CE87_0B07,
0x029B_FCDB_2DCE_28D9,
0x59F2_815B_16F8_1798,
];
const G_Y: [u64; 4] = [
0x483A_DA77_26A3_C465,
0x5DA4_FBFC_0E11_08A8,
0xFD17_B448_A685_5419,
0x9C47_D08F_FB10_D4B8,
];
const CURVE_B: FieldElement = FieldElement::from_limbs([
0x0000_0000_0000_0007,
0x0000_0000_0000_0000,
0x0000_0000_0000_0000,
0x0000_0000_0000_0000,
]);
#[derive(Clone, Copy, Debug)]
pub struct AffinePoint {
pub(crate) x: FieldElement,
pub(crate) y: FieldElement,
infinity: bool,
}
#[derive(Clone, Copy, Debug)]
pub struct ProjectivePoint {
x: FieldElement,
y: FieldElement,
z: FieldElement,
}
#[derive(Clone, Debug)]
pub struct EncodedPoint {
bytes: Vec<u8>,
}
impl AffinePoint {
pub const IDENTITY: Self = Self {
x: FieldElement::ZERO,
y: FieldElement::ZERO,
infinity: true,
};
pub const GENERATOR: Self = Self {
x: FieldElement::from_limbs(G_X),
y: FieldElement::from_limbs(G_Y),
infinity: false,
};
pub fn from_coordinates(x: FieldElement, y: FieldElement) -> CtOption<Self> {
let point = Self {
x,
y,
infinity: false,
};
let y2 = y.mul(&y);
let x3 = x.mul(&x).mul(&x);
let rhs = x3.add(&CURVE_B);
let on_curve = y2.ct_eq(&rhs);
CtOption::new(point, on_curve)
}
pub fn is_identity(&self) -> Choice {
Choice::from_bool(self.infinity)
}
pub fn to_projective(&self) -> ProjectivePoint {
if self.infinity {
ProjectivePoint::IDENTITY
} else {
ProjectivePoint {
x: self.x,
y: self.y,
z: FieldElement::ONE,
}
}
}
pub fn from_encoded_point(encoded: &EncodedPoint) -> CtOption<Self> {
let bytes = encoded.as_bytes();
if bytes.is_empty() {
return CtOption::new(Self::IDENTITY, Choice::from_bool(true));
}
match bytes[0] {
0x00 => CtOption::new(Self::IDENTITY, Choice::from_bool(bytes.len() == 1)),
0x02 | 0x03 => {
if bytes.len() != 33 {
return CtOption::new(Self::IDENTITY, Choice::from_bool(false));
}
let mut x_bytes = [0u8; 32];
x_bytes.copy_from_slice(&bytes[1..33]);
let x = FieldElement::from_bytes(&x_bytes);
let x3 = x.mul(&x).mul(&x);
let y2 = x3.add(&CURVE_B);
let y = y2.sqrt();
let y_bytes = y.to_bytes();
let is_odd = (y_bytes[31] & 1) == 1;
let expected_odd = bytes[0] == 0x03;
let y_final = if is_odd == expected_odd { y } else { y.neg() };
CtOption::new(
AffinePoint {
x,
y: y_final,
infinity: false,
},
Choice::from_bool(true),
)
}
0x04 => {
if bytes.len() != 65 {
return CtOption::new(Self::IDENTITY, Choice::from_bool(false));
}
let mut x_bytes = [0u8; 32];
let mut y_bytes = [0u8; 32];
x_bytes.copy_from_slice(&bytes[1..33]);
y_bytes.copy_from_slice(&bytes[33..65]);
let x = FieldElement::from_bytes(&x_bytes);
let y = FieldElement::from_bytes(&y_bytes);
Self::from_coordinates(x, y)
}
_ => CtOption::new(Self::IDENTITY, Choice::from_bool(false)),
}
}
pub fn to_encoded_point(&self, compress: bool) -> EncodedPoint {
if self.infinity {
return EncodedPoint { bytes: vec![0x00] };
}
if compress {
let x_bytes = self.x.to_bytes();
let y_bytes = self.y.to_bytes();
let is_odd = (y_bytes[31] & 1) == 1;
let prefix = if is_odd { 0x03 } else { 0x02 };
let mut bytes = Vec::with_capacity(33);
bytes.push(prefix);
bytes.extend_from_slice(&x_bytes);
EncodedPoint { bytes }
} else {
let x_bytes = self.x.to_bytes();
let y_bytes = self.y.to_bytes();
let mut bytes = Vec::with_capacity(65);
bytes.push(0x04);
bytes.extend_from_slice(&x_bytes);
bytes.extend_from_slice(&y_bytes);
EncodedPoint { bytes }
}
}
}
impl ProjectivePoint {
pub const IDENTITY: Self = Self {
x: FieldElement::ONE,
y: FieldElement::ONE,
z: FieldElement::ZERO,
};
pub const GENERATOR: Self = Self {
x: FieldElement::from_limbs(G_X),
y: FieldElement::from_limbs(G_Y),
z: FieldElement::ONE,
};
pub fn is_identity(&self) -> Choice {
self.z.ct_eq(&FieldElement::ZERO)
}
pub fn to_affine(&self) -> AffinePoint {
if self.is_identity().is_true() {
return AffinePoint::IDENTITY;
}
let z_inv = self.z.invert();
let z_inv2 = z_inv.mul(&z_inv);
let z_inv3 = z_inv2.mul(&z_inv);
let x = self.x.mul(&z_inv2);
let y = self.y.mul(&z_inv3);
AffinePoint {
x,
y,
infinity: false,
}
}
pub fn add(&self, other: &Self) -> Self {
if other.is_identity().is_true() {
return *self;
}
if self.is_identity().is_true() {
return *other;
}
let z1z1 = self.z.mul(&self.z);
let z2z2 = other.z.mul(&other.z);
let u1 = self.x.mul(&z2z2);
let u2 = other.x.mul(&z1z1);
let s1 = self.y.mul(&other.z).mul(&z2z2);
let s2 = other.y.mul(&self.z).mul(&z1z1);
let h = u2.sub(&u1);
let i = h.mul(&h).mul(&FieldElement::from_limbs([4, 0, 0, 0]));
let j = h.mul(&i);
let r = s2.sub(&s1).mul(&FieldElement::from_limbs([2, 0, 0, 0]));
let v = u1.mul(&i);
let x3 = r
.mul(&r)
.sub(&j)
.sub(&v.mul(&FieldElement::from_limbs([2, 0, 0, 0])));
let y3 = r
.mul(&v.sub(&x3))
.sub(&s1.mul(&j).mul(&FieldElement::from_limbs([2, 0, 0, 0])));
let z3 = self.z.add(&other.z).mul(&h).sub(&z1z1).sub(&z2z2).mul(&h);
Self {
x: x3,
y: y3,
z: z3,
}
}
pub fn double(&self) -> Self {
if self.is_identity().is_true() || self.y.ct_eq(&FieldElement::ZERO).is_true() {
return Self::IDENTITY;
}
let xx = self.x.mul(&self.x);
let yy = self.y.mul(&self.y);
let yyyy = yy.mul(&yy);
let s = self
.x
.add(&yy)
.mul(&self.x.add(&yy))
.sub(&xx)
.sub(&yyyy)
.mul(&FieldElement::from_limbs([2, 0, 0, 0]));
let m = xx.mul(&FieldElement::from_limbs([3, 0, 0, 0]));
let x3 = m
.mul(&m)
.sub(&s.mul(&FieldElement::from_limbs([2, 0, 0, 0])));
let y3 = m
.mul(&s.sub(&x3))
.sub(&yyyy.mul(&FieldElement::from_limbs([8, 0, 0, 0])));
let z3 = self
.y
.mul(&self.z)
.mul(&FieldElement::from_limbs([2, 0, 0, 0]));
Self {
x: x3,
y: y3,
z: z3,
}
}
pub fn mul(&self, scalar: &Scalar) -> Self {
let mut result = Self::IDENTITY;
let addend = *self;
let scalar_bytes = scalar.to_bytes();
for byte in scalar_bytes.iter() {
for i in 0..8 {
result = result.double();
if ((byte >> (7 - i)) & 1) == 1 {
result = result.add(&addend);
}
}
}
result
}
pub fn to_encoded_point(&self, compress: bool) -> EncodedPoint {
self.to_affine().to_encoded_point(compress)
}
}
impl From<AffinePoint> for ProjectivePoint {
fn from(affine: AffinePoint) -> Self {
affine.to_projective()
}
}
impl From<ProjectivePoint> for AffinePoint {
fn from(projective: ProjectivePoint) -> Self {
projective.to_affine()
}
}
impl ConstantTimeEq for AffinePoint {
fn ct_eq(&self, other: &Self) -> Choice {
let both_identity = self.is_identity() & other.is_identity();
let both_valid = (!self.is_identity()) & (!other.is_identity());
let coords_eq = self.x.ct_eq(&other.x) & self.y.ct_eq(&other.y);
both_identity | (both_valid & coords_eq)
}
}
impl ConstantTimeEq for ProjectivePoint {
fn ct_eq(&self, other: &Self) -> Choice {
self.to_affine().ct_eq(&other.to_affine())
}
}
impl core::ops::Mul<Scalar> for &ProjectivePoint {
type Output = ProjectivePoint;
fn mul(self, rhs: Scalar) -> Self::Output {
self.mul(&rhs)
}
}
impl core::ops::Mul<&Scalar> for &ProjectivePoint {
type Output = ProjectivePoint;
fn mul(self, rhs: &Scalar) -> Self::Output {
self.mul(rhs)
}
}
impl EncodedPoint {
pub fn from_bytes(bytes: &[u8]) -> CtOption<Self> {
if bytes.is_empty() {
return CtOption::new(Self { bytes: vec![] }, Choice::from_bool(false));
}
let valid = match bytes[0] {
0x00 => bytes.len() == 1,
0x02 | 0x03 => bytes.len() == 33,
0x04 => bytes.len() == 65,
_ => false,
};
CtOption::new(
Self {
bytes: bytes.to_vec(),
},
Choice::from_bool(valid),
)
}
pub fn as_bytes(&self) -> &[u8] {
&self.bytes
}
}