Skip to content

Commit

Permalink
Add extension traits for exposing coordinates of curve points
Browse files Browse the repository at this point in the history
We use extension traits to limit how generic the generic code can be,
forcing it to be aware of the specific curve model that the coordinates
are associated with.

Closes #30.
  • Loading branch information
str4d committed Jul 30, 2023
1 parent 696c212 commit 8d104f1
Show file tree
Hide file tree
Showing 5 changed files with 229 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ and this library adheres to Rust's notion of
## [Unreleased]
### Added
- `group::CurveAffine`
- `group::coordinates` module, containing extension traits and structs that
provide generic access to the coordinates of elliptic curve points.

### Changed
- The curve-related traits have been refactored around the new `CurveAffine`
Expand Down
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ homepage = "https://github.com/zkcrypto/group"
repository = "https://github.com/zkcrypto/group"
edition = "2021"

[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs", "--html-in-header", "katex-header.html"]

[dependencies]
ff = { version = "0.13", default-features = false }
rand = { version = "0.8", optional = true, default-features = false }
Expand Down
15 changes: 15 additions & 0 deletions katex-header.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.css" integrity="sha384-GvrOXuhMATgEsSwCs4smul74iXGOixntILdUW9XmUC6+HX0sLNAK3q71HotJqlAn" crossorigin="anonymous">
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.js" integrity="sha384-cpW21h6RZv/phavutF+AuVYrr+dA8xD9zs6FwLpaCct6O9ctzYFfFr4dgmgccOTx" crossorigin="anonymous"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/contrib/auto-render.min.js" integrity="sha384-+VBxd3r6XgURycqtZ117nYw44OOcIax56Z4dCRWbxyPt0Koah1uHoK0o4+/RRE05" crossorigin="anonymous"></script>
<script>
document.addEventListener("DOMContentLoaded", function() {
renderMathInElement(document.body, {
delimiters: [
{left: "$$", right: "$$", display: true},
{left: "\\(", right: "\\)", display: false},
{left: "$", right: "$", display: false},
{left: "\\[", right: "\\]", display: true}
]
});
});
</script>
206 changes: 206 additions & 0 deletions src/coordinates.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
//! Extension traits and structs that provide generic access to the coordinates of
//! elliptic curve points.
//!
//! Coordinates are meaningless without the context of the curve equation that constrains
//! them. To safely expose them in a generic context, we use extension traits to restrict
//! the scope of the generic curve parameter; this ensures that the code can only be used
//! with curve implementations that explicitly expose their use of a specific curve model.

use subtle::{Choice, ConditionallySelectable, CtOption};

use crate::CurveAffine;

//
// Twisted Edwards curve
//

/// An affine elliptic curve point on a twisted Edwards curve
/// $a \cdot x^2 + y^2 = 1 + d \cdot x^2 \cdot y^2$.
pub trait TwistedEdwardsPoint: CurveAffine + Default + ConditionallySelectable {
/// Field element type used in the curve equation.
type Base: Copy + ConditionallySelectable;

/// The parameter $a$ in the twisted Edwards curve equation.
///
/// When $a = 1$, this reduces to an ordinary Edwards curve.
const A: Self::Base;

/// The parameter $d$ in the twisted Edwards curve equation.
const D: Self::Base;

/// Obtains a point given $(x, y)$, failing if it is not on the curve.
fn from_bare_coordinates(x: Self::Base, y: Self::Base) -> CtOption<Self>;

/// Obtains a point given its coordinates.
fn from_coordinates(coords: TwistedEdwardsCoordinates<Self>) -> Self;

/// Returns the coordinates of this point.
///
/// For twisted Edwards curves, the identity has valid coordinates on the curve, so
/// this method is infallible.
fn coordinates(&self) -> TwistedEdwardsCoordinates<Self>;
}

/// The affine coordinates for a [`TwistedEdwardsPoint`].
#[derive(Clone, Copy, Debug, Default)]
pub struct TwistedEdwardsCoordinates<P: TwistedEdwardsPoint> {
x: P::Base,
y: P::Base,
}

impl<P: TwistedEdwardsPoint> TwistedEdwardsCoordinates<P> {
/// Obtains a `TwistedEdwardsCoordinates` value given $(x, y)$, failing if it is not
/// on the curve.
pub fn from_coordinates(x: P::Base, y: P::Base) -> CtOption<Self> {
// We use `P::from_bare_coordinates` to validate the coordinates.
P::from_bare_coordinates(x, y).map(|_| TwistedEdwardsCoordinates { x, y })
}

/// Returns the x-coordinate.
pub fn x(&self) -> P::Base {
self.x
}

/// Returns the y-coordinate.
pub fn y(&self) -> P::Base {
self.y
}
}

impl<P: TwistedEdwardsPoint> ConditionallySelectable for TwistedEdwardsCoordinates<P> {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
TwistedEdwardsCoordinates {
x: P::Base::conditional_select(&a.x, &b.x, choice),
y: P::Base::conditional_select(&a.y, &b.y, choice),
}
}
}

