Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
262 changes: 174 additions & 88 deletions ec/src/curve_edwards.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
//! - Binary Edwards curves (characteristic $2$): Bernstein–Lange–Rezaeian Farashahi (2008)
//! - Odd characteristic: <https://hyperelliptic.org/EFD/g1p/auto-edwards.html>

use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
use fp::{ref_field_impl, ref_field_trait_impl};
use core::fmt;
use fp::field_ops::{FieldOps, FieldRandom};

Expand All @@ -38,14 +40,15 @@ use crate::point_edwards::EdwardsPoint;
///
/// In odd characteristic only `d2` is used (the parameter `d`).
/// In characteristic 2 both `d1` and `d2` are used.
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
pub struct EdwardsCurve<F: FieldOps> {
/// The invariant d1 in the equation
pub d1: F,
/// The invariant d2 in the equation
pub d2: F,
}


impl<F> fmt::Display for EdwardsCurve<F>
where
F: FieldOps + fmt::Display,
Expand Down Expand Up @@ -79,117 +82,159 @@ where
}
}

impl<F: FieldOps + FieldRandom> EdwardsCurve<F> {
/// Construct an odd-characteristic Edwards curve `x² + y² = 1 + d·x²·y²`.
///
/// Stores `d` as `d2`; `d1` is set to zero (unused).
pub fn new(d: F) -> Self {
assert!(F::characteristic()[0] != 2, "use new_binary() for char 2");
assert!(d != F::zero(), "d must be nonzero");
assert!(d != F::one(), "d must not be 1");
Self {
d1: F::zero(),
d2: d,

ref_field_impl! {
impl<F: FieldOps + FieldRandom> EdwardsCurve<F> {
/// Construct an odd-characteristic Edwards curve `x² + y² = 1 + d·x²·y²`.
///
/// Stores `d` as `d2`; `d1` is set to zero (unused).
pub fn new(d: F) -> Self {
assert!(F::characteristic()[0] != 2, "use new_binary() for char 2");
assert!(d != F::zero(), "d must be nonzero");
assert!(d != F::one(), "d must not be 1");
Self { d1: F::zero(), d2: d }
}
}

/// Construct a binary Edwards curve
/// `d₁(x+y) + d₂(x²+y²) = xy + xy(x+y) + x²y²`.
pub fn new_binary(d1: F, d2: F) -> Self {
assert_eq!(F::characteristic()[0], 2, "use new() for odd char");
assert!(d1 != F::zero(), "d1 must be nonzero");
let d1_sq = <F as FieldOps>::square(&d1);
assert!(d2 != d1_sq + d1, "d2 must differ from d1² + d1");
Self { d1, d2 }
}
/// Construct a binary Edwards curve
/// `d₁(x+y) + d₂(x²+y²) = xy + xy(x+y) + x²y²`.
pub fn new_binary(d1: F, d2: F) -> Self {
assert_eq!(F::characteristic()[0], 2, "use new() for odd char");
assert!(d1 != F::zero(), "d1 must be nonzero");

/// Convenience accessor: the parameter `d` in odd characteristic.
pub fn d(&self) -> F {
self.d2
}
let d1_sq = <F as FieldOps>::square(&d1);
let forbidden = &d1_sq + &d1;
assert!(d2 != forbidden, "d2 must differ from d1² + d1");

/// Check whether the affine point `(x, y)` lies on the curve.
pub fn contains(&self, x: &F, y: &F) -> bool {
if F::characteristic()[0] != 2 {
// x² + y² == 1 + d·x²·y²
let x2 = <F as FieldOps>::square(x);
let y2 = <F as FieldOps>::square(y);
x2 + y2 == F::one() + self.d2 * x2 * y2
} else {
// d₁(x+y) + d₂(x²+y²) == xy + xy(x+y) + x²y²
let x2 = <F as FieldOps>::square(x);
let y2 = <F as FieldOps>::square(y);
let xy = *x * *y;
let xpy = *x + *y;
self.d1 * xpy + self.d2 * (x2 + y2) == xy + xy * xpy + x2 * y2
Self { d1, d2 }
}
}

/// Sample a random affine point on this Edwards curve using the provided RNG.
///
/// This currently uses a square-root-based construction and is implemented
/// only for odd characteristic. It returns a point `P` such that
/// `self.is_on_curve(&P)` holds.
pub fn random_point(&self, rng: &mut (impl rand::CryptoRng + rand::Rng)) -> EdwardsPoint<F> {
assert!(
F::characteristic()[0] != 2,
"random_point currently implemented only for odd characteristic"
);

loop {
let x = F::random(rng);
let x2 = <F as FieldOps>::square(&x);
let denom = F::one() - self.d2 * x2;

if bool::from(denom.is_zero()) {
continue;
/// Convenience accessor: the parameter `d` in odd characteristic.
pub fn d(&self) -> F {
self.d2.clone()
}

/// Check whether the affine point `(x, y)` lies on the curve.
pub fn contains(&self, x: &F, y: &F) -> bool {
if F::characteristic()[0] != 2 {
// x² + y² == 1 + d·x²·y²
let x2 = <F as FieldOps>::square(x);
let y2 = <F as FieldOps>::square(y);

let lhs = &x2 + &y2;

let x2y2 = &x2 * &y2;
let dx2y2 = &self.d2 * &x2y2;
let one = F::one();
let rhs = &one + &dx2y2;

lhs == rhs
} else {
// d₁(x+y) + d₂(x²+y²) == xy + xy(x+y) + x²y²
let x2 = <F as FieldOps>::square(x);
let y2 = <F as FieldOps>::square(y);
let xy = x * y;
let xpy = x + y;

let lhs1 = &self.d1 * &xpy;
let x2py2 = &x2 + &y2;
let lhs2 = &self.d2 * &x2py2;
let lhs = &lhs1 + &lhs2;

let xy_xpy = &xy * &xpy;
let x2y2 = &x2 * &y2;
let rhs_tmp = &xy + &xy_xpy;
let rhs = &rhs_tmp + &x2y2;

lhs == rhs
}
}

/// Sample a random affine point on this Edwards curve using the provided RNG.
///
/// This currently uses a square-root-based construction and is implemented
/// only for odd characteristic. It returns a point `P` such that
/// `self.is_on_curve(&P)` holds.
pub fn random_point(&self, rng: &mut (impl rand::CryptoRng + rand::Rng)) -> EdwardsPoint<F> {
assert!(
F::characteristic()[0] != 2,
"random_point currently implemented only for odd characteristic"
);

loop {
let x = F::random(rng);
let x2 = <F as FieldOps>::square(&x);
let denom = &F::one() - &(&self.d2 * &x2);

if bool::from(denom.is_zero()) {
continue;
}

let rhs = (F::one() - x2) * denom.invert().unwrap();
let rhs = &(&F::one() - &x2) * &denom.invert().unwrap();

if let Some(y) = rhs.sqrt().into_option() {
let p = EdwardsPoint::new(x, y);
debug_assert!(self.is_on_curve(&p));
return p;
if let Some(y) = rhs.sqrt().into_option() {
let p = EdwardsPoint::new(x, y);
debug_assert!(self.is_on_curve(&p));
return p;
}
}
}
}
}

impl<F: FieldOps + FieldRandom> Curve for EdwardsCurve<F> {
type BaseField = F;
type Point = EdwardsPoint<F>;

fn is_on_curve(&self, point: &Self::Point) -> bool {
self.contains(&point.x, &point.y)
}

fn random_point(&self, rng: &mut (impl rand::CryptoRng + rand::Rng)) -> Self::Point {
EdwardsCurve::random_point(self, rng)
}

fn j_invariant(&self) -> F {
















ref_field_trait_impl!{
impl<F: FieldOps + FieldRandom> Curve for EdwardsCurve<F> {
type BaseField = F;
type Point = EdwardsPoint<F>;

fn is_on_curve(&self, point: &Self::Point) -> bool {
self.contains(&point.x, &point.y)
}

fn random_point(&self, rng: &mut (impl rand::CryptoRng + rand::Rng)) -> Self::Point {
EdwardsCurve::random_point(self, rng)
}

fn j_invariant(&self) -> F {
if F::characteristic()[0] != 2 {
// j = 16(1 + 14d + d²)³ / (d(1 − d)⁴)
let d = self.d2;
let d2 = <F as FieldOps>::square(&d);
let two = <F as FieldOps>::double(&F::one());
let four = <F as FieldOps>::double(&two);
let eight = <F as FieldOps>::double(&four);
let fourteen = eight + four + two;
let fourteen = &eight + &(&four + &two);
let sixteen = <F as FieldOps>::double(&eight);

let inner = F::one() + fourteen * d + d2;
let inner_cubed = inner * <F as FieldOps>::square(&inner);
let numer = sixteen * inner_cubed;
let inner = &F::one() + &(&(&fourteen * &d) + &d2);
let inner_cubed = &inner * &<F as FieldOps>::square(&inner);
let numer = &sixteen * &inner_cubed;

let one_minus_d = F::one() - d;
let one_minus_d = &F::one() - &d;
let omd2 = <F as FieldOps>::square(&one_minus_d);
let omd4 = <F as FieldOps>::square(&omd2);
let denom = d * omd4;
let denom = &d * &omd4;

numer
* denom
&numer
* &denom
.invert()
.into_option()
.expect("d(1-d)^4 must be invertible")
Expand All @@ -198,19 +243,60 @@ impl<F: FieldOps + FieldRandom> Curve for EdwardsCurve<F> {
let d1_sq = <F as FieldOps>::square(&self.d1);
let d1_4 = <F as FieldOps>::square(&d1_sq);
let d2_sq = <F as FieldOps>::square(&self.d2);
let denom = d1_4 * (d1_4 + d1_sq + d2_sq);
let denom = &d1_4 * &(&d1_4 + &(&d1_sq + &d2_sq));
denom
.invert()
.into_option()
.expect("j-invariant denominator must be invertible")
}
}

fn a_invariants(&self) -> Vec<Self::BaseField> {
if F::characteristic()[0] != 2 {
vec![self.d2]
} else {
vec![self.d1, self.d2]
}
}
}
}

fn a_invariants(&self) -> Vec<Self::BaseField> {
if F::characteristic()[0] != 2 {
vec![self.d2]
} else {
vec![self.d1, self.d2]

// ---------------------------------------------------------------------------
// Constant-time functionalities
// ---------------------------------------------------------------------------

impl<F> ConditionallySelectable for EdwardsCurve<F>
where
F: FieldOps + Copy,
{
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
Self {
d1: F::conditional_select(&a.d1, &b.d1, choice),
d2: F::conditional_select(&a.d2, &b.d2, choice),
}
}

fn conditional_assign(&mut self, other: &Self, choice: Choice) {
F::conditional_assign(&mut self.d1, &other.d1, choice);
F::conditional_assign(&mut self.d2, &other.d2, choice);
}

fn conditional_swap(a: &mut Self, b: &mut Self, choice: Choice) {
F::conditional_swap(&mut a.d1, &mut b.d1, choice);
F::conditional_swap(&mut a.d2, &mut b.d2, choice);
}
}

impl<F> ConstantTimeEq for EdwardsCurve<F>
where
F: FieldOps + Copy + ConstantTimeEq,
{
fn ct_eq(&self, other: &Self) -> Choice {
self.d1.ct_eq(&other.d1) & self.d2.ct_eq(&other.d2)
}

fn ct_ne(&self, other: &Self) -> Choice {
!self.ct_eq(other)
}
}
Loading
Loading