diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 39f9e7b..6607146 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,7 +43,7 @@ jobs: strategy: matrix: target: - - wasm32-wasi + - wasm32-wasip1 - thumbv6m-none-eabi - thumbv7em-none-eabihf steps: @@ -81,7 +81,6 @@ jobs: - uses: actions/checkout@v4 - run: cargo fetch # Requires #![deny(rustdoc::broken_intra_doc_links)] in crates. - - run: sudo apt-get -y install libfontconfig1-dev - name: Check intra-doc links run: cargo doc --all-features --document-private-items diff --git a/CHANGELOG.md b/CHANGELOG.md index aec3f6d..7cf6177 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,38 @@ and this library adheres to Rust's notion of ## [Unreleased] +## [0.14.0] - 2026-06-01 +### Added +- `group::CurveAffine`, an affine-representation trait that the curve-specific + affine traits are now built on top of. +- `group::Group::mul_by_generator`, with a default implementation. Implementors + can override it to take advantage of precomputed tables. +- `group::Group::try_random(rng: &mut R) -> Result`, + a new trait method that must be implemented by downstreams. It samples a + non-identity group element using a fallible RNG and propagates the RNG's error. + +### Changed +- MSRV is now 1.85.0. +- Bumped dependencies to `ff 0.14`, `rand_core 0.10`. +- `group::Group::random(rng: impl RngCore) -> Self` has been changed to + `Group::random(rng: &mut R) -> Self`, to enable passing a + trait object as the RNG. It now has a default implementation in terms of + `Group::try_random`. +- 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 - Bumped `ff` to `0.13` diff --git a/Cargo.lock b/Cargo.lock index 46af6c4..a00b43c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,12 +1,12 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "ff" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +checksum = "a1f686ab92a9fb0eaf188f6c6c87b89490baa6fdb0db4544ba4dc47f7942489f" dependencies = [ "rand_core", "subtle", @@ -14,7 +14,7 @@ dependencies = [ [[package]] name = "group" -version = "0.13.0" +version = "0.14.0" dependencies = [ "ff", "memuse", @@ -26,36 +26,36 @@ dependencies = [ [[package]] name = "memuse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2145869435ace5ea6ea3d35f59be559317ec9a0d04e1812d5f185a87b6d36f1a" +checksum = "3d97bbf43eb4f088f8ca469930cde17fa036207c9a5e02ccc5107c4e8b17c964" [[package]] name = "rand" -version = "0.8.5" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8" dependencies = [ "rand_core", ] [[package]] name = "rand_core" -version = "0.6.4" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" [[package]] name = "rand_xorshift" -version = "0.3.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +checksum = "60aa6af80be32871323012e02e6e65f8a7cc7890931ae421d217ad8fe0df2ccf" dependencies = [ "rand_core", ] [[package]] name = "subtle" -version = "2.4.1" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" diff --git a/Cargo.toml b/Cargo.toml index bff1cf0..236d244 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "group" -version = "0.13.0" +version = "0.14.0" authors = [ "Sean Bowe ", "Jack Grigg ", ] edition = "2021" -rust-version = "1.56" +rust-version = "1.85" readme = "README.md" license = "MIT/Apache-2.0" @@ -16,10 +16,10 @@ homepage = "https://github.com/zkcrypto/group" repository = "https://github.com/zkcrypto/group" [dependencies] -ff = { version = "0.13", default-features = false } -rand = { version = "0.8", optional = true, default-features = false } -rand_core = { version = "0.6", default-features = false } -rand_xorshift = { version = "0.3", optional = true } +ff = { version = "0.14", default-features = false } +rand = { version = "0.10", optional = true, default-features = false } +rand_core = { version = "0.10", default-features = false } +rand_xorshift = { version = "0.5", optional = true } subtle = { version = "2.2.1", default-features = false } # Crate for exposing the dynamic memory usage of the w-NAF structs. diff --git a/README.md b/README.md index c3c0640..fd57744 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ wider discussion. ## Minimum Supported Rust Version -Requires Rust **1.56** or higher. +Requires Rust **1.85** or higher. Minimum supported Rust version can be changed in the future, but it will be done with a minor version bump. diff --git a/rust-toolchain.toml b/rust-toolchain.toml index de43b23..efd3308 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "1.56.0" +channel = "1.85.0" components = [ "clippy", "rustfmt" ] 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..46cd0a8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,7 @@ use core::fmt; use core::iter::Sum; use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; use ff::PrimeField; -use rand_core::RngCore; +use rand_core::{Rng, TryRng}; use subtle::{Choice, CtOption}; pub mod cofactor; @@ -73,10 +73,23 @@ pub trait Group: type Scalar: PrimeField; /// Returns an element chosen uniformly at random from the non-identity elements of - /// this group. + /// this group using a user-provided infallible RNG. /// - /// This function is non-deterministic, and samples from the user-provided RNG. - fn random(rng: impl RngCore) -> Self; + /// This is a convenience wrapper around [`Group::try_random`] for RNGs that cannot + /// fail. Use [`Group::try_random`] if your RNG may fail (for example, an OS-backed + /// entropy source). + fn random(rng: &mut R) -> Self { + let Ok(out) = Self::try_random(rng); + out + } + + /// Returns an element chosen uniformly at random from the non-identity elements of + /// this group using a user-provided fallible RNG. + /// + /// Returns `Err` propagating the RNG's error if the underlying RNG fails to produce + /// the randomness required to sample an element. Implementors of `Group` must + /// provide this method; [`Group::random`] is derived from it for infallible RNGs. + fn try_random(rng: &mut R) -> Result; /// Returns the additive identity, also known as the "neutral element". fn identity() -> Self; @@ -90,18 +103,22 @@ pub trait Group: /// Doubles this element. #[must_use] fn double(&self) -> Self; + + /// Multiply by the generator of the prime-order subgroup. + #[must_use] + fn mul_by_generator(scalar: &Self::Scalar) -> Self { + Self::generator() * scalar + } } -/// 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; /// 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 +127,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..d5f6b1f 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([ @@ -309,7 +305,7 @@ fn random_addition_tests() { assert_eq!(aplusa, aplusamixed); } - let mut tmp = vec![G::identity(); 6]; + let mut tmp = [G::identity(); 6]; // (a + b) + c tmp[0] = a; @@ -378,8 +374,8 @@ fn random_transformation_tests() { for _ in 0..10 { let mut v = (0..1000).map(|_| G::random(&mut rng)).collect::>(); - use rand::distributions::{Distribution, Uniform}; - let between = Uniform::new(0, 1000); + use rand::distr::{Distribution, Uniform}; + let between = Uniform::new(0, 1000).unwrap(); // Sprinkle in some normalized points for _ in 0..5 { v[between.sample(&mut rng)] = G::identity(); @@ -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, diff --git a/src/wnaf.rs b/src/wnaf.rs index 175d676..5adaf66 100644 --- a/src/wnaf.rs +++ b/src/wnaf.rs @@ -255,6 +255,12 @@ pub struct Wnaf { window_size: W, } +impl Default for Wnaf<(), Vec, Vec> { + fn default() -> Self { + Self::new() + } +} + impl Wnaf<(), Vec, Vec> { /// Construct a new wNAF context without allocating. pub fn new() -> Self { @@ -334,7 +340,7 @@ impl<'a, G: Group> Wnaf> { } #[cfg(feature = "wnaf-memuse")] -impl<'a, G: Group> memuse::DynamicUsage for Wnaf> { +impl memuse::DynamicUsage for Wnaf> { fn dynamic_usage(&self) -> usize { // The heap memory for the window table is counted in the parent `Wnaf`. self.scalar.dynamic_usage() @@ -359,7 +365,7 @@ impl<'a, G: Group> Wnaf, &'a [i64]> { } #[cfg(feature = "wnaf-memuse")] -impl<'a, G: Group + memuse::DynamicUsage> memuse::DynamicUsage for Wnaf, &'a [i64]> { +impl memuse::DynamicUsage for Wnaf, &[i64]> { fn dynamic_usage(&self) -> usize { // The heap memory for the scalar representation is counted in the parent `Wnaf`. self.base.dynamic_usage() @@ -426,7 +432,7 @@ impl WnafScalar { WnafScalar { wnaf, - field: PhantomData::default(), + field: PhantomData, } } }