diff --git a/src/fp.rs b/src/fp.rs index 281c66e6..715334c5 100644 --- a/src/fp.rs +++ b/src/fp.rs @@ -76,6 +76,16 @@ const MODULUS: [u64; 6] = [ 0x1a01_11ea_397f_e69a, ]; +/// (p - 1) / 2 = 0xd0088f51cbff34d258dd3db21a5d66bb23ba5c279c2895fb39869507b587b120f55ffff58a9ffffdcff7fffffffd555 +const MODULUES_MINUS_ONE_OVER_TWO: [u64; 6] = [ + 0xdcff7fffffffd555, + 0xf55ffff58a9ffff, + 0xb39869507b587b12, + 0xb23ba5c279c2895f, + 0x258dd3db21a5d66b, + 0xd0088f51cbff34d +]; + /// INV = -(p^{-1} mod 2^64) mod 2^64 const INV: u64 = 0x89f3_fffc_fffc_fffd; @@ -320,6 +330,21 @@ impl Fp { res } + #[inline] + /// Computes the legendre symbol of this element. + pub fn legendre_symbol(&self) -> isize { + let legendre = self.pow_vartime(&MODULUES_MINUS_ONE_OVER_TWO); + if legendre == Fp::zero() { + 0 + } + else if legendre == Fp::one() { + 1 + } + else { + -1 + } +} + #[inline] pub fn sqrt(&self) -> CtOption { // We use Shank's method, as p = 3 (mod 4). This means @@ -889,6 +914,16 @@ fn test_from_bytes() { assert!(bool::from(Fp::from_bytes(&[0xff; 48]).is_none())); } +#[test] +fn test_legendre() { + let four = Fp([4, 0, 0, 0, 0, 0]); + + assert_eq!(Fp::zero().legendre_symbol(), 0); + assert_eq!(Fp::one().legendre_symbol(), 1); + assert_eq!((Fp::one() + Fp::one()).legendre_symbol(), -1); + assert_eq!(four.legendre_symbol(), 1); +} + #[test] fn test_sqrt() { // a = 4 diff --git a/src/scalar.rs b/src/scalar.rs index 84810c8f..e15ff837 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -80,6 +80,15 @@ const MODULUS: Scalar = Scalar([ 0x73ed_a753_299d_7d48, ]); +/// Constant representing (q - 1) / 2 +/// (q - 1) / 2 = 0x39f6d3a994cebea4199cec0404d0ec02a9ded2017fff2dff7fffffff80000000 +const MODULUES_MINUS_ONE_OVER_TWO: [u64; 4] = [ + 0x7fffffff80000000, + 0xa9ded2017fff2dff, + 0x199cec0404d0ec02, + 0x39f6d3a994cebea4 +]; + /// The modulus as u32 limbs. #[cfg(all(feature = "bits", not(target_pointer_width = "64")))] const MODULUS_LIMBS_32: [u32; 8] = [ @@ -343,6 +352,20 @@ impl Scalar { Scalar::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7) } + /// Computes the legendre symbol of this element. + pub fn legendre_symbol(&self) -> isize { + let legendre = self.pow(&MODULUES_MINUS_ONE_OVER_TWO); + if legendre == Scalar::zero() { + 0 + } + else if legendre == Scalar::one() { + 1 + } + else { + -1 + } + } + /// Computes the square root of this element, if it exists. pub fn sqrt(&self) -> CtOption { // Tonelli-Shank's algorithm for q mod 16 = 1 @@ -1174,6 +1197,16 @@ fn test_invert_is_pow() { } } +#[test] +fn test_legendre() { + let five = Scalar([5, 0, 0, 0]); + + assert_eq!(Scalar::zero().legendre_symbol(), 0); + assert_eq!(Scalar::one().legendre_symbol(), 1); + assert_eq!((Scalar::one() + Scalar::one()).legendre_symbol(), 1); + assert_eq!(five.legendre_symbol(), -1); +} + #[test] fn test_sqrt() { {