Migrate to `ff` revision with square root backports

This commit is contained in:
Jack Grigg 2022-10-28 10:36:06 +00:00
parent f8ba48de4e
commit 6921efd8fd
5 changed files with 60 additions and 171 deletions

View File

@ -6,6 +6,14 @@ and this project adheres to Rust's notion of
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Changed
- Migrated to `ff 0.13`, `group 0.13`.
### Removed
- `pasta_curves::arithmetic::SqrtRatio` (use `ff::Field::{sqrt_ratio, sqrt_alt}`
instead).
- `pasta_curves::arithmetic::SqrtTables` (from public API, as it isn't suitable
for generic usage).
## [0.4.1] - 2022-10-13
### Added

View File

@ -72,3 +72,6 @@ sqrt-table = ["alloc", "lazy_static"]
repr-c = []
uninline-portable = []
serde = ["hex", "serde_crate"]
[patch.crates-io]
ff = { git = "https://github.com/zkcrypto/ff.git", rev = "9a844a72d30ea9f859cd46dcc2237a1ae3277ddc" }

View File

@ -4,99 +4,35 @@
use core::mem::size_of;
use static_assertions::const_assert;
use subtle::{Choice, ConditionallySelectable, CtOption};
use super::Group;
use core::assert;
#[cfg(feature = "sqrt-table")]
use alloc::{boxed::Box, vec::Vec};
#[cfg(feature = "sqrt-table")]
use core::marker::PhantomData;
#[cfg(feature = "sqrt-table")]
use subtle::Choice;
const_assert!(size_of::<usize>() >= 4);
/// A trait that exposes additional operations related to calculating square roots of
/// An internal trait that exposes additional operations related to calculating square roots of
/// prime-order finite fields.
pub trait SqrtRatio: ff::PrimeField {
/// The value $(T-1)/2$ such that $2^S \cdot T = p - 1$ with $T$ odd.
const T_MINUS1_OVER2: [u64; 4];
/// Raise this field element to the power [`Self::T_MINUS1_OVER2`].
pub(crate) trait SqrtTableHelpers: ff::PrimeField {
/// Raise this field element to the power $(t-1)/2$.
///
/// 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)
}
fn pow_by_t_minus1_over2(&self) -> Self;
/// Gets the lower 32 bits of this field element when expressed
/// canonically.
fn get_lower_32(&self) -> u32;
/// Computes:
///
/// - $(\textsf{true}, \sqrt{\textsf{num}/\textsf{div}})$, if $\textsf{num}$ and
/// $\textsf{div}$ are nonzero and $\textsf{num}/\textsf{div}$ is a square in the
/// field;
/// - $(\textsf{true}, 0)$, if $\textsf{num}$ is zero;
/// - $(\textsf{false}, 0)$, if $\textsf{num}$ is nonzero and $\textsf{div}$ is zero;
/// - $(\textsf{false}, \sqrt{G_S \cdot \textsf{num}/\textsf{div}})$, if
/// $\textsf{num}$ and $\textsf{div}$ are nonzero and $\textsf{num}/\textsf{div}$ is
/// a nonsquare in the field;
///
/// where $G_S$ is a non-square.
///
/// For `pasta_curves`, $G_S$ is currently [`ff::PrimeField::root_of_unity`], a
/// generator of the order $2^S$ subgroup. Users of this crate should not rely on this
/// generator being fixed; it may be changed in future crate versions to simplify the
/// implementation of the SSWU hash-to-curve algorithm.
///
/// The choice of root from sqrt is unspecified.
fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) {
// General implementation:
//
// a = num * inv0(div)
// = { 0 if div is zero
// { num/div otherwise
//
// b = G_S * a
// = { 0 if div is zero
// { G_S*num/div otherwise
//
// Since G_S is non-square, a and b are either both zero (and both square), or
// only one of them is square. We can therefore choose the square root to return
// based on whether a is square, but for the boolean output we need to handle the
// num != 0 && div == 0 case specifically.
let a = div.invert().unwrap_or_else(Self::zero) * num;
let b = a * Self::root_of_unity();
let sqrt_a = a.sqrt();
let sqrt_b = b.sqrt();
let num_is_zero = num.is_zero();
let div_is_zero = div.is_zero();
let is_square = sqrt_a.is_some();
let is_nonsquare = sqrt_b.is_some();
assert!(bool::from(
num_is_zero | div_is_zero | (is_square ^ is_nonsquare)
));
(
is_square & !(!num_is_zero & div_is_zero),
CtOption::conditional_select(&sqrt_b, &sqrt_a, is_square).unwrap(),
)
}
/// Equivalent to `Self::sqrt_ratio(self, one())`.
fn sqrt_alt(&self) -> (Choice, Self) {
Self::sqrt_ratio(self, &Self::one())
}
}
/// This trait is a common interface for dealing with elements of a finite
/// field.
pub trait FieldExt: SqrtRatio + From<bool> + Ord + Group<Scalar = Self> {
pub trait FieldExt: ff::PrimeField + From<bool> + Ord + Group<Scalar = Self> {
/// Modulus of the field written as a string for display purposes
const MODULUS: &'static str;
@ -119,77 +55,11 @@ pub trait FieldExt: SqrtRatio + From<bool> + Ord + Group<Scalar = Self> {
/// byte representation of an integer.
fn from_bytes_wide(bytes: &[u8; 64]) -> Self;
/// Exponentiates `self` by `by`, where `by` is a little-endian order
/// integer exponent.
fn pow(&self, by: &[u64; 4]) -> Self {
let mut res = Self::one();
for e in by.iter().rev() {
for i in (0..64).rev() {
res = res.square();
let mut tmp = res;
tmp *= self;
res.conditional_assign(&tmp, (((*e >> i) & 0x1) as u8).into());
}
}
res
}
/// Gets the lower 128 bits of this field element when expressed
/// canonically.
fn get_lower_128(&self) -> u128;
}
/// TonelliShanks' square-root algorithm for `p mod 16 = 1`.
///
/// https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5)
///
/// `tm1d2` should be set to `(t - 1) // 2`, where `t = (modulus - 1) >> F::S`.
#[cfg(not(feature = "sqrt-table"))]
#[cfg_attr(docsrs, doc(cfg(not(feature = "sqrt-table"))))]
pub(crate) fn sqrt_tonelli_shanks<F: ff::PrimeField, S: AsRef<[u64]>>(
f: &F,
tm1d2: S,
) -> CtOption<F> {
use subtle::ConstantTimeEq;
// w = self^((t - 1) // 2)
let w = f.pow_vartime(tm1d2);
let mut v = F::S;
let mut x = w * f;
let mut b = x * w;
// Initialize z as the 2^S root of unity.
let mut z = F::root_of_unity();
for max_v in (1..=F::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(&F::one());
let squared = F::conditional_select(&tmp, &z, tmp_is_one).square();
tmp = F::conditional_select(&squared, &tmp, tmp_is_one);
let new_z = F::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 = F::conditional_select(&z, &new_z, j_less_than_v);
}
let result = x * z;
x = F::conditional_select(&result, &x, b.ct_eq(&F::one()));
z = z.square();
b *= z;
v = k;
}
CtOption::new(
x,
(x * x).ct_eq(f), // Only return Some if it's the square root.
)
}
/// Parameters for a perfect hash function used in square root computation.
#[cfg(feature = "sqrt-table")]
#[cfg_attr(docsrs, doc(cfg(feature = "sqrt-table")))]
@ -201,7 +71,7 @@ struct SqrtHasher<F: FieldExt> {
}
#[cfg(feature = "sqrt-table")]
impl<F: FieldExt> SqrtHasher<F> {
impl<F: FieldExt + SqrtTableHelpers> 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
@ -216,7 +86,7 @@ impl<F: FieldExt> SqrtHasher<F> {
#[cfg(feature = "sqrt-table")]
#[cfg_attr(docsrs, doc(cfg(feature = "sqrt-table")))]
#[derive(Debug)]
pub struct SqrtTables<F: FieldExt> {
pub(crate) struct SqrtTables<F: FieldExt> {
hasher: SqrtHasher<F>,
inv: Vec<u8>,
g0: Box<[F; 256]>,
@ -226,7 +96,7 @@ pub struct SqrtTables<F: FieldExt> {
}
#[cfg(feature = "sqrt-table")]
impl<F: FieldExt> SqrtTables<F> {
impl<F: FieldExt + SqrtTableHelpers> SqrtTables<F> {
/// Build tables given parameters for the perfect hash.
pub fn new(hash_xor: u32, hash_mod: usize) -> Self {
use alloc::vec;

View File

@ -11,7 +11,7 @@ use lazy_static::lazy_static;
#[cfg(feature = "bits")]
use ff::{FieldBits, PrimeFieldBits};
use crate::arithmetic::{adc, mac, sbb, FieldExt, Group, SqrtRatio};
use crate::arithmetic::{adc, mac, sbb, FieldExt, Group, SqrtTableHelpers};
#[cfg(feature = "sqrt-table")]
use crate::arithmetic::SqrtTables;
@ -232,6 +232,7 @@ const DELTA: Fp = Fp::from_raw([
]);
/// `(t - 1) // 2` where t * 2^s + 1 = p with t odd.
#[cfg(any(test, not(feature = "sqrt-table")))]
const T_MINUS1_OVER2: [u64; 4] = [
0x04a6_7c8d_cc96_9876,
0x0000_0000_1123_4c7e,
@ -510,6 +511,21 @@ impl ff::Field for Fp {
self.square()
}
fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) {
#[cfg(feature = "sqrt-table")]
{
FP_TABLES.sqrt_ratio(num, div)
}
#[cfg(not(feature = "sqrt-table"))]
ff::helpers::sqrt_ratio_generic(num, div)
}
#[cfg(feature = "sqrt-table")]
fn sqrt_alt(&self) -> (Choice, Self) {
FP_TABLES.sqrt_alt(self)
}
/// Computes the square root of this element, if it exists.
fn sqrt(&self) -> CtOption<Self> {
#[cfg(feature = "sqrt-table")]
@ -519,7 +535,7 @@ impl ff::Field for Fp {
}
#[cfg(not(feature = "sqrt-table"))]
crate::arithmetic::sqrt_tonelli_shanks(self, &T_MINUS1_OVER2)
ff::helpers::sqrt_tonelli_shanks(self, &T_MINUS1_OVER2)
}
/// Computes the multiplicative inverse of this element,
@ -669,9 +685,7 @@ lazy_static! {
static ref FP_TABLES: SqrtTables<Fp> = SqrtTables::new(0x11BE, 1098);
}
impl SqrtRatio for Fp {
const T_MINUS1_OVER2: [u64; 4] = T_MINUS1_OVER2;
impl SqrtTableHelpers for Fp {
fn pow_by_t_minus1_over2(&self) -> Self {
let sqr = |x: Fp, i: u32| (0..i).fold(x, |x, _| x.square());
@ -709,16 +723,6 @@ impl SqrtRatio for Fp {
tmp.0[0] as u32
}
#[cfg(feature = "sqrt-table")]
fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) {
FP_TABLES.sqrt_ratio(num, div)
}
#[cfg(feature = "sqrt-table")]
fn sqrt_alt(&self) -> (Choice, Self) {
FP_TABLES.sqrt_alt(self)
}
}
impl FieldExt for Fp {

View File

@ -11,7 +11,7 @@ use lazy_static::lazy_static;
#[cfg(feature = "bits")]
use ff::{FieldBits, PrimeFieldBits};
use crate::arithmetic::{adc, mac, sbb, FieldExt, Group, SqrtRatio};
use crate::arithmetic::{adc, mac, sbb, FieldExt, Group, SqrtTableHelpers};
#[cfg(feature = "sqrt-table")]
use crate::arithmetic::SqrtTables;
@ -232,6 +232,7 @@ const DELTA: Fq = Fq::from_raw([
]);
/// `(t - 1) // 2` where t * 2^s + 1 = p with t odd.
#[cfg(any(test, not(feature = "sqrt-table")))]
const T_MINUS1_OVER2: [u64; 4] = [
0x04ca_546e_c623_7590,
0x0000_0000_1123_4c7e,
@ -510,6 +511,21 @@ impl ff::Field for Fq {
self.square()
}
fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) {
#[cfg(feature = "sqrt-table")]
{
FQ_TABLES.sqrt_ratio(num, div)
}
#[cfg(not(feature = "sqrt-table"))]
ff::helpers::sqrt_ratio_generic(num, div)
}
#[cfg(feature = "sqrt-table")]
fn sqrt_alt(&self) -> (Choice, Self) {
FQ_TABLES.sqrt_alt(self)
}
/// Computes the square root of this element, if it exists.
fn sqrt(&self) -> CtOption<Self> {
#[cfg(feature = "sqrt-table")]
@ -519,7 +535,7 @@ impl ff::Field for Fq {
}
#[cfg(not(feature = "sqrt-table"))]
crate::arithmetic::sqrt_tonelli_shanks(self, &T_MINUS1_OVER2)
ff::helpers::sqrt_tonelli_shanks(self, &T_MINUS1_OVER2)
}
/// Computes the multiplicative inverse of this element,
@ -668,9 +684,7 @@ lazy_static! {
static ref FQ_TABLES: SqrtTables<Fq> = SqrtTables::new(0x116A9E, 1206);
}
impl SqrtRatio for Fq {
const T_MINUS1_OVER2: [u64; 4] = T_MINUS1_OVER2;
impl SqrtTableHelpers for Fq {
fn pow_by_t_minus1_over2(&self) -> Self {
let sqr = |x: Fq, i: u32| (0..i).fold(x, |x, _| x.square());
@ -708,16 +722,6 @@ impl SqrtRatio for Fq {
tmp.0[0] as u32
}
#[cfg(feature = "sqrt-table")]
fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) {
FQ_TABLES.sqrt_ratio(num, div)
}
#[cfg(feature = "sqrt-table")]
fn sqrt_alt(&self) -> (Choice, Self) {
FQ_TABLES.sqrt_alt(self)
}
}
impl FieldExt for Fq {