diff --git a/Cargo.lock b/Cargo.lock index 24c6046..b93bdd3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -35,8 +35,7 @@ dependencies = [ [[package]] name = "rustcrypto-ff" version = "0.14.0-rc.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd2a8adb347447693cd2ba0d218c4b66c62da9b0a5672b17b981e4291ec65ff6" +source = "git+https://github.com/RustCrypto/ff#37c269c485d1803b76ac921ac8b0db83645393b4" dependencies = [ "rand_core", "subtle", diff --git a/Cargo.toml b/Cargo.toml index 361f4b9..46d93c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,3 +33,6 @@ wnaf-memuse = ["alloc", "memuse"] [badges] maintenance = { status = "actively-developed" } + +[patch.crates-io.rustcrypto-ff] +git = "https://github.com/RustCrypto/ff" diff --git a/rust-toolchain.toml b/rust-toolchain.toml index e22c344..efd3308 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] channel = "1.85.0" -components = ["clippy", "rustfmt"] +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 a41714e..a9ae8a4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,7 +9,6 @@ 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}; @@ -74,25 +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. + /// 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 { - 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 + let Ok(out) = Self::try_random(rng); + out } /// Returns an element chosen uniformly at random from the non-identity elements of - /// this group. + /// this group using a user-provided fallible RNG. /// - /// This function is non-deterministic, and samples from the user-provided RNG. - fn try_from_rng(rng: &mut R) -> Result; + /// 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; @@ -114,16 +111,14 @@ pub trait Group: } } -/// 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()) { @@ -132,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 c81a926..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; @@ -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 6de3d71..5adaf66 100644 --- a/src/wnaf.rs +++ b/src/wnaf.rs @@ -144,55 +144,29 @@ 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. /// /// This function must be provided a `table` and `wnaf` that were constructed with /// the same window size; otherwise, it may panic or produce invalid results. -#[inline] pub(crate) fn wnaf_exp(table: &[G], wnaf: &[i64]) -> G { - wnaf_multi_exp(&[table], &[wnaf]) -} - -/// Performs w-NAF multi-exponentiation using the interleaved window method, also known as -/// Straus' method. -/// -/// The key insight is that when computing this sum by means of additions and doublings, the -/// doublings can be shared by performing the additions within an inner loop. -/// -/// This function must be provided with `tables` and `wnafs` that were constructed with -/// the same window size; otherwise, it may panic or produce invalid results. -pub(crate) fn wnaf_multi_exp(tables: &[&[G]], wnafs: &[&[i64]]) -> G { - debug_assert_eq!(tables.len(), wnafs.len()); - let window_size = wnafs.iter().map(|w| w.len()).max().unwrap_or(0); - let mut result = G::identity(); + let mut found_one = false; - for i in (0..window_size).rev() { - // Only double once per iteration of the loop + for n in wnaf.iter().rev() { if found_one { result = result.double(); } - for (&table, &wnaf) in tables.iter().zip(wnafs.iter()) { - let n = wnaf.get(i).copied().unwrap_or(0); - if n != 0 { - found_one = true; + if *n != 0 { + found_one = true; - if n > 0 { - result += &table[(n / 2) as usize]; - } else { - result -= &table[((-n) / 2) as usize]; - } + if *n > 0 { + result += &table[(n / 2) as usize]; + } else { + result -= &table[((-n) / 2) as usize]; } } } @@ -341,7 +315,7 @@ impl Wnaf<(), Vec, Vec> { let window_size = 4; // Compute the wNAF form of the scalar. - wnaf_form(&mut self.scalar, scalar.to_le_repr(), window_size); + wnaf_form(&mut self.scalar, scalar.to_repr(), window_size); // Return a Wnaf object that mutably borrows the base storage location, but // immutably borrows the computed wNAF form scalar location. @@ -366,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() @@ -391,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() @@ -419,7 +393,7 @@ impl>> Wnaf { where B: AsRef<[G]>, { - wnaf_form(self.scalar.as_mut(), scalar.to_le_repr(), self.window_size); + wnaf_form(self.scalar.as_mut(), scalar.to_repr(), self.window_size); wnaf_exp(self.base.as_ref(), self.scalar.as_mut()) } } @@ -454,11 +428,11 @@ impl WnafScalar { let mut wnaf = vec![]; // Compute the w-NAF form of the scalar. - wnaf_form(&mut wnaf, scalar.to_le_repr(), WINDOW_SIZE); + wnaf_form(&mut wnaf, scalar.to_repr(), WINDOW_SIZE); WnafScalar { wnaf, - field: PhantomData::default(), + field: PhantomData, } } } @@ -525,21 +499,6 @@ impl WnafBase { WnafBase { table } } - - /// Perform a multiscalar multiplication. - pub fn multiscalar_mul<'a, I, J>(scalars: I, bases: J) -> G - where - I: Iterator>, - J: Iterator, - { - let wnafs = scalars - .map(|scalar| scalar.wnaf.as_slice()) - .collect::>(); - - let tables = bases.map(|base| base.table.as_slice()).collect::>(); - - wnaf_multi_exp(tables.as_slice(), wnafs.as_slice()) - } } impl Mul<&WnafScalar>