@@ -10,10 +10,10 @@ use crate::edwards::affine::PointBytes;
1010use crate :: field:: FieldElement ;
1111use crate :: * ;
1212use elliptic_curve:: {
13- CurveGroup , Error ,
13+ BatchNormalize , CurveGroup , Error ,
1414 array:: Array ,
1515 group:: { Group , GroupEncoding , cofactor:: CofactorGroup , prime:: PrimeGroup } ,
16- ops:: LinearCombination ,
16+ ops:: { BatchInvert , LinearCombination } ,
1717 point:: NonIdentity ,
1818} ;
1919use hash2curve:: ExpandMsgXof ;
@@ -296,6 +296,14 @@ impl CurveGroup for EdwardsPoint {
296296 fn to_affine ( & self ) -> AffinePoint {
297297 self . to_affine ( )
298298 }
299+
300+ #[ cfg( feature = "alloc" ) ]
301+ #[ inline]
302+ fn batch_normalize ( projective : & [ Self ] , affine : & mut [ Self :: AffineRepr ] ) {
303+ assert_eq ! ( projective. len( ) , affine. len( ) ) ;
304+ let mut zs = alloc:: vec![ FieldElement :: ONE ; projective. len( ) ] ;
305+ batch_normalize_generic ( projective, zs. as_mut_slice ( ) , affine) ;
306+ }
299307}
300308
301309impl EdwardsPoint {
@@ -714,11 +722,76 @@ impl<'de> serdect::serde::Deserialize<'de> for EdwardsPoint {
714722
715723impl elliptic_curve:: zeroize:: DefaultIsZeroes for EdwardsPoint { }
716724
725+ impl < const N : usize > BatchNormalize < [ EdwardsPoint ; N ] > for EdwardsPoint {
726+ type Output = [ <Self as CurveGroup >:: AffineRepr ; N ] ;
727+
728+ #[ inline]
729+ fn batch_normalize ( points : & [ Self ; N ] ) -> [ <Self as CurveGroup >:: AffineRepr ; N ] {
730+ let zs = [ FieldElement :: ONE ; N ] ;
731+ let mut affine_points = [ AffinePoint :: IDENTITY ; N ] ;
732+ batch_normalize_generic ( points, zs, & mut affine_points) ;
733+ affine_points
734+ }
735+ }
736+
737+ #[ cfg( feature = "alloc" ) ]
738+ impl BatchNormalize < [ EdwardsPoint ] > for EdwardsPoint {
739+ type Output = Vec < <Self as CurveGroup >:: AffineRepr > ;
740+
741+ #[ inline]
742+ fn batch_normalize ( points : & [ Self ] ) -> Vec < <Self as CurveGroup >:: AffineRepr > {
743+ use alloc:: vec;
744+
745+ let mut zs = vec ! [ FieldElement :: ONE ; points. len( ) ] ;
746+ let mut affine_points = vec ! [ AffinePoint :: IDENTITY ; points. len( ) ] ;
747+ batch_normalize_generic ( points, zs. as_mut_slice ( ) , & mut affine_points) ;
748+ affine_points
749+ }
750+ }
751+
752+ /// Generic implementation of batch normalization.
753+ fn batch_normalize_generic < P , Z , I , O > ( points : & P , mut zs : Z , out : & mut O )
754+ where
755+ FieldElement : BatchInvert < Z , Output = CtOption < I > > ,
756+ P : AsRef < [ EdwardsPoint ] > + ?Sized ,
757+ Z : AsMut < [ FieldElement ] > ,
758+ I : AsRef < [ FieldElement ] > ,
759+ O : AsMut < [ AffinePoint ] > + ?Sized ,
760+ {
761+ let points = points. as_ref ( ) ;
762+ let out = out. as_mut ( ) ;
763+
764+ for ( i, point) in points. iter ( ) . enumerate ( ) {
765+ // Even a single zero value will fail inversion for the entire batch.
766+ // Put a dummy value (above `FieldElement::ONE`) so inversion succeeds
767+ // and treat that case specially later-on.
768+ zs. as_mut ( ) [ i] . conditional_assign ( & point. Z , !point. Z . ct_eq ( & FieldElement :: ZERO ) ) ;
769+ }
770+
771+ // This is safe to unwrap since we assured that all elements are non-zero
772+ let zs_inverses = <FieldElement as BatchInvert < Z > >:: batch_invert ( zs)
773+ . expect ( "all elements should be non-zero" ) ;
774+
775+ for i in 0 ..out. len ( ) {
776+ // If the `z` coordinate is non-zero, we can use it to invert;
777+ // otherwise it defaults to the `IDENTITY` value.
778+ out[ i] = AffinePoint :: conditional_select (
779+ & AffinePoint {
780+ x : points[ i] . X * zs_inverses. as_ref ( ) [ i] ,
781+ y : points[ i] . Y * zs_inverses. as_ref ( ) [ i] ,
782+ } ,
783+ & AffinePoint :: IDENTITY ,
784+ points[ i] . Z . ct_eq ( & FieldElement :: ZERO ) ,
785+ ) ;
786+ }
787+ }
788+
717789#[ cfg( test) ]
718790mod tests {
719791 use super :: * ;
720792 use elliptic_curve:: Field ;
721793 use hex_literal:: hex;
794+ use rand_core:: OsRng ;
722795
723796 fn hex_to_field ( hex : & ' static str ) -> FieldElement {
724797 assert_eq ! ( hex. len( ) , 56 * 2 ) ;
@@ -1013,4 +1086,33 @@ mod tests {
10131086
10141087 assert_eq ! ( computed_commitment, expected_commitment) ;
10151088 }
1089+
1090+ #[ test]
1091+ fn batch_normalize ( ) {
1092+ let points: [ EdwardsPoint ; 2 ] = [
1093+ EdwardsPoint :: try_from_rng ( & mut OsRng ) . unwrap ( ) ,
1094+ EdwardsPoint :: try_from_rng ( & mut OsRng ) . unwrap ( ) ,
1095+ ] ;
1096+
1097+ let affine_points = <EdwardsPoint as BatchNormalize < _ > >:: batch_normalize ( & points) ;
1098+
1099+ for ( point, affine_point) in points. into_iter ( ) . zip ( affine_points) {
1100+ assert_eq ! ( affine_point, point. to_affine( ) ) ;
1101+ }
1102+ }
1103+
1104+ #[ test]
1105+ #[ cfg( feature = "alloc" ) ]
1106+ fn batch_normalize_alloc ( ) {
1107+ let points = alloc:: vec![
1108+ EdwardsPoint :: try_from_rng( & mut OsRng ) . unwrap( ) ,
1109+ EdwardsPoint :: try_from_rng( & mut OsRng ) . unwrap( ) ,
1110+ ] ;
1111+
1112+ let affine_points = <EdwardsPoint as BatchNormalize < _ > >:: batch_normalize ( points. as_slice ( ) ) ;
1113+
1114+ for ( point, affine_point) in points. into_iter ( ) . zip ( affine_points) {
1115+ assert_eq ! ( affine_point, point. to_affine( ) ) ;
1116+ }
1117+ }
10161118}
0 commit comments