diff --git a/lib/crypto/src/arithmetic/mod.rs b/lib/crypto/src/arithmetic/mod.rs index 635b424cb..1ed2c56b2 100644 --- a/lib/crypto/src/arithmetic/mod.rs +++ b/lib/crypto/src/arithmetic/mod.rs @@ -12,6 +12,8 @@ use core::{ use limb::Limb; use zeroize::Zeroize; +use crate::bits::BitIteratorBE; + /// Defines a big integer with a constant length. pub trait BigInteger: 'static @@ -43,6 +45,7 @@ pub trait BigInteger: + for<'a> BitOrAssign<&'a Self> + BitOr + for<'a> BitOr<&'a Self, Output = Self> + + BitIteratorBE { /// Number of `usize` limbs representing `Self`. const NUM_LIMBS: usize; diff --git a/lib/crypto/src/arithmetic/uint.rs b/lib/crypto/src/arithmetic/uint.rs index 29bd73fc3..4205cf330 100644 --- a/lib/crypto/src/arithmetic/uint.rs +++ b/lib/crypto/src/arithmetic/uint.rs @@ -681,6 +681,12 @@ impl BitIteratorBE for Uint { } } +impl BitIteratorBE for &[Limb] { + fn bit_be_iter(&self) -> impl Iterator { + self.iter().rev().flat_map(Limb::bit_be_iter) + } +} + /// Parse a number from a string in a given radix. /// /// This implementation can be slow on big numbers and possibly fail constant diff --git a/lib/crypto/src/curve/helpers.rs b/lib/crypto/src/curve/helpers.rs new file mode 100644 index 000000000..63e985336 --- /dev/null +++ b/lib/crypto/src/curve/helpers.rs @@ -0,0 +1,170 @@ +//! Helper macros for implementing common traits for curve types. + +/// Implements additive operations by deferring to an implementation on &Self. +#[macro_export] +macro_rules! impl_additive_ops_from_ref { + ($type:ident, $params:ident) => { + #[allow(unused_qualifications)] + impl core::ops::Add for $type

{ + type Output = Self; + + #[inline] + fn add(self, other: Self) -> Self { + let mut result = self; + result += &other; + result + } + } + + #[allow(unused_qualifications)] + impl<'a, P: $params> core::ops::Add<&'a mut Self> for $type

{ + type Output = Self; + + #[inline] + fn add(self, other: &'a mut Self) -> Self { + let mut result = self; + result += &*other; + result + } + } + + impl<'b, P: $params> core::ops::Add<$type

> for &'b $type

{ + type Output = $type

; + + #[inline] + fn add(self, mut other: $type

) -> $type

{ + other += self; + other + } + } + + #[allow(unused_qualifications)] + impl<'a, 'b, P: $params> core::ops::Add<&'a $type

> for &'b $type

{ + type Output = $type

; + + #[inline] + fn add(self, other: &'a $type

) -> $type

{ + let mut result = *self; + result += &*other; + result + } + } + + #[allow(unused_qualifications)] + impl<'a, 'b, P: $params> core::ops::Add<&'a mut $type

> + for &'b $type

+ { + type Output = $type

; + + #[inline] + fn add(self, other: &'a mut $type

) -> $type

{ + let mut result = *self; + result += &*other; + result + } + } + + impl<'b, P: $params> core::ops::Sub<$type

> for &'b $type

{ + type Output = $type

; + + #[inline] + fn sub(self, other: $type

) -> $type

{ + let mut result = *self; + result -= &other; + result + } + } + + #[allow(unused_qualifications)] + impl<'a, 'b, P: $params> core::ops::Sub<&'a $type

> for &'b $type

{ + type Output = $type

; + + #[inline] + fn sub(self, other: &'a $type

) -> $type

{ + let mut result = *self; + result -= &*other; + result + } + } + + #[allow(unused_qualifications)] + impl<'a, 'b, P: $params> core::ops::Sub<&'a mut $type

> + for &'b $type

+ { + type Output = $type

; + + #[inline] + fn sub(self, other: &'a mut $type

) -> $type

{ + let mut result = *self; + result -= &*other; + result + } + } + + #[allow(unused_qualifications)] + impl core::ops::Sub for $type

