From e4f1c59477086d986297143964a924e6da2a4e6b Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Tue, 14 Nov 2023 17:05:43 -0700 Subject: [PATCH] k256: add `batch_normalize_generic` Adds a generic implementation of `batch_normalize` which abstracts over backing storages. This is just implemented in `k256` for now, but could potentially be further abstracted and extracted into `elliptic-curve`. --- Cargo.lock | 2 +- Cargo.toml | 5 +- k256/src/arithmetic/projective.rs | 80 +++++++++++++------------------ primeorder/src/projective.rs | 15 ++---- 4 files changed, 43 insertions(+), 59 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d2209256..900341be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -372,7 +372,7 @@ checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "elliptic-curve" version = "0.13.6" -source = "git+https://github.com/ycscaly/traits?branch=batch_invert#017fa2972f23b9e67bcbff2ccca2f1905b8f4aa3" +source = "git+https://github.com/RustCrypto/traits.git?branch=elliptic-curve/remove-references-from-batch-generics#2ef7ed3de01a034eed4e48bd75189bbdd3d0780f" dependencies = [ "base16ct", "base64ct", diff --git a/Cargo.toml b/Cargo.toml index 2428e2a6..942d8471 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,5 +17,6 @@ members = [ [profile.dev] opt-level = 2 -[patch.crates-io] -elliptic-curve = { git = "https://github.com/RustCrypto/traits" } \ No newline at end of file +[patch.crates-io.elliptic-curve] +git = "https://github.com/RustCrypto/traits.git" +branch = "elliptic-curve/remove-references-from-batch-generics" diff --git a/k256/src/arithmetic/projective.rs b/k256/src/arithmetic/projective.rs index fbabf3e5..0bccd1f4 100644 --- a/k256/src/arithmetic/projective.rs +++ b/k256/src/arithmetic/projective.rs @@ -256,69 +256,57 @@ impl From for ProjectivePoint { } } -impl BatchNormalize<&[ProjectivePoint; N]> for ProjectivePoint { +impl BatchNormalize<[ProjectivePoint; N]> for ProjectivePoint { type Output = [Self::AffineRepr; N]; - fn batch_normalize( - points: &[Self; N], - ) -> >::Output { + fn batch_normalize(points: &[Self; N]) -> [Self::AffineRepr; N] { let mut zs = [FieldElement::ONE; N]; - - for i in 0..N { - if points[i].z != FieldElement::ZERO { - // Even a single zero value will fail inversion for the entire batch. - // Put a dummy value (above `FieldElement::ONE`) so inversion succeeds - // and treat that case specially later-on. - zs[i] = points[i].z; - } - } - - // This is safe to unwrap since we assured that all elements are non-zero - let zs_inverses = >::batch_invert(&zs).unwrap(); - let mut affine_points = [AffinePoint::IDENTITY; N]; - for i in 0..N { - if points[i].z != FieldElement::ZERO { - // If the `z` coordinate is non-zero, we can use it to invert; - // otherwise it defaults to the `IDENTITY` value in initialization. - affine_points[i] = points[i].to_affine_internal(zs_inverses[i]) - } - } - + batch_normalize_generic(points, &mut zs, &mut affine_points); affine_points } } #[cfg(feature = "alloc")] -impl BatchNormalize<&[ProjectivePoint]> for ProjectivePoint { +impl BatchNormalize<[ProjectivePoint]> for ProjectivePoint { type Output = Vec; - fn batch_normalize(points: &[Self]) -> >::Output { + fn batch_normalize(points: &[Self]) -> Vec { let mut zs: Vec<_> = vec![FieldElement::ONE; points.len()]; + let mut affine_points: Vec<_> = vec![AffinePoint::IDENTITY; points.len()]; + batch_normalize_generic(points, zs.as_mut_slice(), &mut affine_points); + affine_points + } +} + +fn batch_normalize_generic(points: &P, zs: &mut Z, out: &mut O) +where + FieldElement: BatchInvert, + P: AsRef<[ProjectivePoint]> + ?Sized, + Z: AsMut<[FieldElement]> + ?Sized, + O: AsMut<[AffinePoint]> + ?Sized, +{ + let points = points.as_ref(); + let out = out.as_mut(); - for i in 0..points.len() { - if points[i].z != FieldElement::ZERO { - // Even a single zero value will fail inversion for the entire batch. - // Put a dummy value (above `FieldElement::ONE`) so inversion succeeds - // and treat that case specially later-on. - zs[i] = points[i].z; - } + for i in 0..points.len() { + if points[i].z != FieldElement::ZERO { + // Even a single zero value will fail inversion for the entire batch. + // Put a dummy value (above `FieldElement::ONE`) so inversion succeeds + // and treat that case specially later-on. + zs.as_mut()[i] = points[i].z; } + } - // This is safe to unwrap since we assured that all elements are non-zero - let zs_inverses: Vec<_> = - >::batch_invert(zs.as_slice()).unwrap(); + // This is safe to unwrap since we assured that all elements are non-zero + let zs_inverses = >::batch_invert(zs).unwrap(); - let mut affine_points: Vec<_> = vec![AffinePoint::IDENTITY; points.len()]; - for i in 0..points.len() { - if points[i].z != FieldElement::ZERO { - // If the `z` coordinate is non-zero, we can use it to invert; - // otherwise it defaults to the `IDENTITY` value in initialization. - affine_points[i] = points[i].to_affine_internal(zs_inverses[i]) - } + for i in 0..out.len() { + if points[i].z != FieldElement::ZERO { + // If the `z` coordinate is non-zero, we can use it to invert; + // otherwise it defaults to the `IDENTITY` value in initialization. + out[i] = points[i].to_affine_internal(zs_inverses.as_ref()[i]) } - - affine_points.into_iter().collect() } } diff --git a/primeorder/src/projective.rs b/primeorder/src/projective.rs index a75249ba..b4da4f1a 100644 --- a/primeorder/src/projective.rs +++ b/primeorder/src/projective.rs @@ -8,7 +8,6 @@ use core::{ iter::Sum, ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}, }; -use elliptic_curve::ops::BatchInvert; use elliptic_curve::{ bigint::{ArrayEncoding, Integer}, generic_array::ArrayLength, @@ -18,7 +17,7 @@ use elliptic_curve::{ prime::{PrimeCurve, PrimeGroup}, Group, GroupEncoding, }, - ops::{LinearCombination, MulByGenerator}, + ops::{BatchInvert, LinearCombination, MulByGenerator}, point::Double, rand_core::RngCore, sec1::{ @@ -340,16 +339,14 @@ where } } -impl BatchNormalize<&[ProjectivePoint; N]> for ProjectivePoint +impl BatchNormalize<[ProjectivePoint; N]> for ProjectivePoint where Self: Double, C: PrimeCurveParams, { type Output = [Self::AffineRepr; N]; - fn batch_normalize( - points: &[Self; N], - ) -> ; N]>>::Output { + fn batch_normalize(points: &[Self; N]) -> [Self::AffineRepr; N] { let mut zs = [C::FieldElement::ONE; N]; for i in 0..N { @@ -378,16 +375,14 @@ where } #[cfg(feature = "alloc")] -impl BatchNormalize<&[ProjectivePoint]> for ProjectivePoint +impl BatchNormalize<[ProjectivePoint]> for ProjectivePoint where Self: Double, C: PrimeCurveParams, { type Output = Vec; - fn batch_normalize( - points: &[Self; N], - ) -> ; N]>>::Output { + fn batch_normalize(points: &[Self; N]) -> Vec { let mut zs: Vec<_> = vec![C::FieldElement::ONE; points.len()]; for i in 0..points.len() {