diff --git a/CHANGELOG.md b/CHANGELOG.md index b6614c0ea..c65028352 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ - [\#408](https://github.com/arkworks-rs/algebra/pull/408) (`ark-ff`) Change the output of `Display` formatting for BigInt & Fp from hex to decimal. - [\#412](https://github.com/arkworks-rs/algebra/pull/412) (`ark-poly`) Rename UV/MVPolynomial to DenseUV/MVPolynomial. - [\#417](https://github.com/arkworks-rs/algebra/pull/417) (`ark-ff`) Remove `ToBytes` and `FromBytes`. +- [\#422](https://github.com/arkworks-rs/algebra/pull/422) (`ark-ff`) Remove `SquareRootField`, and move functionality to `Field` - [\#425](https://github.com/arkworks-rs/algebra/pull/425) (`ark-ec`) Refactor `VariableBase` struct to `VariableBaseMSM` trait and implement it for `GroupProjective`. - [\#438](https://github.com/arkworks-rs/algebra/pull/438) (`ark-ec`) Rename modules, structs, and traits related to `ec`. - `short_weierstrass_jacobian` → `short_weierstrass` diff --git a/ec/src/hashing/curve_maps/swu/mod.rs b/ec/src/hashing/curve_maps/swu/mod.rs index 3fca8e090..0245ffab3 100644 --- a/ec/src/hashing/curve_maps/swu/mod.rs +++ b/ec/src/hashing/curve_maps/swu/mod.rs @@ -1,5 +1,5 @@ use crate::models::short_weierstrass::SWCurveConfig; -use ark_ff::{BigInteger, Field, One, PrimeField, SquareRootField, Zero}; +use ark_ff::{BigInteger, Field, One, PrimeField, Zero}; use ark_std::string::ToString; use core::marker::PhantomData; diff --git a/ec/src/hashing/tests/mod.rs b/ec/src/hashing/tests/mod.rs index 7f59ccc9f..ea4b12c44 100644 --- a/ec/src/hashing/tests/mod.rs +++ b/ec/src/hashing/tests/mod.rs @@ -12,11 +12,10 @@ use crate::{ CurveConfig, }; use ark_ff::{ - biginteger::BigInteger64, field_hashers::DefaultFieldHasher, fields::Fp64, BigInt, MontBackend, - MontFp, + biginteger::BigInteger64, field_hashers::DefaultFieldHasher, fields::Fp64, BigInt, Field, + MontBackend, MontFp, }; -use ark_ff::SquareRootField; use ark_std::vec::Vec; use ark_test_curves::bls12_381::{Fq, Fq2, Fq6}; use hashbrown::HashMap; @@ -119,7 +118,7 @@ fn test_field_division() { /// Check that the hashing parameters are sane: zeta should be a non-square #[test] fn checking_the_hashing_parameters() { - assert!(!SquareRootField::legendre(&TestSWUMapToCurveParams::ZETA).is_qr()); + assert!(!Field::legendre(&TestSWUMapToCurveParams::ZETA).is_qr()); } /// The point of the test is to get a simple SWU compatible curve and make diff --git a/ec/src/lib.rs b/ec/src/lib.rs index f795272c6..c85ae86b2 100644 --- a/ec/src/lib.rs +++ b/ec/src/lib.rs @@ -20,7 +20,7 @@ extern crate derivative; extern crate ark_std; use ark_ff::{ - fields::{Field, PrimeField, SquareRootField}, + fields::{Field, PrimeField}, UniformRand, }; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; @@ -50,7 +50,7 @@ pub mod wnaf; /// how to compute a pairing over a pairing-friendly curve. pub trait PairingEngine: Sized + 'static + Copy + Debug + Sync + Send + Eq + PartialEq { /// This is the scalar field of the G1/G2 groups. - type Fr: PrimeField + SquareRootField; + type Fr: PrimeField; /// The projective representation of an element in G1. type G1Projective: ProjectiveCurve @@ -87,10 +87,10 @@ pub trait PairingEngine: Sized + 'static + Copy + Debug + Sync + Send + Eq + Par type G2Prepared: Default + Clone + Send + Sync + Debug + From; /// The base field that hosts G1. - type Fq: PrimeField + SquareRootField; + type Fq: PrimeField; /// The extension field that hosts G2. - type Fqe: SquareRootField; + type Fqe: Field; /// The extension field that hosts the target group of the pairing. type Fqk: Field; @@ -161,7 +161,7 @@ pub trait ProjectiveCurve: + From<::Affine> { type Config: CurveConfig; - type ScalarField: PrimeField + SquareRootField; + type ScalarField: PrimeField; type BaseField: Field; type Affine: AffineCurve< Config = Self::Config, @@ -251,7 +251,7 @@ pub trait AffineCurve: /// The group defined by this curve has order `h * r` where `r` is a large /// prime. `Self::ScalarField` is the prime field defined by `r` - type ScalarField: PrimeField + SquareRootField + Into<::BigInt>; + type ScalarField: PrimeField + Into<::BigInt>; /// The finite field over which this curve is defined. type BaseField: Field; diff --git a/ec/src/models/bls12/mod.rs b/ec/src/models/bls12/mod.rs index 1ad90d72b..a2d6228f4 100644 --- a/ec/src/models/bls12/mod.rs +++ b/ec/src/models/bls12/mod.rs @@ -6,7 +6,7 @@ use ark_ff::fields::{ fp12_2over3over2::{Fp12, Fp12Config}, fp2::Fp2Config, fp6_3over2::Fp6Config, - BitIteratorBE, Field, Fp2, PrimeField, SquareRootField, + BitIteratorBE, Field, Fp2, PrimeField, }; use core::marker::PhantomData; use num_traits::{One, Zero}; @@ -33,7 +33,7 @@ pub trait Bls12Parameters: 'static { /// What kind of twist is this? const TWIST_TYPE: TwistType; - type Fp: PrimeField + SquareRootField + Into<::BigInt>; + type Fp: PrimeField + Into<::BigInt>; type Fp2Config: Fp2Config; type Fp6Config: Fp6Config; type Fp12Config: Fp12Config; diff --git a/ec/src/models/bn/mod.rs b/ec/src/models/bn/mod.rs index 2a6e69a4e..0786283cd 100644 --- a/ec/src/models/bn/mod.rs +++ b/ec/src/models/bn/mod.rs @@ -6,7 +6,7 @@ use ark_ff::fields::{ fp12_2over3over2::{Fp12, Fp12Config}, fp2::Fp2Config, fp6_3over2::Fp6Config, - Field, Fp2, PrimeField, SquareRootField, + Field, Fp2, PrimeField, }; use num_traits::One; @@ -31,7 +31,7 @@ pub trait BnParameters: 'static { const TWIST_TYPE: TwistType; const TWIST_MUL_BY_Q_X: Fp2; const TWIST_MUL_BY_Q_Y: Fp2; - type Fp: PrimeField + SquareRootField + Into<::BigInt>; + type Fp: PrimeField + Into<::BigInt>; type Fp2Config: Fp2Config; type Fp6Config: Fp6Config; type Fp12Config: Fp12Config; diff --git a/ec/src/models/bw6/mod.rs b/ec/src/models/bw6/mod.rs index 2f263f5b2..7b7de6549 100644 --- a/ec/src/models/bw6/mod.rs +++ b/ec/src/models/bw6/mod.rs @@ -5,7 +5,7 @@ use crate::{ use ark_ff::fields::{ fp3::Fp3Config, fp6_2over3::{Fp6, Fp6Config}, - BitIteratorBE, Field, PrimeField, SquareRootField, + BitIteratorBE, Field, PrimeField, }; use num_traits::One; @@ -24,7 +24,7 @@ pub trait BW6Parameters: 'static + Eq + PartialEq { const ATE_LOOP_COUNT_2: &'static [i8]; const ATE_LOOP_COUNT_2_IS_NEGATIVE: bool; const TWIST_TYPE: TwistType; - type Fp: PrimeField + SquareRootField + Into<::BigInt>; + type Fp: PrimeField + Into<::BigInt>; type Fp3Config: Fp3Config; type Fp6Config: Fp6Config; type G1Parameters: SWCurveConfig; diff --git a/ec/src/models/mnt4/mod.rs b/ec/src/models/mnt4/mod.rs index c4c3ba2e6..f8b096ad5 100644 --- a/ec/src/models/mnt4/mod.rs +++ b/ec/src/models/mnt4/mod.rs @@ -5,7 +5,7 @@ use crate::{ use ark_ff::{ fp2::{Fp2, Fp2Config}, fp4::{Fp4, Fp4Config}, - BitIteratorBE, Field, PrimeField, SquareRootField, + BitIteratorBE, Field, PrimeField, }; use num_traits::{One, Zero}; @@ -30,8 +30,8 @@ pub trait MNT4Parameters: 'static { const FINAL_EXPONENT_LAST_CHUNK_1: ::BigInt; const FINAL_EXPONENT_LAST_CHUNK_W0_IS_NEG: bool; const FINAL_EXPONENT_LAST_CHUNK_ABS_OF_W0: ::BigInt; - type Fp: PrimeField + SquareRootField + Into<::BigInt>; - type Fr: PrimeField + SquareRootField + Into<::BigInt>; + type Fp: PrimeField + Into<::BigInt>; + type Fr: PrimeField + Into<::BigInt>; type Fp2Config: Fp2Config; type Fp4Config: Fp4Config; type G1Parameters: SWCurveConfig; diff --git a/ec/src/models/mnt6/mod.rs b/ec/src/models/mnt6/mod.rs index 0e5c4b6b3..55a16ba69 100644 --- a/ec/src/models/mnt6/mod.rs +++ b/ec/src/models/mnt6/mod.rs @@ -5,7 +5,7 @@ use crate::{ use ark_ff::{ fp3::{Fp3, Fp3Config}, fp6_2over3::{Fp6, Fp6Config}, - BitIteratorBE, Field, PrimeField, SquareRootField, + BitIteratorBE, Field, PrimeField, }; use num_traits::{One, Zero}; @@ -30,8 +30,8 @@ pub trait MNT6Parameters: 'static { const FINAL_EXPONENT_LAST_CHUNK_1: ::BigInt; const FINAL_EXPONENT_LAST_CHUNK_W0_IS_NEG: bool; const FINAL_EXPONENT_LAST_CHUNK_ABS_OF_W0: ::BigInt; - type Fp: PrimeField + SquareRootField + Into<::BigInt>; - type Fr: PrimeField + SquareRootField + Into<::BigInt>; + type Fp: PrimeField + Into<::BigInt>; + type Fr: PrimeField + Into<::BigInt>; type Fp3Config: Fp3Config; type Fp6Config: Fp6Config; type G1Parameters: SWCurveConfig; diff --git a/ec/src/models/mod.rs b/ec/src/models/mod.rs index 69dc8e117..c44ce75a5 100644 --- a/ec/src/models/mod.rs +++ b/ec/src/models/mod.rs @@ -1,4 +1,4 @@ -use ark_ff::{Field, PrimeField, SquareRootField}; +use ark_ff::{Field, PrimeField}; pub mod bls12; pub mod bn; @@ -16,10 +16,10 @@ pub mod twisted_edwards; /// prime-order subgroup of the curve. pub trait CurveConfig: Send + Sync + Sized + 'static { /// Base field that the curve is defined over. - type BaseField: Field + SquareRootField; + type BaseField: Field; /// Finite prime field corresponding to an appropriate prime-order subgroup /// of the curve group. - type ScalarField: PrimeField + SquareRootField + Into<::BigInt>; + type ScalarField: PrimeField + Into<::BigInt>; const COFACTOR: &'static [u64]; const COFACTOR_INV: Self::ScalarField; diff --git a/ec/src/models/short_weierstrass.rs b/ec/src/models/short_weierstrass.rs index 6297cca27..ae5b90d9e 100644 --- a/ec/src/models/short_weierstrass.rs +++ b/ec/src/models/short_weierstrass.rs @@ -11,7 +11,7 @@ use ark_std::{ }; use ark_ff::{ - fields::{Field, PrimeField, SquareRootField}, + fields::{Field, PrimeField}, ToConstraintField, UniformRand, }; diff --git a/ec/src/models/twisted_edwards.rs b/ec/src/models/twisted_edwards.rs index 27ed607c9..5ccd536fb 100644 --- a/ec/src/models/twisted_edwards.rs +++ b/ec/src/models/twisted_edwards.rs @@ -18,7 +18,7 @@ use num_traits::{One, Zero}; use zeroize::Zeroize; use ark_ff::{ - fields::{Field, PrimeField, SquareRootField}, + fields::{Field, PrimeField}, ToConstraintField, UniformRand, }; diff --git a/ff/src/fields/arithmetic.rs b/ff/src/fields/arithmetic.rs index 985322595..3e02dd0db 100644 --- a/ff/src/fields/arithmetic.rs +++ b/ff/src/fields/arithmetic.rs @@ -1,66 +1,3 @@ -macro_rules! sqrt_impl { - ($Self:ident, $P:tt, $self:expr) => {{ - // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) - // Actually this is just normal Tonelli-Shanks; since `P::Generator` - // is a quadratic non-residue, `P::ROOT_OF_UNITY = P::GENERATOR ^ t` - // is also a quadratic non-residue (since `t` is odd). - if $self.is_zero() { - return Some($Self::zero()); - } - // Try computing the square root (x at the end of the algorithm) - // Check at the end of the algorithm if x was a square root - // Begin Tonelli-Shanks - let mut z = $Self::qnr_to_t(); - let mut w = $self.pow($P::TRACE_MINUS_ONE_DIV_TWO); - let mut x = w * $self; - let mut b = x * &w; - - let mut v = $P::TWO_ADICITY as usize; - - while !b.is_one() { - let mut k = 0usize; - - let mut b2k = b; - while !b2k.is_one() { - // invariant: b2k = b^(2^k) after entering this loop - b2k.square_in_place(); - k += 1; - } - - if k == ($P::TWO_ADICITY as usize) { - // We are in the case where self^(T * 2^k) = x^(P::MODULUS - 1) = 1, - // which means that no square root exists. - return None; - } - let j = v - k; - w = z; - for _ in 1..j { - w.square_in_place(); - } - - z = w.square(); - b *= &z; - x *= &w; - v = k; - } - // Is x the square root? If so, return it. - if (x.square() == *$self) { - return Some(x); - } else { - // Consistency check that if no square root is found, - // it is because none exists. - #[cfg(debug_assertions)] - { - use crate::fields::LegendreSymbol::*; - if ($self.legendre() != QuadraticNonResidue) { - panic!("Input has a square root per its legendre symbol, but it was not found") - } - } - None - } - }}; -} - // Implements AddAssign on Self by deferring to an implementation on &Self #[macro_export] macro_rules! impl_additive_ops_from_ref { diff --git a/ff/src/fields/mod.rs b/ff/src/fields/mod.rs index 02836e63b..5b240a122 100644 --- a/ff/src/fields/mod.rs +++ b/ff/src/fields/mod.rs @@ -130,8 +130,12 @@ pub trait Field: + From { type BasePrimeField: PrimeField; + type BasePrimeFieldIter: Iterator; + /// Determines the algorithm for computing square roots. + const SQRT_PRECOMP: Option>; + /// The additive identity of the field. const ZERO: Self; /// The multiplicative identity of the field. @@ -187,6 +191,29 @@ pub trait Field: /// from a hash-function or RNG output. fn from_random_bytes_with_flags(bytes: &[u8]) -> Option<(Self, F)>; + /// Returns a `LegendreSymbol`, which indicates whether this field element + /// is 1 : a quadratic residue + /// 0 : equal to 0 + /// -1 : a quadratic non-residue + fn legendre(&self) -> LegendreSymbol; + + /// Returns the square root of self, if it exists. + #[must_use] + fn sqrt(&self) -> Option { + match Self::SQRT_PRECOMP { + Some(tv) => tv.sqrt(self), + None => unimplemented!(), + } + } + + /// Sets `self` to be the square root of `self`, if it exists. + fn sqrt_in_place(&mut self) -> Option<&mut Self> { + (*self).sqrt().map(|sqrt| { + *self = sqrt; + self + }) + } + /// Returns `self * self`. #[must_use] fn square(&self) -> Self; @@ -417,30 +444,13 @@ pub trait PrimeField: } } -/// The interface for a field that supports an efficient square-root operation. -pub trait SquareRootField: Field { - /// Returns a `LegendreSymbol`, which indicates whether this field element - /// is - /// - 1: a quadratic residue - /// - 0: equal to 0 - /// - -1: a quadratic non-residue - fn legendre(&self) -> LegendreSymbol; - - /// Returns the square root of self, if it exists. - #[must_use] - fn sqrt(&self) -> Option; - - /// Sets `self` to be the square root of `self`, if it exists. - fn sqrt_in_place(&mut self) -> Option<&mut Self>; -} - /// Indication of the field element's quadratic residuosity /// /// # Examples /// ``` /// # use ark_std::test_rng; /// # use ark_std::UniformRand; -/// # use ark_test_curves::{LegendreSymbol, Field, SquareRootField, bls12_381::Fq as Fp}; +/// # use ark_test_curves::{LegendreSymbol, Field, bls12_381::Fq as Fp}; /// let a: Fp = Fp::rand(&mut test_rng()); /// let b = a.square(); /// assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue); @@ -459,7 +469,7 @@ impl LegendreSymbol { /// ``` /// # use ark_std::test_rng; /// # use ark_std::UniformRand; - /// # use ark_test_curves::{LegendreSymbol, Field, SquareRootField, bls12_381::Fq as Fp}; + /// # use ark_test_curves::{LegendreSymbol, Field, bls12_381::Fq as Fp}; /// let a: Fp = Fp::rand(&mut test_rng()); /// let b: Fp = a.square(); /// assert!(!b.legendre().is_zero()); @@ -472,7 +482,7 @@ impl LegendreSymbol { /// /// # Examples /// ``` - /// # use ark_test_curves::{Fp2Config, LegendreSymbol, SquareRootField, bls12_381::{Fq, Fq2Config}}; + /// # use ark_test_curves::{Fp2Config, Field, LegendreSymbol, bls12_381::{Fq, Fq2Config}}; /// let a: Fq = Fq2Config::NONRESIDUE; /// assert!(a.legendre().is_qnr()); /// ``` @@ -486,7 +496,7 @@ impl LegendreSymbol { /// # use ark_std::test_rng; /// # use ark_test_curves::bls12_381::Fq as Fp; /// # use ark_std::UniformRand; - /// # use ark_ff::{LegendreSymbol, Field, SquareRootField}; + /// # use ark_ff::{LegendreSymbol, Field}; /// let a: Fp = Fp::rand(&mut test_rng()); /// let b: Fp = a.square(); /// assert!(b.legendre().is_qr()); @@ -496,6 +506,73 @@ impl LegendreSymbol { } } +#[non_exhaustive] +pub enum SqrtPrecomputation { + // Tonelli-Shanks algorithm works for all elements, no matter what the modulus is. + TonelliShanks(u32, &'static dyn AsRef<[u64]>, F), +} + +impl SqrtPrecomputation { + fn sqrt(&self, elem: &F) -> Option { + match self { + SqrtPrecomputation::TonelliShanks(two_adicity, trace_minus_one_div_two, qnr_to_t) => { + // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) + // Actually this is just normal Tonelli-Shanks; since `P::Generator` + // is a quadratic non-residue, `P::ROOT_OF_UNITY = P::GENERATOR ^ t` + // is also a quadratic non-residue (since `t` is odd). + if elem.is_zero() { + return Some(F::zero()); + } + // Try computing the square root (x at the end of the algorithm) + // Check at the end of the algorithm if x was a square root + // Begin Tonelli-Shanks + let mut z = *qnr_to_t; + let mut w = elem.pow(trace_minus_one_div_two); + let mut x = w * elem; + let mut b = x * &w; + + let mut v = *two_adicity as usize; + + while !b.is_one() { + let mut k = 0usize; + + let mut b2k = b; + while !b2k.is_one() { + // invariant: b2k = b^(2^k) after entering this loop + b2k.square_in_place(); + k += 1; + } + + if k == (*two_adicity as usize) { + // We are in the case where self^(T * 2^k) = x^(P::MODULUS - 1) = 1, + // which means that no square root exists. + return None; + } + let j = v - k; + w = z; + for _ in 1..j { + w.square_in_place(); + } + + z = w.square(); + b *= &z; + x *= &w; + v = k; + } + // Is x the square root? If so, return it. + if x.square() == *elem { + return Some(x); + } else { + // Consistency check that if no square root is found, + // it is because none exists. + debug_assert!(!matches!(elem.legendre(), LegendreSymbol::QuadraticResidue)); + None + } + }, + } + } +} + /// Iterates over a slice of `u64` in *big-endian* order. #[derive(Debug)] pub struct BitIteratorBE> { diff --git a/ff/src/fields/models/cubic_extension.rs b/ff/src/fields/models/cubic_extension.rs index 99681b335..fcfaaadd3 100644 --- a/ff/src/fields/models/cubic_extension.rs +++ b/ff/src/fields/models/cubic_extension.rs @@ -21,11 +21,11 @@ use ark_std::rand::{ use crate::{ fields::{Field, PrimeField}, - ToConstraintField, UniformRand, + LegendreSymbol, SqrtPrecomputation, ToConstraintField, UniformRand, }; /// Defines a Cubic extension field from a cubic non-residue. -pub trait CubicExtConfig: 'static + Send + Sync { +pub trait CubicExtConfig: 'static + Send + Sync + Sized { /// The prime field that this cubic extension is eventually an extension of. type BasePrimeField: PrimeField; /// The base field that this field is a cubic extension of. @@ -38,6 +38,9 @@ pub trait CubicExtConfig: 'static + Send + Sync { /// Frobenius endomorphism. type FrobCoeff: Field; + /// Determines the algorithm for computing square roots. + const SQRT_PRECOMP: Option>>; + /// The degree of the extension over the base prime field. const DEGREE_OVER_BASE_PRIME_FIELD: usize; @@ -156,6 +159,9 @@ type BaseFieldIter

= <

::BaseField as Field>::BasePrimeFi impl Field for CubicExtField

{ type BasePrimeField = P::BasePrimeField; type BasePrimeFieldIter = Chain, Chain, BaseFieldIter

>>; + + const SQRT_PRECOMP: Option> = P::SQRT_PRECOMP; + const ZERO: Self = Self::new(P::BaseField::ZERO, P::BaseField::ZERO, P::BaseField::ZERO); const ONE: Self = Self::new(P::BaseField::ONE, P::BaseField::ZERO, P::BaseField::ZERO); @@ -251,6 +257,11 @@ impl Field for CubicExtField

{ self } + /// Returns the Legendre symbol. + fn legendre(&self) -> LegendreSymbol { + self.norm().legendre() + } + fn inverse(&self) -> Option { if self.is_zero() { None diff --git a/ff/src/fields/models/fp/mod.rs b/ff/src/fields/models/fp/mod.rs index 4e540fb8d..87674cbd5 100644 --- a/ff/src/fields/models/fp/mod.rs +++ b/ff/src/fields/models/fp/mod.rs @@ -18,7 +18,7 @@ use ark_std::{ mod montgomery_backend; pub use montgomery_backend::*; -use crate::{BigInt, BigInteger, FftField, Field, LegendreSymbol, PrimeField, SquareRootField}; +use crate::{BigInt, BigInteger, FftField, Field, LegendreSymbol, PrimeField, SqrtPrecomputation}; /// A trait that specifies the configuration of a prime field. /// Also specifies how to perform arithmetic on field elements. pub trait FpConfig: Send + Sync + 'static + Sized { @@ -59,6 +59,11 @@ pub trait FpConfig: Send + Sync + 'static + Sized { /// FFT. const LARGE_SUBGROUP_ROOT_OF_UNITY: Option> = None; + /// Precomputed material for use when computing square roots. + /// Currently uses the generic Tonelli-Shanks, + /// which works for every modulus. + const SQRT_PRECOMP: Option>>; + /// Set a += b. fn add_assign(a: &mut Fp, b: &Fp); @@ -77,68 +82,6 @@ pub trait FpConfig: Send + Sync + 'static + Sized { /// Compute a^{-1} if `a` is not zero. fn inverse(a: &Fp) -> Option>; - /// Compute the square root of a, if it exists. - fn square_root(a: &Fp) -> Option> { - // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) - // Actually this is just normal Tonelli-Shanks; since [`Self::GENERATOR`] - // is a quadratic non-residue, `P::ROOT_OF_UNITY = P::GENERATOR ^ t` - // is also a quadratic non-residue (since `t` is odd). - if a.is_zero() { - return Some(Fp::zero()); - } - // Try computing the square root (x at the end of the algorithm) - // Check at the end of the algorithm if x was a square root - // Begin Tonelli-Shanks - let mut z = Fp::TWO_ADIC_ROOT_OF_UNITY; - let mut w = a.pow(Fp::::TRACE_MINUS_ONE_DIV_TWO); - let mut x = w * a; - let mut b = x * &w; - - let mut v = Self::TWO_ADICITY as usize; - - while !b.is_one() { - let mut k = 0usize; - - let mut b2k = b; - while !b2k.is_one() { - // invariant: b2k = b^(2^k) after entering this loop - b2k.square_in_place(); - k += 1; - } - - if k == (Self::TWO_ADICITY as usize) { - // We are in the case where self^(T * 2^k) = x^(P::MODULUS - 1) = 1, - // which means that no square root exists. - return None; - } - let j = v - k; - w = z; - for _ in 1..j { - w.square_in_place(); - } - - z = w.square(); - b *= &z; - x *= &w; - v = k; - } - // Is x the square root? If so, return it. - if x.square() == *a { - Some(x) - } else { - // Consistency check that if no square root is found, - // it is because none exists. - #[cfg(debug_assertions)] - { - use crate::fields::LegendreSymbol::*; - if a.legendre() != QuadraticNonResidue { - panic!("Input has a square root per its Legendre symbol, but it was not found") - } - } - None - } - } - /// Construct a field element from an integer in the range /// `0..(Self::MODULUS - 1)`. Returns `None` if the integer is outside /// this range. @@ -233,6 +176,7 @@ impl, const N: usize> Field for Fp { type BasePrimeField = Self; type BasePrimeFieldIter = iter::Once; + const SQRT_PRECOMP: Option> = P::SQRT_PRECOMP; const ZERO: Self = P::ZERO; const ONE: Self = P::ONE; @@ -347,6 +291,21 @@ impl, const N: usize> Field for Fp { /// The Frobenius map has no effect in a prime field. #[inline] fn frobenius_map(&mut self, _: usize) {} + + #[inline] + fn legendre(&self) -> LegendreSymbol { + use crate::fields::LegendreSymbol::*; + + // s = self^((MODULUS - 1) // 2) + let s = self.pow(Self::MODULUS_MINUS_ONE_DIV_TWO); + if s.is_zero() { + Zero + } else if s.is_one() { + QuadraticResidue + } else { + QuadraticNonResidue + } + } } impl, const N: usize> PrimeField for Fp { @@ -376,35 +335,6 @@ impl, const N: usize> FftField for Fp { const LARGE_SUBGROUP_ROOT_OF_UNITY: Option = P::LARGE_SUBGROUP_ROOT_OF_UNITY; } -impl, const N: usize> SquareRootField for Fp { - #[inline] - fn legendre(&self) -> LegendreSymbol { - use crate::fields::LegendreSymbol::*; - - // s = self^((MODULUS - 1) // 2) - let s = self.pow(Self::MODULUS_MINUS_ONE_DIV_TWO); - if s.is_zero() { - Zero - } else if s.is_one() { - QuadraticResidue - } else { - QuadraticNonResidue - } - } - - #[inline] - fn sqrt(&self) -> Option { - P::square_root(self) - } - - fn sqrt_in_place(&mut self) -> Option<&mut Self> { - (*self).sqrt().map(|sqrt| { - *self = sqrt; - self - }) - } -} - /// Note that this implementation of `Ord` compares field elements viewing /// them as integers in the range 0, 1, ..., P::MODULUS - 1. However, other /// implementations of `PrimeField` might choose a different ordering, and diff --git a/ff/src/fields/models/fp/montgomery_backend.rs b/ff/src/fields/models/fp/montgomery_backend.rs index d339676a8..f10239a3c 100644 --- a/ff/src/fields/models/fp/montgomery_backend.rs +++ b/ff/src/fields/models/fp/montgomery_backend.rs @@ -1,7 +1,7 @@ use ark_std::{marker::PhantomData, Zero}; use super::{Fp, FpConfig}; -use crate::{biginteger::arithmetic as fa, BigInt, BigInteger}; +use crate::{biginteger::arithmetic as fa, BigInt, BigInteger, PrimeField, SqrtPrecomputation}; use ark_ff_macros::unroll_for_loops; /// A trait that specifies the constants and arithmetic procedures @@ -55,6 +55,15 @@ pub trait MontConfig: 'static + Sync + Send + Sized { /// Used for mixed-radix FFT. const LARGE_SUBGROUP_ROOT_OF_UNITY: Option, N>> = None; + /// Precomputed material for use when computing square roots. + /// The default is to use the standard Tonelli-Shanks algorithm. + const SQRT_PRECOMP: Option, N>>> = + Some(SqrtPrecomputation::TonelliShanks( + >::TWO_ADICITY, + &, N>>::TRACE_MINUS_ONE_DIV_TWO, + Self::TWO_ADIC_ROOT_OF_UNITY, + )); + /// Set a += b; fn add_assign(a: &mut Fp, N>, b: &Fp, N>) { // This cannot exceed the backing capacity. @@ -385,6 +394,7 @@ impl, const N: usize> FpConfig for MontBackend { const SMALL_SUBGROUP_BASE: Option = T::SMALL_SUBGROUP_BASE; const SMALL_SUBGROUP_BASE_ADICITY: Option = T::SMALL_SUBGROUP_BASE_ADICITY; const LARGE_SUBGROUP_ROOT_OF_UNITY: Option> = T::LARGE_SUBGROUP_ROOT_OF_UNITY; + const SQRT_PRECOMP: Option>> = T::SQRT_PRECOMP; fn add_assign(a: &mut Fp, b: &Fp) { T::add_assign(a, b) diff --git a/ff/src/fields/models/fp3.rs b/ff/src/fields/models/fp3.rs index 91f75236b..f3dad8620 100644 --- a/ff/src/fields/models/fp3.rs +++ b/ff/src/fields/models/fp3.rs @@ -5,8 +5,7 @@ use core::marker::PhantomData; /// Trait that specifies constants and methods for defining degree-three extension fields. pub trait Fp3Config: 'static + Send + Sync + Sized { /// Base prime field underlying this extension. - type Fp: PrimeField + SquareRootField; - + type Fp: PrimeField; /// Cubic non-residue in `Self::Fp` used to construct the extension /// field. That is, `NONRESIDUE` is such that the cubic polynomial /// `f(X) = X^3 - Self::NONRESIDUE` in Fp\[X\] is irreducible in `Self::Fp`. @@ -42,6 +41,13 @@ impl CubicExtConfig for Fp3ConfigWrapper

{ const NONRESIDUE: Self::BaseField = P::NONRESIDUE; + const SQRT_PRECOMP: Option>> = + Some(SqrtPrecomputation::TonelliShanks( + P::TWO_ADICITY, + &P::TRACE_MINUS_ONE_DIV_TWO, + P::QUADRATIC_NONRESIDUE_TO_T, + )); + const FROBENIUS_COEFF_C1: &'static [Self::FrobCoeff] = P::FROBENIUS_COEFF_FP3_C1; const FROBENIUS_COEFF_C2: &'static [Self::FrobCoeff] = P::FROBENIUS_COEFF_FP3_C2; @@ -90,30 +96,4 @@ impl Fp3

{ self.c1.mul_assign(value); self.c2.mul_assign(value); } - - /// Returns the value of QNR^T. - #[inline] - pub fn qnr_to_t() -> Self { - P::QUADRATIC_NONRESIDUE_TO_T - } -} - -impl SquareRootField for Fp3

{ - /// Returns the Legendre symbol. - fn legendre(&self) -> LegendreSymbol { - self.norm().legendre() - } - - /// Returns the square root of self, if it exists. - fn sqrt(&self) -> Option { - sqrt_impl!(Self, P, self) - } - - /// Sets `self` to be the square root of `self`, if it exists. - fn sqrt_in_place(&mut self) -> Option<&mut Self> { - (*self).sqrt().map(|sqrt| { - *self = sqrt; - self - }) - } } diff --git a/ff/src/fields/models/fp6_3over2.rs b/ff/src/fields/models/fp6_3over2.rs index 458eaffb0..8240b6a52 100644 --- a/ff/src/fields/models/fp6_3over2.rs +++ b/ff/src/fields/models/fp6_3over2.rs @@ -7,6 +7,9 @@ pub trait Fp6Config: 'static + Send + Sync + Copy { const NONRESIDUE: Fp2; + /// Determines the algorithm for computing square roots. + const SQRT_PRECOMP: Option>> = None; + /// Coefficients for the Frobenius automorphism. const FROBENIUS_COEFF_FP6_C1: &'static [Fp2]; const FROBENIUS_COEFF_FP6_C2: &'static [Fp2]; @@ -24,6 +27,8 @@ impl CubicExtConfig for Fp6ConfigWrapper

{ type BaseField = Fp2; type FrobCoeff = Fp2; + const SQRT_PRECOMP: Option>> = P::SQRT_PRECOMP; + const DEGREE_OVER_BASE_PRIME_FIELD: usize = 6; const NONRESIDUE: Self::BaseField = P::NONRESIDUE; diff --git a/ff/src/fields/models/quadratic_extension.rs b/ff/src/fields/models/quadratic_extension.rs index 94538b696..33c5d3f46 100644 --- a/ff/src/fields/models/quadratic_extension.rs +++ b/ff/src/fields/models/quadratic_extension.rs @@ -21,8 +21,8 @@ use ark_std::rand::{ use crate::{ biginteger::BigInteger, - fields::{Field, LegendreSymbol, PrimeField, SquareRootField}, - ToConstraintField, UniformRand, + fields::{Field, LegendreSymbol, PrimeField}, + SqrtPrecomputation, ToConstraintField, UniformRand, }; /// Defines a Quadratic extension field from a quadratic non-residue. @@ -232,11 +232,13 @@ impl One for QuadExtField

{ } type BaseFieldIter

= <

::BaseField as Field>::BasePrimeFieldIter; - impl Field for QuadExtField

{ type BasePrimeField = P::BasePrimeField; + type BasePrimeFieldIter = Chain, BaseFieldIter

>; + const SQRT_PRECOMP: Option> = None; + const ZERO: Self = Self::new(P::BaseField::ZERO, P::BaseField::ZERO); const ONE: Self = Self::new(P::BaseField::ONE, P::BaseField::ZERO); @@ -384,12 +386,7 @@ impl Field for QuadExtField

{ self.c1.frobenius_map(power); P::mul_base_field_by_frob_coeff(&mut self.c1, power); } -} -impl SquareRootField for QuadExtField

-where - P::BaseField: SquareRootField + From, -{ fn legendre(&self) -> LegendreSymbol { // The LegendreSymbol in a field of order q for an element x can be // computed as x^((q-1)/2). @@ -435,7 +432,7 @@ where two_inv.div2(); let two_inv = P::BasePrimeField::from(two_inv); - let two_inv = P::BaseField::from(two_inv); + let two_inv = P::BaseField::from_base_prime_field(two_inv); alpha.sqrt().and_then(|alpha| { let mut delta = (alpha + &self.c0) * &two_inv; diff --git a/ff/src/lib.rs b/ff/src/lib.rs index 609d2f7d4..344245bc2 100644 --- a/ff/src/lib.rs +++ b/ff/src/lib.rs @@ -42,7 +42,7 @@ pub use ark_std::vec; pub mod prelude { pub use crate::biginteger::BigInteger; - pub use crate::fields::{Field, PrimeField, SquareRootField}; + pub use crate::fields::{Field, PrimeField}; pub use ark_std::UniformRand; diff --git a/test-curves/src/bls12_381/tests.rs b/test-curves/src/bls12_381/tests.rs index 63d89ce3e..ba7e2c9bb 100644 --- a/test-curves/src/bls12_381/tests.rs +++ b/test-curves/src/bls12_381/tests.rs @@ -2,7 +2,7 @@ use ark_ec::{ models::short_weierstrass::SWCurveConfig, AffineCurve, PairingEngine, ProjectiveCurve, }; -use ark_ff::{Field, One, SquareRootField, UniformRand, Zero}; +use ark_ff::{Field, One, UniformRand, Zero}; use crate::bls12_381::{g1, Fq, Fq2, Fq6, FqConfig, Fr, FrConfig, G1Affine, G1Projective}; use ark_algebra_test_templates::{ diff --git a/test-curves/src/bn384_small_two_adicity/tests.rs b/test-curves/src/bn384_small_two_adicity/tests.rs index 58cb3274b..2c34fe99e 100644 --- a/test-curves/src/bn384_small_two_adicity/tests.rs +++ b/test-curves/src/bn384_small_two_adicity/tests.rs @@ -2,7 +2,7 @@ use ark_ec::{ models::short_weierstrass::SWCurveConfig, AffineCurve, PairingEngine, ProjectiveCurve, }; -use ark_ff::{Field, One, SquareRootField, UniformRand, Zero}; +use ark_ff::{Field, One, UniformRand, Zero}; use ark_std::{rand::Rng, test_rng}; use crate::bn384_small_two_adicity::{g1, Fq, FqConfig, Fr, FrConfig, G1Affine, G1Projective}; diff --git a/test-curves/src/mnt6_753/mod.rs b/test-curves/src/mnt6_753/mod.rs index 8b1820844..d8171d56f 100644 --- a/test-curves/src/mnt6_753/mod.rs +++ b/test-curves/src/mnt6_753/mod.rs @@ -6,3 +6,6 @@ pub use self::fq::*; pub mod fq3; pub use self::fq3::*; + +#[cfg(test)] +mod tests; diff --git a/test-curves/src/mnt6_753/tests.rs b/test-curves/src/mnt6_753/tests.rs new file mode 100644 index 000000000..ff1e87f02 --- /dev/null +++ b/test-curves/src/mnt6_753/tests.rs @@ -0,0 +1,17 @@ +#![allow(unused_imports)] +use ark_ec::{ + models::short_weierstrass::SWCurveConfig, AffineCurve, PairingEngine, ProjectiveCurve, +}; +use ark_ff::{Field, One, UniformRand, Zero}; + +use crate::mnt6_753::{Fq, Fq3, FqConfig, Fr, FrConfig}; +use ark_algebra_test_templates::{ + curves::*, fields::*, generate_field_test, generate_g1_test, msm::*, +}; +use ark_std::{ + ops::{AddAssign, MulAssign, SubAssign}, + rand::Rng, + test_rng, +}; + +generate_field_test!(mnt6_753; fq3; ); diff --git a/test-templates/src/fields.rs b/test-templates/src/fields.rs index c240ed70c..a34005d0c 100644 --- a/test-templates/src/fields.rs +++ b/test-templates/src/fields.rs @@ -1,7 +1,7 @@ #![allow(unused)] #![allow(clippy::eq_op)] use ark_ff::{ - fields::{FftField, Field, LegendreSymbol, PrimeField, SquareRootField}, + fields::{FftField, Field, LegendreSymbol, PrimeField}, Fp, MontBackend, MontConfig, }; use ark_serialize::{buffer_bit_byte_size, Flags, SWFlags}; @@ -190,7 +190,7 @@ fn random_field_tests() { } } -fn random_sqrt_tests() { +fn random_sqrt_tests() { let mut rng = ark_std::test_rng(); for _ in 0..ITERATIONS { @@ -402,7 +402,7 @@ pub fn montgomery_primefield_test, const N: usize>() { } } -pub fn sqrt_field_test(elem: F) { +pub fn sqrt_field_test(elem: F) { let square = elem.square(); let sqrt = square.sqrt().unwrap(); assert!(sqrt == elem || sqrt == -elem); diff --git a/test-templates/src/lib.rs b/test-templates/src/lib.rs index 26e4f4636..dc063fd69 100644 --- a/test-templates/src/lib.rs +++ b/test-templates/src/lib.rs @@ -277,6 +277,14 @@ macro_rules! generate_field_test { frobenius_test::(Fq::characteristic(), 13); } + #[test] + #[should_panic(expected = "not implemented")] + fn test_fq6_sqrt() { + let mut rng = ark_std::test_rng(); + let g: Fq6 = UniformRand::rand(&mut rng); + sqrt_field_test(g); + } + generate_field_test!($($tail)*); };