diff --git a/CHANGELOG.md b/CHANGELOG.md index aec3f6d..c7d7203 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,26 @@ and this library adheres to Rust's notion of [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [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` + 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/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/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/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 27ed5c9..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")] @@ -92,16 +94,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 +112,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,