Skip to content

Commit

Permalink
Implement group traits for EdwardsPoint
Browse files Browse the repository at this point in the history
  • Loading branch information
str4d committed Dec 20, 2022
1 parent dccdb92 commit 715e438
Showing 1 changed file with 340 additions and 0 deletions.
340 changes: 340 additions & 0 deletions src/edwards.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,13 @@ use core::ops::{Mul, MulAssign};
#[cfg(feature = "digest")]
use digest::{generic_array::typenum::U64, Digest};

#[cfg(feature = "group")]
use {
group::{cofactor::CofactorGroup, prime::PrimeGroup, GroupEncoding},
rand_core::RngCore,
subtle::CtOption,
};

use subtle::Choice;
use subtle::ConditionallyNegatable;
use subtle::ConditionallySelectable;
Expand Down Expand Up @@ -1107,6 +1114,339 @@ 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::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<Self> {
// 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<Self> {
// 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<SubgroupPoint> 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 AddAssign<&SubgroupPoint> for SubgroupPoint {
fn add_assign(&mut self, rhs: &SubgroupPoint) {
self.0 += rhs.0
}
}

#[cfg(feature = "group")]
define_add_assign_variants!(LHS = SubgroupPoint, RHS = SubgroupPoint);

#[cfg(feature = "group")]
impl AddAssign<&SubgroupPoint> for EdwardsPoint {
fn add_assign(&mut self, rhs: &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 SubAssign<&SubgroupPoint> for SubgroupPoint {
fn sub_assign(&mut self, rhs: &SubgroupPoint) {
self.0 -= rhs.0;
}
}

#[cfg(feature = "group")]
define_sub_assign_variants!(LHS = SubgroupPoint, RHS = SubgroupPoint);

#[cfg(feature = "group")]
impl SubAssign<&SubgroupPoint> for EdwardsPoint {
fn sub_assign(&mut self, rhs: &SubgroupPoint) {
*self -= rhs.0;
}
}

#[cfg(feature = "group")]
define_sub_assign_variants!(LHS = EdwardsPoint, RHS = SubgroupPoint);

#[cfg(feature = "group")]
impl<T> Sum<T> for SubgroupPoint
where
T: Borrow<SubgroupPoint>,
{
fn sum<I>(iter: I) -> Self
where
I: Iterator<Item = T>,
{
use group::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 MulAssign<&Scalar> for SubgroupPoint {
fn mul_assign(&mut self, scalar: &Scalar) {
self.0 *= scalar;
}
}

#[cfg(feature = "group")]
define_mul_assign_variants!(LHS = SubgroupPoint, RHS = Scalar);

#[cfg(feature = "group")]
impl group::Group for SubgroupPoint {
type Scalar = Scalar;

fn random(mut rng: impl RngCore) -> Self {
use group::ff::Field;

// This will almost never loop, but `Group::random` is documented as returning a
// non-identity element.
let s = loop {
let s: Scalar = 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 = <EdwardsPoint as GroupEncoding>::Repr;

fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
EdwardsPoint::from_bytes(bytes).and_then(|p| p.into_subgroup())
}

fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
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<Self::Subgroup> {
CtOption::new(SubgroupPoint(self), CofactorGroup::is_torsion_free(&self))
}

fn is_torsion_free(&self) -> Choice {
(self * constants::BASEPOINT_ORDER).ct_eq(&Self::identity())
}
}

// ------------------------------------------------------------------------
// Tests
// ------------------------------------------------------------------------
Expand Down

0 comments on commit 715e438

Please sign in to comment.