@@ -10,10 +10,10 @@ use crate::edwards::affine::PointBytes;
10
10
use crate :: field:: FieldElement ;
11
11
use crate :: * ;
12
12
use elliptic_curve:: {
13
- CurveGroup , Error ,
13
+ BatchNormalize , CurveGroup , Error ,
14
14
array:: Array ,
15
15
group:: { Group , GroupEncoding , cofactor:: CofactorGroup , prime:: PrimeGroup } ,
16
- ops:: LinearCombination ,
16
+ ops:: { BatchInvert , LinearCombination } ,
17
17
point:: NonIdentity ,
18
18
} ;
19
19
use hash2curve:: ExpandMsgXof ;
@@ -296,6 +296,14 @@ impl CurveGroup for EdwardsPoint {
296
296
fn to_affine ( & self ) -> AffinePoint {
297
297
self . to_affine ( )
298
298
}
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
+ }
299
307
}
300
308
301
309
impl EdwardsPoint {
@@ -714,11 +722,76 @@ impl<'de> serdect::serde::Deserialize<'de> for EdwardsPoint {
714
722
715
723
impl elliptic_curve:: zeroize:: DefaultIsZeroes for EdwardsPoint { }
716
724
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
+
717
789
#[ cfg( test) ]
718
790
mod tests {
719
791
use super :: * ;
720
792
use elliptic_curve:: Field ;
721
793
use hex_literal:: hex;
794
+ use rand_core:: OsRng ;
722
795
723
796
fn hex_to_field ( hex : & ' static str ) -> FieldElement {
724
797
assert_eq ! ( hex. len( ) , 56 * 2 ) ;
@@ -1013,4 +1086,33 @@ mod tests {
1013
1086
1014
1087
assert_eq ! ( computed_commitment, expected_commitment) ;
1015
1088
}
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
+ }
1016
1118
}
0 commit comments