From 1b6cf8525118398f1f37c4073db61de67253fe89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michele=20Orr=C3=B9?= Date: Sun, 6 Aug 2017 15:24:01 +0200 Subject: [PATCH 1/7] Add Legendre symbol for Fq and Fq2. --- src/bls12_381/fq.rs | 45 +++++++++++++++++++++++++++++++++++++++++++- src/bls12_381/fq2.rs | 30 ++++++++++++++++++++++++++++- src/bls12_381/fr.rs | 29 +++++++++++++++++++++++++--- src/lib.rs | 8 ++++++++ 4 files changed, 107 insertions(+), 5 deletions(-) diff --git a/src/bls12_381/fq.rs b/src/bls12_381/fq.rs index 7f07dfd..73d1e16 100644 --- a/src/bls12_381/fq.rs +++ b/src/bls12_381/fq.rs @@ -1,4 +1,4 @@ -use ::{Field, PrimeField, SqrtField, PrimeFieldRepr, PrimeFieldDecodingError}; +use ::{Field, LegendreField, PrimeField, SqrtField, PrimeFieldRepr, PrimeFieldDecodingError}; use std::cmp::Ordering; use super::fq2::Fq2; @@ -832,6 +832,23 @@ impl SqrtField for Fq { } } +impl LegendreField for Fq { + fn legendre(&self) -> i32 { + // (q - 1) / 2 = + // 2001204777610833696708894912867952078278441409969503942666029068062015825245418932221343814564507832018947136279893 + let x = self.pow([0xcff7fffffffd555, 0xf55ffff58a9ffffd, + 0x39869507b587b120, 0x23ba5c279c2895fb, + 0x58dd3db21a5d66bb, 0xd0088f51cbff34d2]); + if x == Self::one() { + 1 + } else if x == Self::zero() { + 0 + } else { + -1 + } + } +} + #[test] fn test_b_coeff() { assert_eq!(Fq::from_repr(FqRepr::from(4)).unwrap(), B_COEFF); @@ -1779,3 +1796,29 @@ fn test_fq_ordering() { fn fq_repr_tests() { ::tests::repr::random_repr_tests::(); } +#[test] +fn test_fq_legendre() { + assert_eq!(1, Fq::one().legendre()); + assert_eq!(0, Fq::zero().legendre()); + + let e = Fq(FqRepr([0x914577fdcc41112, 0x1a6c20f3392c28e2, 0xd53f75da0c40fd21, + 0xb747c10d13caf0d0, 0x0de1adc19c24d8d2, 0x2103f924191033d2])); + assert_eq!(-1, e.legendre()); + + + // f + let e = Fq(FqRepr([0xe51d5292ae8126f, 0x382d60874f48db82, 0xb0dde25abc614254, + 0x34f4456bd18813df, 0x2c668010247ee04c, 0x44cb8bbdd7c4f1b0])); + assert_eq!(-1, e.legendre()); + + // f ** 9 + let e = Fq(FqRepr([0x69fc8eb1b590c712, 0xd73f4fb6fd34042e, 0xb5677ef2ed0eede7, + 0x367d831c592848c8, 0xb60615cc44e533f5, 0x127da624461b200e])); + + assert_eq!(-1, e.legendre()); + + let e = Fq(FqRepr([0x83c7ad9e29b7facc, 0x97b3c8fbdb50cc39, 0x9e2ccd0eb5db5e72, + 0xc74a00d90e1b247d, 0x90e38ef46c8d7eb7, 0x16882d6aa70bb469])); + assert_eq!(1, e.legendre()); + +} diff --git a/src/bls12_381/fq2.rs b/src/bls12_381/fq2.rs index a4d8e7c..ecb2965 100644 --- a/src/bls12_381/fq2.rs +++ b/src/bls12_381/fq2.rs @@ -1,5 +1,5 @@ use rand::{Rng, Rand}; -use ::{Field, SqrtField}; +use ::{Field, LegendreField, SqrtField}; use super::fq::{Fq, FROBENIUS_COEFF_FQ2_C1, NEGATIVE_ONE}; use std::cmp::Ordering; @@ -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 { @@ -185,6 +196,12 @@ impl SqrtField for Fq2 { } } +impl LegendreField for Fq2 { + fn legendre(&self) -> i32 { + Fq2::norm(&self).legendre() + } +} + #[test] fn test_fq2_ordering() { let mut a = Fq2 { @@ -412,6 +429,17 @@ fn test_fq2_sqrt() { ); } +#[test] +fn test_fq2_legendre() { + // i^2 = -1 + let mut m1 = Fq2::one(); + m1.negate(); + assert_eq!(1, m1.legendre()); + + m1.mul_by_nonresidue(); + assert_eq!(-1, m1.legendre()); +} + #[cfg(test)] use rand::{SeedableRng, XorShiftRng}; diff --git a/src/bls12_381/fr.rs b/src/bls12_381/fr.rs index 3796c51..4081011 100644 --- a/src/bls12_381/fr.rs +++ b/src/bls12_381/fr.rs @@ -1,4 +1,4 @@ -use ::{Field, PrimeField, SqrtField, PrimeFieldRepr, PrimeFieldDecodingError}; +use ::{Field, LegendreField, PrimeField, SqrtField, PrimeFieldRepr, PrimeFieldDecodingError}; // r = 52435875175126190479447740508185965837690552500527637822603658699938581184513 const MODULUS: FrRepr = FrRepr([0xffffffff00000001, 0x53bda402fffe5bfe, 0x3339d80809a1d805, 0x73eda753299d7d48]); @@ -559,8 +559,7 @@ impl SqrtField for Fr { return Some(*self); } - // if self^((r - 1) // 2) != 1 - if self.pow([0x7fffffff80000000, 0xa9ded2017fff2dff, 0x199cec0404d0ec02, 0x39f6d3a994cebea4]) != Self::one() { + if self.legendre() != 1 { None } else { let mut c = Fr(ROOT_OF_UNITY); @@ -598,6 +597,19 @@ impl SqrtField for Fr { } } +impl LegendreField for Fr { + fn legendre(&self) -> i32 { + // (q - 1) / 2 = 26217937587563095239723870254092982918845276250263818911301829349969290592256 + let x = self.pow([0x7fffffff80000000, 0xa9ded2017fff2dff, 0x199cec0404d0ec02, 0x39f6d3a994cebea4]); + if x == Self::one() { + 1 + } else if x == Self::zero() { + 0 + } else { + -1 + } + } +} #[cfg(test)] use rand::{SeedableRng, XorShiftRng, Rand}; @@ -778,6 +790,17 @@ fn test_fr_repr_sub_noborrow() { assert!(!x.sub_noborrow(&FrRepr([0xffffffff00000001, 0x53bda402fffe5bfe, 0x3339d80809a1d805, 0x73eda753299d7d48]))) } +#[test] +fn test_fr_legendre() { + assert_eq!(1, Fr::one().legendre()); + assert_eq!(0, Fr::zero().legendre()); + + let e = Fr(FrRepr([0x0dbc5349cd5664da, 0x8ac5b6296e3ae29d, 0x127cb819feceaa3b, 0x3a6b21fb03867191])); + assert_eq!(1, e.legendre()); + let e = Fr(FrRepr([0x96341aefd047c045, 0x9b5f4254500a4d65, 0x1ee08223b68ac240, 0x31d9cd545c0ec7c6])); + assert_eq!(-1, e.legendre()); +} + #[test] fn test_fr_repr_add_nocarry() { let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); diff --git a/src/lib.rs b/src/lib.rs index 1f7e724..abd7328 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -332,6 +332,14 @@ pub trait SqrtField: Field fn sqrt(&self) -> Option; } +/// This trait represents an element of a field that has a Legendre symbol described for it. +pub trait LegendreField: Field +{ + /// Returns the legendre symbol of the field element. + fn legendre(&self) -> i32; +} + + /// 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. From 57b4e7362720b3a2969469ecb16c50058db5083e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michele=20Orr=C3=B9?= Date: Wed, 9 Aug 2017 14:09:54 +0200 Subject: [PATCH 2/7] Create enum for LegendreSymbol, fix test. --- src/bls12_381/ec.rs | 2 +- src/bls12_381/fq.rs | 57 +++++++++++++++++--------------------------- src/bls12_381/fq2.rs | 9 +++---- src/bls12_381/fr.rs | 34 ++++++++++++-------------- src/lib.rs | 9 ++++++- 5 files changed, 51 insertions(+), 60 deletions(-) diff --git a/src/bls12_381/ec.rs b/src/bls12_381/ec.rs index ae6e9c2..9116844 100644 --- a/src/bls12_381/ec.rs +++ b/src/bls12_381/ec.rs @@ -1480,7 +1480,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 73d1e16..d2efa0d 100644 --- a/src/bls12_381/fq.rs +++ b/src/bls12_381/fq.rs @@ -833,19 +833,14 @@ impl SqrtField for Fq { } impl LegendreField for Fq { - fn legendre(&self) -> i32 { - // (q - 1) / 2 = - // 2001204777610833696708894912867952078278441409969503942666029068062015825245418932221343814564507832018947136279893 - let x = self.pow([0xcff7fffffffd555, 0xf55ffff58a9ffffd, - 0x39869507b587b120, 0x23ba5c279c2895fb, - 0x58dd3db21a5d66bb, 0xd0088f51cbff34d2]); - if x == Self::one() { - 1 - } else if x == Self::zero() { - 0 - } else { - -1 - } + fn legendre(&self) -> ::LegendreSymbol { + use ::LegendreSymbol::*; + + let s = self.pow([0xdcff7fffffffd555, 0xf55ffff58a9ffff, 0xb39869507b587b12, + 0xb23ba5c279c2895f, 0x258dd3db21a5d66b, 0xd0088f51cbff34d]); + if s == Fq::zero() { Zero } + else if s == Fq::one() { QResidue } + else { QNonResidue } } } @@ -1320,12 +1315,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))); @@ -1796,29 +1791,21 @@ fn test_fq_ordering() { fn fq_repr_tests() { ::tests::repr::random_repr_tests::(); } + #[test] fn test_fq_legendre() { - assert_eq!(1, Fq::one().legendre()); - assert_eq!(0, Fq::zero().legendre()); - - let e = Fq(FqRepr([0x914577fdcc41112, 0x1a6c20f3392c28e2, 0xd53f75da0c40fd21, - 0xb747c10d13caf0d0, 0x0de1adc19c24d8d2, 0x2103f924191033d2])); - assert_eq!(-1, e.legendre()); - - - // f - let e = Fq(FqRepr([0xe51d5292ae8126f, 0x382d60874f48db82, 0xb0dde25abc614254, - 0x34f4456bd18813df, 0x2c668010247ee04c, 0x44cb8bbdd7c4f1b0])); - assert_eq!(-1, e.legendre()); - - // f ** 9 - let e = Fq(FqRepr([0x69fc8eb1b590c712, 0xd73f4fb6fd34042e, 0xb5677ef2ed0eede7, - 0x367d831c592848c8, 0xb60615cc44e533f5, 0x127da624461b200e])); + use ::LegendreSymbol::*; - assert_eq!(-1, e.legendre()); + assert_eq!(QResidue, Fq::one().legendre()); + assert_eq!(Zero, Fq::zero().legendre()); - let e = Fq(FqRepr([0x83c7ad9e29b7facc, 0x97b3c8fbdb50cc39, 0x9e2ccd0eb5db5e72, - 0xc74a00d90e1b247d, 0x90e38ef46c8d7eb7, 0x16882d6aa70bb469])); - assert_eq!(1, e.legendre()); + assert_eq!(QNonResidue, Fq::from_repr(FqRepr::from(2)).unwrap().legendre()); + assert_eq!(QResidue, Fq::from_repr(FqRepr::from(4)).unwrap().legendre()); + let e = FqRepr([0x52a112f249778642, 0xd0bedb989b7991f, 0xdad3b6681aa63c05, + 0xf2efc0bb4721b283, 0x6057a98f18c24733, 0x1022c2fd122889e4]); + assert_eq!(QNonResidue, Fq::from_repr(e).unwrap().legendre()); + let e = FqRepr([0x6dae594e53a96c74, 0x19b16ca9ba64b37b, 0x5c764661a59bfc68, + 0xaa346e9b31c60a, 0x346059f9d87a9fa9, 0x1d61ac6bfd5c88b]); + assert_eq!(QResidue, Fq::from_repr(e).unwrap().legendre()); } diff --git a/src/bls12_381/fq2.rs b/src/bls12_381/fq2.rs index ecb2965..0c9218c 100644 --- a/src/bls12_381/fq2.rs +++ b/src/bls12_381/fq2.rs @@ -197,7 +197,7 @@ impl SqrtField for Fq2 { } impl LegendreField for Fq2 { - fn legendre(&self) -> i32 { + fn legendre(&self) -> ::LegendreSymbol { Fq2::norm(&self).legendre() } } @@ -431,13 +431,14 @@ fn test_fq2_sqrt() { #[test] fn test_fq2_legendre() { + use ::LegendreSymbol::*; + // i^2 = -1 let mut m1 = Fq2::one(); m1.negate(); - assert_eq!(1, m1.legendre()); - + assert_eq!(QResidue, m1.legendre()); m1.mul_by_nonresidue(); - assert_eq!(-1, m1.legendre()); + assert_eq!(QNonResidue, m1.legendre()); } #[cfg(test)] diff --git a/src/bls12_381/fr.rs b/src/bls12_381/fr.rs index 4081011..7842de5 100644 --- a/src/bls12_381/fr.rs +++ b/src/bls12_381/fr.rs @@ -1,4 +1,5 @@ use ::{Field, LegendreField, PrimeField, SqrtField, PrimeFieldRepr, PrimeFieldDecodingError}; +use ::LegendreSymbol::*; // r = 52435875175126190479447740508185965837690552500527637822603658699938581184513 const MODULUS: FrRepr = FrRepr([0xffffffff00000001, 0x53bda402fffe5bfe, 0x3339d80809a1d805, 0x73eda753299d7d48]); @@ -559,7 +560,7 @@ impl SqrtField for Fr { return Some(*self); } - if self.legendre() != 1 { + if let QNonResidue = self.legendre() { None } else { let mut c = Fr(ROOT_OF_UNITY); @@ -598,16 +599,11 @@ impl SqrtField for Fr { } impl LegendreField for Fr { - fn legendre(&self) -> i32 { - // (q - 1) / 2 = 26217937587563095239723870254092982918845276250263818911301829349969290592256 - let x = self.pow([0x7fffffff80000000, 0xa9ded2017fff2dff, 0x199cec0404d0ec02, 0x39f6d3a994cebea4]); - if x == Self::one() { - 1 - } else if x == Self::zero() { - 0 - } else { - -1 - } + fn legendre(&self) -> ::LegendreSymbol { + let s = self.pow([0x7fffffff80000000, 0xa9ded2017fff2dff, 0x199cec0404d0ec02, 0x39f6d3a994cebea4]); + if s == Self::zero() { Zero } + else if s == Self::one() { QResidue } + else { QNonResidue } } } #[cfg(test)] @@ -792,13 +788,13 @@ fn test_fr_repr_sub_noborrow() { #[test] fn test_fr_legendre() { - assert_eq!(1, Fr::one().legendre()); - assert_eq!(0, Fr::zero().legendre()); + assert_eq!(QResidue, Fr::one().legendre()); + assert_eq!(Zero, Fr::zero().legendre()); - let e = Fr(FrRepr([0x0dbc5349cd5664da, 0x8ac5b6296e3ae29d, 0x127cb819feceaa3b, 0x3a6b21fb03867191])); - assert_eq!(1, e.legendre()); - let e = Fr(FrRepr([0x96341aefd047c045, 0x9b5f4254500a4d65, 0x1ee08223b68ac240, 0x31d9cd545c0ec7c6])); - assert_eq!(-1, e.legendre()); + let e = FrRepr([0x0dbc5349cd5664da, 0x8ac5b6296e3ae29d, 0x127cb819feceaa3b, 0x3a6b21fb03867191]); + assert_eq!(QResidue, Fr::from_repr(e).unwrap().legendre()); + let e = FrRepr([0x96341aefd047c045, 0x9b5f4254500a4d65, 0x1ee08223b68ac240, 0x31d9cd545c0ec7c6]); + assert_eq!(QNonResidue, Fr::from_repr(e).unwrap().legendre()); } #[test] @@ -1038,12 +1034,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 abd7328..0ba79aa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -336,7 +336,7 @@ pub trait SqrtField: Field pub trait LegendreField: Field { /// Returns the legendre symbol of the field element. - fn legendre(&self) -> i32; + fn legendre(&self) -> LegendreSymbol; } @@ -417,6 +417,13 @@ pub trait PrimeFieldRepr: Sized + } } +#[derive(Debug, PartialEq)] +pub enum LegendreSymbol { + Zero = 0, + QResidue = 1, + QNonResidue = -1 +} + /// An error that may occur when trying to interpret a `PrimeFieldRepr` as a /// `PrimeField` element. #[derive(Debug)] From 2d3f498e751dc4fda452994f6f5c64eaec2adc94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michele=20Orr=C3=B9?= Date: Thu, 10 Aug 2017 18:18:46 +0200 Subject: [PATCH 3/7] Polish sqrt in fr.rs: use pattern matching with Legendre enums. --- src/bls12_381/fr.rs | 62 ++++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/src/bls12_381/fr.rs b/src/bls12_381/fr.rs index 7842de5..f914942 100644 --- a/src/bls12_381/fr.rs +++ b/src/bls12_381/fr.rs @@ -555,45 +555,42 @@ impl SqrtField for Fr { 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 let QNonResidue = self.legendre() { - 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), + QNonResidue => None, + QResidue => { + 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) + } } } } @@ -606,6 +603,7 @@ impl LegendreField for Fr { else { QNonResidue } } } + #[cfg(test)] use rand::{SeedableRng, XorShiftRng, Rand}; From 6feb0f802f1d8e1e8b092ac1d734b7b4d2294208 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michele=20Orr=C3=B9?= Date: Thu, 10 Aug 2017 18:28:17 +0200 Subject: [PATCH 4/7] Merge traits SqrtField and LegendreField into SqrtField. --- src/bls12_381/fq.rs | 24 ++++++++++++------------ src/bls12_381/fq2.rs | 13 ++++++------- src/bls12_381/fr.rs | 19 +++++++++---------- src/lib.rs | 10 +++------- 4 files changed, 30 insertions(+), 36 deletions(-) diff --git a/src/bls12_381/fq.rs b/src/bls12_381/fq.rs index d2efa0d..5f92b1d 100644 --- a/src/bls12_381/fq.rs +++ b/src/bls12_381/fq.rs @@ -1,4 +1,4 @@ -use ::{Field, LegendreField, PrimeField, SqrtField, PrimeFieldRepr, PrimeFieldDecodingError}; +use ::{Field, PrimeField, SqrtField, PrimeFieldRepr, PrimeFieldDecodingError}; use std::cmp::Ordering; use super::fq2::Fq2; @@ -810,6 +810,17 @@ impl Fq { } impl SqrtField for Fq { + + fn legendre(&self) -> ::LegendreSymbol { + use ::LegendreSymbol::*; + + let s = self.pow([0xdcff7fffffffd555, 0xf55ffff58a9ffff, 0xb39869507b587b12, + 0xb23ba5c279c2895f, 0x258dd3db21a5d66b, 0xd0088f51cbff34d]); + if s == Fq::zero() { Zero } + else if s == Fq::one() { QResidue } + else { QNonResidue } + } + fn sqrt(&self) -> Option { // Shank's algorithm for q mod 4 = 3 // https://eprint.iacr.org/2012/685.pdf (page 9, algorithm 2) @@ -832,17 +843,6 @@ impl SqrtField for Fq { } } -impl LegendreField for Fq { - fn legendre(&self) -> ::LegendreSymbol { - use ::LegendreSymbol::*; - - let s = self.pow([0xdcff7fffffffd555, 0xf55ffff58a9ffff, 0xb39869507b587b12, - 0xb23ba5c279c2895f, 0x258dd3db21a5d66b, 0xd0088f51cbff34d]); - if s == Fq::zero() { Zero } - else if s == Fq::one() { QResidue } - else { QNonResidue } - } -} #[test] fn test_b_coeff() { diff --git a/src/bls12_381/fq2.rs b/src/bls12_381/fq2.rs index 0c9218c..fb385d7 100644 --- a/src/bls12_381/fq2.rs +++ b/src/bls12_381/fq2.rs @@ -1,5 +1,5 @@ use rand::{Rng, Rand}; -use ::{Field, LegendreField, SqrtField}; +use ::{Field, SqrtField}; use super::fq::{Fq, FROBENIUS_COEFF_FQ2_C1, NEGATIVE_ONE}; use std::cmp::Ordering; @@ -156,6 +156,11 @@ impl Field for Fq2 { } impl SqrtField for Fq2 { + + fn legendre(&self) -> ::LegendreSymbol { + Fq2::norm(&self).legendre() + } + fn sqrt(&self) -> Option { // Algorithm 9, https://eprint.iacr.org/2012/685.pdf @@ -196,12 +201,6 @@ impl SqrtField for Fq2 { } } -impl LegendreField for Fq2 { - fn legendre(&self) -> ::LegendreSymbol { - Fq2::norm(&self).legendre() - } -} - #[test] fn test_fq2_ordering() { let mut a = Fq2 { diff --git a/src/bls12_381/fr.rs b/src/bls12_381/fr.rs index f914942..7d98625 100644 --- a/src/bls12_381/fr.rs +++ b/src/bls12_381/fr.rs @@ -1,4 +1,4 @@ -use ::{Field, LegendreField, PrimeField, SqrtField, PrimeFieldRepr, PrimeFieldDecodingError}; +use ::{Field, PrimeField, SqrtField, PrimeFieldRepr, PrimeFieldDecodingError}; use ::LegendreSymbol::*; // r = 52435875175126190479447740508185965837690552500527637822603658699938581184513 @@ -552,6 +552,14 @@ impl Fr { } impl SqrtField for Fr { + + fn legendre(&self) -> ::LegendreSymbol { + let s = self.pow([0x7fffffff80000000, 0xa9ded2017fff2dff, 0x199cec0404d0ec02, 0x39f6d3a994cebea4]); + if s == Self::zero() { Zero } + else if s == Self::one() { QResidue } + else { QNonResidue } + } + fn sqrt(&self) -> Option { // Tonelli-Shank's algorithm for q mod 16 = 1 // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) @@ -595,15 +603,6 @@ impl SqrtField for Fr { } } -impl LegendreField for Fr { - fn legendre(&self) -> ::LegendreSymbol { - let s = self.pow([0x7fffffff80000000, 0xa9ded2017fff2dff, 0x199cec0404d0ec02, 0x39f6d3a994cebea4]); - if s == Self::zero() { Zero } - else if s == Self::one() { QResidue } - else { QNonResidue } - } -} - #[cfg(test)] use rand::{SeedableRng, XorShiftRng, Rand}; diff --git a/src/lib.rs b/src/lib.rs index 0ba79aa..a0fc2b5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -327,18 +327,14 @@ 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 an element of a field that has a Legendre symbol described for it. -pub trait LegendreField: Field -{ - /// Returns the legendre symbol of the field element. - fn legendre(&self) -> LegendreSymbol; -} - /// 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 From a86d0b72706b466115c01be9a1ec637c220c2e1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michele=20Orr=C3=B9?= Date: Sat, 12 Aug 2017 11:50:30 +0200 Subject: [PATCH 5/7] s/Q/Quadratic/ in LegendreSymbol enum. Proposed by @ebfull. --- src/bls12_381/fq.rs | 14 +++++++------- src/bls12_381/fq2.rs | 4 ++-- src/bls12_381/fr.rs | 14 +++++++------- src/lib.rs | 4 ++-- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/bls12_381/fq.rs b/src/bls12_381/fq.rs index 5f92b1d..8bb2186 100644 --- a/src/bls12_381/fq.rs +++ b/src/bls12_381/fq.rs @@ -817,8 +817,8 @@ impl SqrtField for Fq { let s = self.pow([0xdcff7fffffffd555, 0xf55ffff58a9ffff, 0xb39869507b587b12, 0xb23ba5c279c2895f, 0x258dd3db21a5d66b, 0xd0088f51cbff34d]); if s == Fq::zero() { Zero } - else if s == Fq::one() { QResidue } - else { QNonResidue } + else if s == Fq::one() { QuadraticResidue } + else { QuadraticNonResidue } } fn sqrt(&self) -> Option { @@ -1796,16 +1796,16 @@ fn fq_repr_tests() { fn test_fq_legendre() { use ::LegendreSymbol::*; - assert_eq!(QResidue, Fq::one().legendre()); + assert_eq!(QuadraticResidue, Fq::one().legendre()); assert_eq!(Zero, Fq::zero().legendre()); - assert_eq!(QNonResidue, Fq::from_repr(FqRepr::from(2)).unwrap().legendre()); - assert_eq!(QResidue, Fq::from_repr(FqRepr::from(4)).unwrap().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!(QNonResidue, Fq::from_repr(e).unwrap().legendre()); + assert_eq!(QuadraticNonResidue, Fq::from_repr(e).unwrap().legendre()); let e = FqRepr([0x6dae594e53a96c74, 0x19b16ca9ba64b37b, 0x5c764661a59bfc68, 0xaa346e9b31c60a, 0x346059f9d87a9fa9, 0x1d61ac6bfd5c88b]); - assert_eq!(QResidue, Fq::from_repr(e).unwrap().legendre()); + assert_eq!(QuadraticResidue, Fq::from_repr(e).unwrap().legendre()); } diff --git a/src/bls12_381/fq2.rs b/src/bls12_381/fq2.rs index fb385d7..6863a35 100644 --- a/src/bls12_381/fq2.rs +++ b/src/bls12_381/fq2.rs @@ -435,9 +435,9 @@ fn test_fq2_legendre() { // i^2 = -1 let mut m1 = Fq2::one(); m1.negate(); - assert_eq!(QResidue, m1.legendre()); + assert_eq!(QuadraticResidue, m1.legendre()); m1.mul_by_nonresidue(); - assert_eq!(QNonResidue, m1.legendre()); + assert_eq!(QuadraticNonResidue, m1.legendre()); } #[cfg(test)] diff --git a/src/bls12_381/fr.rs b/src/bls12_381/fr.rs index 7d98625..0f159de 100644 --- a/src/bls12_381/fr.rs +++ b/src/bls12_381/fr.rs @@ -556,8 +556,8 @@ impl SqrtField for Fr { fn legendre(&self) -> ::LegendreSymbol { let s = self.pow([0x7fffffff80000000, 0xa9ded2017fff2dff, 0x199cec0404d0ec02, 0x39f6d3a994cebea4]); if s == Self::zero() { Zero } - else if s == Self::one() { QResidue } - else { QNonResidue } + else if s == Self::one() { QuadraticResidue } + else { QuadraticNonResidue } } fn sqrt(&self) -> Option { @@ -565,8 +565,8 @@ impl SqrtField for Fr { // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) match self.legendre() { Zero => Some(*self), - QNonResidue => None, - QResidue => { + QuadraticNonResidue => None, + QuadraticResidue => { let mut c = Fr(ROOT_OF_UNITY); // r = self^((t + 1) // 2) let mut r = self.pow([0x7fff2dff80000000, 0x4d0ec02a9ded201, 0x94cebea4199cec04, 0x39f6d3a9]); @@ -785,13 +785,13 @@ fn test_fr_repr_sub_noborrow() { #[test] fn test_fr_legendre() { - assert_eq!(QResidue, Fr::one().legendre()); + assert_eq!(QuadraticResidue, Fr::one().legendre()); assert_eq!(Zero, Fr::zero().legendre()); let e = FrRepr([0x0dbc5349cd5664da, 0x8ac5b6296e3ae29d, 0x127cb819feceaa3b, 0x3a6b21fb03867191]); - assert_eq!(QResidue, Fr::from_repr(e).unwrap().legendre()); + assert_eq!(QuadraticResidue, Fr::from_repr(e).unwrap().legendre()); let e = FrRepr([0x96341aefd047c045, 0x9b5f4254500a4d65, 0x1ee08223b68ac240, 0x31d9cd545c0ec7c6]); - assert_eq!(QNonResidue, Fr::from_repr(e).unwrap().legendre()); + assert_eq!(QuadraticNonResidue, Fr::from_repr(e).unwrap().legendre()); } #[test] diff --git a/src/lib.rs b/src/lib.rs index a0fc2b5..e25ae49 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -416,8 +416,8 @@ pub trait PrimeFieldRepr: Sized + #[derive(Debug, PartialEq)] pub enum LegendreSymbol { Zero = 0, - QResidue = 1, - QNonResidue = -1 + QuadraticResidue = 1, + QuadraticNonResidue = -1 } /// An error that may occur when trying to interpret a `PrimeFieldRepr` as a From 2ac2d1213d2a2cbafea6ba1645231b319f9d030c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michele=20Orr=C3=B9?= Date: Fri, 11 Aug 2017 15:12:15 +0200 Subject: [PATCH 6/7] Some (easy) cleanups as suggested from @daira. Thanks! --- src/bls12_381/ec.rs | 1 + src/bls12_381/fq.rs | 1 + src/bls12_381/fq2.rs | 1 + src/lib.rs | 2 +- 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/bls12_381/ec.rs b/src/bls12_381/ec.rs index 9116844..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 { diff --git a/src/bls12_381/fq.rs b/src/bls12_381/fq.rs index 8bb2186..cb4f44b 100644 --- a/src/bls12_381/fq.rs +++ b/src/bls12_381/fq.rs @@ -814,6 +814,7 @@ 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 } diff --git a/src/bls12_381/fq2.rs b/src/bls12_381/fq2.rs index 6863a35..3095661 100644 --- a/src/bls12_381/fq2.rs +++ b/src/bls12_381/fq2.rs @@ -432,6 +432,7 @@ fn test_fq2_sqrt() { fn test_fq2_legendre() { use ::LegendreSymbol::*; + assert_eq!(Zero, Fq2::zero().legendre()); // i^2 = -1 let mut m1 = Fq2::one(); m1.negate(); diff --git a/src/lib.rs b/src/lib.rs index e25ae49..9798d0d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -327,7 +327,7 @@ 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. + /// Returns the Legendre symbol of the field element. fn legendre(&self) -> LegendreSymbol; /// Returns the square root of the field element, if it is From 9846ad2d177863c03f524a79aff04bb2f7734ce1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michele=20Orr=C3=B9?= Date: Wed, 23 Aug 2017 20:26:56 +0200 Subject: [PATCH 7/7] Some (easy) cleanups as suggested from @ebfull. Thanks! --- src/bls12_381/fq2.rs | 4 ++-- src/bls12_381/fr.rs | 1 + src/tests/field.rs | 5 ++++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/bls12_381/fq2.rs b/src/bls12_381/fq2.rs index 3095661..aa6ccde 100644 --- a/src/bls12_381/fq2.rs +++ b/src/bls12_381/fq2.rs @@ -158,7 +158,7 @@ impl Field for Fq2 { impl SqrtField for Fq2 { fn legendre(&self) -> ::LegendreSymbol { - Fq2::norm(&self).legendre() + self.norm().legendre() } fn sqrt(&self) -> Option { @@ -578,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 0f159de..058cb6a 100644 --- a/src/bls12_381/fr.rs +++ b/src/bls12_381/fr.rs @@ -554,6 +554,7 @@ 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 } 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 {