{ + type Output = Self; + + #[inline] + fn sub(self, other: Self) -> Self { + let mut result = self; + result -= &other; + result + } + } + + #[allow(unused_qualifications)] + impl<'a, P: $params> core::ops::Sub<&'a mut Self> for $type

{ + type Output = Self; + + #[inline] + fn sub(self, other: &'a mut Self) -> Self { + let mut result = self; + result -= &*other; + result + } + } + + #[allow(unused_qualifications)] + impl core::iter::Sum for $type

{ + fn sum>(iter: I) -> Self { + iter.fold(Self::zero(), core::ops::Add::add) + } + } + + #[allow(unused_qualifications)] + impl<'a, P: $params> core::iter::Sum<&'a Self> for $type

{ + fn sum>(iter: I) -> Self { + iter.fold(Self::zero(), core::ops::Add::add) + } + } + + #[allow(unused_qualifications)] + impl core::ops::AddAssign for $type

{ + fn add_assign(&mut self, other: Self) { + *self += &other + } + } + + #[allow(unused_qualifications)] + impl core::ops::SubAssign for $type

{ + fn sub_assign(&mut self, other: Self) { + *self -= &other + } + } + + #[allow(unused_qualifications)] + impl<'a, P: $params> core::ops::AddAssign<&'a mut Self> for $type

{ + fn add_assign(&mut self, other: &'a mut Self) { + *self += &*other + } + } + + #[allow(unused_qualifications)] + impl<'a, P: $params> core::ops::SubAssign<&'a mut Self> for $type

{ + fn sub_assign(&mut self, other: &'a mut Self) { + *self -= &*other + } + } + }; +} diff --git a/lib/crypto/src/curve/mod.rs b/lib/crypto/src/curve/mod.rs new file mode 100644 index 000000000..c008d4ee6 --- /dev/null +++ b/lib/crypto/src/curve/mod.rs @@ -0,0 +1,283 @@ +//! This module provides common operations to work with elliptic curves. + +use alloc::vec::Vec; +use core::{ + fmt::{Debug, Display}, + hash::Hash, + ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}, +}; + +use num_traits::Zero; +use zeroize::Zeroize; + +use crate::{ + bits::BitIteratorBE, + field::{group::AdditiveGroup, prime::PrimeField, Field}, +}; + +mod helpers; +pub mod sw; +pub mod te; + +/// Elliptic curves can be represented via different "models" with varying +/// efficiency properties. +/// +/// `CurveConfig` bundles together the types that are common +/// to all models of the given curve, namely the `BaseField` over which the +/// curve is defined, and the `ScalarField` defined by the appropriate +/// prime-order subgroup of the curve. +pub trait CurveConfig: Send + Sync + Sized + 'static { + /// Base field that the curve is defined over. + type BaseField: Field; + /// Finite prime field corresponding to an appropriate prime-order subgroup + /// of the curve group. + type ScalarField: PrimeField; + + /// The cofactor of this curve, represented as a sequence of little-endian + /// limbs. + const COFACTOR: &'static [u64]; + + /// The inverse of the cofactor. + const COFACTOR_INV: Self::ScalarField; + + /// Returns `true` if the cofactor is one. + fn cofactor_is_one() -> bool { + Self::COFACTOR[0] == 1 + && Self::COFACTOR.iter().skip(1).all(Zero::is_zero) + } +} + +/// Represents (elements of) a group of prime order `r`. +pub trait PrimeGroup: AdditiveGroup { + /// The scalar field `F_r`, where `r` is the order of this group. + type ScalarField: PrimeField; + + /// Returns a fixed generator of this group. + #[must_use] + fn generator() -> Self; + + /// Performs scalar multiplication of this element. + #[must_use] + fn mul_bigint(&self, other: impl BitIteratorBE) -> Self; + + /// Computes `other * self`, where `other` is a *big-endian* + /// bit representation of some integer. + #[must_use] + fn mul_bits_be(&self, other: impl Iterator) -> Self { + let mut res = Self::zero(); + for b in other.skip_while(|b| !b) { + // skip leading zeros + res.double_in_place(); + if b { + res += self; + } + } + res + } +} + +/// An opaque representation of an elliptic curve group element that is suitable +/// for efficient group arithmetic. +/// +/// The point is guaranteed to be in the correct prime order subgroup. +pub trait CurveGroup: + PrimeGroup + + Add + + AddAssign + + Sub + + SubAssign + + From + + Into + + core::iter::Sum + + for<'a> core::iter::Sum<&'a Self::Affine> +{ + /// Associated configuration for this curve. + type Config: CurveConfig< + ScalarField = Self::ScalarField, + BaseField = Self::BaseField, + >; + + /// The field over which this curve is defined. + type BaseField: Field; + + /// The affine representation of this element. + type Affine: AffineRepr< + Config = Self::Config, + Group = Self, + ScalarField = Self::ScalarField, + BaseField = Self::BaseField, + > + From + + Into; + + /// Type representing an element of the full elliptic curve group, not just + /// the prime order subgroup. + type FullGroup; + + /// Normalizes a slice of group elements into affine. + #[must_use] + fn normalize_batch(v: &[Self]) -> Vec; + + /// Converts `self` into the affine representation. + fn into_affine(self) -> Self::Affine { + self.into() + } +} + +/// The canonical representation of an elliptic curve group element. +/// This should represent the affine coordinates of the point corresponding +/// to this group element. +/// +/// The point is guaranteed to be in the correct prime order subgroup. +pub trait AffineRepr: + Eq + + 'static + + Sized + + Copy + + Clone + + Default + + Send + + Sync + + Hash + + Debug + + Display + + Zeroize + + Neg + + From<::Group> + + Into<::Group> + + Add + + for<'a> Add<&'a Self, Output = Self::Group> + + Add + + for<'a> Add<&'a Self::Group, Output = Self::Group> + + Sub + + for<'a> Sub<&'a Self, Output = Self::Group> + + Sub + + for<'a> Sub<&'a Self::Group, Output = Self::Group> + + Mul + + for<'a> Mul<&'a Self::ScalarField, Output = Self::Group> +{ + /// Associated configuration for this curve. + type Config: CurveConfig< + ScalarField = Self::ScalarField, + BaseField = Self::BaseField, + >; + + /// Finite prime field corresponding to an appropriate prime-order subgroup + /// of the curve group. + type ScalarField: PrimeField; + + /// Base field that the curve is defined over. + type BaseField: Field; + + /// The projective representation of points on this curve. + type Group: CurveGroup< + Config = Self::Config, + Affine = Self, + ScalarField = Self::ScalarField, + BaseField = Self::BaseField, + > + From + + Into + + MulAssign; // needed due to https://github.com/rust-lang/rust/issues/69640 + + /// Returns the x and y coordinates of this affine point. + fn xy(&self) -> Option<(Self::BaseField, Self::BaseField)>; + + /// Returns the x coordinate of this affine point. + fn x(&self) -> Option { + self.xy().map(|(x, _)| x) + } + + /// Returns the y coordinate of this affine point. + fn y(&self) -> Option { + self.xy().map(|(_, y)| y) + } + + /// Returns the point at infinity. + fn zero() -> Self; + + /// Is `self` the point at infinity? + fn is_zero(&self) -> bool { + self.xy().is_none() + } + + /// Returns a fixed generator of unknown exponent. + #[must_use] + fn generator() -> Self; + + /// Converts self into the projective representation. + fn into_group(self) -> Self::Group { + self.into() + } + + /// Performs scalar multiplication of this element with mixed addition. + #[must_use] + fn mul_bigint(&self, by: impl BitIteratorBE) -> Self::Group; + + /// Performs cofactor clearing. + /// The default method is simply to multiply by the cofactor. + /// For some curve families more efficient methods exist. + #[must_use] + fn clear_cofactor(&self) -> Self; + + /// Multiplies this element by the cofactor and output the + /// resulting projective element. + #[must_use] + fn mul_by_cofactor_to_group(&self) -> Self::Group; + + /// Multiplies this element by the cofactor. + #[must_use] + fn mul_by_cofactor(&self) -> Self { + self.mul_by_cofactor_to_group().into() + } + + /// Multiplies this element by the inverse of the cofactor in + /// `Self::ScalarField`. + #[must_use] + fn mul_by_cofactor_inv(&self) -> Self { + self.mul_bigint(Self::Config::COFACTOR_INV.into_bigint()).into() + } +} + +/// Given a vector of field elements `v_i`, compute the vector `v_i^(-1)` +pub fn batch_inversion(v: &mut [F]) { + batch_inversion_and_mul(v, &F::one()); +} + +/// Given a vector of field elements `v_i`, compute the vector `coeff * +/// v_i^(-1)`. +fn batch_inversion_and_mul(v: &mut [F], coeff: &F) { + // Montgomery’s Trick and Fast Implementation of Masked AES + // Genelle, Prouff and Quisquater + // Section 3.2 + // but with an optimization to multiply every element in the returned vector + // by coeff. + + // First pass: compute [a, ab, abc, ...] + let mut prod = Vec::with_capacity(v.len()); + let mut tmp = F::one(); + for f in v.iter().filter(|f| !f.is_zero()) { + tmp *= f; + prod.push(tmp); + } + + // Invert `tmp`. + tmp = tmp.inverse().unwrap(); // Guaranteed to be nonzero. + + // Multiply product by coeff, so all inverses will be scaled by coeff. + tmp *= coeff; + + // Second pass: iterate backwards to compute inverses + for (f, s) in v + .iter_mut() + // Backwards + .rev() + // Ignore normalized elements + .filter(|f| !f.is_zero()) + // Backwards, skip last element, fill in one for last term. + .zip(prod.into_iter().rev().skip(1).chain(Some(F::one()))) + { + // tmp := tmp * f; f := tmp * s = 1/f + let new_tmp = tmp * *f; + *f = tmp * s; + tmp = new_tmp; + } +} diff --git a/lib/crypto/src/curve/sw/affine.rs b/lib/crypto/src/curve/sw/affine.rs new file mode 100644 index 000000000..146b68118 --- /dev/null +++ b/lib/crypto/src/curve/sw/affine.rs @@ -0,0 +1,270 @@ +//! Affine coordinates for a point on a Short Weierstrass curve +//! ([Affine Space]). +//! +//! [Affine Space]: https://en.wikipedia.org/wiki/Affine_space + +use core::{ + borrow::Borrow, + fmt::{Debug, Display, Formatter}, + ops::{Add, Mul, Neg, Sub}, +}; + +use educe::Educe; +use num_traits::{One, Zero}; +use zeroize::Zeroize; + +use super::{Projective, SWCurveConfig}; +use crate::{ + bits::BitIteratorBE, + curve::AffineRepr, + field::{group::AdditiveGroup, prime::PrimeField, Field}, +}; + +/// Affine coordinates for a point on an elliptic curve in short Weierstrass +/// form, over the base field `P::BaseField`. +#[derive(Educe)] +#[educe(Copy, Clone, PartialEq, Eq, Hash)] +#[must_use] +pub struct Affine { + #[doc(hidden)] + pub x: P::BaseField, + #[doc(hidden)] + pub y: P::BaseField, + #[doc(hidden)] + pub infinity: bool, +} + +impl PartialEq> for Affine

{ + fn eq(&self, other: &Projective

) -> bool { + self.into_group() == *other + } +} + +impl Display for Affine

{ + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + if self.infinity { + write!(f, "infinity") + } else { + write!(f, "({}, {})", self.x, self.y) + } + } +} + +impl Debug for Affine

{ + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + if self.infinity { + write!(f, "infinity") + } else { + write!(f, "({}, {})", self.x, self.y) + } + } +} + +impl Affine

{ + /// Constructs a group element from x and y coordinates. + /// Performs checks to ensure that the point is on the curve and is in the + /// right subgroup. + /// + /// # Panics + /// + /// * If point is not on curve. + /// * If point is not in the prime-order subgroup. + pub fn new(x: P::BaseField, y: P::BaseField) -> Self { + let point = Self { x, y, infinity: false }; + assert!(point.is_on_curve()); + assert!(point.is_in_correct_subgroup_assuming_on_curve()); + point + } + + /// Constructs a group element from x and y coordinates. + /// + /// # Warning + /// + /// Does *not* perform any checks to ensure the point is in the curve or + /// is in the right subgroup. + pub const fn new_unchecked(x: P::BaseField, y: P::BaseField) -> Self { + Self { x, y, infinity: false } + } + + /// Additive identity element of the curve group. + pub const fn identity() -> Self { + Self { x: P::BaseField::ZERO, y: P::BaseField::ZERO, infinity: true } + } + + /// Checks if `self` is a valid point on the curve. + pub fn is_on_curve(&self) -> bool { + if !self.infinity { + // Rust does not optimise away addition with zero + let mut x3b = P::add_b(self.x.square() * self.x); + if !P::COEFF_A.is_zero() { + x3b += P::mul_by_a(self.x); + }; + self.y.square() == x3b + } else { + true + } + } +} + +impl Affine

{ + /// Checks if `self` is in the subgroup having order that equaling that of + /// `P::ScalarField`. + // DISCUSS Maybe these function names are too verbose? + pub fn is_in_correct_subgroup_assuming_on_curve(&self) -> bool { + P::is_in_correct_subgroup_assuming_on_curve(self) + } +} + +impl Zeroize for Affine

{ + // The phantom data does not contain element-specific data + // and thus does not need to be zeroized. + fn zeroize(&mut self) { + self.x.zeroize(); + self.y.zeroize(); + self.infinity.zeroize(); + } +} + +impl AffineRepr for Affine

{ + type BaseField = P::BaseField; + type Config = P; + type Group = Projective

; + type ScalarField = P::ScalarField; + + fn xy(&self) -> Option<(Self::BaseField, Self::BaseField)> { + (!self.infinity).then_some((self.x, self.y)) + } + + #[inline] + fn generator() -> Self { + P::GENERATOR + } + + fn zero() -> Self { + Self { x: P::BaseField::ZERO, y: P::BaseField::ZERO, infinity: true } + } + + fn mul_bigint(&self, by: impl BitIteratorBE) -> Self::Group { + P::mul_affine(self, by) + } + + /// Multiplies this element by the cofactor and output the + /// resulting projective element. + #[must_use] + fn mul_by_cofactor_to_group(&self) -> Self::Group { + P::mul_affine(self, Self::Config::COFACTOR) + } + + /// Performs cofactor clearing. + /// The default method is simply to multiply by the cofactor. + /// Some curves can implement a more efficient algorithm. + fn clear_cofactor(&self) -> Self { + P::clear_cofactor(self) + } +} + +impl Neg for Affine

{ + type Output = Self; + + /// If `self.is_zero()`, returns `self` (`== Self::zero()`). + /// Else, returns `(x, -y)`, where `self = (x, y)`. + #[inline] + fn neg(mut self) -> Self { + self.y.neg_in_place(); + self + } +} + +impl> Add for Affine

{ + type Output = Projective

; + + fn add(self, other: T) -> Projective

{ + let mut copy = self.into_group(); + copy += other.borrow(); + copy + } +} + +impl Add> for Affine

{ + type Output = Projective

; + + fn add(self, other: Projective

) -> Projective

{ + other + self + } +} + +impl<'a, P: SWCurveConfig> Add<&'a Projective

> for Affine

{ + type Output = Projective

; + + fn add(self, other: &'a Projective

) -> Projective

{ + *other + self + } +} + +impl> Sub for Affine

{ + type Output = Projective

; + + fn sub(self, other: T) -> Projective

{ + let mut copy = self.into_group(); + copy -= other.borrow(); + copy + } +} + +impl Sub> for Affine

{ + type Output = Projective

; + + fn sub(self, other: Projective

) -> Projective

{ + self + (-other) + } +} + +impl<'a, P: SWCurveConfig> Sub<&'a Projective

> for Affine

{ + type Output = Projective

; + + fn sub(self, other: &'a Projective

) -> Projective

{ + self + (-*other) + } +} + +impl Default for Affine

{ + #[inline] + fn default() -> Self { + Self::identity() + } +} + +impl> Mul for Affine

{ + type Output = Projective

; + + #[inline] + fn mul(self, other: T) -> Self::Output { + self.mul_bigint(other.borrow().into_bigint()) + } +} + +// The projective point X, Y, Z is represented in the affine +// coordinates as X/Z^2, Y/Z^3. +impl From> for Affine

{ + #[inline] + fn from(p: Projective

) -> Affine

{ + if p.is_zero() { + Affine::identity() + } else if p.z.is_one() { + // If Z is one, the point is already normalized. + Affine::new_unchecked(p.x, p.y) + } else { + // Z is nonzero, so it must have an inverse in a field. + let zinv = p.z.inverse().unwrap(); + let zinv_squared = zinv.square(); + + // X/Z^2 + let x = p.x * zinv_squared; + + // Y/Z^3 + let y = p.y * (zinv_squared * zinv); + + Affine::new_unchecked(x, y) + } + } +} diff --git a/lib/crypto/src/curve/sw/mod.rs b/lib/crypto/src/curve/sw/mod.rs new file mode 100644 index 000000000..43f6f4e43 --- /dev/null +++ b/lib/crypto/src/curve/sw/mod.rs @@ -0,0 +1,139 @@ +//! This module contains definitions for the [Short Weierstrass model] of the +//! curve. +//! +//! [Short Weierstrass model]: https://www.hyperelliptic.org/EFD/g1p/auto-shortw.html + +use num_traits::Zero; + +mod affine; +pub use affine::*; + +mod projective; +pub use projective::*; + +use crate::{ + bits::BitIteratorBE, + curve::AffineRepr, + field::{group::AdditiveGroup, prime::PrimeField}, +}; + +/// Constants and convenience functions that collectively define the +/// [Short Weierstrass model] of the curve. +/// +/// In this model, the curve equation is `y² = x³ + a * x + b`, for constants +/// `a` and `b`. +/// +/// [Short Weierstrass model]: https://www.hyperelliptic.org/EFD/g1p/auto-shortw.html +pub trait SWCurveConfig: super::CurveConfig { + /// Coefficient `a` of the curve equation. + const COEFF_A: Self::BaseField; + /// Coefficient `b` of the curve equation. + const COEFF_B: Self::BaseField; + /// Generator of the prime-order subgroup. + const GENERATOR: Affine; + + /// Helper method for computing `elem * Self::COEFF_A`. + /// + /// The default implementation should be overridden only if + /// the product can be computed faster than standard field multiplication + /// (eg: via doubling if `COEFF_A == 2`, or if `COEFF_A.is_zero()`). + #[inline(always)] + fn mul_by_a(elem: Self::BaseField) -> Self::BaseField { + if Self::COEFF_A.is_zero() { + Self::BaseField::ZERO + } else { + elem * Self::COEFF_A + } + } + + /// Helper method for computing `elem + Self::COEFF_B`. + /// + /// The default implementation should be overridden only if + /// the sum can be computed faster than standard field addition (eg: via + /// doubling). + #[inline(always)] + fn add_b(elem: Self::BaseField) -> Self::BaseField { + if Self::COEFF_B.is_zero() { + elem + } else { + elem + Self::COEFF_B + } + } + + /// Check if the provided curve point is in the prime-order subgroup. + /// + /// The default implementation multiplies `item` by the order `r` of the + /// prime-order subgroup, and checks if the result is zero. If the + /// curve's cofactor is one, this check automatically returns true. + /// Implementors can choose to override this default impl + /// if the given curve has faster methods + /// for performing this check (for example, via leveraging curve + /// isomorphisms). + fn is_in_correct_subgroup_assuming_on_curve(item: &Affine) -> bool { + if Self::cofactor_is_one() { + true + } else { + Self::mul_affine(item, Self::ScalarField::characteristic()) + .is_zero() + } + } + + /// Performs cofactor clearing. + /// The default method is simply to multiply by the cofactor. + /// Some curves can implement a more efficient algorithm. + fn clear_cofactor(item: &Affine) -> Affine { + item.mul_by_cofactor() + } + + /// Default implementation of group multiplication for projective + /// coordinates + fn mul_projective( + base: &Projective, + scalar: impl BitIteratorBE, + ) -> Projective { + sw_double_and_add_projective(base, scalar) + } + + /// Default implementation of group multiplication for affine + /// coordinates. + fn mul_affine( + base: &Affine, + scalar: impl BitIteratorBE, + ) -> Projective { + sw_double_and_add_affine(base, scalar) + } +} + +/// Standard double-and-add method for multiplication by a scalar. +#[inline(always)] +pub fn sw_double_and_add_affine( + base: &Affine

, + scalar: impl BitIteratorBE, +) -> Projective

{ + let mut res = Projective::zero(); + for b in scalar.bit_be_trimmed_iter() { + res.double_in_place(); + if b { + res += base; + } + } + + res +} + +/// Standard double-and-add method for multiplication by a scalar. +#[inline(always)] +pub fn sw_double_and_add_projective( + base: &Projective

, + scalar: impl BitIteratorBE, +) -> Projective

{ + let mut res = Projective::zero(); + for b in scalar.bit_be_trimmed_iter() { + res.double_in_place(); + if b { + res += base; + } + } + + res +} diff --git a/lib/crypto/src/curve/sw/projective.rs b/lib/crypto/src/curve/sw/projective.rs new file mode 100644 index 000000000..7cefd5375 --- /dev/null +++ b/lib/crypto/src/curve/sw/projective.rs @@ -0,0 +1,597 @@ +//! Projective coordinates for a point on a Short Weierstrass curve +//! ([Homogeneous coordinates]). +//! +//! [Homogeneous coordinates]: https://en.wikipedia.org/wiki/Homogeneous_coordinates + +use alloc::vec::Vec; +use core::{ + borrow::Borrow, + fmt::{Debug, Display, Formatter}, + hash::{Hash, Hasher}, + ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}, +}; + +use educe::Educe; +use num_traits::{One, Zero}; +use zeroize::Zeroize; + +use super::{Affine, SWCurveConfig}; +use crate::{ + bits::BitIteratorBE, + curve::{batch_inversion, AffineRepr, CurveGroup, PrimeGroup}, + field::{group::AdditiveGroup, prime::PrimeField, Field}, + impl_additive_ops_from_ref, +}; + +/// Jacobian coordinates for a point on an elliptic curve in short Weierstrass +/// form, over the base field `P::BaseField`. This struct implements arithmetic +/// via the Jacobian formulae +#[derive(Educe)] +#[educe(Copy, Clone)] +#[must_use] +pub struct Projective { + /// `X / Z` projection of the affine `X` + pub x: P::BaseField, + /// `Y / Z` projection of the affine `Y` + pub y: P::BaseField, + /// Projective multiplicative inverse. Will be `0` only at infinity. + pub z: P::BaseField, +} + +impl Display for Projective

{ + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + write!(f, "{}", Affine::from(*self)) + } +} + +impl Debug for Projective

{ + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + if self.is_zero() { + write!(f, "infinity") + } else { + write!(f, "({}, {}, {})", self.x, self.y, self.z) + } + } +} + +impl Eq for Projective

{} +impl PartialEq for Projective

{ + fn eq(&self, other: &Self) -> bool { + if self.is_zero() { + return other.is_zero(); + } + + if other.is_zero() { + return false; + } + + // The points (X, Y, Z) and (X', Y', Z') + // are equal when (X * Z^2) = (X' * Z'^2) + // and (Y * Z^3) = (Y' * Z'^3). + let z1z1 = self.z.square(); + let z2z2 = other.z.square(); + + if self.x * z2z2 == other.x * z1z1 { + self.y * (z2z2 * other.z) == other.y * (z1z1 * self.z) + } else { + false + } + } +} + +impl PartialEq> for Projective

{ + fn eq(&self, other: &Affine

) -> bool { + *self == other.into_group() + } +} + +impl Hash for Projective

{ + fn hash(&self, state: &mut H) { + self.into_affine().hash(state); + } +} + +impl Default for Projective

{ + #[inline] + fn default() -> Self { + Self::zero() + } +} + +impl Projective

{ + /// Constructs a new group element without checking whether the coordinates + /// specify a point in the subgroup. + pub const fn new_unchecked( + x: P::BaseField, + y: P::BaseField, + z: P::BaseField, + ) -> Self { + Self { x, y, z } + } + + /// Constructs a new group element in a way while enforcing that points are + /// in the prime-order subgroup. + /// + /// # Panics + /// + /// * If point is not on curve. + /// * If point is not in the prime-order subgroup. + pub fn new(x: P::BaseField, y: P::BaseField, z: P::BaseField) -> Self { + let p = Self::new_unchecked(x, y, z).into_affine(); + assert!(p.is_on_curve()); + assert!(p.is_in_correct_subgroup_assuming_on_curve()); + p.into() + } +} + +impl Zeroize for Projective

{ + fn zeroize(&mut self) { + self.x.zeroize(); + self.y.zeroize(); + self.z.zeroize(); + } +} + +impl Zero for Projective

{ + /// Returns the point at infinity, which always has Z = 0. + #[inline] + fn zero() -> Self { + Self::new_unchecked( + P::BaseField::one(), + P::BaseField::one(), + P::BaseField::zero(), + ) + } + + /// Checks whether `self.z.is_zero()`. + #[inline] + fn is_zero(&self) -> bool { + self.z == P::BaseField::ZERO + } +} + +impl AdditiveGroup for Projective

{ + type Scalar = P::ScalarField; + + const ZERO: Self = Self::new_unchecked( + P::BaseField::ONE, + P::BaseField::ONE, + P::BaseField::ZERO, + ); + + /// Sets `self = 2 * self`. Note that Jacobian formulae are incomplete, and + /// so doubling cannot be computed as `self + self`. Instead, this + /// implementation uses the following specialized doubling formulae: + /// + /// * [`P::A` is zero](http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l) + /// * [`P::A` is not zero](https://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#doubling-dbl-2007-bl) + fn double_in_place(&mut self) -> &mut Self { + if self.is_zero() { + return self; + } + + if P::COEFF_A == P::BaseField::ZERO { + // A = X1^2 + let mut a = self.x; + a.square_in_place(); + + // B = Y1^2 + let mut b = self.y; + b.square_in_place(); + + // C = B^2 + let mut c = b; + c.square_in_place(); + + // D = 2*((X1+B)^2-A-C) + // = 2 * (X1 + Y1^2)^2 - A - C + // = 2 * 2 * X1 * Y1^2 + let d = if [1, 2].contains(&P::BaseField::extension_degree()) { + let mut d = self.x; + d *= &b; + d.double_in_place().double_in_place(); + d + } else { + let mut d = self.x; + d += &b; + d.square_in_place(); + d -= a; + d -= c; + d.double_in_place(); + d + }; + + // E = 3*A + let e = a + &*a.double_in_place(); + + // Z3 = 2*Y1*Z1 + self.z *= &self.y; + self.z.double_in_place(); + + // F = E^2 + // X3 = F-2*D + self.x = e; + self.x.square_in_place(); + self.x -= &d.double(); + + // Y3 = E*(D-X3)-8*C + self.y = d; + self.y -= &self.x; + self.y *= &e; + self.y -= c.double_in_place().double_in_place().double_in_place(); + self + } else { + // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l + // XX = X1^2 + let xx = self.x.square(); + + // YY = Y1^2 + let yy = self.y.square(); + + // YYYY = YY^2 + let mut yyyy = yy; + yyyy.square_in_place(); + + // ZZ = Z1^2 + let mut zz = self.z; + zz.square_in_place(); + + // S = 2*((X1+YY)^2-XX-YYYY) + let s = ((self.x + yy).square() - xx - yyyy).double(); + + // M = 3*XX+a*ZZ^2 + let mut m = xx; + m.double_in_place(); + m += &xx; + m += &P::mul_by_a(zz.square()); + + // T = M^2-2*S + // X3 = T + self.x = m; + self.x.square_in_place(); + self.x -= s.double(); + + // Z3 = (Y1+Z1)^2-YY-ZZ + // Can be calculated as Z3 = 2*Y1*Z1, and this is faster. + self.z *= self.y; + self.z.double_in_place(); + + // Y3 = M*(S-X3)-8*YYYY + self.y = s; + self.y -= &self.x; + self.y *= &m; + self.y -= + yyyy.double_in_place().double_in_place().double_in_place(); + + self + } + } +} + +impl PrimeGroup for Projective

{ + type ScalarField = P::ScalarField; + + #[inline] + fn generator() -> Self { + Affine::generator().into() + } + + #[inline] + fn mul_bigint(&self, other: impl BitIteratorBE) -> Self { + P::mul_projective(self, other) + } +} + +impl CurveGroup for Projective

{ + type Affine = Affine

; + type BaseField = P::BaseField; + type Config = P; + type FullGroup = Affine

; + + /// Normalizes a slice of projective elements so that + /// conversion to affine is cheap. + /// + /// In more detail, this method converts a curve point in Jacobian + /// coordinates (x, y, z) into an equivalent representation (x/z^2, + /// y/z^3, 1). + /// + /// For `N = v.len()`, this costs 1 inversion + 6N field multiplications + N + /// field squarings. + /// + /// (Where batch inversion comprises 3N field multiplications + 1 inversion + /// of these operations) + #[inline] + fn normalize_batch(v: &[Self]) -> Vec { + let mut z_s = v.iter().map(|g| g.z).collect::>(); + + batch_inversion(&mut z_s); + + // Perform affine transformations + v.iter() + .zip(z_s) + .map(|(g, z)| { + if g.is_zero() { + Affine::identity() + } else { + let z2 = z.square(); + let x = g.x * z2; + let y = g.y * z2 * z; + Affine::new_unchecked(x, y) + } + }) + .collect() + } +} + +impl Neg for Projective

{ + type Output = Self; + + #[inline] + fn neg(mut self) -> Self { + self.y = -self.y; + self + } +} + +impl>> AddAssign for Projective

{ + /// Using + fn add_assign(&mut self, other: T) { + let other = other.borrow(); + if let Some((other_x, other_y)) = other.xy() { + if self.is_zero() { + self.x = other_x; + self.y = other_y; + self.z = P::BaseField::one(); + return; + } + + // Z1Z1 = Z1^2 + let mut z1z1 = self.z; + z1z1.square_in_place(); + + // U2 = X2*Z1Z1 + let mut u2 = other_x; + u2 *= &z1z1; + + // S2 = Y2*Z1*Z1Z1 + let mut s2 = self.z; + s2 *= &other_y; + s2 *= &z1z1; + + if self.x == u2 { + if self.y == s2 { + // The two points are equal, so we double. + self.double_in_place(); + } else { + // a + (-a) = 0 + *self = Self::zero(); + } + } else { + // H = U2-X1 + let mut h = u2; + h -= &self.x; + + // HH = H^2 + let mut hh = h; + hh.square_in_place(); + + // I = 4*HH + let mut i = hh; + i.double_in_place().double_in_place(); + + // J = -H*I + let mut j = h; + j.neg_in_place(); + j *= &i; + + // r = 2*(S2-Y1) + let mut r = s2; + r -= &self.y; + r.double_in_place(); + + // V = X1*I + let mut v = self.x; + v *= &i; + + // X3 = r^2 + J - 2*V + self.x = r.square(); + self.x += &j; + self.x -= &v.double(); + + // Y3 = r*(V-X3) + 2*Y1*J + v -= &self.x; + self.y.double_in_place(); + self.y = P::BaseField::sum_of_products(&[r, self.y], &[v, j]); + + // Z3 = 2 * Z1 * H; + // Can alternatively be computed as (Z1+H)^2-Z1Z1-HH, but the + // latter is slower. + self.z *= &h; + self.z.double_in_place(); + } + } + } +} + +impl>> Add for Projective

{ + type Output = Self; + + fn add(mut self, other: T) -> Self { + let other = other.borrow(); + self += other; + self + } +} + +impl>> SubAssign for Projective

{ + fn sub_assign(&mut self, other: T) { + *self += -(*other.borrow()); + } +} + +impl>> Sub for Projective

{ + type Output = Self; + + fn sub(mut self, other: T) -> Self { + self -= other.borrow(); + self + } +} + +impl_additive_ops_from_ref!(Projective, SWCurveConfig); + +impl<'a, P: SWCurveConfig> Add<&'a Self> for Projective

{ + type Output = Self; + + #[inline] + fn add(mut self, other: &'a Self) -> Self { + self += other; + self + } +} + +impl<'a, P: SWCurveConfig> AddAssign<&'a Self> for Projective

{ + fn add_assign(&mut self, other: &'a Self) { + if self.is_zero() { + *self = *other; + return; + } + + if other.is_zero() { + return; + } + + // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl + // Works for all curves. + + // Z1Z1 = Z1^2 + let z1z1 = self.z.square(); + + // Z2Z2 = Z2^2 + let z2z2 = other.z.square(); + + // U1 = X1*Z2Z2 + let mut u1 = self.x; + u1 *= &z2z2; + + // U2 = X2*Z1Z1 + let mut u2 = other.x; + u2 *= &z1z1; + + // S1 = Y1*Z2*Z2Z2 + let mut s1 = self.y; + s1 *= &other.z; + s1 *= &z2z2; + + // S2 = Y2*Z1*Z1Z1 + let mut s2 = other.y; + s2 *= &self.z; + s2 *= &z1z1; + + if u1 == u2 { + if s1 == s2 { + // The two points are equal, so we double. + self.double_in_place(); + } else { + // a + (-a) = 0 + *self = Self::zero(); + } + } else { + // H = U2-U1 + let mut h = u2; + h -= &u1; + + // I = (2*H)^2 + let mut i = h; + i.double_in_place().square_in_place(); + + // J = -H*I + let mut j = h; + j.neg_in_place(); + j *= &i; + + // r = 2*(S2-S1) + let mut r = s2; + r -= &s1; + r.double_in_place(); + + // V = U1*I + let mut v = u1; + v *= &i; + + // X3 = r^2 + J - 2*V + self.x = r; + self.x.square_in_place(); + self.x += &j; + self.x -= &(v.double()); + + // Y3 = r*(V - X3) + 2*S1*J + v -= &self.x; + self.y = s1; + self.y.double_in_place(); + self.y = P::BaseField::sum_of_products(&[r, self.y], &[v, j]); + + // Z3 = ((Z1+Z2)^2 - Z1Z1 - Z2Z2)*H + // This is equal to Z3 = 2 * Z1 * Z2 * H, and computing it this way + // is faster. + self.z *= other.z; + self.z.double_in_place(); + self.z *= &h; + } + } +} + +impl<'a, P: SWCurveConfig> Sub<&'a Self> for Projective

{ + type Output = Self; + + #[inline] + fn sub(mut self, other: &'a Self) -> Self { + self -= other; + self + } +} + +impl<'a, P: SWCurveConfig> SubAssign<&'a Self> for Projective

{ + fn sub_assign(&mut self, other: &'a Self) { + *self += &(-(*other)); + } +} + +impl> MulAssign + for Projective

+{ + fn mul_assign(&mut self, other: T) { + *self = self.mul_bigint(other.borrow().into_bigint()); + } +} + +impl> Mul for Projective

{ + type Output = Self; + + #[inline] + fn mul(mut self, other: T) -> Self { + self *= other; + self + } +} + +// The affine point X, Y is represented in the Jacobian +// coordinates with Z = 1. +impl From> for Projective

{ + #[inline] + fn from(p: Affine

) -> Projective

{ + p.xy().map_or(Projective::zero(), |(x, y)| Self { + x, + y, + z: P::BaseField::one(), + }) + } +} + +impl>> core::iter::Sum + for Projective

+{ + fn sum>(iter: I) -> Self { + iter.fold(Projective::zero(), |sum, x| sum + x.borrow()) + } +} diff --git a/lib/crypto/src/curve/te/affine.rs b/lib/crypto/src/curve/te/affine.rs new file mode 100644 index 000000000..93945222c --- /dev/null +++ b/lib/crypto/src/curve/te/affine.rs @@ -0,0 +1,249 @@ +//! Affine coordinates for a point on a Twisted Edwards curve ([Affine Space]). +//! +//! [Affine Space]: https://en.wikipedia.org/wiki/Affine_space + +use core::{ + borrow::Borrow, + fmt::{Debug, Display, Formatter}, + ops::{Add, Mul, Neg, Sub}, +}; + +use educe::Educe; +use num_traits::{One, Zero}; +use zeroize::Zeroize; + +use super::{Projective, TECurveConfig}; +use crate::{ + bits::BitIteratorBE, + curve::AffineRepr, + field::{group::AdditiveGroup, prime::PrimeField, Field}, +}; + +/// Affine coordinates for a point on a twisted Edwards curve, over the +/// base field `P::BaseField`. +#[derive(Educe)] +#[educe(Copy, Clone, PartialEq, Eq, Hash)] +#[must_use] +pub struct Affine { + /// X coordinate of the point represented as a field element + pub x: P::BaseField, + /// Y coordinate of the point represented as a field element + pub y: P::BaseField, +} + +impl Display for Affine

{ + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + if self.is_zero() { + write!(f, "infinity") + } else { + write!(f, "({}, {})", self.x, self.y) + } + } +} + +impl Debug for Affine

{ + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + if self.is_zero() { + write!(f, "infinity") + } else { + write!(f, "({}, {})", self.x, self.y) + } + } +} + +impl PartialEq> for Affine

{ + fn eq(&self, other: &Projective

) -> bool { + self.into_group() == *other + } +} + +impl Affine

{ + /// Construct a new group element without checking whether the coordinates + /// specify a point in the subgroup. + pub const fn new_unchecked(x: P::BaseField, y: P::BaseField) -> Self { + Self { x, y } + } + + /// Construct a new group element in a way while enforcing that points are + /// in the prime-order subgroup. + /// + /// # Panics + /// + /// * If point is not on curve. + /// * If point is not in the prime-order subgroup. + pub fn new(x: P::BaseField, y: P::BaseField) -> Self { + let p = Self::new_unchecked(x, y); + assert!(p.is_on_curve()); + assert!(p.is_in_correct_subgroup_assuming_on_curve()); + p + } + + /// Construct the identity of the group + pub const fn zero() -> Self { + Self::new_unchecked(P::BaseField::ZERO, P::BaseField::ONE) + } + + /// Is this point the identity? + pub fn is_zero(&self) -> bool { + self.x.is_zero() && self.y.is_one() + } + + /// Checks that the current point is on the elliptic curve. + pub fn is_on_curve(&self) -> bool { + let x2 = self.x.square(); + let y2 = self.y.square(); + + let lhs = y2 + P::mul_by_a(x2); + let rhs = P::BaseField::one() + (P::COEFF_D * (x2 * y2)); + + lhs == rhs + } +} + +impl Affine

{ + /// Checks if `self` is in the subgroup having order equaling that of + /// `P::ScalarField` given it is on the curve. + pub fn is_in_correct_subgroup_assuming_on_curve(&self) -> bool { + P::is_in_correct_subgroup_assuming_on_curve(self) + } +} + +impl AffineRepr for Affine

{ + type BaseField = P::BaseField; + type Config = P; + type Group = Projective

; + type ScalarField = P::ScalarField; + + fn xy(&self) -> Option<(Self::BaseField, Self::BaseField)> { + (!self.is_zero()).then_some((self.x, self.y)) + } + + fn generator() -> Self { + P::GENERATOR + } + + fn zero() -> Self { + Self::new_unchecked(P::BaseField::ZERO, P::BaseField::ONE) + } + + fn mul_bigint(&self, by: impl BitIteratorBE) -> Self::Group { + P::mul_affine(self, by) + } + + /// Multiplies this element by the cofactor and output the + /// resulting projective element. + fn mul_by_cofactor_to_group(&self) -> Self::Group { + P::mul_affine(self, Self::Config::COFACTOR) + } + + /// Performs cofactor clearing. + /// The default method is simply to multiply by the cofactor. + /// Some curves can implement a more efficient algorithm. + fn clear_cofactor(&self) -> Self { + P::clear_cofactor(self) + } +} + +impl Zeroize for Affine

{ + // The phantom data does not contain element-specific data + // and thus does not need to be zeroized. + fn zeroize(&mut self) { + self.x.zeroize(); + self.y.zeroize(); + } +} + +impl Neg for Affine

{ + type Output = Self; + + fn neg(self) -> Self { + Self::new_unchecked(-self.x, self.y) + } +} + +impl> Add for Affine

{ + type Output = Projective

; + + fn add(self, other: T) -> Self::Output { + let mut copy = self.into_group(); + copy += other.borrow(); + copy + } +} + +impl Add> for Affine

{ + type Output = Projective

; + + fn add(self, other: Projective

) -> Projective

{ + other + self + } +} + +impl<'a, P: TECurveConfig> Add<&'a Projective

> for Affine

{ + type Output = Projective

; + + fn add(self, other: &'a Projective

) -> Projective

{ + *other + self + } +} + +impl> Sub for Affine

{ + type Output = Projective

; + + fn sub(self, other: T) -> Self::Output { + let mut copy = self.into_group(); + copy -= other.borrow(); + copy + } +} + +impl Sub> for Affine

{ + type Output = Projective

; + + fn sub(self, other: Projective

) -> Projective

{ + self + (-other) + } +} + +impl<'a, P: TECurveConfig> Sub<&'a Projective

> for Affine

{ + type Output = Projective

; + + fn sub(self, other: &'a Projective

) -> Projective

{ + self + (-*other) + } +} + +impl Default for Affine

{ + #[inline] + fn default() -> Self { + Self::zero() + } +} + +impl> Mul for Affine

{ + type Output = Projective

; + + #[inline] + fn mul(self, other: T) -> Self::Output { + self.mul_bigint(other.borrow().into_bigint()) + } +} + +// The projective point X, Y, T, Z is represented in the affine +// coordinates as X/Z, Y/Z. +impl From> for Affine

{ + fn from(p: Projective

) -> Affine

{ + if p.is_zero() { + Affine::zero() + } else if p.z.is_one() { + // If Z is one, the point is already normalized. + Affine::new_unchecked(p.x, p.y) + } else { + // Z is nonzero, so it must have inverse in a field. + let z_inv = p.z.inverse().unwrap(); + let x = p.x * z_inv; + let y = p.y * z_inv; + Affine::new_unchecked(x, y) + } + } +} diff --git a/lib/crypto/src/curve/te/mod.rs b/lib/crypto/src/curve/te/mod.rs new file mode 100644 index 000000000..3cf35b38e --- /dev/null +++ b/lib/crypto/src/curve/te/mod.rs @@ -0,0 +1,112 @@ +//! This module contains definitions for the [Twisted Edwards model] of the +//! curve. +//! +//! [Twisted Edwards model]: https://www.hyperelliptic.org/EFD/g1p/auto-twisted.html + +use num_traits::Zero; + +mod affine; +pub use affine::*; + +mod projective; +pub use projective::*; + +use crate::{ + bits::BitIteratorBE, + curve::AffineRepr, + field::{group::AdditiveGroup, prime::PrimeField}, +}; + +/// Constants and convenience functions +/// that define the [Twisted Edwards model] of the curve. +/// +/// In this model, the curve equation is `a * x² + y² = 1 + d * x² * y²`, for +/// constants `a` and `d`. +/// +/// [Twisted Edwards model]: https://www.hyperelliptic.org/EFD/g1p/auto-twisted.html +pub trait TECurveConfig: super::CurveConfig { + /// Coefficient `a` of the curve equation. + const COEFF_A: Self::BaseField; + /// Coefficient `d` of the curve equation. + const COEFF_D: Self::BaseField; + /// Generator of the prime-order subgroup. + const GENERATOR: Affine; + + /// Model parameters for the Montgomery curve that is birationally + /// equivalent to this curve. + type MontCurveConfig: MontCurveConfig; + + /// Helper method for computing `elem * Self::COEFF_A`. + /// + /// The default implementation should be overridden only if + /// the product can be computed faster than standard field multiplication + /// (eg: via doubling if `COEFF_A == 2`, or if `COEFF_A.is_zero()`). + #[inline(always)] + fn mul_by_a(elem: Self::BaseField) -> Self::BaseField { + elem * Self::COEFF_A + } + + /// Checks that the current point is in the prime order subgroup given + /// the point on the curve. + fn is_in_correct_subgroup_assuming_on_curve(item: &Affine) -> bool { + Self::mul_affine(item, Self::ScalarField::characteristic()).is_zero() + } + + /// Performs cofactor clearing. + /// The default method is simply to multiply by the cofactor. + /// For some curve families though, it is sufficient to multiply + /// by a smaller scalar. + fn clear_cofactor(item: &Affine) -> Affine { + item.mul_by_cofactor() + } + + /// Default implementation of group multiplication for projective + /// coordinates + fn mul_projective( + base: &Projective, + scalar: impl BitIteratorBE, + ) -> Projective { + let mut res = Projective::zero(); + for b in scalar.bit_be_trimmed_iter() { + res.double_in_place(); + if b { + res += base; + } + } + + res + } + + /// Default implementation of group multiplication for affine + /// coordinates + fn mul_affine( + base: &Affine, + scalar: impl BitIteratorBE, + ) -> Projective { + let mut res = Projective::zero(); + for b in scalar.bit_be_trimmed_iter() { + res.double_in_place(); + if b { + res += base; + } + } + + res + } +} + +/// Constants and convenience functions that collectively define the [Montgomery model](https://www.hyperelliptic.org/EFD/g1p/auto-montgom.html) +/// of the curve. +/// +/// In this model, the curve equation is `b * y² = x³ + a * x² + x`, for +/// constants `a` and `b`. +pub trait MontCurveConfig: super::CurveConfig { + /// Coefficient `a` of the curve equation. + const COEFF_A: Self::BaseField; + /// Coefficient `b` of the curve equation. + const COEFF_B: Self::BaseField; + + /// Model parameters for the Twisted Edwards curve that is birationally + /// equivalent to this curve. + type TECurveConfig: TECurveConfig; +} diff --git a/lib/crypto/src/curve/te/projective.rs b/lib/crypto/src/curve/te/projective.rs new file mode 100644 index 000000000..592ec2ad7 --- /dev/null +++ b/lib/crypto/src/curve/te/projective.rs @@ -0,0 +1,428 @@ +//! Projective coordinates for a point on a Twisted Edwards curve +//! ([Homogeneous coordinates]). +//! +//! [Homogeneous coordinates]: https://en.wikipedia.org/wiki/Homogeneous_coordinates + +use alloc::vec::Vec; +use core::{ + borrow::Borrow, + fmt::{Display, Formatter}, + hash::{Hash, Hasher}, + ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}, +}; + +use educe::Educe; +use num_traits::{One, Zero}; +use zeroize::Zeroize; + +use super::{Affine, MontCurveConfig, TECurveConfig}; +use crate::{ + bits::BitIteratorBE, + curve::{batch_inversion, AffineRepr, CurveGroup, PrimeGroup}, + field::{group::AdditiveGroup, prime::PrimeField, Field}, + impl_additive_ops_from_ref, +}; + +/// `Projective` implements Extended Twisted Edwards Coordinates +/// as described in [\[HKCD08\]](https://eprint.iacr.org/2008/522.pdf). +/// +/// This implementation uses the unified addition formulae from that paper (see +/// Section 3.1). +#[derive(Educe)] +#[educe(Copy, Clone, Eq(bound(P: TECurveConfig)), Debug)] +#[must_use] +pub struct Projective { + pub x: P::BaseField, + pub y: P::BaseField, + pub t: P::BaseField, + pub z: P::BaseField, +} + +impl PartialEq> for Projective

{ + fn eq(&self, other: &Affine

) -> bool { + *self == other.into_group() + } +} + +impl Display for Projective

{ + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + write!(f, "{}", Affine::from(*self)) + } +} + +impl PartialEq for Projective

{ + fn eq(&self, other: &Self) -> bool { + if self.is_zero() { + return other.is_zero(); + } + + if other.is_zero() { + return false; + } + + // x1/z1 == x2/z2 <==> x1 * z2 == x2 * z1 + (self.x * other.z) == (other.x * self.z) + && (self.y * other.z) == (other.y * self.z) + } +} + +impl Hash for Projective

{ + fn hash(&self, state: &mut H) { + self.into_affine().hash(state); + } +} + +impl Default for Projective

{ + #[inline] + fn default() -> Self { + Self::zero() + } +} + +impl Projective

{ + /// Construct a new group element without checking whether the coordinates + /// specify a point in the subgroup. + pub const fn new_unchecked( + x: P::BaseField, + y: P::BaseField, + t: P::BaseField, + z: P::BaseField, + ) -> Self { + Self { x, y, t, z } + } + + /// Construct a new group element in a way while enforcing that points are + /// in the prime-order subgroup. + /// + /// # Panics + /// + /// * If point is not on curve. + /// * If point is not in the prime-order subgroup. + pub fn new( + x: P::BaseField, + y: P::BaseField, + t: P::BaseField, + z: P::BaseField, + ) -> Self { + let p = Self::new_unchecked(x, y, t, z).into_affine(); + assert!(p.is_on_curve()); + assert!(p.is_in_correct_subgroup_assuming_on_curve()); + p.into() + } +} +impl Zeroize for Projective

{ + // The phantom data does not contain element-specific data + // and thus does not need to be zeroized. + fn zeroize(&mut self) { + self.x.zeroize(); + self.y.zeroize(); + self.t.zeroize(); + self.z.zeroize(); + } +} + +impl Zero for Projective

{ + fn zero() -> Self { + Self::new_unchecked( + P::BaseField::zero(), + P::BaseField::one(), + P::BaseField::zero(), + P::BaseField::one(), + ) + } + + fn is_zero(&self) -> bool { + self.x.is_zero() + && self.y == self.z + && !self.y.is_zero() + && self.t.is_zero() + } +} + +impl AdditiveGroup for Projective

{ + type Scalar = P::ScalarField; + + const ZERO: Self = Self::new_unchecked( + P::BaseField::ZERO, + P::BaseField::ONE, + P::BaseField::ZERO, + P::BaseField::ONE, + ); + + fn double_in_place(&mut self) -> &mut Self { + // See "Twisted Edwards Curves Revisited" + // Huseyin Hisil, Kenneth Koon-Ho Wong, Gary Carter, and Ed Dawson + // 3.3 Doubling in E^e + // Source: https://www.hyperelliptic.org/EFD/g1p/data/twisted/extended/doubling/dbl-2008-hwcd + + // A = X1^2 + let a = self.x.square(); + // B = Y1^2 + let b = self.y.square(); + // C = 2 * Z1^2 + let c = self.z.square().double(); + // D = a * A + let d = P::mul_by_a(a); + // E = (X1 + Y1)^2 - A - B + let e = (self.x + self.y).square() - a - b; + // G = D + B + let g = d + b; + // F = G - C + let f = g - c; + // H = D - B + let h = d - b; + // X3 = E * F + self.x = e * f; + // Y3 = G * H + self.y = g * h; + // T3 = E * H + self.t = e * h; + // Z3 = F * G + self.z = f * g; + + self + } +} + +impl PrimeGroup for Projective

{ + type ScalarField = P::ScalarField; + + fn generator() -> Self { + Affine::generator().into() + } + + #[inline] + fn mul_bigint(&self, other: impl BitIteratorBE) -> Self { + P::mul_projective(self, other) + } +} + +impl CurveGroup for Projective

{ + type Affine = Affine

; + type BaseField = P::BaseField; + type Config = P; + type FullGroup = Affine

; + + fn normalize_batch(v: &[Self]) -> Vec { + // A projective curve element (x, y, t, z) is normalized + // to its affine representation, by the conversion + // (x, y, t, z) -> (x/z, y/z, t/z, 1) + // Batch normalizing N twisted edwards curve elements costs: + // 1 inversion + 6N field multiplications + // (batch inversion requires 3N multiplications + 1 inversion) + let mut z_s = v.iter().map(|g| g.z).collect::>(); + + batch_inversion(&mut z_s); + + // Perform affine transformations + v.iter() + .zip(z_s) + .map(|(g, z)| { + if g.is_zero() { + Affine::zero() + } else { + let x = g.x * z; + let y = g.y * z; + Affine::new_unchecked(x, y) + } + }) + .collect() + } +} + +impl Neg for Projective

{ + type Output = Self; + + fn neg(mut self) -> Self { + self.x = -self.x; + self.t = -self.t; + self + } +} + +impl>> AddAssign for Projective

{ + fn add_assign(&mut self, other: T) { + let other = other.borrow(); + // See "Twisted Edwards Curves Revisited" + // Huseyin Hisil, Kenneth Koon-Ho Wong, Gary Carter, and Ed Dawson + // 3.1 Unified Addition in E^e + // Source: https://www.hyperelliptic.org/EFD/g1p/data/twisted/extended/addition/madd-2008-hwcd + + // A = X1*X2 + let a = self.x * other.x; + // B = Y1*Y2 + let b = self.y * other.y; + // C = T1*d*T2 + let c = P::COEFF_D * self.t * other.x * other.y; + + // D = Z1 + let d = self.z; + // E = (X1+Y1)*(X2+Y2)-A-B + let e = (self.x + self.y) * (other.x + other.y) - a - b; + // F = D-C + let f = d - c; + // G = D+C + let g = d + c; + // H = B-a*A + let h = b - P::mul_by_a(a); + // X3 = E*F + self.x = e * f; + // Y3 = G*H + self.y = g * h; + // T3 = E*H + self.t = e * h; + // Z3 = F*G + self.z = f * g; + } +} + +impl>> Add for Projective

{ + type Output = Self; + + fn add(mut self, other: T) -> Self { + let other = other.borrow(); + self += other; + self + } +} + +impl>> SubAssign for Projective

{ + fn sub_assign(&mut self, other: T) { + *self += -(*other.borrow()); + } +} + +impl>> Sub for Projective

{ + type Output = Self; + + fn sub(mut self, other: T) -> Self { + self -= other.borrow(); + self + } +} + +impl_additive_ops_from_ref!(Projective, TECurveConfig); + +impl<'a, P: TECurveConfig> Add<&'a Self> for Projective

{ + type Output = Self; + + fn add(mut self, other: &'a Self) -> Self { + self += other; + self + } +} + +impl<'a, P: TECurveConfig> Sub<&'a Self> for Projective

{ + type Output = Self; + + fn sub(mut self, other: &'a Self) -> Self { + self -= other; + self + } +} + +impl<'a, P: TECurveConfig> AddAssign<&'a Self> for Projective

{ + fn add_assign(&mut self, other: &'a Self) { + // See "Twisted Edwards Curves Revisited" (https://eprint.iacr.org/2008/522.pdf) + // by Huseyin Hisil, Kenneth Koon-Ho Wong, Gary Carter, and Ed Dawson + // 3.1 Unified Addition in E^e + + // A = x1 * x2 + let a = self.x * other.x; + + // B = y1 * y2 + let b = self.y * other.y; + + // C = d * t1 * t2 + let c = P::COEFF_D * self.t * other.t; + + // D = z1 * z2 + let d = self.z * other.z; + + // H = B - aA + let h = b - P::mul_by_a(a); + + // E = (x1 + y1) * (x2 + y2) - A - B + let e = (self.x + self.y) * (other.x + other.y) - a - b; + + // F = D - C + let f = d - c; + + // G = D + C + let g = d + c; + + // x3 = E * F + self.x = e * f; + + // y3 = G * H + self.y = g * h; + + // t3 = E * H + self.t = e * h; + + // z3 = F * G + self.z = f * g; + } +} + +impl<'a, P: TECurveConfig> SubAssign<&'a Self> for Projective

{ + fn sub_assign(&mut self, other: &'a Self) { + *self += -(*other); + } +} + +impl> MulAssign + for Projective

+{ + fn mul_assign(&mut self, other: T) { + *self = self.mul_bigint(other.borrow().into_bigint()); + } +} + +impl> Mul for Projective

{ + type Output = Self; + + #[inline] + fn mul(mut self, other: T) -> Self { + self *= other; + self + } +} + +impl>> core::iter::Sum + for Projective

+{ + fn sum(iter: I) -> Self + where + I: Iterator, + { + iter.fold(Self::zero(), |acc, x| acc + x.borrow()) + } +} + +// The affine point (X, Y) is represented in the Extended Projective coordinates +// with Z = 1. +impl From> for Projective

{ + fn from(p: Affine

) -> Projective

{ + Self::new_unchecked(p.x, p.y, p.x * p.y, P::BaseField::one()) + } +} + +#[derive(Educe)] +#[educe(Copy, Clone, PartialEq, Eq, Debug, Hash)] +pub struct MontgomeryAffine { + pub x: P::BaseField, + pub y: P::BaseField, +} + +impl Display for MontgomeryAffine

{ + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + write!(f, "MontgomeryAffine(x={}, y={})", self.x, self.y) + } +} + +impl MontgomeryAffine

{ + pub fn new(x: P::BaseField, y: P::BaseField) -> Self { + Self { x, y } + } +} diff --git a/lib/crypto/src/field/fp.rs b/lib/crypto/src/field/fp.rs index 541745213..1332a9d91 100644 --- a/lib/crypto/src/field/fp.rs +++ b/lib/crypto/src/field/fp.rs @@ -519,6 +519,10 @@ impl, const N: usize> AdditiveGroup for Fp { impl, const N: usize> Field for Fp { const ONE: Self = Fp::new_unchecked(P::R); + fn extension_degree() -> usize { + 1 + } + #[inline] fn square(&self) -> Self { let mut temp = *self; diff --git a/lib/crypto/src/field/mod.rs b/lib/crypto/src/field/mod.rs index 4b64eaa5b..5149c9ce2 100644 --- a/lib/crypto/src/field/mod.rs +++ b/lib/crypto/src/field/mod.rs @@ -101,6 +101,10 @@ pub trait Field: /// The multiplicative identity of the field. const ONE: Self; + /// Returns the extension degree of this field. + #[must_use] + fn extension_degree() -> usize; + /// Returns `self * self`. #[must_use] fn square(&self) -> Self; @@ -151,4 +155,14 @@ pub trait Field: // If res is empty, return one. res.unwrap_or(Self::ONE) } + + /// Returns `sum([a_i * b_i])`. + #[inline] + fn sum_of_products(a: &[Self; T], b: &[Self; T]) -> Self { + let mut sum = Self::zero(); + for i in 0..a.len() { + sum += a[i] * b[i]; + } + sum + } } diff --git a/lib/crypto/src/field/prime.rs b/lib/crypto/src/field/prime.rs index d920386e1..737772356 100644 --- a/lib/crypto/src/field/prime.rs +++ b/lib/crypto/src/field/prime.rs @@ -23,13 +23,6 @@ pub trait PrimeField: Self::MODULUS } - /// Returns the extension degree of this field with respect - /// to `Self::BasePrimeField`. - #[must_use] - fn extension_degree() -> usize { - 1 - } - /// Construct a prime field element from a big integer. fn from_bigint(repr: Self::BigInt) -> Self; diff --git a/lib/crypto/src/lib.rs b/lib/crypto/src/lib.rs index 5474da175..e22b1b372 100644 --- a/lib/crypto/src/lib.rs +++ b/lib/crypto/src/lib.rs @@ -37,5 +37,6 @@ pub mod poseidon2; pub use keccak::KeccakBuilder; +pub mod curve; #[cfg(all(test, feature = "std"))] mod test_helpers;