From 696c2128529b5a9e18eed46d1da531753695db04 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 29 Jul 2023 17:32:01 +0000 Subject: [PATCH 1/2] Introduce `CurveAffine` trait This unifies the methods previously exposed by the `PrimeCurveAffine` and `CofactorCurveAffine` traits. The prime-order and cofactor traits are now all marker traits, and their affine-specific traits are automatically derived. --- CHANGELOG.md | 18 ++++++++++++++++++ src/cofactor.rs | 48 ++++------------------------------------------- src/lib.rs | 49 +++++++++++++++++++++++++++++++++++++++++------- src/prime.rs | 44 ++++--------------------------------------- src/tests/mod.rs | 8 ++------ 5 files changed, 70 insertions(+), 97 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aec3f6d..66eea08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,24 @@ and this library adheres to Rust's notion of [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added +- `group::CurveAffine` + +### Changed +- The curve-related traits have been refactored around the new `CurveAffine` + trait: + - `group::Curve::AffineRepr` has been renamed to `Curve::Affine`. + - All of the trait methods and associated types on the following traits have + been removed (use `group::Curve::Affine` or the `group::CurveAffine` trait + instead; trait implementors must implement `group::CurveAffine` instead + using the same logic): + - `group::cofactor::CofactorCurve` + - `group::cofactor::CofactorCurveAffine` + - `group::prime::PrimeCurve` + - `group::prime::PrimeCurveAffine` + - `group::cofactor::CofactorCurveAffine` and `group::prime::PrimeCurveAffine` + now have blanket implementations for all types `C: group::CurveAffine` where + `C::Curve` implements `CofactorCurve` or `PrimeCurve` respectively. ## [0.13.0] - 2022-12-06 ### Changed diff --git a/src/cofactor.rs b/src/cofactor.rs index 84bfe0a..fae0a89 100644 --- a/src/cofactor.rs +++ b/src/cofactor.rs @@ -1,9 +1,6 @@ -use core::fmt; -use core::ops::{Mul, Neg}; -use ff::PrimeField; use subtle::{Choice, CtOption}; -use crate::{prime::PrimeGroup, Curve, Group, GroupEncoding, GroupOps, GroupOpsOwned}; +use crate::{prime::PrimeGroup, Curve, CurveAffine, Group, GroupEncoding, GroupOps, GroupOpsOwned}; /// This trait represents an element of a cryptographic group with a large prime-order /// subgroup and a comparatively-small cofactor. @@ -54,47 +51,10 @@ pub trait CofactorGroup: /// Efficient representation of an elliptic curve point guaranteed to be /// in the correct prime order subgroup. -pub trait CofactorCurve: - Curve::Affine> + CofactorGroup -{ - type Affine: CofactorCurveAffine - + Mul - + for<'r> Mul<&'r Self::Scalar, Output = Self>; -} +pub trait CofactorCurve: Curve + CofactorGroup {} /// Affine representation of an elliptic curve point guaranteed to be /// in the correct prime order subgroup. -pub trait CofactorCurveAffine: - GroupEncoding - + Copy - + Clone - + Sized - + Send - + Sync - + fmt::Debug - + PartialEq - + Eq - + 'static - + Neg - + Mul<::Scalar, Output = ::Curve> - + for<'r> Mul< - &'r ::Scalar, - Output = ::Curve, - > -{ - type Scalar: PrimeField; - type Curve: CofactorCurve; - - /// Returns the additive identity. - fn identity() -> Self; +pub trait CofactorCurveAffine: CurveAffine {} - /// Returns a fixed generator of unknown exponent. - fn generator() -> Self; - - /// Determines if this point represents the point at infinity; the - /// additive identity. - fn is_identity(&self) -> Choice; - - /// Converts this element to its curve representation. - fn to_curve(&self) -> Self::Curve; -} +impl CofactorCurveAffine for C where C::Curve: CofactorCurve {} diff --git a/src/lib.rs b/src/lib.rs index 27ed5c9..4d84743 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -92,16 +92,16 @@ pub trait Group: fn double(&self) -> Self; } -/// Efficient representation of an elliptic curve point guaranteed. -pub trait Curve: - Group + GroupOps<::AffineRepr> + GroupOpsOwned<::AffineRepr> -{ +/// Efficient representation of an elliptic curve point. +pub trait Curve: Group + GroupOps + GroupOpsOwned { /// The affine representation for this elliptic curve. - type AffineRepr; + type Affine: CurveAffine + + Mul + + for<'r> Mul<&'r Self::Scalar, Output = Self>; /// Converts a batch of projective elements into affine elements. This function will /// panic if `p.len() != q.len()`. - fn batch_normalize(p: &[Self], q: &mut [Self::AffineRepr]) { + fn batch_normalize(p: &[Self], q: &mut [Self::Affine]) { assert_eq!(p.len(), q.len()); for (p, q) in p.iter().zip(q.iter_mut()) { @@ -110,7 +110,42 @@ pub trait Curve: } /// Converts this element into its affine representation. - fn to_affine(&self) -> Self::AffineRepr; + fn to_affine(&self) -> Self::Affine; +} + +/// Affine representation of an elliptic curve point. +pub trait CurveAffine: + GroupEncoding + + Copy + + fmt::Debug + + Eq + + Send + + Sync + + 'static + + Neg + + Mul<::Scalar, Output = Self::Curve> + + for<'r> Mul<&'r ::Scalar, Output = Self::Curve> +{ + /// The efficient representation for this elliptic curve. + type Curve: Curve; + + /// Scalars modulo the order of this group's scalar field. + /// + /// This associated type is temporary, and will be removed once downstream users have + /// migrated to using `Curve` as the primary generic bound. + type Scalar: PrimeField; + + /// Returns the additive identity. + fn identity() -> Self; + + /// Returns a fixed generator of unknown exponent. + fn generator() -> Self; + + /// Determines if this point represents the additive identity. + fn is_identity(&self) -> Choice; + + /// Converts this affine point to its efficient representation. + fn to_curve(&self) -> Self::Curve; } pub trait GroupEncoding: Sized { diff --git a/src/prime.rs b/src/prime.rs index 174888e..0964782 100644 --- a/src/prime.rs +++ b/src/prime.rs @@ -1,50 +1,14 @@ -use core::fmt; -use core::ops::{Mul, Neg}; -use ff::PrimeField; -use subtle::Choice; - -use crate::{Curve, Group, GroupEncoding}; +use crate::{Curve, CurveAffine, Group, GroupEncoding}; /// This trait represents an element of a prime-order cryptographic group. pub trait PrimeGroup: Group + GroupEncoding {} /// Efficient representation of an elliptic curve point guaranteed to be /// in the correct prime order subgroup. -pub trait PrimeCurve: Curve::Affine> + PrimeGroup { - type Affine: PrimeCurveAffine - + Mul - + for<'r> Mul<&'r Self::Scalar, Output = Self>; -} +pub trait PrimeCurve: Curve + PrimeGroup {} /// Affine representation of an elliptic curve point guaranteed to be /// in the correct prime order subgroup. -pub trait PrimeCurveAffine: GroupEncoding - + Copy - + Clone - + Sized - + Send - + Sync - + fmt::Debug - + PartialEq - + Eq - + 'static - + Neg - + Mul<::Scalar, Output = ::Curve> - + for<'r> Mul<&'r ::Scalar, Output = ::Curve> -{ - type Scalar: PrimeField; - type Curve: PrimeCurve; - - /// Returns the additive identity. - fn identity() -> Self; - - /// Returns a fixed generator of unknown exponent. - fn generator() -> Self; - - /// Determines if this point represents the point at infinity; the - /// additive identity. - fn is_identity(&self) -> Choice; +pub trait PrimeCurveAffine: CurveAffine {} - /// Converts this element to its curve representation. - fn to_curve(&self) -> Self::Curve; -} +impl PrimeCurveAffine for C where C::Curve: PrimeCurve {} diff --git a/src/tests/mod.rs b/src/tests/mod.rs index ff79a9b..89d0f15 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -4,11 +4,7 @@ use ff::{Field, PrimeField}; use rand::SeedableRng; use rand_xorshift::XorShiftRng; -use crate::{ - prime::{PrimeCurve, PrimeCurveAffine}, - wnaf::WnafGroup, - GroupEncoding, UncompressedEncoding, -}; +use crate::{prime::PrimeCurve, wnaf::WnafGroup, CurveAffine, GroupEncoding, UncompressedEncoding}; pub fn curve_tests() { let mut rng = XorShiftRng::from_seed([ @@ -426,7 +422,7 @@ fn random_compressed_encoding_tests() { pub fn random_uncompressed_encoding_tests() where - ::Affine: UncompressedEncoding, + G::Affine: UncompressedEncoding, { let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, From 8d104f18dbca5b0ab66f052130cc6456ff465e08 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sun, 30 Jul 2023 12:16:26 +0000 Subject: [PATCH 2/2] 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")]