From c4750a74abada71abfcfbf576fd03816b0ae2bfb Mon Sep 17 00:00:00 2001 From: rustaceanrob Date: Tue, 7 Jul 2026 19:34:31 +0100 Subject: [PATCH] Simplify field traits --- curve/src/lib.rs | 117 +++++++++++++++++++---------------- field/src/lib.rs | 155 +++++++++++++++++++---------------------------- 2 files changed, 129 insertions(+), 143 deletions(-) diff --git a/curve/src/lib.rs b/curve/src/lib.rs index 12af534..c710e3e 100644 --- a/curve/src/lib.rs +++ b/curve/src/lib.rs @@ -1,8 +1,8 @@ use core::fmt; use core::hash::{Hash, Hasher}; -use field::{FieldElement, FieldOrder, Limbs, Secp256k1FieldOrder, Secp256k1GroupOrder, Sqrt3Mod4}; +use field::{Field, FieldElement, Secp256k1FieldOrder, Secp256k1GroupOrder, Sqrt3Mod4}; -pub trait Curve: fmt::Debug + Clone + Copy + PartialEq + Eq { +pub trait Curve: fmt::Debug + Clone + Copy + PartialEq + Eq { fn a(&self) -> FieldElement; fn b(&self) -> FieldElement; @@ -10,7 +10,7 @@ pub trait Curve: fmt::Debug + Clone + Copy + PartialEq + Eq { p1.add(&p2, self.a()) } - fn multiply(&self, scalar: Scalar, point: Point) -> Point { + fn multiply(&self, scalar: Scalar, point: Point) -> Point { point.mul(scalar, self.a()) } @@ -40,11 +40,12 @@ pub trait Curve: fmt::Debug + Clone + Copy + PartialEq + Eq { pub struct Secp256k1Curve; impl Secp256k1Curve { - pub const A: FieldElement = FieldElement::ZERO; + pub const A: FieldElement = + FieldElement::from_limbs_unchecked([0, 0, 0, 0]); pub const B: FieldElement = FieldElement::from_limbs_unchecked([7, 0, 0, 0]); - pub const GENERATOR: Point = Point::from_affine( + pub const GENERATOR: Point = Point::from_jacobian_unchecked( FieldElement::from_limbs_unchecked([ 0x59F2815B16F81798, 0x029BFCDB2DCE28D9, @@ -57,6 +58,7 @@ impl Secp256k1Curve { 0x5DA4FBFC0E1108A8, 0x483ADA7726A3C465, ]), + FieldElement::from_limbs_unchecked([1, 0, 0, 0]), ); pub fn point_from_scalar(scalar: Scalar) -> Point { @@ -75,14 +77,14 @@ impl Curve for Secp256k1Curve { } #[derive(Debug, Clone, Copy)] -pub struct Point { +pub struct Point { x: FieldElement, y: FieldElement, z: FieldElement, } #[derive(Debug, Clone, Copy, PartialEq, Eq, std::hash::Hash)] -pub enum PointRepresentation { +pub enum PointRepresentation { Affine { x: FieldElement, y: FieldElement, @@ -90,23 +92,29 @@ pub enum PointRepresentation { Infinity, } -impl Point { - pub const INFINITY: Self = Self { - x: FieldElement::ONE, - y: FieldElement::ONE, - z: FieldElement::ZERO, - }; - - pub const fn from_affine(x: FieldElement, y: FieldElement) -> Self { +impl Point { + pub fn infinity() -> Self { Self { - x, - y, - z: FieldElement::ONE, + x: FieldElement::one(), + y: FieldElement::one(), + z: FieldElement::zero(), } } + pub const fn from_jacobian_unchecked( + x: FieldElement, + y: FieldElement, + z: FieldElement, + ) -> Self { + Self { x, y, z } + } + + pub fn from_affine(x: FieldElement, y: FieldElement) -> Self { + Self::from_jacobian_unchecked(x, y, FieldElement::one()) + } + pub fn representation(&self) -> PointRepresentation { - if self.z == FieldElement::ZERO { + if self.z == FieldElement::zero() { return PointRepresentation::Infinity; } let z_inv = self.z.inv(); @@ -119,19 +127,19 @@ impl Point { } pub fn is_infinity(&self) -> bool { - self.z == FieldElement::ZERO + self.z == FieldElement::zero() } pub fn neg(&self) -> Self { Self { x: self.x, - y: FieldElement::ZERO - self.y, + y: FieldElement::zero() - self.y, z: self.z, } } fn double(&self, a: FieldElement) -> Self { - if self.z == FieldElement::ZERO { + if self.z == FieldElement::zero() { return *self; } let xx = self.x * self.x; @@ -160,10 +168,10 @@ impl Point { } fn add(&self, other: &Self, a: FieldElement) -> Self { - if self.z == FieldElement::ZERO { + if self.z == FieldElement::zero() { return *other; } - if other.z == FieldElement::ZERO { + if other.z == FieldElement::zero() { return *self; } let z1z1 = self.z * self.z; @@ -174,11 +182,11 @@ impl Point { let s2 = other.y * self.z * z1z1; let h = u2 - u1; let r_half = s2 - s1; - if h == FieldElement::ZERO { - if r_half == FieldElement::ZERO { + if h == FieldElement::zero() { + if r_half == FieldElement::zero() { return self.double(a); } else { - return Self::INFINITY; + return Self::infinity(); } } let two_h = h + h; @@ -200,8 +208,8 @@ impl Point { } } - fn mul(&self, scalar: Scalar, a: FieldElement) -> Self { - let mut result = Self::INFINITY; + fn mul(&self, scalar: Scalar, a: FieldElement) -> Self { + let mut result = Self::infinity(); for &limb in scalar.0.as_ref().iter().rev() { for bit_idx in (0..u64::BITS).rev() { result = result.double(a); @@ -214,10 +222,10 @@ impl Point { } } -impl PartialEq for Point { +impl PartialEq for Point { fn eq(&self, other: &Self) -> bool { - let z1_zero = self.z == FieldElement::ZERO; - let z2_zero = other.z == FieldElement::ZERO; + let z1_zero = self.z == FieldElement::zero(); + let z2_zero = other.z == FieldElement::zero(); if z1_zero || z2_zero { return z1_zero && z2_zero; } @@ -229,28 +237,30 @@ impl PartialEq for Point { } } -impl Eq for Point {} +impl Eq for Point {} -impl Hash for Point { +impl Hash for Point { fn hash(&self, state: &mut H) { self.representation().hash(state); } } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, std::hash::Hash)] -pub struct Scalar(F::Limbs); +pub struct Scalar(F::Limbs); -impl Scalar { +impl Scalar { pub const fn from_limbs(limbs: F::Limbs) -> Self { Self(limbs) } pub fn from_u64(x: u64) -> Self { - Self(::from_u64(x)) + let mut limbs = F::Limbs::default(); + limbs.as_mut()[0] = x; + Self(limbs) } pub fn from_u128(n: u128) -> Self { - let mut limbs = ::ZERO; + let mut limbs = F::Limbs::default(); { let s = limbs.as_mut(); s[0] = n as u64; @@ -262,7 +272,7 @@ impl Scalar { } } -impl> Scalar { +impl> Scalar { pub fn from_bytes(bytes: [u8; 32]) -> Self { Self([ u64::from_le_bytes(bytes[0..8].try_into().unwrap()), @@ -340,7 +350,7 @@ mod tests { } fn infinity() -> Pt { - Pt::INFINITY + Pt::infinity() } #[test] @@ -499,10 +509,10 @@ mod tests { struct E; impl Curve for E { fn a(&self) -> FieldElement { - FieldElement::ONE + FieldElement::one() } fn b(&self) -> FieldElement { - FieldElement::ZERO + FieldElement::zero() } } assert_eq!(FieldElement::from_u64(1728), E.j_invariant()); @@ -510,7 +520,10 @@ mod tests { #[test] fn representation_of_infinity_is_infinity_variant() { - assert_eq!(Pt::INFINITY.representation(), PointRepresentation::Infinity); + assert_eq!( + Pt::infinity().representation(), + PointRepresentation::Infinity + ); } #[test] @@ -573,7 +586,7 @@ mod tests { 0x5DA4FBFC0E1108A8, 0x483ADA7726A3C465, ]); - assert!(y == g_y || y == Fe::ZERO - g_y); + assert!(y == g_y || y == Fe::zero() - g_y); } PointRepresentation::Infinity => panic!("expected affine"), } @@ -585,13 +598,13 @@ mod tests { struct E; impl Curve for E { fn a(&self) -> Fe { - Fe::ONE + Fe::one() } fn b(&self) -> Fe { - Fe::ZERO - Fe::TWO + Fe::zero() - Fe::two() } } - assert!(E.lift(Fe::ZERO).is_none()); + assert!(E.lift(Fe::zero()).is_none()); } #[test] @@ -600,18 +613,18 @@ mod tests { struct E; impl Curve for E { fn a(&self) -> Fe { - Fe::ONE + Fe::one() } fn b(&self) -> Fe { - Fe::ZERO - Fe::TWO + Fe::zero() - Fe::two() } } - let p = E.lift(Fe::ONE).expect("(1, 0) is on E"); + let p = E.lift(Fe::one()).expect("(1, 0) is on E"); assert_eq!( p.representation(), PointRepresentation::Affine { - x: Fe::ONE, - y: Fe::ZERO + x: Fe::one(), + y: Fe::zero() } ); } diff --git a/field/src/lib.rs b/field/src/lib.rs index df8e0da..5c1c3dc 100644 --- a/field/src/lib.rs +++ b/field/src/lib.rs @@ -3,39 +3,14 @@ use core::hash::Hash; use core::marker::PhantomData; use core::ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign}; -pub trait Limbs: Copy + fmt::Debug + PartialEq + Eq + Hash + AsRef<[u64]> + AsMut<[u64]> { - const ZERO: Self; - const ONE: Self; - const TWO: Self; - const THREE: Self; - fn from_u64(x: u64) -> Self; +pub trait Limbs: + Copy + fmt::Debug + PartialEq + Eq + Hash + AsRef<[u64]> + AsMut<[u64]> + Default +{ } -impl Limbs for [u64; N] { - const ZERO: Self = [0u64; N]; - const ONE: Self = { - let mut r = [0u64; N]; - r[0] = 1; - r - }; - const TWO: Self = { - let mut r = [0u64; N]; - r[0] = 2; - r - }; - const THREE: Self = { - let mut r = [0u64; N]; - r[0] = 3; - r - }; - fn from_u64(x: u64) -> Self { - let mut r = [0u64; N]; - r[0] = x; - r - } -} +impl Limbs for [u64; N] where [u64; N]: Default {} -pub trait FieldOrder: fmt::Debug + Clone + Copy + PartialEq + Eq { +pub trait Field: fmt::Debug + Clone + Copy + PartialEq + Eq { type Limbs: Limbs; const MODULUS: Self::Limbs; } @@ -43,7 +18,7 @@ pub trait FieldOrder: fmt::Debug + Clone + Copy + PartialEq + Eq { #[derive(Debug, Clone, Copy, PartialEq, Eq, std::hash::Hash)] pub struct Secp256k1FieldOrder; -impl FieldOrder for Secp256k1FieldOrder { +impl Field for Secp256k1FieldOrder { type Limbs = [u64; 4]; const MODULUS: [u64; 4] = [ 0xFFFFFFFEFFFFFC2F, @@ -56,7 +31,7 @@ impl FieldOrder for Secp256k1FieldOrder { #[derive(Debug, Clone, Copy, PartialEq, Eq, std::hash::Hash)] pub struct Secp256k1GroupOrder; -impl FieldOrder for Secp256k1GroupOrder { +impl Field for Secp256k1GroupOrder { type Limbs = [u64; 4]; const MODULUS: [u64; 4] = [ 0xBFD25E8CD0364141, @@ -72,7 +47,7 @@ impl FieldOrder for Secp256k1GroupOrder { #[derive(Debug, Clone, Copy, PartialEq, Eq, std::hash::Hash)] pub struct Csidh512FieldOrder; -impl FieldOrder for Csidh512FieldOrder { +impl Field for Csidh512FieldOrder { type Limbs = [u64; 8]; const MODULUS: [u64; 8] = [ 0x1B81B90533C6C87B, @@ -88,41 +63,39 @@ impl FieldOrder for Csidh512FieldOrder { /// Marker trait for prime fields with p ≡ 3 (mod 4), enabling the fast /// square-root x^((p+1)/4). -pub trait Sqrt3Mod4: FieldOrder {} +pub trait Sqrt3Mod4: Field {} impl Sqrt3Mod4 for Secp256k1FieldOrder {} impl Sqrt3Mod4 for Csidh512FieldOrder {} #[derive(Debug, Clone, Copy, PartialEq, Eq, std::hash::Hash)] -pub struct FieldElement { +pub struct FieldElement { limbs: P::Limbs, _marker: PhantomData

, } -impl FieldElement

{ - pub const ZERO: Self = Self { - limbs: ::ZERO, - _marker: PhantomData, - }; +impl FieldElement

{ + pub fn zero() -> Self { + Self::from_u64(0) + } - pub const ONE: Self = Self { - limbs: ::ONE, - _marker: PhantomData, - }; + pub fn one() -> Self { + Self::from_u64(1) + } - pub const TWO: Self = Self { - limbs: ::TWO, - _marker: PhantomData, - }; + pub fn two() -> Self { + Self::from_u64(2) + } - pub const THREE: Self = Self { - limbs: ::THREE, - _marker: PhantomData, - }; + pub fn three() -> Self { + Self::from_u64(3) + } pub fn from_u64(x: u64) -> Self { + let mut limbs = P::Limbs::default(); + limbs.as_mut()[0] = x; Self { - limbs: ::from_u64(x), + limbs, _marker: PhantomData, } } @@ -140,7 +113,7 @@ impl FieldElement

{ let m = P::MODULUS; let m_slice = m.as_ref(); - let mut sum = ::ZERO; + let mut sum = P::Limbs::default(); let mut c = false; for (i, s) in sum.as_mut().iter_mut().enumerate() { let (v, nc) = a[i].carrying_add(b[i], c); @@ -149,7 +122,7 @@ impl FieldElement

{ } let carry = c; - let mut diff = ::ZERO; + let mut diff = P::Limbs::default(); let mut br = false; { let sum_slice = sum.as_ref(); @@ -174,7 +147,7 @@ impl FieldElement

{ let m = P::MODULUS; let m_slice = m.as_ref(); - let mut diff = ::ZERO; + let mut diff = P::Limbs::default(); let mut br = false; for (i, d) in diff.as_mut().iter_mut().enumerate() { let (v, nb) = a[i].borrowing_sub(b[i], br); @@ -184,7 +157,7 @@ impl FieldElement

{ let borrow = br; let limbs = if borrow { - let mut r = ::ZERO; + let mut r = P::Limbs::default(); let mut c = false; { let diff_slice = diff.as_ref(); @@ -206,7 +179,7 @@ impl FieldElement

{ #[inline] pub fn mul(&self, rhs: &Self) -> Self { - let (lo, hi) = wide_mul::(&self.limbs, &rhs.limbs); + let (lo, hi) = wide_mul::

(&self.limbs, &rhs.limbs); let limbs = reduce_wide::

(lo, hi); Self { limbs, @@ -215,7 +188,7 @@ impl FieldElement

{ } pub fn pow(&self, exp: P::Limbs) -> Self { - let mut result = Self::ONE; + let mut result = Self::one(); for &limb in exp.as_ref().iter().rev() { for bit_idx in (0..u64::BITS).rev() { result = result.mul(&result); @@ -277,7 +250,7 @@ impl FieldElement

{ } } -impl> FieldElement

{ +impl> FieldElement

{ pub fn from_bytes_unchecked(bytes: [u8; 32]) -> Self { let limbs = [ u64::from_le_bytes(bytes[0..8].try_into().unwrap()), @@ -302,12 +275,12 @@ impl> FieldElement

{ } #[inline] -fn wide_mul(a: &L, b: &L) -> (L, L) { +fn wide_mul(a: &P::Limbs, b: &P::Limbs) -> (P::Limbs, P::Limbs) { let a_slice = a.as_ref(); let b_slice = b.as_ref(); let n = a_slice.len(); - let mut lo = L::ZERO; - let mut hi = L::ZERO; + let mut lo = P::Limbs::default(); + let mut hi = P::Limbs::default(); { let lo_slice = lo.as_mut(); let hi_slice = hi.as_mut(); @@ -335,7 +308,7 @@ fn wide_mul(a: &L, b: &L) -> (L, L) { } #[inline] -fn ge_modulus(x: &P::Limbs) -> bool { +fn ge_modulus(x: &P::Limbs) -> bool { let x_slice = x.as_ref(); let m = P::MODULUS; let m_slice = m.as_ref(); @@ -348,14 +321,14 @@ fn ge_modulus(x: &P::Limbs) -> bool { } #[inline] -fn reduce_wide(lo: P::Limbs, hi: P::Limbs) -> P::Limbs { +fn reduce_wide(lo: P::Limbs, hi: P::Limbs) -> P::Limbs { let lo_slice = lo.as_ref(); let hi_slice = hi.as_ref(); let n = lo_slice.len(); let m = P::MODULUS; let m_slice = m.as_ref(); - let mut r = ::ZERO; + let mut r = P::Limbs::default(); let total_bits = 2 * n * 64; for bit_idx in (0..total_bits).rev() { let r_slice = r.as_mut(); @@ -383,121 +356,121 @@ fn reduce_wide(lo: P::Limbs, hi: P::Limbs) -> P::Limbs { r } -impl Add for FieldElement

{ +impl Add for FieldElement

{ type Output = Self; fn add(self, rhs: Self) -> Self { FieldElement::

::add(&self, &rhs) } } -impl Add<&FieldElement

> for FieldElement

{ +impl Add<&FieldElement

> for FieldElement

{ type Output = FieldElement

; fn add(self, rhs: &FieldElement

) -> FieldElement

{ FieldElement::

::add(&self, rhs) } } -impl Add> for &FieldElement

{ +impl Add> for &FieldElement

{ type Output = FieldElement

; fn add(self, rhs: FieldElement

) -> FieldElement

{ FieldElement::

::add(self, &rhs) } } -impl Add<&FieldElement

> for &FieldElement

{ +impl Add<&FieldElement

> for &FieldElement

{ type Output = FieldElement

; fn add(self, rhs: &FieldElement

) -> FieldElement

{ FieldElement::

::add(self, rhs) } } -impl AddAssign for FieldElement

{ +impl AddAssign for FieldElement

{ fn add_assign(&mut self, rhs: Self) { *self = FieldElement::

::add(self, &rhs); } } -impl AddAssign<&FieldElement

> for FieldElement

{ +impl AddAssign<&FieldElement

> for FieldElement

{ fn add_assign(&mut self, rhs: &FieldElement

) { *self = FieldElement::

::add(self, rhs); } } -impl Sub for FieldElement

{ +impl Sub for FieldElement

{ type Output = Self; fn sub(self, rhs: Self) -> Self { FieldElement::

::sub(&self, &rhs) } } -impl Sub<&FieldElement

> for FieldElement

{ +impl Sub<&FieldElement

> for FieldElement

{ type Output = FieldElement

; fn sub(self, rhs: &FieldElement

) -> FieldElement

{ FieldElement::

::sub(&self, rhs) } } -impl Sub> for &FieldElement

{ +impl Sub> for &FieldElement

{ type Output = FieldElement

; fn sub(self, rhs: FieldElement

) -> FieldElement

{ FieldElement::

::sub(self, &rhs) } } -impl Sub<&FieldElement

> for &FieldElement

{ +impl Sub<&FieldElement

> for &FieldElement

{ type Output = FieldElement

; fn sub(self, rhs: &FieldElement

) -> FieldElement

{ FieldElement::

::sub(self, rhs) } } -impl SubAssign for FieldElement

{ +impl SubAssign for FieldElement

{ fn sub_assign(&mut self, rhs: Self) { *self = FieldElement::

::sub(self, &rhs); } } -impl SubAssign<&FieldElement

> for FieldElement

{ +impl SubAssign<&FieldElement

> for FieldElement

{ fn sub_assign(&mut self, rhs: &FieldElement

) { *self = FieldElement::

::sub(self, rhs); } } -impl Mul for FieldElement

{ +impl Mul for FieldElement

{ type Output = Self; fn mul(self, rhs: Self) -> Self { FieldElement::

::mul(&self, &rhs) } } -impl Mul<&FieldElement

> for FieldElement

{ +impl Mul<&FieldElement

> for FieldElement

{ type Output = FieldElement

; fn mul(self, rhs: &FieldElement

) -> FieldElement

{ FieldElement::

::mul(&self, rhs) } } -impl Mul> for &FieldElement

{ +impl Mul> for &FieldElement

{ type Output = FieldElement

; fn mul(self, rhs: FieldElement

) -> FieldElement

{ FieldElement::

::mul(self, &rhs) } } -impl Mul<&FieldElement

> for &FieldElement

{ +impl Mul<&FieldElement

> for &FieldElement

{ type Output = FieldElement

; fn mul(self, rhs: &FieldElement

) -> FieldElement

{ FieldElement::

::mul(self, rhs) } } -impl MulAssign for FieldElement

{ +impl MulAssign for FieldElement

{ fn mul_assign(&mut self, rhs: Self) { *self = FieldElement::

::mul(self, &rhs); } } -impl MulAssign<&FieldElement

> for FieldElement

{ +impl MulAssign<&FieldElement

> for FieldElement

{ fn mul_assign(&mut self, rhs: &FieldElement

) { *self = FieldElement::

::mul(self, rhs); } @@ -733,7 +706,7 @@ mod tests { #[test] fn inv_of_one_is_one() { - assert_eq!(Fp::ONE.inv().limbs, [1, 0, 0, 0]); + assert_eq!(Fp::one().inv().limbs, [1, 0, 0, 0]); } #[test] @@ -780,13 +753,13 @@ mod tests { #[test] fn sqrt_of_zero_is_zero() { - assert_eq!(Fp::ZERO.sqrt(), Fp::ZERO); + assert_eq!(Fp::zero().sqrt(), Fp::zero()); } #[test] fn sqrt_of_one_squares_to_one() { - let r = Fp::ONE.sqrt(); - assert_eq!(r * r, Fp::ONE); + let r = Fp::one().sqrt(); + assert_eq!(r * r, Fp::one()); } #[test] @@ -818,13 +791,13 @@ mod tests { #[test] fn csidh_sqrt_of_zero_is_zero() { - assert_eq!(Fc::ZERO.sqrt(), Fc::ZERO); + assert_eq!(Fc::zero().sqrt(), Fc::zero()); } #[test] fn csidh_sqrt_of_one_squares_to_one() { - let r = Fc::ONE.sqrt(); - assert_eq!(r * r, Fc::ONE); + let r = Fc::one().sqrt(); + assert_eq!(r * r, Fc::one()); } #[test]