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")]