diff --git a/ec/src/curve_edwards.rs b/ec/src/curve_edwards.rs index dbdac31..f37ff72 100644 --- a/ec/src/curve_edwards.rs +++ b/ec/src/curve_edwards.rs @@ -28,6 +28,8 @@ //! - Binary Edwards curves (characteristic $2$): Bernstein–Lange–Rezaeian Farashahi (2008) //! - Odd characteristic: +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; +use fp::{ref_field_impl, ref_field_trait_impl}; use core::fmt; use fp::field_ops::{FieldOps, FieldRandom}; @@ -38,7 +40,7 @@ use crate::point_edwards::EdwardsPoint; /// /// In odd characteristic only `d2` is used (the parameter `d`). /// In characteristic 2 both `d1` and `d2` are used. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Copy)] pub struct EdwardsCurve { /// The invariant d1 in the equation pub d1: F, @@ -46,6 +48,7 @@ pub struct EdwardsCurve { pub d2: F, } + impl fmt::Display for EdwardsCurve where F: FieldOps + fmt::Display, @@ -79,96 +82,138 @@ where } } -impl EdwardsCurve { - /// Construct an odd-characteristic Edwards curve `x² + y² = 1 + d·x²·y²`. - /// - /// Stores `d` as `d2`; `d1` is set to zero (unused). - pub fn new(d: F) -> Self { - assert!(F::characteristic()[0] != 2, "use new_binary() for char 2"); - assert!(d != F::zero(), "d must be nonzero"); - assert!(d != F::one(), "d must not be 1"); - Self { - d1: F::zero(), - d2: d, + +ref_field_impl! { + impl EdwardsCurve { + /// Construct an odd-characteristic Edwards curve `x² + y² = 1 + d·x²·y²`. + /// + /// Stores `d` as `d2`; `d1` is set to zero (unused). + pub fn new(d: F) -> Self { + assert!(F::characteristic()[0] != 2, "use new_binary() for char 2"); + assert!(d != F::zero(), "d must be nonzero"); + assert!(d != F::one(), "d must not be 1"); + Self { d1: F::zero(), d2: d } } - } - /// Construct a binary Edwards curve - /// `d₁(x+y) + d₂(x²+y²) = xy + xy(x+y) + x²y²`. - pub fn new_binary(d1: F, d2: F) -> Self { - assert_eq!(F::characteristic()[0], 2, "use new() for odd char"); - assert!(d1 != F::zero(), "d1 must be nonzero"); - let d1_sq = ::square(&d1); - assert!(d2 != d1_sq + d1, "d2 must differ from d1² + d1"); - Self { d1, d2 } - } + /// Construct a binary Edwards curve + /// `d₁(x+y) + d₂(x²+y²) = xy + xy(x+y) + x²y²`. + pub fn new_binary(d1: F, d2: F) -> Self { + assert_eq!(F::characteristic()[0], 2, "use new() for odd char"); + assert!(d1 != F::zero(), "d1 must be nonzero"); - /// Convenience accessor: the parameter `d` in odd characteristic. - pub fn d(&self) -> F { - self.d2 - } + let d1_sq = ::square(&d1); + let forbidden = &d1_sq + &d1; + assert!(d2 != forbidden, "d2 must differ from d1² + d1"); - /// Check whether the affine point `(x, y)` lies on the curve. - pub fn contains(&self, x: &F, y: &F) -> bool { - if F::characteristic()[0] != 2 { - // x² + y² == 1 + d·x²·y² - let x2 = ::square(x); - let y2 = ::square(y); - x2 + y2 == F::one() + self.d2 * x2 * y2 - } else { - // d₁(x+y) + d₂(x²+y²) == xy + xy(x+y) + x²y² - let x2 = ::square(x); - let y2 = ::square(y); - let xy = *x * *y; - let xpy = *x + *y; - self.d1 * xpy + self.d2 * (x2 + y2) == xy + xy * xpy + x2 * y2 + Self { d1, d2 } } - } - /// Sample a random affine point on this Edwards curve using the provided RNG. - /// - /// This currently uses a square-root-based construction and is implemented - /// only for odd characteristic. It returns a point `P` such that - /// `self.is_on_curve(&P)` holds. - pub fn random_point(&self, rng: &mut (impl rand::CryptoRng + rand::Rng)) -> EdwardsPoint { - assert!( - F::characteristic()[0] != 2, - "random_point currently implemented only for odd characteristic" - ); - - loop { - let x = F::random(rng); - let x2 = ::square(&x); - let denom = F::one() - self.d2 * x2; - - if bool::from(denom.is_zero()) { - continue; + /// Convenience accessor: the parameter `d` in odd characteristic. + pub fn d(&self) -> F { + self.d2.clone() + } + + /// Check whether the affine point `(x, y)` lies on the curve. + pub fn contains(&self, x: &F, y: &F) -> bool { + if F::characteristic()[0] != 2 { + // x² + y² == 1 + d·x²·y² + let x2 = ::square(x); + let y2 = ::square(y); + + let lhs = &x2 + &y2; + + let x2y2 = &x2 * &y2; + let dx2y2 = &self.d2 * &x2y2; + let one = F::one(); + let rhs = &one + &dx2y2; + + lhs == rhs + } else { + // d₁(x+y) + d₂(x²+y²) == xy + xy(x+y) + x²y² + let x2 = ::square(x); + let y2 = ::square(y); + let xy = x * y; + let xpy = x + y; + + let lhs1 = &self.d1 * &xpy; + let x2py2 = &x2 + &y2; + let lhs2 = &self.d2 * &x2py2; + let lhs = &lhs1 + &lhs2; + + let xy_xpy = &xy * &xpy; + let x2y2 = &x2 * &y2; + let rhs_tmp = &xy + &xy_xpy; + let rhs = &rhs_tmp + &x2y2; + + lhs == rhs } + } + + /// Sample a random affine point on this Edwards curve using the provided RNG. + /// + /// This currently uses a square-root-based construction and is implemented + /// only for odd characteristic. It returns a point `P` such that + /// `self.is_on_curve(&P)` holds. + pub fn random_point(&self, rng: &mut (impl rand::CryptoRng + rand::Rng)) -> EdwardsPoint { + assert!( + F::characteristic()[0] != 2, + "random_point currently implemented only for odd characteristic" + ); + + loop { + let x = F::random(rng); + let x2 = ::square(&x); + let denom = &F::one() - &(&self.d2 * &x2); + + if bool::from(denom.is_zero()) { + continue; + } - let rhs = (F::one() - x2) * denom.invert().unwrap(); + let rhs = &(&F::one() - &x2) * &denom.invert().unwrap(); - if let Some(y) = rhs.sqrt().into_option() { - let p = EdwardsPoint::new(x, y); - debug_assert!(self.is_on_curve(&p)); - return p; + if let Some(y) = rhs.sqrt().into_option() { + let p = EdwardsPoint::new(x, y); + debug_assert!(self.is_on_curve(&p)); + return p; + } } } } } -impl Curve for EdwardsCurve { - type BaseField = F; - type Point = EdwardsPoint; - fn is_on_curve(&self, point: &Self::Point) -> bool { - self.contains(&point.x, &point.y) - } - fn random_point(&self, rng: &mut (impl rand::CryptoRng + rand::Rng)) -> Self::Point { - EdwardsCurve::random_point(self, rng) - } - fn j_invariant(&self) -> F { + + + + + + + + + + + + + + + + +ref_field_trait_impl!{ + impl Curve for EdwardsCurve { + type BaseField = F; + type Point = EdwardsPoint; + + fn is_on_curve(&self, point: &Self::Point) -> bool { + self.contains(&point.x, &point.y) + } + + fn random_point(&self, rng: &mut (impl rand::CryptoRng + rand::Rng)) -> Self::Point { + EdwardsCurve::random_point(self, rng) + } + + fn j_invariant(&self) -> F { if F::characteristic()[0] != 2 { // j = 16(1 + 14d + d²)³ / (d(1 − d)⁴) let d = self.d2; @@ -176,20 +221,20 @@ impl Curve for EdwardsCurve { let two = ::double(&F::one()); let four = ::double(&two); let eight = ::double(&four); - let fourteen = eight + four + two; + let fourteen = &eight + &(&four + &two); let sixteen = ::double(&eight); - let inner = F::one() + fourteen * d + d2; - let inner_cubed = inner * ::square(&inner); - let numer = sixteen * inner_cubed; + let inner = &F::one() + &(&(&fourteen * &d) + &d2); + let inner_cubed = &inner * &::square(&inner); + let numer = &sixteen * &inner_cubed; - let one_minus_d = F::one() - d; + let one_minus_d = &F::one() - &d; let omd2 = ::square(&one_minus_d); let omd4 = ::square(&omd2); - let denom = d * omd4; + let denom = &d * &omd4; - numer - * denom + &numer + * &denom .invert() .into_option() .expect("d(1-d)^4 must be invertible") @@ -198,19 +243,60 @@ impl Curve for EdwardsCurve { let d1_sq = ::square(&self.d1); let d1_4 = ::square(&d1_sq); let d2_sq = ::square(&self.d2); - let denom = d1_4 * (d1_4 + d1_sq + d2_sq); + let denom = &d1_4 * &(&d1_4 + &(&d1_sq + &d2_sq)); denom .invert() .into_option() .expect("j-invariant denominator must be invertible") + } + } + + fn a_invariants(&self) -> Vec { + if F::characteristic()[0] != 2 { + vec![self.d2] + } else { + vec![self.d1, self.d2] + } } } +} - fn a_invariants(&self) -> Vec { - if F::characteristic()[0] != 2 { - vec![self.d2] - } else { - vec![self.d1, self.d2] + +// --------------------------------------------------------------------------- +// Constant-time functionalities +// --------------------------------------------------------------------------- + +impl ConditionallySelectable for EdwardsCurve +where + F: FieldOps + Copy, +{ + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Self { + d1: F::conditional_select(&a.d1, &b.d1, choice), + d2: F::conditional_select(&a.d2, &b.d2, choice), } } + + fn conditional_assign(&mut self, other: &Self, choice: Choice) { + F::conditional_assign(&mut self.d1, &other.d1, choice); + F::conditional_assign(&mut self.d2, &other.d2, choice); + } + + fn conditional_swap(a: &mut Self, b: &mut Self, choice: Choice) { + F::conditional_swap(&mut a.d1, &mut b.d1, choice); + F::conditional_swap(&mut a.d2, &mut b.d2, choice); + } } + +impl ConstantTimeEq for EdwardsCurve +where + F: FieldOps + Copy + ConstantTimeEq, +{ + fn ct_eq(&self, other: &Self) -> Choice { + self.d1.ct_eq(&other.d1) & self.d2.ct_eq(&other.d2) + } + + fn ct_ne(&self, other: &Self) -> Choice { + !self.ct_eq(other) + } +} \ No newline at end of file diff --git a/ec/src/curve_jacobi_intersection.rs b/ec/src/curve_jacobi_intersection.rs index 7c4ea86..7c3469b 100644 --- a/ec/src/curve_jacobi_intersection.rs +++ b/ec/src/curve_jacobi_intersection.rs @@ -17,14 +17,24 @@ //! y² = x³ + (2-a)x² + (1-a)x. //! ``` //! +<<<<<<< HEAD + +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; +use fp::field_ops::FieldOps; +use fp::{ref_field_impl, ref_field_trait_impl}; +======= use core::fmt; use fp::field_ops::{FieldOps, FieldRandom}; +>>>>>>> origin/main use crate::curve_ops::Curve; use crate::curve_weierstrass::WeierstrassCurve; use crate::point_jacobi_intersection::JacobiIntersectionPoint; /// A Jacobi-intersection curve. +<<<<<<< HEAD +#[derive(Debug, Clone, PartialEq, Eq, Copy)] +======= /// /// With the equation /// $$ @@ -37,11 +47,22 @@ use crate::point_jacobi_intersection::JacobiIntersectionPoint; /// y^2 = x^3 + (2 - a)x^2 + (1 - a)x. /// $$ #[derive(Debug, Clone, PartialEq, Eq)] +>>>>>>> origin/main pub struct JacobiIntersectionCurve { /// The variable a in the definition of the curve pub a: F, } +<<<<<<< HEAD +ref_field_impl! { + impl JacobiIntersectionCurve { + /// Construct a Jacobi intersection from its parameter `a`. + pub fn new(a: F) -> Self { + assert!(F::characteristic()[0] != 2, "Jacobi intersections require char(F) != 2"); + assert!(Self::is_smooth(&a), "singular Jacobi intersection"); + Self { a } + } +======= impl fmt::Display for JacobiIntersectionCurve where F: FieldOps + fmt::Display, @@ -69,21 +90,52 @@ impl JacobiIntersectionCurve { assert!(Self::is_smooth(&a), "singular Jacobi intersection"); Self { a } } +>>>>>>> origin/main - /// Smoothness requirement: `a != 0` and `a != 1`. - pub fn is_smooth(a: &F) -> bool { - *a != F::zero() && *a != F::one() - } + /// Smoothness requirement: `a != 0` and `a != 1`. + pub fn is_smooth(a: &F) -> bool { + let zero = F::zero(); + let one = F::one(); + a != &zero && a != &one + } + + /// Check both defining quadrics. + pub fn contains(&self, s: &F, c: &F, d: &F) -> bool { + let s2 = ::square(s); + let c2 = ::square(c); + let d2 = ::square(d); - /// Check both defining quadrics. - pub fn contains(&self, s: &F, c: &F, d: &F) -> bool { - let s2 = ::square(s); - let c2 = ::square(c); - let d2 = ::square(d); + let lhs1 = &s2 + &c2; + let lhs2 = &(&self.a * &s2) + &d2; + let one = F::one(); + +<<<<<<< HEAD + lhs1 == one && lhs2 == one + } + + pub fn a_invariants(&self) -> [F; 1] { + [self.a.clone()] + } - s2 + c2 == F::one() && self.a * s2 + d2 == F::one() + /// Birationally equivalent Weierstrass model + /// `y² = x³ + (2-a)x² + (1-a)x`. + pub fn to_weierstrass_curve(&self) -> WeierstrassCurve { + let one = F::one(); + let two = ::double(&one); + + let a2 = &two - &self.a; + let a4 = &one - &self.a; + + WeierstrassCurve::new(F::zero(), a2, F::zero(), a4, F::zero()) + } } +} +ref_field_trait_impl! { + impl Curve for JacobiIntersectionCurve { + type BaseField = F; + type Point = JacobiIntersectionPoint; +======= /// Returns the corresponding invariant `a` (not the a-invariants /// of the Jacobian) pub fn a_invariants(&self) -> [F; 1] { @@ -129,20 +181,64 @@ impl JacobiIntersectionCurve { impl Curve for JacobiIntersectionCurve { type BaseField = F; type Point = JacobiIntersectionPoint; +>>>>>>> origin/main - fn is_on_curve(&self, point: &Self::Point) -> bool { - self.contains(&point.s, &point.c, &point.d) - } + fn is_on_curve(&self, point: &Self::Point) -> bool { + self.contains(&point.s, &point.c, &point.d) + } +<<<<<<< HEAD + fn random_point(&self) -> Self::Point { + todo!() + } +======= fn random_point(&self, rng: &mut (impl rand::CryptoRng + rand::Rng)) -> Self::Point { JacobiIntersectionCurve::random_point(self, rng) } +>>>>>>> origin/main + + fn j_invariant(&self) -> F { + self.to_weierstrass_curve().j_invariant() + } + + fn a_invariants(&self) -> Vec { + JacobiIntersectionCurve::a_invariants(self).to_vec() + } + } +} - fn j_invariant(&self) -> F { - self.to_weierstrass_curve().j_invariant() +// --------------------------------------------------------------------------- +// Constant-time functionalities +// --------------------------------------------------------------------------- + +impl ConditionallySelectable for JacobiIntersectionCurve +where + F: FieldOps + Copy, +{ + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Self { + a: F::conditional_select(&a.a, &b.a, choice), + } } - fn a_invariants(&self) -> Vec { - JacobiIntersectionCurve::a_invariants(self).to_vec() + fn conditional_assign(&mut self, other: &Self, choice: Choice) { + F::conditional_assign(&mut self.a, &other.a, choice); + } + + fn conditional_swap(a: &mut Self, b: &mut Self, choice: Choice) { + F::conditional_swap(&mut a.a, &mut b.a, choice); } } + +impl ConstantTimeEq for JacobiIntersectionCurve +where + F: FieldOps + Copy + ConstantTimeEq, +{ + fn ct_eq(&self, other: &Self) -> Choice { + self.a.ct_eq(&other.a) + } + + fn ct_ne(&self, other: &Self) -> Choice { + !self.ct_eq(other) + } +} \ No newline at end of file diff --git a/ec/src/curve_jacobi_quartic.rs b/ec/src/curve_jacobi_quartic.rs index 618890c..12c810a 100644 --- a/ec/src/curve_jacobi_quartic.rs +++ b/ec/src/curve_jacobi_quartic.rs @@ -14,16 +14,30 @@ //! *Jacobi Quartic Curves Revisited* (2009), which treats the more general //! “extended Jacobi quartic” family with arbitrary `a` and `d` satisfying //! `d(a²-d) ≠ 0`. +<<<<<<< HEAD + +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; +use fp::field_ops::FieldOps; +use fp::{ref_field_impl, ref_field_trait_impl}; +======= use core::fmt; use fp::field_ops::{FieldOps, FieldRandom}; +>>>>>>> origin/main use crate::curve_ops::Curve; use crate::point_jacobi_quartic::JacobiQuarticPoint; /// A Jacobi quartic curve /// +<<<<<<< HEAD +/// ```text +/// y² = d x⁴ + 2 a x² + 1 +/// ``` +#[derive(Debug, Clone, PartialEq, Eq, Copy)] +======= /// $$y^2 = d x^4 + 2 a x^2 + 1$ #[derive(Debug, Clone, PartialEq, Eq)] +>>>>>>> origin/main pub struct JacobiQuarticCurve { /// Invariant a in the definition pub a: F, @@ -31,6 +45,15 @@ pub struct JacobiQuarticCurve { pub d: F, } +<<<<<<< HEAD +ref_field_impl! { + impl JacobiQuarticCurve { + /// Construct a Jacobi quartic curve from `(a, d)`. + pub fn new(a: F, d: F) -> Self { + assert!(F::characteristic()[0] != 2, "Jacobi quartics require char(F) != 2"); + assert!(Self::is_smooth(&a, &d), "singular Jacobi quartic"); + Self { a, d } +======= impl fmt::Display for JacobiQuarticCurve where F: FieldOps + fmt::Display, @@ -68,29 +91,57 @@ impl JacobiQuarticCurve { pub fn is_smooth(a: &F, d: &F) -> bool { if bool::from(d.is_zero()) { return false; +>>>>>>> origin/main } - let a2 = ::square(a); - a2 != *d - } +<<<<<<< HEAD + /// Smoothness criterion from the discriminant + /// `Δ = 256 d (a² - d)² ≠ 0`. + pub fn is_smooth(a: &F, d: &F) -> bool { + if bool::from(d.is_zero()) { + return false; + } +======= /// Check whether `(x, y)` lies on $y^2 = d x^4 + 2 a x^2 + 1$. pub fn contains(&self, x: &F, y: &F) -> bool { let x2 = ::square(x); let x4 = ::square(&x2); let y2 = ::square(y); let two = ::double(&F::one()); +>>>>>>> origin/main - y2 == self.d * x4 + two * self.a * x2 + F::one() - } + let a2 = ::square(a); + a2 != d.clone() + } - /// Return `[a, d]`. - pub fn a_invariants(&self) -> [F; 2] { - [self.a, self.d] - } + /// Check whether `(x, y)` lies on `y² = d x⁴ + 2 a x² + 1`. + pub fn contains(&self, x: &F, y: &F) -> bool { + let x2 = ::square(x); + let x4 = ::square(&x2); + let y2 = ::square(y); + + let one = F::one(); + let two = ::double(&one); + + let dx4 = &self.d * &x4; + let ax2 = &self.a * &x2; + let two_ax2 = &two * &ax2; + + let rhs_tmp = &dx4 + &two_ax2; + let rhs = &rhs_tmp + &one; - /// Return the affine identity `(0, 1)`. - pub fn neutral_point(&self) -> JacobiQuarticPoint { - JacobiQuarticPoint::identity() + y2 == rhs + } + + /// Return `[a, d]`. + pub fn a_invariants(&self) -> [F; 2] { + [self.a.clone(), self.d.clone()] + } + + /// Return the affine identity `(0, 1)`. + pub fn neutral_point(&self) -> JacobiQuarticPoint { + JacobiQuarticPoint::identity() + } } /// Sample a random affine point on this Jacobi quartic using the provided RNG. @@ -117,14 +168,38 @@ impl JacobiQuarticCurve { } } +<<<<<<< HEAD +ref_field_trait_impl! { + impl Curve for JacobiQuarticCurve { + type BaseField = F; + type Point = JacobiQuarticPoint; +======= impl Curve for JacobiQuarticCurve { type BaseField = F; type Point = JacobiQuarticPoint; +>>>>>>> origin/main - fn is_on_curve(&self, point: &Self::Point) -> bool { - self.contains(&point.x, &point.y) - } + fn is_on_curve(&self, point: &Self::Point) -> bool { + self.contains(&point.x, &point.y) + } + +<<<<<<< HEAD + fn random_point(&self) -> Self::Point { + todo!() + } + + /// The paper gives + /// + /// ```text + /// j = 64 d^{-1} (a²-d)^{-2} (a²+3d)³. + /// ``` + fn j_invariant(&self) -> F { + let a2 = ::square(&self.a); + let one = F::one(); + let two = ::double(&one); + let three = &two + &one; +======= fn random_point(&self, rng: &mut (impl rand::CryptoRng + rand::Rng)) -> Self::Point { JacobiQuarticCurve::random_point(self, rng) } @@ -140,21 +215,69 @@ impl Curve for JacobiQuarticCurve { &F::one(), ))); let sixty_four = eight * eight; // 8 * 8 = 64 +>>>>>>> origin/main - let num_base = a2 + three * self.d; - let num = sixty_four * num_base * ::square(&num_base); + let four = ::double(&two); + let eight = ::double(&four); + let sixty_four = &eight * &eight; - let diff = a2 - self.d; - let denom = self.d * ::square(&diff); - let denom_inv = denom - .invert() - .into_option() - .expect("Jacobi quartic j-invariant denominator must be invertible"); + let three_d = &three * &self.d; + let num_base = &a2 + &three_d; + let num_base_sq = ::square(&num_base); + let num_base_cubed = &num_base * &num_base_sq; + let num = &sixty_four * &num_base_cubed; - num * denom_inv + let diff = &a2 - &self.d; + let diff_sq = ::square(&diff); + let denom = &self.d * &diff_sq; + let denom_inv = ::invert(&denom) + .into_option() + .expect("Jacobi quartic j-invariant denominator must be invertible"); + + &num * &denom_inv + } + + fn a_invariants(&self) -> Vec { + JacobiQuarticCurve::a_invariants(self).to_vec() + } } +} - fn a_invariants(&self) -> Vec { - JacobiQuarticCurve::a_invariants(self).to_vec() +// --------------------------------------------------------------------------- +// Constant-time functionalities +// --------------------------------------------------------------------------- + +impl ConditionallySelectable for JacobiQuarticCurve +where + F: FieldOps + Copy, +{ + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Self { + a: F::conditional_select(&a.a, &b.a, choice), + d: F::conditional_select(&a.d, &b.d, choice), + } + } + + fn conditional_assign(&mut self, other: &Self, choice: Choice) { + F::conditional_assign(&mut self.a, &other.a, choice); + F::conditional_assign(&mut self.d, &other.d, choice); + } + + fn conditional_swap(a: &mut Self, b: &mut Self, choice: Choice) { + F::conditional_swap(&mut a.a, &mut b.a, choice); + F::conditional_swap(&mut a.d, &mut b.d, choice); } } + +impl ConstantTimeEq for JacobiQuarticCurve +where + F: FieldOps + Copy + ConstantTimeEq, +{ + fn ct_eq(&self, other: &Self) -> Choice { + self.a.ct_eq(&other.a) & self.d.ct_eq(&other.d) + } + + fn ct_ne(&self, other: &Self) -> Choice { + !self.ct_eq(other) + } +} \ No newline at end of file diff --git a/ec/src/curve_montgomery.rs b/ec/src/curve_montgomery.rs index 769e5a0..41323c4 100644 --- a/ec/src/curve_montgomery.rs +++ b/ec/src/curve_montgomery.rs @@ -34,9 +34,15 @@ //! additions and doublings, which is especially convenient for constant-time //! scalar multiplication. +<<<<<<< HEAD +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; +use fp::field_ops::FieldOps; +use fp::{ref_field_impl, ref_field_trait_impl}; +======= use core::fmt; use fp::field_ops::{FieldOps, FieldRandom}; +>>>>>>> origin/main use crate::curve_ops::Curve; use crate::point_montgomery::KummerPoint; @@ -45,7 +51,7 @@ use crate::point_montgomery::KummerPoint; /// $By^2 = x(x^2+ Ax + 1)$ /// /// over a field `F`. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Copy)] pub struct MontgomeryCurve { /// The invariant $A$ in the equation of the montgomery curve pub a: F, @@ -53,6 +59,14 @@ pub struct MontgomeryCurve { pub b: F, } +<<<<<<< HEAD +ref_field_impl! { + impl MontgomeryCurve { + + pub fn new(a: F, b: F) -> Self { + assert!(Self::is_smooth(&a, &b)); + Self { a, b } +======= impl fmt::Display for MontgomeryCurve where @@ -110,26 +124,40 @@ impl MontgomeryCurve { } else { // In char 2, smoothness criterion is just b != 0 !bool::from(b.is_zero()) +>>>>>>> origin/main } - } +<<<<<<< HEAD + pub fn is_smooth(a: &F, b: &F) -> bool { + if F::characteristic()[0] != 2 { + let two = ::double(&F::one()); + let minus_two = -&two; + + !bool::from(b.is_zero()) && a != &two && a != &minus_two + } else { + !bool::from(b.is_zero()) + } + } +======= // ------------------------------------------------------------------- // Invariants and constants // ------------------------------------------------------------------- +>>>>>>> origin/main - /// Return the model coefficients `[A, B]`. - pub fn a_invariants(&self) -> [F; 2] { - [self.a.clone(), self.b.clone()] - } + pub fn a_invariants(&self) -> [F; 2] { + [self.a.clone(), self.b.clone()] + } + + pub fn a24(&self) -> F { + assert_ne!(F::characteristic()[0], 2); - /// Return the Montgomery-ladder constant `A24 = (a + 2)/4`. - pub fn a24(&self) -> F { - assert_ne!(F::characteristic()[0], 2); - let two = ::double(&F::one()); - let four = ::double(&two); - let four_inv = ::invert(&four).unwrap(); + let two = ::double(&F::one()); + let four = ::double(&two); + let four_inv = ::invert(&four).unwrap(); - (self.a + two) * four_inv + let tmp = &self.a + &two; + &tmp * &four_inv + } } } @@ -158,6 +186,38 @@ impl MontgomeryCurve { // Curve predicates // ------------------------------------------------------------------- +<<<<<<< HEAD +ref_field_trait_impl! { + impl Curve for MontgomeryCurve { + + type BaseField = F; + type Point = KummerPoint; + + fn is_on_curve(&self, point: &Self::Point) -> bool { + if point.is_identity() { + return true; + } + + let z_inv = ::invert(&point.z); + if bool::from(z_inv.is_none()) { + return true; + } + + let x = &point.x * &z_inv.unwrap(); + + if F::characteristic()[0] != 2 { + let xsq = ::square(&x); + let xcubed = &x * &xsq; + let axsq = &self.a * &xsq; + + let sum1 = &xcubed + &axsq; + let sum = &sum1 + &x; + + let binv = ::invert(&self.b).unwrap(); + let val = &sum * &binv; + + ::legendre(&val) > -1i8 +======= impl Curve for MontgomeryCurve { type BaseField = F; type Point = KummerPoint; @@ -182,24 +242,84 @@ impl Curve for MontgomeryCurve { let binv = ::invert(&self.b).unwrap(); let sum = xcubed + axsq + x; ::legendre(&(sum * binv)) > -1i8 +>>>>>>> origin/main } else { if bool::from(x.is_zero()) { - // The point with x = 0 always lies on the curve. true } else { +<<<<<<< HEAD +======= // If x != 0, set t = y/x, which gives the equation t^2 + t = x + a + b^2/x. // So x is the x-coordinate of a point on the curve iff there is some t in F // s.t. t^2 + t = x + a + b^2/x, which translates into // Tr_{F/F_2}(x + a + b^2/x) == 0. +>>>>>>> origin/main let bsq = ::square(&self.b); let xinv = x.invert().unwrap(); - let rhs = x + self.a + bsq * xinv; + + let tmp1 = &x + &self.a; + let tmp2 = &bsq * &xinv; + let rhs = &tmp1 + &tmp2; + bool::from(rhs.trace().is_zero()) } } } + +<<<<<<< HEAD + fn random_point(&self) -> Self::Point { + todo!() + } + + fn j_invariant(&self) -> F { + if F::characteristic()[0] != 2 { + let two = ::double(&F::one()); + let three = &two + &F::one(); + let four = ::double(&two); + let sixteen = ::square(&four); + let twofivesix = ::square(&sixteen); + + let asq = ::square(&self.a); + let asq_min_three = &asq - &three; + + let asq_min_three_sq = ::square(&asq_min_three); + let asq_min_three_cubed = &asq_min_three * &asq_min_three_sq; + + let denom = &asq - &four; + let denom_inv = ::invert(&denom) + .into_option() + .expect("a != ±2"); + + let tmp = &twofivesix * &asq_min_three_cubed; + &tmp * &denom_inv + } else { + let bsq = ::square(&self.b); + let bfourth = ::square(&bsq); + ::invert(&bfourth).unwrap() + } + } + + fn a_invariants(&self) -> Vec { + MontgomeryCurve::a_invariants(self).to_vec() + } } +} + + +// --------------------------------------------------------------------------- +// Constant-time functionalities +// --------------------------------------------------------------------------- + +impl ConditionallySelectable for MontgomeryCurve +where + F: FieldOps + Copy, +{ + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Self{ + a: F::conditional_select(& a.a, &b.a, choice), + b: F::conditional_select(& a.b, &b.b, choice), +======= fn random_point(&self, rng: &mut (impl rand::CryptoRng + rand::Rng)) -> Self::Point { MontgomeryCurve::random_point(self, rng) } @@ -228,10 +348,35 @@ impl Curve for MontgomeryCurve { let bsq = ::square(&self.b); let bfourth = ::square(&bsq); ::invert(&bfourth).unwrap() +>>>>>>> origin/main } } - fn a_invariants(&self) -> Vec { - MontgomeryCurve::a_invariants(self).to_vec() + fn conditional_assign(&mut self, other: &Self, choice: Choice) { + F::conditional_assign(&mut self.a, &other.a, choice); + F::conditional_assign(&mut self.b, &other.b, choice); + } +<<<<<<< HEAD + + fn conditional_swap(a: &mut Self, b: &mut Self, choice: Choice) { + F::conditional_swap(&mut a.a, &mut b.a, choice); + F::conditional_swap(&mut a.b, &mut b.b, choice); } } + +impl ConstantTimeEq for MontgomeryCurve +where + F: FieldOps + Copy + ConstantTimeEq, +{ + fn ct_eq(&self, other: &Self) -> Choice { + self.a.ct_eq(&other.a) & self.b.ct_eq(&other.b) + } + + fn ct_ne(&self, other: &Self) -> Choice { !self.ct_eq(other) } +} + + + +======= +} +>>>>>>> origin/main diff --git a/ec/src/curve_weierstrass.rs b/ec/src/curve_weierstrass.rs index c037ee2..74e867f 100644 --- a/ec/src/curve_weierstrass.rs +++ b/ec/src/curve_weierstrass.rs @@ -22,6 +22,8 @@ use core::fmt; use fp::field_ops::{FieldOps, FieldRandom}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; +use fp::{ref_field_fns, ref_field_impl, ref_field_trait_impl}; use crate::curve_ops::Curve; use crate::point_weierstrass::AffinePoint; @@ -34,7 +36,7 @@ use crate::point_weierstrass::AffinePoint; /// /// All five coefficients are stored explicitly; the short Weierstrass /// case simply has $a_1 = a_2 = a_3 = 0$. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Copy)] pub struct WeierstrassCurve { /// a-invariants pub a1: F, @@ -70,136 +72,138 @@ where } } -impl WeierstrassCurve { - // ------------------------------------------------------------------- - // Constructors - // ------------------------------------------------------------------- - - /// Constructs a curve from the five general Weierstrass coefficients - /// $(a_1, a_2, a_3, a_4, a_6)$. - /// - /// The curve is required to be smooth, i.e. its discriminant $\Delta \ne 0$. - /// - /// # Panics - /// - /// Panics if the curve is singular. - pub fn new(a1: F, a2: F, a3: F, a4: F, a6: F) -> Self { - assert!(Self::is_smooth(&a1, &a2, &a3, &a4, &a6)); - - Self { a1, a2, a3, a4, a6 } - } - - /// Constructs the **short Weierstrass** curve - /// - /// $$ - /// y^2 = x^3 + ax + b. - /// $$ - /// - /// This sets $a_1 = a_2 = a_3 = 0$, $a_4 = a$, and $a_6 = b$. - /// - /// This form is only valid when $\mathrm{char}(F) \ne 2, 3$. - /// - /// # Panics - /// - /// Panics if the characteristic is $\le 3$ or if the curve is singular. - pub fn new_short(a: F, b: F) -> Self { - assert!(F::characteristic()[0] > 3); - assert!(Self::is_smooth(&F::zero(), &F::zero(), &F::zero(), &a, &b)); - - Self { - a1: F::zero(), - a2: F::zero(), - a3: F::zero(), - a4: a, - a6: b, +ref_field_impl! { + impl WeierstrassCurve { + // ------------------------------------------------------------------- + // Constructors + // ------------------------------------------------------------------- + + /// Constructs a curve from the five general Weierstrass coefficients + /// $(a_1, a_2, a_3, a_4, a_6)$. + /// + /// The curve is required to be smooth, i.e. its discriminant $\Delta \ne 0$. + /// + /// # Panics + /// + /// Panics if the curve is singular. + pub fn new(a1: F, a2: F, a3: F, a4: F, a6: F) -> Self { + assert!(Self::is_smooth(&a1, &a2, &a3, &a4, &a6)); + + Self { a1, a2, a3, a4, a6 } } - } - - /// Returns `true` if and only if the curve defined by the given - /// $a$-invariants is smooth. - /// - /// This is equivalent to checking that the discriminant satisfies - /// $\Delta \ne 0$. - pub fn is_smooth(a1: &F, a2: &F, a3: &F, a4: &F, a6: &F) -> bool { - discriminant_from_coeffs::(&a1, &a2, &a3, &a4, &a6) != F::zero() - } - // ------------------------------------------------------------------- - // Invariants - // ------------------------------------------------------------------- - - /// Returns the five $a$-invariants $[a_1, a_2, a_3, a_4, a_6]$. - pub fn a_invariants(&self) -> [F; 5] { - [ - self.a1.clone(), - self.a2.clone(), - self.a3.clone(), - self.a4.clone(), - self.a6.clone(), - ] - } - - /// Returns the four $b$-invariants $[b_2, b_4, b_6, b_8]$. - pub fn b_invariants(&self) -> [F; 4] { - [self.b2(), self.b4(), self.b6(), self.b8()] - } - - // ------------------------------------------------------------------- - // Curve predicates - // ------------------------------------------------------------------- - - /// Checks whether the affine point $(x, y)$ satisfies the curve equation. - /// - /// $$ - /// y^2 + a_1 xy + a_3 y = x^3 + a_2 x^2 + a_4 x + a_6. - /// $$ - pub fn contains(&self, x: &F, y: &F) -> bool { - let lhs = { - let y2 = ::square(y); - let a1xy = self.a1 * *x * *y; - let a3y = self.a3 * *y; - y2 + a1xy + a3y - }; - - let rhs = { - let x2 = ::square(x); - let x3 = *x * x2; - let a2x2 = self.a2 * x2; - let a4x = self.a4 * *x; - x3 + a2x2 + a4x + self.a6 - }; - - lhs == rhs - } - - /// Sample a random affine point on this curve using the provided RNG. - /// - /// This currently uses a square-root-based construction and is implemented - /// only for odd characteristic. It returns a finite affine point `P` such - /// that `self.is_on_curve(&P)` holds. - pub fn random_point(&self, rng: &mut (impl rand::CryptoRng + rand::Rng)) -> AffinePoint{ - assert!(F::characteristic()[0] != 2, "random_point currently implemented for odd characteristic"); + /// Constructs the **short Weierstrass** curve + /// + /// $$ + /// y^2 = x^3 + ax + b. + /// $$ + /// + /// This sets $a_1 = a_2 = a_3 = 0$, $a_4 = a$, and $a_6 = b$. + /// + /// This form is only valid when $\mathrm{char}(F) \ne 2, 3$. + /// + /// # Panics + /// + /// Panics if the characteristic is $\le 3$ or if the curve is singular. + pub fn new_short(a: F, b: F) -> Self { + assert!(F::characteristic()[0] > 3); + assert!(Self::is_smooth(&F::zero(), &F::zero(), &F::zero(), &a, &b)); + + Self { + a1: F::zero(), + a2: F::zero(), + a3: F::zero(), + a4: a, + a6: b, + } + } - let two = ::double(&F::one()); - let two_inv = two.invert().into_option().expect("2 must be invertible"); + /// Returns `true` if and only if the curve defined by the given + /// $a$-invariants is smooth. + /// + /// This is equivalent to checking that the discriminant satisfies + /// $\Delta \ne 0$. + pub fn is_smooth(a1: &F, a2: &F, a3: &F, a4: &F, a6: &F) -> bool { + discriminant_from_coeffs::(&a1, &a2, &a3, &a4, &a6) != F::zero() + } - loop { - let x = F::random(rng); + // ------------------------------------------------------------------- + // Invariants + // ------------------------------------------------------------------- + + /// Returns the five $a$-invariants $[a_1, a_2, a_3, a_4, a_6]$. + pub fn a_invariants(&self) -> [F; 5] { + [ + self.a1.clone(), + self.a2.clone(), + self.a3.clone(), + self.a4.clone(), + self.a6.clone(), + ] + } - let b = self.a1 * x + self.a3; - let x2 = ::square(&x); - let rhs = x * x2 + self.a2 * x2 + self.a4 * x + self.a6; + /// Returns the four $b$-invariants $[b_2, b_4, b_6, b_8]$. + pub fn b_invariants(&self) -> [F; 4] { + [self.b2(), self.b4(), self.b6(), self.b8()] + } - // Solve y^2 + b y = rhs via discriminant: - // disc = b^2 + 4 rhs - let disc = ::square(&b) - + ::double(&::double(&rhs)); + // ------------------------------------------------------------------- + // Curve predicates + // ------------------------------------------------------------------- + + /// Checks whether the affine point $(x, y)$ satisfies the curve equation. + /// + /// $$ + /// y^2 + a_1 xy + a_3 y = x^3 + a_2 x^2 + a_4 x + a_6. + /// $$ + pub fn contains(&self, x: &F, y: &F) -> bool { + let lhs = { + let y2 = ::square(y); + let a1xy = &self.a1 * &(x * y); + let a3y = &self.a3 * y; + &y2 + &(&a1xy + &a3y) + }; + + let rhs = { + let x2 = ::square(x); + let x3 = x * &x2; + let a2x2 = &self.a2 * &x2; + let a4x = &self.a4 * x; + &x3 + &(&a2x2 + &(&a4x + &self.a6)) + }; + + lhs == rhs + } - if let Some(sqrt_disc) = disc.sqrt().into_option() { - let y = (-b + sqrt_disc) * two_inv; - let p = AffinePoint::new(x, y); - debug_assert!(self.is_on_curve(&p)); - return p; + /// Sample a random affine point on this curve using the provided RNG. + /// + /// This currently uses a square-root-based construction and is implemented + /// only for odd characteristic. It returns a finite affine point `P` such + /// that `self.is_on_curve(&P)` holds. + pub fn random_point(&self, rng: &mut (impl rand::CryptoRng + rand::Rng)) -> AffinePoint{ + assert!(F::characteristic()[0] != 2, "random_point currently implemented for odd characteristic"); + + let two = ::double(&F::one()); + let two_inv = two.invert().into_option().expect("2 must be invertible"); + + loop { + let x = F::random(rng); + + let b = self.a1 * x + self.a3; + let x2 = ::square(&x); + let rhs = x * x2 + self.a2 * x2 + self.a4 * x + self.a6; + + // Solve y^2 + b y = rhs via discriminant: + // disc = b^2 + 4 rhs + let disc = ::square(&b) + + ::double(&::double(&rhs)); + + if let Some(sqrt_disc) = disc.sqrt().into_option() { + let y = (-b + sqrt_disc) * two_inv; + let p = AffinePoint::new(x, y); + debug_assert!(self.is_on_curve(&p)); + return p; + } } } } @@ -209,94 +213,99 @@ impl WeierstrassCurve { // Discriminant private helpers (Silverman, §III.1) // ------------------------------------------------------------------- -/// `b₂ = a₁² + 4a₂`. -fn b2_from_coeffs(a1: &F, a2: &F) -> F { - let a1_sq = ::square(&a1); - let four_a2 = ::double(&::double(&a2)); - a1_sq + four_a2 -} - -/// `b₄ = a₁a₃ + 2a₄`. -fn b4_from_coeffs(a1: &F, a3: &F, a4: &F) -> F { - let a1a3 = *a1 * *a3; - let two_a4 = ::double(&a4); - a1a3 + two_a4 -} - -/// `b₆ = a₃² + 4a₆`. -fn b6_from_coeffs(a3: &F, a6: &F) -> F { - let a3_sq = ::square(&a3); - let four_a6 = ::double(&::double(&a6)); - a3_sq + four_a6 -} - -/// `b₈ = a₁²a₆ + 4a₂a₆ − a₁a₃a₄ + a₂a₃² − a₄²`. -fn b8_from_coeffs(a1: &F, a2: &F, a3: &F, a4: &F, a6: &F) -> F { - let a1sq_a6 = ::square(&a1) * *a6; - let four_a2_a6 = ::double(&::double(&(*a2 * *a6))); - let a1_a3_a4 = *a1 * *a3 * *a4; - let a2_a3sq = *a2 * ::square(&a3); - let a4sq = ::square(&a4); - - a1sq_a6 + four_a2_a6 - a1_a3_a4 + a2_a3sq - a4sq -} - -/// The discriminant `Δ = −b₂²b₈ − 8b₄³ − 27b₆² + 9b₂b₄b₆`. -/// -/// The curve is non-singular if and only if `Δ ≠ 0`. -/// (Only meaningful when `char(F) ≠ 2, 3`.) -fn discriminant_from_coeffs(a1: &F, a2: &F, a3: &F, a4: &F, a6: &F) -> F { - let b2 = b2_from_coeffs::(&a1, &a2); - let b4 = b4_from_coeffs::(&a1, &a3, &a4); - let b6 = b6_from_coeffs::(&a3, &a6); - let b8 = b8_from_coeffs::(&a1, &a2, &a3, &a4, &a6); - - // Helper: build small integer constants from repeated doubling / adding - let two = ::double(&F::one()); - let three = two + F::one(); - let eight = ::double(&::double(&two)); - let nine = ::square(&three); - let twentyseven = nine * three; - - // −b₂²b₈ - let term1 = -::square(&b2) * b8; - - // −8b₄³ - let b4_cubed = ::square(&b4) * b4; - let term2 = -eight * b4_cubed; - - // −27b₆² - let term3 = -twentyseven * ::square(&b6); - - // 9b₂b₄b₆ - let term4 = nine * b2 * b4 * b6; - - term1 + term2 + term3 + term4 -} - -/// `c_4 = b_2² - 24b_4`. -fn c4_from_coeffs(a1: &F, a2: &F, a3: &F, a4: &F) -> F { - let b2 = b2_from_coeffs::(&a1, &a2); - let b4 = b4_from_coeffs::(&a1, &a3, &a4); - let b2_sq = ::square(&b2); - - // Helper: build small integer constants from repeated doubling / adding - let two = ::double(&F::one()); - let three = two + F::one(); - let eight = ::double(&::double(&two)); - let twentyfour = eight * three; - - b2_sq - twentyfour * b4 -} - -fn j_inv_from_coeffs(a1: &F, a2: &F, a3: &F, a4: &F, a6: &F) -> F { - let c4 = c4_from_coeffs::(&a1, &a2, &a3, &a4); - let c4_sq = ::square(&c4); - let c4_cubed = c4 * c4_sq; - - let delta = discriminant_from_coeffs::(&a1, &a2, &a3, &a4, &a6); - let delta_inv = ::invert(&delta).unwrap(); - c4_cubed * delta_inv +ref_field_fns!{ + /// `b₂ = a₁² + 4a₂`. + fn b2_from_coeffs(a1: &F, a2: &F) -> F { + let a1_sq = ::square(&a1); + let four_a2 = ::double(&::double(&a2)); + &a1_sq + &four_a2 + } + + /// `b₄ = a₁a₃ + 2a₄`. + fn b4_from_coeffs(a1: &F, a3: &F, a4: &F) -> F { + let a1a3 = a1 * a3; + let two_a4 = ::double(a4); + &a1a3 + &two_a4 + } + + /// `b₆ = a₃² + 4a₆`. + fn b6_from_coeffs(a3: &F, a6: &F) -> F { + let a3_sq = ::square(&a3); + let four_a6 = ::double(&::double(&a6)); + &a3_sq + &four_a6 + } + + /// `b₈ = a₁²a₆ + 4a₂a₆ − a₁a₃a₄ + a₂a₃² − a₄²`. + fn b8_from_coeffs(a1: &F, a2: &F, a3: &F, a4: &F, a6: &F) -> F { + let a1sq_a6 = &::square(&a1) * a6; + let four_a2_a6 = ::double(&::double(&(a2 * a6))); + let a1_a3_a4 = a1 * &(a3 * a4); + let a2_a3sq = a2 * &::square(&a3); + let a4sq = ::square(&a4); + + &a1sq_a6 + &(&four_a2_a6 - &(&a1_a3_a4 + &(&a2_a3sq - &a4sq))) + } + + /// The discriminant `Δ = −b₂²b₈ − 8b₄³ − 27b₆² + 9b₂b₄b₆`. + /// + /// The curve is non-singular if and only if `Δ ≠ 0`. + /// (Only meaningful when `char(F) ≠ 2, 3`.) + fn discriminant_from_coeffs(a1: &F, a2: &F, a3: &F, a4: &F, a6: &F) -> F { + let b2 = b2_from_coeffs::(&a1, &a2); + let b4 = b4_from_coeffs::(&a1, &a3, &a4); + let b6 = b6_from_coeffs::(&a3, &a6); + let b8 = b8_from_coeffs::(&a1, &a2, &a3, &a4, &a6); + + // Helper: build small integer constants from repeated doubling / adding + let two = ::double(&F::one()); + let three = &two + &F::one(); + let eight = ::double(&::double(&two)); + let nine = ::square(&three); + let twentyseven = &nine * &three; + + // −b₂²b₈ + let term1 = -::square(&b2) * b8; + + // −8b₄³ + let b4_cubed = &::square(&b4) * &b4; + let term2 = -&(&eight * &b4_cubed); + + // −27b₆² + let term3 = -twentyseven * ::square(&b6); + + // 9b₂b₄b₆ + let term4 = &nine * &(&b2 * &(&b4 * &b6)); + + let tmp1 = &term1 + &term2; + let tmp2 = &tmp1 + &term3; + &tmp2 + &term4 + } + + /// `c₄ = b₂² - 24b₄`. + fn c4_from_coeffs(a1: &F, a2: &F, a3: &F, a4: &F) -> F { + let b2 = b2_from_coeffs::(a1, a2); + let b4 = b4_from_coeffs::(a1, a3, a4); + let b2_sq = ::square(&b2); + + let two = ::double(&F::one()); + let three = &two + &F::one(); + let four = ::double(&two); + let eight = ::double(&four); + let twentyfour = &eight * &three; + + let twentyfour_b4 = &twentyfour * &b4; + &b2_sq - &twentyfour_b4 + } + + fn j_inv_from_coeffs(a1: &F, a2: &F, a3: &F, a4: &F, a6: &F) -> F { + let c4 = c4_from_coeffs::(a1, a2, a3, a4); + let c4_sq = ::square(&c4); + let c4_cubed = &c4 * &c4_sq; + + let delta = discriminant_from_coeffs::(a1, a2, a3, a4, a6); + let delta_inv = ::invert(&delta).unwrap(); + &c4_cubed * &delta_inv + } } // ------------------------------------------------------------------- @@ -340,6 +349,7 @@ impl WeierstrassCurve { // Curve predicates // ------------------------------------------------------------------- + impl Curve for WeierstrassCurve { type BaseField = F; type Point = AffinePoint; @@ -372,3 +382,55 @@ impl Curve for WeierstrassCurve { WeierstrassCurve::a_invariants(self).to_vec() } } + + + +// --------------------------------------------------------------------------- +// Constant-time functionalities +// --------------------------------------------------------------------------- + +impl ConditionallySelectable for WeierstrassCurve +where + F: FieldOps + Copy, +{ + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Self{ + a1: F::conditional_select(& a.a1, &b.a1, choice), + a2: F::conditional_select(& a.a2, &b.a2, choice), + a3: F::conditional_select(& a.a3, &b.a3, choice), + a4: F::conditional_select(& a.a4, &b.a4, choice), + a6: F::conditional_select(& a.a6, &b.a6, choice), + } + } + + fn conditional_assign(&mut self, other: &Self, choice: Choice) { + F::conditional_assign(&mut self.a1, &other.a1, choice); + F::conditional_assign(&mut self.a2, &other.a2, choice); + F::conditional_assign(&mut self.a3, &other.a3, choice); + F::conditional_assign(&mut self.a4, &other.a4, choice); + F::conditional_assign(&mut self.a6, &other.a6, choice); + } + + fn conditional_swap(a: &mut Self, b: &mut Self, choice: Choice) { + F::conditional_swap(&mut a.a1, &mut b.a1, choice); + F::conditional_swap(&mut a.a2, &mut b.a2, choice); + F::conditional_swap(&mut a.a3, &mut b.a3, choice); + F::conditional_swap(&mut a.a4, &mut b.a4, choice); + F::conditional_swap(&mut a.a6, &mut b.a6, choice); + } +} + +impl ConstantTimeEq for WeierstrassCurve +where + F: FieldOps + Copy + ConstantTimeEq, +{ + fn ct_eq(&self, other: &Self) -> Choice { + self.a1.ct_eq(&other.a1) & + self.a2.ct_eq(&other.a2) & + self.a3.ct_eq(&other.a3) & + self.a4.ct_eq(&other.a4) & + self.a6.ct_eq(&other.a6) + } + + fn ct_ne(&self, other: &Self) -> Choice { !self.ct_eq(other) } +} \ No newline at end of file diff --git a/ec/src/point_edwards.rs b/ec/src/point_edwards.rs index acce732..715ec10 100644 --- a/ec/src/point_edwards.rs +++ b/ec/src/point_edwards.rs @@ -40,6 +40,7 @@ use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; use crate::curve_edwards::EdwardsCurve; use crate::point_ops::PointOps; use fp::field_ops::FieldOps; +use fp::{ref_field_impl, ref_field_trait_impl}; /// An affine point on an Edwards curve, for any characteristic. #[derive(Debug, Clone, Copy)] @@ -90,7 +91,7 @@ impl Eq for EdwardsPoint {} // --------------------------------------------------------------------------- impl EdwardsPoint { - /// Construct an affine Edwards point. No on-curve check. + /// Construct an affine Edwards point. No on-curve check. pub fn new(x: F, y: F) -> Self { Self { x, y } } @@ -165,72 +166,61 @@ where // Group operations // --------------------------------------------------------------------------- -impl EdwardsPoint { - /// Negate a point. - /// - Odd char: `-(x, y) = (-x, y)` - /// - Char 2: `-(x, y) = (y, x)` - pub fn negate(&self, _curve: &EdwardsCurve) -> Self { - if F::characteristic()[0] != 2 { - Self::new(-self.x, self.y) - } else { - Self::new(self.y, self.x) - } - } - - /// Add two points on the Edwards curve. - pub fn add(&self, other: &Self, curve: &EdwardsCurve) -> Self { - if F::characteristic()[0] != 2 { - self.add_odd(other, curve) - } else { - self.add_binary(other, curve) +ref_field_impl! { + impl EdwardsPoint { + /// Negate a point. + /// - Odd char: `-(x, y) = (-x, y)` + /// - Char 2: `-(x, y) = (y, x)` + pub fn negate(&self, _curve: &EdwardsCurve) -> Self { + if F::characteristic()[0] != 2 { + Self::new(-&self.x, self.y.clone()) + } else { + Self::new(self.y.clone(), self.x.clone()) + } } - } - /// Double a point. Both addition laws are strongly unified, so this - /// just delegates to `add`. - pub fn double(&self, curve: &EdwardsCurve) -> Self { - self.add(self, curve) - } - - /// Scalar multiplication `[k]P` (constant-time double-and-add). - pub fn scalar_mul(&self, k: &[u64], curve: &EdwardsCurve) -> Self { - let mut result = Self::identity(); - - for &limb in k.iter().rev() { - for bit in (0..64).rev() { - let doubled = result.double(curve); - let added = doubled.add(self, curve); - let choice = Choice::from(((limb >> bit) & 1) as u8); - result = Self::conditional_select(&doubled, &added, choice); + /// Add two points on the Edwards curve. + pub fn add(&self, other: &Self, curve: &EdwardsCurve) -> Self { + if F::characteristic()[0] != 2 { + self.add_odd(other, curve) + } else { + self.add_binary(other, curve) } } - result - } - - // ----------------------------------------------------------------------- - // Odd-characteristic addition - // - // x₃ = (x₁y₂ + y₁x₂) / (1 + d·x₁x₂y₁y₂) - // y₃ = (y₁y₂ − x₁x₂) / (1 − d·x₁x₂y₁y₂) - // ----------------------------------------------------------------------- - - fn add_odd(&self, other: &Self, curve: &EdwardsCurve) -> Self { - let d = curve.d2; + /// Double a point. Both addition laws are strongly unified, so this + /// just delegates to `add`. + pub fn double(&self, curve: &EdwardsCurve) -> Self { + self.add(self, curve) + } - let x1y2 = self.x * other.y; - let y1x2 = self.y * other.x; - let x1x2 = self.x * other.x; - let y1y2 = self.y * other.y; + /// Scalar multiplication `[k]P` (constant-time double-and-add). + pub fn scalar_mul(&self, k: &[u64], curve: &EdwardsCurve) -> Self { + let mut result = Self::identity(); - let dxy = d * x1x2 * y1y2; + for &limb in k.iter().rev() { + for bit in (0..64).rev() { + let doubled = result.double(curve); + let added = doubled.add(self, curve); + let choice = Choice::from(((limb >> bit) & 1) as u8); + result = Self::conditional_select(&doubled, &added, choice); + } + } - let one = F::one(); - let x_num = x1y2 + y1x2; - let x_den = one + dxy; - let y_num = y1y2 - x1x2; - let y_den = one - dxy; + result + } + // ----------------------------------------------------------------------- + // Odd-characteristic addition + // ----------------------------------------------------------------------- + +<<<<<<< HEAD + fn add_odd(&self, other: &Self, curve: &EdwardsCurve) -> Self { + let x1y2 = &self.x * &other.y; + let y1x2 = &self.y * &other.x; + let x1x2 = &self.x * &other.x; + let y1y2 = &self.y * &other.y; +======= let x_den_inv = x_den .invert() .into_option() @@ -239,40 +229,45 @@ impl EdwardsPoint { .invert() .into_option() .expect("Edwards addition: y-denominator must be invertible"); +>>>>>>> origin/main - Self::new(x_num * x_den_inv, y_num * y_den_inv) - } + let x1x2y1y2 = &x1x2 * &y1y2; + let dxy = &curve.d2 * &x1x2y1y2; + + let one = F::one(); + let x_num = &x1y2 + &y1x2; + let x_den = &one + &dxy; + let y_num = &y1y2 - &x1x2; + let y_den = &one - &dxy; + + let x_den_inv = ::invert(&x_den) + .into_option() + .expect("Edwards addition: x-denominator must be invertible"); + let y_den_inv = ::invert(&y_den) + .into_option() + .expect("Edwards addition: y-denominator must be invertible"); + + let x3 = &x_num * &x_den_inv; + let y3 = &y_num * &y_den_inv; + + Self::new(x3, y3) + } - // ----------------------------------------------------------------------- - // Characteristic-2 addition (Bernstein–Lange–Rezaeian Farashahi §3) - // - // w₁ = x₁+y₁, w₂ = x₂+y₂ - // A = x₁²+x₁, B = y₁²+y₁ - // - // x₃ = (d₁(x₁+x₂) + d₂·w₁·w₂ + A·(x₂(y₁+y₂+1)+y₁y₂)) - // / (d₁ + A·w₂) - // y₃ = (d₁(y₁+y₂) + d₂·w₁·w₂ + B·(y₂(x₁+x₂+1)+x₁x₂)) - // / (d₁ + B·w₂) - // ----------------------------------------------------------------------- - - fn add_binary(&self, other: &Self, curve: &EdwardsCurve) -> Self { - let x1 = self.x; - let y1 = self.y; - let x2 = other.x; - let y2 = other.y; - let d1 = curve.d1; - let d2 = curve.d2; - - let w1 = x1 + y1; - let w2 = x2 + y2; - - let a = ::square(&x1) + x1; - let b = ::square(&y1) + y1; - - let d2_w1w2 = d2 * w1 * w2; - let x1x2 = x1 * x2; - let y1y2 = y1 * y2; + // ----------------------------------------------------------------------- + // Characteristic-2 addition + // ----------------------------------------------------------------------- +<<<<<<< HEAD + fn add_binary(&self, other: &Self, curve: &EdwardsCurve) -> Self { + let w1 = &self.x + &self.y; + let w2 = &other.x + &other.y; + + let x1_sq = ::square(&self.x); + let a = &x1_sq + &self.x; + + let y1_sq = ::square(&self.y); + let b = &y1_sq + &self.y; +======= let x_num = d1 * (x1 + x2) + d2_w1w2 + a * (x2 * (y1 + y2 + F::one()) + y1y2); let x_den = d1 + a * w2; @@ -287,25 +282,126 @@ impl EdwardsPoint { .invert() .into_option() .expect("binary Edwards addition: y-denom must be invertible"); +>>>>>>> origin/main + + let w1w2 = &w1 * &w2; + let d2_w1w2 = &curve.d2 * &w1w2; + + let x1x2 = &self.x * &other.x; + let y1y2 = &self.y * &other.y; + + let x1_plus_x2 = &self.x + &other.x; + let y1_plus_y2 = &self.y + &other.y; + let one = F::one(); + + let ysum_plus_one = &y1_plus_y2 + &one; + let x_part = &other.x * &ysum_plus_one; + let x_paren = &x_part + &y1y2; + let ax = &a * &x_paren; + let d1x = &curve.d1 * &x1_plus_x2; + let x_num_tmp = &d1x + &d2_w1w2; + let x_num = &x_num_tmp + &ax; + let x_den = &curve.d1 + &(&a * &w2); + + let xsum_plus_one = &x1_plus_x2 + &one; + let y_part = &other.y * &xsum_plus_one; + let y_paren = &y_part + &x1x2; + let by = &b * &y_paren; + let d1y = &curve.d1 * &y1_plus_y2; + let y_num_tmp = &d1y + &d2_w1w2; + let y_num = &y_num_tmp + &by; + let y_den = &curve.d1 + &(&b * &w2); + + let x_den_inv = ::invert(&x_den) + .into_option() + .expect("binary Edwards addition: x-denom must be invertible"); + let y_den_inv = ::invert(&y_den) + .into_option() + .expect("binary Edwards addition: y-denom must be invertible"); + + let x3 = &x_num * &x_den_inv; + let y3 = &y_num * &y_den_inv; + + Self::new(x3, y3) + } + + // ----------------------------------------------------------------------- + // w-coordinate helpers for char-2 Montgomery ladder + // ----------------------------------------------------------------------- + + /// Differential addition on the `w`-line (`w = x + y`, char 2 only). + /// + /// Given `w₁ = w(Q−P)`, `w₂ = w(P)`, `w₃ = w(Q)`, compute `w₅ = w(P+Q)`. + pub fn w_diff_add(w1: &F, w2: &F, w3: &F, curve: &EdwardsCurve) -> F { + let r = w2 * w3; + let s = ::square(&r); + + let one = F::one(); + let tmp1 = &one + w2; + let tmp2 = &tmp1 + w3; + let rt = &r * &tmp2; + let t = &rt + &s; + + let d1_inv = ::invert(&curve.d1) + .into_option() + .expect("d1 invertible"); + let d2_over_d1 = &curve.d2 * &d1_inv; + let coeff = &d2_over_d1 + &one; + + let coeff_s = &coeff * &s; + let den_tmp = &curve.d1 + &t; + let den = &den_tmp + &coeff_s; + let den_inv = ::invert(&den) + .into_option() + .expect("w-diff-add denominator invertible"); + + let frac = &t * &den_inv; + &frac + w1 + } - Self::new(x_num * x_den_inv, y_num * y_den_inv) + /// `w`-coordinate doubling (`w = x + y`, char 2 only). + /// + /// Given `w₂ = w(P)`, compute `w₄ = w(2P)`. + pub fn w_double(w2: &F, curve: &EdwardsCurve) -> F { + let a = ::square(w2); + let j = ::square(&a); + + let d1_inv = ::invert(&curve.d1) + .into_option() + .expect("d1 invertible"); + let d2_over_d1 = &curve.d2 * &d1_inv; + + let num = &a + &j; + let coeff_j = &d2_over_d1 * &j; + let den_tmp = &curve.d1 + &a; + let den = &den_tmp + &coeff_j; + let den_inv = ::invert(&den) + .into_option() + .expect("w-double denominator invertible"); + + &num * &den_inv + } } } -// --------------------------------------------------------------------------- -// w-coordinate helpers for char-2 Montgomery ladder -// --------------------------------------------------------------------------- +ref_field_trait_impl! { + impl PointOps for EdwardsPoint { + type BaseField = F; + type Curve = EdwardsCurve; -impl EdwardsPoint { - /// Differential addition on the `w`-line (`w = x + y`, char 2 only). - /// - /// Given `w₁ = w(Q−P)`, `w₂ = w(P)`, `w₃ = w(Q)`, compute `w₅ = w(P+Q)`. - pub fn w_diff_add(w1: &F, w2: &F, w3: &F, curve: &EdwardsCurve) -> F { - let r = *w2 * *w3; - let s = ::square(&r); - let one = F::one(); - let t = r * (one + *w2 + *w3) + s; + fn identity(_curve: &Self::Curve) -> Self { + EdwardsPoint::::identity() + } +<<<<<<< HEAD + fn is_identity(&self) -> bool { + EdwardsPoint::::is_identity(self) + } + + fn negate(&self, curve: &Self::Curve) -> Self { + EdwardsPoint::::negate(self, curve) + } +======= let d2_over_d1 = curve.d2 * curve.d1.invert().into_option().expect("d1 invertible"); let coeff = d2_over_d1 + one; @@ -314,9 +410,15 @@ impl EdwardsPoint { .invert() .into_option() .expect("w-diff-add denominator invertible"); +>>>>>>> origin/main - t * den_inv + *w1 + fn scalar_mul(&self, k: &[u64], curve: &Self::Curve) -> Self { + EdwardsPoint::::scalar_mul(self, k, curve) + } } +<<<<<<< HEAD +} +======= /// `w`-coordinate doubling (`w = x + y`, char 2 only). /// @@ -362,3 +464,4 @@ impl PointOps for EdwardsPoint { EdwardsPoint::::scalar_mul(self, k, curve) } } +>>>>>>> origin/main diff --git a/ec/src/point_jacobi_intersection.rs b/ec/src/point_jacobi_intersection.rs index 3998c03..076a0c5 100644 --- a/ec/src/point_jacobi_intersection.rs +++ b/ec/src/point_jacobi_intersection.rs @@ -15,6 +15,7 @@ use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; use crate::curve_jacobi_intersection::JacobiIntersectionCurve; use crate::point_ops::{PointAdd, PointOps}; use fp::field_ops::FieldOps; +use fp::{ref_field_impl, ref_field_trait_impl}; /// An affine point $(s, c, d)$ on a Jacobi intersection. #[derive(Debug, Clone, Copy)] @@ -142,6 +143,13 @@ where } } +<<<<<<< HEAD +ref_field_impl! { + impl JacobiIntersectionPoint { + /// Negation: `-(s, c, d) = (-s, c, d)`. + pub fn negate(&self, _curve: &JacobiIntersectionCurve) -> Self { + Self::new(-&self.s, self.c.clone(), self.d.clone()) +======= impl JacobiIntersectionPoint { /// Negation: `-(s, c, d) = (-s, c, d)`. pub fn negate(&self, _curve: &JacobiIntersectionCurve) -> Self { @@ -222,35 +230,132 @@ impl JacobiIntersectionPoint { let choice = Choice::from(((limb >> bit) & 1) as u8); result = Self::conditional_select(&doubled, &added, choice); } +>>>>>>> origin/main } - result + /// Affine addition formulas from the EFD: + /// + /// ```text + /// s₃ = (c₂ s₁ d₂ + d₁ s₂ c₁)/(c₂² + (d₁ s₂)²) + /// c₃ = (c₂ c₁ - d₁ s₂ s₁ d₂)/(c₂² + (d₁ s₂)²) + /// d₃ = (d₁ d₂ - a s₁ c₁ s₂ c₂)/(c₂² + (d₁ s₂)²). + /// ``` + pub fn add(&self, other: &Self, curve: &JacobiIntersectionCurve) -> Self { + let d1s2 = &self.d * &other.s; + let other_c_sq = ::square(&other.c); + let d1s2_sq = ::square(&d1s2); + let denom = &other_c_sq + &d1s2_sq; + let denom_inv = ::invert(&denom) + .into_option() + .expect("Jacobi-intersection addition denominator vanished"); + + let s_term1_tmp = &other.c * &self.s; + let s_term1 = &s_term1_tmp * &other.d; + let s_term2_tmp = &self.d * &other.s; + let s_term2 = &s_term2_tmp * &self.c; + let s_num = &s_term1 + &s_term2; + let s3 = &s_num * &denom_inv; + + let c_term1 = &other.c * &self.c; + let c_term2_tmp1 = &self.d * &other.s; + let c_term2_tmp2 = &c_term2_tmp1 * &self.s; + let c_term2 = &c_term2_tmp2 * &other.d; + let c_num = &c_term1 - &c_term2; + let c3 = &c_num * &denom_inv; + + let d_term1 = &self.d * &other.d; + let d_term2_tmp1 = &curve.a * &self.s; + let d_term2_tmp2 = &d_term2_tmp1 * &self.c; + let d_term2_tmp3 = &d_term2_tmp2 * &other.s; + let d_term2 = &d_term2_tmp3 * &other.c; + let d_num = &d_term1 - &d_term2; + let d3 = &d_num * &denom_inv; + + Self::new(s3, c3, d3) + } + + /// Affine doubling formulas from the EFD: + /// + /// ```text + /// s₃ = 2c s d / (c² + (d s)²) + /// c₃ = (c² - d² s²) / (c² + (d s)²) + /// d₃ = (d² - a s² c²) / (c² + (d s)²). + /// ``` + pub fn double(&self, curve: &JacobiIntersectionCurve) -> Self { + let ds = &self.d * &self.s; + let c_sq = ::square(&self.c); + let ds_sq = ::square(&ds); + let denom = &c_sq + &ds_sq; + let denom_inv = ::invert(&denom) + .into_option() + .expect("Jacobi-intersection doubling denominator vanished"); + + let one = F::one(); + let two = ::double(&one); + let s_sq = ::square(&self.s); + let d_sq = ::square(&self.d); + + let s3_tmp1 = &two * &self.c; + let s3_tmp2 = &s3_tmp1 * &self.s; + let s3_num = &s3_tmp2 * &self.d; + let s3 = &s3_num * &denom_inv; + + let d_sq_s_sq = &d_sq * &s_sq; + let c3_num = &c_sq - &d_sq_s_sq; + let c3 = &c3_num * &denom_inv; + + let s_sq_c_sq = &s_sq * &c_sq; + let a_s_sq_c_sq = &curve.a * &s_sq_c_sq; + let d3_num = &d_sq - &a_s_sq_c_sq; + let d3 = &d3_num * &denom_inv; + + Self::new(s3, c3, d3) + } + + pub fn scalar_mul(&self, k: &[u64], curve: &JacobiIntersectionCurve) -> Self { + let mut result = Self::identity(); + + for &limb in k.iter().rev() { + for bit in (0..64).rev() { + let doubled = result.double(curve); + let added = doubled.add(self, curve); + let choice = Choice::from(((limb >> bit) & 1) as u8); + result = Self::conditional_select(&doubled, &added, choice); + } + } + + result + } } } -impl PointOps for JacobiIntersectionPoint { - type BaseField = F; - type Curve = JacobiIntersectionCurve; +ref_field_trait_impl! { + impl PointOps for JacobiIntersectionPoint { + type BaseField = F; + type Curve = JacobiIntersectionCurve; - fn identity(_curve: &Self::Curve) -> Self { - JacobiIntersectionPoint::::identity() - } + fn identity(_curve: &Self::Curve) -> Self { + JacobiIntersectionPoint::::identity() + } - fn is_identity(&self) -> bool { - JacobiIntersectionPoint::::is_identity(self) - } + fn is_identity(&self) -> bool { + JacobiIntersectionPoint::::is_identity(self) + } - fn negate(&self, curve: &Self::Curve) -> Self { - JacobiIntersectionPoint::::negate(self, curve) - } + fn negate(&self, curve: &Self::Curve) -> Self { + JacobiIntersectionPoint::::negate(self, curve) + } - fn scalar_mul(&self, k: &[u64], curve: &Self::Curve) -> Self { - JacobiIntersectionPoint::::scalar_mul(self, k, curve) + fn scalar_mul(&self, k: &[u64], curve: &Self::Curve) -> Self { + JacobiIntersectionPoint::::scalar_mul(self, k, curve) + } } } -impl PointAdd for JacobiIntersectionPoint { - fn add(&self, other: &Self, curve: &Self::Curve) -> Self { - JacobiIntersectionPoint::::add(self, other, curve) +ref_field_trait_impl! { + impl PointAdd for JacobiIntersectionPoint { + fn add(&self, other: &Self, curve: &Self::Curve) -> Self { + JacobiIntersectionPoint::::add(self, other, curve) + } } -} +} \ No newline at end of file diff --git a/ec/src/point_jacobi_quartic.rs b/ec/src/point_jacobi_quartic.rs index 3b8a21a..e6cebb1 100644 --- a/ec/src/point_jacobi_quartic.rs +++ b/ec/src/point_jacobi_quartic.rs @@ -21,6 +21,7 @@ use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; use crate::curve_jacobi_quartic::JacobiQuarticCurve; use crate::point_ops::{PointAdd, PointOps}; use fp::field_ops::FieldOps; +use fp::{ref_field_impl, ref_field_trait_impl}; /// An affine point `(x, y)` on a Jacobi quartic curve. #[derive(Debug, Clone, Copy)] @@ -57,7 +58,33 @@ impl PartialEq for JacobiQuarticPoint { } impl Eq for JacobiQuarticPoint {} +ref_field_impl! { + impl JacobiQuarticPoint { + pub fn new(x: F, y: F) -> Self { + Self { x, y } + } + +<<<<<<< HEAD + /// The neutral element `(0, 1)`. + pub fn identity() -> Self { + Self { + x: F::zero(), + y: F::one(), + } + } + + pub fn is_identity(&self) -> bool { + self.x == F::zero() && self.y == F::one() + } + /// The affine order-2 point `(0, -1)`. + pub fn order_two_point() -> Self { + let one = F::one(); + Self { + x: F::zero(), + y: - &one, + } +======= impl JacobiQuarticPoint { /// Create a new point `(x,y)` pub fn new(x: F, y: F) -> Self { @@ -82,6 +109,7 @@ impl JacobiQuarticPoint { Self { x: F::zero(), y: -F::one(), +>>>>>>> origin/main } } } @@ -121,127 +149,154 @@ where } } -impl JacobiQuarticPoint { - /// Negation on a Jacobi quartic: `-(x, y) = (-x, y)`. - pub fn negate(&self, _curve: &JacobiQuarticCurve) -> Self { - Self::new(-self.x, self.y) - } - - /// Dedicated affine doubling from equations (9) and (10): - /// - /// ```text - /// μ = 2y / (2 + 2ax² − y²) - /// x₃ = μx - /// y₃ = μ(μ − y) − 1. - /// ``` - /// - pub fn double(&self, curve: &JacobiQuarticCurve) -> Self { - if self.is_identity() { - return *self; +ref_field_impl! { + impl JacobiQuarticPoint { + /// Negation on a Jacobi quartic: `-(x, y) = (-x, y)`. + pub fn negate(&self, _curve: &JacobiQuarticCurve) -> Self { + Self::new(-&self.x, self.y.clone()) } - let x2 = ::square(&self.x); - let y2 = ::square(&self.y); - let two = ::double(&F::one()); - - let denom = two + two * curve.a * x2 - y2; - let denom_inv = denom.invert().into_option() - .expect("Jacobi quartic doubling denominator vanished; result leaves affine chart or input is exceptional"); - - let mu = two * self.y * denom_inv; - let x3 = mu * self.x; - let y3 = mu * (mu - self.y) - F::one(); + /// Dedicated affine doubling from equations (9) and (10): + /// + /// ```text + /// μ = 2y / (2 + 2ax² − y²) + /// x₃ = μx + /// y₃ = μ(μ − y) − 1. + /// ``` + pub fn double(&self, curve: &JacobiQuarticCurve) -> Self { + if self.is_identity() { + return *self; + } - Self::new(x3, y3) - } + let x2 = ::square(&self.x); + let y2 = ::square(&self.y); - /// Affine addition using equations (1) and (2): - /// - /// ```text - /// x₃ = (x₁y₂ + y₁x₂)/(1 − d x₁²x₂²) - /// y₃ = ((y₁y₂ + 2ax₁x₂)(1 + d x₁²x₂²) + 2d x₁x₂(x₁² + x₂²)) - /// /(1 − d x₁²x₂²)². - /// ``` - /// - pub fn add(&self, other: &Self, curve: &JacobiQuarticCurve) -> Self { - if self.is_identity() { - return *other; - } - if other.is_identity() { - return *self; - } - if *self == *other { - return self.double(curve); - } - if *other == self.negate(curve) { - return Self::identity(); - } + let one = F::one(); + let two = ::double(&one); - let x1 = self.x; - let y1 = self.y; - let x2 = other.x; - let y2 = other.y; + let ax2 = &curve.a * &x2; + let two_ax2 = &two * &ax2; + let denom_tmp = &two + &two_ax2; + let denom = &denom_tmp - &y2; + let denom_inv = ::invert(&denom) + .into_option() + .expect("Jacobi quartic doubling denominator vanished; result leaves affine chart or input is exceptional"); - let x1_sq = ::square(&x1); - let x2_sq = ::square(&x2); - let x1x2 = x1 * x2; - let y1y2 = y1 * y2; - let dx4 = curve.d * x1_sq * x2_sq; - let two = ::double(&F::one()); + let two_y = &two * &self.y; + let mu = &two_y * &denom_inv; + let x3 = &mu * &self.x; - let denom = F::one() - dx4; - let denom_inv = denom.invert().into_option() - .expect("Jacobi quartic addition denominator vanished; choose d nonsquare / odd-order subgroup or use a projective model"); + let mu_minus_y = &mu - &self.y; + let mu_times = &mu * &mu_minus_y; + let y3 = &mu_times - &one; - let x3 = (x1 * y2 + y1 * x2) * denom_inv; - - let numer_y = (y1y2 + two * curve.a * x1x2) * (F::one() + dx4) - + two * curve.d * x1x2 * (x1_sq + x2_sq); - let y3 = numer_y * ::square(&denom_inv); + Self::new(x3, y3) + } - Self::new(x3, y3) - } + /// Affine addition using equations (1) and (2): + /// + /// ```text + /// x₃ = (x₁y₂ + y₁x₂)/(1 − d x₁²x₂²) + /// y₃ = ((y₁y₂ + 2ax₁x₂)(1 + d x₁²x₂²) + 2d x₁x₂(x₁² + x₂²)) + /// /(1 − d x₁²x₂²)². + /// ``` + pub fn add(&self, other: &Self, curve: &JacobiQuarticCurve) -> Self { + if self.is_identity() { + return *other; + } + if other.is_identity() { + return *self; + } + if *self == *other { + return self.double(curve); + } + if *other == self.negate(curve) { + return Self::identity(); + } - /// Constant-time double-and-add in the same style as the Edwards code. - pub fn scalar_mul(&self, k: &[u64], curve: &JacobiQuarticCurve) -> Self { - let mut result = Self::identity(); + let x1_sq = ::square(&self.x); + let x2_sq = ::square(&other.x); + let x1x2 = &self.x * &other.x; + let y1y2 = &self.y * &other.y; + let x1_sq_x2_sq = &x1_sq * &x2_sq; + let dx4 = &curve.d * &x1_sq_x2_sq; + + let one = F::one(); + let two = ::double(&one); + + let denom = &one - &dx4; + let denom_inv = ::invert(&denom) + .into_option() + .expect("Jacobi quartic addition denominator vanished; choose d nonsquare / odd-order subgroup or use a projective model"); + + let x1y2 = &self.x * &other.y; + let y1x2 = &self.y * &other.x; + let x_num = &x1y2 + &y1x2; + let x3 = &x_num * &denom_inv; + + let two_a = &two * &curve.a; + let two_a_x1x2 = &two_a * &x1x2; + let y_part = &y1y2 + &two_a_x1x2; + let one_plus_dx4 = &one + &dx4; + let first_term = &y_part * &one_plus_dx4; + + let x_sq_sum = &x1_sq + &x2_sq; + let two_d = &two * &curve.d; + let two_d_x1x2 = &two_d * &x1x2; + let second_term = &two_d_x1x2 * &x_sq_sum; + + let numer_y = &first_term + &second_term; + let denom_inv_sq = ::square(&denom_inv); + let y3 = &numer_y * &denom_inv_sq; + + Self::new(x3, y3) + } - for &limb in k.iter().rev() { - for bit in (0..64).rev() { - let doubled = result.double(curve); - let added = doubled.add(self, curve); - let choice = Choice::from(((limb >> bit) & 1) as u8); - result = Self::conditional_select(&doubled, &added, choice); + /// Constant-time double-and-add in the same style as the Edwards code. + pub fn scalar_mul(&self, k: &[u64], curve: &JacobiQuarticCurve) -> Self { + let mut result = Self::identity(); + + for &limb in k.iter().rev() { + for bit in (0..64).rev() { + let doubled = result.double(curve); + let added = doubled.add(self, curve); + let choice = Choice::from(((limb >> bit) & 1) as u8); + result = Self::conditional_select(&doubled, &added, choice); + } } - } - result + result + } } } -impl PointOps for JacobiQuarticPoint { - type BaseField = F; - type Curve = JacobiQuarticCurve; +ref_field_trait_impl! { + impl PointOps for JacobiQuarticPoint { + type BaseField = F; + type Curve = JacobiQuarticCurve; - fn identity(_curve: &Self::Curve) -> Self { - JacobiQuarticPoint::::identity() - } + fn identity(_curve: &Self::Curve) -> Self { + JacobiQuarticPoint::::identity() + } - fn is_identity(&self) -> bool { - JacobiQuarticPoint::::is_identity(self) - } + fn is_identity(&self) -> bool { + JacobiQuarticPoint::::is_identity(self) + } - fn negate(&self, curve: &Self::Curve) -> Self { - JacobiQuarticPoint::::negate(self, curve) - } + fn negate(&self, curve: &Self::Curve) -> Self { + JacobiQuarticPoint::::negate(self, curve) + } - fn scalar_mul(&self, k: &[u64], curve: &Self::Curve) -> Self { - JacobiQuarticPoint::::scalar_mul(self, k, curve) + fn scalar_mul(&self, k: &[u64], curve: &Self::Curve) -> Self { + JacobiQuarticPoint::::scalar_mul(self, k, curve) + } } } -impl PointAdd for JacobiQuarticPoint { - fn add(&self, other: &Self, curve: &Self::Curve) -> Self { - JacobiQuarticPoint::::add(self, other, curve) +ref_field_trait_impl! { + impl PointAdd for JacobiQuarticPoint { + fn add(&self, other: &Self, curve: &Self::Curve) -> Self { + JacobiQuarticPoint::::add(self, other, curve) + } } -} +} \ No newline at end of file diff --git a/ec/src/point_montgomery.rs b/ec/src/point_montgomery.rs index 249716b..22624be 100644 --- a/ec/src/point_montgomery.rs +++ b/ec/src/point_montgomery.rs @@ -33,7 +33,12 @@ use subtle::{Choice, CtOption, ConditionallySelectable, ConstantTimeEq}; use crate::curve_montgomery::MontgomeryCurve; use crate::point_ops::PointOps; +<<<<<<< HEAD +use fp::field_ops::FieldOps; +use fp::{ref_field_impl, ref_field_trait_impl}; +======= use fp::field_ops::{FieldOps}; +>>>>>>> origin/main /// A point on the Kummer line of a Montgomery curve, represented by `(X : Z)`. /// @@ -53,6 +58,28 @@ pub struct KummerPoint { // Manual trait impls // --------------------------------------------------------------------------- +<<<<<<< HEAD +ref_field_trait_impl! { + impl PartialEq for KummerPoint { + /// Equality of projective x-line points. + /// + /// A standard criterion is cross-multiplication: + /// ```text + /// X1 Z2 = X2 Z1. + /// ``` + fn eq(&self, other: &Self) -> bool { + let lhs = &self.x * &other.z; + let rhs = &other.x * &self.z; + lhs == rhs + } + } +} + +ref_field_trait_impl! { + impl Eq for KummerPoint + { } +} +======= impl fmt::Display for KummerPoint where F: FieldOps + Copy + fmt::Display, @@ -99,11 +126,32 @@ where } impl Eq for KummerPoint where F: FieldOps + ConstantTimeEq {} +>>>>>>> origin/main // --------------------------------------------------------------------------- // Constructors // --------------------------------------------------------------------------- +<<<<<<< HEAD +ref_field_impl! { + impl KummerPoint { + /// Construct a projective x-line point `(X : Z)` without validation. + pub fn new(x: F, z: F) -> Self { + assert!(!bool::from((&x * &z).is_zero())); + Self{ x, z } + } + + /// Construct the finite x-line point corresponding to the affine + /// x-coordinate `x`, i.e. `(x : 1)`. + pub fn from_x(x: F) -> Self { + Self{ x, z: F::one() } + } + + /// The image of the identity point on the Kummer line. + pub fn identity() -> Self { + Self{ x: F::one(), z: F::zero()} + } +======= impl KummerPoint { /// Construct a projective x-line point `(X : Z)` without validation. pub fn new(x: F, z: F) -> Self { @@ -124,16 +172,18 @@ impl KummerPoint { z: F::zero(), } } +>>>>>>> origin/main - /// Return `true` if this point is the image of identity. - pub fn is_identity(&self) -> bool { - bool::from(self.z.is_zero()) - } + /// Return `true` if this point is the image of identity. + pub fn is_identity(&self) -> bool { + bool::from(self.z.is_zero()) + } - /// Attempt to recover the affine x-coordinate (succeeds when Z != 0). - pub fn to_x(&self) -> CtOption { - let z_inv = self.z.invert(); - z_inv.map(|zinv| self.x * zinv) + /// Attempt to recover the affine x-coordinate (succeeds when Z != 0). + pub fn to_x(&self) -> CtOption { + let z_inv = self.z.invert(); + z_inv.map(|zinv| &self.x * &zinv) + } } } @@ -165,15 +215,17 @@ where impl ConstantTimeEq for KummerPoint where - F: FieldOps + Copy + ConstantTimeEq, + F: FieldOps + ConstantTimeEq, + for<'a, 'b> &'a F: std::ops::Mul<&'b F, Output = F>, { - /// Constant-time projective equality test on the Kummer line. - /// ```text - /// X1 Z2 ?= X2 Z1 - /// ``` fn ct_eq(&self, other: &Self) -> Choice { +<<<<<<< HEAD + let x1z2 = &self.x * &other.z; + let x2z1 = &other.x * &self.z; +======= let x1z2 = self.x * other.z; let x2z1 = other.x * self.z; +>>>>>>> origin/main x1z2.ct_eq(&x2z1) } @@ -186,19 +238,26 @@ where // X-only arithmetic // --------------------------------------------------------------------------- -impl KummerPoint { - /// Point doubling on the Kummer line. - /// - /// Given `x(P)` in projective form `(X:Z)`, compute `x([2]P)`. - pub fn xdouble(&self, curve: &MontgomeryCurve) -> Self { - if F::characteristic()[0] != 2 { - let sumsq = ::square(&(self.x + self.z)); - let diffsq = ::square(&(self.x - self.z)); - let fourxz = sumsq - diffsq; - let a24 = curve.a24(); - - let new_z = fourxz * (diffsq + a24 * fourxz); - +ref_field_impl! { + impl KummerPoint { + /// Point doubling on the Kummer line. + /// + /// Given `x(P)` in projective form `(X:Z)`, compute `x([2]P)`. + pub fn xdouble(&self, curve: &MontgomeryCurve) -> Self { + if F::characteristic()[0] != 2 { + let sum = &self.x + &self.z; + let diff = &self.x - &self.z; + + let sumsq = ::square(&sum); + let diffsq = ::square(&diff); + +<<<<<<< HEAD + let fourxz = &sumsq - &diffsq; + let a24 = curve.a24(); + + let tmp = &diffsq + &(&a24 * &fourxz); + let new_z = &fourxz * &tmp; +======= Self { x: sumsq * diffsq, z: new_z, @@ -211,22 +270,23 @@ impl KummerPoint { Self { x: new_x, z: new_z } } } +>>>>>>> origin/main - /// Differential addition. Given (in projective form `(X:Z)`): - /// - /// - `self = x(P)`, - /// - `other = x(Q)`, - /// - `diff = x(P - Q)`, - /// - /// compute `x(P + Q)`. - 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); + Self { + x: &sumsq * &diffsq, + z: new_z, + } + } else { + let temp_x = &self.x + &(&curve.b * &self.z); + let sq1 = ::square(&temp_x); + let new_x = ::square(&sq1); - let new_x = diff.z * ::square(&(u + v)); - let new_z = diff.x * ::square(&(u - v)); + let xz = &self.x * &self.z; + let new_z = ::square(&xz); +<<<<<<< HEAD + Self { x: new_x, z: new_z } +======= Self { x: new_x, z: new_z } } else { let x1z2 = self.x * other.z; @@ -276,13 +336,90 @@ impl KummerPoint { r0 = r0_dbl; r1 = r0_plus_r1; +>>>>>>> origin/main } } - // Final swap to undo the last deferred permutation. - Self::conditional_swap(&mut r0, &mut r1, swap); - r0 + /// Differential addition. Given (in projective form `(X:Z)`): + /// + /// - `self = x(P)`, + /// - `other = x(Q)`, + /// - `diff = x(P - Q)`, + /// + /// compute `x(P + Q)`. + 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 upv = &u + &v; + let umv = &u - &v; + + let new_x = &diff.z * &::square(&upv); + let new_z = &diff.x * &::square(&umv); + + Self { x: new_x, z: new_z } + } else { + let x1z2 = &self.x * &other.z; + let x2z1 = &other.x * &self.z; + + let sum = &x1z2 + &x2z1; + let sum_sq = ::square(&sum); + + let prod = &x1z2 * &x2z1; + + let new_x = &(&diff.x * &sum_sq) + &(&diff.z * &prod); + let new_z = &diff.z * &sum_sq; + + Self { x: new_x, z: new_z } + } + } + + /// Montgomery ladder for scalar multiplication. + /// + /// Given an x-line point `x(P)` and a scalar `k`, compute `x([k]P)`. + /// The scalar `k` is given as a slice of `u64` limbs in **little-endian** + /// order (same convention as `FieldOps::pow`). + pub fn scalar_mul(&self, k: &[u64], curve: &MontgomeryCurve) -> Self { + if self.is_identity() || k.is_empty() { + return Self::identity(); + } + + // Ladder state: + // r0 = x([m]P) + // r1 = x([m+1]P) + 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); + + // Swap exactly when the current bit differs from the previous one. + let do_swap = swap ^ ki; + Self::conditional_swap(&mut r0, &mut r1, do_swap); + swap = ki; + + // After the swap, the invariant is arranged so that: + // r0 <- [2]r0 + // r1 <- r0 + r1 + // and x(r1 - r0) remains x(P). + let r0_dbl = r0.xdouble(curve); + let r0_plus_r1 = r0.xadd(&r1, self); + + r0 = r0_dbl; + r1 = r0_plus_r1; + } + } + + // Final swap to undo the last deferred permutation. + Self::conditional_swap(&mut r0, &mut r1, swap); + + r0 + } } } @@ -290,31 +427,30 @@ impl KummerPoint { // PointOps bridge // --------------------------------------------------------------------------- -impl PointOps for KummerPoint -where - F: FieldOps + Copy, -{ - type BaseField = F; - type Curve = MontgomeryCurve; +ref_field_trait_impl! { + impl PointOps for KummerPoint { + type BaseField = F; + type Curve = MontgomeryCurve; - /// Return the identity image on the Kummer line. - fn identity(_curve: &Self::Curve) -> Self { - KummerPoint::::identity() - } + /// Return the identity image on the Kummer line. + fn identity(_curve: &Self::Curve) -> Self { + KummerPoint::::identity() + } - /// Return `true` if this is the identity image. - fn is_identity(&self) -> bool { - KummerPoint::::is_identity(self) - } + /// Return `true` if this is the identity image. + fn is_identity(&self) -> bool { + KummerPoint::::is_identity(self) + } - /// Negation is trivial on the Kummer line because `P` and `-P` have the - /// same image. - fn negate(&self, _curve: &Self::Curve) -> Self { - *self - } + /// Negation is trivial on the Kummer line because `P` and `-P` have the + /// same image. + fn negate(&self, _curve: &Self::Curve) -> Self { + *self + } - /// Scalar multiplication is naturally implemented by the Montgomery ladder. - fn scalar_mul(&self, k: &[u64], curve: &Self::Curve) -> Self { - KummerPoint::::scalar_mul(self, k, curve) + /// Scalar multiplication is naturally implemented by the Montgomery ladder. + fn scalar_mul(&self, k: &[u64], curve: &Self::Curve) -> Self { + KummerPoint::::scalar_mul(self, k, curve) + } } } diff --git a/ec/src/point_ops.rs b/ec/src/point_ops.rs index 5c15972..a69fc9f 100644 --- a/ec/src/point_ops.rs +++ b/ec/src/point_ops.rs @@ -13,7 +13,8 @@ use subtle::ConditionallySelectable; /// (`Add`, `Sub`, `Mul`, `Neg`) because point addition and negation usually /// need access to the curve parameters. The clean abstraction boundary is a /// method-based API taking `&Self::Curve` explicitly. -pub trait PointOps: Clone + ConditionallySelectable { +/// +pub trait PointOps: Clone + ConditionallySelectable { /// The base field $\mathbb{F}_{p^M}$ type BaseField: FieldOps; diff --git a/ec/src/point_weierstrass.rs b/ec/src/point_weierstrass.rs index 113469b..31cdfcb 100644 --- a/ec/src/point_weierstrass.rs +++ b/ec/src/point_weierstrass.rs @@ -9,6 +9,12 @@ //! //! The addition formulas implement the general Weierstrass group law: //! +//! | Operation | Algorithm | Cost | +//! |-------------------|------------------------------------------|----------------| +//! | Negate | `-(x,y) = (x, -y - a_1x - a_3)` | 3 mul + 2 add | +//! | Add (P ≠ ±Q) | Chord-and-tangent (general Weierstrass) | 1 inv + 6 mul | +//! | Double | Tangent (general Weierstrass) | 1 inv + 7 mul | +//! | Scalar multiply | Montgomery ladder (scalar-constant-time) | O(n) doubles | //! | Operation | Algorithm | Cost | //! |------------------|------------------------------------------|-------------------| //! | Negate | $-(x,y) = (x, -y - a_1 x - a_3)$ | $3$ mul + $2$ add | @@ -26,6 +32,7 @@ use crate::curve_weierstrass::WeierstrassCurve; use crate::point_ops::PointOps; use fp::field_ops::FieldOps; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; +use fp::{ref_field_impl, ref_field_trait_impl, ref_field_trait_impl_path}; /// An affine point on a Weierstrass elliptic curve over `F`. /// @@ -82,8 +89,9 @@ where impl Eq for AffinePoint where -F: FieldOps + ConstantTimeEq -{ } + F: FieldOps + ConstantTimeEq, +{ +} // --------------------------------------------------------------------------- // Constructors @@ -183,36 +191,40 @@ where // Group operations // --------------------------------------------------------------------------- -impl AffinePoint { - /// Negate a point: `-(x, y) = (x, −y − a₁x − a₃)`. - pub fn negate(&self, curve: &WeierstrassCurve) -> Self { - if self.infinity { - return Self::identity(); - } +ref_field_impl! { + impl AffinePoint { + /// Negate a point: `-(x, y) = (x, −y − a₁x − a₃)`. + pub fn negate(&self, curve: &WeierstrassCurve) -> Self { + if self.infinity { + return Self::identity(); + } - let neg_y = -self.y - curve.a1 * self.x - curve.a3; + let neg_y = -&(&self.y + &(&(&curve.a1 * &self.x) - &curve.a3)); - Self::new(self.x.clone(), neg_y) - } + Self::new(self.x.clone(), neg_y) + } - /// Double a point: `[2]P`. - /// - /// Uses the tangent-line formula for the general Weierstrass model: - /// - /// ```text - /// λ = (3x₁² + 2a₂x₁ + a₄ − a₁y₁) / (2y₁ + a₁x₁ + a₃) - /// x₃ = λ² + a₁λ − a₂ − 2x₁ - /// y₃ = λ(x₁ − x₃) − y₁ − a₁x₃ − a₃ - /// ``` - /// - /// Returns `O` when the tangent is vertical (i.e. `2y + a₁x + a₃ = 0`). - pub fn double(&self, curve: &WeierstrassCurve) -> Self { + /// Double a point: `[2]P`. + /// + /// Uses the tangent-line formula for the general Weierstrass model: + /// + /// ```text + /// λ = (3x₁² + 2a₂x₁ + a₄ − a₁y₁) / (2y₁ + a₁x₁ + a₃) + /// x₃ = λ² + a₁λ − a₂ − 2x₁ + /// y₃ = λ(x₁ − x₃) − y₁ − a₁x₃ − a₃ + /// ``` + /// + /// Returns `O` when the tangent is vertical (i.e. `2y + a₁x + a₃ = 0`). + pub fn double(&self, curve: &WeierstrassCurve) -> Self { if self.infinity { return Self::identity(); } // denominator = 2y₁ + a₁x₁ + a₃ - let denom = ::double(&self.y) + curve.a1 * self.x + curve.a3; + let two_y = ::double(&self.y); + let a1x = &curve.a1 * &self.x; + let tmp = &two_y + &a1x; + let denom = &tmp + &curve.a3; // If denominator is zero the tangent is vertical → result is O. let denom_inv = match denom.invert().into_option() { @@ -222,161 +234,179 @@ impl AffinePoint { // numerator = 3x₁² + 2a₂x₁ + a₄ − a₁y₁ let numer = { - let x1_sq = ::square(&self.x); - let three_x1_sq = x1_sq + ::double(&x1_sq); - let two_a2_x1 = ::double(&(curve.a2 * self.x)); - let a1y1 = curve.a1 * self.y; - three_x1_sq + two_a2_x1 + curve.a4 - a1y1 - }; + let x1_sq = ::square(&self.x); + let two_x1_sq = ::double(&x1_sq); + let three_x1_sq = &x1_sq + &two_x1_sq; - let lambda = numer * denom_inv; + let a2x1 = &curve.a2 * &self.x; + let two_a2_x1 = ::double(&a2x1); - // x₃ = λ² + a₁λ − a₂ − 2x₁ - let x3 = { - let lam_sq = ::square(&lambda); - let a1_lam = curve.a1 * lambda; - let two_x1 = ::double(&self.x); - lam_sq + a1_lam - curve.a2 - two_x1 - }; + let a1y1 = &curve.a1 * &self.y; - // y₃ = λ(x₁ − x₃) − y₁ − a₁x₃ − a₃ - let y3 = { - let dx = self.x - x3; - let lam_dx = lambda * dx; - let a1x3 = curve.a1 * x3; + let tmp1 = &three_x1_sq + &two_a2_x1; + let tmp2 = &tmp1 + &curve.a4; + &tmp2 - &a1y1 + }; - lam_dx - self.y - a1x3 - curve.a3 - }; + let lambda = &numer * &denom_inv; - Self::new(x3, y3) - } + // x₃ = λ² + a₁λ − a₂ − 2x₁ + let x3 = { + let lam_sq = ::square(&lambda); + let a1_lam = &curve.a1 * λ + let two_x1 = ::double(&self.x); - /// Add two points: `P + Q`. - /// - /// Handles all cases: - /// - Either operand is `O` → return the other. - /// - `P = Q` → delegate to [`double`](Self::double). - /// - `P = −Q` (same x, opposite y) → return `O`. - /// - General chord: - /// - /// ```text - /// λ = (y₂ − y₁) / (x₂ − x₁) - /// x₃ = λ² + a₁λ − a₂ − x₁ − x₂ - /// y₃ = λ(x₁ − x₃) − y₁ − a₁x₃ − a₃ - /// ``` - pub fn add(&self, other: &Self, curve: &WeierstrassCurve) -> Self { - // O + Q = Q - if self.infinity { - return other.clone(); - } - // P + O = P - if other.infinity { - return self.clone(); + let tmp1 = &lam_sq + &a1_lam; + let tmp2 = &tmp1 - &curve.a2; + &tmp2 - &two_x1 + }; + + // y₃ = λ(x₁ − x₃) − y₁ − a₁x₃ − a₃ + let y3 = { + let dx = &self.x - &x3; + let lam_dx = &lambda * &dx; + let a1x3 = &curve.a1 * &x3; + + let tmp1 = &lam_dx - &self.y; + let tmp2 = &tmp1 - &a1x3; + &tmp2 - &curve.a3 + }; + + Self::new(x3, y3) } - // Same x-coordinate? - if self.x == other.x { - if self.y == other.y { - // P = Q → doubling - return self.double(curve); + /// Add two points: `P + Q`. + /// + /// Handles all cases: + /// - Either operand is `O` → return the other. + /// - `P = Q` → delegate to [`double`](Self::double). + /// - `P = −Q` (same x, opposite y) → return `O`. + /// - General chord: + /// + /// ```text + /// λ = (y₂ − y₁) / (x₂ − x₁) + /// x₃ = λ² + a₁λ − a₂ − x₁ − x₂ + /// y₃ = λ(x₁ − x₃) − y₁ − a₁x₃ − a₃ + /// ``` + pub fn add(&self, other: &Self, curve: &WeierstrassCurve) -> Self { + // O + Q = Q + if self.infinity { + return other.clone(); + } + // P + O = P + if other.infinity { + return self.clone(); } - // P = −Q → identity - // (This also covers the char-2 case where y₁ + y₂ + a₁x + a₃ = 0.) - return Self::identity(); - } - // General chord - let dx = other.x - self.x; - let dy = other.y - self.y; - - // dx ≠ 0 guaranteed by the x₁ ≠ x₂ check above - let dx_inv = dx - .invert() - .into_option() - .expect("dx must be invertible (x₁ ≠ x₂)"); - let lambda = dy * dx_inv; - - // x₃ = λ² + a₁λ − a₂ − x₁ − x₂ - let x3 = { - let lam_sq = ::square(&lambda); - let a1_lam = curve.a1 * lambda; - lam_sq + a1_lam - curve.a2 - self.x - other.x - }; + // Same x-coordinate? + if self.x == other.x { + if self.y == other.y { + // P = Q → doubling + return self.double(curve); + } + // P = −Q → identity + // (This also covers the char-2 case where y₁ + y₂ + a₁x + a₃ = 0.) + return Self::identity(); + } - // y₃ = λ(x₁ − x₃) − y₁ − a₁x₃ − a₃ - let y3 = { - let dx3 = self.x - x3; - let lam_dx3 = lambda * dx3; - let a1x3 = curve.a1 * x3; - lam_dx3 - self.y - a1x3 - curve.a3 - }; + // General chord + let dx = &other.x - &self.x; + let dy = &other.y - &self.y; - Self::new(x3, y3) - } + // dx ≠ 0 guaranteed by the x₁ ≠ x₂ check above + let dx_inv = ::invert(&dx) + .into_option() + .expect("dx must be invertible (x₁ ≠ x₂)"); + let lambda = &dy * &dx_inv; - /// Multiply `self` by `k` - /// - /// # Arguments - /// - /// * `&self` - Point on curve (type: `Self`) - /// * `k` - Integer (type: `&[u64]`) - /// * `curve` - The curve we're on (type: `& as PointOps>::Curve`) - /// - /// # Returns - /// - /// The point `k * self` (type: `Self`) - pub fn scalar_mul(&self, k: &[u64], curve: & as PointOps>::Curve) -> Self { - let mut r0 = Self::identity(); - let mut r1 = self.clone(); + // x₃ = λ² + a₁λ − a₂ − x₁ − x₂ + let x3 = { + let lam_sq = ::square(&lambda); + let a1_lam = &curve.a1 * λ - for &limb in k.iter().rev() { - for bit in (0..64).rev() { - let choice = Choice::from(((limb >> bit) & 1) as u8); + let tmp1 = &lam_sq + &a1_lam; + let tmp2 = &tmp1 - &curve.a2; + let tmp3 = &tmp2 - &self.x; + &tmp3 - &other.x + }; - Self::conditional_swap(&mut r0, &mut r1, choice); + // y₃ = λ(x₁ − x₃) − y₁ − a₁x₃ − a₃ + let y3 = { + let dx = &self.x - &x3; + let lam_dx = &lambda * &dx; + let a1x3 = &curve.a1 * &x3; - let sum = r0.add(&r1, curve); - let dbl = r0.double(curve); - r1 = sum; - r0 = dbl; + let tmp1 = &lam_dx - &self.y; + let tmp2 = &tmp1 - &a1x3; + &tmp2 - &curve.a3 + }; + + Self::new(x3, y3) - Self::conditional_swap(&mut r0, &mut r1, choice); - } } - r0 + /// Multiply `self` by `k` + /// + /// # Arguments + /// + /// * `&self` - Point on curve (type: `Self`) + /// * `k` - Integer (type: `&[u64]`) + /// * `curve` - The curve we're on (type: `& as PointOps>::Curve`) + /// + /// # Returns + /// + /// The point `k * self` (type: `Self`) + pub fn scalar_mul(&self, k: &[u64], curve: & as PointOps>::Curve) -> Self { + let mut r0 = Self::identity(); + let mut r1 = self.clone(); + + for &limb in k.iter().rev() { + for bit in (0..64).rev() { + let choice = Choice::from(((limb >> bit) & 1) as u8); + + Self::conditional_swap(&mut r0, &mut r1, choice); + + let sum = r0.add(&r1, curve); + let dbl = r0.double(curve); + r1 = sum; + r0 = dbl; + + Self::conditional_swap(&mut r0, &mut r1, choice); + } + } + + r0 + } } } -impl PointOps for AffinePoint -where - F: FieldOps, -{ - type BaseField = F; - type Curve = WeierstrassCurve; +ref_field_trait_impl! { + impl PointOps for AffinePoint { + type BaseField = F; + type Curve = WeierstrassCurve; - fn identity(_curve: &Self::Curve) -> Self { - AffinePoint::::identity() - } + fn identity(_curve: &Self::Curve) -> Self { + AffinePoint::::identity() + } - fn is_identity(&self) -> bool { - self.infinity - } + fn is_identity(&self) -> bool { + self.infinity + } - fn negate(&self, curve: &Self::Curve) -> Self { - AffinePoint::::negate(self, curve) - } + fn negate(&self, curve: &Self::Curve) -> Self { + AffinePoint::::negate(self, curve) + } - fn scalar_mul(&self, k: &[u64], curve: &Self::Curve) -> Self { - AffinePoint::::scalar_mul(self, k, curve) + fn scalar_mul(&self, k: &[u64], curve: &Self::Curve) -> Self { + AffinePoint::::scalar_mul(self, k, curve) + } } } -impl crate::point_ops::PointAdd for AffinePoint -where - F: FieldOps, -{ - fn add(&self, other: &Self, curve: &Self::Curve) -> Self { - AffinePoint::::add(self, other, curve) +ref_field_trait_impl_path! { + impl (crate::point_ops::PointAdd) for AffinePoint { + fn add(&self, other: &Self, curve: &Self::Curve) -> Self { + AffinePoint::::add(self, other, curve) + } } -} +} \ No newline at end of file diff --git a/ec/tests/curve_montgomery_tests.rs b/ec/tests/curve_montgomery_tests.rs index 4b55ed1..ecd2e22 100644 --- a/ec/tests/curve_montgomery_tests.rs +++ b/ec/tests/curve_montgomery_tests.rs @@ -23,8 +23,10 @@ fn all_x_coords_montgomery_19(a: F19, b: F19) -> Vec { for xint in 0..19u64 { let x = fp(xint); - let rhs_num = x * (x * x + a * x + F19::one()); - let rhs = rhs_num * F19::invert(&b).expect("b should be invertible mod 19"); + let x2 = &x * &x; + let ax = &a * &x; + let rhs_num = &x * &(&x2 + &(&ax + &F19::one())); + let rhs = &rhs_num * &F19::invert(&b).expect("b should be invertible mod 19"); let mut has_y = false; for y in 0..19u64 { @@ -70,7 +72,7 @@ fn montgomery_curve_rejects_b_zero() { #[test] fn montgomery_curve_rejects_a_equal_plus_minus_two() { let two = fp(2); - let minus_two = -two; + let minus_two = -&two; assert!(!MontgomeryCurve::::is_smooth(&two, &fp(1))); assert!(!MontgomeryCurve::::is_smooth(&minus_two, &fp(1))); @@ -81,7 +83,7 @@ fn montgomery_a24_matches_formula() { let c = MontgomeryCurve::new(fp(3), fp(5)); let four_inv = fp(4).invert().unwrap(); - let expected = (fp(3) + fp(2)) * four_inv; + let expected = &(&fp(3) + &fp(2)) * &four_inv; assert_eq!(c.a24(), expected); } @@ -102,6 +104,7 @@ fn montgomery_rejects_some_non_points() { let all_x = all_x_coords_montgomery_19(fp(3), fp(5)); for x in 0..19u64 { + println!("x = {}", x); let p = KummerPoint::from_x(fp(x)); let expected = all_x.contains(&fp(x)); assert_eq!( @@ -119,9 +122,9 @@ fn montgomery_j_invariant_matches_formula() { let a = fp(3); let asq = a.square(); - let num = fp(256) * (asq - fp(3)) * (asq - fp(3)).square(); - let den_inv = (asq - fp(4)).invert().unwrap(); - let expected = num * den_inv; + let num = &fp(256) * &(&(&asq - &fp(3)) * &(&asq - &fp(3)).square()); + let den_inv = (&asq - &fp(4)).invert().unwrap(); + let expected = &num * &den_inv; assert_eq!(c.j_invariant(), expected); } \ No newline at end of file diff --git a/ec/tests/point_edwards_test.rs b/ec/tests/point_edwards_test.rs index d1473fd..56d2afa 100644 --- a/ec/tests/point_edwards_test.rs +++ b/ec/tests/point_edwards_test.rs @@ -64,10 +64,10 @@ fn find_point_65537(curve: &EdwardsCurve) -> Option for xv in 2u64..1000 { let x = fp65537(xv); let x2 = ::square(&x); - let num = F65537::one() - x2; - let den = F65537::one() - d * x2; + let num = &F65537::one() - &x2; + let den = &F65537::one() - &(&d * &x2); if let Some(den_inv) = den.invert().into_option() { - let y2 = num * den_inv; + let y2 = &num * &den_inv; if let Some(y) = y2.sqrt().into_option() { let p = EdwardsPoint::new(x, y); if curve.is_on_curve(&p) { @@ -439,9 +439,9 @@ fn gf16_scalar_mul_double() { fn gf16_w_double() { let c = binary_edwards_curve(); for p in all_bin_edwards(&c) { - let w = p.x + p.y; + let w = &p.x + &p.y; let dbl = p.double(&c); - let w_expected = dbl.x + dbl.y; + let w_expected = &dbl.x + &dbl.y; let w_got = EdwardsPoint::::w_double(&w, &c); assert_eq!( w_got, w_expected, @@ -463,10 +463,10 @@ fn gf16_w_diff_add() { let diff = q.add(&p.negate(&c), &c); // Q - P let sum = p.add(q, &c); // P + Q - let w1 = diff.x + diff.y; - let w2 = p.x + p.y; - let w3 = q.x + q.y; - let w5_expected = sum.x + sum.y; + let w1 = &diff.x + &diff.y; + let w2 = &p.x + &p.y; + let w3 = &q.x + &q.y; + let w5_expected = &sum.x + &sum.y; let w5_got = EdwardsPoint::::w_diff_add(&w1, &w2, &w3, &c); assert_eq!(w5_got, w5_expected); diff --git a/ec/tests/point_montgomery_tests.rs b/ec/tests/point_montgomery_tests.rs index 2c6409d..9edb6cb 100644 --- a/ec/tests/point_montgomery_tests.rs +++ b/ec/tests/point_montgomery_tests.rs @@ -23,14 +23,18 @@ fn fq(n: u64) -> F59 { fn curve() -> MontgomeryCurve { MontgomeryCurve::new(fp(3), fp(5)) } +<<<<<<< HEAD +//fn curve2() -> MontgomeryCurve { MontgomeryCurve::new(fq(7), fq(1)) } +======= +>>>>>>> origin/main fn all_x_coords_montgomery_19(a: F19, b: F19) -> Vec { let mut xs = Vec::new(); for xint in 0..19u64 { let x = fp(xint); - let rhs_num = x * (x * x + a * x + F19::one()); - let rhs = rhs_num * F19::invert(&b).expect("b should be invertible mod 19"); + let rhs_num = &x * &(&(&(&x * &x) + &(&a * &x)) + &F19::one()); + let rhs = &rhs_num * &F19::invert(&b).expect("b should be invertible mod 19"); let mut has_y = false; for y in 0..19u64 { diff --git a/fp/examples/fp_demo.rs b/fp/examples/fp_demo.rs index 4683b22..36c17cd 100644 --- a/fp/examples/fp_demo.rs +++ b/fp/examples/fp_demo.rs @@ -246,7 +246,7 @@ fn main() { // quick sanity: a * a^{-1} == 1 let a = GF256::random(&mut rng); let a_inv = a.invert().unwrap(); - let prod = a * a_inv; + let prod = &a * &a_inv; println!(" sanity a = {a}"); println!(" sanity 1/a = {a_inv}"); println!(" a * 1/a = {prod} (should be 1)"); @@ -259,7 +259,7 @@ fn main() { let a = GF2_511::random(&mut rng); let a_inv = a.invert().unwrap(); - let prod = a * a_inv; + let prod = &a * &a_inv; println!(" sanity a = {a}"); println!(" sanity 1/a = {a_inv}"); println!(" a * 1/a = {prod} (should be 1)"); @@ -281,7 +281,7 @@ fn main() { // quick sanity: a * a^{-1} == 1 let b = Fp19_2::random(&mut rng); let b_inv = b.invert().unwrap(); - let prod = b * b_inv; + let prod = &b * &b_inv; println!(" sanity b = {b}"); println!(" sanity 1/b = {b_inv}"); println!(" b * 1/b = {prod} (should be 1)"); @@ -301,7 +301,7 @@ fn main() { let a2_inv = a2.invert().unwrap(); println!(" sanity a = {a2}"); println!(" sanity 1/a = {a2_inv}"); - println!(" a * 1/a = {} (should be 1)", a2 * a2_inv); + println!(" a * 1/a = {} (should be 1)", &a2 * &a2_inv); println!("\n===== F_{{97283^3}} = F_97283[x]/(x^3+x+1) ====="); for i in 0..5 { @@ -312,7 +312,7 @@ fn main() { let a3_inv = a3.invert().unwrap(); println!(" sanity a = {a3}"); println!(" sanity 1/a = {a3_inv}"); - println!(" a * 1/a = {} (should be 1)", a3 * a3_inv); + println!(" a * 1/a = {} (should be 1)", &a3 * &a3_inv); println!("\n===== F_{{97283^4}} = F_97283[x]/(x^4+2x^2+2) ====="); for i in 0..5 { @@ -323,6 +323,6 @@ fn main() { let a4_inv = a4.invert().unwrap(); println!(" sanity a = {a4}"); println!(" sanity 1/a = {a4_inv}"); - println!(" a * 1/a = {} (should be 1)", a4 * a4_inv); + println!(" a * 1/a = {} (should be 1)", &a4 * &a4_inv); } \ No newline at end of file diff --git a/fp/src/f2_element.rs b/fp/src/f2_element.rs index 75918b2..0c7cab4 100644 --- a/fp/src/f2_element.rs +++ b/fp/src/f2_element.rs @@ -89,40 +89,48 @@ impl ConstantTimeEq for F2Element { // Operator overloads // --------------------------------------------------------------------------- -impl Add for F2Element { - type Output = Self; +impl<'a, 'b> Add<&'b F2Element> +for &'a F2Element +{ + type Output = F2Element; - fn add(self, rhs: Self) -> Self { - Self::new(self.value ^ rhs.value) + fn add(self, rhs: &'b F2Element) -> Self::Output { + ::add(self, rhs) } } -impl Sub for F2Element { - type Output = Self; +impl<'a, 'b> Sub<&'b F2Element> +for &'a F2Element +{ + type Output = F2Element; - fn sub(self, rhs: Self) -> Self { - //subtraction = addition in F_2 - Self::new(self.value ^ rhs.value) + fn sub(self, rhs: &'b F2Element) -> Self::Output { + ::sub(self, rhs) } } -impl Mul for F2Element { - type Output = Self; - fn mul(self, rhs: Self) -> Self { - Self::new(self.value & rhs.value) +impl<'a, 'b> Mul<&'b F2Element> +for &'a F2Element +{ + type Output = F2Element; + + fn mul(self, rhs: &'b F2Element) -> Self::Output { + ::mul(self, rhs) } } -impl Neg for F2Element { - type Output = Self; +impl<'a> Neg for &'a F2Element +{ + type Output = F2Element; - fn neg(self) -> Self { - // x = -x for any x in F_2 - self + fn neg(self) -> Self::Output { + ::negate(self) } } + + // --------------------------------------------------------------------------- // FieldOps implementation // --------------------------------------------------------------------------- diff --git a/fp/src/f2_ext.rs b/fp/src/f2_ext.rs index 2d9513b..c088958 100644 --- a/fp/src/f2_ext.rs +++ b/fp/src/f2_ext.rs @@ -6,6 +6,7 @@ use crypto_bigint::Uint; use std::marker::PhantomData; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + // --------------------------------------------------------------------------- // IrreduciblePoly — the only thing callers need to implement for a new field // --------------------------------------------------------------------------- @@ -347,46 +348,55 @@ where // Operator overloads (delegate to the FieldOps methods below) // =========================================================================== -impl Add for F2Ext +impl<'a, 'b, const LIMBS: usize, P> Add<&'b F2Ext> +for &'a F2Ext where P: BinaryIrreducible, { - type Output = Self; - fn add(self, rhs: Self) -> Self { - FieldOps::add(&self, &rhs) + type Output = F2Ext; + + fn add(self, rhs: &'b F2Ext) -> Self::Output { + as FieldOps>::add(self, rhs) } } -impl Sub for F2Ext +impl<'a, 'b, const LIMBS: usize, P> Sub<&'b F2Ext> +for &'a F2Ext where P: BinaryIrreducible, { - type Output = Self; - fn sub(self, rhs: Self) -> Self { - FieldOps::sub(&self, &rhs) + type Output = F2Ext; + + fn sub(self, rhs: &'b F2Ext) -> Self::Output { + as FieldOps>::sub(self, rhs) } } -impl Mul for F2Ext + +impl<'a, 'b, const LIMBS: usize, P> Mul<&'b F2Ext> +for &'a F2Ext where P: BinaryIrreducible, { - type Output = Self; - fn mul(self, rhs: Self) -> Self { - FieldOps::mul(&self, &rhs) + type Output = F2Ext; + + fn mul(self, rhs: &'b F2Ext) -> Self::Output { + as FieldOps>::mul(self, rhs) } } -impl Neg for F2Ext +impl<'a, const LIMBS: usize, P> Neg for &'a F2Ext where P: BinaryIrreducible, { - type Output = Self; - fn neg(self) -> Self { - FieldOps::negate(&self) + type Output = F2Ext; + + fn neg(self) -> Self::Output { + as FieldOps>::negate(self) } } + // =========================================================================== // FieldOps implementation // =========================================================================== @@ -395,7 +405,6 @@ impl FieldOps for F2Ext where P: BinaryIrreducible, { - // to be done: legendre, sqrt fn zero() -> Self { Self::from_uint(Uint::::ZERO) } diff --git a/fp/src/field_ops.rs b/fp/src/field_ops.rs index 5be1002..ecfa593 100644 --- a/fp/src/field_ops.rs +++ b/fp/src/field_ops.rs @@ -1,6 +1,5 @@ //! Core trait that every field element in the tower must implement. -use std::ops::{Add, Mul, Neg, Sub}; use subtle::{Choice, ConditionallySelectable, CtOption}; /// Trait for generating cryptographically secure random field elements. @@ -35,10 +34,6 @@ pub trait FieldOps: + Clone + PartialEq + Eq - + Add - + Sub - + Mul - + Neg + Default + ConditionallySelectable { @@ -288,3 +283,343 @@ pub trait FieldFromRepr: FieldOps { /// Constructs a field element from the given representation. fn from_repr(x: Self::Repr) -> Self; } + + +// ------------------------------------------------------------------- +// Macros for using operator overloads on referenced values +// ------------------------------------------------------------------- + +#[macro_export] +macro_rules! ref_field_impl { + (impl<$F:ident> $Ty:ty { $($body:tt)* }) => { + impl<$F> $Ty + where + $F: $crate::field_ops::FieldOps, + for<'a, 'b> &'a $F: std::ops::Add<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Sub<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Mul<&'b $F, Output = $F>, + for<'a> &'a $F: std::ops::Neg, + { + $($body)* + } + }; + + (impl<$F:ident : $First:ident $(+ $Rest:ident)*> $Ty:ty { $($body:tt)* }) => { + impl<$F: $First $(+ $Rest)*> $Ty + where + $F: $crate::field_ops::FieldOps, + for<'a, 'b> &'a $F: std::ops::Add<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Sub<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Mul<&'b $F, Output = $F>, + for<'a> &'a $F: std::ops::Neg, + { + $($body)* + } + }; +} + +#[macro_export] +macro_rules! ref_field_trait_impl { + (impl<$F:ident> $Trait:ident for $Ty:ty { $($body:tt)* }) => { + impl<$F> $Trait for $Ty + where + $F: $crate::field_ops::FieldOps, + for<'a, 'b> &'a $F: std::ops::Add<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Sub<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Mul<&'b $F, Output = $F>, + for<'a> &'a $F: std::ops::Neg, + { + $($body)* + } + }; + + (impl<$F:ident : $First:ident $(+ $Rest:ident)*> $Trait:ident for $Ty:ty { $($body:tt)* }) => { + impl<$F: $First $(+ $Rest)*> $Trait for $Ty + where + $F: $crate::field_ops::FieldOps, + for<'a, 'b> &'a $F: std::ops::Add<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Sub<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Mul<&'b $F, Output = $F>, + for<'a> &'a $F: std::ops::Neg, + { + $($body)* + } + }; +} + +#[macro_export] +macro_rules! ref_field_trait_impl_path { + (impl<$F:ident> ($Trait:path) for $Ty:ty { $($body:tt)* }) => { + impl<$F> $Trait for $Ty + where + $F: $crate::field_ops::FieldOps, + for<'a, 'b> &'a $F: std::ops::Add<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Sub<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Mul<&'b $F, Output = $F>, + for<'a> &'a $F: std::ops::Neg, + { + $($body)* + } + }; + + (impl<$F:ident : $First:ident $(+ $Rest:ident)*> ($Trait:path) for $Ty:ty { $($body:tt)* }) => { + impl<$F: $First $(+ $Rest)*> $Trait for $Ty + where + $F: $crate::field_ops::FieldOps, + for<'a, 'b> &'a $F: std::ops::Add<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Sub<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Mul<&'b $F, Output = $F>, + for<'a> &'a $F: std::ops::Neg, + { + $($body)* + } + }; +} + +#[macro_export] +macro_rules! ref_field_fn { + (fn $name:ident<$F:ident> ( $($args:tt)* ) -> $Ret:ty $body:block) => { + fn $name<$F>($($args)*) -> $Ret + where + $F: $crate::field_ops::FieldOps, + for<'a, 'b> &'a $F: std::ops::Add<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Sub<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Mul<&'b $F, Output = $F>, + for<'a> &'a $F: std::ops::Neg, + $body + }; + + (fn $name:ident<$F:ident : $First:ident $(+ $Rest:ident)*> ( $($args:tt)* ) -> $Ret:ty $body:block) => { + fn $name<$F: $First $(+ $Rest)*>($($args)*) -> $Ret + where + $F: $crate::field_ops::FieldOps, + for<'a, 'b> &'a $F: std::ops::Add<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Sub<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Mul<&'b $F, Output = $F>, + for<'a> &'a $F: std::ops::Neg, + $body + }; +} + +#[macro_export] +macro_rules! ref_field_fns { + ( + $(#[$meta:meta])* + fn $name:ident<$F:ident> ( $($args:tt)* ) -> $Ret:ty $body:block + $($rest:tt)* + ) => { + $(#[$meta])* + $crate::ref_field_fn! { + fn $name<$F>($($args)*) -> $Ret $body + } + $crate::ref_field_fns! { $($rest)* } + }; + + ( + $(#[$meta:meta])* + fn $name:ident<$F:ident : $First:ident $(+ $Rest:ident)*> ( $($args:tt)* ) -> $Ret:ty $body:block + $($rest:tt)* + ) => { + $(#[$meta])* + $crate::ref_field_fn! { + fn $name<$F: $First $(+ $Rest)*>($($args)*) -> $Ret $body + } + $crate::ref_field_fns! { $($rest)* } + }; +} + + + + + + + + + + + + + + + + + + +/* +/// For inherent impls like: +/// +/// ref_field_impl! { +/// impl WeierstrassCurve { +/// ... +/// } +/// } +/// +#[macro_export] +macro_rules! ref_field_impl { + (impl<$F:ident> $Ty:ty { $($body:tt)* }) => { + impl<$F> $Ty + where + $F: $crate::field_ops::FieldOps, + for<'a, 'b> &'a $F: std::ops::Add<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Sub<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Mul<&'b $F, Output = $F>, + for<'a> &'a $F: std::ops::Neg, + { + $($body)* + } + }; + + (impl<$F:ident : $($Bounds:tt)+> $Ty:ty { $($body:tt)* }) => { + impl<$F: $($Bounds)+> $Ty + where + $F: $crate::field_ops::FieldOps, + for<'a, 'b> &'a $F: std::ops::Add<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Sub<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Mul<&'b $F, Output = $F>, + for<'a> &'a $F: std::ops::Neg, + { + $($body)* + } + }; +} + +/// For trait impls like: +/// +/// ref_field_trait_impl! { +/// impl Curve for WeierstrassCurve { +/// ... +/// } +/// } +#[macro_export] +macro_rules! ref_field_trait_impl { + (impl<$F:ident> $Trait:ident for $Ty:ty { $($body:tt)* }) => { + impl<$F> $Trait for $Ty + where + $F: $crate::field_ops::FieldOps, + for<'a, 'b> &'a $F: std::ops::Add<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Sub<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Mul<&'b $F, Output = $F>, + for<'a> &'a $F: std::ops::Neg, + { + $($body)* + } + }; + + (impl<$F:ident : $($Bounds:tt)+> $Trait:ident for $Ty:ty { $($body:tt)* }) => { + impl<$F: $($Bounds)+> $Trait for $Ty + where + $F: $crate::field_ops::FieldOps, + for<'a, 'b> &'a $F: std::ops::Add<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Sub<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Mul<&'b $F, Output = $F>, + for<'a> &'a $F: std::ops::Neg, + { + $($body)* + } + }; +} + +/// For trait impls when one wants to specify the path like: +/// +/// ref_field_trait_impl_path! { +/// impl (crate::point_ops::PointAdd) for AffinePoint { +/// ... +/// } +/// } +#[macro_export] +macro_rules! ref_field_trait_impl_path { + (impl<$F:ident> ($Trait:path) for $Ty:ty { $($body:tt)* }) => { + impl<$F> $Trait for $Ty + where + $F: $crate::field_ops::FieldOps, + for<'a, 'b> &'a $F: std::ops::Add<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Sub<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Mul<&'b $F, Output = $F>, + for<'a> &'a $F: std::ops::Neg, + { + $($body)* + } + }; + + (impl<$F:ident : $($Bounds:tt)+> ($Trait:path) for $Ty:ty { $($body:tt)* }) => { + impl<$F: $($Bounds)+> $Trait for $Ty + where + $F: $crate::field_ops::FieldOps, + for<'a, 'b> &'a $F: std::ops::Add<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Sub<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Mul<&'b $F, Output = $F>, + for<'a> &'a $F: std::ops::Neg, + { + $($body)* + } + }; +} + +/// For single free functions like: +/// +/// ref_field_fn! { +/// fn b2_from_coeffs(a1: &F, a2: &F) -> F { +/// ... +/// } +/// } +#[macro_export] +macro_rules! ref_field_fn { + (fn $name:ident<$F:ident> ( $($args:tt)* ) -> $Ret:ty $body:block) => { + fn $name<$F>($($args)*) -> $Ret + where + $F: $crate::field_ops::FieldOps, + for<'a, 'b> &'a $F: std::ops::Add<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Sub<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Mul<&'b $F, Output = $F>, + for<'a> &'a $F: std::ops::Neg, + $body + }; + + (fn $name:ident<$F:ident : $($Bounds:tt)+> ( $($args:tt)* ) -> $Ret:ty $body:block) => { + fn $name<$F: $($Bounds)+>($($args)*) -> $Ret + where + $F: $crate::field_ops::FieldOps, + for<'a, 'b> &'a $F: std::ops::Add<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Sub<&'b $F, Output = $F>, + for<'a, 'b> &'a $F: std::ops::Mul<&'b $F, Output = $F>, + for<'a> &'a $F: std::ops::Neg, + $body + }; +} + +/// For batches of free functions like: +/// +/// ref_field_fn! { +/// fn b2_from_coeffs(a1: &F, a2: &F) -> F { ... } +/// ... +/// fn b4_from_coeffs(a1: &F, a3: &F, a4: &F) -> F { ... } +/// } +#[macro_export] +macro_rules! ref_field_fns { + () => {}; + + ( + $(#[$meta:meta])* + fn $name:ident<$F:ident> ( $($args:tt)* ) -> $Ret:ty $body:block + $($rest:tt)* + ) => { + $(#[$meta])* + $crate::ref_field_fn! { + fn $name<$F>($($args)*) -> $Ret $body + } + $crate::ref_field_fns! { $($rest)* } + }; + + ( + $(#[$meta:meta])* + fn $name:ident<$F:ident : $($Bounds:tt)+> ( $($args:tt)* ) -> $Ret:ty $body:block + $($rest:tt)* + ) => { + $(#[$meta])* + $crate::ref_field_fn! { + fn $name<$F: $($Bounds)+>($($args)*) -> $Ret $body + } + $crate::ref_field_fns! { $($rest)* } + }; +} +*/ \ No newline at end of file diff --git a/fp/src/fp_element.rs b/fp/src/fp_element.rs index d1da88b..12cd28c 100644 --- a/fp/src/fp_element.rs +++ b/fp/src/fp_element.rs @@ -196,49 +196,51 @@ where // Operator overloads // --------------------------------------------------------------------------- -impl Add for FpElement +impl<'a, 'b, MOD, const LIMBS: usize> Add<&'b FpElement> +for &'a FpElement where MOD: ConstPrimeMontyParams, { - type Output = Self; - fn add(self, rhs: Self) -> Self { - Self { - value: self.value + rhs.value, - } + type Output = FpElement; + + fn add(self, rhs: &'b FpElement) -> Self::Output { + as FieldOps>::add(self, rhs) } } -impl Sub for FpElement +impl<'a, 'b, MOD, const LIMBS: usize> Sub<&'b FpElement> +for &'a FpElement where MOD: ConstPrimeMontyParams, { - type Output = Self; - fn sub(self, rhs: Self) -> Self { - Self { - value: self.value - rhs.value, - } + type Output = FpElement; + + fn sub(self, rhs: &'b FpElement) -> Self::Output { + as FieldOps>::sub(self, rhs) } } -impl Mul for FpElement + +impl<'a, 'b, MOD, const LIMBS: usize> Mul<&'b FpElement> +for &'a FpElement where MOD: ConstPrimeMontyParams, { - type Output = Self; - fn mul(self, rhs: Self) -> Self { - Self { - value: self.value * rhs.value, - } + type Output = FpElement; + + fn mul(self, rhs: &'b FpElement) -> Self::Output { + as FieldOps>::mul(self, rhs) } } -impl Neg for FpElement +impl<'a, MOD, const LIMBS: usize> Neg for &'a FpElement where MOD: ConstPrimeMontyParams, { - type Output = Self; - fn neg(self) -> Self { - Self { value: -self.value } + type Output = FpElement; + + fn neg(self) -> Self::Output { + as FieldOps>::negate(self) } } diff --git a/fp/src/fp_ext.rs b/fp/src/fp_ext.rs index 7f3c5b5..bc2581f 100644 --- a/fp/src/fp_ext.rs +++ b/fp/src/fp_ext.rs @@ -696,58 +696,65 @@ where // Operator overloads (delegate to the FieldOps methods below) // =========================================================================== -impl Add - for FpExt +impl<'a, 'b, MOD, const LIMBS: usize, const M: usize, const N: usize, P, TSCONSTS> +Add<&'b FpExt> +for &'a FpExt where MOD: ConstPrimeMontyParams, P: IrreduciblePoly, TSCONSTS: TonelliShanksConstants, { - type Output = Self; - fn add(self, rhs: Self) -> Self { - FieldOps::add(&self, &rhs) + type Output = FpExt; + fn add(self, rhs: &'b FpExt) -> Self::Output { + as FieldOps>::add(self, rhs) } } -impl Sub - for FpExt +impl<'a, 'b, MOD, const LIMBS: usize, const M: usize, const N: usize, P, TSCONSTS> +Sub<&'b FpExt> +for &'a FpExt where MOD: ConstPrimeMontyParams, P: IrreduciblePoly, TSCONSTS: TonelliShanksConstants, { - type Output = Self; - fn sub(self, rhs: Self) -> Self { - FieldOps::sub(&self, &rhs) + type Output = FpExt; + fn sub(self, rhs: &'b FpExt) -> Self::Output { + as FieldOps>::sub(self, rhs) } } -impl Mul - for FpExt +impl<'a, 'b, MOD, const LIMBS: usize, const M: usize, const N: usize, P, TSCONSTS> +Mul<&'b FpExt> +for &'a FpExt where MOD: ConstPrimeMontyParams, P: IrreduciblePoly, TSCONSTS: TonelliShanksConstants, { - type Output = Self; - fn mul(self, rhs: Self) -> Self { - FieldOps::mul(&self, &rhs) + type Output = FpExt; + fn mul(self, rhs: &'b FpExt) -> Self::Output { + as FieldOps>::mul(self, rhs) } } -impl Neg - for FpExt +impl<'a, MOD, const LIMBS: usize, const M: usize, const N: usize, P, TSCONSTS> +Neg +for &'a FpExt where MOD: ConstPrimeMontyParams, P: IrreduciblePoly, TSCONSTS: TonelliShanksConstants, { - type Output = Self; - fn neg(self) -> Self { - FieldOps::negate(&self) + type Output = FpExt; + fn neg(self) -> Self::Output { + as FieldOps>::negate(self) } } + + + // =========================================================================== // Helper functions for Tonelli-Shanks algorithm // =========================================================================== @@ -778,8 +785,9 @@ fn ts_loop TSCONSTS: TonelliShanksConstants, { let mut v = TSCONSTS::S as u32; - *x = x.mul(*w); - let mut b = x.mul(*w); + *x = &*x * w ; // to take the prod of a &mut one has to dereference it (so it isn't mut anymore) and then reference to it + let mut b = &*x * w; + //x.mul(*w); let mut z = FpExt::new(TSCONSTS::root_of_unity()); for max_v in (1..=TSCONSTS::S as u32).rev() { @@ -799,10 +807,10 @@ fn ts_loop z = FpExt::conditional_select(&z, &new_z, j_less_than_v); } - let result = x.mul(z); + let result = &*x * &z; *x = FpExt::conditional_select(&result, x, b.ct_eq(&FpExt::one())); z = z.square(); - b = b.mul(z); + b = &b * &z; v = k; } } @@ -873,9 +881,9 @@ where } fn square(&self) -> Self { - // Calls our own FieldOps::mul, not Mul::mul (operator), to avoid the - // supertrait ambiguity documented in field_ops.rs. - ::mul(self, self) + // Calls our own FieldOps::mul through the operator overload, + // not Mul::mul (operator), to avoid the supertrait ambiguity documented in field_ops.rs. + self * self } fn double(&self) -> Self { @@ -933,7 +941,7 @@ where let mut result = self.clone(); let mut conj = self.frobenius(); for _ in 1..M { - result = ::mul(&result, &conj); + result = &result * &conj; conj = conj.frobenius(); } result @@ -946,7 +954,7 @@ where let mut result = self.clone(); let mut conj = self.frobenius(); for _ in 1..M { - result = ::add(&result, &conj); + result = &result + &conj; conj = conj.frobenius(); } result @@ -977,7 +985,7 @@ where ts_loop(&mut x, &w); CtOption::new( x, - x.mul(x).ct_eq(self), // Only return Some if it's the square root. + (::square(&x)).ct_eq(self), // Only return Some if it's the square root. ) } @@ -1011,15 +1019,19 @@ where let e1_limbs = e1.as_limbs().map(|limb| limb.0); // Compute x^(2^(S - 1) - 1) * (x * w^4)^(2^(S - 1)) - let myinv = self - .pow_vartime(&e1_limbs) - .mul((self.mul(&w.pow_vartime(&[4 as u64]))).pow_vartime(&e0_limbs)); + // pow_vartime? + + let first_term = self.pow_vartime(&e1_limbs); + let xw4 = self * &w.pow_vartime(&[4 as u64]); + let second_term = xw4.pow_vartime(&e0_limbs); + + let myinv = &first_term * &second_term; ( CtOption::new(myinv, is_invertible), CtOption::new( mysqrt, - mysqrt.mul(mysqrt).ct_eq(self), // Only return Some if it's the square root. + (&mysqrt * &mysqrt).ct_eq(self), // Only return Some if it's the square root. ), ) } @@ -1039,7 +1051,7 @@ where /// The inverse of the squareroot of `self` (type: `CtOption`) fn inv_sqrt(&self) -> CtOption { let (inv, sqrt) = self.inverse_and_sqrt(); - inv.and_then(|a| sqrt.map(|b| a * b)) + inv.and_then(|a| sqrt.map(|b| &a * &b)) } /// Inverse of `self` and squareroot of `rhs` in 1 exponentiation @@ -1057,17 +1069,18 @@ where fn invertme_sqrtother(&self, rhs: &Self) -> (CtOption, CtOption) { let is_invertible = !self.is_zero(); - let x = self.mul(self).mul(*rhs); + let x = rhs * &(self * self); + //self.mul(self).mul(*rhs); let (myinv, mysqrt) = x.inverse_and_sqrt(); let myinv_value = myinv.unwrap_or(Self::zero()); let mysqrt_value = mysqrt.unwrap_or(Self::zero()); - let inv_value = self.mul(rhs).mul(myinv_value); - let sqrt_value = inv_value.mul(mysqrt_value); + let inv_value = self * &(rhs * &myinv_value); + let sqrt_value = &inv_value * &mysqrt_value; let inv_is_some = is_invertible & myinv.is_some(); - let sqrt_is_some = inv_is_some & mysqrt.is_some() & (sqrt_value.mul(sqrt_value)).ct_eq(rhs); + let sqrt_is_some = inv_is_some & mysqrt.is_some() & (sqrt_value.square()).ct_eq(rhs); ( CtOption::new(inv_value, inv_is_some), @@ -1092,16 +1105,16 @@ where /// if `rhs` is invertible and the ratio has an FpM squareroot /// (type: `CtOption`) fn sqrt_ratio(&self, rhs: &Self) -> CtOption { - let x = self.mul(&self.mul(self)).mul(*rhs); + let x = self * &(&(self.square()) * rhs); let (myinv, mysqrt) = x.inverse_and_sqrt(); let myinv_value = myinv.unwrap_or(Self::zero()); let mysqrt_value = mysqrt.unwrap_or(Self::zero()); - let ans_value = self.mul(self).mul(myinv_value).mul(mysqrt_value); + let ans_value = &self.square() * &(&myinv_value * &mysqrt_value); let inv_is_some = myinv.is_some(); let ans_is_some = - inv_is_some & mysqrt.is_some() & (mysqrt_value.mul(mysqrt_value)).ct_eq(&x); + inv_is_some & mysqrt.is_some() & (mysqrt_value.square()).ct_eq(&x); CtOption::new(ans_value, ans_is_some) } diff --git a/fp/tests/f2_ext_tests.rs b/fp/tests/f2_ext_tests.rs index c5d0185..66bd4af 100644 --- a/fp/tests/f2_ext_tests.rs +++ b/fp/tests/f2_ext_tests.rs @@ -42,7 +42,7 @@ fn random_elements_test() { } let a_inv = a.invert().unwrap(); - let prod = a_inv * a; + let prod = &a_inv * &a; assert!(bool::from(prod.is_one())); } @@ -103,25 +103,25 @@ fn inv_zero_is_none() { fn add_coefficient_wise() { let a = elt(0b1_1111); // x^4 + x^3 + x^2 + x + 1 let b = elt(0b101_0111); // x^6 + x^4 + x^2 + x + 1 - let c = FieldOps::add(&a, &b); // x^6 + x^3 = 0b100_1000 + let c = &a + &b; // x^6 + x^3 = 0b100_1000 assert_eq!(c, elt(0b100_1000)); let d = elt(0b10_1010); // x^5 + x^3 + x let e = elt(0b1101_0100); // x^7 + x^6 + x^4 + x^2 - let f = FieldOps::add(&d, &e); // x^7 + x^6 + x^5 + x^4 + x^3 + x^2 + x = 0b1111_1110 + let f = &d + &e; // x^7 + x^6 + x^5 + x^4 + x^3 + x^2 + x = 0b1111_1110 assert_eq!(f, elt(0b1111_1110)); } #[test] fn negate_then_add_is_zero() { let a = elt(0b1_1111); // 1 + x + x^2 + x^3 + x^4 - assert!(bool::from(FieldOps::add(&a, &a.negate()).is_zero())); + assert!(bool::from((&a + &a.negate()).is_zero())); } #[test] fn add_zero_is_identity() { let a = elt(0b1_1111); // x^4 + x^3 + x^2 + x + 1 - assert_eq!(FieldOps::add(&a, &F256::zero()), a); + assert_eq!(&a + &F256::zero(), a); } #[test] @@ -137,7 +137,7 @@ fn double_equals_zero() { #[test] fn mul_by_one_is_identity() { let a = elt(0b1_1111); // x^4 + x^3 + x^2 + x + 1 - assert_eq!(FieldOps::mul(&a, &F256::one()), a); + assert_eq!(&a * &F256::one(), a); } #[test] @@ -154,15 +154,15 @@ fn reduce_concrete_values() { fn mul_concrete_values() { let a = elt(0b1_1001); // x^4 + x^3 + 1 let b = elt(0b101_0011); // x^6 + x^4 + x + 1 - let c = FieldOps::mul(&a, &b); // x^7 + x^5 + x^4 + x^3 + x = 0b1011_1010 + let c = &a * &b; // x^7 + x^5 + x^4 + x^3 + x = 0b1011_1010 assert_eq!(c, elt(0b1011_1010)); let d = elt(0b10_1010); // x^5 + x^3 + x let e = elt(0b1101_0100); // x^7 + x^6 + x^4 + x^2 - let f = FieldOps::mul(&d, &e); // x^7 + x^3 + x^2 = 0b1000_1100 + let f = &d * &e; // x^7 + x^3 + x^2 = 0b1000_1100 assert_eq!(f, elt(0b1000_1100)); - let g = FieldOps::mul(&b, &d); // x^7 + x^2 + x = 0b1000_0110 + let g = &b * &d; // x^7 + x^2 + x = 0b1000_0110 assert_eq!(g, elt(0b1000_0110)); } @@ -170,8 +170,7 @@ fn mul_concrete_values() { fn mul_commutativity() { let a = elt(0b1_1001); // x^4 + x^3 + 1 let b = elt(0b101_0011); // x^6 + x^4 + x + 1 - assert_eq!(FieldOps::mul(&a, &b), FieldOps::mul(&b, &a)); -} + assert_eq!(&a * &b, &b * &a);} #[test] fn mul_associativity() { @@ -179,8 +178,8 @@ fn mul_associativity() { let b = elt(0b101_0011); // x^6 + x^4 + x + 1 let c = FieldOps::mul(&a, &b); // x^7 + x^5 + x^4 + x^3 + x = 0b1011_1010 assert_eq!( - FieldOps::mul(&FieldOps::mul(&a, &b), &c), - FieldOps::mul(&a, &FieldOps::mul(&b, &c)) + &(&a * &b) * &c, + &a * &(&b * &c) ); } @@ -188,18 +187,17 @@ fn mul_associativity() { fn mul_distributivity() { let a = elt(0b1_1001); // x^4 + x^3 + 1 let b = elt(0b101_0011); // x^6 + x^4 + x + 1 - let c = FieldOps::mul(&a, &b); // x^7 + x^5 + x^4 + x^3 + x = 0b1011_1010 + let c = &a * &b; // x^7 + x^5 + x^4 + x^3 + x = 0b1011_1010 assert_eq!( - FieldOps::mul(&a, &FieldOps::add(&b, &c)), - FieldOps::add(&FieldOps::mul(&a, &b), &FieldOps::mul(&a, &c)) + &a * &(&b + &c), + &(&a * &b) + &(&a * &c) ); } #[test] fn square_equals_mul_self() { let a = elt(0b1_1001); // x^4 + x^3 + 1 - assert_eq!(a.square(), FieldOps::mul(&a, &a)); -} + assert_eq!(a.square(), &a * &a);} // ----------------------------------------------------------------------- // Inversion @@ -364,9 +362,9 @@ fn sqrt_of_product() { .expect("(x^6 + x^4 + x + 1) is a square in F_{256}"); // c = x^7 + x^5 + x^4 + x^3 + x, sqrt_c = x^6 + x^5 + x^4 + x^2 + x = 0b111_0110 - let c = FieldOps::mul(&a, &b); + let c = &a * &b; let sqrt_c = c .sqrt() .expect("(x^7 + x^5 + x^4 + x^3 + x) is a square in F_{256}"); - assert_eq!(sqrt_c, FieldOps::mul(&sqrt_a, &sqrt_b)); + assert_eq!(sqrt_c, &sqrt_a * &sqrt_b); } diff --git a/fp/tests/f2_tests.rs b/fp/tests/f2_tests.rs index 5e2004f..88c7b37 100644 --- a/fp/tests/f2_tests.rs +++ b/fp/tests/f2_tests.rs @@ -29,32 +29,17 @@ mod tests { fn add_mod_2() { // 0 + 1 ≡ 1 (mod 2) // 1 + 1 ≡ 0 (mod 2) - assert_eq!( - F2Element::from_u64(0) + F2Element::from_u64(1), - F2Element::ONE - ); - assert_eq!( - F2Element::from_u64(1) + F2Element::from_u64(1), - F2Element::ZERO - ); + assert_eq!(&F2Element::from_u64(0) + &F2Element::from_u64(1), F2Element::ONE); + assert_eq!(&F2Element::from_u64(1) + &F2Element::from_u64(1), F2Element::ZERO); } #[test] fn mul_mod_2() { // 0 * 0 ≡ 0 (mod 2) // 0 * 1 ≡ 0 (mod 2) // 1 * 1 ≡ 1 (mod 2) - assert_eq!( - F2Element::from_u64(0) * F2Element::from_u64(0), - F2Element::ZERO - ); - assert_eq!( - F2Element::from_u64(0) * F2Element::from_u64(1), - F2Element::ZERO - ); - assert_eq!( - F2Element::from_u64(1) * F2Element::from_u64(1), - F2Element::ONE - ); + assert_eq!(&F2Element::from_u64(0) * &F2Element::from_u64(0), F2Element::ZERO); + assert_eq!(&F2Element::from_u64(0) * &F2Element::from_u64(1), F2Element::ZERO); + assert_eq!(&F2Element::from_u64(1) * &F2Element::from_u64(1), F2Element::ONE); } #[test] fn neg_mod_2() { diff --git a/fp/tests/fp_ext_tests.rs b/fp/tests/fp_ext_tests.rs index 2ee34f0..725ba4e 100644 --- a/fp/tests/fp_ext_tests.rs +++ b/fp/tests/fp_ext_tests.rs @@ -91,7 +91,7 @@ fn random_elements_test() { } let a_inv = a.invert().unwrap(); - let prod = a_inv * a; + let prod = &a_inv * &a; assert!(bool::from(prod.is_one())); } @@ -141,20 +141,20 @@ fn nonzero_is_not_zero() { #[test] fn add_coefficient_wise() { - let c = FieldOps::add(&el(3, 2), &el(1, 1)); + let c = &el(3, 2) + &el(1, 1); assert_eq!(c.coeffs[0].as_limbs()[0], 4); assert_eq!(c.coeffs[1].as_limbs()[0], 3); } #[test] fn add_wraps_mod_p() { - let c = FieldOps::add(&el(18, 0), &el(5, 0)); + let c = &el(18, 0) + &el(5, 0); assert_eq!(c.coeffs[0].as_limbs()[0], 4); } #[test] fn sub_coefficient_wise() { - let c = FieldOps::sub(&el(5, 4), &el(2, 1)); + let c = &el(5, 4) - &el(2, 1); assert_eq!(c.coeffs[0].as_limbs()[0], 3); assert_eq!(c.coeffs[1].as_limbs()[0], 3); } @@ -162,19 +162,19 @@ fn sub_coefficient_wise() { #[test] fn negate_then_add_is_zero() { let a = el(7, 3); - assert!(bool::from(FieldOps::add(&a, &a.negate()).is_zero())); + assert!(bool::from((&a + &a.negate()).is_zero())); } #[test] fn add_zero_is_identity() { let a = el(5, 11); - assert_eq!(FieldOps::add(&a, &F19_2::zero()), a); + assert_eq!(&a + &F19_2::zero(), a); } #[test] fn double_equals_add_self() { let a = el(3, 7); - assert_eq!(a.double(), FieldOps::add(&a, &a)); + assert_eq!(a.double(), &a + &a); } // ----------------------------------------------------------------------- @@ -184,13 +184,13 @@ fn double_equals_add_self() { #[test] fn mul_by_one_is_identity() { let a = el(5, 11); - assert_eq!(FieldOps::mul(&a, &F19_2::one()), a); + assert_eq!(&a * &F19_2::one(), a); } #[test] fn mul_x_squared_reduces_to_minus_one() { let x = el(0, 1); - let r = FieldOps::mul(&x, &x); + let r = &x * &x; assert_eq!(r.coeffs[0].as_limbs()[0], 18); assert!(bool::from(r.coeffs[1].is_zero())); } @@ -198,7 +198,7 @@ fn mul_x_squared_reduces_to_minus_one() { #[test] fn mul_concrete_values() { // (3+2x)(1+x) = 1 + 5x - let r = FieldOps::mul(&el(3, 2), &el(1, 1)); + let r = &el(3, 2) * &el(1, 1); assert_eq!(r.coeffs[0].as_limbs()[0], 1); assert_eq!(r.coeffs[1].as_limbs()[0], 5); } @@ -207,7 +207,7 @@ fn mul_concrete_values() { fn mul_commutativity() { let a = el(7, 3); let b = el(2, 11); - assert_eq!(FieldOps::mul(&a, &b), FieldOps::mul(&b, &a)); + assert_eq!(&a * &b, &b * &a); } #[test] @@ -216,8 +216,8 @@ fn mul_associativity() { let b = el(3, 4); let c = el(5, 6); assert_eq!( - FieldOps::mul(&FieldOps::mul(&a, &b), &c), - FieldOps::mul(&a, &FieldOps::mul(&b, &c)) + &(&a * &b) * &c, + &a * &(&b * &c) ); } @@ -227,15 +227,15 @@ fn mul_distributivity() { let b = el(1, 5); let c = el(4, 7); assert_eq!( - FieldOps::mul(&a, &FieldOps::add(&b, &c)), - FieldOps::add(&FieldOps::mul(&a, &b), &FieldOps::mul(&a, &c)) + &a * &(&b + &c), + &(&a * &b) + &(&a * &c) ); } #[test] fn square_equals_mul_self() { let a = el(4, 9); - assert_eq!(a.square(), FieldOps::mul(&a, &a)); + assert_eq!(a.square(), &a * &a); } // ----------------------------------------------------------------------- @@ -262,7 +262,7 @@ fn invert_correctness() { let a = el(a0, a1); let inv = a.invert().unwrap(); assert!( - bool::from(FieldOps::mul(&a, &inv).is_one()), + bool::from((&a * &inv).is_one()), "inv failed for ({},{})", a0, a1 @@ -407,7 +407,7 @@ fn inv_and_sqrt_test_quad() { let mut a = el(x, y); a = a.pow(&[2]); let (myinv, mysqrt) = a.inverse_and_sqrt(); - let should_be_one = FieldOps::mul(&a, &myinv.unwrap()); + let should_be_one = &a * &myinv.unwrap(); assert_eq!(mysqrt.unwrap().pow(&[2]), a); assert!(bool::from(should_be_one.is_one())); } @@ -444,7 +444,7 @@ fn invme_sqrtother_test_quad() { let mut b = el(x, y); b = b.pow(&[2]); let (myinv, mysqrt) = a.invertme_sqrtother(&b); - let should_be_one = FieldOps::mul(&a, &myinv.unwrap()); + let should_be_one = &a * &myinv.unwrap(); assert_eq!(mysqrt.unwrap().pow(&[2]), b); assert!(bool::from(should_be_one.is_one())); } @@ -551,7 +551,7 @@ fn cubic_zero_one() { fn cubic_x_cubed_reduces() { // x³ ≡ 2 mod (x³−2) let x = el3(0, 1, 0); - let x3 = FieldOps::mul(&FieldOps::mul(&x, &x), &x); + let x3 = &(&x * &x) * &x; assert_eq!(x3.coeffs[0].as_limbs()[0], 2); assert!(bool::from(x3.coeffs[1].is_zero())); assert!(bool::from(x3.coeffs[2].is_zero())); @@ -561,14 +561,14 @@ fn cubic_x_cubed_reduces() { fn cubic_mul_commutativity() { let a = el3(1, 2, 3); let b = el3(4, 5, 6); - assert_eq!(FieldOps::mul(&a, &b), FieldOps::mul(&b, &a)); + assert_eq!(&a * &b, &b * &a); } #[test] fn cubic_invert_correctness() { let a = el3(1, 2, 3); assert!(bool::from( - FieldOps::mul(&a, &a.invert().expect("invertible")).is_one() + (&a * &a.invert().expect("invertible")).is_one() )); } @@ -614,7 +614,7 @@ fn inv_and_sqrt_test_cubic() { let mut a = el3(x, y, z); a = a.pow(&[2]); let (myinv, mysqrt) = a.inverse_and_sqrt(); - let should_be_one = FieldOps::mul(&a, &myinv.unwrap()); + let should_be_one = &a * &myinv.unwrap(); assert_eq!(mysqrt.unwrap().pow(&[2]), a); assert!(bool::from(should_be_one.is_one())); } @@ -648,7 +648,7 @@ fn invme_sqrtother_test_cubic() { let mut b = el3(x, y, z); b = b.pow(&[2]); let (myinv, mysqrt) = a.invertme_sqrtother(&b); - let should_be_one = FieldOps::mul(&a, &myinv.unwrap()); + let should_be_one = &a * &myinv.unwrap(); assert_eq!(mysqrt.unwrap().pow(&[2]), b); assert!(bool::from(should_be_one.is_one())); } diff --git a/fp/tests/fp_tests.rs b/fp/tests/fp_tests.rs index 73b108f..6412293 100644 --- a/fp/tests/fp_tests.rs +++ b/fp/tests/fp_tests.rs @@ -46,7 +46,7 @@ fn add_mod_p() { // 17 + 5 = 22 \equiv 3 (mod 19) let a = F19::from_u64(17); let b = F19::from_u64(5); - assert_eq!((a + b).as_limbs()[0], 3); + assert_eq!((&a + &b).as_limbs()[0], 3); } #[test] @@ -54,7 +54,7 @@ fn sub_mod_p() { // 3 − 7 = −4 \equiv 15 (mod 19) let a = F19::from_u64(3); let b = F19::from_u64(7); - assert_eq!((a - b).as_limbs()[0], 15); + assert_eq!((&a - &b).as_limbs()[0], 15); } #[test] @@ -62,14 +62,14 @@ fn mul_mod_p() { // 7 x 8 = 56 \equiv 18 (mod 19) let a = F19::from_u64(7); let b = F19::from_u64(8); - assert_eq!((a * b).as_limbs()[0], 18); + assert_eq!((&a * &b).as_limbs()[0], 18); } #[test] fn neg_mod_p() { // −3 \equiv 16 (mod 19) let a = F19::from_u64(3); - assert_eq!((-a).as_limbs()[0], 16); + assert_eq!((-&a).as_limbs()[0], 16); } #[test] @@ -91,7 +91,7 @@ fn inv_works() { // 7 × 7^-1 \equiv 1 (mod 19) let a = F19::from_u64(7); let inv = a.invert().unwrap(); - assert_eq!((a * inv).as_limbs()[0], 1); + assert_eq!((&a * &inv).as_limbs()[0], 1); } #[test] @@ -129,5 +129,5 @@ fn sqrt_of_qr() { // √4 = 2 (mod 19) let four = F19::from_u64(4); let root = four.sqrt().expect("4 is a QR mod 19"); - assert_eq!((root * root).as_limbs()[0], 4); + assert_eq!((&root * &root).as_limbs()[0], 4); } \ No newline at end of file diff --git a/isogeny/Cargo.toml b/isogeny/Cargo.toml index a06511d..9e9b4c8 100644 --- a/isogeny/Cargo.toml +++ b/isogeny/Cargo.toml @@ -20,6 +20,7 @@ path = "src/lib.rs" fp = { workspace = true } ec = { workspace = true } crypto-bigint = { workspace = true } +subtle = "2.6" [package.metadata.docs.rs] rustdoc-args = ["--html-in-header", "katex-header.html"] diff --git a/isogeny/src/isogeny_ops.rs b/isogeny/src/isogeny_ops.rs new file mode 100644 index 0000000..34af953 --- /dev/null +++ b/isogeny/src/isogeny_ops.rs @@ -0,0 +1,232 @@ +//! Generic isogeny abstraction. +//! +//! # Overview +//! +//! An isogeny is a morphism of elliptic curves +//! +//! ```text +//! φ : E -> E' +//! ``` +//! +//! that is also a group homomorphism. +//! +//! This trait is meant to capture the **minimal common interface** +//! that any concrete isogeny implementation should expose: +//! +//! - its base field, +//! - its domain and codomain curves, +//! - the point representations used on those curves, +//! - its degree, +//! - evaluation on points, +//! - and whether it is separable. +//! +//! # Why separate `DomainPoint` and `CodomainPoint`? +//! +//! In principle, the source and target curves may use different native +//! point representations. +//! +//! For example: +//! - the domain could use affine Weierstrass points, +//! - the codomain could use projective points, +//! - or an x-only Montgomery/Kummer representation. +//! +//! So we keep both associated types explicit instead of assuming that the +//! same point type is used on both sides. + +use fp::field_ops::FieldOps; +use ec::point_ops::PointOps; +use ec::curve_ops::Curve; +use subtle::Choice; + +/// Generic interface for an isogeny. +/// +/// An implementation of this trait represents a map +/// +/// ```text +/// φ : E -> E' +/// ``` +/// +/// together with the information needed to evaluate it on points. +/// +/// The trait is intentionally small: it only describes the common +/// functionality of isogenies, without forcing a particular internal +/// representation (kernel-based, Vélu formulas, x-only formulas, chains, etc.). +pub trait IsogenyOps: Clone { + /// Base field over which both the domain and codomain curves are defined. + type BaseField: FieldOps; + + /// Domain curve `E` of the isogeny `φ : E -> E'`. + type DomainCurve: Curve; + + /// Codomain curve `E'` of the isogeny `φ : E -> E'`. + type CodomainCurve: Curve; + + /// Native point representation used when evaluating points on the domain. + type DomainPoint: PointOps< + Curve = Self::DomainCurve, + BaseField = Self::BaseField, + >; + + /// Native point representation produced when evaluating the isogeny. + type CodomainPoint: PointOps< + Curve = Self::CodomainCurve, + BaseField = Self::BaseField, + >; + + /// Return the domain curve of the isogeny. + fn domain(&self) -> &Self::DomainCurve; + + /// Return the codomain curve of the isogeny. + fn codomain(&self) -> &Self::CodomainCurve; + + /// Return the degree of the isogeny. + /// + /// For a composition `ψ ∘ φ`, the degree should be + /// + /// ```text + /// deg(ψ ∘ φ) = deg(ψ) deg(φ). + /// ``` + fn degree(&self) -> u64; + + /// Evaluate the isogeny at a point of the domain curve. + /// + /// If `φ : E -> E'` and `P ∈ E`, this returns `φ(P) ∈ E'`. + fn evaluate(&self, p: &Self::DomainPoint) -> Self::CodomainPoint; + + /// Return whether the isogeny is separable. + /// + /// In many protocol settings one mainly works with separable isogenies, + /// but we keep this as an explicit property of the map. + fn is_separable(&self) -> Choice; +} + +/// Extension trait for isogenies whose dual isogeny is available. +/// +/// If +/// +/// ```text +/// φ : E -> E' +/// ``` +/// +/// is an isogeny, then its dual +/// +/// ```text +/// φ^∨ : E' -> E +/// ``` +/// +/// goes in the opposite direction, has the same degree, and satisfies +/// the standard duality relations. +pub trait DualIsogenyOps: IsogenyOps { + /// Type of the dual isogeny. + /// + /// Note how domain and codomain are swapped, and likewise the point + /// representations on the source and target. + type Dual: IsogenyOps< + BaseField = Self::BaseField, + DomainCurve = Self::CodomainCurve, + CodomainCurve = Self::DomainCurve, + DomainPoint = Self::CodomainPoint, + CodomainPoint = Self::DomainPoint, + >; + + /// Return the dual isogeny. + fn dual(&self) -> Self::Dual; +} + +/// A wrapper representing the composition of two isogenies. +/// +/// If +/// +/// ```text +/// first : E -> E' +/// second : E' -> E'', +/// ``` +/// +/// then `CompositeIsogeny { first, second }` represents +/// +/// ```text +/// second ∘ first : E -> E''. +/// ``` +/// +/// # Why a wrapper? +/// +/// This is a very convenient generic representation of composition: +/// instead of trying to "simplify" the composite into a single special +/// formula, we just store the two maps and evaluate them one after the other. +/// +/// This is often the cleanest first design, especially when different +/// concrete isogeny families may be composed together. +#[derive(Clone)] +pub struct CompositeIsogeny { + /// The first isogeny in the composition. + pub first: I1, + + /// The second isogeny in the composition. + pub second: I2, +} + +impl IsogenyOps for CompositeIsogeny +where +// The first map is an arbitrary isogeny + I1: IsogenyOps, + +// The second map must start where the first one ends. +// +// Concretely, if +// first : E -> E' +// then we require +// second : E' -> E'' +// +// This means: +// - same base field, +// - domain curve of `second` = codomain curve of `first`, +// - domain point type of `second` = codomain point type of `first`. + I2: IsogenyOps< + BaseField = I1::BaseField, + DomainCurve = I1::CodomainCurve, + DomainPoint = I1::CodomainPoint, + >, +{ + /// The composite map is still defined over the same base field. + type BaseField = I1::BaseField; + + /// The domain of `second ∘ first` is the domain of `first`. + type DomainCurve = I1::DomainCurve; + + /// The codomain of `second ∘ first` is the codomain of `second`. + type CodomainCurve = I2::CodomainCurve; + + /// Points fed into the composite are points of the domain of `first`. + type DomainPoint = I1::DomainPoint; + + /// Points output by the composite are points of the codomain of `second`. + type CodomainPoint = I2::CodomainPoint; + + fn domain(&self) -> &Self::DomainCurve { + self.first.domain() + } + + fn codomain(&self) -> &Self::CodomainCurve { + self.second.codomain() + } + + fn degree(&self) -> u64 { + // By multiplicativity of degree: + // deg(second ∘ first) = deg(second) * deg(first) + self.first.degree() * self.second.degree() + } + + fn evaluate(&self, p: &Self::DomainPoint) -> Self::CodomainPoint { + // First apply `first : E -> E'` + let q = self.first.evaluate(p); + + // Then apply `second : E' -> E''` + self.second.evaluate(&q) + } + + fn is_separable(&self) -> Choice { + // A composition of separable isogenies is separable. + // So the composite is separable exactly when both factors are. + self.first.is_separable() & self.second.is_separable() + } +} \ No newline at end of file diff --git a/isogeny/src/lib.rs b/isogeny/src/lib.rs index 12de6c6..60ab3b5 100644 --- a/isogeny/src/lib.rs +++ b/isogeny/src/lib.rs @@ -7,3 +7,5 @@ pub mod isogeny; pub mod kernel; +mod isogeny_ops; +mod montgomery; diff --git a/isogeny/src/montgomery.rs b/isogeny/src/montgomery.rs new file mode 100644 index 0000000..c10020c --- /dev/null +++ b/isogeny/src/montgomery.rs @@ -0,0 +1,84 @@ +/* + +//! Isogenies for montgomery curves +//! +//! AT THE MOMENT THIS WORKS ONLY IN ODD CHARACTERISTIC!!! + + +use subtle::{ConstantTimeEq}; + +use fp::field_ops::FieldOps; +use ec::curve_montgomery::MontgomeryCurve; +use ec::point_montgomery::KummerPoint; +use fp::{ref_field_impl, ref_field_trait_impl}; + +#[derive(Debug, Clone)] +pub struct MontgomeryIsogeny { + pub domain: MontgomeryCurve, + pub codomain: MontgomeryCurve, + // x-coordinate of the generator of the kernel + pub xk: KummerPoint, +} + + +// --------------------------------------------------------------------------- +// Manual trait impls +// --------------------------------------------------------------------------- + +ref_field_trait_impl! { + impl PartialEq for MontgomeryIsogeny { + fn eq(&self, other: &Self) -> bool { + self.domain == other.domain && self.codomain == other.codomain && self.xk == other.xk + } + } +} + + +impl Eq for MontgomeryIsogeny +where + F: FieldOps + ConstantTimeEq, +{ } + + +// --------------------------------------------------------------------------- +// Constructors +// --------------------------------------------------------------------------- + + +ref_field_impl! { + impl MontgomeryIsogeny { + pub fn new(domain: MontgomeryCurve, xk: KummerPoint) -> Self { + let (a_codomain, b_codomain) = Self::new_codomain_helper(&domain, xk); + Self{ + domain, + codomain: MontgomeryCurve::new(a_codomain, b_codomain), + xk + } + } + + fn new_codomain_helper(domain: & MontgomeryCurve, xk: KummerPoint) -> (F, F) { + todo!() + } + } +} + + + + +// --------------------------------------------------------------------------- +// Constant-time functionalities +// --------------------------------------------------------------------------- + + + + + + + + + + + + + +*/ \ No newline at end of file