93
93
// affine and projective cakes and eat both of them too.
94
94
#![ allow( non_snake_case) ]
95
95
96
+ use cfg_if:: cfg_if;
96
97
use core:: array:: TryFromSliceError ;
97
98
use core:: borrow:: Borrow ;
98
99
use core:: fmt:: Debug ;
@@ -101,8 +102,6 @@ use core::ops::{Add, Neg, Sub};
101
102
use core:: ops:: { AddAssign , SubAssign } ;
102
103
use core:: ops:: { Mul , MulAssign } ;
103
104
104
- use cfg_if:: cfg_if;
105
-
106
105
#[ cfg( feature = "digest" ) ]
107
106
use digest:: { generic_array:: typenum:: U64 , Digest } ;
108
107
@@ -112,7 +111,7 @@ use {
112
111
subtle:: CtOption ,
113
112
} ;
114
113
115
- #[ cfg( feature = "group" ) ]
114
+ #[ cfg( any ( test , feature = "rand_core" ) ) ]
116
115
use rand_core:: RngCore ;
117
116
118
117
use subtle:: Choice ;
@@ -151,6 +150,8 @@ use crate::traits::{Identity, IsIdentity};
151
150
use crate :: traits:: MultiscalarMul ;
152
151
#[ cfg( feature = "alloc" ) ]
153
152
use crate :: traits:: { VartimeMultiscalarMul , VartimePrecomputedMultiscalarMul } ;
153
+ #[ cfg( feature = "alloc" ) ]
154
+ use alloc:: vec:: Vec ;
154
155
155
156
// ------------------------------------------------------------------------
156
157
// Compressed points
@@ -567,9 +568,31 @@ impl EdwardsPoint {
567
568
let recip = self . Z . invert ( ) ;
568
569
let x = & self . X * & recip;
569
570
let y = & self . Y * & recip;
570
- let mut s: [ u8 ; 32 ] ;
571
+ Self :: compress_affine ( x, y)
572
+ }
571
573
572
- s = y. as_bytes ( ) ;
574
+ /// Compress several `EdwardsPoint`s into `CompressedEdwardsY` format, using a batch inversion
575
+ /// for a significant speedup.
576
+ #[ cfg( feature = "alloc" ) ]
577
+ pub fn compress_batch ( inputs : & [ EdwardsPoint ] ) -> Vec < CompressedEdwardsY > {
578
+ let mut zs = inputs. iter ( ) . map ( |input| input. Z ) . collect :: < Vec < _ > > ( ) ;
579
+ FieldElement :: batch_invert ( & mut zs) ;
580
+
581
+ inputs
582
+ . iter ( )
583
+ . zip ( & zs)
584
+ . map ( |( input, recip) | {
585
+ let x = & input. X * recip;
586
+ let y = & input. Y * recip;
587
+ Self :: compress_affine ( x, y)
588
+ } )
589
+ . collect ( )
590
+ }
591
+
592
+ /// Compress affine Edwards coordinates into `CompressedEdwardsY` format.
593
+ #[ inline]
594
+ fn compress_affine ( x : FieldElement , y : FieldElement ) -> CompressedEdwardsY {
595
+ let mut s = y. as_bytes ( ) ;
573
596
s[ 31 ] ^= x. is_negative ( ) . unwrap_u8 ( ) << 7 ;
574
597
CompressedEdwardsY ( s)
575
598
}
@@ -605,6 +628,33 @@ impl EdwardsPoint {
605
628
. expect ( "Montgomery conversion to Edwards point in Elligator failed" )
606
629
. mul_by_cofactor ( )
607
630
}
631
+
632
+ /// Return an `EdwardsPoint` chosen uniformly at random using a user-provided RNG.
633
+ ///
634
+ /// # Inputs
635
+ ///
636
+ /// * `rng`: any RNG which implements `RngCore`
637
+ ///
638
+ /// # Returns
639
+ ///
640
+ /// A random `EdwardsPoint`.
641
+ ///
642
+ /// # Implementation
643
+ ///
644
+ /// Uses rejection sampling, generating a random `CompressedEdwardsY` and then attempting point
645
+ /// decompression, rejecting invalid points.
646
+ #[ cfg( any( test, feature = "rand_core" ) ) ]
647
+ pub fn random ( mut rng : impl RngCore ) -> Self {
648
+ let mut repr = CompressedEdwardsY ( [ 0u8 ; 32 ] ) ;
649
+ loop {
650
+ rng. fill_bytes ( & mut repr. 0 ) ;
651
+ if let Some ( p) = repr. decompress ( ) {
652
+ if !IsIdentity :: is_identity ( & p) {
653
+ break p;
654
+ }
655
+ }
656
+ }
657
+ }
608
658
}
609
659
610
660
// ------------------------------------------------------------------------
@@ -1291,16 +1341,9 @@ impl Debug for EdwardsPoint {
1291
1341
impl group:: Group for EdwardsPoint {
1292
1342
type Scalar = Scalar ;
1293
1343
1294
- fn random ( mut rng : impl RngCore ) -> Self {
1295
- let mut repr = CompressedEdwardsY ( [ 0u8 ; 32 ] ) ;
1296
- loop {
1297
- rng. fill_bytes ( & mut repr. 0 ) ;
1298
- if let Some ( p) = repr. decompress ( ) {
1299
- if !IsIdentity :: is_identity ( & p) {
1300
- break p;
1301
- }
1302
- }
1303
- }
1344
+ fn random ( rng : impl RngCore ) -> Self {
1345
+ // Call the inherent `pub fn random` defined above
1346
+ Self :: random ( rng)
1304
1347
}
1305
1348
1306
1349
fn identity ( ) -> Self {
@@ -2019,6 +2062,31 @@ mod test {
2019
2062
EdwardsPoint :: identity( ) . compress( ) ,
2020
2063
CompressedEdwardsY :: identity( )
2021
2064
) ;
2065
+
2066
+ #[ cfg( feature = "alloc" ) ]
2067
+ {
2068
+ let compressed = EdwardsPoint :: compress_batch ( & [ EdwardsPoint :: identity ( ) ] ) ;
2069
+ assert_eq ! ( & compressed, & [ CompressedEdwardsY :: identity( ) ] ) ;
2070
+ }
2071
+ }
2072
+
2073
+ #[ cfg( feature = "alloc" ) ]
2074
+ #[ test]
2075
+ fn compress_batch ( ) {
2076
+ let mut rng = rand:: thread_rng ( ) ;
2077
+
2078
+ // TODO(tarcieri): proptests?
2079
+ // Make some points deterministically then randomly
2080
+ let mut points = ( 1u64 ..16 )
2081
+ . map ( |n| constants:: ED25519_BASEPOINT_POINT * Scalar :: from ( n) )
2082
+ . collect :: < Vec < _ > > ( ) ;
2083
+ points. extend ( core:: iter:: repeat_with ( || EdwardsPoint :: random ( & mut rng) ) . take ( 100 ) ) ;
2084
+ let compressed = EdwardsPoint :: compress_batch ( & points) ;
2085
+
2086
+ // Check that the batch-compressed points match the individually compressed ones
2087
+ for ( point, compressed) in points. iter ( ) . zip ( & compressed) {
2088
+ assert_eq ! ( & point. compress( ) , compressed) ;
2089
+ }
2022
2090
}
2023
2091
2024
2092
#[ test]
0 commit comments