Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 105 additions & 2 deletions ed448-goldilocks/src/decaf/points.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
use crate::curve::twedwards::affine::AffinePoint as InnerAffinePoint;
use crate::curve::twedwards::extended::ExtendedPoint;
use crate::field::FieldElement;
use crate::*;

use elliptic_curve::{
CurveGroup, Error, Group,
BatchNormalize, CurveGroup, Error, Group,
array::Array,
consts::U56,
group::{GroupEncoding, cofactor::CofactorGroup, prime::PrimeGroup},
ops::LinearCombination,
ops::{BatchInvert, LinearCombination},
point::NonIdentity,
};

Expand Down Expand Up @@ -249,6 +250,14 @@ impl CurveGroup for DecafPoint {
fn to_affine(&self) -> Self::AffineRepr {
DecafAffinePoint(self.0.to_extensible().to_affine())
}

#[cfg(feature = "alloc")]
#[inline]
fn batch_normalize(projective: &[Self], affine: &mut [Self::AffineRepr]) {
assert_eq!(projective.len(), affine.len());
let mut zs = alloc::vec![FieldElement::ONE; projective.len()];
batch_normalize_generic(projective, zs.as_mut_slice(), affine);
}
}

impl From<EdwardsPoint> for DecafPoint {
Expand Down Expand Up @@ -595,11 +604,76 @@ impl From<NonIdentity<DecafPoint>> for DecafPoint {
}
}

impl<const N: usize> BatchNormalize<[DecafPoint; N]> for DecafPoint {
type Output = [<Self as CurveGroup>::AffineRepr; N];

#[inline]
fn batch_normalize(points: &[Self; N]) -> [<Self as CurveGroup>::AffineRepr; N] {
let zs = [FieldElement::ONE; N];
let mut affine_points = [DecafAffinePoint::IDENTITY; N];
batch_normalize_generic(points, zs, &mut affine_points);
affine_points
}
}

#[cfg(feature = "alloc")]
impl BatchNormalize<[DecafPoint]> for DecafPoint {
type Output = Vec<<Self as CurveGroup>::AffineRepr>;

#[inline]
fn batch_normalize(points: &[Self]) -> Vec<<Self as CurveGroup>::AffineRepr> {
use alloc::vec;

let mut zs = vec![FieldElement::ONE; points.len()];
let mut affine_points = vec![DecafAffinePoint::IDENTITY; points.len()];
batch_normalize_generic(points, zs.as_mut_slice(), &mut affine_points);
affine_points
}
}

/// Generic implementation of batch normalization.
fn batch_normalize_generic<P, Z, I, O>(points: &P, mut zs: Z, out: &mut O)
where
FieldElement: BatchInvert<Z, Output = CtOption<I>>,
P: AsRef<[DecafPoint]> + ?Sized,
Z: AsMut<[FieldElement]>,
I: AsRef<[FieldElement]>,
O: AsMut<[DecafAffinePoint]> + ?Sized,
{
let points = points.as_ref();
let out = out.as_mut();

for (i, point) in points.iter().enumerate() {
// 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].conditional_assign(&point.0.Z, !point.0.Z.ct_eq(&FieldElement::ZERO));
}

// This is safe to unwrap since we assured that all elements are non-zero
let zs_inverses = <FieldElement as BatchInvert<Z>>::batch_invert(zs)
.expect("all elements should be non-zero");

for i in 0..out.len() {
// If the `z` coordinate is non-zero, we can use it to invert;
// otherwise it defaults to the `IDENTITY` value.
out[i] = DecafAffinePoint::conditional_select(
&DecafAffinePoint(InnerAffinePoint {
x: points[i].0.X * zs_inverses.as_ref()[i],
y: points[i].0.Y * zs_inverses.as_ref()[i],
}),
&DecafAffinePoint::IDENTITY,
points[i].0.Z.ct_eq(&FieldElement::ZERO),
);
}
}

#[cfg(test)]
mod test {
use super::*;
use crate::TWISTED_EDWARDS_BASE_POINT;
use hash2curve::ExpandMsgXof;
use rand_core::OsRng;
use sha3::Shake256;

#[test]
Expand Down Expand Up @@ -797,4 +871,33 @@ mod test {
// assert_eq!(rhs, expected);
// }
// }

#[test]
fn batch_normalize() {
let points: [DecafPoint; 2] = [
DecafPoint::try_from_rng(&mut OsRng).unwrap(),
DecafPoint::try_from_rng(&mut OsRng).unwrap(),
];

let affine_points = <DecafPoint as BatchNormalize<_>>::batch_normalize(&points);

for (point, affine_point) in points.into_iter().zip(affine_points) {
assert_eq!(affine_point, point.to_affine());
}
}

#[test]
#[cfg(feature = "alloc")]
fn batch_normalize_alloc() {
let points = alloc::vec![
DecafPoint::try_from_rng(&mut OsRng).unwrap(),
DecafPoint::try_from_rng(&mut OsRng).unwrap(),
];

let affine_points = <DecafPoint as BatchNormalize<_>>::batch_normalize(points.as_slice());

for (point, affine_point) in points.into_iter().zip(affine_points) {
assert_eq!(affine_point, point.to_affine());
}
}
}