@@ -5,7 +5,7 @@ use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
55
66use crate :: curve:: scalar_mul:: variable_base;
77use crate :: curve:: twedwards:: extended:: ExtendedPoint as TwistedExtendedPoint ;
8- use crate :: field:: FieldElement ;
8+ use crate :: field:: { ConstMontyType , FieldElement } ;
99use crate :: * ;
1010use elliptic_curve:: {
1111 CurveGroup , Error ,
@@ -722,8 +722,57 @@ impl EdwardsPoint {
722722 /// prime-order subgroup;
723723 /// * `false` if `self` has a nonzero torsion component and is not
724724 /// in the prime-order subgroup.
725+ // See https://eprint.iacr.org/2022/1164.
725726 pub fn is_torsion_free ( & self ) -> Choice {
726- ( self * EdwardsScalar :: new ( ORDER ) ) . ct_eq ( & Self :: IDENTITY )
727+ const A : FieldElement = FieldElement ( ConstMontyType :: new ( & U448 :: from_be_hex (
728+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffffffffffffffffffffffffffffffffffffffffffffffffeceaf" ,
729+ ) ) ) ;
730+ const A1 : FieldElement = FieldElement ( ConstMontyType :: new ( & U448 :: from_u64 ( 156320 ) ) ) ;
731+ const MINUS_SQRT_B1 : FieldElement = FieldElement ( ConstMontyType :: new ( & U448 :: from_be_hex (
732+ "749a7410536c225f1025ca374176557d7839611d691caad26d74a1fca5cfad15f196642c0a4484b67f321025577cc6b5a6f443c2eaa36327" ,
733+ ) ) ) ;
734+
735+ let mut e = self . X * ( self . Z - self . Y ) ;
736+ let ee = e. square ( ) ;
737+ let mut u = FieldElement :: A_PLUS_TWO_OVER_FOUR * ( self . Z + self . Y ) * e * self . X ;
738+ let w = self . Z . double ( ) * ( self . Z - self . Y ) ;
739+
740+ let u2 = u. double ( ) . double ( ) ;
741+ let w2 = w. double ( ) ;
742+
743+ let mut w1 = u2. sqrt ( ) ;
744+ let mut ok = w1. square ( ) . ct_eq ( & u2) ;
745+ let u1 = ( u2 - A1 * ee - w1 * w2) . half ( ) ;
746+
747+ // If `u1` happens not to be a square, then `sqrt(u1)` returns `sqrt(-u1)`
748+ // in that case (since we are in a finite field GF(q) with q = 3 mod 4,
749+ // if `u1` is not a square then `-u1` must be a square). In such a case, we
750+ // should replace `(u1,w1)` with `((B1*e^4)/u1, -w1)`. To avoid the division,
751+ // we instead switch to an isomorphic curve; namely:
752+ // u2 = B1*(e^4)*u1
753+ // w2 = -w1*u1
754+ // e2 = e*u1
755+ // Then:
756+ // w = sqrt(u2) = sqrt(-B1)*(e^2)*sqrt(-u1)
757+ // u = (w^2 - A*e^2 - w*w1)/2
758+ let mut w = u1. sqrt ( ) ;
759+ let u1_is_square = w. square ( ) . ct_eq ( & u1) ;
760+ w1. conditional_assign ( & -( w1 * u1) , !u1_is_square) ;
761+ e. conditional_assign ( & ( e * u1) , !u1_is_square) ;
762+ w. conditional_assign ( & ( MINUS_SQRT_B1 * ee * w) , !u1_is_square) ;
763+ u = ( w. square ( ) - A * e. square ( ) - w * w1) . half ( ) ;
764+
765+ ok &= u. is_square ( ) ;
766+
767+ // If the source point was a low-order point, then the computations
768+ // above are incorrect. We handle this case here; among the
769+ // low-order points, only the neutral point is in the prime-order
770+ // subgroup.
771+ let is_low_order = self . X . is_zero ( ) | self . Y . is_zero ( ) ;
772+ let is_neutral = self . Y . ct_eq ( & self . Z ) ;
773+ ok ^= is_low_order & ( ok ^ is_neutral) ;
774+
775+ ok
727776 }
728777
729778 /// Hash a message to a point on the curve
@@ -972,6 +1021,8 @@ mod tests {
9721021 use super :: * ;
9731022 use elliptic_curve:: Field ;
9741023 use hex_literal:: hex;
1024+ use proptest:: prelude:: any;
1025+ use proptest:: proptest;
9751026 use rand_core:: TryRngCore ;
9761027
9771028 fn hex_to_field ( hex : & ' static str ) -> FieldElement {
@@ -1267,4 +1318,15 @@ mod tests {
12671318
12681319 assert_eq ! ( computed_commitment, expected_commitment) ;
12691320 }
1321+
1322+ proptest ! {
1323+ #[ test]
1324+ fn fuzz_is_torsion_free(
1325+ bytes in any:: <[ u8 ; 57 ] >( )
1326+ ) {
1327+ let scalar = EdwardsScalar :: from_bytes_mod_order( & bytes. into( ) ) ;
1328+ let point = EdwardsPoint :: mul_by_generator( & scalar) ;
1329+ assert_eq!( point. is_torsion_free( ) . unwrap_u8( ) , 1 ) ;
1330+ }
1331+ }
12701332}
0 commit comments