diff --git a/ec/src/hashing/curve_maps/swu/mod.rs b/ec/src/hashing/curve_maps/swu/mod.rs index 47330c52c..e3a16a729 100644 --- a/ec/src/hashing/curve_maps/swu/mod.rs +++ b/ec/src/hashing/curve_maps/swu/mod.rs @@ -206,6 +206,9 @@ mod test { /// AFFINE_GENERATOR_COEFFS = (G1_GENERATOR_X, G1_GENERATOR_Y) const GENERATOR: Affine = Affine::new_unchecked(MontFp!("62"), MontFp!("70")); + + /// We use `bool` because the point (0, 0) could be on the curve. + type ZeroIndicator = bool; } impl SWUConfig for TestSWUMapToCurveConfig { diff --git a/ec/src/hashing/curve_maps/wb/mod.rs b/ec/src/hashing/curve_maps/wb/mod.rs index 4e2644009..6d32c51b7 100644 --- a/ec/src/hashing/curve_maps/wb/mod.rs +++ b/ec/src/hashing/curve_maps/wb/mod.rs @@ -165,6 +165,9 @@ mod test { /// AFFINE_GENERATOR_COEFFS = (G1_GENERATOR_X, G1_GENERATOR_Y) const GENERATOR: Affine = Affine::new_unchecked(MontFp!("62"), MontFp!("70")); + + /// We use `()` because the point (0, 0) is not on the curve. + type ZeroIndicator = (); } /// Testing WB19 hashing on a small curve @@ -193,11 +196,14 @@ mod test { const COEFF_A: F127 = MontFp!("109"); /// COEFF_B = 124 - #[rustfmt::skip] + #[rustfmt::skip] const COEFF_B: F127 = MontFp!("124"); /// AFFINE_GENERATOR_COEFFS = (G1_GENERATOR_X, G1_GENERATOR_Y) const GENERATOR: Affine = Affine::new_unchecked(MontFp!("84"), MontFp!("2")); + + /// We use `bool because the point (0, 0) could be on the curve. + type ZeroIndicator = bool; } /// SWU parameters for E_isogenous diff --git a/ec/src/models/bn/g1.rs b/ec/src/models/bn/g1.rs index 4a6ea65b8..fdfb3b9e9 100644 --- a/ec/src/models/bn/g1.rs +++ b/ec/src/models/bn/g1.rs @@ -44,7 +44,7 @@ impl<'a, P: BnConfig> From<&'a G1Projective

> for G1Prepared

{ impl G1Prepared

{ pub fn is_zero(&self) -> bool { - self.0.infinity + self.0.is_zero() } } diff --git a/ec/src/models/bn/g2.rs b/ec/src/models/bn/g2.rs index 434624520..5b7276813 100644 --- a/ec/src/models/bn/g2.rs +++ b/ec/src/models/bn/g2.rs @@ -103,7 +103,7 @@ impl Default for G2Prepared

{ impl From> for G2Prepared

{ fn from(q: G2Affine

) -> Self { - if q.infinity { + if q.is_zero() { G2Prepared { ell_coeffs: vec![], infinity: true, diff --git a/ec/src/models/bw6/g1.rs b/ec/src/models/bw6/g1.rs index 6960d19d7..7f09b4094 100644 --- a/ec/src/models/bw6/g1.rs +++ b/ec/src/models/bw6/g1.rs @@ -45,7 +45,7 @@ impl<'a, P: BW6Config> From<&'a G1Projective

> for G1Prepared

{ impl G1Prepared

{ pub fn is_zero(&self) -> bool { - self.0.infinity + self.0.is_zero() } } diff --git a/ec/src/models/bw6/g2.rs b/ec/src/models/bw6/g2.rs index 8c826efcd..0dac90407 100644 --- a/ec/src/models/bw6/g2.rs +++ b/ec/src/models/bw6/g2.rs @@ -48,7 +48,7 @@ impl Default for G2Prepared

{ impl From> for G2Prepared

{ fn from(q: G2Affine

) -> Self { - if q.infinity { + if q.is_zero() { return Self { ell_coeffs_1: vec![], ell_coeffs_2: vec![], diff --git a/ec/src/models/short_weierstrass/affine.rs b/ec/src/models/short_weierstrass/affine.rs index b1340de3a..87ac6cf07 100644 --- a/ec/src/models/short_weierstrass/affine.rs +++ b/ec/src/models/short_weierstrass/affine.rs @@ -18,7 +18,7 @@ use ark_ff::{fields::Field, PrimeField, ToConstraintField, UniformRand}; use zeroize::Zeroize; -use super::{Projective, SWCurveConfig, SWFlags}; +use super::{Projective, SWCurveConfig, SWFlags, ZeroInd}; use crate::AffineRepr; /// Affine coordinates for a point on an elliptic curve in short Weierstrass @@ -38,7 +38,7 @@ pub struct Affine { #[doc(hidden)] pub y: P::BaseField, #[doc(hidden)] - pub infinity: bool, + pub infinity: P::ZeroIndicator, } impl PartialEq> for Affine

{ @@ -49,7 +49,7 @@ impl PartialEq> for Affine

{ impl Display for Affine

{ fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - match self.infinity { + match self.is_zero() { true => write!(f, "infinity"), false => write!(f, "({}, {})", self.x, self.y), } @@ -58,7 +58,7 @@ impl Display for Affine

{ impl Debug for Affine

{ fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - match self.infinity { + match self.is_zero() { true => write!(f, "infinity"), false => write!(f, "({}, {})", self.x, self.y), } @@ -72,7 +72,7 @@ impl Affine

{ let point = Self { x, y, - infinity: false, + infinity: P::ZeroIndicator::IS_NOT_ZERO, }; assert!(point.is_on_curve()); assert!(point.is_in_correct_subgroup_assuming_on_curve()); @@ -89,15 +89,18 @@ impl Affine

{ Self { x, y, - infinity: false, + infinity: P::ZeroIndicator::IS_NOT_ZERO, } } pub const fn identity() -> Self { + // Setting these to zero is *load-bearing* and important. + // These are the values that represent the identity element + // when `P::ZeroIndicator` is `()`. Self { x: P::BaseField::ZERO, y: P::BaseField::ZERO, - infinity: true, + infinity: P::ZeroIndicator::IS_ZERO, } } @@ -142,7 +145,7 @@ impl Affine

{ /// Checks if `self` is a valid point on the curve. pub fn is_on_curve(&self) -> bool { - if !self.infinity { + if !self.is_zero() { // Rust does not optimise away addition with zero let mut x3b = P::add_b(self.x.square() * self.x); if !P::COEFF_A.is_zero() { @@ -155,7 +158,7 @@ impl Affine

{ } pub fn to_flags(&self) -> SWFlags { - if self.infinity { + if self.is_zero() { SWFlags::PointAtInfinity } else if self.y <= -self.y { SWFlags::YIsPositive @@ -206,7 +209,7 @@ impl AffineRepr for Affine

{ type Group = Projective

; fn xy(&self) -> Option<(&Self::BaseField, &Self::BaseField)> { - (!self.infinity).then(|| (&self.x, &self.y)) + (!self.is_zero()).then(|| (&self.x, &self.y)) } #[inline] @@ -215,11 +218,7 @@ impl AffineRepr for Affine

{ } fn zero() -> Self { - Self { - x: P::BaseField::ZERO, - y: P::BaseField::ZERO, - infinity: true, - } + Self::identity() } fn from_random_bytes(bytes: &[u8]) -> Option { @@ -382,6 +381,7 @@ impl CanonicalDeserialize for Affine

{ impl ToConstraintField for Affine where M::BaseField: ToConstraintField, + M::ZeroIndicator: ToConstraintField, { #[inline] fn to_field_elements(&self) -> Option> { diff --git a/ec/src/models/short_weierstrass/group.rs b/ec/src/models/short_weierstrass/group.rs index 0513229f2..aa3dad1ae 100644 --- a/ec/src/models/short_weierstrass/group.rs +++ b/ec/src/models/short_weierstrass/group.rs @@ -620,6 +620,7 @@ impl CanonicalDeserialize for Projective

{ impl ToConstraintField for Projective where M::BaseField: ToConstraintField, + M::ZeroIndicator: ToConstraintField, { #[inline] fn to_field_elements(&self) -> Option> { diff --git a/ec/src/models/short_weierstrass/mod.rs b/ec/src/models/short_weierstrass/mod.rs index 965cbb83f..86ea0367b 100644 --- a/ec/src/models/short_weierstrass/mod.rs +++ b/ec/src/models/short_weierstrass/mod.rs @@ -2,7 +2,10 @@ use ark_serialize::{ CanonicalDeserialize, CanonicalDeserializeWithFlags, CanonicalSerialize, CanonicalSerializeWithFlags, Compress, SerializationError, Valid, Validate, }; -use ark_std::io::{Read, Write}; +use ark_std::{ + hash::Hash, + io::{Read, Write}, +}; use ark_ff::fields::Field; @@ -30,6 +33,9 @@ pub trait SWCurveConfig: super::CurveConfig { /// Generator of the prime-order subgroup. const GENERATOR: Affine; + /// A type that is stored in `Affine` to indicate whether the point is at infinity. + type ZeroIndicator: ZeroInd; + /// Helper method for computing `elem * Self::COEFF_A`. /// /// The default implementation should be overridden only if @@ -124,7 +130,7 @@ pub trait SWCurveConfig: super::CurveConfig { mut writer: W, compress: ark_serialize::Compress, ) -> Result<(), SerializationError> { - let (x, y, flags) = match item.infinity { + let (x, y, flags) = match item.is_zero() { true => ( Self::BaseField::zero(), Self::BaseField::zero(), @@ -198,3 +204,30 @@ pub trait SWCurveConfig: super::CurveConfig { } } } + +pub trait ZeroInd: + Hash + Ord + Eq + Copy + Sync + Send + Sized + 'static +{ + const IS_ZERO: Self; + const IS_NOT_ZERO: Self; + fn is_zero(point: &Affine) -> bool; + fn zeroize(&mut self) { + *self = Self::IS_NOT_ZERO; + } +} + +impl> ZeroInd for bool { + const IS_ZERO: Self = true; + const IS_NOT_ZERO: Self = false; + fn is_zero(point: &Affine) -> bool { + point.infinity + } +} + +impl ZeroInd for () { + const IS_ZERO: Self = (); + const IS_NOT_ZERO: Self = (); + fn is_zero(point: &Affine) -> bool { + point.x.is_zero() & point.y.is_zero() + } +} diff --git a/test-curves/src/bls12_381/g1.rs b/test-curves/src/bls12_381/g1.rs index f653e6f7a..78098b573 100644 --- a/test-curves/src/bls12_381/g1.rs +++ b/test-curves/src/bls12_381/g1.rs @@ -36,6 +36,8 @@ impl short_weierstrass::SWCurveConfig for Config { /// AFFINE_GENERATOR_COEFFS = (G1_GENERATOR_X, G1_GENERATOR_Y) const GENERATOR: G1Affine = G1Affine::new_unchecked(G1_GENERATOR_X, G1_GENERATOR_Y); + type ZeroIndicator = (); + #[inline(always)] fn mul_by_a(_: Self::BaseField) -> Self::BaseField { Self::BaseField::zero() diff --git a/test-curves/src/bls12_381/g1_swu_iso.rs b/test-curves/src/bls12_381/g1_swu_iso.rs index 8532422eb..012e1b17f 100644 --- a/test-curves/src/bls12_381/g1_swu_iso.rs +++ b/test-curves/src/bls12_381/g1_swu_iso.rs @@ -43,6 +43,7 @@ impl SWCurveConfig for SwuIsoConfig { const COEFF_B: Fq = MontFp!("2906670324641927570491258158026293881577086121416628140204402091718288198173574630967936031029026176254968826637280"); const GENERATOR: G1Affine = G1Affine::new_unchecked(G1_GENERATOR_X, G1_GENERATOR_Y); + type ZeroIndicator = (); } /// Lexicographically smallest, valid x-coordinate of a point P on the curve (with its corresponding y) multiplied by the cofactor. diff --git a/test-curves/src/bls12_381/g2.rs b/test-curves/src/bls12_381/g2.rs index f4c970910..f8ba1b874 100644 --- a/test-curves/src/bls12_381/g2.rs +++ b/test-curves/src/bls12_381/g2.rs @@ -53,6 +53,8 @@ impl short_weierstrass::SWCurveConfig for Config { /// AFFINE_GENERATOR_COEFFS = (G2_GENERATOR_X, G2_GENERATOR_Y) const GENERATOR: G2Affine = G2Affine::new_unchecked(G2_GENERATOR_X, G2_GENERATOR_Y); + type ZeroIndicator = bool; + #[inline(always)] fn mul_by_a(_: Self::BaseField) -> Self::BaseField { Self::BaseField::zero() diff --git a/test-curves/src/bls12_381/g2_swu_iso.rs b/test-curves/src/bls12_381/g2_swu_iso.rs index 0790cf99f..a4e431ea2 100644 --- a/test-curves/src/bls12_381/g2_swu_iso.rs +++ b/test-curves/src/bls12_381/g2_swu_iso.rs @@ -56,6 +56,8 @@ impl SWCurveConfig for SwuIsoConfig { const COEFF_B: Fq2 = Fq2::new(MontFp!("1012"), MontFp!("1012")); const GENERATOR: G2Affine = G2Affine::new_unchecked(G2_GENERATOR_X, G2_GENERATOR_Y); + + type ZeroIndicator = bool; } /// Lexicographically smallest, valid x-coordinate of a point P on the curve (with its corresponding y) multiplied by the cofactor. diff --git a/test-curves/src/bn384_small_two_adicity/g1.rs b/test-curves/src/bn384_small_two_adicity/g1.rs index 82704be8e..12e351dab 100644 --- a/test-curves/src/bn384_small_two_adicity/g1.rs +++ b/test-curves/src/bn384_small_two_adicity/g1.rs @@ -33,6 +33,9 @@ impl short_weierstrass::SWCurveConfig for Config { /// AFFINE_GENERATOR_COEFFS = (G1_GENERATOR_X, G1_GENERATOR_Y) const GENERATOR: G1Affine = G1Affine::new_unchecked(G1_GENERATOR_X, G1_GENERATOR_Y); + /// We can use `()`, because the point `(0, 0)` is not on the curve. + type ZeroIndicator = (); + #[inline(always)] fn mul_by_a(_: Self::BaseField) -> Self::BaseField { Self::BaseField::zero() diff --git a/test-curves/src/mnt4_753/g1.rs b/test-curves/src/mnt4_753/g1.rs index 2a329a56c..204eee263 100644 --- a/test-curves/src/mnt4_753/g1.rs +++ b/test-curves/src/mnt4_753/g1.rs @@ -36,6 +36,9 @@ impl short_weierstrass::SWCurveConfig for Config { /// AFFINE_GENERATOR_COEFFS = (G1_GENERATOR_X, G1_GENERATOR_Y) const GENERATOR: G1Affine = G1Affine::new_unchecked(G1_GENERATOR_X, G1_GENERATOR_Y); + + /// We use `bool because `(0, 0)` could be on the curve. + type ZeroIndicator = bool; } // Generator of G1 diff --git a/test-curves/src/secp256k1/g1.rs b/test-curves/src/secp256k1/g1.rs index 9e7e4de24..b1f216992 100644 --- a/test-curves/src/secp256k1/g1.rs +++ b/test-curves/src/secp256k1/g1.rs @@ -34,6 +34,9 @@ impl SWCurveConfig for Config { fn mul_by_a(_: Self::BaseField) -> Self::BaseField { Self::BaseField::zero() } + + /// We use `()` because `(0, 0)` cannot be on the curve. + type ZeroIndicator = (); } /// G_GENERATOR_X = 55066263022277343669578718895168534326250603453777594175500187360389116729240