//! Projective curve points. #![allow(clippy::needless_range_loop, clippy::op_ref)] use crate::{point_arithmetic::PointArithmetic, AffinePoint, Field, PrimeCurveParams}; use core::{ borrow::Borrow, iter::Sum, ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}, }; use elliptic_curve::{ bigint::{ArrayEncoding, Integer}, generic_array::ArrayLength, group::{ self, cofactor::CofactorGroup, prime::{PrimeCurve, PrimeGroup}, Group, GroupEncoding, }, ops::{LinearCombination, MulByGenerator}, point::Double, rand_core::RngCore, sec1::{ CompressedPoint, EncodedPoint, FromEncodedPoint, ModulusSize, ToEncodedPoint, UncompressedPointSize, }, subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}, zeroize::DefaultIsZeroes, Error, FieldBytes, FieldBytesSize, PublicKey, Result, Scalar, }; /// Point on a Weierstrass curve in projective coordinates. #[derive(Clone, Copy, Debug)] pub struct ProjectivePoint { pub(crate) x: C::FieldElement, pub(crate) y: C::FieldElement, pub(crate) z: C::FieldElement, } impl ProjectivePoint where C: PrimeCurveParams, { /// Additive identity of the group a.k.a. the point at infinity. pub const IDENTITY: Self = Self { x: C::FieldElement::ZERO, y: C::FieldElement::ONE, z: C::FieldElement::ZERO, }; /// Base point of the curve. pub const GENERATOR: Self = Self { x: C::GENERATOR.0, y: C::GENERATOR.1, z: C::FieldElement::ONE, }; /// Returns the affine representation of this point, or `None` if it is the identity. pub fn to_affine(&self) -> AffinePoint { self.z .invert() .map(|zinv| AffinePoint { x: self.x * &zinv, y: self.y * &zinv, infinity: 0, }) .unwrap_or(AffinePoint::IDENTITY) } /// Returns `-self`. pub fn neg(&self) -> Self { Self { x: self.x, y: -self.y, z: self.z, } } /// Returns `self + other`. pub fn add(&self, other: &Self) -> Self { C::PointArithmetic::add(self, other) } /// Returns `self + other`. fn add_mixed(&self, other: &AffinePoint) -> Self { C::PointArithmetic::add_mixed(self, other) } /// Returns `self - other`. pub fn sub(&self, other: &Self) -> Self { self.add(&other.neg()) } /// Returns `self - other`. fn sub_mixed(&self, other: &AffinePoint) -> Self { self.add_mixed(&other.neg()) } /// Returns `[k] self`. fn mul(&self, k: &Scalar) -> Self where Self: Double, { let k = Into::::into(*k).to_le_byte_array(); let mut pc = [Self::default(); 16]; pc[0] = Self::IDENTITY; pc[1] = *self; for i in 2..16 { pc[i] = if i % 2 == 0 { Double::double(&pc[i / 2]) } else { pc[i - 1].add(self) }; } let mut q = Self::IDENTITY; let mut pos = C::Uint::BITS - 4; loop { let slot = (k[pos >> 3] >> (pos & 7)) & 0xf; let mut t = ProjectivePoint::IDENTITY; for i in 1..16 { t.conditional_assign( &pc[i], Choice::from(((slot as usize ^ i).wrapping_sub(1) >> 8) as u8 & 1), ); } q = q.add(&t); if pos == 0 { break; } q = Double::double(&Double::double(&Double::double(&Double::double(&q)))); pos -= 4; } q } } impl CofactorGroup for ProjectivePoint where Self: Double, C: PrimeCurveParams, FieldBytes: Copy, FieldBytesSize: ModulusSize, CompressedPoint: Copy, as ArrayLength>::ArrayType: Copy, { type Subgroup = Self; fn clear_cofactor(&self) -> Self::Subgroup { *self } fn into_subgroup(self) -> CtOption { CtOption::new(self, 1.into()) } fn is_torsion_free(&self) -> Choice { 1.into() } } impl ConditionallySelectable for ProjectivePoint where C: PrimeCurveParams, { fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { Self { x: C::FieldElement::conditional_select(&a.x, &b.x, choice), y: C::FieldElement::conditional_select(&a.y, &b.y, choice), z: C::FieldElement::conditional_select(&a.z, &b.z, choice), } } } impl ConstantTimeEq for ProjectivePoint where C: PrimeCurveParams, { fn ct_eq(&self, other: &Self) -> Choice { self.to_affine().ct_eq(&other.to_affine()) } } impl Default for ProjectivePoint where C: PrimeCurveParams, { fn default() -> Self { Self::IDENTITY } } impl DefaultIsZeroes for ProjectivePoint where C: PrimeCurveParams {} impl Double for ProjectivePoint { fn double(&self) -> Self { C::PointArithmetic::double(self) } } impl Eq for ProjectivePoint where C: PrimeCurveParams {} impl From> for ProjectivePoint where C: PrimeCurveParams, { fn from(p: AffinePoint) -> Self { let projective = ProjectivePoint { x: p.x, y: p.y, z: C::FieldElement::ONE, }; Self::conditional_select(&projective, &Self::IDENTITY, p.is_identity()) } } impl From<&AffinePoint> for ProjectivePoint where C: PrimeCurveParams, { fn from(p: &AffinePoint) -> Self { Self::from(*p) } } impl From> for ProjectivePoint where C: PrimeCurveParams, { fn from(public_key: PublicKey) -> ProjectivePoint { AffinePoint::from(public_key).into() } } impl From<&PublicKey> for ProjectivePoint where C: PrimeCurveParams, { fn from(public_key: &PublicKey) -> ProjectivePoint { AffinePoint::::from(public_key).into() } } impl FromEncodedPoint for ProjectivePoint where C: PrimeCurveParams, FieldBytes: Copy, FieldBytesSize: ModulusSize, CompressedPoint: Copy, { fn from_encoded_point(p: &EncodedPoint) -> CtOption { AffinePoint::::from_encoded_point(p).map(Self::from) } } impl Group for ProjectivePoint where Self: Double, C: PrimeCurveParams, { type Scalar = Scalar; fn random(mut rng: impl RngCore) -> Self { Self::GENERATOR * as Field>::random(&mut rng) } fn identity() -> Self { Self::IDENTITY } fn generator() -> Self { Self::GENERATOR } fn is_identity(&self) -> Choice { self.ct_eq(&Self::IDENTITY) } #[must_use] fn double(&self) -> Self { Double::double(self) } } impl GroupEncoding for ProjectivePoint where C: PrimeCurveParams, FieldBytes: Copy, FieldBytesSize: ModulusSize, CompressedPoint: Copy, as ArrayLength>::ArrayType: Copy, { type Repr = CompressedPoint; fn from_bytes(bytes: &Self::Repr) -> CtOption { as GroupEncoding>::from_bytes(bytes).map(Into::into) } fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { // No unchecked conversion possible for compressed points Self::from_bytes(bytes) } fn to_bytes(&self) -> Self::Repr { self.to_affine().to_bytes() } } impl group::Curve for ProjectivePoint where Self: Double, C: PrimeCurveParams, { type AffineRepr = AffinePoint; fn to_affine(&self) -> AffinePoint { ProjectivePoint::to_affine(self) } } impl LinearCombination for ProjectivePoint where Self: Double, C: PrimeCurveParams, { } impl MulByGenerator for ProjectivePoint where Self: Double, C: PrimeCurveParams, { fn mul_by_generator(scalar: &Self::Scalar) -> Self { // TODO(tarcieri): precomputed basepoint tables Self::generator() * scalar } } impl PrimeGroup for ProjectivePoint where Self: Double, C: PrimeCurveParams, FieldBytes: Copy, FieldBytesSize: ModulusSize, CompressedPoint: Copy, as ArrayLength>::ArrayType: Copy, { } impl PrimeCurve for ProjectivePoint where Self: Double, C: PrimeCurveParams, FieldBytes: Copy, FieldBytesSize: ModulusSize, CompressedPoint: Copy, as ArrayLength>::ArrayType: Copy, { type Affine = AffinePoint; } impl PartialEq for ProjectivePoint where C: PrimeCurveParams, { fn eq(&self, other: &Self) -> bool { self.ct_eq(other).into() } } impl ToEncodedPoint for ProjectivePoint where C: PrimeCurveParams, FieldBytesSize: ModulusSize, CompressedPoint: Copy, as ArrayLength>::ArrayType: Copy, { fn to_encoded_point(&self, compress: bool) -> EncodedPoint { self.to_affine().to_encoded_point(compress) } } impl TryFrom> for PublicKey where C: PrimeCurveParams, { type Error = Error; fn try_from(point: ProjectivePoint) -> Result> { AffinePoint::::from(point).try_into() } } impl TryFrom<&ProjectivePoint> for PublicKey where C: PrimeCurveParams, { type Error = Error; fn try_from(point: &ProjectivePoint) -> Result> { AffinePoint::::from(point).try_into() } } // // Arithmetic trait impls // impl Add> for ProjectivePoint where C: PrimeCurveParams, { type Output = ProjectivePoint; fn add(self, other: ProjectivePoint) -> ProjectivePoint { ProjectivePoint::add(&self, &other) } } impl Add<&ProjectivePoint> for &ProjectivePoint where C: PrimeCurveParams, { type Output = ProjectivePoint; fn add(self, other: &ProjectivePoint) -> ProjectivePoint { ProjectivePoint::add(self, other) } } impl Add<&ProjectivePoint> for ProjectivePoint where C: PrimeCurveParams, { type Output = ProjectivePoint; fn add(self, other: &ProjectivePoint) -> ProjectivePoint { ProjectivePoint::add(&self, other) } } impl AddAssign> for ProjectivePoint where C: PrimeCurveParams, { fn add_assign(&mut self, rhs: ProjectivePoint) { *self = ProjectivePoint::add(self, &rhs); } } impl AddAssign<&ProjectivePoint> for ProjectivePoint where C: PrimeCurveParams, { fn add_assign(&mut self, rhs: &ProjectivePoint) { *self = ProjectivePoint::add(self, rhs); } } impl Add> for ProjectivePoint where C: PrimeCurveParams, { type Output = ProjectivePoint; fn add(self, other: AffinePoint) -> ProjectivePoint { ProjectivePoint::add_mixed(&self, &other) } } impl Add<&AffinePoint> for &ProjectivePoint where C: PrimeCurveParams, { type Output = ProjectivePoint; fn add(self, other: &AffinePoint) -> ProjectivePoint { ProjectivePoint::add_mixed(self, other) } } impl Add<&AffinePoint> for ProjectivePoint where C: PrimeCurveParams, { type Output = ProjectivePoint; fn add(self, other: &AffinePoint) -> ProjectivePoint { ProjectivePoint::add_mixed(&self, other) } } impl AddAssign> for ProjectivePoint where C: PrimeCurveParams, { fn add_assign(&mut self, rhs: AffinePoint) { *self = ProjectivePoint::add_mixed(self, &rhs); } } impl AddAssign<&AffinePoint> for ProjectivePoint where C: PrimeCurveParams, { fn add_assign(&mut self, rhs: &AffinePoint) { *self = ProjectivePoint::add_mixed(self, rhs); } } impl Sum for ProjectivePoint where C: PrimeCurveParams, { fn sum>(iter: I) -> Self { iter.fold(ProjectivePoint::IDENTITY, |a, b| a + b) } } impl<'a, C> Sum<&'a ProjectivePoint> for ProjectivePoint where C: PrimeCurveParams, { fn sum>>(iter: I) -> Self { iter.cloned().sum() } } impl Sub> for ProjectivePoint where C: PrimeCurveParams, { type Output = ProjectivePoint; fn sub(self, other: ProjectivePoint) -> ProjectivePoint { ProjectivePoint::sub(&self, &other) } } impl Sub<&ProjectivePoint> for &ProjectivePoint where C: PrimeCurveParams, { type Output = ProjectivePoint; fn sub(self, other: &ProjectivePoint) -> ProjectivePoint { ProjectivePoint::sub(self, other) } } impl Sub<&ProjectivePoint> for ProjectivePoint where C: PrimeCurveParams, { type Output = ProjectivePoint; fn sub(self, other: &ProjectivePoint) -> ProjectivePoint { ProjectivePoint::sub(&self, other) } } impl SubAssign> for ProjectivePoint where C: PrimeCurveParams, { fn sub_assign(&mut self, rhs: ProjectivePoint) { *self = ProjectivePoint::sub(self, &rhs); } } impl SubAssign<&ProjectivePoint> for ProjectivePoint where C: PrimeCurveParams, { fn sub_assign(&mut self, rhs: &ProjectivePoint) { *self = ProjectivePoint::sub(self, rhs); } } impl Sub> for ProjectivePoint where C: PrimeCurveParams, { type Output = ProjectivePoint; fn sub(self, other: AffinePoint) -> ProjectivePoint { ProjectivePoint::sub_mixed(&self, &other) } } impl Sub<&AffinePoint> for &ProjectivePoint where C: PrimeCurveParams, { type Output = ProjectivePoint; fn sub(self, other: &AffinePoint) -> ProjectivePoint { ProjectivePoint::sub_mixed(self, other) } } impl Sub<&AffinePoint> for ProjectivePoint where C: PrimeCurveParams, { type Output = ProjectivePoint; fn sub(self, other: &AffinePoint) -> ProjectivePoint { ProjectivePoint::sub_mixed(&self, other) } } impl SubAssign> for ProjectivePoint where C: PrimeCurveParams, { fn sub_assign(&mut self, rhs: AffinePoint) { *self = ProjectivePoint::sub_mixed(self, &rhs); } } impl SubAssign<&AffinePoint> for ProjectivePoint where C: PrimeCurveParams, { fn sub_assign(&mut self, rhs: &AffinePoint) { *self = ProjectivePoint::sub_mixed(self, rhs); } } impl Mul for ProjectivePoint where Self: Double, C: PrimeCurveParams, S: Borrow>, { type Output = Self; fn mul(self, scalar: S) -> Self { ProjectivePoint::mul(&self, scalar.borrow()) } } impl Mul<&Scalar> for &ProjectivePoint where C: PrimeCurveParams, ProjectivePoint: Double, { type Output = ProjectivePoint; fn mul(self, scalar: &Scalar) -> ProjectivePoint { ProjectivePoint::mul(self, scalar) } } impl MulAssign for ProjectivePoint where Self: Double, C: PrimeCurveParams, S: Borrow>, { fn mul_assign(&mut self, scalar: S) { *self = ProjectivePoint::mul(self, scalar.borrow()); } } impl Neg for ProjectivePoint where C: PrimeCurveParams, { type Output = ProjectivePoint; fn neg(self) -> ProjectivePoint { ProjectivePoint::neg(&self) } } impl<'a, C> Neg for &'a ProjectivePoint where C: PrimeCurveParams, { type Output = ProjectivePoint; fn neg(self) -> ProjectivePoint { ProjectivePoint::neg(self) } }