From 8d104f18dbca5b0ab66f052130cc6456ff465e08 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sun, 30 Jul 2023 12:16:26 +0000 Subject: [PATCH] Add extension traits for exposing coordinates of curve points 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 zkcrypto/group#30. --- CHANGELOG.md | 2 + Cargo.toml | 4 + katex-header.html | 15 ++++ src/coordinates.rs | 206 +++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 + 5 files changed, 229 insertions(+) create mode 100644 katex-header.html create mode 100644 src/coordinates.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 66eea08..c7d7203 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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` diff --git a/Cargo.toml b/Cargo.toml index 60c0854..28cb226 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 } diff --git a/katex-header.html b/katex-header.html new file mode 100644 index 0000000..588b1eb --- /dev/null +++ b/katex-header.html @@ -0,0 +1,15 @@ + + + + \ No newline at end of file diff --git a/src/coordinates.rs b/src/coordinates.rs new file mode 100644 index 0000000..cba0a89 --- /dev/null +++ b/src/coordinates.rs @@ -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; + + /// Obtains a point given its coordinates. + fn from_coordinates(coords: TwistedEdwardsCoordinates) -> 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; +} + +/// The affine coordinates for a [`TwistedEdwardsPoint`]. +#[derive(Clone, Copy, Debug, Default)] +pub struct TwistedEdwardsCoordinates { + x: P::Base, + y: P::Base, +} + +impl TwistedEdwardsCoordinates

{ + /// 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 { + // 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 ConditionallySelectable for TwistedEdwardsCoordinates

{ + 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; + + /// Obtains a point given its coordinates. + fn from_coordinates(coords: MontgomeryCoordinates) -> Self; + + /// Returns the coordinates of this point. + /// + /// Returns `None` if this is the identity. + fn coordinates(&self) -> CtOption>; +} + +/// The affine coordinates for a [`MontgomeryCoordinates`]. +#[derive(Clone, Copy, Debug, Default)] +pub struct MontgomeryCoordinates { + u: P::Base, + v: P::Base, +} + +impl MontgomeryCoordinates

{ + /// 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 { + // 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 ConditionallySelectable for MontgomeryCoordinates

{ + 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; + + /// Obtains a point given its coordinates. + fn from_coordinates(coords: ShortWeierstrassCoordinates) -> Self; + + /// Returns the coordinates of this point. + /// + /// Returns `None` if this is the identity. + fn coordinates(&self) -> CtOption>; +} + +/// The affine coordinates for a [`ShortWeierstrassCoordinates`]. +#[derive(Clone, Copy, Debug, Default)] +pub struct ShortWeierstrassCoordinates { + x: P::Base, + y: P::Base, +} + +impl ShortWeierstrassCoordinates

{ + /// 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 { + // 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 ConditionallySelectable for ShortWeierstrassCoordinates

{ + 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), + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 4d84743..9ec208d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,6 +21,8 @@ pub mod prime; #[cfg(feature = "tests")] pub mod tests; +pub mod coordinates; + #[cfg(feature = "alloc")] mod wnaf; #[cfg(feature = "alloc")]