mirror of
https://github.com/Qortal/pirate-librustzcash.git
synced 2025-02-01 08:12:14 +00:00
Constant-time field square root
WARNING: THIS IS NOT FULLY CONSTANT TIME YET! This will be fixed once we migrate to the jubjub and bls12_381 crates.
This commit is contained in:
parent
40749da9a7
commit
3d2acf48ce
@ -1,7 +1,4 @@
|
||||
use ff::{
|
||||
Field, LegendreSymbol, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr, ScalarEngine,
|
||||
SqrtField,
|
||||
};
|
||||
use ff::{Field, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr, ScalarEngine, SqrtField};
|
||||
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
|
||||
use pairing::{Engine, PairingCurveAffine};
|
||||
|
||||
@ -10,7 +7,7 @@ use std::cmp::Ordering;
|
||||
use std::fmt;
|
||||
use std::num::Wrapping;
|
||||
use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
||||
use subtle::{Choice, ConditionallySelectable, CtOption};
|
||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
|
||||
|
||||
const MODULUS_R: Wrapping<u32> = Wrapping(64513);
|
||||
|
||||
@ -23,6 +20,12 @@ impl Default for Fr {
|
||||
}
|
||||
}
|
||||
|
||||
impl ConstantTimeEq for Fr {
|
||||
fn ct_eq(&self, other: &Fr) -> Choice {
|
||||
(self.0).0.ct_eq(&(other.0).0)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Fr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
write!(f, "{}", (self.0).0)
|
||||
@ -179,25 +182,9 @@ impl Field for Fr {
|
||||
}
|
||||
|
||||
impl SqrtField for Fr {
|
||||
fn legendre(&self) -> LegendreSymbol {
|
||||
// s = self^((r - 1) // 2)
|
||||
let s = self.pow([32256]);
|
||||
if s == <Fr as Field>::zero() {
|
||||
LegendreSymbol::Zero
|
||||
} else if s == <Fr as Field>::one() {
|
||||
LegendreSymbol::QuadraticResidue
|
||||
} else {
|
||||
LegendreSymbol::QuadraticNonResidue
|
||||
}
|
||||
}
|
||||
|
||||
fn sqrt(&self) -> Option<Self> {
|
||||
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)
|
||||
match self.legendre() {
|
||||
LegendreSymbol::Zero => Some(*self),
|
||||
LegendreSymbol::QuadraticNonResidue => None,
|
||||
LegendreSymbol::QuadraticResidue => {
|
||||
let mut c = Fr::root_of_unity();
|
||||
// r = self^((t + 1) // 2)
|
||||
let mut r = self.pow([32]);
|
||||
@ -227,9 +214,7 @@ impl SqrtField for Fr {
|
||||
m = i;
|
||||
}
|
||||
|
||||
Some(r)
|
||||
}
|
||||
}
|
||||
CtOption::new(r, (r * r).ct_eq(self))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -413,98 +413,75 @@ fn prime_field_constants_and_sqrt(
|
||||
);
|
||||
let generator = biguint_to_u64_vec((generator.clone() * &r) % &modulus, limbs);
|
||||
|
||||
let mod_minus_1_over_2 =
|
||||
biguint_to_u64_vec((&modulus - BigUint::from_str("1").unwrap()) >> 1, limbs);
|
||||
let legendre_impl = quote! {
|
||||
fn legendre(&self) -> ::ff::LegendreSymbol {
|
||||
// s = self^((modulus - 1) // 2)
|
||||
let s = self.pow(#mod_minus_1_over_2);
|
||||
if s == Self::zero() {
|
||||
::ff::LegendreSymbol::Zero
|
||||
} else if s == Self::one() {
|
||||
::ff::LegendreSymbol::QuadraticResidue
|
||||
} else {
|
||||
::ff::LegendreSymbol::QuadraticNonResidue
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let sqrt_impl =
|
||||
if (&modulus % BigUint::from_str("4").unwrap()) == BigUint::from_str("3").unwrap() {
|
||||
let mod_minus_3_over_4 =
|
||||
biguint_to_u64_vec((&modulus - BigUint::from_str("3").unwrap()) >> 2, limbs);
|
||||
|
||||
// Compute -R as (m - r)
|
||||
let rneg = biguint_to_u64_vec(&modulus - &r, limbs);
|
||||
let sqrt_impl = if (&modulus % BigUint::from_str("4").unwrap())
|
||||
== BigUint::from_str("3").unwrap()
|
||||
{
|
||||
let mod_plus_1_over_4 =
|
||||
biguint_to_u64_vec((&modulus + BigUint::from_str("1").unwrap()) >> 2, limbs);
|
||||
|
||||
quote! {
|
||||
impl ::ff::SqrtField for #name {
|
||||
#legendre_impl
|
||||
fn sqrt(&self) -> ::subtle::CtOption<Self> {
|
||||
use ::subtle::ConstantTimeEq;
|
||||
|
||||
fn sqrt(&self) -> Option<Self> {
|
||||
// Shank's algorithm for q mod 4 = 3
|
||||
// https://eprint.iacr.org/2012/685.pdf (page 9, algorithm 2)
|
||||
// Because r = 3 (mod 4)
|
||||
// sqrt can be done with only one exponentiation,
|
||||
// via the computation of self^((r + 1) // 4) (mod r)
|
||||
let sqrt = self.pow(#mod_plus_1_over_4);
|
||||
|
||||
let mut a1 = self.pow(#mod_minus_3_over_4);
|
||||
|
||||
let mut a0 = a1.square();
|
||||
a0.mul_assign(self);
|
||||
|
||||
if a0.0 == #repr(#rneg) {
|
||||
None
|
||||
} else {
|
||||
a1.mul_assign(self);
|
||||
Some(a1)
|
||||
}
|
||||
::subtle::CtOption::new(
|
||||
sqrt,
|
||||
(sqrt * &sqrt).ct_eq(self), // Only return Some if it's the square root.
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (&modulus % BigUint::from_str("16").unwrap()) == BigUint::from_str("1").unwrap() {
|
||||
let t_plus_1_over_2 = biguint_to_u64_vec((&t + BigUint::one()) >> 1, limbs);
|
||||
let t = biguint_to_u64_vec(t.clone(), limbs);
|
||||
let t_minus_1_over_2 = biguint_to_u64_vec((&t - BigUint::one()) >> 1, limbs);
|
||||
|
||||
quote! {
|
||||
impl ::ff::SqrtField for #name {
|
||||
#legendre_impl
|
||||
|
||||
fn sqrt(&self) -> Option<Self> {
|
||||
fn sqrt(&self) -> ::subtle::CtOption<Self> {
|
||||
// Tonelli-Shank's algorithm for q mod 16 = 1
|
||||
// https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5)
|
||||
use ::subtle::{ConditionallySelectable, ConstantTimeEq};
|
||||
|
||||
match self.legendre() {
|
||||
::ff::LegendreSymbol::Zero => Some(*self),
|
||||
::ff::LegendreSymbol::QuadraticNonResidue => None,
|
||||
::ff::LegendreSymbol::QuadraticResidue => {
|
||||
let mut c = #name(ROOT_OF_UNITY);
|
||||
let mut r = self.pow(#t_plus_1_over_2);
|
||||
let mut t = self.pow(#t);
|
||||
let mut m = S;
|
||||
// w = self^((t - 1) // 2)
|
||||
let w = self.pow(#t_minus_1_over_2);
|
||||
|
||||
while t != Self::one() {
|
||||
let mut i = 1;
|
||||
{
|
||||
let mut t2i = t.square();
|
||||
loop {
|
||||
if t2i == Self::one() {
|
||||
break;
|
||||
}
|
||||
t2i = t2i.square();
|
||||
i += 1;
|
||||
}
|
||||
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 = #name(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: ::subtle::Choice = 1.into();
|
||||
|
||||
for j in 2..max_v {
|
||||
let tmp_is_one = tmp.ct_eq(&#name::one());
|
||||
let squared = #name::conditional_select(&tmp, &z, tmp_is_one).square();
|
||||
tmp = #name::conditional_select(&squared, &tmp, tmp_is_one);
|
||||
let new_z = #name::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 = #name::conditional_select(&z, &new_z, j_less_than_v);
|
||||
}
|
||||
|
||||
for _ in 0..(m - i - 1) {
|
||||
c = c.square();
|
||||
}
|
||||
r.mul_assign(&c);
|
||||
c = c.square();
|
||||
t.mul_assign(&c);
|
||||
m = i;
|
||||
let result = x * &z;
|
||||
x = #name::conditional_select(&result, &x, b.ct_eq(&#name::one()));
|
||||
z = z.square();
|
||||
b = b * &z;
|
||||
v = k;
|
||||
}
|
||||
|
||||
Some(r)
|
||||
}
|
||||
}
|
||||
::subtle::CtOption::new(
|
||||
x,
|
||||
(x * &x).ct_eq(self), // Only return Some if it's the square root.
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -771,6 +748,13 @@ fn prime_field_impl(
|
||||
let multiply_impl = mul_impl(quote! {self}, quote! {other}, limbs);
|
||||
let montgomery_impl = mont_impl(limbs);
|
||||
|
||||
// (self.0).0[0].ct_eq(&(other.0).0[0]) & (self.0).0[1].ct_eq(&(other.0).0[1]) & ...
|
||||
let mut ct_eq_impl = proc_macro2::TokenStream::new();
|
||||
ct_eq_impl.append_separated(
|
||||
(0..limbs).map(|i| quote! { (self.0).0[#i].ct_eq(&(other.0).0[#i]) }),
|
||||
proc_macro2::Punct::new('&', proc_macro2::Spacing::Alone),
|
||||
);
|
||||
|
||||
// (self.0).0[0], (self.0).0[1], ..., 0, 0, 0, 0, ...
|
||||
let mut into_repr_params = proc_macro2::TokenStream::new();
|
||||
into_repr_params.append_separated(
|
||||
@ -797,6 +781,12 @@ fn prime_field_impl(
|
||||
}
|
||||
}
|
||||
|
||||
impl ::subtle::ConstantTimeEq for #name {
|
||||
fn ct_eq(&self, other: &#name) -> ::subtle::Choice {
|
||||
#ct_eq_impl
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::cmp::PartialEq for #name {
|
||||
fn eq(&self, other: &#name) -> bool {
|
||||
self.0 == other.0
|
||||
|
@ -94,12 +94,9 @@ pub trait Field:
|
||||
|
||||
/// This trait represents an element of a field that has a square root operation described for it.
|
||||
pub trait SqrtField: Field {
|
||||
/// Returns the Legendre symbol of the field element.
|
||||
fn legendre(&self) -> LegendreSymbol;
|
||||
|
||||
/// Returns the square root of the field element, if it is
|
||||
/// quadratic residue.
|
||||
fn sqrt(&self) -> Option<Self>;
|
||||
fn sqrt(&self) -> CtOption<Self>;
|
||||
}
|
||||
|
||||
/// This trait represents a wrapper around a biginteger which can encode any element of a particular
|
||||
@ -199,13 +196,6 @@ pub trait PrimeFieldRepr:
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum LegendreSymbol {
|
||||
Zero = 0,
|
||||
QuadraticResidue = 1,
|
||||
QuadraticNonResidue = -1,
|
||||
}
|
||||
|
||||
/// An error that may occur when trying to interpret a `PrimeFieldRepr` as a
|
||||
/// `PrimeField` element.
|
||||
#[derive(Debug)]
|
||||
|
@ -97,7 +97,7 @@ macro_rules! curve_impl {
|
||||
///
|
||||
/// If and only if `greatest` is set will the lexicographically
|
||||
/// largest y-coordinate be selected.
|
||||
fn get_point_from_x(x: $basefield, greatest: bool) -> Option<$affine> {
|
||||
fn get_point_from_x(x: $basefield, greatest: bool) -> CtOption<$affine> {
|
||||
// Compute x^3 + b
|
||||
let mut x3b = x.square();
|
||||
x3b.mul_assign(&x);
|
||||
@ -199,8 +199,9 @@ macro_rules! curve_impl {
|
||||
let x = $basefield::random(rng);
|
||||
let greatest = rng.next_u32() % 2 != 0;
|
||||
|
||||
if let Some(p) = $affine::get_point_from_x(x, greatest) {
|
||||
let p = p.scale_by_cofactor();
|
||||
let p = $affine::get_point_from_x(x, greatest);
|
||||
if p.is_some().into() {
|
||||
let p = p.unwrap().scale_by_cofactor();
|
||||
|
||||
if !p.is_zero() {
|
||||
return p;
|
||||
@ -603,6 +604,7 @@ pub mod g1 {
|
||||
use rand_core::RngCore;
|
||||
use std::fmt;
|
||||
use std::ops::{AddAssign, MulAssign, Neg, SubAssign};
|
||||
use subtle::CtOption;
|
||||
|
||||
curve_impl!(
|
||||
"G1",
|
||||
@ -807,7 +809,12 @@ pub mod g1 {
|
||||
let x = Fq::from_repr(x)
|
||||
.map_err(|e| GroupDecodingError::CoordinateDecodingError("x coordinate", e))?;
|
||||
|
||||
G1Affine::get_point_from_x(x, greatest).ok_or(GroupDecodingError::NotOnCurve)
|
||||
let ret = G1Affine::get_point_from_x(x, greatest);
|
||||
if ret.is_some().into() {
|
||||
Ok(ret.unwrap())
|
||||
} else {
|
||||
Err(GroupDecodingError::NotOnCurve)
|
||||
}
|
||||
}
|
||||
}
|
||||
fn from_affine(affine: G1Affine) -> Self {
|
||||
@ -919,7 +926,9 @@ pub mod g1 {
|
||||
rhs.mul_assign(&x);
|
||||
rhs.add_assign(&G1Affine::get_coeff_b());
|
||||
|
||||
if let Some(y) = rhs.sqrt() {
|
||||
let y = rhs.sqrt();
|
||||
if y.is_some().into() {
|
||||
let y = y.unwrap();
|
||||
let yrepr = y.into_repr();
|
||||
let negy = y.neg();
|
||||
let negyrepr = negy.into_repr();
|
||||
@ -1270,6 +1279,7 @@ pub mod g2 {
|
||||
use rand_core::RngCore;
|
||||
use std::fmt;
|
||||
use std::ops::{AddAssign, MulAssign, Neg, SubAssign};
|
||||
use subtle::CtOption;
|
||||
|
||||
curve_impl!(
|
||||
"G2",
|
||||
@ -1498,7 +1508,12 @@ pub mod g2 {
|
||||
})?,
|
||||
};
|
||||
|
||||
G2Affine::get_point_from_x(x, greatest).ok_or(GroupDecodingError::NotOnCurve)
|
||||
let ret = G2Affine::get_point_from_x(x, greatest);
|
||||
if ret.is_some().into() {
|
||||
Ok(ret.unwrap())
|
||||
} else {
|
||||
Err(GroupDecodingError::NotOnCurve)
|
||||
}
|
||||
}
|
||||
}
|
||||
fn from_affine(affine: G2Affine) -> Self {
|
||||
@ -1623,7 +1638,9 @@ pub mod g2 {
|
||||
rhs.mul_assign(&x);
|
||||
rhs.add_assign(&G2Affine::get_coeff_b());
|
||||
|
||||
if let Some(y) = rhs.sqrt() {
|
||||
let y = rhs.sqrt();
|
||||
if y.is_some().into() {
|
||||
let y = y.unwrap();
|
||||
let negy = y.neg();
|
||||
|
||||
let p = G2Affine {
|
||||
|
@ -2074,8 +2074,9 @@ fn test_fq_sqrt() {
|
||||
// Ensure sqrt(a)^2 = a for random a
|
||||
let a = Fq::random(&mut rng);
|
||||
|
||||
if let Some(tmp) = a.sqrt() {
|
||||
assert_eq!(a, tmp.square());
|
||||
let tmp = a.sqrt();
|
||||
if tmp.is_some().into() {
|
||||
assert_eq!(a, tmp.unwrap().square());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2205,7 +2206,7 @@ fn test_fq_root_of_unity() {
|
||||
Fq::root_of_unity()
|
||||
);
|
||||
assert_eq!(Fq::root_of_unity().pow([1 << Fq::S]), Fq::one());
|
||||
assert!(Fq::multiplicative_generator().sqrt().is_none());
|
||||
assert!(bool::from(Fq::multiplicative_generator().sqrt().is_none()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -2231,40 +2232,3 @@ fn test_fq_ordering() {
|
||||
fn fq_repr_tests() {
|
||||
crate::tests::repr::random_repr_tests::<Fq>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fq_legendre() {
|
||||
use ff::LegendreSymbol::*;
|
||||
use ff::SqrtField;
|
||||
|
||||
assert_eq!(QuadraticResidue, Fq::one().legendre());
|
||||
assert_eq!(Zero, Fq::zero().legendre());
|
||||
|
||||
assert_eq!(
|
||||
QuadraticNonResidue,
|
||||
Fq::from_repr(FqRepr::from(2)).unwrap().legendre()
|
||||
);
|
||||
assert_eq!(
|
||||
QuadraticResidue,
|
||||
Fq::from_repr(FqRepr::from(4)).unwrap().legendre()
|
||||
);
|
||||
|
||||
let e = FqRepr([
|
||||
0x52a112f249778642,
|
||||
0xd0bedb989b7991f,
|
||||
0xdad3b6681aa63c05,
|
||||
0xf2efc0bb4721b283,
|
||||
0x6057a98f18c24733,
|
||||
0x1022c2fd122889e4,
|
||||
]);
|
||||
assert_eq!(QuadraticNonResidue, Fq::from_repr(e).unwrap().legendre());
|
||||
let e = FqRepr([
|
||||
0x6dae594e53a96c74,
|
||||
0x19b16ca9ba64b37b,
|
||||
0x5c764661a59bfc68,
|
||||
0xaa346e9b31c60a,
|
||||
0x346059f9d87a9fa9,
|
||||
0x1d61ac6bfd5c88b,
|
||||
]);
|
||||
assert_eq!(QuadraticResidue, Fq::from_repr(e).unwrap().legendre());
|
||||
}
|
||||
|
@ -244,15 +244,13 @@ impl Field for Fq2 {
|
||||
}
|
||||
|
||||
impl SqrtField for Fq2 {
|
||||
fn legendre(&self) -> ::ff::LegendreSymbol {
|
||||
self.norm().legendre()
|
||||
}
|
||||
|
||||
fn sqrt(&self) -> Option<Self> {
|
||||
/// WARNING: THIS IS NOT ACTUALLY CONSTANT TIME YET!
|
||||
/// THIS WILL BE REPLACED BY THE bls12_381 CRATE, WHICH IS CONSTANT TIME!
|
||||
fn sqrt(&self) -> CtOption<Self> {
|
||||
// Algorithm 9, https://eprint.iacr.org/2012/685.pdf
|
||||
|
||||
if self.is_zero() {
|
||||
Some(Self::zero())
|
||||
CtOption::new(Self::zero(), Choice::from(1))
|
||||
} else {
|
||||
// a1 = self^((q - 3) / 4)
|
||||
let mut a1 = self.pow([
|
||||
@ -275,7 +273,7 @@ impl SqrtField for Fq2 {
|
||||
};
|
||||
|
||||
if a0 == neg1 {
|
||||
None
|
||||
CtOption::new(Self::zero(), Choice::from(0))
|
||||
} else {
|
||||
a1.mul_assign(self);
|
||||
|
||||
@ -298,7 +296,7 @@ impl SqrtField for Fq2 {
|
||||
a1.mul_assign(&alpha);
|
||||
}
|
||||
|
||||
Some(a1)
|
||||
CtOption::new(a1, Choice::from(1))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -993,18 +991,6 @@ fn test_fq2_sqrt() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fq2_legendre() {
|
||||
use ff::LegendreSymbol::*;
|
||||
|
||||
assert_eq!(Zero, Fq2::zero().legendre());
|
||||
// i^2 = -1
|
||||
let mut m1 = Fq2::one().neg();
|
||||
assert_eq!(QuadraticResidue, m1.legendre());
|
||||
m1.mul_by_nonresidue();
|
||||
assert_eq!(QuadraticNonResidue, m1.legendre());
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
use rand_core::SeedableRng;
|
||||
#[cfg(test)]
|
||||
|
@ -278,30 +278,6 @@ fn test_fr_repr_sub_noborrow() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fr_legendre() {
|
||||
use ff::LegendreSymbol::*;
|
||||
use ff::SqrtField;
|
||||
|
||||
assert_eq!(QuadraticResidue, Fr::one().legendre());
|
||||
assert_eq!(Zero, Fr::zero().legendre());
|
||||
|
||||
let e = FrRepr([
|
||||
0x0dbc5349cd5664da,
|
||||
0x8ac5b6296e3ae29d,
|
||||
0x127cb819feceaa3b,
|
||||
0x3a6b21fb03867191,
|
||||
]);
|
||||
assert_eq!(QuadraticResidue, Fr::from_repr(e).unwrap().legendre());
|
||||
let e = FrRepr([
|
||||
0x96341aefd047c045,
|
||||
0x9b5f4254500a4d65,
|
||||
0x1ee08223b68ac240,
|
||||
0x31d9cd545c0ec7c6,
|
||||
]);
|
||||
assert_eq!(QuadraticNonResidue, Fr::from_repr(e).unwrap().legendre());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fr_repr_add_nocarry() {
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -833,8 +809,9 @@ fn test_fr_sqrt() {
|
||||
// Ensure sqrt(a)^2 = a for random a
|
||||
let a = Fr::random(&mut rng);
|
||||
|
||||
if let Some(tmp) = a.sqrt() {
|
||||
assert_eq!(a, tmp.square());
|
||||
let tmp = a.sqrt();
|
||||
if tmp.is_some().into() {
|
||||
assert_eq!(a, tmp.unwrap().square());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -996,7 +973,7 @@ fn test_fr_root_of_unity() {
|
||||
Fr::root_of_unity()
|
||||
);
|
||||
assert_eq!(Fr::root_of_unity().pow([1 << Fr::S]), Fr::one());
|
||||
assert!(Fr::multiplicative_generator().sqrt().is_none());
|
||||
assert!(bool::from(Fr::multiplicative_generator().sqrt().is_none()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -193,7 +193,10 @@ fn test_g1_uncompressed_invalid_vectors() {
|
||||
x3b.mul_assign(&x);
|
||||
x3b.add_assign(&Fq::from_repr(FqRepr::from(4)).unwrap()); // TODO: perhaps expose coeff_b through API?
|
||||
|
||||
if let Some(y) = x3b.sqrt() {
|
||||
let y = x3b.sqrt();
|
||||
if y.is_some().into() {
|
||||
let y = y.unwrap();
|
||||
|
||||
// We know this is on the curve, but it's likely not going to be in the correct subgroup.
|
||||
x.into_repr().write_be(&mut o.as_mut()[0..]).unwrap();
|
||||
y.into_repr().write_be(&mut o.as_mut()[48..]).unwrap();
|
||||
@ -332,7 +335,10 @@ fn test_g2_uncompressed_invalid_vectors() {
|
||||
c1: Fq::from_repr(FqRepr::from(4)).unwrap(),
|
||||
}); // TODO: perhaps expose coeff_b through API?
|
||||
|
||||
if let Some(y) = x3b.sqrt() {
|
||||
let y = x3b.sqrt();
|
||||
if y.is_some().into() {
|
||||
let y = y.unwrap();
|
||||
|
||||
// We know this is on the curve, but it's likely not going to be in the correct subgroup.
|
||||
x.c1.into_repr().write_be(&mut o.as_mut()[0..]).unwrap();
|
||||
x.c0.into_repr().write_be(&mut o.as_mut()[48..]).unwrap();
|
||||
@ -424,7 +430,7 @@ fn test_g1_compressed_invalid_vectors() {
|
||||
x3b.mul_assign(&x);
|
||||
x3b.add_assign(&Fq::from_repr(FqRepr::from(4)).unwrap()); // TODO: perhaps expose coeff_b through API?
|
||||
|
||||
if let Some(_) = x3b.sqrt() {
|
||||
if x3b.sqrt().is_some().into() {
|
||||
x.add_assign(&Fq::one());
|
||||
} else {
|
||||
x.into_repr().write_be(&mut o.as_mut()[0..]).unwrap();
|
||||
@ -448,7 +454,7 @@ fn test_g1_compressed_invalid_vectors() {
|
||||
x3b.mul_assign(&x);
|
||||
x3b.add_assign(&Fq::from_repr(FqRepr::from(4)).unwrap()); // TODO: perhaps expose coeff_b through API?
|
||||
|
||||
if let Some(_) = x3b.sqrt() {
|
||||
if x3b.sqrt().is_some().into() {
|
||||
// We know this is on the curve, but it's likely not going to be in the correct subgroup.
|
||||
x.into_repr().write_be(&mut o.as_mut()[0..]).unwrap();
|
||||
o.as_mut()[0] |= 0b1000_0000;
|
||||
@ -556,7 +562,7 @@ fn test_g2_compressed_invalid_vectors() {
|
||||
c1: Fq::from_repr(FqRepr::from(4)).unwrap(),
|
||||
}); // TODO: perhaps expose coeff_b through API?
|
||||
|
||||
if let Some(_) = x3b.sqrt() {
|
||||
if x3b.sqrt().is_some().into() {
|
||||
x.add_assign(&Fq2::one());
|
||||
} else {
|
||||
x.c1.into_repr().write_be(&mut o.as_mut()[0..]).unwrap();
|
||||
@ -587,7 +593,7 @@ fn test_g2_compressed_invalid_vectors() {
|
||||
c1: Fq::from_repr(FqRepr::from(4)).unwrap(),
|
||||
}); // TODO: perhaps expose coeff_b through API?
|
||||
|
||||
if let Some(_) = x3b.sqrt() {
|
||||
if x3b.sqrt().is_some().into() {
|
||||
// We know this is on the curve, but it's likely not going to be in the correct subgroup.
|
||||
x.c1.into_repr().write_be(&mut o.as_mut()[0..]).unwrap();
|
||||
x.c0.into_repr().write_be(&mut o.as_mut()[48..]).unwrap();
|
||||
|
@ -1,4 +1,4 @@
|
||||
use ff::{Field, LegendreSymbol, PrimeField, SqrtField};
|
||||
use ff::{Field, PrimeField, SqrtField};
|
||||
use rand_core::{RngCore, SeedableRng};
|
||||
use rand_xorshift::XorShiftRng;
|
||||
|
||||
@ -32,7 +32,6 @@ pub fn random_sqrt_tests<F: SqrtField>() {
|
||||
for _ in 0..10000 {
|
||||
let a = F::random(&mut rng);
|
||||
let b = a.square();
|
||||
assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue);
|
||||
|
||||
let b = b.sqrt().unwrap();
|
||||
let negb = b.neg();
|
||||
@ -43,7 +42,6 @@ pub fn random_sqrt_tests<F: SqrtField>() {
|
||||
let mut c = F::one();
|
||||
for _ in 0..10000 {
|
||||
let mut b = c.square();
|
||||
assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue);
|
||||
|
||||
b = b.sqrt().unwrap();
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField};
|
||||
use std::ops::{AddAssign, MulAssign, Neg, SubAssign};
|
||||
use subtle::{Choice, CtOption};
|
||||
use subtle::CtOption;
|
||||
|
||||
use super::{montgomery, JubjubEngine, JubjubParams, PrimeOrder, Unknown};
|
||||
|
||||
@ -126,7 +126,7 @@ impl<E: JubjubEngine> Point<E, Unknown> {
|
||||
// tmp1 = (y^2 - 1) / (dy^2 + 1)
|
||||
tmp1.mul_assign(&tmp2);
|
||||
|
||||
match tmp1.sqrt().map(|mut x| {
|
||||
tmp1.sqrt().map(|mut x| {
|
||||
if x.into_repr().is_odd() != sign {
|
||||
x = x.neg();
|
||||
}
|
||||
@ -141,10 +141,7 @@ impl<E: JubjubEngine> Point<E, Unknown> {
|
||||
z: E::Fr::one(),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}) {
|
||||
Some(p) => CtOption::new(p, Choice::from(1)),
|
||||
None => CtOption::new(Point::zero(), Choice::from(0)),
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1,12 +1,11 @@
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use ff::{
|
||||
adc, mac_with_carry, sbb, BitIterator, Field,
|
||||
LegendreSymbol::{self, *},
|
||||
PrimeField, PrimeFieldDecodingError, PrimeFieldRepr, SqrtField,
|
||||
adc, mac_with_carry, sbb, BitIterator, Field, PrimeField, PrimeFieldDecodingError,
|
||||
PrimeFieldRepr, SqrtField,
|
||||
};
|
||||
use rand_core::RngCore;
|
||||
use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
||||
use subtle::{Choice, ConditionallySelectable, CtOption};
|
||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
|
||||
|
||||
use super::ToUniform;
|
||||
|
||||
@ -264,6 +263,15 @@ impl Default for Fs {
|
||||
}
|
||||
}
|
||||
|
||||
impl ConstantTimeEq for Fs {
|
||||
fn ct_eq(&self, other: &Fs) -> Choice {
|
||||
(self.0).0[0].ct_eq(&(other.0).0[0])
|
||||
& (self.0).0[1].ct_eq(&(other.0).0[1])
|
||||
& (self.0).0[2].ct_eq(&(other.0).0[2])
|
||||
& (self.0).0[3].ct_eq(&(other.0).0[3])
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for Fs {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
write!(f, "Fs({})", self.into_repr())
|
||||
@ -731,24 +739,7 @@ impl ToUniform for Fs {
|
||||
}
|
||||
|
||||
impl SqrtField for Fs {
|
||||
fn legendre(&self) -> LegendreSymbol {
|
||||
// s = self^((s - 1) // 2)
|
||||
let s = self.pow([
|
||||
0x684b872f6b7b965b,
|
||||
0x53341049e6640841,
|
||||
0x83339d80809a1d80,
|
||||
0x73eda753299d7d4,
|
||||
]);
|
||||
if s == Self::zero() {
|
||||
Zero
|
||||
} else if s == Self::one() {
|
||||
QuadraticResidue
|
||||
} else {
|
||||
QuadraticNonResidue
|
||||
}
|
||||
}
|
||||
|
||||
fn sqrt(&self) -> Option<Self> {
|
||||
fn sqrt(&self) -> CtOption<Self> {
|
||||
// Shank's algorithm for s mod 4 = 3
|
||||
// https://eprint.iacr.org/2012/685.pdf (page 9, algorithm 2)
|
||||
|
||||
@ -761,13 +752,9 @@ impl SqrtField for Fs {
|
||||
]);
|
||||
let mut a0 = a1.square();
|
||||
a0.mul_assign(self);
|
||||
|
||||
if a0 == NEGATIVE_ONE {
|
||||
None
|
||||
} else {
|
||||
a1.mul_assign(self);
|
||||
Some(a1)
|
||||
}
|
||||
|
||||
CtOption::new(a1, !a0.ct_eq(&NEGATIVE_ONE))
|
||||
}
|
||||
}
|
||||
|
||||
@ -1025,27 +1012,6 @@ fn test_fs_repr_sub_noborrow() {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fs_legendre() {
|
||||
assert_eq!(QuadraticResidue, Fs::one().legendre());
|
||||
assert_eq!(Zero, Fs::zero().legendre());
|
||||
|
||||
let e = FsRepr([
|
||||
0x8385eec23df1f88e,
|
||||
0x9a01fb412b2dba16,
|
||||
0x4c928edcdd6c22f,
|
||||
0x9f2df7ef69ecef9,
|
||||
]);
|
||||
assert_eq!(QuadraticResidue, Fs::from_repr(e).unwrap().legendre());
|
||||
let e = FsRepr([
|
||||
0xe8ed9f299da78568,
|
||||
0x35efdebc88b2209,
|
||||
0xc82125cb1f916dbe,
|
||||
0x6813d2b38c39bd0,
|
||||
]);
|
||||
assert_eq!(QuadraticNonResidue, Fs::from_repr(e).unwrap().legendre());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fr_repr_add_nocarry() {
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -1569,8 +1535,9 @@ fn test_fs_sqrt() {
|
||||
// Ensure sqrt(a)^2 = a for random a
|
||||
let a = Fs::random(&mut rng);
|
||||
|
||||
if let Some(tmp) = a.sqrt() {
|
||||
assert_eq!(a, tmp.square());
|
||||
let tmp = a.sqrt();
|
||||
if tmp.is_some().into() {
|
||||
assert_eq!(a, tmp.unwrap().square());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1730,5 +1697,5 @@ fn test_fs_root_of_unity() {
|
||||
Fs::root_of_unity()
|
||||
);
|
||||
assert_eq!(Fs::root_of_unity().pow([1 << Fs::S]), Fs::one());
|
||||
assert!(Fs::multiplicative_generator().sqrt().is_none());
|
||||
assert!(bool::from(Fs::multiplicative_generator().sqrt().is_none()));
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField};
|
||||
use std::ops::{AddAssign, MulAssign, Neg, SubAssign};
|
||||
use subtle::CtOption;
|
||||
|
||||
use super::{edwards, JubjubEngine, JubjubParams, PrimeOrder, Unknown};
|
||||
|
||||
@ -47,7 +48,7 @@ impl<E: JubjubEngine, Subgroup> PartialEq for Point<E, Subgroup> {
|
||||
}
|
||||
|
||||
impl<E: JubjubEngine> Point<E, Unknown> {
|
||||
pub fn get_for_x(x: E::Fr, sign: bool, params: &E::Params) -> Option<Self> {
|
||||
pub fn get_for_x(x: E::Fr, sign: bool, params: &E::Params) -> CtOption<Self> {
|
||||
// Given an x on the curve, y = sqrt(x^3 + A*x^2 + x)
|
||||
|
||||
let mut x2 = x.square();
|
||||
@ -58,22 +59,19 @@ impl<E: JubjubEngine> Point<E, Unknown> {
|
||||
x2.mul_assign(&x);
|
||||
rhs.add_assign(&x2);
|
||||
|
||||
match rhs.sqrt() {
|
||||
Some(mut y) => {
|
||||
rhs.sqrt().map(|mut y| {
|
||||
if y.into_repr().is_odd() != sign {
|
||||
y = y.neg();
|
||||
}
|
||||
|
||||
Some(Point {
|
||||
Point {
|
||||
x,
|
||||
y,
|
||||
infinity: false,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
})
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// This guarantees the point is in the prime order subgroup
|
||||
#[must_use]
|
||||
@ -88,8 +86,9 @@ impl<E: JubjubEngine> Point<E, Unknown> {
|
||||
let x = E::Fr::random(rng);
|
||||
let sign = rng.next_u32() % 2 != 0;
|
||||
|
||||
if let Some(p) = Self::get_for_x(x, sign, params) {
|
||||
return p;
|
||||
let p = Self::get_for_x(x, sign, params);
|
||||
if p.is_some().into() {
|
||||
return p.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use super::{edwards, montgomery, JubjubEngine, JubjubParams, PrimeOrder};
|
||||
|
||||
use ff::{Field, LegendreSymbol, PrimeField, PrimeFieldRepr, SqrtField};
|
||||
use ff::{Field, PrimeField, PrimeFieldRepr, SqrtField};
|
||||
use std::ops::{AddAssign, MulAssign, Neg, SubAssign};
|
||||
|
||||
use rand_core::{RngCore, SeedableRng};
|
||||
@ -319,8 +319,8 @@ fn test_jubjub_params<E: JubjubEngine>(params: &E::Params) {
|
||||
// The twisted Edwards addition law is complete when d is nonsquare
|
||||
// and a is square.
|
||||
|
||||
assert!(params.edwards_d().legendre() == LegendreSymbol::QuadraticNonResidue);
|
||||
assert!(a.legendre() == LegendreSymbol::QuadraticResidue);
|
||||
assert!(bool::from(params.edwards_d().sqrt().is_none()));
|
||||
assert!(bool::from(a.sqrt().is_some()));
|
||||
}
|
||||
|
||||
{
|
||||
@ -330,30 +330,30 @@ fn test_jubjub_params<E: JubjubEngine>(params: &E::Params) {
|
||||
let mut tmp = *params.edwards_d();
|
||||
|
||||
// 1 / d is nonsquare
|
||||
assert!(tmp.invert().unwrap().legendre() == LegendreSymbol::QuadraticNonResidue);
|
||||
assert!(bool::from(tmp.invert().unwrap().sqrt().is_none()));
|
||||
|
||||
// tmp = -d
|
||||
tmp = tmp.neg();
|
||||
|
||||
// -d is nonsquare
|
||||
assert!(tmp.legendre() == LegendreSymbol::QuadraticNonResidue);
|
||||
assert!(bool::from(tmp.sqrt().is_none()));
|
||||
|
||||
// 1 / -d is nonsquare
|
||||
assert!(tmp.invert().unwrap().legendre() == LegendreSymbol::QuadraticNonResidue);
|
||||
assert!(bool::from(tmp.invert().unwrap().sqrt().is_none()));
|
||||
}
|
||||
|
||||
{
|
||||
// Check that A^2 - 4 is nonsquare:
|
||||
let mut tmp = params.montgomery_a().square();
|
||||
tmp.sub_assign(&E::Fr::from_str("4").unwrap());
|
||||
assert!(tmp.legendre() == LegendreSymbol::QuadraticNonResidue);
|
||||
assert!(bool::from(tmp.sqrt().is_none()));
|
||||
}
|
||||
|
||||
{
|
||||
// Check that A - 2 is nonsquare:
|
||||
let mut tmp = params.montgomery_a().clone();
|
||||
tmp.sub_assign(&E::Fr::from_str("2").unwrap());
|
||||
assert!(tmp.legendre() == LegendreSymbol::QuadraticNonResidue);
|
||||
assert!(bool::from(tmp.sqrt().is_none()));
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -1025,8 +1025,9 @@ mod test {
|
||||
let x = Fr::random(rng);
|
||||
let s: bool = rng.next_u32() % 2 != 0;
|
||||
|
||||
if let Some(p) = montgomery::Point::<Bls12, _>::get_for_x(x, s, params) {
|
||||
break p;
|
||||
let p = montgomery::Point::<Bls12, _>::get_for_x(x, s, params);
|
||||
if p.is_some().into() {
|
||||
break p.unwrap();
|
||||
}
|
||||
};
|
||||
|
||||
@ -1034,8 +1035,9 @@ mod test {
|
||||
let x = Fr::random(rng);
|
||||
let s: bool = rng.next_u32() % 2 != 0;
|
||||
|
||||
if let Some(p) = montgomery::Point::<Bls12, _>::get_for_x(x, s, params) {
|
||||
break p;
|
||||
let p = montgomery::Point::<Bls12, _>::get_for_x(x, s, params);
|
||||
if p.is_some().into() {
|
||||
break p.unwrap();
|
||||
}
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user