//
// Montgomery curve
//

/// An affine elliptic curve point on a Montgomery curve
/// $B \cdot v^2 = u^3 + A \cdot u^2 + u$.
///
/// For these curves, it is required that $B \cdot (A^2 - 4) ≠ 0$, which implies that
/// $A ≠ ±2$ and $B ≠ 0$.
pub trait MontgomeryPoint: CurveAffine + Default + ConditionallySelectable {
/// Field element type used in the curve equation.
type Base: Copy + ConditionallySelectable;

/// The parameter $A$ in the Montgomery curve equation.
const A: Self::Base;

/// The parameter $B$ in the Montgomery curve equation.
const B: Self::Base;

/// Obtains a point given $(u, v)$, failing if it is not on the curve.
fn from_bare_coordinates(u: Self::Base, v: Self::Base) -> CtOption<Self>;

/// Obtains a point given its coordinates.
fn from_coordinates(coords: MontgomeryCoordinates<Self>) -> Self;

/// Returns the coordinates of this point.
///
/// Returns `None` if this is the identity.
fn coordinates(&self) -> CtOption<MontgomeryCoordinates<Self>>;
}

/// The affine coordinates for a [`MontgomeryCoordinates`].
#[derive(Clone, Copy, Debug, Default)]
pub struct MontgomeryCoordinates<P: MontgomeryPoint> {
u: P::Base,
v: P::Base,
}

impl<P: MontgomeryPoint> MontgomeryCoordinates<P> {
/// Obtains a `MontgomeryCoordinates` value given $(u, v)$, failing if it is not on
/// the curve.
pub fn from_coordinates(u: P::Base, v: P::Base) -> CtOption<Self> {
// We use `P::from_bare_coordinates` to validate the coordinates.
P::from_bare_coordinates(u, v).map(|_| MontgomeryCoordinates { u, v })
}

/// Returns the u-coordinate.
pub fn u(&self) -> P::Base {
self.u
}

/// Returns the v-coordinate.
pub fn v(&self) -> P::Base {
self.v
}
}

impl<P: MontgomeryPoint> ConditionallySelectable for MontgomeryCoordinates<P> {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
MontgomeryCoordinates {
u: P::Base::conditional_select(&a.u, &b.u, choice),
v: P::Base::conditional_select(&a.v, &b.v, choice),
}
}
}

//
// Short Weierstrass curve
//

/// An affine elliptic curve point on a short Weierstrass curve
/// $y^2 = x^3 + a \cdot x + b$.
pub trait ShortWeierstrassPoint: CurveAffine + Default + ConditionallySelectable {
/// Field element type used in the curve equation.
type Base: Copy + ConditionallySelectable;

/// The parameter $a$ in the short Weierstrass curve equation.
const A: Self::Base;

/// The parameter $b$ in the short Weierstrass curve equation.
const B: Self::Base;

/// Obtains a point given $(x, y)$, failing if it is not on the curve.
fn from_bare_coordinates(x: Self::Base, y: Self::Base) -> CtOption<Self>;

/// Obtains a point given its coordinates.
fn from_coordinates(coords: ShortWeierstrassCoordinates<Self>) -> Self;

/// Returns the coordinates of this point.
///
/// Returns `None` if this is the identity.
fn coordinates(&self) -> CtOption<ShortWeierstrassCoordinates<Self>>;
}

/// The affine coordinates for a [`ShortWeierstrassCoordinates`].
#[derive(Clone, Copy, Debug, Default)]
pub struct ShortWeierstrassCoordinates<P: ShortWeierstrassPoint> {
x: P::Base,
y: P::Base,
}

impl<P: ShortWeierstrassPoint> ShortWeierstrassCoordinates<P> {
/// Obtains a `ShortWeierstrassCoordinates` value given $(x, y)$, failing if it is not
/// on the curve.
pub fn from_coordinates(x: P::Base, y: P::Base) -> CtOption<Self> {
// We use `P::from_bare_coordinates` to validate the coordinates.
P::from_bare_coordinates(x, y).map(|_| ShortWeierstrassCoordinates { x, y })
}

/// Returns the x-coordinate.
pub fn x(&self) -> P::Base {
self.x
}

/// Returns the y-coordinate.
pub fn y(&self) -> P::Base {
self.y
}
}

impl<P: ShortWeierstrassPoint> ConditionallySelectable for ShortWeierstrassCoordinates<P> {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
ShortWeierstrassCoordinates {
x: P::Base::conditional_select(&a.x, &b.x, choice),
y: P::Base::conditional_select(&a.y, &b.y, choice),
}
}
}
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ pub mod prime;
#[cfg(feature = "tests")]
pub mod tests;

pub mod coordinates;

#[cfg(feature = "alloc")]
mod wnaf;
#[cfg(feature = "alloc")]
Expand Down

0 comments on commit 8d104f1

Please sign in to comment.