@@ -128,6 +128,9 @@ use subtle::ConstantTimeEq;
128128#[ cfg( feature = "zeroize" ) ]
129129use zeroize:: Zeroize ;
130130
131+ #[ cfg( test) ]
132+ use proptest:: { arbitrary:: Arbitrary , strategy:: BoxedStrategy } ;
133+
131134use crate :: constants;
132135
133136use crate :: field:: FieldElement ;
@@ -1771,6 +1774,18 @@ impl CofactorGroup for EdwardsPoint {
17711774 }
17721775}
17731776
1777+ #[ cfg( test) ]
1778+ impl Arbitrary for EdwardsPoint {
1779+ type Parameters = ( ) ;
1780+ type Strategy = BoxedStrategy < Self > ;
1781+
1782+ fn arbitrary_with ( _: Self :: Parameters ) -> Self :: Strategy {
1783+ use proptest:: prelude:: * ;
1784+
1785+ Strategy :: prop_map ( any :: < Scalar > ( ) , |scalar| EdwardsPoint :: mul_base ( & scalar) ) . boxed ( )
1786+ }
1787+ }
1788+
17741789// ------------------------------------------------------------------------
17751790// Tests
17761791// ------------------------------------------------------------------------
@@ -1779,7 +1794,7 @@ impl CofactorGroup for EdwardsPoint {
17791794mod test {
17801795 use super :: * ;
17811796
1782- use rand_core :: TryRngCore ;
1797+ use proptest :: { prelude :: * , property_test } ;
17831798
17841799 #[ cfg( feature = "alloc" ) ]
17851800 use alloc:: vec:: Vec ;
@@ -2066,17 +2081,14 @@ mod test {
20662081 }
20672082
20682083 /// Check that mul_base_clamped and mul_clamped agree
2069- #[ test]
2070- fn mul_base_clamped ( ) {
2071- let mut csprng = rand_core:: OsRng ;
2084+ #[ property_test( config = ProptestConfig { cases: 50 , .. ProptestConfig :: default ( ) } ) ]
2085+ fn mul_base_clamped ( b : EdwardsPoint , a : [ [ u8 ; 32 ] ; 100 ] ) {
2086+ #[ cfg( not( feature = "precomputed-tables" ) ) ]
2087+ drop ( b) ;
20722088
20732089 // Make a random curve point in the curve. Give it torsion to make things interesting.
20742090 #[ cfg( feature = "precomputed-tables" ) ]
2075- let random_point = {
2076- let mut b = [ 0u8 ; 32 ] ;
2077- csprng. try_fill_bytes ( & mut b) . unwrap ( ) ;
2078- EdwardsPoint :: mul_base_clamped ( b) + constants:: EIGHT_TORSION [ 1 ]
2079- } ;
2091+ let random_point = b + constants:: EIGHT_TORSION [ 1 ] ;
20802092 // Make a basepoint table from the random point. We'll use this with mul_base_clamped
20812093 #[ cfg( feature = "precomputed-tables" ) ]
20822094 let random_table = EdwardsBasepointTableRadix256 :: create ( & random_point) ;
@@ -2086,28 +2098,25 @@ mod test {
20862098 // Test that mul_base_clamped and mul_clamped agree on a large integer. Even after
20872099 // clamping, this integer is not reduced mod l.
20882100 let a_bytes = [ 0xff ; 32 ] ;
2089- assert_eq ! (
2101+ prop_assert_eq ! (
20902102 EdwardsPoint :: mul_base_clamped( a_bytes) ,
20912103 constants:: ED25519_BASEPOINT_POINT . mul_clamped( a_bytes)
20922104 ) ;
20932105 #[ cfg( feature = "precomputed-tables" ) ]
2094- assert_eq ! (
2106+ prop_assert_eq ! (
20952107 random_table. mul_base_clamped( a_bytes) ,
20962108 random_point. mul_clamped( a_bytes)
20972109 ) ;
20982110
20992111 // Test agreement on random integers
2100- for _ in 0 .. 100 {
2112+ for a_bytes in a {
21012113 // This will be reduced mod l with probability l / 2^256 ≈ 6.25%
2102- let mut a_bytes = [ 0u8 ; 32 ] ;
2103- csprng. try_fill_bytes ( & mut a_bytes) . unwrap ( ) ;
2104-
2105- assert_eq ! (
2114+ prop_assert_eq ! (
21062115 EdwardsPoint :: mul_base_clamped( a_bytes) ,
21072116 constants:: ED25519_BASEPOINT_POINT . mul_clamped( a_bytes)
21082117 ) ;
21092118 #[ cfg( feature = "precomputed-tables" ) ]
2110- assert_eq ! (
2119+ prop_assert_eq ! (
21112120 random_table. mul_base_clamped( a_bytes) ,
21122121 random_point. mul_clamped( a_bytes)
21132122 ) ;
@@ -2182,22 +2191,14 @@ mod test {
21822191 }
21832192 }
21842193
2194+ #[ property_test]
21852195 #[ cfg( feature = "alloc" ) ]
2186- #[ test]
2187- fn compress_batch ( ) {
2188- let mut rng = rand:: rng ( ) ;
2189-
2190- // TODO(tarcieri): proptests?
2191- // Make some points deterministically then randomly
2192- let mut points = ( 1u64 ..16 )
2193- . map ( |n| constants:: ED25519_BASEPOINT_POINT * Scalar :: from ( n) )
2194- . collect :: < Vec < _ > > ( ) ;
2195- points. extend ( core:: iter:: repeat_with ( || EdwardsPoint :: random ( & mut rng) ) . take ( 100 ) ) ;
2196+ fn compress_batch ( points : Vec < EdwardsPoint > ) {
21962197 let compressed = EdwardsPoint :: compress_batch ( & points) ;
21972198
21982199 // Check that the batch-compressed points match the individually compressed ones
21992200 for ( point, compressed) in points. iter ( ) . zip ( & compressed) {
2200- assert_eq ! ( & point. compress( ) , compressed) ;
2201+ prop_assert_eq ! ( & point. compress( ) , compressed) ;
22012202 }
22022203 }
22032204
@@ -2238,14 +2239,11 @@ mod test {
22382239 assert ! ( P1 . compress( ) . to_bytes( ) == P2 . compress( ) . to_bytes( ) ) ;
22392240 }
22402241
2241- // A single iteration of a consistency check for MSM.
2242+ # [ property_test ]
22422243 #[ cfg( feature = "alloc" ) ]
2243- fn multiscalar_consistency_iter ( n : usize ) {
2244- let mut rng = rand:: rng ( ) ;
2245-
2244+ fn multiscalar_consistency ( xs : Vec < Scalar > ) {
22462245 // Construct random coefficients x0, ..., x_{n-1},
22472246 // followed by some extra hardcoded ones.
2248- let xs = ( 0 ..n) . map ( |_| Scalar :: random ( & mut rng) ) . collect :: < Vec < _ > > ( ) ;
22492247 let check = xs. iter ( ) . map ( |xi| xi * xi) . sum :: < Scalar > ( ) ;
22502248
22512249 // Construct points G_i = x_i * B
@@ -2258,58 +2256,13 @@ mod test {
22582256 // Compute H3 = <xs, Gs> = sum(xi^2) * B
22592257 let H3 = EdwardsPoint :: mul_base ( & check) ;
22602258
2261- assert_eq ! ( H1 , H3 ) ;
2262- assert_eq ! ( H2 , H3 ) ;
2263- }
2264-
2265- // Use different multiscalar sizes to hit different internal
2266- // parameters.
2267-
2268- #[ test]
2269- #[ cfg( feature = "alloc" ) ]
2270- fn multiscalar_consistency_n_100 ( ) {
2271- let iters = 50 ;
2272- for _ in 0 ..iters {
2273- multiscalar_consistency_iter ( 100 ) ;
2274- }
2275- }
2276-
2277- #[ test]
2278- #[ cfg( feature = "alloc" ) ]
2279- fn multiscalar_consistency_n_250 ( ) {
2280- let iters = 50 ;
2281- for _ in 0 ..iters {
2282- multiscalar_consistency_iter ( 250 ) ;
2283- }
2259+ prop_assert_eq ! ( H1 , H3 ) ;
2260+ prop_assert_eq ! ( H2 , H3 ) ;
22842261 }
22852262
2286- #[ test ]
2263+ #[ property_test ]
22872264 #[ cfg( feature = "alloc" ) ]
2288- fn multiscalar_consistency_n_500 ( ) {
2289- let iters = 50 ;
2290- for _ in 0 ..iters {
2291- multiscalar_consistency_iter ( 500 ) ;
2292- }
2293- }
2294-
2295- #[ test]
2296- #[ cfg( feature = "alloc" ) ]
2297- fn multiscalar_consistency_n_1000 ( ) {
2298- let iters = 50 ;
2299- for _ in 0 ..iters {
2300- multiscalar_consistency_iter ( 1000 ) ;
2301- }
2302- }
2303-
2304- #[ test]
2305- #[ cfg( feature = "alloc" ) ]
2306- fn batch_to_montgomery ( ) {
2307- let mut rng = rand:: rng ( ) ;
2308-
2309- let scalars = ( 0 ..128 )
2310- . map ( |_| Scalar :: random ( & mut rng) )
2311- . collect :: < Vec < _ > > ( ) ;
2312-
2265+ fn batch_to_montgomery ( scalars : Vec < Scalar > ) {
23132266 let points = scalars
23142267 . iter ( )
23152268 . map ( EdwardsPoint :: mul_base)
@@ -2320,25 +2273,19 @@ mod test {
23202273 . map ( EdwardsPoint :: to_montgomery)
23212274 . collect :: < Vec < _ > > ( ) ;
23222275
2323- for i in [ 0 , 1 , 2 , 3 , 10 , 50 , 128 ] {
2324- let invs = EdwardsPoint :: to_montgomery_batch ( & points[ ..i] ) ;
2325- assert_eq ! ( & invs, & single_monts[ ..i] ) ;
2326- }
2276+ let invs = EdwardsPoint :: to_montgomery_batch ( & points) ;
2277+ prop_assert_eq ! ( & invs, & single_monts) ;
23272278 }
23282279
2329- #[ test ]
2280+ #[ property_test ]
23302281 #[ cfg( feature = "alloc" ) ]
2331- fn vartime_precomputed_vs_nonprecomputed_multiscalar ( ) {
2332- let mut rng = rand:: rng ( ) ;
2333-
2334- let static_scalars = ( 0 ..128 )
2335- . map ( |_| Scalar :: random ( & mut rng) )
2336- . collect :: < Vec < _ > > ( ) ;
2337-
2338- let dynamic_scalars = ( 0 ..128 )
2339- . map ( |_| Scalar :: random ( & mut rng) )
2340- . collect :: < Vec < _ > > ( ) ;
2341-
2282+ fn vartime_precomputed_vs_nonprecomputed_multiscalar (
2283+ #[ strategy = any :: < Vec < Scalar > > ( ) . prop_flat_map ( |v1| {
2284+ let len = v1. len ( ) ;
2285+ ( Just ( v1) , prop:: collection:: vec ( any :: < Scalar > ( ) , len) )
2286+ } ) ]
2287+ ( static_scalars, dynamic_scalars) : ( Vec < Scalar > , Vec < Scalar > ) ,
2288+ ) {
23422289 let check_scalar: Scalar = static_scalars
23432290 . iter ( )
23442291 . chain ( dynamic_scalars. iter ( ) )
@@ -2356,8 +2303,7 @@ mod test {
23562303
23572304 let precomputation = VartimeEdwardsPrecomputation :: new ( static_points. iter ( ) ) ;
23582305
2359- assert_eq ! ( precomputation. len( ) , 128 ) ;
2360- assert ! ( !precomputation. is_empty( ) ) ;
2306+ prop_assert_eq ! ( precomputation. len( ) , static_scalars. len( ) ) ;
23612307
23622308 let P = precomputation. vartime_mixed_multiscalar_mul (
23632309 & static_scalars,
@@ -2373,8 +2319,8 @@ mod test {
23732319
23742320 let R = EdwardsPoint :: mul_base ( & check_scalar) ;
23752321
2376- assert_eq ! ( P . compress( ) , R . compress( ) ) ;
2377- assert_eq ! ( Q . compress( ) , R . compress( ) ) ;
2322+ prop_assert_eq ! ( P . compress( ) , R . compress( ) ) ;
2323+ prop_assert_eq ! ( Q . compress( ) , R . compress( ) ) ;
23782324 }
23792325
23802326 mod vartime {
0 commit comments