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:
Jack Grigg
2019-05-15 10:35:14 +01:00
parent 40749da9a7
commit 3d2acf48ce
14 changed files with 223 additions and 345 deletions

View File

@@ -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)),
}
})
})
}

View File

@@ -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);
a1.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()));
}

View File

@@ -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,21 +59,18 @@ impl<E: JubjubEngine> Point<E, Unknown> {
x2.mul_assign(&x);
rhs.add_assign(&x2);
match rhs.sqrt() {
Some(mut y) => {
if y.into_repr().is_odd() != sign {
y = y.neg();
}
Some(Point {
x,
y,
infinity: false,
_marker: PhantomData,
})
rhs.sqrt().map(|mut y| {
if y.into_repr().is_odd() != sign {
y = y.neg();
}
None => None,
}
Point {
x,
y,
infinity: false,
_marker: PhantomData,
}
})
}
/// This guarantees the point is in the prime order subgroup
@@ -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();
}
}
}

View File

@@ -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()));
}
{