diff --git a/benches/bn256_field.rs b/benches/bn256_field.rs index fa8db6a3..d3495dfe 100644 --- a/benches/bn256_field.rs +++ b/benches/bn256_field.rs @@ -1,5 +1,5 @@ use criterion::{black_box, criterion_group, criterion_main, Criterion, Throughput}; -use halo2curves::{bn256::*, ff::Field, legendre::Legendre}; +use halo2curves::{bn256::*, ff::Field, ff_ext::Legendre}; use rand::SeedableRng; use rand_xorshift::XorShiftRng; @@ -45,9 +45,6 @@ pub fn bench_bn256_field(c: &mut Criterion) { group.bench_function("bn256_fq_legendre", |bencher| { bencher.iter(|| black_box(&a).legendre()) }); - group.bench_function("bn256_fq_jacobi", |bencher| { - bencher.iter(|| black_box(&a).jacobi()) - }); } criterion_group!(benches, bench_bn256_field); diff --git a/src/bn256/fq.rs b/src/bn256/fq.rs index 56be690f..23da849a 100644 --- a/src/bn256/fq.rs +++ b/src/bn256/fq.rs @@ -4,6 +4,7 @@ use crate::bn256::assembly::field_arithmetic_asm; use crate::{arithmetic::macx, field_arithmetic, field_specific}; use crate::arithmetic::{adc, mac, sbb}; +use crate::extend_field_legendre; use crate::ff::{FromUniformBytes, PrimeField, WithSmallOrderMulGroup}; use crate::{ field_bits, field_common, impl_add_binop_specify_output, impl_binops_additive, @@ -160,7 +161,7 @@ impl Fq { } } -prime_field_legendre!(Fq); +extend_field_legendre!(Fq); impl ff::Field for Fq { const ZERO: Self = Self::zero(); @@ -284,7 +285,7 @@ impl WithSmallOrderMulGroup<3> for Fq { #[cfg(test)] mod test { use super::*; - use crate::legendre::Legendre; + use crate::ff_ext::Legendre; use ff::Field; use rand_core::OsRng; @@ -297,7 +298,7 @@ mod test { let a = Fq::random(OsRng); let mut b = a; b = b.square(); - assert_eq!(b.legendre(), Fq::ONE); + assert_eq!(b.legendre(), 1); let b = b.sqrt().unwrap(); let mut negb = b; @@ -310,7 +311,7 @@ mod test { for _ in 0..10000 { let mut b = c; b = b.square(); - assert_eq!(b.legendre(), Fq::ONE); + assert_eq!(b.legendre(), 1); b = b.sqrt().unwrap(); diff --git a/src/bn256/fq2.rs b/src/bn256/fq2.rs index 661c8c00..7325b21b 100644 --- a/src/bn256/fq2.rs +++ b/src/bn256/fq2.rs @@ -1,6 +1,6 @@ use super::fq::{Fq, NEGATIVE_ONE}; use crate::ff::{Field, FromUniformBytes, PrimeField, WithSmallOrderMulGroup}; -use crate::legendre::Legendre; +use crate::ff_ext::Legendre; use core::convert::TryInto; use core::ops::{Add, Mul, Neg, Sub}; use rand::RngCore; @@ -125,30 +125,6 @@ impl_binops_additive!(Fq2, Fq2); impl_binops_multiplicative!(Fq2, Fq2); impl_sum_prod!(Fq2); -impl Legendre for Fq2 { - type BasePrimeField = Fq; - - #[inline] - fn legendre_exp() -> &'static [u64] { - lazy_static::lazy_static! { - // (p-1) / 2 - static ref LEGENDRE_EXP: Vec = - (num_bigint::BigUint::from_bytes_le((-::ONE).to_repr().as_ref())/2usize).to_u64_digits(); - } - &LEGENDRE_EXP - } - - /// Norm of Fq2 as extension field in i over Fq - #[inline] - fn norm(&self) -> Self::BasePrimeField { - let mut t0 = self.c0; - let mut t1 = self.c1; - t0 = t0.square(); - t1 = t1.square(); - t1 + t0 - } -} - impl Fq2 { #[inline] pub const fn zero() -> Fq2 { @@ -317,6 +293,22 @@ impl Fq2 { tmp }) } + + /// Norm of Fq2 as extension field in i over Fq + #[inline] + fn norm(&self) -> Fq { + let mut t0 = self.c0; + let mut t1 = self.c1; + t0 = t0.square(); + t1 = t1.square(); + t1 + t0 + } +} + +impl Legendre for Fq2 { + fn legendre(&self) -> i64 { + self.norm().legendre() + } } impl Field for Fq2 { @@ -679,7 +671,7 @@ pub fn test_sqrt() { for _ in 0..10000 { let a = Fq2::random(&mut rng); - if a.legendre() == -Fq::ONE { + if a.legendre() == -1 { assert!(bool::from(a.sqrt().is_none())); } } @@ -688,7 +680,7 @@ pub fn test_sqrt() { let a = Fq2::random(&mut rng); let mut b = a; b.square_assign(); - assert_eq!(b.legendre(), Fq::ONE); + assert_eq!(b.legendre(), 1); let b = b.sqrt().unwrap(); let mut negb = b; @@ -701,7 +693,7 @@ pub fn test_sqrt() { for _ in 0..10000 { let mut b = c; b.square_assign(); - assert_eq!(b.legendre(), Fq::ONE); + assert_eq!(b.legendre(), 1); b = b.sqrt().unwrap(); diff --git a/src/bn256/fr.rs b/src/bn256/fr.rs index 9e02fcdc..c256f488 100644 --- a/src/bn256/fr.rs +++ b/src/bn256/fr.rs @@ -19,6 +19,7 @@ pub use table::FR_TABLE; use crate::impl_from_u64; use crate::arithmetic::{adc, mac, sbb}; +use crate::extend_field_legendre; use crate::ff::{FromUniformBytes, PrimeField, WithSmallOrderMulGroup}; use crate::{ field_bits, field_common, impl_add_binop_specify_output, impl_binops_additive, @@ -165,7 +166,7 @@ field_common!( R3 ); impl_sum_prod!(Fr); -prime_field_legendre!(Fr); +extend_field_legendre!(Fr); #[cfg(not(feature = "bn256-table"))] impl_from_u64!(Fr, R2); diff --git a/src/derive/field.rs b/src/derive/field.rs index 912a888e..566e1c71 100644 --- a/src/derive/field.rs +++ b/src/derive/field.rs @@ -26,8 +26,8 @@ macro_rules! field_common { ) => { /// Bernstein-Yang modular multiplicative inverter created for the modulus equal to /// the characteristic of the field to invert positive integers in the Montgomery form. - const BYINVERTOR: $crate::ff_inverse::BYInverter<6> = - $crate::ff_inverse::BYInverter::<6>::new(&$modulus.0, &$r2.0); + const BYINVERTOR: $crate::ff_ext::inverse::BYInverter<6> = + $crate::ff_ext::inverse::BYInverter::<6>::new(&$modulus.0, &$r2.0); impl $field { /// Returns zero, the additive identity. @@ -44,6 +44,7 @@ macro_rules! field_common { /// Returns the multiplicative inverse of the /// element. If it is zero, the method fails. + #[inline(always)] pub fn invert(&self) -> CtOption { if let Some(inverse) = BYINVERTOR.invert(&self.0) { CtOption::new(Self(inverse), Choice::from(1)) @@ -52,10 +53,14 @@ macro_rules! field_common { } } - // Returns the Legendre symbol, where the numerator and denominator + // Returns the Jacobi symbol, where the numerator and denominator // are the element and the characteristic of the field, respectively. + // The Jacobi symbol is applicable to odd moduli + // while the Legendre symbol is applicable to prime moduli. + // They are equivalent for prime moduli. + #[inline(always)] pub fn jacobi(&self) -> i64 { - $crate::ff_jacobi::jacobi::<5>(&self.0, &$modulus.0) + $crate::ff_ext::jacobi::jacobi::<5>(&self.0, &$modulus.0) } fn from_u512(limbs: [u64; 8]) -> $field { @@ -359,28 +364,6 @@ macro_rules! field_common { Ok(()) } } - - #[test] - fn test_jacobi() { - use rand::SeedableRng; - use $crate::ff::Field; - use $crate::legendre::Legendre; - let mut rng = rand_xorshift::XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - for _ in 0..100000 { - let e = $field::random(&mut rng); - assert_eq!( - e.legendre(), - match e.jacobi() { - 1 => $field::ONE, - -1 => -$field::ONE, - _ => $field::ZERO, - } - ); - } - } }; } diff --git a/src/ff_inverse.rs b/src/ff_ext/inverse.rs similarity index 100% rename from src/ff_inverse.rs rename to src/ff_ext/inverse.rs diff --git a/src/ff_jacobi.rs b/src/ff_ext/jacobi.rs similarity index 100% rename from src/ff_jacobi.rs rename to src/ff_ext/jacobi.rs diff --git a/src/ff_ext/mod.rs b/src/ff_ext/mod.rs new file mode 100644 index 00000000..2766a013 --- /dev/null +++ b/src/ff_ext/mod.rs @@ -0,0 +1,32 @@ +pub mod inverse; +pub mod jacobi; +use subtle::{Choice, ConstantTimeEq}; + +pub trait Legendre { + fn legendre(&self) -> i64; + + #[inline(always)] + fn ct_quadratic_non_residue(&self) -> Choice { + self.legendre().ct_eq(&-1) + } + + #[inline(always)] + fn ct_quadratic_residue(&self) -> Choice { + // The legendre symbol returns 0 for 0 + // and 1 for quadratic residues, + // we consider 0 a square hence quadratic residue. + self.legendre().ct_ne(&-1) + } +} + +#[macro_export] +macro_rules! extend_field_legendre { + ($field:ident ) => { + impl $crate::ff_ext::Legendre for $field { + #[inline(always)] + fn legendre(&self) -> i64 { + self.jacobi() + } + } + }; +} diff --git a/src/hash_to_curve.rs b/src/hash_to_curve.rs index 22251102..2602df40 100644 --- a/src/hash_to_curve.rs +++ b/src/hash_to_curve.rs @@ -5,7 +5,7 @@ use pasta_curves::arithmetic::CurveExt; use static_assertions::const_assert; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; -use crate::legendre::Legendre; +use crate::ff_ext::Legendre; /// Hashes over a message and writes the output to all of `buf`. /// Modified from https://github.com/zcash/pasta_curves/blob/7e3fc6a4919f6462a32b79dd226cb2587b7961eb/src/hashtocurve.rs#L11. diff --git a/src/legendre.rs b/src/legendre.rs deleted file mode 100644 index 7e4b9971..00000000 --- a/src/legendre.rs +++ /dev/null @@ -1,50 +0,0 @@ -use ff::{Field, PrimeField}; -use subtle::{Choice, ConstantTimeEq}; - -pub trait Legendre: Field { - type BasePrimeField: PrimeField; - - // This is (p-1)/2 where p is the modulus of the base prime field - fn legendre_exp() -> &'static [u64]; - - fn norm(&self) -> Self::BasePrimeField; - - #[inline] - fn legendre(&self) -> Self::BasePrimeField { - self.norm().pow(Self::legendre_exp()) - } - - #[inline] - fn ct_quadratic_residue(&self) -> Choice { - self.legendre().ct_eq(&Self::BasePrimeField::ONE) - } - - #[inline] - fn ct_quadratic_non_residue(&self) -> Choice { - self.legendre().ct_eq(&-Self::BasePrimeField::ONE) - } -} - -#[macro_export] -macro_rules! prime_field_legendre { - ($field:ident ) => { - impl $crate::legendre::Legendre for $field { - type BasePrimeField = Self; - - #[inline] - fn legendre_exp() -> &'static [u64] { - lazy_static::lazy_static! { - // (p-1) / 2 - static ref LEGENDRE_EXP: Vec = - (num_bigint::BigUint::from_bytes_le((-<$field as ff::Field>::ONE).to_repr().as_ref())/2usize).to_u64_digits(); - } - &*LEGENDRE_EXP - } - - #[inline] - fn norm(&self) -> Self::BasePrimeField { - self.clone() - } - } - }; -} diff --git a/src/lib.rs b/src/lib.rs index a09fb05f..0ef9d3e3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,12 +1,9 @@ mod arithmetic; -mod ff_inverse; -mod ff_jacobi; +pub mod ff_ext; pub mod fft; pub mod hash_to_curve; pub mod msm; pub mod multicore; -#[macro_use] -pub mod legendre; pub mod serde; pub mod bn256; diff --git a/src/pasta/mod.rs b/src/pasta/mod.rs index 078b663e..164697b5 100644 --- a/src/pasta/mod.rs +++ b/src/pasta/mod.rs @@ -38,9 +38,6 @@ const ENDO_PARAMS_EP: EndoParameters = EndoParameters { endo!(Eq, Fp, ENDO_PARAMS_EQ); endo!(Ep, Fq, ENDO_PARAMS_EP); -prime_field_legendre!(Fp); -prime_field_legendre!(Fq); - #[test] fn test_endo() { use ff::Field; @@ -74,9 +71,3 @@ fn test_endo() { } } } - -#[test] -fn test_quadratic_residue() { - crate::tests::field::random_quadratic_residue_test::(); - crate::tests::field::random_quadratic_residue_test::(); -} diff --git a/src/secp256k1/fp.rs b/src/secp256k1/fp.rs index db496559..1538544f 100644 --- a/src/secp256k1/fp.rs +++ b/src/secp256k1/fp.rs @@ -1,4 +1,5 @@ use crate::arithmetic::{adc, mac, macx, sbb}; +use crate::extend_field_legendre; use crate::ff::{FromUniformBytes, PrimeField, WithSmallOrderMulGroup}; use crate::{ field_arithmetic, field_bits, field_common, field_specific, impl_add_binop_specify_output, @@ -287,7 +288,7 @@ impl WithSmallOrderMulGroup<3> for Fp { const ZETA: Self = ZETA; } -prime_field_legendre!(Fp); +extend_field_legendre!(Fp); #[cfg(test)] mod test { diff --git a/src/secp256k1/fq.rs b/src/secp256k1/fq.rs index c6cb8e06..09087227 100644 --- a/src/secp256k1/fq.rs +++ b/src/secp256k1/fq.rs @@ -1,4 +1,5 @@ use crate::arithmetic::{adc, mac, macx, sbb}; +use crate::extend_field_legendre; use crate::ff::{FromUniformBytes, PrimeField, WithSmallOrderMulGroup}; use crate::{ field_arithmetic, field_bits, field_common, field_specific, impl_add_binop_specify_output, @@ -294,7 +295,7 @@ impl WithSmallOrderMulGroup<3> for Fq { const ZETA: Self = ZETA; } -prime_field_legendre!(Fq); +extend_field_legendre!(Fq); #[cfg(test)] mod test { diff --git a/src/secp256r1/fp.rs b/src/secp256r1/fp.rs index 331545c3..f3497c81 100644 --- a/src/secp256r1/fp.rs +++ b/src/secp256r1/fp.rs @@ -1,4 +1,5 @@ use crate::arithmetic::{adc, mac, macx, sbb}; +use crate::extend_field_legendre; use crate::ff::{FromUniformBytes, PrimeField, WithSmallOrderMulGroup}; use crate::{ field_arithmetic, field_bits, field_common, field_specific, impl_add_binop_specify_output, @@ -305,7 +306,7 @@ impl WithSmallOrderMulGroup<3> for Fp { const ZETA: Self = ZETA; } -prime_field_legendre!(Fp); +extend_field_legendre!(Fp); #[cfg(test)] mod test { diff --git a/src/secp256r1/fq.rs b/src/secp256r1/fq.rs index 077ec331..86005d35 100644 --- a/src/secp256r1/fq.rs +++ b/src/secp256r1/fq.rs @@ -1,4 +1,5 @@ use crate::arithmetic::{adc, mac, macx, sbb}; +use crate::extend_field_legendre; use crate::ff::{FromUniformBytes, PrimeField, WithSmallOrderMulGroup}; use core::fmt; use core::ops::{Add, Mul, Neg, Sub}; @@ -289,7 +290,7 @@ impl WithSmallOrderMulGroup<3> for Fq { const ZETA: Self = ZETA; } -prime_field_legendre!(Fq); +extend_field_legendre!(Fq); #[cfg(test)] mod test { diff --git a/src/tests/curve.rs b/src/tests/curve.rs index 2f93bbb4..95bece49 100644 --- a/src/tests/curve.rs +++ b/src/tests/curve.rs @@ -1,8 +1,8 @@ #![allow(clippy::eq_op)] use crate::ff::Field; +use crate::ff_ext::Legendre; use crate::group::prime::PrimeCurveAffine; -use crate::legendre::Legendre; use crate::tests::fe_from_str; use crate::{group::GroupEncoding, serde::SerdeObject}; use crate::{hash_to_curve, CurveAffine, CurveExt}; diff --git a/src/tests/field.rs b/src/tests/field.rs index 02f5509f..020bb985 100644 --- a/src/tests/field.rs +++ b/src/tests/field.rs @@ -1,7 +1,6 @@ use crate::serde::SerdeObject; -use crate::{ff::Field, legendre::Legendre}; +use crate::{ff::Field, ff_ext::Legendre}; use ark_std::{end_timer, start_timer}; -use ff::PrimeField; use rand::{RngCore, SeedableRng}; use rand_xorshift::XorShiftRng; @@ -296,7 +295,7 @@ where end_timer!(start); } -pub fn random_quadratic_residue_test() { +pub fn random_quadratic_residue_test() { let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5,