From f60e2a6108e7dc9ee84707ec6adc394a6a29a161 Mon Sep 17 00:00:00 2001 From: rustaceanrob Date: Wed, 8 Jul 2026 12:03:40 +0100 Subject: [PATCH 1/4] Add windowed point mult --- curve/src/lib.rs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/curve/src/lib.rs b/curve/src/lib.rs index a9dce69..e2cc3ad 100644 --- a/curve/src/lib.rs +++ b/curve/src/lib.rs @@ -213,14 +213,22 @@ impl Point { } fn mul(&self, scalar: Scalar, a: FieldElement) -> Self { + let mut table = [Self::infinity(); 16]; + table[1] = *self; + for k in 2..16 { + table[k] = table[k - 1].add(self, a); + } + + let limbs = scalar.0.as_ref(); + let n_windows = limbs.len() * 16; 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); - if (limb >> bit_idx) & 1 == 1 { - result = result.add(self, a); - } - } + for i in (0..n_windows).rev() { + result = result.double(a); + result = result.double(a); + result = result.double(a); + result = result.double(a); + let chunk = ((limbs[i / 16] >> ((i % 16) * 4)) & 0xF) as usize; + result = result.add(&table[chunk], a); } result } From 588d8ffc1d8bf9930bf42eac17e019cc89b59d45 Mon Sep 17 00:00:00 2001 From: rustaceanrob Date: Wed, 8 Jul 2026 12:12:12 +0100 Subject: [PATCH 2/4] Use CIOS montegomery multiplication --- field/src/lib.rs | 120 ++++++++++++++++++----------------------------- 1 file changed, 46 insertions(+), 74 deletions(-) diff --git a/field/src/lib.rs b/field/src/lib.rs index 5c8b3c7..56c2662 100644 --- a/field/src/lib.rs +++ b/field/src/lib.rs @@ -321,39 +321,6 @@ impl> FieldElement

{ } } -#[inline] -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 = P::Limbs::default(); - let mut hi = P::Limbs::default(); - { - let lo_slice = lo.as_mut(); - let hi_slice = hi.as_mut(); - for i in 0..n { - let mut carry = 0u64; - for (j, &bj) in b_slice.iter().enumerate() { - let idx = i + j; - let cell = if idx < n { - lo_slice[idx] - } else { - hi_slice[idx - n] - }; - let (product, new_carry) = a_slice[i].carrying_mul_add(bj, cell, carry); - if idx < n { - lo_slice[idx] = product; - } else { - hi_slice[idx - n] = product; - } - carry = new_carry; - } - hi_slice[i] = carry; - } - } - (lo, hi) -} - #[inline] fn ge_modulus(x: &P::Limbs) -> bool { let x_slice = x.as_ref(); @@ -367,59 +334,64 @@ fn ge_modulus(x: &P::Limbs) -> bool { true } -fn montgomery_reduce(mut lo: P::Limbs, mut hi: P::Limbs) -> P::Limbs { - let n = lo.as_ref().len(); +pub fn mont_mul(a: &P::Limbs, b: &P::Limbs) -> P::Limbs { + let a = a.as_ref(); + let b = b.as_ref(); let m = P::MODULUS; let p = m.as_ref(); let p_inv = P::PARAMS.p_inv; - let mut extra_carry = false; + let n = a.len(); - for i in 0..n { - let m_i = lo.as_ref()[i].wrapping_mul(p_inv); + let mut t = P::Limbs::default(); + let mut t_hi: u64 = 0; + let mut t_hi1: u64 = 0; + + for &bi in b { let mut carry = 0u64; - for (j, &pj) in p.iter().enumerate() { - let idx = i + j; - let cell = if idx < n { - lo.as_ref()[idx] - } else { - hi.as_ref()[idx - n] - }; - let (prod, new_carry) = m_i.carrying_mul_add(pj, cell, carry); - if idx < n { - lo.as_mut()[idx] = prod; - } else { - hi.as_mut()[idx - n] = prod; + { + let t_mut = t.as_mut(); + for j in 0..n { + let prod = + (a[j] as u128) * (bi as u128) + (t_mut[j] as u128) + (carry as u128); + t_mut[j] = prod as u64; + carry = (prod >> 64) as u64; } - carry = new_carry; - } - let mut k = i + n; - while carry != 0 && k < 2 * n { - let cell = hi.as_ref()[k - n]; - let (v, nc) = cell.overflowing_add(carry); - hi.as_mut()[k - n] = v; - carry = if nc { 1 } else { 0 }; - k += 1; } - if carry != 0 { - extra_carry = true; + let s = (t_hi as u128) + (carry as u128); + t_hi = s as u64; + t_hi1 = t_hi1.wrapping_add((s >> 64) as u64); + + let mm = t.as_ref()[0].wrapping_mul(p_inv); + let prod0 = (mm as u128) * (p[0] as u128) + (t.as_ref()[0] as u128); + let mut carry = (prod0 >> 64) as u64; + { + let t_mut = t.as_mut(); + for j in 1..n { + let prod = (mm as u128) * (p[j] as u128) + + (t_mut[j] as u128) + + (carry as u128); + t_mut[j - 1] = prod as u64; + carry = (prod >> 64) as u64; + } } + let s = (t_hi as u128) + (carry as u128); + t.as_mut()[n - 1] = s as u64; + let cout = (s >> 64) as u64; + let s2 = (t_hi1 as u128) + (cout as u128); + t_hi = s2 as u64; + t_hi1 = (s2 >> 64) as u64; } - let mut r = hi; - if extra_carry || ge_modulus::

