use crate::finite_field::FieldElement;
use super::{super::EllipticCurve, AffineInfinity, ProjectivePoint};
#[derive(Clone, Copy, Eq, PartialEq)]
pub struct AffinePoint<C: EllipticCurve> {
x: FieldElement<4, C>,
y: FieldElement<4, C>,
}
impl<C: EllipticCurve> core::fmt::Display for AffinePoint<C> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("(")?;
core::fmt::Display::fmt(&self.x, f)?;
f.write_str(", ")?;
core::fmt::Display::fmt(&self.y, f)?;
f.write_str(")")?;
Ok(())
}
}
impl<C: EllipticCurve> core::fmt::Debug for AffinePoint<C> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(concat!(stringify!(AffinePoint), " { x: "))?;
core::fmt::Debug::fmt(&self.x, f)?;
f.write_str(", y: ")?;
core::fmt::Debug::fmt(&self.y, f)?;
f.write_str(" }")?;
Ok(())
}
}
impl<C: EllipticCurve> AffinePoint<C> {
pub fn is_on_curve(x: &FieldElement<4, C>, y: &FieldElement<4, C>) -> bool {
let mut x_cube_ax_b = x.sqr();
x_cube_ax_b.mul_assign(x);
x_cube_ax_b.add_assign(&x.mul(&C::A));
x_cube_ax_b.add_assign(&C::B);
y.sqr() == x_cube_ax_b
}
pub fn new(x: FieldElement<4, C>, y: FieldElement<4, C>) -> Option<Self> {
match Self::is_on_curve(&x, &y) {
true => Some(Self { x, y }),
false => None,
}
}
pub fn x(&self) -> FieldElement<4, C> {
self.x
}
pub fn x_ref(&self) -> &FieldElement<4, C> {
&self.x
}
pub fn y(&self) -> FieldElement<4, C> {
self.y
}
pub fn y_ref(&self) -> &FieldElement<4, C> {
&self.y
}
pub const fn as_projective(self) -> ProjectivePoint<C> {
unsafe { ProjectivePoint::new_unchecked(self.x, self.y, FieldElement::ONE) }
}
pub const unsafe fn new_unchecked(x: FieldElement<4, C>, y: FieldElement<4, C>) -> Self {
Self { x, y }
}
pub fn add(&self, rhs: &Self) -> Self {
let slope = rhs.y.sub(&self.y).div(&rhs.x.sub(&self.x));
self.third_point_on_line(rhs, &slope)
}
pub fn neg(&self) -> Self {
Self {
x: self.x,
y: self.y.neg(),
}
}
pub fn neg_assign(&mut self) {
self.y.neg_assign();
}
pub fn double(&self) -> Self {
let slope = {
let mut slope = self.x.sqr();
slope.mul_digit_assign(3);
slope.add_assign(&C::A);
let tmp = self.y.double();
slope.div(&tmp)
};
self.third_point_on_line(self, &slope)
}
pub fn double_assign(&mut self) {
*self = self.double();
}
fn third_point_on_line(&self, other: &Self, slope: &FieldElement<4, C>) -> Self {
let mut x = slope.sqr();
x.sub_assign(&self.x);
x.sub_assign(&other.x);
let mut y = slope.mul(&self.x.sub(&x));
y.sub_assign(&self.y);
Self { x, y }
}
}
impl<C: EllipticCurve> TryFrom<ProjectivePoint<C>> for AffinePoint<C> {
type Error = AffineInfinity;
fn try_from(value: ProjectivePoint<C>) -> Result<AffinePoint<C>, Self::Error> {
value.as_affine().ok_or(AffineInfinity)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::big_int::UBigInt;
use crate::ec::Secp256r1;
#[test]
fn is_on_curve() {
let base_point = Secp256r1::BASE_POINT
.as_affine()
.expect("BP is valid affine point");
assert!(AffinePoint::is_on_curve(
base_point.x_ref(),
base_point.y_ref()
));
let x = unsafe {
FieldElement::<4, Secp256r1>::new_unchecked(UBigInt([
0xa60b48fc47669978,
0xc08969e277f21b35,
0x8a52380304b51ac3,
0x7cf27b188d034f7e,
]))
};
let y = unsafe {
FieldElement::new_unchecked(UBigInt([
0x9e04b79d227873d1,
0xba7dade63ce98229,
0x293d9ac69f7430db,
0x07775510db8ed040,
]))
};
assert!(AffinePoint::is_on_curve(&x, &y));
}
#[test]
fn add() {
let x = unsafe {
FieldElement::<4, Secp256r1>::new_unchecked(UBigInt([
0xa60b48fc47669978,
0xc08969e277f21b35,
0x8a52380304b51ac3,
0x7cf27b188d034f7e,
]))
};
let y = unsafe {
FieldElement::new_unchecked(UBigInt([
0x9e04b79d227873d1,
0xba7dade63ce98229,
0x293d9ac69f7430db,
0x07775510db8ed040,
]))
};
let k_2 = unsafe { AffinePoint::new_unchecked(x, y) };
let x = unsafe {
FieldElement::new_unchecked(UBigInt([
0xfb41661bc6e7fd6c,
0xe6c6b721efada985,
0xc8f7ef951d4bf165,
0x5ecbe4d1a6330a44,
]))
};
let y = unsafe {
FieldElement::new_unchecked(UBigInt([
0x9a79b127a27d5032,
0xd82ab036384fb83d,
0x374b06ce1a64a2ec,
0x8734640c4998ff7e,
]))
};
let k_3 = unsafe { AffinePoint::new_unchecked(x, y) };
let sum = k_2.add(&Secp256r1::BASE_POINT.as_affine().unwrap());
assert_eq!(sum, k_3);
let also_sum = Secp256r1::BASE_POINT.as_affine().unwrap().add(&k_2);
assert_eq!(also_sum, k_3);
}
#[test]
fn double() {
let x = unsafe {
FieldElement::<4, Secp256r1>::new_unchecked(UBigInt([
0xa60b48fc47669978,
0xc08969e277f21b35,
0x8a52380304b51ac3,
0x7cf27b188d034f7e,
]))
};
let y = unsafe {
FieldElement::new_unchecked(UBigInt([
0x9e04b79d227873d1,
0xba7dade63ce98229,
0x293d9ac69f7430db,
0x07775510db8ed040,
]))
};
let k_2 = unsafe { AffinePoint::new_unchecked(x, y) };
let sum = Secp256r1::BASE_POINT.as_affine().unwrap().double();
assert_eq!(sum, k_2);
}
}