@@ -7,10 +7,10 @@ use crate::edwards::affine::PointBytes;
77use crate :: field:: FieldElement ;
88use crate :: * ;
99use elliptic_curve:: {
10- CurveGroup , Error ,
10+ BatchNormalize , CurveGroup , Error ,
1111 array:: Array ,
1212 group:: { Group , GroupEncoding , cofactor:: CofactorGroup , prime:: PrimeGroup } ,
13- ops:: LinearCombination ,
13+ ops:: { BatchInvert , LinearCombination } ,
1414 point:: NonIdentity ,
1515} ;
1616use hash2curve:: ExpandMsgXof ;
@@ -288,6 +288,14 @@ impl CurveGroup for EdwardsPoint {
288288 fn to_affine ( & self ) -> AffinePoint {
289289 self . to_affine ( )
290290 }
291+
292+ #[ cfg( feature = "alloc" ) ]
293+ #[ inline]
294+ fn batch_normalize ( projective : & [ Self ] , affine : & mut [ Self :: AffineRepr ] ) {
295+ assert_eq ! ( projective. len( ) , affine. len( ) ) ;
296+ let mut zs = alloc:: vec![ FieldElement :: ONE ; projective. len( ) ] ;
297+ batch_normalize_generic ( projective, zs. as_mut_slice ( ) , affine) ;
298+ }
291299}
292300
293301impl EdwardsPoint {
@@ -669,11 +677,76 @@ impl<'de> serdect::serde::Deserialize<'de> for EdwardsPoint {
669677
670678impl elliptic_curve:: zeroize:: DefaultIsZeroes for EdwardsPoint { }
671679
680+ impl < const N : usize > BatchNormalize < [ EdwardsPoint ; N ] > for EdwardsPoint {
681+ type Output = [ <Self as CurveGroup >:: AffineRepr ; N ] ;
682+
683+ #[ inline]
684+ fn batch_normalize ( points : & [ Self ; N ] ) -> [ <Self as CurveGroup >:: AffineRepr ; N ] {
685+ let zs = [ FieldElement :: ONE ; N ] ;
686+ let mut affine_points = [ AffinePoint :: IDENTITY ; N ] ;
687+ batch_normalize_generic ( points, zs, & mut affine_points) ;
688+ affine_points
689+ }
690+ }
691+
692+ #[ cfg( feature = "alloc" ) ]
693+ impl BatchNormalize < [ EdwardsPoint ] > for EdwardsPoint {
694+ type Output = Vec < <Self as CurveGroup >:: AffineRepr > ;
695+
696+ #[ inline]
697+ fn batch_normalize ( points : & [ Self ] ) -> Vec < <Self as CurveGroup >:: AffineRepr > {
698+ use alloc:: vec;
699+
700+ let mut zs = vec ! [ FieldElement :: ONE ; points. len( ) ] ;
701+ let mut affine_points = vec ! [ AffinePoint :: IDENTITY ; points. len( ) ] ;
702+ batch_normalize_generic ( points, zs. as_mut_slice ( ) , & mut affine_points) ;
703+ affine_points
704+ }
705+ }
706+
707+ /// Generic implementation of batch normalization.
708+ fn batch_normalize_generic < P , Z , I , O > ( points : & P , mut zs : Z , out : & mut O )
709+ where
710+ FieldElement : BatchInvert < Z , Output = CtOption < I > > ,
711+ P : AsRef < [ EdwardsPoint ] > + ?Sized ,
712+ Z : AsMut < [ FieldElement ] > ,
713+ I : AsRef < [ FieldElement ] > ,
714+ O : AsMut < [ AffinePoint ] > + ?Sized ,
715+ {
716+ let points = points. as_ref ( ) ;
717+ let out = out. as_mut ( ) ;
718+
719+ for ( i, point) in points. iter ( ) . enumerate ( ) {
720+ // Even a single zero value will fail inversion for the entire batch.
721+ // Put a dummy value (above `FieldElement::ONE`) so inversion succeeds
722+ // and treat that case specially later-on.
723+ zs. as_mut ( ) [ i] . conditional_assign ( & point. Z , !point. Z . ct_eq ( & FieldElement :: ZERO ) ) ;
724+ }
725+
726+ // This is safe to unwrap since we assured that all elements are non-zero
727+ let zs_inverses = <FieldElement as BatchInvert < Z > >:: batch_invert ( zs)
728+ . expect ( "all elements should be non-zero" ) ;
729+
730+ for i in 0 ..out. len ( ) {
731+ // If the `z` coordinate is non-zero, we can use it to invert;
732+ // otherwise it defaults to the `IDENTITY` value.
733+ out[ i] = AffinePoint :: conditional_select (
734+ & AffinePoint {
735+ x : points[ i] . X * zs_inverses. as_ref ( ) [ i] ,
736+ y : points[ i] . Y * zs_inverses. as_ref ( ) [ i] ,
737+ } ,
738+ & AffinePoint :: IDENTITY ,
739+ points[ i] . Z . ct_eq ( & FieldElement :: ZERO ) ,
740+ ) ;
741+ }
742+ }
743+
672744#[ cfg( test) ]
673745mod tests {
674746 use super :: * ;
675747 use elliptic_curve:: Field ;
676748 use hex_literal:: hex;
749+ use rand_core:: OsRng ;
677750
678751 fn hex_to_field ( hex : & ' static str ) -> FieldElement {
679752 assert_eq ! ( hex. len( ) , 56 * 2 ) ;
@@ -968,4 +1041,33 @@ mod tests {
9681041
9691042 assert_eq ! ( computed_commitment, expected_commitment) ;
9701043 }
1044+
1045+ #[ test]
1046+ fn batch_normalize ( ) {
1047+ let points: [ EdwardsPoint ; 2 ] = [
1048+ EdwardsPoint :: try_from_rng ( & mut OsRng ) . unwrap ( ) ,
1049+ EdwardsPoint :: try_from_rng ( & mut OsRng ) . unwrap ( ) ,
1050+ ] ;
1051+
1052+ let affine_points = <EdwardsPoint as BatchNormalize < _ > >:: batch_normalize ( & points) ;
1053+
1054+ for ( point, affine_point) in points. into_iter ( ) . zip ( affine_points) {
1055+ assert_eq ! ( affine_point, point. to_affine( ) ) ;
1056+ }
1057+ }
1058+
1059+ #[ test]
1060+ #[ cfg( feature = "alloc" ) ]
1061+ fn batch_normalize_alloc ( ) {
1062+ let points = alloc:: vec![
1063+ EdwardsPoint :: try_from_rng( & mut OsRng ) . unwrap( ) ,
1064+ EdwardsPoint :: try_from_rng( & mut OsRng ) . unwrap( ) ,
1065+ ] ;
1066+
1067+ let affine_points = <EdwardsPoint as BatchNormalize < _ > >:: batch_normalize ( points. as_slice ( ) ) ;
1068+
1069+ for ( point, affine_point) in points. into_iter ( ) . zip ( affine_points) {
1070+ assert_eq ! ( affine_point, point. to_affine( ) ) ;
1071+ }
1072+ }
9711073}
0 commit comments