(&r) { - let mut b = false; - for (i, ri) in r.as_mut().iter_mut().enumerate() { - let (d, nb) = ri.borrowing_sub(p[i], b); - *ri = d; - b = nb; + if t_hi != 0 || ge_modulus::

(&t) { + let t_mut = t.as_mut(); + let mut borrow = false; + for i in 0..n { + let (d, nb) = t_mut[i].borrowing_sub(p[i], borrow); + t_mut[i] = d; + borrow = nb; } } - r -} - -pub fn mont_mul(a: &P::Limbs, b: &P::Limbs) -> P::Limbs { - let (lo, hi) = wide_mul::

(a, b); - montgomery_reduce::

(lo, hi) + t } impl Add for FieldElement

{ From 0a5daefc7c143fb03e55f8e3c264abfc9ee907c4 Mon Sep 17 00:00:00 2001 From: rustaceanrob Date: Wed, 8 Jul 2026 12:39:05 +0100 Subject: [PATCH 3/4] Add `README` --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..a0bbc60 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# ectools + +This repository implements finite field arithmetic, elliptic curves in short Weierstrass form, and other primitives that may be used in more complex protocols, such as pairings, isogenies, etc. When cryptographic protocols are proposed in academia, these crates may be used to implement them and assess performance and complexity. + +## Crates +- `curve` implementation of point addition and multiplication over a generic elliptic curve +- `field` airthmetic over finite fields From 66dc80f4746d86ad3ee0f0c5e8900e0b1417f4ee Mon Sep 17 00:00:00 2001 From: rustaceanrob Date: Wed, 8 Jul 2026 12:40:04 +0100 Subject: [PATCH 4/4] Format fix --- field/src/lib.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/field/src/lib.rs b/field/src/lib.rs index 56c2662..5c76b48 100644 --- a/field/src/lib.rs +++ b/field/src/lib.rs @@ -351,8 +351,7 @@ pub fn mont_mul(a: &P::Limbs, b: &P::Limbs) -> P::Limbs { { let t_mut = t.as_mut(); for j in 0..n { - let prod = - (a[j] as u128) * (bi as u128) + (t_mut[j] as u128) + (carry as u128); + let prod = (a[j] as u128) * (bi as u128) + (t_mut[j] as u128) + (carry as u128); t_mut[j] = prod as u64; carry = (prod >> 64) as u64; } @@ -367,9 +366,7 @@ pub fn mont_mul(a: &P::Limbs, b: &P::Limbs) -> P::Limbs { { let t_mut = t.as_mut(); for j in 1..n { - let prod = (mm as u128) * (p[j] as u128) - + (t_mut[j] as u128) - + (carry as u128); + let prod = (mm as u128) * (p[j] as u128) + (t_mut[j] as u128) + (carry as u128); t_mut[j - 1] = prod as u64; carry = (prod >> 64) as u64; }