diff --git a/Cargo.toml b/Cargo.toml index 792b2f1..109666e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,8 @@ metrics-macros = "=0.1.0-alpha.9" num_cpus = "1.13" rand = "0.7" blake2b_simd = "0.5" +lazy_static = "1.4.0" +static_assertions = "1.1.0" [features] sanity-checks = [] diff --git a/src/arithmetic/fields.rs b/src/arithmetic/fields.rs index 196e3ba..6a35cc0 100644 --- a/src/arithmetic/fields.rs +++ b/src/arithmetic/fields.rs @@ -1,10 +1,17 @@ //! This module contains the `Field` abstraction that allows us to write //! code that generalizes over a pair of fields. +use core::mem::size_of; +use static_assertions::const_assert; +use std::assert; +use std::convert::TryInto; +use std::marker::PhantomData; use subtle::{Choice, ConstantTimeEq, CtOption}; use super::Group; +const_assert!(size_of::() >= 4); + /// This trait is a common interface for dealing with elements of a finite /// field. pub trait FieldExt: @@ -16,6 +23,9 @@ pub trait FieldExt: /// Inverse of `ROOT_OF_UNITY` const ROOT_OF_UNITY_INV: Self; + /// The value $(T-1)/2$ such that $2^S \cdot T = p - 1$ with $T$ odd. + const T_MINUS1_OVER2: [u64; 4]; + /// Generator of the $t-order$ multiplicative subgroup const DELTA: Self; @@ -32,6 +42,23 @@ pub trait FieldExt: /// Element of multiplicative order $3$. const ZETA: Self; + /// Computes: + /// + /// * (true, sqrt(num/div)), if num and div are nonzero and num/div is a square in the field; + /// * (true, 0), if num is zero; + /// * (false, 0), if num is nonzero and div is zero; + /// * (false, sqrt(ROOT_OF_UNITY * num/div)), if num and div are nonzero and num/div is a nonsquare in the field; + /// + /// where ROOT_OF_UNITY is a generator of the order 2^n subgroup (and therefore a nonsquare). + /// + /// The choice of root from sqrt is unspecified. + fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self); + + /// Equivalent to sqrt_ratio(self, one()). + fn sqrt_alt(&self) -> (Choice, Self) { + Self::sqrt_ratio(self, &Self::one()) + } + /// This computes a random element of the field using system randomness. fn rand() -> Self { Self::random(rand::rngs::OsRng) @@ -77,6 +104,16 @@ pub trait FieldExt: /// canonically. fn get_lower_128(&self) -> u128; + /// Gets the lower 32 bits of this field element when expressed + /// canonically. + fn get_lower_32(&self) -> u32; + + /// Raise this field element to the power T_MINUS1_OVER2. + /// Field implementations may override this to use an efficient addition chain. + fn pow_by_t_minus1_over2(&self) -> Self { + ff::Field::pow_vartime(&self, &Self::T_MINUS1_OVER2) + } + /// Performs a batch inversion using Montgomery's trick, returns the product /// of every inverse. Zero inputs are ignored. fn batch_invert(v: &mut [Self]) -> Self { @@ -103,6 +140,201 @@ pub trait FieldExt: } } +/// Parameters for a perfect hash function used in square root computation. +#[derive(Debug)] +struct SqrtHasher { + hash_xor: u32, + hash_mod: usize, + marker: PhantomData, +} + +impl SqrtHasher { + /// Returns a perfect hash of x for use with SqrtTables::inv. + fn hash(&self, x: &F) -> usize { + // This is just the simplest constant-time perfect hash construction that could + // possibly work. The 32 low-order bits are unique within the 2^S order subgroup, + // then the xor acts as "salt" to injectively randomize the output when taken modulo + // `hash_mod`. Since the table is small, we do not need anything more complicated. + ((x.get_lower_32() ^ self.hash_xor) as usize) % self.hash_mod + } +} + +/// Tables used for square root computation. +#[derive(Debug)] +pub struct SqrtTables { + hasher: SqrtHasher, + inv: Vec, + g0: Box<[F; 256]>, + g1: Box<[F; 256]>, + g2: Box<[F; 256]>, + g3: Box<[F; 129]>, +} + +impl SqrtTables { + /// Build tables given parameters for the perfect hash. + pub fn new(hash_xor: u32, hash_mod: usize) -> Self { + let hasher = SqrtHasher { + hash_xor, + hash_mod, + marker: PhantomData, + }; + + let gtab: Vec> = (0..4) + .scan(F::ROOT_OF_UNITY, |gi, _| { + // gi == ROOT_OF_UNITY^(256^i) + let gtab_i: Vec = (0..256) + .scan(F::one(), |acc, _| { + let res = *acc; + *acc *= *gi; + Some(res) + }) + .collect(); + *gi = gtab_i[255] * *gi; + Some(gtab_i) + }) + .collect(); + + // Now invert gtab[3]. + let mut inv: Vec = vec![1; hash_mod]; + for j in 0..256 { + let hash = hasher.hash(>ab[3][j]); + // 1 is the last value to be assigned, so this ensures there are no collisions. + assert!(inv[hash] == 1); + inv[hash] = ((256 - j) & 0xFF) as u8; + } + + SqrtTables:: { + hasher, + inv, + g0: Box::new(gtab[0][..].try_into().unwrap()), + g1: Box::new(gtab[1][..].try_into().unwrap()), + g2: Box::new(gtab[2][..].try_into().unwrap()), + g3: Box::new(gtab[3][0..129].try_into().unwrap()), + } + } + + /// Computes: + /// + /// * (true, sqrt(num/div)), if num and div are nonzero and num/div is a square in the field; + /// * (true, 0), if num is zero; + /// * (false, 0), if num is nonzero and div is zero; + /// * (false, sqrt(ROOT_OF_UNITY * num/div)), if num and div are nonzero and num/div is a nonsquare in the field; + /// + /// where ROOT_OF_UNITY is a generator of the order 2^n subgroup (and therefore a nonsquare). + /// + /// The choice of root from sqrt is unspecified. + pub fn sqrt_ratio(&self, num: &F, div: &F) -> (Choice, F) { + // Based on: + // * [Sarkar2020](https://eprint.iacr.org/2020/1407) + // * [BDLSY2012](https://cr.yp.to/papers.html#ed25519) + // + // We need to calculate uv and v, where v = u^((T-1)/2), u = num/div, and p-1 = T * 2^S. + // We can rewrite as follows: + // + // v = (num/div)^((T-1)/2) + // = num^((T-1)/2) * div^(p-1 - (T-1)/2) [Fermat's Little Theorem] + // = " * div^(T * 2^S - (T-1)/2) + // = " * div^((2^(S+1) - 1)*(T-1)/2 + 2^S) + // = (num * div^(2^(S+1) - 1))^((T-1)/2) * div^(2^S) + // + // Let w = (num * div^(2^(S+1) - 1))^((T-1)/2) * div^(2^S - 1). + // Then v = w * div, and uv = num * v / div = num * w. + // + // We calculate: + // + // s = div^(2^S - 1) using an addition chain + // t = div^(2^(S+1) - 1) = s^2 * div + // w = (num * t)^((T-1)/2) * s using another addition chain + // + // then u and uv as above. The addition chains are given in + // https://github.com/zcash/pasta/blob/master/addchain_sqrt.py . + // The overall cost of this part is similar to a single full-width exponentiation, + // regardless of S. + + let sqr = |x: F, i: u32| (0..i).fold(x, |x, _| x.square()); + + // s = div^(2^S - 1) + let s = (0..5).fold(*div, |d: F, i| sqr(d, 1 << i) * d); + + // t == div^(2^(S+1) - 1) + let t = s.square() * div; + + // w = (num * t)^((T-1)/2) * s + let w = (t * num).pow_by_t_minus1_over2() * s; + + // v == u^((T-1)/2) + let v = w * div; + + // uv = u * v + let uv = w * num; + + let res = self.sqrt_common(&uv, &v); + + let sqdiv = res.square() * div; + let is_square = (sqdiv - num).ct_is_zero(); + let is_nonsquare = (sqdiv - F::ROOT_OF_UNITY * num).ct_is_zero(); + assert!(bool::from( + num.ct_is_zero() | div.ct_is_zero() | (is_square ^ is_nonsquare) + )); + + (is_square, res) + } + + /// Same as sqrt_ratio(u, one()) but more efficient. + pub fn sqrt_alt(&self, u: &F) -> (Choice, F) { + let v = u.pow_by_t_minus1_over2(); + let uv = *u * v; + + let res = self.sqrt_common(&uv, &v); + + let sq = res.square(); + let is_square = (sq - u).ct_is_zero(); + let is_nonsquare = (sq - F::ROOT_OF_UNITY * u).ct_is_zero(); + assert!(bool::from(u.ct_is_zero() | (is_square ^ is_nonsquare))); + + (is_square, res) + } + + /// Common part of sqrt_ratio and sqrt_alt: return res given v = u^((T-1)/2) and uv = u * v. + fn sqrt_common(&self, uv: &F, v: &F) -> F { + let sqr = |x: F, i: u32| (0..i).fold(x, |x, _| x.square()); + let inv = |x: F| self.inv[self.hasher.hash(&x)] as usize; + + let x3 = *uv * v; + let x2 = sqr(x3, 8); + let x1 = sqr(x2, 8); + let x0 = sqr(x1, 8); + + // i = 0, 1 + let mut t_ = inv(x0); // = t >> 16 + // 1 == x0 * ROOT_OF_UNITY^(t_ << 24) + assert!(t_ < 0x100); + let alpha = x1 * self.g2[t_]; + + // i = 2 + t_ += inv(alpha) << 8; // = t >> 8 + // 1 == x1 * ROOT_OF_UNITY^(t_ << 16) + assert!(t_ < 0x10000); + let alpha = x2 * self.g1[t_ & 0xFF] * self.g2[t_ >> 8]; + + // i = 3 + t_ += inv(alpha) << 16; // = t + // 1 == x2 * ROOT_OF_UNITY^(t_ << 8) + assert!(t_ < 0x1000000); + let alpha = x3 * self.g0[t_ & 0xFF] * self.g1[(t_ >> 8) & 0xFF] * self.g2[t_ >> 16]; + + t_ += inv(alpha) << 24; // = t << 1 + // 1 == x3 * ROOT_OF_UNITY^t_ + t_ = (t_ + 1) >> 1; + assert!(t_ <= 0x80000000); + + *uv * self.g0[t_ & 0xFF] + * self.g1[(t_ >> 8) & 0xFF] + * self.g2[(t_ >> 16) & 0xFF] + * self.g3[t_ >> 24] + } +} + /// Compute a + b + carry, returning the result and the new carry over. #[inline(always)] pub(crate) const fn adc(a: u64, b: u64, carry: u64) -> (u64, u64) { diff --git a/src/pasta/fields/fp.rs b/src/pasta/fields/fp.rs index c28182e..07a6d3d 100644 --- a/src/pasta/fields/fp.rs +++ b/src/pasta/fields/fp.rs @@ -2,10 +2,11 @@ use bitvec::{array::BitArray, order::Lsb0}; use core::convert::TryInto; use core::fmt; use core::ops::{Add, Mul, Neg, Sub}; +use lazy_static::lazy_static; use rand::RngCore; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; -use crate::arithmetic::{adc, mac, sbb, FieldExt, Group}; +use crate::arithmetic::{adc, mac, sbb, FieldExt, Group, SqrtTables}; /// This represents an element of $\mathbb{F}_p$ where /// @@ -491,45 +492,8 @@ impl ff::Field for Fp { /// Computes the square root of this element, if it exists. fn sqrt(&self) -> CtOption { - // Tonelli-Shank's algorithm for p mod 16 = 1 - // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) - - // w = self^((t - 1) // 2) - let w = self.pow_vartime(&[0x04a67c8dcc969876, 0x11234c7e, 0x0, 0x20000000]); - - let mut v = S; - let mut x = self * w; - let mut b = x * w; - - // Initialize z as the 2^S root of unity. - let mut z = ROOT_OF_UNITY; - - for max_v in (1..=S).rev() { - let mut k = 1; - let mut tmp = b.square(); - let mut j_less_than_v: Choice = 1.into(); - - for j in 2..max_v { - let tmp_is_one = tmp.ct_eq(&Fp::one()); - let squared = Fp::conditional_select(&tmp, &z, tmp_is_one).square(); - tmp = Fp::conditional_select(&squared, &tmp, tmp_is_one); - let new_z = Fp::conditional_select(&z, &squared, tmp_is_one); - j_less_than_v &= !j.ct_eq(&v); - k = u32::conditional_select(&j, &k, tmp_is_one); - z = Fp::conditional_select(&z, &new_z, j_less_than_v); - } - - let result = x * z; - x = Fp::conditional_select(&result, &x, b.ct_eq(&Fp::one())); - z = z.square(); - b *= z; - v = k; - } - - CtOption::new( - x, - (x * x).ct_eq(self), // Only return Some if it's the square root. - ) + let (is_square, res) = self.sqrt_alt(); + CtOption::new(res, is_square) } /// Computes the multiplicative inverse of this element, @@ -635,6 +599,11 @@ impl ff::PrimeField for Fp { } } +lazy_static! { + // The perfect hash parameters are found by `squareroottab.sage` in zcash/pasta. + static ref FP_TABLES: SqrtTables = SqrtTables::new(0x11BE, 1098); +} + impl FieldExt for Fp { const ROOT_OF_UNITY: Self = ROOT_OF_UNITY; const ROOT_OF_UNITY_INV: Self = Fp::from_raw([ @@ -643,6 +612,12 @@ impl FieldExt for Fp { 0xb4ed8e647196dad1, 0x2cd5282c53116b5c, ]); + const T_MINUS1_OVER2: [u64; 4] = [ + 0x04a67c8dcc969876, + 0x0000000011234c7e, + 0x0000000000000000, + 0x20000000, + ]; const DELTA: Self = DELTA; const TWO_INV: Self = Fp::from_raw([ 0xcc96987680000001, @@ -664,6 +639,14 @@ impl FieldExt for Fp { 0x12ccca834acdba71, ]); + fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) { + FP_TABLES.sqrt_ratio(num, div) + } + + fn sqrt_alt(&self) -> (Choice, Self) { + FP_TABLES.sqrt_alt(self) + } + fn ct_is_zero(&self) -> Choice { self.ct_eq(&Self::zero()) } @@ -740,6 +723,45 @@ impl FieldExt for Fp { u128::from(tmp.0[0]) | (u128::from(tmp.0[1]) << 64) } + + fn get_lower_32(&self) -> u32 { + // TODO: don't reduce, just hash the Montgomery form. (Requires rebuilding perfect hash table.) + let tmp = Fp::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); + + tmp.0[0] as u32 + } + + fn pow_by_t_minus1_over2(&self) -> Self { + let sqr = |x: Fp, i: u32| (0..i).fold(x, |x, _| x.square()); + + let r10 = self.square(); + let r11 = r10 * self; + let r110 = r11.square(); + let r111 = r110 * self; + let r1001 = r111 * r10; + let r1101 = r111 * r110; + let ra = sqr(*self, 129) * self; + let rb = sqr(ra, 7) * r1001; + let rc = sqr(rb, 7) * r1101; + let rd = sqr(rc, 4) * r11; + let re = sqr(rd, 6) * r111; + let rf = sqr(re, 3) * r111; + let rg = sqr(rf, 10) * r1001; + let rh = sqr(rg, 5) * r1001; + let ri = sqr(rh, 4) * r1001; + let rj = sqr(ri, 3) * r111; + let rk = sqr(rj, 4) * r1001; + let rl = sqr(rk, 5) * r11; + let rm = sqr(rl, 4) * r111; + let rn = sqr(rm, 4) * r11; + let ro = sqr(rn, 6) * r1001; + let rp = sqr(ro, 5) * r1101; + let rq = sqr(rp, 4) * r11; + let rr = sqr(rq, 7) * r111; + let rs = sqr(rr, 3) * r11; + let rt = rs.square(); + rt + } } #[cfg(test)] @@ -778,6 +800,59 @@ fn test_sqrt() { assert!(v == Fp::TWO_INV || (-v) == Fp::TWO_INV); } +#[test] +fn test_pow_by_t_minus1_over2() { + // NB: TWO_INV is standing in as a "random" field element + let v = (Fp::TWO_INV).pow_by_t_minus1_over2(); + assert!(v == ff::Field::pow_vartime(&Fp::TWO_INV, &Fp::T_MINUS1_OVER2)); +} + +#[test] +fn test_sqrt_ratio_and_alt() { + // (true, sqrt(num/div)), if num and div are nonzero and num/div is a square in the field + let num = (Fp::TWO_INV).square(); + let div = Fp::from_u64(25); + let div_inverse = div.invert().unwrap(); + let expected = Fp::TWO_INV * Fp::from_u64(5).invert().unwrap(); + let (is_square, v) = Fp::sqrt_ratio(&num, &div); + assert!(bool::from(is_square)); + assert!(v == expected || (-v) == expected); + + let (is_square_alt, v_alt) = Fp::sqrt_alt(&(num * div_inverse)); + assert!(bool::from(is_square_alt)); + assert!(v_alt == v); + + // (false, sqrt(ROOT_OF_UNITY * num/div)), if num and div are nonzero and num/div is a nonsquare in the field + let num = num * Fp::ROOT_OF_UNITY; + let expected = Fp::TWO_INV * Fp::ROOT_OF_UNITY * Fp::from_u64(5).invert().unwrap(); + let (is_square, v) = Fp::sqrt_ratio(&num, &div); + assert!(!bool::from(is_square)); + assert!(v == expected || (-v) == expected); + + let (is_square_alt, v_alt) = Fp::sqrt_alt(&(num * div_inverse)); + assert!(!bool::from(is_square_alt)); + assert!(v_alt == v); + + // (true, 0), if num is zero + let num = Fp::zero(); + let expected = Fp::zero(); + let (is_square, v) = Fp::sqrt_ratio(&num, &div); + assert!(bool::from(is_square)); + assert!(v == expected); + + let (is_square_alt, v_alt) = Fp::sqrt_alt(&(num * div_inverse)); + assert!(bool::from(is_square_alt)); + assert!(v_alt == v); + + // (false, 0), if num is nonzero and div is zero + let num = (Fp::TWO_INV).square(); + let div = Fp::zero(); + let expected = Fp::zero(); + let (is_square, v) = Fp::sqrt_ratio(&num, &div); + assert!(!bool::from(is_square)); + assert!(v == expected); +} + #[test] fn test_zeta() { assert_eq!( diff --git a/src/pasta/fields/fq.rs b/src/pasta/fields/fq.rs index 6c3c989..3bd0e47 100644 --- a/src/pasta/fields/fq.rs +++ b/src/pasta/fields/fq.rs @@ -2,10 +2,11 @@ use bitvec::{array::BitArray, order::Lsb0}; use core::convert::TryInto; use core::fmt; use core::ops::{Add, Mul, Neg, Sub}; +use lazy_static::lazy_static; use rand::RngCore; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; -use crate::arithmetic::{adc, mac, sbb, FieldExt, Group}; +use crate::arithmetic::{adc, mac, sbb, FieldExt, Group, SqrtTables}; /// This represents an element of $\mathbb{F}_q$ where /// @@ -491,45 +492,8 @@ impl ff::Field for Fq { /// Computes the square root of this element, if it exists. fn sqrt(&self) -> CtOption { - // Tonelli-Shank's algorithm for q mod 16 = 1 - // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) - - // w = self^((t - 1) // 2) - let w = self.pow_vartime(&[0x04ca546ec6237590, 0x11234c7e, 0x0, 0x20000000]); - - let mut v = S; - let mut x = self * w; - let mut b = x * w; - - // Initialize z as the 2^S root of unity. - let mut z = ROOT_OF_UNITY; - - for max_v in (1..=S).rev() { - let mut k = 1; - let mut tmp = b.square(); - let mut j_less_than_v: Choice = 1.into(); - - for j in 2..max_v { - let tmp_is_one = tmp.ct_eq(&Fq::one()); - let squared = Fq::conditional_select(&tmp, &z, tmp_is_one).square(); - tmp = Fq::conditional_select(&squared, &tmp, tmp_is_one); - let new_z = Fq::conditional_select(&z, &squared, tmp_is_one); - j_less_than_v &= !j.ct_eq(&v); - k = u32::conditional_select(&j, &k, tmp_is_one); - z = Fq::conditional_select(&z, &new_z, j_less_than_v); - } - - let result = x * z; - x = Fq::conditional_select(&result, &x, b.ct_eq(&Fq::one())); - z = z.square(); - b *= z; - v = k; - } - - CtOption::new( - x, - (x * x).ct_eq(self), // Only return Some if it's the square root. - ) + let (is_square, res) = self.sqrt_alt(); + CtOption::new(res, is_square) } /// Computes the multiplicative inverse of this element, @@ -635,6 +599,11 @@ impl ff::PrimeField for Fq { } } +lazy_static! { + // The perfect hash parameters are found by `squareroottab.sage` in zcash/pasta. + static ref FQ_TABLES: SqrtTables = SqrtTables::new(0x116A9E, 1206); +} + impl FieldExt for Fq { const ROOT_OF_UNITY: Self = ROOT_OF_UNITY; const ROOT_OF_UNITY_INV: Self = Fq::from_raw([ @@ -643,6 +612,12 @@ impl FieldExt for Fq { 0xf4c8f353124086c1, 0x2235e1a7415bf936, ]); + const T_MINUS1_OVER2: [u64; 4] = [ + 0x04ca546ec6237590, + 0x0000000011234c7e, + 0x0000000000000000, + 0x20000000, + ]; const DELTA: Self = DELTA; const TWO_INV: Self = Fq::from_raw([ 0xc623759080000001, @@ -664,6 +639,14 @@ impl FieldExt for Fq { 0x06819a58283e528e, ]); + fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) { + FQ_TABLES.sqrt_ratio(num, div) + } + + fn sqrt_alt(&self) -> (Choice, Self) { + FQ_TABLES.sqrt_alt(self) + } + fn ct_is_zero(&self) -> Choice { self.ct_eq(&Self::zero()) } @@ -740,6 +723,45 @@ impl FieldExt for Fq { u128::from(tmp.0[0]) | (u128::from(tmp.0[1]) << 64) } + + fn get_lower_32(&self) -> u32 { + // TODO: don't reduce, just hash the Montgomery form. (Requires rebuilding perfect hash table.) + let tmp = Fq::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); + + tmp.0[0] as u32 + } + + fn pow_by_t_minus1_over2(&self) -> Self { + let sqr = |x: Fq, i: u32| (0..i).fold(x, |x, _| x.square()); + + let s10 = self.square(); + let s11 = s10 * self; + let s111 = s11.square() * self; + let s1001 = s111 * s10; + let s1011 = s1001 * s10; + let s1101 = s1011 * s10; + let sa = sqr(*self, 129) * self; + let sb = sqr(sa, 7) * s1001; + let sc = sqr(sb, 7) * s1101; + let sd = sqr(sc, 4) * s11; + let se = sqr(sd, 6) * s111; + let sf = sqr(se, 3) * s111; + let sg = sqr(sf, 10) * s1001; + let sh = sqr(sg, 4) * s1001; + let si = sqr(sh, 5) * s1001; + let sj = sqr(si, 5) * s1001; + let sk = sqr(sj, 3) * s1001; + let sl = sqr(sk, 4) * s1011; + let sm = sqr(sl, 4) * s1011; + let sn = sqr(sm, 5) * s11; + let so = sqr(sn, 4) * self; + let sp = sqr(so, 5) * s11; + let sq = sqr(sp, 4) * s111; + let sr = sqr(sq, 5) * s1011; + let ss = sqr(sr, 3) * self; + let st = sqr(ss, 4); + st + } } #[cfg(test)] @@ -778,6 +800,59 @@ fn test_sqrt() { assert!(v == Fq::TWO_INV || (-v) == Fq::TWO_INV); } +#[test] +fn test_pow_by_t_minus1_over2() { + // NB: TWO_INV is standing in as a "random" field element + let v = (Fq::TWO_INV).pow_by_t_minus1_over2(); + assert!(v == ff::Field::pow_vartime(&Fq::TWO_INV, &Fq::T_MINUS1_OVER2)); +} + +#[test] +fn test_sqrt_ratio_and_alt() { + // (true, sqrt(num/div)), if num and div are nonzero and num/div is a square in the field + let num = (Fq::TWO_INV).square(); + let div = Fq::from_u64(25); + let div_inverse = div.invert().unwrap(); + let expected = Fq::TWO_INV * Fq::from_u64(5).invert().unwrap(); + let (is_square, v) = Fq::sqrt_ratio(&num, &div); + assert!(bool::from(is_square)); + assert!(v == expected || (-v) == expected); + + let (is_square_alt, v_alt) = Fq::sqrt_alt(&(num * div_inverse)); + assert!(bool::from(is_square_alt)); + assert!(v_alt == v); + + // (false, sqrt(ROOT_OF_UNITY * num/div)), if num and div are nonzero and num/div is a nonsquare in the field + let num = num * Fq::ROOT_OF_UNITY; + let expected = Fq::TWO_INV * Fq::ROOT_OF_UNITY * Fq::from_u64(5).invert().unwrap(); + let (is_square, v) = Fq::sqrt_ratio(&num, &div); + assert!(!bool::from(is_square)); + assert!(v == expected || (-v) == expected); + + let (is_square_alt, v_alt) = Fq::sqrt_alt(&(num * div_inverse)); + assert!(!bool::from(is_square_alt)); + assert!(v_alt == v); + + // (true, 0), if num is zero + let num = Fq::zero(); + let expected = Fq::zero(); + let (is_square, v) = Fq::sqrt_ratio(&num, &div); + assert!(bool::from(is_square)); + assert!(v == expected); + + let (is_square_alt, v_alt) = Fq::sqrt_alt(&(num * div_inverse)); + assert!(bool::from(is_square_alt)); + assert!(v_alt == v); + + // (false, 0), if num is nonzero and div is zero + let num = (Fq::TWO_INV).square(); + let div = Fq::zero(); + let expected = Fq::zero(); + let (is_square, v) = Fq::sqrt_ratio(&num, &div); + assert!(!bool::from(is_square)); + assert!(v == expected); +} + #[test] fn test_zeta() { assert_eq!( diff --git a/src/poly/commitment.rs b/src/poly/commitment.rs index a9b6c74..11e9d9e 100644 --- a/src/poly/commitment.rs +++ b/src/poly/commitment.rs @@ -38,8 +38,8 @@ impl Params { // This is usually a limitation on the curve, but we also want 32-bit // architectures to be supported. assert!(k < 32); - // No goofy hardware please. - assert!(core::mem::size_of::() >= 4); + + // In src/arithmetic/fields.rs we ensure that usize is at least 32 bits. let n: u64 = 1 << k;