Auto merge of #38 - mmaker:feature/legendre-symbol, r=ebfull

Add Legendre symbol.
This commit is contained in:
bmerge
2017-08-24 19:24:15 +00:00
6 changed files with 131 additions and 39 deletions

View File

@@ -166,6 +166,7 @@ macro_rules! curve_impl {
fn into_projective(&self) -> $projective { fn into_projective(&self) -> $projective {
(*self).into() (*self).into()
} }
} }
impl Rand for $projective { impl Rand for $projective {

View File

@@ -810,6 +810,18 @@ impl Fq {
} }
impl SqrtField for 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<Self> { fn sqrt(&self) -> Option<Self> {
// Shank's algorithm for q mod 4 = 3 // Shank's algorithm for q mod 4 = 3
// https://eprint.iacr.org/2012/685.pdf (page 9, algorithm 2) // https://eprint.iacr.org/2012/685.pdf (page 9, algorithm 2)
@@ -832,6 +844,7 @@ impl SqrtField for Fq {
} }
} }
#[test] #[test]
fn test_b_coeff() { fn test_b_coeff() {
assert_eq!(Fq::from_repr(FqRepr::from(4)).unwrap(), B_COEFF); assert_eq!(Fq::from_repr(FqRepr::from(4)).unwrap(), B_COEFF);
@@ -1779,3 +1792,21 @@ fn test_fq_ordering() {
fn fq_repr_tests() { fn fq_repr_tests() {
::tests::repr::random_repr_tests::<FqRepr>(); ::tests::repr::random_repr_tests::<FqRepr>();
} }
#[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());
}

View File

@@ -44,6 +44,17 @@ impl Fq2 {
self.c0.sub_assign(&self.c1); self.c0.sub_assign(&self.c1);
self.c1.add_assign(&t0); 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 { impl Rand for Fq2 {
@@ -145,6 +156,11 @@ impl Field for Fq2 {
} }
impl SqrtField for Fq2 { impl SqrtField for Fq2 {
fn legendre(&self) -> ::LegendreSymbol {
self.norm().legendre()
}
fn sqrt(&self) -> Option<Self> { fn sqrt(&self) -> Option<Self> {
// Algorithm 9, https://eprint.iacr.org/2012/685.pdf // 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)] #[cfg(test)]
use rand::{SeedableRng, XorShiftRng}; use rand::{SeedableRng, XorShiftRng};

View File

@@ -1,4 +1,5 @@
use ::{Field, PrimeField, SqrtField, PrimeFieldRepr, PrimeFieldDecodingError}; use ::{Field, PrimeField, SqrtField, PrimeFieldRepr, PrimeFieldDecodingError};
use ::LegendreSymbol::*;
// r = 52435875175126190479447740508185965837690552500527637822603658699938581184513 // r = 52435875175126190479447740508185965837690552500527637822603658699938581184513
const MODULUS: FrRepr = FrRepr([0xffffffff00000001, 0x53bda402fffe5bfe, 0x3339d80809a1d805, 0x73eda753299d7d48]); const MODULUS: FrRepr = FrRepr([0xffffffff00000001, 0x53bda402fffe5bfe, 0x3339d80809a1d805, 0x73eda753299d7d48]);
@@ -551,18 +552,22 @@ impl Fr {
} }
impl SqrtField for 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<Self> { fn sqrt(&self) -> Option<Self> {
// Tonelli-Shank's algorithm for q mod 16 = 1 // Tonelli-Shank's algorithm for q mod 16 = 1
// https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5)
match self.legendre() {
if self.is_zero() { Zero => Some(*self),
return Some(*self); QuadraticNonResidue => None,
} QuadraticResidue => {
// if self^((r - 1) // 2) != 1
if self.pow([0x7fffffff80000000, 0xa9ded2017fff2dff, 0x199cec0404d0ec02, 0x39f6d3a994cebea4]) != Self::one() {
None
} else {
let mut c = Fr(ROOT_OF_UNITY); let mut c = Fr(ROOT_OF_UNITY);
// r = self^((t + 1) // 2) // r = self^((t + 1) // 2)
let mut r = self.pow([0x7fff2dff80000000, 0x4d0ec02a9ded201, 0x94cebea4199cec04, 0x39f6d3a9]); let mut r = self.pow([0x7fff2dff80000000, 0x4d0ec02a9ded201, 0x94cebea4199cec04, 0x39f6d3a9]);
@@ -596,6 +601,7 @@ impl SqrtField for Fr {
Some(r) Some(r)
} }
} }
}
} }
#[cfg(test)] #[cfg(test)]
@@ -778,6 +784,17 @@ fn test_fr_repr_sub_noborrow() {
assert!(!x.sub_noborrow(&FrRepr([0xffffffff00000001, 0x53bda402fffe5bfe, 0x3339d80809a1d805, 0x73eda753299d7d48]))) 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] #[test]
fn test_fr_repr_add_nocarry() { fn test_fr_repr_add_nocarry() {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);

View File

@@ -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. /// This trait represents an element of a field that has a square root operation described for it.
pub trait SqrtField: Field 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 /// Returns the square root of the field element, if it is
/// quadratic residue. /// quadratic residue.
fn sqrt(&self) -> Option<Self>; fn sqrt(&self) -> Option<Self>;
} }
/// This trait represents a wrapper around a biginteger which can encode any element of a particular /// 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 /// prime field. It is a smart wrapper around a sequence of `u64` limbs, least-significant digit
/// first. /// 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 /// An error that may occur when trying to interpret a `PrimeFieldRepr` as a
/// `PrimeField` element. /// `PrimeField` element.
#[derive(Debug)] #[derive(Debug)]

View File

@@ -1,5 +1,5 @@
use rand::{Rng, SeedableRng, XorShiftRng}; use rand::{Rng, SeedableRng, XorShiftRng};
use ::{SqrtField, Field, PrimeField}; use ::{SqrtField, Field, PrimeField, LegendreSymbol};
pub fn random_frobenius_tests<F: Field, C: AsRef<[u64]>>(characteristic: C, maxpower: usize) { pub fn random_frobenius_tests<F: Field, C: AsRef<[u64]>>(characteristic: C, maxpower: usize) {
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
@@ -26,6 +26,7 @@ pub fn random_sqrt_tests<F: SqrtField>() {
let a = F::rand(&mut rng); let a = F::rand(&mut rng);
let mut b = a; let mut b = a;
b.square(); b.square();
assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue);
let b = b.sqrt().unwrap(); let b = b.sqrt().unwrap();
let mut negb = b; let mut negb = b;
@@ -38,6 +39,8 @@ pub fn random_sqrt_tests<F: SqrtField>() {
for _ in 0..10000 { for _ in 0..10000 {
let mut b = c; let mut b = c;
b.square(); b.square();
assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue);
b = b.sqrt().unwrap(); b = b.sqrt().unwrap();
if b != c { if b != c {