From ac8f1279ad98b5ca8c34ee245e559f29d8de6eb1 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Fri, 18 Jul 2025 15:59:39 +0200 Subject: [PATCH 1/2] Reuse Edwards windowed scalar multiplication for Decaf --- ed448-goldilocks/src/curve/scalar_mul.rs | 2 -- .../src/curve/scalar_mul/double_and_add.rs | 20 --------------- .../src/curve/scalar_mul/variable_base.rs | 25 ++++++++++++++++--- ed448-goldilocks/src/decaf/ops.rs | 6 ++--- 4 files changed, 25 insertions(+), 28 deletions(-) delete mode 100644 ed448-goldilocks/src/curve/scalar_mul/double_and_add.rs diff --git a/ed448-goldilocks/src/curve/scalar_mul.rs b/ed448-goldilocks/src/curve/scalar_mul.rs index b6ff71a1e..f25bfa715 100644 --- a/ed448-goldilocks/src/curve/scalar_mul.rs +++ b/ed448-goldilocks/src/curve/scalar_mul.rs @@ -1,7 +1,5 @@ -pub(crate) mod double_and_add; // pub(crate) mod double_base; pub(crate) mod variable_base; pub(crate) mod window; -pub(crate) use double_and_add::double_and_add; pub(crate) use variable_base::variable_base; diff --git a/ed448-goldilocks/src/curve/scalar_mul/double_and_add.rs b/ed448-goldilocks/src/curve/scalar_mul/double_and_add.rs deleted file mode 100644 index 917ba16d5..000000000 --- a/ed448-goldilocks/src/curve/scalar_mul/double_and_add.rs +++ /dev/null @@ -1,20 +0,0 @@ -use crate::curve::twedwards::extended::ExtendedPoint; -use crate::curve::twedwards::extensible::ExtensiblePoint; -use subtle::{Choice, ConditionallySelectable}; - -/// Traditional double and add algorithm -pub(crate) fn double_and_add(point: &ExtendedPoint, s_bits: [bool; 448]) -> ExtensiblePoint { - let mut result = ExtensiblePoint::IDENTITY; - - // NB, we reverse here, so we are going from MSB to LSB - // XXX: Would be great if subtle had a From for Choice. But maybe that is not it's purpose? - for bit in s_bits.into_iter().rev() { - result = result.double(); - - let mut p = ExtendedPoint::IDENTITY; - p.conditional_assign(point, Choice::from(bit as u8)); - result = result.to_extended().add_extended(&p); - } - - result -} diff --git a/ed448-goldilocks/src/curve/scalar_mul/variable_base.rs b/ed448-goldilocks/src/curve/scalar_mul/variable_base.rs index 8379bcbe6..54e0c0232 100644 --- a/ed448-goldilocks/src/curve/scalar_mul/variable_base.rs +++ b/ed448-goldilocks/src/curve/scalar_mul/variable_base.rs @@ -1,11 +1,12 @@ #![allow(non_snake_case)] use super::window::wnaf::LookupTable; -use crate::EdwardsScalar; +use crate::Scalar; use crate::curve::twedwards::{extended::ExtendedPoint, extensible::ExtensiblePoint}; +use crate::field::CurveWithScalar; use subtle::{Choice, ConditionallyNegatable}; -pub fn variable_base(point: &ExtendedPoint, s: &EdwardsScalar) -> ExtensiblePoint { +pub fn variable_base(point: &ExtendedPoint, s: &Scalar) -> ExtensiblePoint { let mut result = ExtensiblePoint::IDENTITY; // Recode Scalar @@ -37,12 +38,30 @@ pub fn variable_base(point: &ExtendedPoint, s: &EdwardsScalar) -> ExtensiblePoin #[cfg(test)] mod test { use super::*; + use crate::EdwardsScalar; use crate::TWISTED_EDWARDS_BASE_POINT; - use crate::curve::scalar_mul::double_and_add; use elliptic_curve::bigint::U448; + use subtle::ConditionallySelectable; #[test] fn test_scalar_mul() { + /// Traditional double and add algorithm + fn double_and_add(point: &ExtendedPoint, s_bits: [bool; 448]) -> ExtensiblePoint { + let mut result = ExtensiblePoint::IDENTITY; + + // NB, we reverse here, so we are going from MSB to LSB + // XXX: Would be great if subtle had a From for Choice. But maybe that is not it's purpose? + for bit in s_bits.into_iter().rev() { + result = result.double(); + + let mut p = ExtendedPoint::IDENTITY; + p.conditional_assign(point, Choice::from(bit as u8)); + result = result.to_extended().add_extended(&p); + } + + result + } + // XXX: In the future use known multiples from Sage in bytes form? let twisted_point = TWISTED_EDWARDS_BASE_POINT; let scalar = EdwardsScalar::new(U448::from_be_hex( diff --git a/ed448-goldilocks/src/decaf/ops.rs b/ed448-goldilocks/src/decaf/ops.rs index 1ba168537..01d59e85b 100644 --- a/ed448-goldilocks/src/decaf/ops.rs +++ b/ed448-goldilocks/src/decaf/ops.rs @@ -1,4 +1,5 @@ -use crate::{DecafAffinePoint, DecafScalar, curve::scalar_mul::double_and_add}; +use crate::curve::scalar_mul::variable_base; +use crate::{DecafAffinePoint, DecafScalar}; use core::{ borrow::Borrow, iter::Sum, @@ -13,8 +14,7 @@ impl Mul<&DecafScalar> for &DecafPoint { type Output = DecafPoint; fn mul(self, scalar: &DecafScalar) -> DecafPoint { - // XXX: We can do better than double and add - DecafPoint(double_and_add(&self.0, scalar.bits()).to_extended()) + DecafPoint(variable_base(&self.0, scalar).to_extended()) } } From d10656ce775f9b55ee6b1e93156de98e7c5e724a Mon Sep 17 00:00:00 2001 From: daxpedda Date: Fri, 18 Jul 2025 16:20:40 +0200 Subject: [PATCH 2/2] Simplify double-and-add code --- .../src/curve/scalar_mul/variable_base.rs | 8 ++++---- ed448-goldilocks/src/curve/twedwards/extensible.rs | 13 ++++++++++++- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/ed448-goldilocks/src/curve/scalar_mul/variable_base.rs b/ed448-goldilocks/src/curve/scalar_mul/variable_base.rs index 54e0c0232..976125a84 100644 --- a/ed448-goldilocks/src/curve/scalar_mul/variable_base.rs +++ b/ed448-goldilocks/src/curve/scalar_mul/variable_base.rs @@ -53,10 +53,10 @@ mod test { // XXX: Would be great if subtle had a From for Choice. But maybe that is not it's purpose? for bit in s_bits.into_iter().rev() { result = result.double(); - - let mut p = ExtendedPoint::IDENTITY; - p.conditional_assign(point, Choice::from(bit as u8)); - result = result.to_extended().add_extended(&p); + result.conditional_assign( + &result.to_extended().add_extended(point), + Choice::from(u8::from(bit)), + ); } result diff --git a/ed448-goldilocks/src/curve/twedwards/extensible.rs b/ed448-goldilocks/src/curve/twedwards/extensible.rs index cca9d04b9..f13ccf63e 100644 --- a/ed448-goldilocks/src/curve/twedwards/extensible.rs +++ b/ed448-goldilocks/src/curve/twedwards/extensible.rs @@ -4,7 +4,7 @@ use super::affine::AffinePoint; use super::extended::ExtendedPoint; use crate::field::FieldElement; -use subtle::{Choice, ConstantTimeEq}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; /// This is the representation that we will do most of the group operations on. // In affine (x,y) is the extensible point (X, Y, Z, T1, T2) @@ -31,6 +31,17 @@ impl ConstantTimeEq for ExtensiblePoint { XZ.ct_eq(&ZX) & YZ.ct_eq(&ZY) } } +impl ConditionallySelectable for ExtensiblePoint { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Self { + X: FieldElement::conditional_select(&a.X, &b.X, choice), + Y: FieldElement::conditional_select(&a.Y, &b.Y, choice), + Z: FieldElement::conditional_select(&a.Z, &b.Z, choice), + T1: FieldElement::conditional_select(&a.T1, &b.T1, choice), + T2: FieldElement::conditional_select(&a.T2, &b.T2, choice), + } + } +} impl PartialEq for ExtensiblePoint { fn eq(&self, other: &ExtensiblePoint) -> bool { self.ct_eq(other).into()