mirror of https://github.com/zcash/halo2.git
Merge pull request #120 from daira/sqrt_ratio
Add sqrt_ratio implementation.
This commit is contained in:
commit
963a91464a
|
@ -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 = []
|
||||
|
|
|
@ -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::<usize>() >= 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<F: FieldExt> {
|
||||
hash_xor: u32,
|
||||
hash_mod: usize,
|
||||
marker: PhantomData<F>,
|
||||
}
|
||||
|
||||
impl<F: FieldExt> SqrtHasher<F> {
|
||||
/// 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<F: FieldExt> {
|
||||
hasher: SqrtHasher<F>,
|
||||
inv: Vec<u8>,
|
||||
g0: Box<[F; 256]>,
|
||||
g1: Box<[F; 256]>,
|
||||
g2: Box<[F; 256]>,
|
||||
g3: Box<[F; 129]>,
|
||||
}
|
||||
|
||||
impl<F: FieldExt> SqrtTables<F> {
|
||||
/// 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<Vec<F>> = (0..4)
|
||||
.scan(F::ROOT_OF_UNITY, |gi, _| {
|
||||
// gi == ROOT_OF_UNITY^(256^i)
|
||||
let gtab_i: Vec<F> = (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<u8> = 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::<F> {
|
||||
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) {
|
||||
|
|
|
@ -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<Self> {
|
||||
// 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<Fp> = 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!(
|
||||
|
|
|
@ -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<Self> {
|
||||
// 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<Fq> = 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!(
|
||||
|
|
|
@ -38,8 +38,8 @@ impl<C: CurveAffine> Params<C> {
|
|||
// 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::<usize>() >= 4);
|
||||
|
||||
// In src/arithmetic/fields.rs we ensure that usize is at least 32 bits.
|
||||
|
||||
let n: u64 = 1 << k;
|
||||
|
||||
|
|
Loading…
Reference in New Issue