diff --git a/src/bls12_381/ec.rs b/src/bls12_381/ec.rs index ae6e9c2..b7041c3 100644 --- a/src/bls12_381/ec.rs +++ b/src/bls12_381/ec.rs @@ -166,6 +166,7 @@ macro_rules! curve_impl { fn into_projective(&self) -> $projective { (*self).into() } + } impl Rand for $projective { @@ -1480,7 +1481,7 @@ pub mod g2 { if let Some(y) = rhs.sqrt() { let mut negy = y; negy.negate(); - + let p = G2Affine { x: x, y: if y < negy { y } else { negy }, diff --git a/src/bls12_381/fq.rs b/src/bls12_381/fq.rs index 7f07dfd..cb4f44b 100644 --- a/src/bls12_381/fq.rs +++ b/src/bls12_381/fq.rs @@ -810,6 +810,18 @@ impl Fq { } impl SqrtField for Fq { + + fn legendre(&self) -> ::LegendreSymbol { + use ::LegendreSymbol::*; + + // s = self^((q - 1) // 2) + let s = self.pow([0xdcff7fffffffd555, 0xf55ffff58a9ffff, 0xb39869507b587b12, + 0xb23ba5c279c2895f, 0x258dd3db21a5d66b, 0xd0088f51cbff34d]); + if s == Fq::zero() { Zero } + else if s == Fq::one() { QuadraticResidue } + else { QuadraticNonResidue } + } + fn sqrt(&self) -> Option { // Shank's algorithm for q mod 4 = 3 // https://eprint.iacr.org/2012/685.pdf (page 9, algorithm 2) @@ -832,6 +844,7 @@ impl SqrtField for Fq { } } + #[test] fn test_b_coeff() { assert_eq!(Fq::from_repr(FqRepr::from(4)).unwrap(), B_COEFF); @@ -1303,12 +1316,12 @@ fn test_fq_sub_assign() { let mut tmp = Fq(FqRepr([0x531221a410efc95b, 0x72819306027e9717, 0x5ecefb937068b746, 0x97de59cd6feaefd7, 0xdc35c51158644588, 0xb2d176c04f2100])); tmp.sub_assign(&Fq(FqRepr([0x98910d20877e4ada, 0x940c983013f4b8ba, 0xf677dc9b8345ba33, 0xbef2ce6b7f577eba, 0xe1ae288ac3222c44, 0x5968bb602790806]))); assert_eq!(tmp, Fq(FqRepr([0x748014838971292c, 0xfd20fad49fddde5c, 0xcf87f198e3d3f336, 0x3d62d6e6e41883db, 0x45a3443cd88dc61b, 0x151d57aaf755ff94]))); - + // Test the opposite subtraction which doesn't test reduction. tmp = Fq(FqRepr([0x98910d20877e4ada, 0x940c983013f4b8ba, 0xf677dc9b8345ba33, 0xbef2ce6b7f577eba, 0xe1ae288ac3222c44, 0x5968bb602790806])); tmp.sub_assign(&Fq(FqRepr([0x531221a410efc95b, 0x72819306027e9717, 0x5ecefb937068b746, 0x97de59cd6feaefd7, 0xdc35c51158644588, 0xb2d176c04f2100]))); assert_eq!(tmp, Fq(FqRepr([0x457eeb7c768e817f, 0x218b052a117621a3, 0x97a8e10812dd02ed, 0x2714749e0f6c8ee3, 0x57863796abde6bc, 0x4e3ba3f4229e706]))); - + // Test for sensible results with zero tmp = Fq(FqRepr::from(0)); tmp.sub_assign(&Fq(FqRepr::from(0))); @@ -1779,3 +1792,21 @@ fn test_fq_ordering() { fn fq_repr_tests() { ::tests::repr::random_repr_tests::(); } + +#[test] +fn test_fq_legendre() { + use ::LegendreSymbol::*; + + assert_eq!(QuadraticResidue, Fq::one().legendre()); + assert_eq!(Zero, Fq::zero().legendre()); + + assert_eq!(QuadraticNonResidue, Fq::from_repr(FqRepr::from(2)).unwrap().legendre()); + assert_eq!(QuadraticResidue, Fq::from_repr(FqRepr::from(4)).unwrap().legendre()); + + let e = FqRepr([0x52a112f249778642, 0xd0bedb989b7991f, 0xdad3b6681aa63c05, + 0xf2efc0bb4721b283, 0x6057a98f18c24733, 0x1022c2fd122889e4]); + assert_eq!(QuadraticNonResidue, Fq::from_repr(e).unwrap().legendre()); + let e = FqRepr([0x6dae594e53a96c74, 0x19b16ca9ba64b37b, 0x5c764661a59bfc68, + 0xaa346e9b31c60a, 0x346059f9d87a9fa9, 0x1d61ac6bfd5c88b]); + assert_eq!(QuadraticResidue, Fq::from_repr(e).unwrap().legendre()); +} diff --git a/src/bls12_381/fq2.rs b/src/bls12_381/fq2.rs index a4d8e7c..aa6ccde 100644 --- a/src/bls12_381/fq2.rs +++ b/src/bls12_381/fq2.rs @@ -44,6 +44,17 @@ impl Fq2 { self.c0.sub_assign(&self.c1); self.c1.add_assign(&t0); } + + /// Norm of Fq2 as extension field in i over Fq + pub fn norm(&self) -> Fq { + let mut t0 = self.c0; + let mut t1 = self.c1; + t0.square(); + t1.square(); + t1.add_assign(&t0); + + t1 + } } impl Rand for Fq2 { @@ -145,6 +156,11 @@ impl Field for Fq2 { } impl SqrtField for Fq2 { + + fn legendre(&self) -> ::LegendreSymbol { + self.norm().legendre() + } + fn sqrt(&self) -> Option { // Algorithm 9, https://eprint.iacr.org/2012/685.pdf @@ -412,6 +428,19 @@ fn test_fq2_sqrt() { ); } +#[test] +fn test_fq2_legendre() { + use ::LegendreSymbol::*; + + assert_eq!(Zero, Fq2::zero().legendre()); + // i^2 = -1 + let mut m1 = Fq2::one(); + m1.negate(); + assert_eq!(QuadraticResidue, m1.legendre()); + m1.mul_by_nonresidue(); + assert_eq!(QuadraticNonResidue, m1.legendre()); +} + #[cfg(test)] use rand::{SeedableRng, XorShiftRng}; @@ -549,7 +578,7 @@ fn bench_fq2_sqrt(b: &mut ::test::Bencher) { #[test] fn fq2_field_tests() { use ::PrimeField; - + ::tests::field::random_field_tests::(); ::tests::field::random_sqrt_tests::(); ::tests::field::random_frobenius_tests::(super::fq::Fq::char(), 13); diff --git a/src/bls12_381/fr.rs b/src/bls12_381/fr.rs index 3796c51..058cb6a 100644 --- a/src/bls12_381/fr.rs +++ b/src/bls12_381/fr.rs @@ -1,4 +1,5 @@ use ::{Field, PrimeField, SqrtField, PrimeFieldRepr, PrimeFieldDecodingError}; +use ::LegendreSymbol::*; // r = 52435875175126190479447740508185965837690552500527637822603658699938581184513 const MODULUS: FrRepr = FrRepr([0xffffffff00000001, 0x53bda402fffe5bfe, 0x3339d80809a1d805, 0x73eda753299d7d48]); @@ -551,49 +552,54 @@ impl Fr { } impl SqrtField for Fr { + + fn legendre(&self) -> ::LegendreSymbol { + // s = self^((r - 1) // 2) + let s = self.pow([0x7fffffff80000000, 0xa9ded2017fff2dff, 0x199cec0404d0ec02, 0x39f6d3a994cebea4]); + if s == Self::zero() { Zero } + else if s == Self::one() { QuadraticResidue } + else { QuadraticNonResidue } + } + fn sqrt(&self) -> Option { // Tonelli-Shank's algorithm for q mod 16 = 1 // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) - - if self.is_zero() { - return Some(*self); - } - - // if self^((r - 1) // 2) != 1 - if self.pow([0x7fffffff80000000, 0xa9ded2017fff2dff, 0x199cec0404d0ec02, 0x39f6d3a994cebea4]) != Self::one() { - None - } else { - let mut c = Fr(ROOT_OF_UNITY); - // r = self^((t + 1) // 2) - let mut r = self.pow([0x7fff2dff80000000, 0x4d0ec02a9ded201, 0x94cebea4199cec04, 0x39f6d3a9]); - // t = self^t - let mut t = self.pow([0xfffe5bfeffffffff, 0x9a1d80553bda402, 0x299d7d483339d808, 0x73eda753]); - let mut m = S; - - while t != Self::one() { + match self.legendre() { + Zero => Some(*self), + QuadraticNonResidue => None, + QuadraticResidue => { + let mut c = Fr(ROOT_OF_UNITY); + // r = self^((t + 1) // 2) + let mut r = self.pow([0x7fff2dff80000000, 0x4d0ec02a9ded201, 0x94cebea4199cec04, 0x39f6d3a9]); + // t = self^t + let mut t = self.pow([0xfffe5bfeffffffff, 0x9a1d80553bda402, 0x299d7d483339d808, 0x73eda753]); + let mut m = S; + + while t != Self::one() { let mut i = 1; - { - let mut t2i = t; - t2i.square(); - loop { - if t2i == Self::one() { - break; - } + { + let mut t2i = t; t2i.square(); - i += 1; + loop { + if t2i == Self::one() { + break; + } + t2i.square(); + i += 1; + } } - } - for _ in 0..(m - i - 1) { + for _ in 0..(m - i - 1) { + c.square(); + } + r.mul_assign(&c); c.square(); + t.mul_assign(&c); + m = i; } - r.mul_assign(&c); - c.square(); - t.mul_assign(&c); - m = i; - } - Some(r) + Some(r) + } } } } @@ -778,6 +784,17 @@ fn test_fr_repr_sub_noborrow() { assert!(!x.sub_noborrow(&FrRepr([0xffffffff00000001, 0x53bda402fffe5bfe, 0x3339d80809a1d805, 0x73eda753299d7d48]))) } +#[test] +fn test_fr_legendre() { + assert_eq!(QuadraticResidue, Fr::one().legendre()); + assert_eq!(Zero, Fr::zero().legendre()); + + let e = FrRepr([0x0dbc5349cd5664da, 0x8ac5b6296e3ae29d, 0x127cb819feceaa3b, 0x3a6b21fb03867191]); + assert_eq!(QuadraticResidue, Fr::from_repr(e).unwrap().legendre()); + let e = FrRepr([0x96341aefd047c045, 0x9b5f4254500a4d65, 0x1ee08223b68ac240, 0x31d9cd545c0ec7c6]); + assert_eq!(QuadraticNonResidue, Fr::from_repr(e).unwrap().legendre()); +} + #[test] fn test_fr_repr_add_nocarry() { let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); @@ -1015,12 +1032,12 @@ fn test_fr_sub_assign() { let mut tmp = Fr(FrRepr([0x6a68c64b6f735a2b, 0xd5f4d143fe0a1972, 0x37c17f3829267c62, 0xa2f37391f30915c])); tmp.sub_assign(&Fr(FrRepr([0xade5adacdccb6190, 0xaa21ee0f27db3ccd, 0x2550f4704ae39086, 0x591d1902e7c5ba27]))); assert_eq!(tmp, Fr(FrRepr([0xbc83189d92a7f89c, 0x7f908737d62d38a3, 0x45aa62cfe7e4c3e1, 0x24ffc5896108547d]))); - + // Test the opposite subtraction which doesn't test reduction. tmp = Fr(FrRepr([0xade5adacdccb6190, 0xaa21ee0f27db3ccd, 0x2550f4704ae39086, 0x591d1902e7c5ba27])); tmp.sub_assign(&Fr(FrRepr([0x6a68c64b6f735a2b, 0xd5f4d143fe0a1972, 0x37c17f3829267c62, 0xa2f37391f30915c]))); assert_eq!(tmp, Fr(FrRepr([0x437ce7616d580765, 0xd42d1ccb29d1235b, 0xed8f753821bd1423, 0x4eede1c9c89528ca]))); - + // Test for sensible results with zero tmp = Fr(FrRepr::from(0)); tmp.sub_assign(&Fr(FrRepr::from(0))); diff --git a/src/lib.rs b/src/lib.rs index 1f7e724..9798d0d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -327,11 +327,15 @@ pub trait Field: Sized + /// This trait represents an element of a field that has a square root operation described for it. pub trait SqrtField: Field { + /// Returns the Legendre symbol of the field element. + fn legendre(&self) -> LegendreSymbol; + /// Returns the square root of the field element, if it is /// quadratic residue. fn sqrt(&self) -> Option; } + /// This trait represents a wrapper around a biginteger which can encode any element of a particular /// prime field. It is a smart wrapper around a sequence of `u64` limbs, least-significant digit /// first. @@ -409,6 +413,13 @@ pub trait PrimeFieldRepr: Sized + } } +#[derive(Debug, PartialEq)] +pub enum LegendreSymbol { + Zero = 0, + QuadraticResidue = 1, + QuadraticNonResidue = -1 +} + /// An error that may occur when trying to interpret a `PrimeFieldRepr` as a /// `PrimeField` element. #[derive(Debug)] diff --git a/src/tests/field.rs b/src/tests/field.rs index 5f99992..bddb93e 100644 --- a/src/tests/field.rs +++ b/src/tests/field.rs @@ -1,5 +1,5 @@ use rand::{Rng, SeedableRng, XorShiftRng}; -use ::{SqrtField, Field, PrimeField}; +use ::{SqrtField, Field, PrimeField, LegendreSymbol}; pub fn random_frobenius_tests>(characteristic: C, maxpower: usize) { let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); @@ -26,6 +26,7 @@ pub fn random_sqrt_tests() { let a = F::rand(&mut rng); let mut b = a; b.square(); + assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue); let b = b.sqrt().unwrap(); let mut negb = b; @@ -38,6 +39,8 @@ pub fn random_sqrt_tests() { for _ in 0..10000 { let mut b = c; b.square(); + assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue); + b = b.sqrt().unwrap(); if b != c {