@@ -7,7 +7,7 @@ use crate::curve::scalar_mul::variable_base;
7
7
use crate :: curve:: twedwards:: IsogenyMap ;
8
8
use crate :: curve:: twedwards:: extended:: ExtendedPoint as TwistedExtendedPoint ;
9
9
use crate :: edwards:: affine:: PointBytes ;
10
- use crate :: field:: FieldElement ;
10
+ use crate :: field:: { ConstMontyType , FieldElement } ;
11
11
use crate :: * ;
12
12
use elliptic_curve:: {
13
13
BatchNormalize , CurveGroup , Error ,
@@ -474,8 +474,57 @@ impl EdwardsPoint {
474
474
/// prime-order subgroup;
475
475
/// * `false` if `self` has a nonzero torsion component and is not
476
476
/// in the prime-order subgroup.
477
+ // See https://eprint.iacr.org/2022/1164.
477
478
pub fn is_torsion_free ( & self ) -> Choice {
478
- ( self * EdwardsScalar :: new ( * ORDER ) ) . ct_eq ( & Self :: IDENTITY )
479
+ const A : FieldElement = FieldElement ( ConstMontyType :: new ( & U448 :: from_be_hex (
480
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffffffffffffffffffffffffffffffffffffffffffffffffeceaf" ,
481
+ ) ) ) ;
482
+ const A1 : FieldElement = FieldElement ( ConstMontyType :: new ( & U448 :: from_u64 ( 156320 ) ) ) ;
483
+ const MINUS_SQRT_B1 : FieldElement = FieldElement ( ConstMontyType :: new ( & U448 :: from_be_hex (
484
+ "749a7410536c225f1025ca374176557d7839611d691caad26d74a1fca5cfad15f196642c0a4484b67f321025577cc6b5a6f443c2eaa36327" ,
485
+ ) ) ) ;
486
+
487
+ let mut e = self . X * ( self . Z - self . Y ) ;
488
+ let ee = e. square ( ) ;
489
+ let mut u = FieldElement :: A_PLUS_TWO_OVER_FOUR * ( self . Z + self . Y ) * e * self . X ;
490
+ let w = self . Z . double ( ) * ( self . Z - self . Y ) ;
491
+
492
+ let u2 = u. double ( ) . double ( ) ;
493
+ let w2 = w. double ( ) ;
494
+
495
+ let mut w1 = u2. sqrt ( ) ;
496
+ let mut ok = w1. square ( ) . ct_eq ( & u2) ;
497
+ let u1 = ( u2 - A1 * ee - w1 * w2) . div_by_2 ( ) ;
498
+
499
+ // If `u1` happens not to be a square, then `sqrt(u1)` returns `sqrt(-u1)`
500
+ // in that case (since we are in a finite field GF(q) with q = 3 mod 4,
501
+ // if `u1` is not a square then `-u1` must be a square). In such a case, we
502
+ // should replace `(u1,w1)` with `((B1*e^4)/u1, -w1)`. To avoid the division,
503
+ // we instead switch to an isomorphic curve; namely:
504
+ // u2 = B1*(e^4)*u1
505
+ // w2 = -w1*u1
506
+ // e2 = e*u1
507
+ // Then:
508
+ // w = sqrt(u2) = sqrt(-B1)*(e^2)*sqrt(-u1)
509
+ // u = (w^2 - A*e^2 - w*w1)/2
510
+ let mut w = u1. sqrt ( ) ;
511
+ let u1_is_square = w. square ( ) . ct_eq ( & u1) ;
512
+ w1. conditional_assign ( & -( w1 * u1) , !u1_is_square) ;
513
+ e. conditional_assign ( & ( e * u1) , !u1_is_square) ;
514
+ w. conditional_assign ( & ( MINUS_SQRT_B1 * ee * w) , !u1_is_square) ;
515
+ u = ( w. square ( ) - A * e. square ( ) - w * w1) . div_by_2 ( ) ;
516
+
517
+ ok &= u. is_square ( ) ;
518
+
519
+ // If the source point was a low-order point, then the computations
520
+ // above are incorrect. We handle this case here; among the
521
+ // low-order points, only the neutral point is in the prime-order
522
+ // subgroup.
523
+ let is_low_order = self . X . is_zero ( ) | self . Y . is_zero ( ) ;
524
+ let is_neutral = self . Y . ct_eq ( & self . Z ) ;
525
+ ok ^= is_low_order & ( ok ^ is_neutral) ;
526
+
527
+ ok
479
528
}
480
529
481
530
/// Hash a message to a point on the curve
@@ -791,7 +840,9 @@ mod tests {
791
840
use super :: * ;
792
841
use elliptic_curve:: Field ;
793
842
use hex_literal:: hex;
794
- use rand_core:: OsRng ;
843
+ use proptest:: prelude:: any;
844
+ use proptest:: proptest;
845
+ use rand_core:: { OsRng , TryRngCore } ;
795
846
796
847
fn hex_to_field ( hex : & ' static str ) -> FieldElement {
797
848
assert_eq ! ( hex. len( ) , 56 * 2 ) ;
@@ -1115,4 +1166,15 @@ mod tests {
1115
1166
assert_eq ! ( affine_point, point. to_affine( ) ) ;
1116
1167
}
1117
1168
}
1169
+
1170
+ proptest ! {
1171
+ #[ test]
1172
+ fn fuzz_is_torsion_free(
1173
+ bytes in any:: <[ u8 ; 57 ] >( )
1174
+ ) {
1175
+ let scalar = EdwardsScalar :: from_bytes_mod_order( & bytes. into( ) ) ;
1176
+ let point = EdwardsPoint :: mul_by_generator( & scalar) ;
1177
+ assert_eq!( point. is_torsion_free( ) . unwrap_u8( ) , 1 ) ;
1178
+ }
1179
+ }
1118
1180
}
0 commit comments