@@ -7,10 +7,10 @@ use crate::edwards::affine::PointBytes;
7
7
use crate :: field:: FieldElement ;
8
8
use crate :: * ;
9
9
use elliptic_curve:: {
10
- CurveGroup , Error ,
10
+ BatchNormalize , CurveGroup , Error ,
11
11
array:: Array ,
12
12
group:: { Group , GroupEncoding , cofactor:: CofactorGroup , prime:: PrimeGroup } ,
13
- ops:: LinearCombination ,
13
+ ops:: { BatchInvert , LinearCombination } ,
14
14
point:: NonIdentity ,
15
15
} ;
16
16
use hash2curve:: ExpandMsgXof ;
@@ -293,6 +293,14 @@ impl CurveGroup for EdwardsPoint {
293
293
fn to_affine ( & self ) -> AffinePoint {
294
294
self . to_affine ( )
295
295
}
296
+
297
+ #[ cfg( feature = "alloc" ) ]
298
+ #[ inline]
299
+ fn batch_normalize ( projective : & [ Self ] , affine : & mut [ Self :: AffineRepr ] ) {
300
+ assert_eq ! ( projective. len( ) , affine. len( ) ) ;
301
+ let mut zs = alloc:: vec![ FieldElement :: ONE ; projective. len( ) ] ;
302
+ batch_normalize_generic ( projective, zs. as_mut_slice ( ) , affine) ;
303
+ }
296
304
}
297
305
298
306
impl EdwardsPoint {
@@ -674,11 +682,76 @@ impl<'de> serdect::serde::Deserialize<'de> for EdwardsPoint {
674
682
675
683
impl elliptic_curve:: zeroize:: DefaultIsZeroes for EdwardsPoint { }
676
684
685
+ impl < const N : usize > BatchNormalize < [ EdwardsPoint ; N ] > for EdwardsPoint {
686
+ type Output = [ <Self as CurveGroup >:: AffineRepr ; N ] ;
687
+
688
+ #[ inline]
689
+ fn batch_normalize ( points : & [ Self ; N ] ) -> [ <Self as CurveGroup >:: AffineRepr ; N ] {
690
+ let zs = [ FieldElement :: ONE ; N ] ;
691
+ let mut affine_points = [ AffinePoint :: IDENTITY ; N ] ;
692
+ batch_normalize_generic ( points, zs, & mut affine_points) ;
693
+ affine_points
694
+ }
695
+ }
696
+
697
+ #[ cfg( feature = "alloc" ) ]
698
+ impl BatchNormalize < [ EdwardsPoint ] > for EdwardsPoint {
699
+ type Output = Vec < <Self as CurveGroup >:: AffineRepr > ;
700
+
701
+ #[ inline]
702
+ fn batch_normalize ( points : & [ Self ] ) -> Vec < <Self as CurveGroup >:: AffineRepr > {
703
+ use alloc:: vec;
704
+
705
+ let mut zs = vec ! [ FieldElement :: ONE ; points. len( ) ] ;
706
+ let mut affine_points = vec ! [ AffinePoint :: IDENTITY ; points. len( ) ] ;
707
+ batch_normalize_generic ( points, zs. as_mut_slice ( ) , & mut affine_points) ;
708
+ affine_points
709
+ }
710
+ }
711
+
712
+ /// Generic implementation of batch normalization.
713
+ fn batch_normalize_generic < P , Z , I , O > ( points : & P , mut zs : Z , out : & mut O )
714
+ where
715
+ FieldElement : BatchInvert < Z , Output = CtOption < I > > ,
716
+ P : AsRef < [ EdwardsPoint ] > + ?Sized ,
717
+ Z : AsMut < [ FieldElement ] > ,
718
+ I : AsRef < [ FieldElement ] > ,
719
+ O : AsMut < [ AffinePoint ] > + ?Sized ,
720
+ {
721
+ let points = points. as_ref ( ) ;
722
+ let out = out. as_mut ( ) ;
723
+
724
+ for ( i, point) in points. iter ( ) . enumerate ( ) {
725
+ // Even a single zero value will fail inversion for the entire batch.
726
+ // Put a dummy value (above `FieldElement::ONE`) so inversion succeeds
727
+ // and treat that case specially later-on.
728
+ zs. as_mut ( ) [ i] . conditional_assign ( & point. Z , !point. Z . ct_eq ( & FieldElement :: ZERO ) ) ;
729
+ }
730
+
731
+ // This is safe to unwrap since we assured that all elements are non-zero
732
+ let zs_inverses = <FieldElement as BatchInvert < Z > >:: batch_invert ( zs)
733
+ . expect ( "all elements should be non-zero" ) ;
734
+
735
+ for i in 0 ..out. len ( ) {
736
+ // If the `z` coordinate is non-zero, we can use it to invert;
737
+ // otherwise it defaults to the `IDENTITY` value.
738
+ out[ i] = AffinePoint :: conditional_select (
739
+ & AffinePoint {
740
+ x : points[ i] . X * zs_inverses. as_ref ( ) [ i] ,
741
+ y : points[ i] . Y * zs_inverses. as_ref ( ) [ i] ,
742
+ } ,
743
+ & AffinePoint :: IDENTITY ,
744
+ points[ i] . Z . ct_eq ( & FieldElement :: ZERO ) ,
745
+ ) ;
746
+ }
747
+ }
748
+
677
749
#[ cfg( test) ]
678
750
mod tests {
679
751
use super :: * ;
680
752
use elliptic_curve:: Field ;
681
753
use hex_literal:: hex;
754
+ use rand_core:: OsRng ;
682
755
683
756
fn hex_to_field ( hex : & ' static str ) -> FieldElement {
684
757
assert_eq ! ( hex. len( ) , 56 * 2 ) ;
@@ -973,4 +1046,33 @@ mod tests {
973
1046
974
1047
assert_eq ! ( computed_commitment, expected_commitment) ;
975
1048
}
1049
+
1050
+ #[ test]
1051
+ fn batch_normalize ( ) {
1052
+ let points: [ EdwardsPoint ; 2 ] = [
1053
+ EdwardsPoint :: try_from_rng ( & mut OsRng ) . unwrap ( ) ,
1054
+ EdwardsPoint :: try_from_rng ( & mut OsRng ) . unwrap ( ) ,
1055
+ ] ;
1056
+
1057
+ let affine_points = <EdwardsPoint as BatchNormalize < _ > >:: batch_normalize ( & points) ;
1058
+
1059
+ for ( point, affine_point) in points. into_iter ( ) . zip ( affine_points) {
1060
+ assert_eq ! ( affine_point, point. to_affine( ) ) ;
1061
+ }
1062
+ }
1063
+
1064
+ #[ test]
1065
+ #[ cfg( feature = "alloc" ) ]
1066
+ fn batch_normalize_alloc ( ) {
1067
+ let points = alloc:: vec![
1068
+ EdwardsPoint :: try_from_rng( & mut OsRng ) . unwrap( ) ,
1069
+ EdwardsPoint :: try_from_rng( & mut OsRng ) . unwrap( ) ,
1070
+ ] ;
1071
+
1072
+ let affine_points = <EdwardsPoint as BatchNormalize < _ > >:: batch_normalize ( points. as_slice ( ) ) ;
1073
+
1074
+ for ( point, affine_point) in points. into_iter ( ) . zip ( affine_points) {
1075
+ assert_eq ! ( affine_point, point. to_affine( ) ) ;
1076
+ }
1077
+ }
976
1078
}
0 commit comments