Skip to content

Commit

Permalink
k256: add batch_normalize_generic
Browse files Browse the repository at this point in the history
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`.
  • Loading branch information
tarcieri committed Nov 15, 2023
1 parent 8e1fff4 commit e4f1c59
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 59 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ members = [
[profile.dev]
opt-level = 2

[patch.crates-io]
elliptic-curve = { git = "https://github.com/RustCrypto/traits" }
[patch.crates-io.elliptic-curve]
git = "https://github.com/RustCrypto/traits.git"
branch = "elliptic-curve/remove-references-from-batch-generics"
80 changes: 34 additions & 46 deletions k256/src/arithmetic/projective.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,69 +256,57 @@ impl From<AffinePoint> for ProjectivePoint {
}
}

impl<const N: usize> BatchNormalize<&[ProjectivePoint; N]> for ProjectivePoint {
impl<const N: usize> BatchNormalize<[ProjectivePoint; N]> for ProjectivePoint {
type Output = [Self::AffineRepr; N];

fn batch_normalize(
points: &[Self; N],
) -> <Self as BatchNormalize<&[ProjectivePoint; 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 = <FieldElement as BatchInvert<_>>::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<Self::AffineRepr>;

fn batch_normalize(points: &[Self]) -> <Self as BatchNormalize<&[ProjectivePoint]>>::Output {
fn batch_normalize(points: &[Self]) -> Vec<Self::AffineRepr> {
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<P, Z, O>(points: &P, zs: &mut Z, out: &mut O)
where
FieldElement: BatchInvert<Z>,
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<_> =
<FieldElement as BatchInvert<_>>::batch_invert(zs.as_slice()).unwrap();
// This is safe to unwrap since we assured that all elements are non-zero
let zs_inverses = <FieldElement as BatchInvert<Z>>::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()
}
}

Expand Down
15 changes: 5 additions & 10 deletions primeorder/src/projective.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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::{
Expand Down Expand Up @@ -340,16 +339,14 @@ where
}
}

impl<const N: usize, C> BatchNormalize<&[ProjectivePoint<C>; N]> for ProjectivePoint<C>
impl<const N: usize, C> BatchNormalize<[ProjectivePoint<C>; N]> for ProjectivePoint<C>
where
Self: Double,
C: PrimeCurveParams,
{
type Output = [Self::AffineRepr; N];

fn batch_normalize(
points: &[Self; N],
) -> <Self as BatchNormalize<&[ProjectivePoint<C>; N]>>::Output {
fn batch_normalize(points: &[Self; N]) -> [Self::AffineRepr; N] {
let mut zs = [C::FieldElement::ONE; N];

for i in 0..N {
Expand Down Expand Up @@ -378,16 +375,14 @@ where
}

#[cfg(feature = "alloc")]
impl<C> BatchNormalize<&[ProjectivePoint<C>]> for ProjectivePoint<C>
impl<C> BatchNormalize<[ProjectivePoint<C>]> for ProjectivePoint<C>
where
Self: Double,
C: PrimeCurveParams,
{
type Output = Vec<Self::AffineRepr>;

fn batch_normalize(
points: &[Self; N],
) -> <Self as BatchNormalize<&[ProjectivePoint<C>; N]>>::Output {
fn batch_normalize(points: &[Self; N]) -> Vec<Self::AffineRepr> {
let mut zs: Vec<_> = vec![C::FieldElement::ONE; points.len()];

for i in 0..points.len() {
Expand Down

0 comments on commit e4f1c59

Please sign in to comment.