Skip to content

Commit

Permalink
k256+primeorder: add batch_normalize_generic (#975)
Browse files Browse the repository at this point in the history
Adds a generic implementation of `batch_normalize` which abstracts over
backing storages.
  • Loading branch information
tarcieri authored Nov 15, 2023
1 parent 8e1fff4 commit 0007c64
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 101 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.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ 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"
2 changes: 1 addition & 1 deletion k256/src/arithmetic/mul.rs
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ static GEN_LOOKUP_TABLE: Lazy<[LookupTable; 33]> = Lazy::new(precompute_gen_look
fn precompute_gen_lookup_table() -> [LookupTable; 33] {
let mut gen = ProjectivePoint::GENERATOR;
let mut res = [LookupTable::default(); 33];
#[allow(clippy::needless_range_loop)]

for i in 0..33 {
res[i] = LookupTable::from(&gen);
// We are storing tables spaced by two radix steps,
Expand Down
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
1 change: 1 addition & 0 deletions k256/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
)]
#![allow(clippy::needless_range_loop)]
#![forbid(unsafe_code)]
#![warn(
clippy::mod_module_files,
Expand Down
90 changes: 39 additions & 51 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,79 +339,68 @@ 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 {
if points[i].z != C::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 = <C::FieldElement as BatchInvert<_>>::batch_invert(&zs).unwrap();

let mut affine_points = [AffinePoint::IDENTITY; N];
for i in 0..N {
if points[i].z != C::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])
}
}

let mut affine_points = [C::AffinePoint::IDENTITY; N];
batch_normalize_generic(points, &mut zs, &mut affine_points);
affine_points
}
}

#[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()];
let mut affine_points: Vec<_> = vec![AffinePoint::IDENTITY; points.len()];
batch_normalize_generic(points, zs.as_mut_slice(), &mut affine_points);
affine_points
}
}

for i in 0..points.len() {
if points[i].z != C::FieldElement::ZERO {
// Even a single zero value will fail inversion for the entire batch.
// Put a dummy value (above `C::FieldElement::ONE`) so inversion succeeds
// and treat that case specially later-on.
zs[i] = points[i].z;
}
/// Generic implementation of batch normalization.
fn batch_normalize_generic<C, P, Z, O>(points: &P, zs: &mut Z, out: &mut O)
where
C: PrimeCurveParams,
C::FieldElement: BatchInvert<Z>,
ProjectivePoint<C>: Double,
P: AsRef<[ProjectivePoint<C>]> + ?Sized,
Z: AsMut<[C::FieldElement]> + ?Sized,
O: AsMut<[AffinePoint<C>]> + ?Sized,
{
let points = points.as_ref();
let out = out.as_mut();

for i in 0..points.len() {
if points[i].z != C::FieldElement::ZERO {
// Even a single zero value will fail inversion for the entire batch.
// Put a dummy value (above `C::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<_> =
<C::FieldElement as Invert>::batch_invert_vec(zs.as_vec()).unwrap();
// This is safe to unwrap since we assured that all elements are non-zero
let zs_inverses = <C::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 != C::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 != C::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

0 comments on commit 0007c64

Please sign in to comment.