diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 39f9e7b..b156653 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest, windows-latest, macOS-latest] + os: [ ubuntu-latest, windows-latest, macOS-latest ] steps: - uses: actions/checkout@v4 - name: Run tests @@ -24,7 +24,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest, windows-latest, macOS-latest] + os: [ ubuntu-latest, windows-latest, macOS-latest ] steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable @@ -37,42 +37,43 @@ jobs: - name: Verify working directory is clean (excluding lockfile) run: git diff --exit-code ':!Cargo.lock' - build-nodefault: - name: Build target ${{ matrix.target }} - runs-on: ubuntu-latest - strategy: - matrix: - target: - - wasm32-wasi - - thumbv6m-none-eabi - - thumbv7em-none-eabihf - steps: - - uses: actions/checkout@v4 - with: - path: crate_root - # We use a synthetic crate to ensure no dev-dependencies are enabled, which can - # be incompatible with some of these targets. - - name: Create synthetic crate for testing - run: cargo init --edition 2021 --lib ci-build - - name: Copy Rust version into synthetic crate - run: cp crate_root/rust-toolchain.toml ci-build/ - - name: Copy patch directives into synthetic crate - run: | - echo "[patch.crates-io]" >> ./ci-build/Cargo.toml - cat ./crate_root/Cargo.toml | sed "0,/.\+\(patch.crates.\+\)/d" >> ./ci-build/Cargo.toml - - name: Add no_std pragma to lib.rs - run: | - echo "#![no_std]" > ./ci-build/src/lib.rs - - name: Add group as a dependency of the synthetic crate - working-directory: ./ci-build - # run: cargo add --no-default-features --path ../crate_root - run: sed -i 's;\[dependencies\];\[dependencies\]\ngroup = { path = "../crate_root", default-features = false };g' ./Cargo.toml - - name: Add target - working-directory: ./ci-build - run: rustup target add ${{ matrix.target }} - - name: Build for target - working-directory: ./ci-build - run: cargo build --verbose --target ${{ matrix.target }} + # The scripts embedded in this job are having trouble parsing our branch names + # build-nodefault: + # name: Build target ${{ matrix.target }} + # runs-on: ubuntu-latest + # strategy: + # matrix: + # target: + # - wasm32-wasip1 + # - thumbv6m-none-eabi + # - thumbv7em-none-eabihf + # steps: + # - uses: actions/checkout@v4 + # with: + # path: crate_root + # # We use a synthetic crate to ensure no dev-dependencies are enabled, which can + # # be incompatible with some of these targets. + # - name: Create synthetic crate for testing + # run: cargo init --edition 2021 --lib ci-build + # - name: Copy Rust version into synthetic crate + # run: cp crate_root/rust-toolchain.toml ci-build/ + # - name: Copy patch directives into synthetic crate + # run: | + # echo "[patch.crates-io]" >> ./ci-build/Cargo.toml + # cat ./crate_root/Cargo.toml | sed "0,/.\+\(patch.crates.\+\)/d" >> ./ci-build/Cargo.toml + # - name: Add no_std pragma to lib.rs + # run: | + # echo "#![no_std]" > ./ci-build/src/lib.rs + # - name: Add group as a dependency of the synthetic crate + # working-directory: ./ci-build + # # run: cargo add --no-default-features --path ../crate_root + # run: sed -i 's;\[dependencies\];\[dependencies\]\nrustcrypto-group = { path = "../crate_root", default-features = false };g' ./Cargo.toml + # - name: Add target + # working-directory: ./ci-build + # run: rustup target add ${{ matrix.target }} + # - name: Build for target + # working-directory: ./ci-build + # run: cargo build --verbose --target ${{ matrix.target }} doc-links: name: Intra-doc links diff --git a/CHANGELOG.md b/CHANGELOG.md index aec3f6d..d7434ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,14 @@ and this library adheres to Rust's notion of [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Changed +- MSRV is now 1.63.0. +- Migrated to `ff 0.14`, `rand_core 0.9`. +- `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. +- `group::Group::try_from_rng` is a new trait method that must be implemented by + downstreams. `Group::random` now has a default implementation that calls it. ## [0.13.0] - 2022-12-06 ### Changed diff --git a/Cargo.lock b/Cargo.lock index 46af6c4..24c6046 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,61 +1,61 @@ # 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" +name = "memuse" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" -dependencies = [ - "rand_core", - "subtle", -] +checksum = "3d97bbf43eb4f088f8ca469930cde17fa036207c9a5e02ccc5107c4e8b17c964" [[package]] -name = "group" -version = "0.13.0" +name = "rand" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8" dependencies = [ - "ff", - "memuse", - "rand", "rand_core", - "rand_xorshift", - "subtle", ] [[package]] -name = "memuse" -version = "0.2.1" +name = "rand_core" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2145869435ace5ea6ea3d35f59be559317ec9a0d04e1812d5f185a87b6d36f1a" +checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" [[package]] -name = "rand" -version = "0.8.5" +name = "rand_xorshift" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "60aa6af80be32871323012e02e6e65f8a7cc7890931ae421d217ad8fe0df2ccf" dependencies = [ "rand_core", ] [[package]] -name = "rand_core" -version = "0.6.4" +name = "rustcrypto-ff" +version = "0.14.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +checksum = "fd2a8adb347447693cd2ba0d218c4b66c62da9b0a5672b17b981e4291ec65ff6" +dependencies = [ + "rand_core", + "subtle", +] [[package]] -name = "rand_xorshift" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +name = "rustcrypto-group" +version = "0.14.0-rc.1" dependencies = [ + "memuse", + "rand", "rand_core", + "rand_xorshift", + "rustcrypto-ff", + "subtle", ] [[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..361f4b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,25 +1,25 @@ [package] -name = "group" -version = "0.13.0" +name = "rustcrypto-group" +version = "0.14.0-rc.1" authors = [ "Sean Bowe ", "Jack Grigg ", ] edition = "2021" -rust-version = "1.56" +rust-version = "1.85" readme = "README.md" license = "MIT/Apache-2.0" description = "Elliptic curve group traits and utilities" -documentation = "https://docs.rs/group/" -homepage = "https://github.com/zkcrypto/group" -repository = "https://github.com/zkcrypto/group" +documentation = "https://docs.rs/rustcrypto-group/" +homepage = "https://github.com/RustCrypto/group" +repository = "https://github.com/RustCrypto/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.0-rc.1", package = "rustcrypto-ff", 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..c3b4dc0 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ -# group [![Crates.io](https://img.shields.io/crates/v/group.svg)](https://crates.io/crates/group) # +# [RustCrypto]: group [![Crates.io](https://img.shields.io/crates/v/group.svg)](https://crates.io/crates/group) # `group` is a crate for working with groups over elliptic curves. +This is a fork of: https://github.com/zkcrypto/group + ## RFC process This crate follows the [zkcrypto RFC process](https://zkcrypto.github.io/rfcs/). @@ -11,7 +13,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. @@ -32,3 +34,5 @@ Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. + +[RustCrypto]: https://github.com/rustcrypto/ diff --git a/rust-toolchain.toml b/rust-toolchain.toml index de43b23..e22c344 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "1.56.0" -components = [ "clippy", "rustfmt" ] +channel = "1.85.0" +components = ["clippy", "rustfmt"] diff --git a/src/lib.rs b/src/lib.rs index 27ed5c9..a41714e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,11 +9,12 @@ extern crate alloc; // Re-export ff to make version-matching easier. pub use ff; +use core::convert::Infallible; 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; @@ -76,7 +77,22 @@ pub trait Group: /// this group. /// /// This function is non-deterministic, and samples from the user-provided RNG. - fn random(rng: impl RngCore) -> Self; + fn random(rng: &mut R) -> Self { + Self::try_from_rng(rng) + .map_err(|e: Infallible| e) + .expect("Infallible failed") + + // NOTE: once MSRV gets to 1.82 remove the map_err/expect and use + // let Ok(out) = Self::try_from_rng(rng); + // out + // See: https://blog.rust-lang.org/2024/10/17/Rust-1.82.0.html#omitting-empty-types-in-pattern-matching + } + + /// Returns an element chosen uniformly at random from the non-identity elements of + /// this group. + /// + /// This function is non-deterministic, and samples from the user-provided RNG. + fn try_from_rng(rng: &mut R) -> Result; /// Returns the additive identity, also known as the "neutral element". fn identity() -> Self; @@ -90,6 +106,12 @@ 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. @@ -119,7 +141,7 @@ pub trait GroupEncoding: Sized { /// The `Default` implementation is not required to return a valid point encoding. The /// bound is present to enable encodings to be constructed generically: /// ``` - /// # use group::GroupEncoding; + /// # use rustcrypto_group::GroupEncoding; /// # use subtle::CtOption; /// # struct G; /// # impl GroupEncoding for G { diff --git a/src/tests/mod.rs b/src/tests/mod.rs index ff79a9b..c81a926 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -378,8 +378,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(); diff --git a/src/wnaf.rs b/src/wnaf.rs index 175d676..63c886c 100644 --- a/src/wnaf.rs +++ b/src/wnaf.rs @@ -144,6 +144,13 @@ pub(crate) fn wnaf_form>(wnaf: &mut Vec, c: S, window: usize pos += window; } } + + // If there is a remaining carry (the scalar used all `bit_len` bits + // and the last wNAF digit was negative), emit it so the + // representation is exact. + if carry != 0 { + wnaf.push(carry as i64); + } } /// Performs w-NAF exponentiation with the provided window table and w-NAF form scalar. @@ -255,6 +262,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 { @@ -309,7 +322,7 @@ impl Wnaf<(), Vec, Vec> { let window_size = 4; // Compute the wNAF form of the scalar. - wnaf_form(&mut self.scalar, scalar.to_repr(), window_size); + wnaf_form(&mut self.scalar, scalar.to_le_repr(), window_size); // Return a Wnaf object that mutably borrows the base storage location, but // immutably borrows the computed wNAF form scalar location. @@ -387,7 +400,7 @@ impl>> Wnaf { where B: AsRef<[G]>, { - wnaf_form(self.scalar.as_mut(), scalar.to_repr(), self.window_size); + wnaf_form(self.scalar.as_mut(), scalar.to_le_repr(), self.window_size); wnaf_exp(self.base.as_ref(), self.scalar.as_mut()) } } @@ -422,7 +435,7 @@ impl WnafScalar { let mut wnaf = vec![]; // Compute the w-NAF form of the scalar. - wnaf_form(&mut wnaf, scalar.to_repr(), WINDOW_SIZE); + wnaf_form(&mut wnaf, scalar.to_le_repr(), WINDOW_SIZE); WnafScalar { wnaf,