Files
cli/vendor/ff/src/helpers.rs

129 lines
4.7 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Helper methods for implementing the `ff` traits.
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
use crate::PrimeField;
/// Constant-time implementation of TonelliShanks' square-root algorithm for
/// `p mod 16 = 1`.
///
/// `tm1d2` should be set to `(t - 1) // 2`, where `t = (modulus - 1) >> F::S`.
///
/// ## Implementing [`Field::sqrt`]
///
/// This function can be used to implement [`Field::sqrt`] for fields that both implement
/// [`PrimeField`] and satisfy `p mod 16 = 1`.
///
/// [`Field::sqrt`]: crate::Field::sqrt
pub fn sqrt_tonelli_shanks<F: PrimeField, S: AsRef<[u64]>>(f: &F, tm1d2: S) -> CtOption<F> {
// This is a constant-time version of https://eprint.iacr.org/2012/685.pdf (page 12,
// algorithm 5). Steps 2-5 of the algorithm are omitted because they are only needed
// to detect non-square input; it is more efficient to do that by checking at the end
// whether the square of the result is the input.
// 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 b2k = b.square();
let mut j_less_than_v: Choice = 1.into();
// This loop has three phases based on the value of k for algorithm 5:
// - for j <= k, we square b2k in order to calculate b^{2^k}.
// - for k < j <= v, we square z in order to calculate ω.
// - for j > v, we do nothing.
for j in 2..max_v {
let b2k_is_one = b2k.ct_eq(&F::ONE);
let squared = F::conditional_select(&b2k, &z, b2k_is_one).square();
b2k = F::conditional_select(&squared, &b2k, b2k_is_one);
let new_z = F::conditional_select(&z, &squared, b2k_is_one);
j_less_than_v &= !j.ct_eq(&v);
k = u32::conditional_select(&j, &k, b2k_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.
)
}
/// 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 this method, $G_S$ is currently [`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.
///
/// ## Implementing [`Field::sqrt_ratio`]
///
/// This function can be used to implement [`Field::sqrt_ratio`] for fields that also
/// implement [`PrimeField`]. If doing so, the default implementation of [`Field::sqrt`]
/// *MUST* be overridden, or else both functions will recurse in a cycle until a stack
/// overflow occurs.
///
/// [`Field::sqrt_ratio`]: crate::Field::sqrt_ratio
/// [`Field::sqrt`]: crate::Field::sqrt
pub fn sqrt_ratio_generic<F: PrimeField>(num: &F, div: &F) -> (Choice, F) {
// 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(F::ZERO) * num;
let b = a * F::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(),
)
}