From a096743a1ac9feaf98859e2a2c393564d3de7982 Mon Sep 17 00:00:00 2001 From: MartinAzon Date: Wed, 15 Apr 2026 14:10:57 +0200 Subject: [PATCH 01/10] Working on isogeny_ops --- ec/src/point_ops.rs | 2 +- isogeny/Cargo.toml | 1 + isogeny/src/isogeny_ops.rs | 36 ++++++++++++++++++++++++++++++++++++ isogeny/src/lib.rs | 1 + 4 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 isogeny/src/isogeny_ops.rs diff --git a/ec/src/point_ops.rs b/ec/src/point_ops.rs index ff15004..a88b0d7 100644 --- a/ec/src/point_ops.rs +++ b/ec/src/point_ops.rs @@ -13,7 +13,7 @@ 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 { type BaseField: FieldOps; type Curve; diff --git a/isogeny/Cargo.toml b/isogeny/Cargo.toml index 3d58743..ecd7aa4 100644 --- a/isogeny/Cargo.toml +++ b/isogeny/Cargo.toml @@ -9,6 +9,7 @@ license.workspace = true fp = { workspace = true } ec = { workspace = true } crypto-bigint = { workspace = true } +subtle = "2.6" [dev-dependencies] # Add test-only dependencies here as needed diff --git a/isogeny/src/isogeny_ops.rs b/isogeny/src/isogeny_ops.rs new file mode 100644 index 0000000..5ea5366 --- /dev/null +++ b/isogeny/src/isogeny_ops.rs @@ -0,0 +1,36 @@ +//! Generic isogeny abstraction + + + +use fp::field_ops::FieldOps; +use ec::point_ops::PointOps; +use ec::curve_ops::Curve; +use subtle::{Choice, ConditionallySelectable}; + +pub trait IsogenyOps: Clone + ConditionallySelectable { + type BaseField: FieldOps; + type DomainCurve: Curve; + type CodomainCurve: Curve; + type DomainPoint: PointOps; + type CodomainPoint: PointOps; + + fn domain(&self) -> &Self::DomainCurve; + fn codomain(&self) -> &Self::CodomainCurve; + fn degree(&self) -> u64; + fn evaluate(&self, p: &Self::DomainPoint) -> Self::CodomainPoint; + fn is_separable(&self) -> Choice; + //fn dual(&self) -> Option; + //fn compose(&self, other: &Self) -> Option; +} + +pub trait DualIsogenyOps: IsogenyOps { + type Dual: IsogenyOps< + BaseField = Self::BaseField, + DomainCurve = Self::CodomainCurve, + CodomainCurve = Self::DomainCurve, + DomainPoint = Self::CodomainPoint, + CodomainPoint = Self::DomainPoint, + >; + + fn dual(&self) -> Self::Dual; +} \ No newline at end of file diff --git a/isogeny/src/lib.rs b/isogeny/src/lib.rs index 12de6c6..12fa19d 100644 --- a/isogeny/src/lib.rs +++ b/isogeny/src/lib.rs @@ -7,3 +7,4 @@ pub mod isogeny; pub mod kernel; +mod isogeny_ops; From 236d455e7e075f569615a72dcaf7485c22ef3080 Mon Sep 17 00:00:00 2001 From: MartinAzon Date: Wed, 15 Apr 2026 14:57:57 +0200 Subject: [PATCH 02/10] added isogeny_ops --- isogeny/src/isogeny_ops.rs | 214 +++++++++++++++++++++++++++++++++++-- 1 file changed, 205 insertions(+), 9 deletions(-) diff --git a/isogeny/src/isogeny_ops.rs b/isogeny/src/isogeny_ops.rs index 5ea5366..34af953 100644 --- a/isogeny/src/isogeny_ops.rs +++ b/isogeny/src/isogeny_ops.rs @@ -1,29 +1,126 @@ -//! Generic isogeny abstraction - - +//! 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, ConditionallySelectable}; +use subtle::Choice; -pub trait IsogenyOps: Clone + ConditionallySelectable { +/// 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; - type DomainPoint: PointOps; - type CodomainPoint: PointOps; + /// 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; - //fn dual(&self) -> Option; - //fn compose(&self, other: &Self) -> Option; } +/// 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, @@ -32,5 +129,104 @@ pub trait DualIsogenyOps: IsogenyOps { 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 From 65c6098bcc256a49fd59e1863108922d38de576e Mon Sep 17 00:00:00 2001 From: MartinAzon Date: Thu, 16 Apr 2026 10:27:50 +0200 Subject: [PATCH 03/10] ct functionalities for curves --- ec/src/curve_edwards.rs | 42 +++++++++++++++- ec/src/curve_jacobi_intersection.rs | 38 ++++++++++++++- ec/src/curve_jacobi_quartic.rs | 43 ++++++++++++++++- ec/src/curve_montgomery.rs | 49 +++++++++++++++++-- ec/src/curve_weierstrass.rs | 56 +++++++++++++++++++++- ec/src/point_montgomery.rs | 5 +- isogeny/src/lib.rs | 1 + isogeny/src/montgomery.rs | 74 +++++++++++++++++++++++++++++ 8 files changed, 293 insertions(+), 15 deletions(-) create mode 100644 isogeny/src/montgomery.rs diff --git a/ec/src/curve_edwards.rs b/ec/src/curve_edwards.rs index b268ba2..ab2b8ff 100644 --- a/ec/src/curve_edwards.rs +++ b/ec/src/curve_edwards.rs @@ -22,6 +22,7 @@ //! "Binary Edwards Curves", 2008. //! Reference (odd char): +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; use fp::field_ops::FieldOps; use crate::curve_ops::Curve; @@ -31,7 +32,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 { pub d1: F, pub d2: F, @@ -134,3 +135,42 @@ impl Curve for EdwardsCurve { } } } + + + +// --------------------------------------------------------------------------- +// 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 903a9bb..e6c55ac 100644 --- a/ec/src/curve_jacobi_intersection.rs +++ b/ec/src/curve_jacobi_intersection.rs @@ -17,14 +17,14 @@ //! ``` //! +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; use fp::field_ops::FieldOps; - use crate::curve_ops::Curve; use crate::curve_weierstrass::WeierstrassCurve; use crate::point_jacobi_intersection::JacobiIntersectionPoint; /// A Jacobi-intersection curve. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Copy)] pub struct JacobiIntersectionCurve { pub a: F, } @@ -83,3 +83,37 @@ impl Curve for JacobiIntersectionCurve { JacobiIntersectionCurve::a_invariants(self).to_vec() } } + + + +// --------------------------------------------------------------------------- +// 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 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 cfb63d3..ad62cb7 100644 --- a/ec/src/curve_jacobi_quartic.rs +++ b/ec/src/curve_jacobi_quartic.rs @@ -13,8 +13,8 @@ //! “extended Jacobi quartic” family with arbitrary `a` and `d` satisfying //! `d(a²-d) ≠ 0`. +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; use fp::field_ops::FieldOps; - use crate::curve_ops::Curve; use crate::point_jacobi_quartic::JacobiQuarticPoint; @@ -23,7 +23,7 @@ use crate::point_jacobi_quartic::JacobiQuarticPoint; /// ```text /// y² = d x⁴ + 2 a x² + 1 /// ``` -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Copy)] pub struct JacobiQuarticCurve { pub a: F, pub d: F, @@ -114,3 +114,42 @@ impl Curve for JacobiQuarticCurve { 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) } +} diff --git a/ec/src/curve_montgomery.rs b/ec/src/curve_montgomery.rs index 03b6b37..cdaaeb5 100644 --- a/ec/src/curve_montgomery.rs +++ b/ec/src/curve_montgomery.rs @@ -34,9 +34,8 @@ //! additions and doublings, which is especially convenient for constant-time //! scalar multiplication. - +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; use fp::field_ops::FieldOps; - use crate::curve_ops::Curve; use crate::point_montgomery::KummerPoint; @@ -47,7 +46,7 @@ use crate::point_montgomery::KummerPoint; /// ``` /// /// over a field `F`. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Copy)] pub struct MontgomeryCurve { pub a: F, pub b: F, @@ -179,4 +178,46 @@ impl Curve for MontgomeryCurve { fn a_invariants(&self) -> Vec { MontgomeryCurve::a_invariants(self).to_vec() } -} \ No newline at end of file +} + + + +// --------------------------------------------------------------------------- +// 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 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); + } + + 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) } +} + + + diff --git a/ec/src/curve_weierstrass.rs b/ec/src/curve_weierstrass.rs index 0651e4a..a20a68e 100644 --- a/ec/src/curve_weierstrass.rs +++ b/ec/src/curve_weierstrass.rs @@ -20,8 +20,8 @@ //! //! via the convenience constructor [`WeierstrassCurve::new_short`]. +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; use fp::field_ops::FieldOps; - use crate::curve_ops::Curve; use crate::point_weierstrass::AffinePoint; @@ -33,7 +33,7 @@ use crate::point_weierstrass::AffinePoint; /// /// All five coefficients are stored explicitly; the short Weierstrass case /// simply has `a1 = a2 = a3 = 0`. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Copy)] pub struct WeierstrassCurve { pub a1: F, pub a2: F, @@ -277,3 +277,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_montgomery.rs b/ec/src/point_montgomery.rs index 0dde18a..7b3f258 100644 --- a/ec/src/point_montgomery.rs +++ b/ec/src/point_montgomery.rs @@ -50,10 +50,7 @@ pub struct KummerPoint { // Manual trait impls // --------------------------------------------------------------------------- -impl PartialEq for KummerPoint -where - F: FieldOps + ConstantTimeEq, -{ +impl PartialEq for KummerPoint { /// Equality of projective x-line points. /// /// A standard criterion is cross-multiplication: diff --git a/isogeny/src/lib.rs b/isogeny/src/lib.rs index 12fa19d..60ab3b5 100644 --- a/isogeny/src/lib.rs +++ b/isogeny/src/lib.rs @@ -8,3 +8,4 @@ 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..646b1fe --- /dev/null +++ b/isogeny/src/montgomery.rs @@ -0,0 +1,74 @@ +//! Isogenies for montgomery curves +//! +//! AT THE MOMENT THIS WORKS ONLY IN ODD CHARACTERISTIC!!! + + +use subtle::{Choice, CtOption, ConditionallySelectable, ConstantTimeEq}; + +use fp::field_ops::FieldOps; +use ec::curve_montgomery::MontgomeryCurve; +use ec::point_montgomery::KummerPoint; + +#[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 +// --------------------------------------------------------------------------- + +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 +// --------------------------------------------------------------------------- + +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 +// --------------------------------------------------------------------------- + + + + + + + + + + + + + From ca546a3bf99f2c9a91a2384c1a180ac889563292 Mon Sep 17 00:00:00 2001 From: MartinAzon Date: Thu, 16 Apr 2026 13:38:43 +0200 Subject: [PATCH 04/10] op overloads take refs as input (works only in fp so far) --- fp/src/f2_element.rs | 44 ++++++++++-------- fp/src/f2_ext.rs | 81 ++++++++++++++++++++++++++++++++- fp/src/field_ops.rs | 11 +++-- fp/src/fp_element.rs | 46 ++++++++++--------- fp/src/fp_ext.rs | 97 +++++++++++++++++++++++----------------- fp/tests/f2_ext_tests.rs | 34 +++++++------- fp/tests/f2_tests.rs | 10 ++--- fp/tests/fp_ext_tests.rs | 46 +++++++++---------- fp/tests/fp_tests.rs | 12 ++--- 9 files changed, 241 insertions(+), 140 deletions(-) diff --git a/fp/src/f2_element.rs b/fp/src/f2_element.rs index f8b45ac..8a39b56 100644 --- a/fp/src/f2_element.rs +++ b/fp/src/f2_element.rs @@ -79,40 +79,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 4db2833..333789c 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 // --------------------------------------------------------------------------- @@ -300,6 +301,85 @@ where // =========================================================================== // Operator overloads (delegate to the FieldOps methods below) // =========================================================================== + +impl<'a, 'b, const LIMBS: usize, P> Add<&'b F2Ext> +for &'a F2Ext +where + P: BinaryIrreducible, +{ + type Output = F2Ext; + + fn add(self, rhs: &'b F2Ext) -> Self::Output { + as FieldOps>::add(self, rhs) + } +} + +impl<'a, 'b, const LIMBS: usize, P> Sub<&'b F2Ext> +for &'a F2Ext +where + P: BinaryIrreducible, +{ + type Output = F2Ext; + + fn sub(self, rhs: &'b F2Ext) -> Self::Output { + as FieldOps>::sub(self, rhs) + } +} + + +impl<'a, 'b, const LIMBS: usize, P> Mul<&'b F2Ext> +for &'a F2Ext +where + P: BinaryIrreducible, +{ + type Output = F2Ext; + + fn mul(self, rhs: &'b F2Ext) -> Self::Output { + as FieldOps>::mul(self, rhs) + } +} + +impl<'a, const LIMBS: usize, P> Neg for &'a F2Ext +where + P: BinaryIrreducible, +{ + type Output = F2Ext; + + fn neg(self) -> Self::Output { + as FieldOps>::negate(self) + } +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + impl Add for F2Ext where @@ -349,7 +429,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 d824f2e..444dea7 100644 --- a/fp/src/field_ops.rs +++ b/fp/src/field_ops.rs @@ -8,14 +8,13 @@ pub trait FieldOps: + Clone + PartialEq + Eq - + Add - + Sub - + Mul - + Neg + Default + ConditionallySelectable -// const time impl will have the following trait too -// + ConditionallySelectable +where + for<'a, 'b> &'a Self: Add<&'b Self, Output = Self>, + for<'a, 'b> &'a Self: Sub<&'b Self, Output = Self>, + for<'a, 'b> &'a Self: Mul<&'b Self, Output = Self>, + for<'a> &'a Self: Neg, { fn zero() -> Self; fn one() -> Self; diff --git a/fp/src/fp_element.rs b/fp/src/fp_element.rs index 3f0c23f..fc31c0f 100644 --- a/fp/src/fp_element.rs +++ b/fp/src/fp_element.rs @@ -115,49 +115,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 1c25af0..469193d 100644 --- a/fp/src/fp_ext.rs +++ b/fp/src/fp_ext.rs @@ -571,58 +571,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 // =========================================================================== @@ -653,8 +660,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() { @@ -674,10 +682,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; } } @@ -743,9 +751,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 { @@ -803,7 +811,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 @@ -816,7 +824,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 @@ -847,7 +855,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. ) } @@ -880,15 +888,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. ), ) } @@ -908,7 +920,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 @@ -926,17 +938,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), @@ -961,16 +974,16 @@ where /// if `rhs` is invertible and the ratio has an FpM squareroot /// (type: (CtOption, 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 0f709d7..16ef1bc 100644 --- a/fp/tests/f2_ext_tests.rs +++ b/fp/tests/f2_ext_tests.rs @@ -70,25 +70,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] @@ -105,7 +105,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); } @@ -124,16 +124,16 @@ 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)); } @@ -141,7 +141,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] @@ -150,8 +150,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) ); } @@ -159,17 +159,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); } @@ -328,7 +328,7 @@ fn sqrt_of_product() { let sqrt_b = b.sqrt().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); } \ No newline at end of file diff --git a/fp/tests/f2_tests.rs b/fp/tests/f2_tests.rs index 969db14..cb04f99 100644 --- a/fp/tests/f2_tests.rs +++ b/fp/tests/f2_tests.rs @@ -16,16 +16,16 @@ mod tests { #[test] 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() { assert_eq!(F2Element::ONE.negate(), F2Element::ONE);} #[test] fn inv_mod_2() { assert_eq!(F2Element::ONE.invert().unwrap(), F2Element::ONE);} diff --git a/fp/tests/fp_ext_tests.rs b/fp/tests/fp_ext_tests.rs index 02a064d..562d82d 100644 --- a/fp/tests/fp_ext_tests.rs +++ b/fp/tests/fp_ext_tests.rs @@ -83,20 +83,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); } @@ -104,19 +104,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); } // ----------------------------------------------------------------------- @@ -126,13 +126,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())); } @@ -140,7 +140,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); } @@ -149,7 +149,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] @@ -158,8 +158,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) ); } @@ -169,15 +169,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); } // ----------------------------------------------------------------------- @@ -204,7 +204,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 @@ -335,7 +335,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())); } @@ -372,7 +372,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())); } @@ -479,7 +479,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())); @@ -489,14 +489,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() )); } @@ -542,7 +542,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())); } @@ -576,7 +576,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 From effbb29f15a906bd5dd47ae24633cfa8b6237e4d Mon Sep 17 00:00:00 2001 From: MartinAzon Date: Thu, 16 Apr 2026 15:35:20 +0200 Subject: [PATCH 05/10] macros work on curve_weierstrassgit add .! --- ec/src/curve_weierstrass.rs | 408 +++++++++++++++++++----------------- fp/src/f2_ext.rs | 70 ------- fp/src/field_ops.rs | 94 ++++++++- isogeny/src/montgomery.rs | 2 +- 4 files changed, 308 insertions(+), 266 deletions(-) diff --git a/ec/src/curve_weierstrass.rs b/ec/src/curve_weierstrass.rs index a20a68e..33f7c33 100644 --- a/ec/src/curve_weierstrass.rs +++ b/ec/src/curve_weierstrass.rs @@ -22,6 +22,7 @@ use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; use fp::field_ops::FieldOps; +use fp::{ref_field_fns, ref_field_impl, ref_field_trait_impl}; use crate::curve_ops::Curve; use crate::point_weierstrass::AffinePoint; @@ -42,91 +43,93 @@ pub struct WeierstrassCurve { pub a6: F, } -impl WeierstrassCurve { - // ------------------------------------------------------------------- - // Constructors - // ------------------------------------------------------------------- - - /// Construct a curve from all five general Weierstrass coefficients. - 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 +ref_field_impl! { + impl WeierstrassCurve { + // ------------------------------------------------------------------- + // Constructors + // ------------------------------------------------------------------- + + /// Construct a curve from all five general Weierstrass coefficients. + 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 + } } - } - /// Construct the **short Weierstrass** curve `y² = x³ + ax + b`. - /// - /// Sets `a₁ = a₂ = a₃ = 0`, `a₄ = a`, `a₆ = b`. - /// Only valid when `char(F) ≠ 2, 3`. - 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, + /// Construct the **short Weierstrass** curve `y² = x³ + ax + b`. + /// + /// Sets `a₁ = a₂ = a₃ = 0`, `a₄ = a`, `a₆ = b`. + /// Only valid when `char(F) ≠ 2, 3`. + 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, + } } - } - 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 - // ------------------------------------------------------------------- - - /// Return the five a-invariants `[a₁, a₂, a₃, a₄, a₆]`. - pub fn a_invariants(&self) -> [F; 5] { - [ - self.a1.clone(), - self.a2.clone(), - self.a3.clone(), - self.a4.clone(), - self.a6.clone(), - ] - } + 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() + } - /// Return the four b-invariants `[b₂, b₄, b₆, b₈]`. - pub fn b_invariants(&self) -> [F; 4] { - [self.b2(), self.b4(), self.b6(), self.b8()] - } + // ------------------------------------------------------------------- + // Invariants + // ------------------------------------------------------------------- + + /// Return the five a-invariants `[a₁, a₂, a₃, a₄, a₆]`. + pub fn a_invariants(&self) -> [F; 5] { + [ + self.a1.clone(), + self.a2.clone(), + self.a3.clone(), + self.a4.clone(), + self.a6.clone(), + ] + } - // ------------------------------------------------------------------- - // Curve predicates - // ------------------------------------------------------------------- + /// Return the four b-invariants `[b₂, b₄, b₆, b₈]`. + pub fn b_invariants(&self) -> [F; 4] { + [self.b2(), self.b4(), self.b6(), self.b8()] + } - /// Check whether the affine point `(x, y)` satisfies the curve equation. - /// - /// ```text - /// y² + a₁xy + a₃y == x³ + a₂x² + a₄x + a₆ - /// ``` - 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 + // ------------------------------------------------------------------- + // Curve predicates + // ------------------------------------------------------------------- + + /// Check whether the affine point `(x, y)` satisfies the curve equation. + /// + /// ```text + /// y² + a₁xy + a₃y == x³ + a₂x² + a₄x + a₆ + /// ``` + 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 + } } } @@ -134,117 +137,140 @@ 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 -} +ref_field_fns! { + /// `b₂ = a₁² + 4a₂`. + fn b2_from_coeffs(a1: &F, a2: &F) -> F { + let a1_sq = ::square(a1); + let two_a2 = ::double(a2); + let four_a2 = ::double(&two_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₁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₃² + 4a₆`. + fn b6_from_coeffs(a3: &F, a6: &F) -> F { + let a3_sq = ::square(a3); + let two_a6 = ::double(a6); + let four_a6 = ::double(&two_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 a1_sq = ::square(a1); + let a1sq_a6 = &a1_sq * 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); + let a2_a6 = a2 * a6; + let two_a2_a6 = ::double(&a2_a6); + let four_a2_a6 = ::double(&two_a2_a6); - a1sq_a6 + four_a2_a6 - a1_a3_a4 + a2_a3sq - a4sq -} + let a1_a3 = a1 * a3; + let a1_a3_a4 = &a1_a3 * a4; -/// 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 -} + let a3_sq = ::square(a3); + let a2_a3sq = a2 * &a3_sq; -/// `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); + let a4_sq = ::square(a4); - // 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; + let tmp1 = &a1sq_a6 + &four_a2_a6; + let tmp2 = &tmp1 - &a1_a3_a4; + let tmp3 = &tmp2 + &a2_a3sq; + &tmp3 - &a4_sq + } - b2_sq - twentyfour * b4 -} + /// 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); + + let two = ::double(&F::one()); + let three = &two + &F::one(); + let four = ::double(&two); + let eight = ::double(&four); + let nine = ::square(&three); + let twentyseven = &nine * &three; + + let b2_sq = ::square(&b2); + let minus_b2_sq = -&b2_sq; + let term1 = &minus_b2_sq * &b8; + + let b4_sq = ::square(&b4); + let b4_cubed = &b4_sq * &b4; + let minus_eight = -&eight; + let term2 = &minus_eight * &b4_cubed; + + let b6_sq = ::square(&b6); + let minus_twentyseven = -&twentyseven; + let term3 = &minus_twentyseven * &b6_sq; + + let b2b4 = &b2 * &b4; + let b2b4b6 = &b2b4 * &b6; + let term4 = &nine * &b2b4b6; + + let tmp1 = &term1 + &term2; + let tmp2 = &tmp1 + &term3; + &tmp2 + &term4 + } -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; + /// `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 delta = discriminant_from_coeffs::(&a1, &a2, &a3, &a4, &a6); - let delta_inv = ::invert(&delta).unwrap(); - c4_cubed * delta_inv -} + 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 + } +} // ------------------------------------------------------------------- // Invariants attached to the model // ------------------------------------------------------------------- -impl WeierstrassCurve { - pub fn b2(&self) -> F { - b2_from_coeffs::(&self.a1, &self.a2) - } - pub fn b4(&self) -> F { - b4_from_coeffs::(&self.a1, &self.a3, &self.a4) - } - pub fn b6(&self) -> F { - b6_from_coeffs::(&self.a3, &self.a6) - } - pub fn b8(&self) -> F { - b8_from_coeffs::(&self.a1, &self.a2, &self.a3, &self.a4, &self.a6) - } - pub fn discriminant(&self) -> F { - discriminant_from_coeffs::(&self.a1, &self.a2, &self.a3, &self.a4, &self.a6) +ref_field_impl! { + impl WeierstrassCurve { + pub fn b2(&self) -> F { + b2_from_coeffs::(&self.a1, &self.a2) + } + pub fn b4(&self) -> F { + b4_from_coeffs::(&self.a1, &self.a3, &self.a4) + } + pub fn b6(&self) -> F { + b6_from_coeffs::(&self.a3, &self.a6) + } + pub fn b8(&self) -> F { + b8_from_coeffs::(&self.a1, &self.a2, &self.a3, &self.a4, &self.a6) + } + pub fn discriminant(&self) -> F { + discriminant_from_coeffs::(&self.a1, &self.a2, &self.a3, &self.a4, &self.a6) + } } } @@ -253,28 +279,30 @@ impl WeierstrassCurve { // Curve predicates // ------------------------------------------------------------------- -impl Curve for WeierstrassCurve { - type BaseField = F; - type Point = AffinePoint; - - fn is_on_curve(&self, point: &Self::Point) -> bool { - if point.infinity { - true - } else { - self.contains(&point.x, &point.y) +ref_field_trait_impl! { + impl Curve for WeierstrassCurve { + type BaseField = F; + type Point = AffinePoint; + + fn is_on_curve(&self, point: &Self::Point) -> bool { + if point.infinity { + true + } else { + self.contains(&point.x, &point.y) + } } - } - fn random_point(&self) -> Self::Point { - todo!() - } + fn random_point(&self) -> Self::Point { + todo!() + } - fn j_invariant(&self) -> F { - j_inv_from_coeffs(&self.a1, &self.a2, &self.a3, &self.a4, &self.a6) - } + fn j_invariant(&self) -> F { + j_inv_from_coeffs(&self.a1, &self.a2, &self.a3, &self.a4, &self.a6) + } - fn a_invariants(&self) -> Vec { - WeierstrassCurve::a_invariants(self).to_vec() + fn a_invariants(&self) -> Vec { + WeierstrassCurve::a_invariants(self).to_vec() + } } } diff --git a/fp/src/f2_ext.rs b/fp/src/f2_ext.rs index 333789c..c02ef91 100644 --- a/fp/src/f2_ext.rs +++ b/fp/src/f2_ext.rs @@ -351,76 +351,6 @@ where } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -impl Add for F2Ext -where - P: BinaryIrreducible, -{ - type Output = Self; - fn add(self, rhs: Self) -> Self { - FieldOps::add(&self, &rhs) - } -} - -impl Sub for F2Ext -where - P: BinaryIrreducible, -{ - type Output = Self; - fn sub(self, rhs: Self) -> Self { - FieldOps::sub(&self, &rhs) - } -} - -impl Mul for F2Ext -where - P: BinaryIrreducible, -{ - type Output = Self; - fn mul(self, rhs: Self) -> Self { - FieldOps::mul(&self, &rhs) - } -} - -impl Neg for F2Ext -where - P: BinaryIrreducible, -{ - type Output = Self; - fn neg(self) -> Self { - FieldOps::negate(&self) - } -} - // =========================================================================== // FieldOps implementation // =========================================================================== diff --git a/fp/src/field_ops.rs b/fp/src/field_ops.rs index 444dea7..439ac35 100644 --- a/fp/src/field_ops.rs +++ b/fp/src/field_ops.rs @@ -10,11 +10,6 @@ pub trait FieldOps: + Eq + Default + ConditionallySelectable -where - for<'a, 'b> &'a Self: Add<&'b Self, Output = Self>, - for<'a, 'b> &'a Self: Sub<&'b Self, Output = Self>, - for<'a, 'b> &'a Self: Mul<&'b Self, Output = Self>, - for<'a> &'a Self: Neg, { fn zero() -> Self; fn one() -> Self; @@ -222,3 +217,92 @@ where fn characteristic() -> Vec; fn degree() -> u32; } + + +// ------------------------------------------------------------------- +// Macros for using operator overloads on referenced values +// ------------------------------------------------------------------- + +/// 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)* + } + }; +} + +/// 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)* + } + }; +} + +/// For 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 + }; +} + +#[macro_export] +macro_rules! ref_field_fns { + ( + $( + $(#[$meta:meta])* + fn $name:ident<$F:ident> ( $($args:tt)* ) -> $Ret:ty $body:block + )* + ) => { + $( + $(#[$meta])* + $crate::ref_field_fn! { + fn $name<$F>($($args)*) -> $Ret $body + } + )* + }; +} \ No newline at end of file diff --git a/isogeny/src/montgomery.rs b/isogeny/src/montgomery.rs index 646b1fe..ca26980 100644 --- a/isogeny/src/montgomery.rs +++ b/isogeny/src/montgomery.rs @@ -49,7 +49,7 @@ impl MontgomeryIsogeny { } fn new_codomain_helper(domain: & MontgomeryCurve, xK: KummerPoint) -> (F, F) { - todo!() + } } From 3d4b8e74723d7af4df6bcde96c8e314b629d00fc Mon Sep 17 00:00:00 2001 From: MartinAzon Date: Thu, 16 Apr 2026 16:52:10 +0200 Subject: [PATCH 06/10] macros work for montgomery too! --- ec/src/curve_montgomery.rs | 178 +++++++++-------- ec/src/point_montgomery.rs | 321 ++++++++++++++++-------------- ec/src/point_weierstrass.rs | 379 +++++++++++++++++++----------------- fp/src/field_ops.rs | 17 +- 4 files changed, 478 insertions(+), 417 deletions(-) diff --git a/ec/src/curve_montgomery.rs b/ec/src/curve_montgomery.rs index cdaaeb5..d9dc14b 100644 --- a/ec/src/curve_montgomery.rs +++ b/ec/src/curve_montgomery.rs @@ -36,6 +36,7 @@ use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; use fp::field_ops::FieldOps; +use fp::{ref_field_impl, ref_field_trait_impl}; use crate::curve_ops::Curve; use crate::point_montgomery::KummerPoint; @@ -52,46 +53,39 @@ pub struct MontgomeryCurve { pub b: F, } -impl MontgomeryCurve { - // ------------------------------------------------------------------- - // Constructor - // ------------------------------------------------------------------- +ref_field_impl! { + impl MontgomeryCurve { - /// Construct a Montgomery curve from its two coefficients `A` and `B`. - pub fn new(a: F, b: F) -> Self { - assert!(Self::is_smooth(&a, &b)); - Self { a, b } - } - - pub fn is_smooth(a: &F, b: &F) -> bool { - if F::characteristic()[0] != 2 { - // In odd char, smoothness criterion is just b != 0 and a != 2, -2 - let two = F::one().double(); - !bool::from(b.is_zero()) && *a != two && *a != two.negate() - } else { - // In char 2, smoothness criterion is just b != 0 - !bool::from(b.is_zero()) + pub fn new(a: F, b: F) -> Self { + assert!(Self::is_smooth(&a, &b)); + Self { a, b } } - } + 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 - // ------------------------------------------------------------------- + pub fn a_invariants(&self) -> [F; 2] { + [self.a.clone(), self.b.clone()] + } - /// Return the model coefficients `[A, B]`. - 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 + } } } @@ -100,83 +94,87 @@ impl MontgomeryCurve { // Curve predicates // ------------------------------------------------------------------- -impl Curve for MontgomeryCurve { - type BaseField = F; - type Point = KummerPoint; +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; + } - /// Return `true` if `point` is a valid Kummer/x-line point for this curve. - fn is_on_curve(&self, point: &Self::Point) -> bool { - if point.is_identity() { - true - } - else { - // Normalise projective (X : Z) to affine x = X / Z. let z_inv = ::invert(&point.z); if bool::from(z_inv.is_none()) { - // Z = 0 means identity, already handled above. return true; } - let x = point.x * z_inv.unwrap(); + + let x = &point.x * &z_inv.unwrap(); + if F::characteristic()[0] != 2 { - // Check if `(x³ + A x² + x)/B` is a square in F or not. let xsq = ::square(&x); - let xcubed = x * xsq; - let axsq = self.a * xsq; + let xcubed = &x * &xsq; + let axsq = &self.a * &xsq; + + let sum1 = &xcubed + &axsq; + let sum = &sum1 + &x; + let binv = ::invert(&self.b).unwrap(); - let sum = xcubed + axsq + x; - ::legendre(&(sum * binv)) > -1i8 - } - else { + let val = &sum * &binv; + + ::legendre(&val) > -1i8 + } else { if bool::from(x.is_zero()) { - // The point with x = 0 always lies on the curve. true - } - else { - // 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. + } else { 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()) } } } - } - fn random_point(&self) -> Self::Point { - todo!() - } - - fn j_invariant(&self) -> F { - if F::characteristic()[0] != 2 { - // The j-invariant of `B y² = x(x² + A x + 1)` is 256*(a² - 3)³/(a² - 4) - 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_cubed = asq_min_three * ::square(&asq_min_three); - - let asq_min_four_inv = ::invert(&(asq - four)).into_option().expect("a should be different from 2, -2."); - - twofivesix * asq_min_three_cubed * asq_min_four_inv + fn random_point(&self) -> Self::Point { + todo!() } - else { - // The j-invariant of `y² + xy = x(x² + A x + B²)` is 1/b⁴ - assert!(!bool::from(self.b.is_zero())); - let bsq = ::square(&self.b); - let bfourth = ::square(& bsq); - ::invert(&bfourth).unwrap() + + 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() + fn a_invariants(&self) -> Vec { + MontgomeryCurve::a_invariants(self).to_vec() + } } } diff --git a/ec/src/point_montgomery.rs b/ec/src/point_montgomery.rs index 7b3f258..2a7184c 100644 --- a/ec/src/point_montgomery.rs +++ b/ec/src/point_montgomery.rs @@ -33,6 +33,7 @@ use subtle::{Choice, CtOption, ConditionallySelectable, ConstantTimeEq}; use crate::curve_montgomery::MontgomeryCurve; use crate::point_ops::PointOps; use fp::field_ops::FieldOps; +use fp::{ref_field_impl, ref_field_trait_impl}; /// A point on the Kummer line of a Montgomery curve, represented by `(X : Z)`. /// @@ -50,54 +51,60 @@ pub struct KummerPoint { // Manual trait impls // --------------------------------------------------------------------------- -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 { - self.x * other.z == other.x * self.z +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 + } } } -impl Eq for KummerPoint -where - F: FieldOps + ConstantTimeEq, -{ } +ref_field_trait_impl! { + impl Eq for KummerPoint + { } +} // --------------------------------------------------------------------------- // Constructors // --------------------------------------------------------------------------- -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 } - } +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() } - } + /// 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()} - } + /// The image of the identity point on the Kummer line. + pub fn identity() -> Self { + Self{ x: F::one(), z: F::zero()} + } - /// 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) + } } } @@ -130,19 +137,18 @@ 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 { - let x1z2 = self.x * other.z; - let x2z1 = other.x * self.z; - x1z2.ct_eq(& x2z1) + let x1z2 = &self.x * &other.z; + let x2z1 = &other.x * &self.z; + x1z2.ct_eq(&x2z1) } - fn ct_ne(&self, other: &Self) -> Choice { !self.ct_eq(other) } + fn ct_ne(&self, other: &Self) -> Choice { + !self.ct_eq(other) + } } @@ -150,103 +156,121 @@ 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); - - Self{ x: sumsq * diffsq, z: new_z } - } - else { - let temp_x = self.x + curve.b * self.z; - let new_x = ::square(&::square(&temp_x)); - let xz = self.x * self.z; - let new_z = ::square(&xz); - Self{ x: new_x, z: new_z } +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); + + 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, + } + } else { + let temp_x = &self.x + &(&curve.b * &self.z); + let sq1 = ::square(&temp_x); + let new_x = ::square(&sq1); + + let xz = &self.x * &self.z; + let new_z = ::square(&xz); + + Self { x: new_x, z: new_z } + } } - } - /// 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 new_x = diff.z * ::square(&(u + v)); - let new_z = diff.x * ::square(&(u - v)); - - Self { x: new_x, z: new_z } - } - else { - let x1z2 = self.x * other.z; - let x2z1 = other.x * self.z; - let sum_squared = ::square(&(x1z2 + x2z1)); + /// 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 new_x = diff.x * sum_squared + diff.z * x1z2 * x2z1; - let new_z = diff.z * sum_squared; + let upv = &u + &v; + let umv = &u - &v; - Self { x: new_x, z: new_z } - } - } + let new_x = &diff.z * &::square(&upv); + let new_z = &diff.x * &::square(&umv); - /// 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(); - } + Self { x: new_x, z: new_z } + } else { + let x1z2 = &self.x * &other.z; + let x2z1 = &other.x * &self.z; - // Ladder state: - // r0 = x([m]P) - // r1 = x([m+1]P) - let mut r0 = Self::identity(); - let mut r1 = *self; + let sum = &x1z2 + &x2z1; + let sum_sq = ::square(&sum); - let mut swap = Choice::from(0u8); + let prod = &x1z2 * &x2z1; - for &limb in k.iter().rev() { - for bit in (0..64).rev() { - let ki = Choice::from(((limb >> bit) & 1) as u8); + let new_x = &(&diff.x * &sum_sq) + &(&diff.z * &prod); + let new_z = &diff.z * &sum_sq; - // 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; + Self { x: new_x, z: new_z } + } + } - // 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); + /// 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(); + } - r0 = r0_dbl; - r1 = r0_plus_r1; + // 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); + // Final swap to undo the last deferred permutation. + Self::conditional_swap(&mut r0, &mut r1, swap); - r0 + r0 + } } } @@ -254,31 +278,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) + } } } \ No newline at end of file diff --git a/ec/src/point_weierstrass.rs b/ec/src/point_weierstrass.rs index 09d3915..9a3dbb1 100644 --- a/ec/src/point_weierstrass.rs +++ b/ec/src/point_weierstrass.rs @@ -12,22 +12,18 @@ //! | 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 | +//! | 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 | - +//! | Scalar multiply | Montgomery ladder (scalar-constant-time) | O(n) doubles | // WARNING: SOME OF THE FUNCTIONS BELOW USE BRANCHES DEPENDING // ON WHETHER A POINT IS AT INFINITY OR NOT!!!! - - -//use std::os::unix::raw::ino_t; -//use crypto_bigint::modular::ConstMontyForm; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; use crate::curve_weierstrass::WeierstrassCurve; use crate::point_ops::PointOps; use fp::field_ops::FieldOps; +use fp::{ref_field_impl, ref_field_trait_impl, ref_field_trait_impl_path}; /// An affine point on a Weierstrass elliptic curve over `F`. /// @@ -47,24 +43,22 @@ pub struct AffinePoint { impl PartialEq for AffinePoint where F: FieldOps + ConstantTimeEq, - { fn eq(&self, other: &Self) -> bool { match (self.infinity, other.infinity) { - (true, true) => true, - (true, false) => false, - (false, true) => false, + (true, true) => true, + (true, false) => false, + (false, true) => false, (false, false) => self.x == other.x && self.y == other.y, } } } - - impl Eq for AffinePoint where -F: FieldOps + ConstantTimeEq -{ } + F: FieldOps + ConstantTimeEq, +{ +} // --------------------------------------------------------------------------- // Constructors @@ -76,12 +70,20 @@ impl AffinePoint { /// **No** on-curve check is performed; use /// [`WeierstrassCurve::contains`] if you need validation. pub fn new(x: F, y: F) -> Self { - Self { x, y, infinity: false } + Self { + x, + y, + infinity: false, + } } /// The point at infinity `O` (the group identity). pub fn identity() -> Self { - Self { x: F::zero(), y: F::zero(), infinity: true } + Self { + x: F::zero(), + y: F::zero(), + infinity: true, + } } /// Returns `true` if this is the point at infinity. @@ -156,190 +158,213 @@ 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 minus_y = -&self.y; + let a1x = &curve.a1 * &self.x; + let tmp = &minus_y - &a1x; + let neg_y = &tmp - &curve.a3; - 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 { - if self.infinity { - return Self::identity(); + Self::new(self.x.clone(), neg_y) } - // denominator = 2y₁ + a₁x₁ + a₃ - let denom = ::double(&self.y) + curve.a1 * self.x + curve.a3; - - // If denominator is zero the tangent is vertical → result is O. - let denom_inv = match denom.invert().into_option() { - Some(inv) => inv, - None => return Self::identity(), - }; - - // 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 lambda = numer * denom_inv; - - // 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 - }; - - // y₃ = λ(x₁ − x₃) − y₁ − a₁x₃ − a₃ - let y3 = { - let dx= self.x - x3; - let lam_dx = lambda * dx; - let a1x3 = curve.a1 *x3; - - lam_dx - self.y - a1x3 - curve.a3 - }; - - Self::new(x3, y3) - } + /// 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(); + } - /// 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(); + // denominator = 2y₁ + a₁x₁ + a₃ + 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 ::invert(&denom).into_option() { + Some(inv) => inv, + None => return Self::identity(), + }; + + // numerator = 3x₁² + 2a₂x₁ + a₄ − a₁y₁ + let numer = { + let x1_sq = ::square(&self.x); + let two_x1_sq = ::double(&x1_sq); + let three_x1_sq = &x1_sq + &two_x1_sq; + + let a2x1 = &curve.a2 * &self.x; + let two_a2_x1 = ::double(&a2x1); + + let a1y1 = &curve.a1 * &self.y; + + let tmp1 = &three_x1_sq + &two_a2_x1; + let tmp2 = &tmp1 + &curve.a4; + &tmp2 - &a1y1 + }; + + let lambda = &numer * &denom_inv; + + // x₃ = λ² + a₁λ − a₂ − 2x₁ + let x3 = { + let lam_sq = ::square(&lambda); + let a1_lam = &curve.a1 * λ + let two_x1 = ::double(&self.x); + + 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 - }; - - // 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 - }; - - Self::new(x3, y3) - } + // 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(); + } - pub fn scalar_mul(&self, k: &[u64], curve: & as PointOps>::Curve) -> Self { - let mut r0 = Self::identity(); - let mut r1 = self.clone(); + // 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 = ::invert(&dx) + .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 * λ + + let tmp1 = &lam_sq + &a1_lam; + let tmp2 = &tmp1 - &curve.a2; + let tmp3 = &tmp2 - &self.x; + &tmp3 - &other.x + }; + + // y₃ = λ(x₁ − x₃) − y₁ − a₁x₃ − a₃ + let y3 = { + let dx3 = &self.x - &x3; + let lam_dx3 = &lambda * &dx3; + let a1x3 = &curve.a1 * &x3; + + let tmp1 = &lam_dx3 - &self.y; + let tmp2 = &tmp1 - &a1x3; + &tmp2 - &curve.a3 + }; + + Self::new(x3, y3) + } + + 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); + 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); + Self::conditional_swap(&mut r0, &mut r1, choice); - let sum = r0.add(&r1, curve); - let dbl = r0.double(curve); - r1 = sum; - r0 = dbl; + let sum = r0.add(&r1, curve); + let dbl = r0.double(curve); + r1 = sum; + r0 = dbl; - Self::conditional_swap(&mut r0, &mut r1, choice); + Self::conditional_swap(&mut r0, &mut r1, choice); + } } - } - r0 + r0 + } } - } +ref_field_trait_impl! { + impl PointOps for AffinePoint { + type BaseField = F; + type Curve = WeierstrassCurve; + fn identity(_curve: &Self::Curve) -> Self { + AffinePoint::::identity() + } + fn is_identity(&self) -> bool { + self.infinity + } -impl PointOps for AffinePoint -where - F: FieldOps, -{ - type BaseField = F; - type Curve = WeierstrassCurve; - - fn identity(_curve: &Self::Curve) -> Self { - AffinePoint::::identity() - } - - 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/fp/src/field_ops.rs b/fp/src/field_ops.rs index 439ac35..3556ba9 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}; pub trait FieldOps: @@ -269,6 +268,22 @@ macro_rules! ref_field_trait_impl { }; } +#[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)* + } + }; +} + /// For free functions like: /// /// ref_field_fn! { From 608eec3359241c3204fc3da13128bdb8d0627451 Mon Sep 17 00:00:00 2001 From: martin-azon Date: Fri, 17 Apr 2026 09:35:03 +0200 Subject: [PATCH 07/10] macros used for all curves now --- ec/src/curve_edwards.rs | 216 ++++++++++------- ec/src/curve_jacobi_intersection.rs | 111 +++++---- ec/src/curve_jacobi_quartic.rs | 172 ++++++++------ ec/src/point_edwards.rs | 354 +++++++++++++++------------- ec/src/point_jacobi_intersection.rs | 201 +++++++++------- ec/src/point_jacobi_quartic.rs | 266 ++++++++++++--------- 6 files changed, 750 insertions(+), 570 deletions(-) diff --git a/ec/src/curve_edwards.rs b/ec/src/curve_edwards.rs index ab2b8ff..5f4e402 100644 --- a/ec/src/curve_edwards.rs +++ b/ec/src/curve_edwards.rs @@ -24,6 +24,7 @@ use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; use fp::field_ops::FieldOps; +use fp::{ref_field_impl, ref_field_trait_impl}; use crate::curve_ops::Curve; use crate::point_edwards::EdwardsPoint; @@ -38,106 +39,143 @@ pub struct EdwardsCurve { pub d2: F, } -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"); + + Self { d1, d2 } + } + + /// 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); - 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 + /// 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 + } } } } -impl Curve for EdwardsCurve { - type BaseField = F; - type Point = EdwardsPoint; +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 is_on_curve(&self, point: &Self::Point) -> bool { + self.contains(&point.x, &point.y) + } - fn random_point(&self) -> Self::Point { - todo!() - } + fn random_point(&self) -> Self::Point { + todo!() + } - fn j_invariant(&self) -> F { - if F::characteristic()[0] != 2 { - // j = 16(1 + 14d + d²)³ / (d(1 − d)⁴) - let d = self.d2; - let d2 = ::square(&d); - let two = ::double(&F::one()); - let four = ::double(&two); - let eight = ::double(&four); - 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 one_minus_d = F::one() - d; - let omd2 = ::square(&one_minus_d); - let omd4 = ::square(&omd2); - let denom = d * omd4; - - numer * denom.invert().into_option() - .expect("d(1-d)^4 must be invertible") - } else { - // j = 1 / (d₁⁴ (d₁⁴ + d₁² + d₂²)) - 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); - denom.invert().into_option() - .expect("j-invariant denominator must be invertible") + fn j_invariant(&self) -> F { + if F::characteristic()[0] != 2 { + // j = 16(1 + 14d + d²)³ / (d(1 − d)⁴) + let d_sq = ::square(&self.d2); + + let one = F::one(); + let two = ::double(&one); + let four = ::double(&two); + let eight = ::double(&four); + let fourteen_tmp = &eight + &four; + let fourteen = &fourteen_tmp + &two; + let sixteen = ::double(&eight); + + let fourteen_d = &fourteen * &self.d2; + let inner_tmp = &one + &fourteen_d; + let inner = &inner_tmp + &d_sq; + let inner_sq = ::square(&inner); + let inner_cubed = &inner * &inner_sq; + let numer = &sixteen * &inner_cubed; + + let one_minus_d = &one - &self.d2; + let omd2 = ::square(&one_minus_d); + let omd4 = ::square(&omd2); + let denom = &self.d2 * &omd4; + + let denom_inv = ::invert(&denom) + .into_option() + .expect("d(1-d)^4 must be invertible"); + + &numer * &denom_inv + } else { + // j = 1 / (d₁⁴ (d₁⁴ + d₁² + d₂²)) + let d1_sq = ::square(&self.d1); + let d1_4 = ::square(&d1_sq); + let d2_sq = ::square(&self.d2); + + let inner_tmp = &d1_4 + &d1_sq; + let inner = &inner_tmp + &d2_sq; + let denom = &d1_4 * &inner; + + ::invert(&denom) + .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.clone()] + } else { + vec![self.d1.clone(), self.d2.clone()] + } } } } - - // --------------------------------------------------------------------------- // Constant-time functionalities // --------------------------------------------------------------------------- @@ -147,9 +185,9 @@ 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), + Self { + d1: F::conditional_select(&a.d1, &b.d1, choice), + d2: F::conditional_select(&a.d2, &b.d2, choice), } } @@ -172,5 +210,7 @@ where self.d1.ct_eq(&other.d1) & self.d2.ct_eq(&other.d2) } - fn ct_ne(&self, other: &Self) -> Choice { !self.ct_eq(other) } + 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 e6c55ac..e98b2ed 100644 --- a/ec/src/curve_jacobi_intersection.rs +++ b/ec/src/curve_jacobi_intersection.rs @@ -19,6 +19,8 @@ use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; use fp::field_ops::FieldOps; +use fp::{ref_field_impl, ref_field_trait_impl}; + use crate::curve_ops::Curve; use crate::curve_weierstrass::WeierstrassCurve; use crate::point_jacobi_intersection::JacobiIntersectionPoint; @@ -29,63 +31,76 @@ pub struct JacobiIntersectionCurve { pub a: F, } -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 } - } +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 } + } - /// 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); - s2 + c2 == F::one() && self.a * s2 + d2 == F::one() - } + let lhs1 = &s2 + &c2; + let lhs2 = &(&self.a * &s2) + &d2; + let one = F::one(); - pub fn a_invariants(&self) -> [F; 1] { - [self.a] - } + lhs1 == one && lhs2 == one + } + + pub fn a_invariants(&self) -> [F; 1] { + [self.a.clone()] + } + + /// 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; - /// Birationally equivalent Weierstrass model - /// `y² = x³ + (2-a)x² + (1-a)x`. - pub fn to_weierstrass_curve(&self) -> WeierstrassCurve { - let two = ::double(&F::one()); - WeierstrassCurve::new(F::zero(), two - self.a, F::zero(), F::one() - self.a, F::zero()) + WeierstrassCurve::new(F::zero(), a2, F::zero(), a4, F::zero()) + } } } -impl Curve for JacobiIntersectionCurve { - type BaseField = F; - type Point = JacobiIntersectionPoint; +ref_field_trait_impl! { + impl Curve for JacobiIntersectionCurve { + type BaseField = F; + type Point = JacobiIntersectionPoint; - 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) + } - fn random_point(&self) -> Self::Point { - todo!() - } + fn random_point(&self) -> Self::Point { + todo!() + } - fn j_invariant(&self) -> F { - self.to_weierstrass_curve().j_invariant() - } + fn j_invariant(&self) -> F { + self.to_weierstrass_curve().j_invariant() + } - fn a_invariants(&self) -> Vec { - JacobiIntersectionCurve::a_invariants(self).to_vec() + fn a_invariants(&self) -> Vec { + JacobiIntersectionCurve::a_invariants(self).to_vec() + } } } - - // --------------------------------------------------------------------------- // Constant-time functionalities // --------------------------------------------------------------------------- @@ -95,8 +110,8 @@ where F: FieldOps + Copy, { fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - Self{ - a: F::conditional_select(& a.a, &b.a, choice) + Self { + a: F::conditional_select(&a.a, &b.a, choice), } } @@ -113,7 +128,11 @@ impl ConstantTimeEq for JacobiIntersectionCurve where F: FieldOps + Copy + ConstantTimeEq, { - fn ct_eq(&self, other: &Self) -> Choice { self.a.ct_eq(&other.a) } + fn ct_eq(&self, other: &Self) -> Choice { + self.a.ct_eq(&other.a) + } - fn ct_ne(&self, other: &Self) -> Choice { !self.ct_eq(other) } + 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 ad62cb7..a2c3ffa 100644 --- a/ec/src/curve_jacobi_quartic.rs +++ b/ec/src/curve_jacobi_quartic.rs @@ -15,6 +15,8 @@ use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; use fp::field_ops::FieldOps; +use fp::{ref_field_impl, ref_field_trait_impl}; + use crate::curve_ops::Curve; use crate::point_jacobi_quartic::JacobiQuarticPoint; @@ -29,94 +31,108 @@ pub struct JacobiQuarticCurve { pub d: F, } -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 } - } +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 } + } - /// 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; + /// 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; + } + + let a2 = ::square(a); + a2 != d.clone() } - let a2 = ::square(a); - a2 != *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 two = ::double(&F::one()); + /// 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); - y2 == self.d * x4 + two * self.a * x2 + F::one() - } + let one = F::one(); + let two = ::double(&one); - /// Return `[a, d]`. - pub fn a_invariants(&self) -> [F; 2] { - [self.a, self.d] - } + 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; + + 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() + /// Return the affine identity `(0, 1)`. + pub fn neutral_point(&self) -> JacobiQuarticPoint { + JacobiQuarticPoint::identity() + } } } -impl Curve for JacobiQuarticCurve { - type BaseField = F; - type Point = JacobiQuarticPoint; +ref_field_trait_impl! { + impl Curve for JacobiQuarticCurve { + type BaseField = F; + type Point = JacobiQuarticPoint; - 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) + } - fn random_point(&self) -> Self::Point { - todo!() - } + 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 three = ::double(&F::one()) + F::one(); - - let eight = ::double( - &::double( - &::double(&F::one()) - ) - ); - let sixty_four = eight * eight; // 8 * 8 = 64 - - let num_base = a2 + three * self.d; - let num = sixty_four * num_base * ::square(&num_base); - - 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"); - - num * denom_inv - } + /// 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; + + let four = ::double(&two); + let eight = ::double(&four); + let sixty_four = &eight * &eight; + + 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; + + 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 // --------------------------------------------------------------------------- @@ -126,9 +142,9 @@ 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), + Self { + a: F::conditional_select(&a.a, &b.a, choice), + d: F::conditional_select(&a.d, &b.d, choice), } } @@ -151,5 +167,7 @@ where self.a.ct_eq(&other.a) & self.d.ct_eq(&other.d) } - fn ct_ne(&self, other: &Self) -> Choice { !self.ct_eq(other) } -} + 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 38f7a98..451e055 100644 --- a/ec/src/point_edwards.rs +++ b/ec/src/point_edwards.rs @@ -22,6 +22,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)] @@ -47,7 +48,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 } } @@ -116,192 +117,215 @@ 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 - } + /// 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) + } - // ----------------------------------------------------------------------- - // 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₂) - // ----------------------------------------------------------------------- + /// 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); + } + } - fn add_odd(&self, other: &Self, curve: &EdwardsCurve) -> Self { - let d = curve.d2; + result + } - let x1y2 = self.x * other.y; - let y1x2 = self.y * other.x; - let x1x2 = self.x * other.x; - let y1y2 = self.y * other.y; + // ----------------------------------------------------------------------- + // Odd-characteristic addition + // ----------------------------------------------------------------------- - let dxy = d * x1x2 * y1y2; + 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 one = F::one(); - let x_num = x1y2 + y1x2; - let x_den = one + dxy; - let y_num = y1y2 - x1x2; - let y_den = one - dxy; + let x1x2y1y2 = &x1x2 * &y1y2; + let dxy = &curve.d2 * &x1x2y1y2; - let x_den_inv = x_den.invert().into_option() - .expect("Edwards addition: x-denominator must be invertible"); - let y_den_inv = y_den.invert().into_option() - .expect("Edwards addition: y-denominator must be invertible"); + let one = F::one(); + let x_num = &x1y2 + &y1x2; + let x_den = &one + &dxy; + let y_num = &y1y2 - &x1x2; + let y_den = &one - &dxy; - Self::new(x_num * x_den_inv, y_num * y_den_inv) - } + 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"); - // ----------------------------------------------------------------------- - // 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; - - let x_num = d1 * (x1 + x2) + d2_w1w2 - + a * (x2 * (y1 + y2 + F::one()) + y1y2); - let x_den = d1 + a * w2; - - let y_num = d1 * (y1 + y2) + d2_w1w2 - + b * (y2 * (x1 + x2 + F::one()) + x1x2); - let y_den = d1 + b * w2; - - let x_den_inv = x_den.invert().into_option() - .expect("binary Edwards addition: x-denom must be invertible"); - let y_den_inv = y_den.invert().into_option() - .expect("binary Edwards addition: y-denom must be invertible"); - - Self::new(x_num * x_den_inv, y_num * y_den_inv) - } -} + let x3 = &x_num * &x_den_inv; + let y3 = &y_num * &y_den_inv; -// --------------------------------------------------------------------------- -// w-coordinate helpers for char-2 Montgomery ladder -// --------------------------------------------------------------------------- - -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; - - let d2_over_d1 = curve.d2 - * curve.d1.invert().into_option().expect("d1 invertible"); - let coeff = d2_over_d1 + one; - - let den = curve.d1 + t + coeff * s; - let den_inv = den.invert().into_option() - .expect("w-diff-add denominator invertible"); - - t * den_inv + *w1 - } - - /// `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); + Self::new(x3, y3) + } - let d2_over_d1 = curve.d2 - * curve.d1.invert().into_option().expect("d1 invertible"); + // ----------------------------------------------------------------------- + // Characteristic-2 addition + // ----------------------------------------------------------------------- + + 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 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) + } - let num = a + j; - let den = curve.d1 + a + d2_over_d1 * j; - let den_inv = den.invert().into_option() - .expect("w-double denominator invertible"); + // ----------------------------------------------------------------------- + // 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 + } - num * 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 + } } } -// --------------------------------------------------------------------------- -// PointOps bridge -// --------------------------------------------------------------------------- - -impl PointOps for EdwardsPoint { - type BaseField = F; - type Curve = EdwardsCurve; +ref_field_trait_impl! { + impl PointOps for EdwardsPoint { + type BaseField = F; + type Curve = EdwardsCurve; - fn identity(_curve: &Self::Curve) -> Self { - EdwardsPoint::::identity() - } + fn identity(_curve: &Self::Curve) -> Self { + EdwardsPoint::::identity() + } - fn is_identity(&self) -> bool { - EdwardsPoint::::is_identity(self) - } + fn is_identity(&self) -> bool { + EdwardsPoint::::is_identity(self) + } - fn negate(&self, curve: &Self::Curve) -> Self { - EdwardsPoint::::negate(self, curve) - } + fn negate(&self, curve: &Self::Curve) -> Self { + EdwardsPoint::::negate(self, curve) + } - fn scalar_mul(&self, k: &[u64], curve: &Self::Curve) -> Self { - EdwardsPoint::::scalar_mul(self, k, curve) + fn scalar_mul(&self, k: &[u64], curve: &Self::Curve) -> Self { + EdwardsPoint::::scalar_mul(self, k, curve) + } } -} +} \ No newline at end of file diff --git a/ec/src/point_jacobi_intersection.rs b/ec/src/point_jacobi_intersection.rs index 40af10b..4bc9a4f 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)] @@ -39,7 +40,11 @@ impl JacobiIntersectionPoint { /// The neutral element `(0, 1, 1)`. pub fn identity() -> Self { - Self { s: F::zero(), c: F::one(), d: F::one() } + Self { + s: F::zero(), + c: F::one(), + d: F::one(), + } } pub fn is_identity(&self) -> bool { @@ -85,98 +90,136 @@ where } } -impl JacobiIntersectionPoint { - /// Negation: `-(s, c, d) = (-s, c, d)`. - pub fn negate(&self, _curve: &JacobiIntersectionCurve) -> Self { - Self::new(-self.s, self.c, self.d) - } +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()) + } - /// 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 denom = ::square(&other.c) + ::square(&d1s2); - let denom_inv = denom.invert().into_option() - .expect("Jacobi-intersection addition denominator vanished"); - - let s3 = (other.c * self.s * other.d + self.d * other.s * self.c) * denom_inv; - let c3 = (other.c * self.c - self.d * other.s * self.s * other.d) * denom_inv; - let d3 = (self.d * other.d - curve.a * self.s * self.c * other.s * other.c) * denom_inv; - - Self::new(s3, c3, d3) - } + /// 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 denom = ::square(&self.c) + ::square(&ds); - let denom_inv = denom.invert().into_option() - .expect("Jacobi-intersection doubling denominator vanished"); - - let two = ::double(&F::one()); - let s_sq = ::square(&self.s); - let c_sq = ::square(&self.c); - let d_sq = ::square(&self.d); - - let s3 = (two * self.c * self.s * self.d) * denom_inv; - let c3 = (c_sq - d_sq * s_sq) * denom_inv; - let d3 = (d_sq - curve.a * s_sq * c_sq) * 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(); + 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); + 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 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 200c388..0b483b7 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)] @@ -36,24 +37,32 @@ impl PartialEq for JacobiQuarticPoint { } impl Eq for JacobiQuarticPoint {} +ref_field_impl! { + impl JacobiQuarticPoint { + pub fn new(x: F, y: F) -> Self { + Self { x, y } + } -impl JacobiQuarticPoint { - pub fn new(x: F, y: F) -> Self { - Self { x, y } - } - - /// The neutral element `(0, 1)`. - pub fn identity() -> Self { - Self { x: F::zero(), y: F::one() } - } + /// 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() - } + 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 { - Self { x: F::zero(), 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, + } + } } } @@ -92,127 +101,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 From d0f410bb986ddf940fe74b346c0f4e49e39163e5 Mon Sep 17 00:00:00 2001 From: martin-azon Date: Fri, 17 Apr 2026 10:37:41 +0200 Subject: [PATCH 08/10] fixed tests according to macros --- ec/tests/curve_montgomery_tests.rs | 17 +++++++----- ec/tests/point_edwards_test.rs | 18 ++++++------- ec/tests/point_montgomery_tests.rs | 6 ++--- isogeny/src/montgomery.rs | 42 ++++++++++++++++++------------ 4 files changed, 48 insertions(+), 35 deletions(-) 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 5dc0f37..6605798 100644 --- a/ec/tests/point_montgomery_tests.rs +++ b/ec/tests/point_montgomery_tests.rs @@ -25,15 +25,15 @@ fn fq(n: u64) -> F59 { fn curve() -> MontgomeryCurve { MontgomeryCurve::new(fp(3), fp(5)) } -fn curve2() -> MontgomeryCurve { MontgomeryCurve::new(fq(7), fq(1)) } +//fn curve2() -> MontgomeryCurve { MontgomeryCurve::new(fq(7), fq(1)) } 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/isogeny/src/montgomery.rs b/isogeny/src/montgomery.rs index ca26980..c10020c 100644 --- a/isogeny/src/montgomery.rs +++ b/isogeny/src/montgomery.rs @@ -1,20 +1,23 @@ +/* + //! Isogenies for montgomery curves //! //! AT THE MOMENT THIS WORKS ONLY IN ODD CHARACTERISTIC!!! -use subtle::{Choice, CtOption, ConditionallySelectable, ConstantTimeEq}; +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, + pub xk: KummerPoint, } @@ -22,12 +25,15 @@ pub struct MontgomeryIsogeny { // Manual trait impls // --------------------------------------------------------------------------- -impl PartialEq for MontgomeryIsogeny { - fn eq(&self, other: &Self) -> bool { - self.domain == other.domain && self.codomain == other.codomain && self.xK == other.xK +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, @@ -38,18 +44,21 @@ where // Constructors // --------------------------------------------------------------------------- -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) { - +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!() + } } } @@ -72,3 +81,4 @@ impl MontgomeryIsogeny { +*/ \ No newline at end of file From 3927304d82a7a8f1622003c08eaf4e73192850c8 Mon Sep 17 00:00:00 2001 From: martin-azon Date: Fri, 17 Apr 2026 15:21:04 +0200 Subject: [PATCH 09/10] fixed conflicts curve_edwards --- ec/src/curve_edwards.rs | 210 +++++++++++++++------------------------- 1 file changed, 77 insertions(+), 133 deletions(-) diff --git a/ec/src/curve_edwards.rs b/ec/src/curve_edwards.rs index f64bf1f..f37ff72 100644 --- a/ec/src/curve_edwards.rs +++ b/ec/src/curve_edwards.rs @@ -28,14 +28,10 @@ //! - Binary Edwards curves (characteristic $2$): Bernstein–Lange–Rezaeian Farashahi (2008) //! - Odd characteristic: -<<<<<<< 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_edwards::EdwardsPoint; @@ -52,19 +48,7 @@ pub struct EdwardsCurve { pub d2: F, } -<<<<<<< HEAD -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 } - } -======= + impl fmt::Display for EdwardsCurve where F: FieldOps + fmt::Display, @@ -98,20 +82,18 @@ 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 } } - } ->>>>>>> origin/main /// Construct a binary Edwards curve /// `d₁(x+y) + d₂(x²+y²) = xy + xy(x+y) + x²y²`. @@ -166,61 +148,72 @@ impl EdwardsCurve { 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(); - - if let Some(y) = rhs.sqrt().into_option() { - let p = EdwardsPoint::new(x, y); - debug_assert!(self.is_on_curve(&p)); - return p; + /// 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(); + + if let Some(y) = rhs.sqrt().into_option() { + let p = EdwardsPoint::new(x, y); + debug_assert!(self.is_on_curve(&p)); + return p; + } } } } } -<<<<<<< HEAD -ref_field_trait_impl! { - impl Curve for EdwardsCurve { + + + + + + + + + + + + + + + + + + + +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) -======= -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 random_point(&self, rng: &mut (impl rand::CryptoRng + rand::Rng)) -> Self::Point { + EdwardsCurve::random_point(self, rng) + } - fn j_invariant(&self) -> F { + fn j_invariant(&self) -> F { if F::characteristic()[0] != 2 { // j = 16(1 + 14d + d²)³ / (d(1 − d)⁴) let d = self.d2; @@ -228,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") @@ -250,74 +243,25 @@ 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") ->>>>>>> origin/main - } - - fn random_point(&self) -> Self::Point { - todo!() - } - - fn j_invariant(&self) -> F { - if F::characteristic()[0] != 2 { - // j = 16(1 + 14d + d²)³ / (d(1 − d)⁴) - let d_sq = ::square(&self.d2); - - let one = F::one(); - let two = ::double(&one); - let four = ::double(&two); - let eight = ::double(&four); - let fourteen_tmp = &eight + &four; - let fourteen = &fourteen_tmp + &two; - let sixteen = ::double(&eight); - - let fourteen_d = &fourteen * &self.d2; - let inner_tmp = &one + &fourteen_d; - let inner = &inner_tmp + &d_sq; - let inner_sq = ::square(&inner); - let inner_cubed = &inner * &inner_sq; - let numer = &sixteen * &inner_cubed; - - let one_minus_d = &one - &self.d2; - let omd2 = ::square(&one_minus_d); - let omd4 = ::square(&omd2); - let denom = &self.d2 * &omd4; - - let denom_inv = ::invert(&denom) - .into_option() - .expect("d(1-d)^4 must be invertible"); - - &numer * &denom_inv - } else { - // j = 1 / (d₁⁴ (d₁⁴ + d₁² + d₂²)) - let d1_sq = ::square(&self.d1); - let d1_4 = ::square(&d1_sq); - let d2_sq = ::square(&self.d2); - - let inner_tmp = &d1_4 + &d1_sq; - let inner = &inner_tmp + &d2_sq; - let denom = &d1_4 * &inner; - - ::invert(&denom) - .into_option() - .expect("j-invariant denominator must be invertible") } } fn a_invariants(&self) -> Vec { if F::characteristic()[0] != 2 { - vec![self.d2.clone()] + vec![self.d2] } else { - vec![self.d1.clone(), self.d2.clone()] + vec![self.d1, self.d2] } } } } + // --------------------------------------------------------------------------- // Constant-time functionalities // --------------------------------------------------------------------------- From 35e27303e885374a3f8ac21cc44bcd87ff18251a Mon Sep 17 00:00:00 2001 From: martin-azon Date: Fri, 17 Apr 2026 15:52:42 +0200 Subject: [PATCH 10/10] solving conflicts for point_weierstrass --- ec/src/point_ops.rs | 5 +- ec/src/point_weierstrass.rs | 250 +++++------------------------------- 2 files changed, 34 insertions(+), 221 deletions(-) diff --git a/ec/src/point_ops.rs b/ec/src/point_ops.rs index 2bddf10..a69fc9f 100644 --- a/ec/src/point_ops.rs +++ b/ec/src/point_ops.rs @@ -13,12 +13,9 @@ 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. -<<<<<<< HEAD +/// pub trait PointOps: Clone + ConditionallySelectable { -======= -pub trait PointOps: Clone + ConditionallySelectable { /// The base field $\mathbb{F}_{p^M}$ ->>>>>>> origin/main type BaseField: FieldOps; /// The elliptic curve we're working on diff --git a/ec/src/point_weierstrass.rs b/ec/src/point_weierstrass.rs index 3944a25..31cdfcb 100644 --- a/ec/src/point_weierstrass.rs +++ b/ec/src/point_weierstrass.rs @@ -9,32 +9,22 @@ //! //! The addition formulas implement the general Weierstrass group law: //! -<<<<<<< HEAD //! | 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 | //! | Add $(P \ne \pm Q)$ | Chord-and-tangent (general Weierstrass) | $1$ inv + $6$ mul | //! | Double | Tangent (general Weierstrass) | $1$ inv + $7$ mul | //! | Scalar multiply | Montgomery ladder (constant-time scalar) | $O(n)$ doublings | ->>>>>>> origin/main // WARNING: SOME OF THE FUNCTIONS BELOW USE BRANCHES DEPENDING // ON WHETHER A POINT IS AT INFINITY OR NOT!!!! -<<<<<<< HEAD -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; -use crate::curve_weierstrass::WeierstrassCurve; -use crate::point_ops::PointOps; -use fp::field_ops::FieldOps; -use fp::{ref_field_impl, ref_field_trait_impl, ref_field_trait_impl_path}; -======= use core::fmt; //use std::os::unix::raw::ino_t; //use crypto_bigint::modular::ConstMontyForm; @@ -42,7 +32,7 @@ use crate::curve_weierstrass::WeierstrassCurve; use crate::point_ops::PointOps; use fp::field_ops::FieldOps; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; ->>>>>>> origin/main +use fp::{ref_field_impl, ref_field_trait_impl, ref_field_trait_impl_path}; /// An affine point on a Weierstrass elliptic curve over `F`. /// @@ -76,8 +66,6 @@ where } } -<<<<<<< HEAD -======= impl fmt::Display for AffinePoint where F: FieldOps + fmt::Display, @@ -99,7 +87,6 @@ where ->>>>>>> origin/main impl Eq for AffinePoint where F: FieldOps + ConstantTimeEq, @@ -204,122 +191,19 @@ where // Group operations // --------------------------------------------------------------------------- -<<<<<<< HEAD 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(); -======= -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; - - 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 { - if self.infinity { - return Self::identity(); - } - - // denominator = 2y₁ + a₁x₁ + a₃ - let denom = ::double(&self.y) + curve.a1 * self.x + curve.a3; - - // If denominator is zero the tangent is vertical → result is O. - let denom_inv = match denom.invert().into_option() { - Some(inv) => inv, - None => return Self::identity(), - }; - - // 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 lambda = numer * denom_inv; - - // 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 - }; - - // y₃ = λ(x₁ − x₃) − y₁ − a₁x₃ − a₃ - let y3 = { - let dx = self.x - x3; - let lam_dx = lambda * dx; - let a1x3 = curve.a1 * x3; - - lam_dx - self.y - a1x3 - curve.a3 - }; - - Self::new(x3, y3) - } - - /// 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(); - } - - // Same x-coordinate? - if self.x == other.x { - if self.y == other.y { - // P = Q → doubling - return self.double(curve); ->>>>>>> origin/main } - let minus_y = -&self.y; - let a1x = &curve.a1 * &self.x; - let tmp = &minus_y - &a1x; - let neg_y = &tmp - &curve.a3; + let neg_y = -&(&self.y + &(&(&curve.a1 * &self.x) - &curve.a3)); Self::new(self.x.clone(), neg_y) } -<<<<<<< HEAD /// Double a point: `[2]P`. /// /// Uses the tangent-line formula for the general Weierstrass model: @@ -332,82 +216,24 @@ impl AffinePoint { /// /// 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(); -======= - // 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 - }; - - // 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 - }; - - Self::new(x3, y3) - } - - /// 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); ->>>>>>> origin/main - } + if self.infinity { + return Self::identity(); + } - // denominator = 2y₁ + a₁x₁ + a₃ - let two_y = ::double(&self.y); - let a1x = &curve.a1 * &self.x; - let tmp = &two_y + &a1x; - let denom = &tmp + &curve.a3; + // denominator = 2y₁ + a₁x₁ + a₃ + 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 ::invert(&denom).into_option() { - Some(inv) => inv, - None => return Self::identity(), - }; + // If denominator is zero the tangent is vertical → result is O. + let denom_inv = match denom.invert().into_option() { + Some(inv) => inv, + None => return Self::identity(), + }; - // numerator = 3x₁² + 2a₂x₁ + a₄ − a₁y₁ - let numer = { + // numerator = 3x₁² + 2a₂x₁ + a₄ − a₁y₁ + let numer = { let x1_sq = ::square(&self.x); let two_x1_sq = ::double(&x1_sq); let three_x1_sq = &x1_sq + &two_x1_sq; @@ -449,7 +275,6 @@ impl AffinePoint { Self::new(x3, y3) } -<<<<<<< HEAD /// Add two points: `P + Q`. /// /// Handles all cases: @@ -504,39 +329,33 @@ impl AffinePoint { let tmp3 = &tmp2 - &self.x; &tmp3 - &other.x }; -======= - r0 - } -} - -impl PointOps for AffinePoint -where - F: FieldOps, -{ - type BaseField = F; - type Curve = WeierstrassCurve; ->>>>>>> origin/main // y₃ = λ(x₁ − x₃) − y₁ − a₁x₃ − a₃ let y3 = { - let dx3 = &self.x - &x3; - let lam_dx3 = &lambda * &dx3; + let dx = &self.x - &x3; + let lam_dx = &lambda * &dx; let a1x3 = &curve.a1 * &x3; -<<<<<<< HEAD - let tmp1 = &lam_dx3 - &self.y; + let tmp1 = &lam_dx - &self.y; let tmp2 = &tmp1 - &a1x3; &tmp2 - &curve.a3 }; -======= - fn is_identity(&self) -> bool { - self.infinity - } ->>>>>>> origin/main Self::new(x3, y3) + } + /// 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(); @@ -583,7 +402,6 @@ ref_field_trait_impl! { } } } -<<<<<<< HEAD ref_field_trait_impl_path! { impl (crate::point_ops::PointAdd) for AffinePoint { @@ -591,6 +409,4 @@ ref_field_trait_impl_path! { AffinePoint::::add(self, other, curve) } } -} -======= ->>>>>>> origin/main +} \ No newline at end of file