From a8cc9ecd014c0c28bd6972fdbe1685c8286de3b9 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Mon, 19 Dec 2022 12:23:51 +0000 Subject: [PATCH] Implement `group` traits for `EdwardsPoint` --- src/edwards.rs | 338 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 338 insertions(+) diff --git a/src/edwards.rs b/src/edwards.rs index edb229ff9..dea964714 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -104,6 +104,13 @@ use core::ops::{Mul, MulAssign}; #[cfg(feature = "digest")] use digest::{generic_array::typenum::U64, Digest}; +#[cfg(feature = "group")] +use { + group_crate::{cofactor::CofactorGroup, prime::PrimeGroup, GroupEncoding}, + rand_core::RngCore, + subtle::CtOption, +}; + use subtle::Choice; use subtle::ConditionallyNegatable; use subtle::ConditionallySelectable; @@ -1107,6 +1114,337 @@ impl Debug for EdwardsPoint { } } +// ------------------------------------------------------------------------ +// group traits +// ------------------------------------------------------------------------ + +// Use the full trait path to avoid Group::identity overlapping Identity::identity in the +// rest of the module (e.g. tests). +#[cfg(feature = "group")] +impl group_crate::Group for EdwardsPoint { + type Scalar = Scalar; + + fn random(mut rng: impl RngCore) -> Self { + let mut repr = CompressedEdwardsY([0u8; 32]); + loop { + rng.fill_bytes(&mut repr.0); + if let Some(p) = repr.decompress() { + if !IsIdentity::is_identity(&p) { + break p; + } + } + } + } + + fn identity() -> Self { + Identity::identity() + } + + fn generator() -> Self { + constants::ED25519_BASEPOINT_POINT + } + + fn is_identity(&self) -> Choice { + self.ct_eq(&Identity::identity()) + } + + fn double(&self) -> Self { + self.double() + } +} + +#[cfg(feature = "group")] +impl GroupEncoding for EdwardsPoint { + type Repr = [u8; 32]; + + fn from_bytes(bytes: &Self::Repr) -> CtOption { + // NOTE: this is duplicated from CompressedEdwardsY::decompress due to the + // constant time requirement. + + let Y = FieldElement::from_bytes(bytes); + let Z = FieldElement::ONE; + let YY = Y.square(); + let u = &YY - &Z; // u = y²-1 + let v = &(&YY * &constants::EDWARDS_D) + &Z; // v = dy²+1 + let (is_valid_y_coord, mut X) = FieldElement::sqrt_ratio_i(&u, &v); + + // FieldElement::sqrt_ratio_i always returns the nonnegative square root, + // so we negate according to the supplied sign bit. + let compressed_sign_bit = Choice::from(bytes[31] >> 7); + X.conditional_negate(compressed_sign_bit); + + CtOption::new( + EdwardsPoint { + X, + Y, + Z, + T: &X * &Y, + }, + is_valid_y_coord, + ) + } + + fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { + // Just use the checked API; there are no checks we can skip. + Self::from_bytes(bytes) + } + + fn to_bytes(&self) -> Self::Repr { + self.compress().to_bytes() + } +} + +/// A `SubgroupPoint` represents a point on the Edwards form of Curve25519, that is +/// guaranteed to be in the prime-order subgroup. +#[cfg(feature = "group")] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct SubgroupPoint(EdwardsPoint); + +#[cfg(feature = "group")] +impl From for EdwardsPoint { + fn from(p: SubgroupPoint) -> Self { + p.0 + } +} + +#[cfg(feature = "group")] +impl Neg for SubgroupPoint { + type Output = Self; + + fn neg(self) -> Self::Output { + SubgroupPoint(-self.0) + } +} + +#[cfg(feature = "group")] +impl<'a, 'b> Add<&'b SubgroupPoint> for &'a SubgroupPoint { + type Output = SubgroupPoint; + fn add(self, other: &'b SubgroupPoint) -> SubgroupPoint { + SubgroupPoint(self.0 + other.0) + } +} + +#[cfg(feature = "group")] +define_add_variants!( + LHS = SubgroupPoint, + RHS = SubgroupPoint, + Output = SubgroupPoint +); + +#[cfg(feature = "group")] +impl<'a, 'b> Add<&'b SubgroupPoint> for &'a EdwardsPoint { + type Output = EdwardsPoint; + fn add(self, other: &'b SubgroupPoint) -> EdwardsPoint { + self + other.0 + } +} + +#[cfg(feature = "group")] +define_add_variants!( + LHS = EdwardsPoint, + RHS = SubgroupPoint, + Output = EdwardsPoint +); + +#[cfg(feature = "group")] +impl<'b> AddAssign<&'b SubgroupPoint> for SubgroupPoint { + fn add_assign(&mut self, rhs: &'b SubgroupPoint) { + self.0 += rhs.0 + } +} + +#[cfg(feature = "group")] +define_add_assign_variants!(LHS = SubgroupPoint, RHS = SubgroupPoint); + +#[cfg(feature = "group")] +impl<'b> AddAssign<&'b SubgroupPoint> for EdwardsPoint { + fn add_assign(&mut self, rhs: &'b SubgroupPoint) { + *self += rhs.0 + } +} + +#[cfg(feature = "group")] +define_add_assign_variants!(LHS = EdwardsPoint, RHS = SubgroupPoint); + +#[cfg(feature = "group")] +impl<'a, 'b> Sub<&'b SubgroupPoint> for &'a SubgroupPoint { + type Output = SubgroupPoint; + fn sub(self, other: &'b SubgroupPoint) -> SubgroupPoint { + SubgroupPoint(self.0 - other.0) + } +} + +#[cfg(feature = "group")] +define_sub_variants!( + LHS = SubgroupPoint, + RHS = SubgroupPoint, + Output = SubgroupPoint +); + +#[cfg(feature = "group")] +impl<'a, 'b> Sub<&'b SubgroupPoint> for &'a EdwardsPoint { + type Output = EdwardsPoint; + fn sub(self, other: &'b SubgroupPoint) -> EdwardsPoint { + self - other.0 + } +} + +#[cfg(feature = "group")] +define_sub_variants!( + LHS = EdwardsPoint, + RHS = SubgroupPoint, + Output = EdwardsPoint +); + +#[cfg(feature = "group")] +impl<'b> SubAssign<&'b SubgroupPoint> for SubgroupPoint { + fn sub_assign(&mut self, rhs: &'b SubgroupPoint) { + self.0 -= rhs.0; + } +} + +#[cfg(feature = "group")] +define_sub_assign_variants!(LHS = SubgroupPoint, RHS = SubgroupPoint); + +#[cfg(feature = "group")] +impl<'b> SubAssign<&'b SubgroupPoint> for EdwardsPoint { + fn sub_assign(&mut self, rhs: &'b SubgroupPoint) { + *self -= rhs.0; + } +} + +#[cfg(feature = "group")] +define_sub_assign_variants!(LHS = EdwardsPoint, RHS = SubgroupPoint); + +#[cfg(feature = "group")] +impl Sum for SubgroupPoint +where + T: Borrow, +{ + fn sum(iter: I) -> Self + where + I: Iterator, + { + use group_crate::Group; + iter.fold(SubgroupPoint::identity(), |acc, item| acc + item.borrow()) + } +} + +#[cfg(feature = "group")] +impl<'a, 'b> Mul<&'b Scalar> for &'a SubgroupPoint { + type Output = SubgroupPoint; + + /// Scalar multiplication: compute `scalar * self`. + /// + /// For scalar multiplication of a basepoint, + /// `EdwardsBasepointTable` is approximately 4x faster. + fn mul(self, scalar: &'b Scalar) -> SubgroupPoint { + SubgroupPoint(self.0 * scalar) + } +} + +#[cfg(feature = "group")] +define_mul_variants!(LHS = Scalar, RHS = SubgroupPoint, Output = SubgroupPoint); + +#[cfg(feature = "group")] +impl<'a, 'b> Mul<&'b SubgroupPoint> for &'a Scalar { + type Output = SubgroupPoint; + + /// Scalar multiplication: compute `scalar * self`. + /// + /// For scalar multiplication of a basepoint, + /// `EdwardsBasepointTable` is approximately 4x faster. + fn mul(self, point: &'b SubgroupPoint) -> SubgroupPoint { + point * self + } +} + +#[cfg(feature = "group")] +define_mul_variants!(LHS = SubgroupPoint, RHS = Scalar, Output = SubgroupPoint); + +#[cfg(feature = "group")] +impl<'b> MulAssign<&'b Scalar> for SubgroupPoint { + fn mul_assign(&mut self, scalar: &'b Scalar) { + self.0 *= scalar; + } +} + +#[cfg(feature = "group")] +define_mul_assign_variants!(LHS = SubgroupPoint, RHS = Scalar); + +#[cfg(feature = "group")] +impl group_crate::Group for SubgroupPoint { + type Scalar = Scalar; + + fn random(mut rng: impl RngCore) -> Self { + // This will almost never loop, but `Group::random` is documented as returning a + // non-identity element. + let s = loop { + let s: Scalar = group_crate::ff::Field::random(&mut rng); + if !s.is_zero_vartime() { + break s; + } + }; + + // This gives an element of the prime-order subgroup. + Self::generator() * s + } + + fn identity() -> Self { + SubgroupPoint(Identity::identity()) + } + + fn generator() -> Self { + SubgroupPoint(EdwardsPoint::generator()) + } + + fn is_identity(&self) -> Choice { + self.0.ct_eq(&Identity::identity()) + } + + fn double(&self) -> Self { + SubgroupPoint(self.0.double()) + } +} + +#[cfg(feature = "group")] +impl GroupEncoding for SubgroupPoint { + type Repr = ::Repr; + + fn from_bytes(bytes: &Self::Repr) -> CtOption { + EdwardsPoint::from_bytes(bytes).and_then(|p| p.into_subgroup()) + } + + fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { + EdwardsPoint::from_bytes_unchecked(bytes).and_then(|p| p.into_subgroup()) + } + + fn to_bytes(&self) -> Self::Repr { + self.0.compress().to_bytes() + } +} + +#[cfg(feature = "group")] +impl PrimeGroup for SubgroupPoint {} + +/// Ristretto has a cofactor of 1. +#[cfg(feature = "group")] +impl CofactorGroup for EdwardsPoint { + type Subgroup = SubgroupPoint; + + fn clear_cofactor(&self) -> Self::Subgroup { + SubgroupPoint(self.mul_by_cofactor()) + } + + fn into_subgroup(self) -> CtOption { + CtOption::new(SubgroupPoint(self), CofactorGroup::is_torsion_free(&self)) + } + + fn is_torsion_free(&self) -> Choice { + (self * constants::BASEPOINT_ORDER).ct_eq(&Self::identity()) + } +} + // ------------------------------------------------------------------------ // Tests // ------------------------------------------------------------------------