mirror of
https://github.com/Qortal/pirate-librustzcash.git
synced 2025-07-30 20:11:23 +00:00
Constant-time field inversion
WARNING: THIS IS NOT ACTUALLY CONSTANT TIME YET! The jubjub and bls12_381 crates will replace our constant-time usages, but we NEED to fix ff_derive because other users will expect it to implement the Field trait correctly.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField};
|
||||
use std::ops::{AddAssign, MulAssign, Neg, SubAssign};
|
||||
use subtle::{Choice, CtOption};
|
||||
|
||||
use super::{montgomery, JubjubEngine, JubjubParams, PrimeOrder, Unknown};
|
||||
|
||||
@@ -90,10 +91,14 @@ impl<E: JubjubEngine> Point<E, Unknown> {
|
||||
y_repr.as_mut()[3] &= 0x7fffffffffffffff;
|
||||
|
||||
match E::Fr::from_repr(y_repr) {
|
||||
Ok(y) => match Self::get_for_y(y, x_sign, params) {
|
||||
Some(p) => Ok(p),
|
||||
None => Err(io::Error::new(io::ErrorKind::InvalidInput, "not on curve")),
|
||||
},
|
||||
Ok(y) => {
|
||||
let p = Self::get_for_y(y, x_sign, params);
|
||||
if bool::from(p.is_some()) {
|
||||
Ok(p.unwrap())
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::InvalidInput, "not on curve"))
|
||||
}
|
||||
}
|
||||
Err(_) => Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"y is not in field",
|
||||
@@ -101,7 +106,7 @@ impl<E: JubjubEngine> Point<E, Unknown> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_for_y(y: E::Fr, sign: bool, params: &E::Params) -> Option<Self> {
|
||||
pub fn get_for_y(y: E::Fr, sign: bool, params: &E::Params) -> CtOption<Self> {
|
||||
// Given a y on the curve, x^2 = (y^2 - 1) / (dy^2 + 1)
|
||||
// This is defined for all valid y-coordinates,
|
||||
// as dy^2 + 1 = 0 has no solution in Fr.
|
||||
@@ -117,33 +122,30 @@ impl<E: JubjubEngine> Point<E, Unknown> {
|
||||
// tmp1 = y^2 - 1
|
||||
tmp1.sub_assign(&E::Fr::one());
|
||||
|
||||
match tmp2.inverse() {
|
||||
Some(tmp2) => {
|
||||
// tmp1 = (y^2 - 1) / (dy^2 + 1)
|
||||
tmp1.mul_assign(&tmp2);
|
||||
tmp2.invert().and_then(|tmp2| {
|
||||
// tmp1 = (y^2 - 1) / (dy^2 + 1)
|
||||
tmp1.mul_assign(&tmp2);
|
||||
|
||||
match tmp1.sqrt() {
|
||||
Some(mut x) => {
|
||||
if x.into_repr().is_odd() != sign {
|
||||
x = x.neg();
|
||||
}
|
||||
|
||||
let mut t = x;
|
||||
t.mul_assign(&y);
|
||||
|
||||
Some(Point {
|
||||
x,
|
||||
y,
|
||||
t,
|
||||
z: E::Fr::one(),
|
||||
_marker: PhantomData,
|
||||
})
|
||||
}
|
||||
None => None,
|
||||
match tmp1.sqrt().map(|mut x| {
|
||||
if x.into_repr().is_odd() != sign {
|
||||
x = x.neg();
|
||||
}
|
||||
|
||||
let mut t = x;
|
||||
t.mul_assign(&y);
|
||||
|
||||
Point {
|
||||
x,
|
||||
y,
|
||||
t,
|
||||
z: E::Fr::one(),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}) {
|
||||
Some(p) => CtOption::new(p, Choice::from(1)),
|
||||
None => CtOption::new(Point::zero(), Choice::from(0)),
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// This guarantees the point is in the prime order subgroup
|
||||
@@ -159,8 +161,9 @@ impl<E: JubjubEngine> Point<E, Unknown> {
|
||||
let y = E::Fr::random(rng);
|
||||
let sign = rng.next_u32() % 2 != 0;
|
||||
|
||||
if let Some(p) = Self::get_for_y(y, sign, params) {
|
||||
return p;
|
||||
let p = Self::get_for_y(y, sign, params);
|
||||
if bool::from(p.is_some()) {
|
||||
return p.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -305,7 +308,7 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
|
||||
|
||||
/// Convert to affine coordinates
|
||||
pub fn to_xy(&self) -> (E::Fr, E::Fr) {
|
||||
let zinv = self.z.inverse().unwrap();
|
||||
let zinv = self.z.invert().unwrap();
|
||||
|
||||
let mut x = self.x;
|
||||
x.mul_assign(&zinv);
|
||||
|
@@ -6,7 +6,7 @@ use ff::{
|
||||
};
|
||||
use rand_core::RngCore;
|
||||
use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
||||
use subtle::{Choice, ConditionallySelectable};
|
||||
use subtle::{Choice, ConditionallySelectable, CtOption};
|
||||
|
||||
use super::ToUniform;
|
||||
|
||||
@@ -258,6 +258,12 @@ impl PrimeFieldRepr for FsRepr {
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Fs(FsRepr);
|
||||
|
||||
impl Default for Fs {
|
||||
fn default() -> Self {
|
||||
Fs::zero()
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for Fs {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
write!(f, "Fs({})", self.into_repr())
|
||||
@@ -526,9 +532,11 @@ impl Field for Fs {
|
||||
ret
|
||||
}
|
||||
|
||||
fn inverse(&self) -> Option<Self> {
|
||||
/// WARNING: THIS IS NOT ACTUALLY CONSTANT TIME YET!
|
||||
/// THIS WILL BE REPLACED BY THE jubjub CRATE, WHICH IS CONSTANT TIME!
|
||||
fn invert(&self) -> CtOption<Self> {
|
||||
if self.is_zero() {
|
||||
None
|
||||
CtOption::new(Self::zero(), Choice::from(0))
|
||||
} else {
|
||||
// Guajardo Kumar Paar Pelzl
|
||||
// Efficient Software-Implementation of Finite Fields with Applications to Cryptography
|
||||
@@ -574,9 +582,9 @@ impl Field for Fs {
|
||||
}
|
||||
|
||||
if u == one {
|
||||
Some(b)
|
||||
CtOption::new(b, Choice::from(1))
|
||||
} else {
|
||||
Some(c)
|
||||
CtOption::new(c, Choice::from(1))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1454,8 +1462,8 @@ fn test_fr_squaring() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fs_inverse() {
|
||||
assert!(Fs::zero().inverse().is_none());
|
||||
fn test_fs_invert() {
|
||||
assert!(bool::from(Fs::zero().invert().is_none()));
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
@@ -1467,7 +1475,7 @@ fn test_fs_inverse() {
|
||||
for _ in 0..1000 {
|
||||
// Ensure that a * a^-1 = 1
|
||||
let mut a = Fs::random(&mut rng);
|
||||
let ainv = a.inverse().unwrap();
|
||||
let ainv = a.invert().unwrap();
|
||||
a.mul_assign(&ainv);
|
||||
assert_eq!(a, one);
|
||||
}
|
||||
|
@@ -139,11 +139,11 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
|
||||
{
|
||||
let mut tmp = E::Fr::one();
|
||||
tmp.sub_assign(&y);
|
||||
u.mul_assign(&tmp.inverse().unwrap())
|
||||
u.mul_assign(&tmp.invert().unwrap())
|
||||
}
|
||||
|
||||
let mut v = u;
|
||||
v.mul_assign(&x.inverse().unwrap());
|
||||
v.mul_assign(&x.invert().unwrap());
|
||||
|
||||
// Scale it into the correct curve constants
|
||||
v.mul_assign(params.scale());
|
||||
@@ -226,7 +226,8 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
|
||||
}
|
||||
{
|
||||
let tmp = self.y.double();
|
||||
delta.mul_assign(&tmp.inverse().expect("y is nonzero so this must be nonzero"));
|
||||
// y is nonzero so this must be nonzero
|
||||
delta.mul_assign(&tmp.invert().unwrap());
|
||||
}
|
||||
|
||||
let mut x3 = delta.square();
|
||||
@@ -272,10 +273,8 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
|
||||
{
|
||||
let mut tmp = other.x;
|
||||
tmp.sub_assign(&self.x);
|
||||
delta.mul_assign(
|
||||
&tmp.inverse()
|
||||
.expect("self.x != other.x, so this must be nonzero"),
|
||||
);
|
||||
// self.x != other.x, so this must be nonzero
|
||||
delta.mul_assign(&tmp.invert().unwrap());
|
||||
}
|
||||
|
||||
let mut x3 = delta.square();
|
||||
|
@@ -234,7 +234,9 @@ fn test_get_for<E: JubjubEngine>(params: &E::Params) {
|
||||
let y = E::Fr::random(rng);
|
||||
let sign = rng.next_u32() % 2 == 1;
|
||||
|
||||
if let Some(mut p) = edwards::Point::<E, _>::get_for_y(y, sign, params) {
|
||||
let p = edwards::Point::<E, _>::get_for_y(y, sign, params);
|
||||
if bool::from(p.is_some()) {
|
||||
let mut p = p.unwrap();
|
||||
assert!(p.to_xy().0.into_repr().is_odd() == sign);
|
||||
p = p.negate();
|
||||
assert!(edwards::Point::<E, _>::get_for_y(y, !sign, params).unwrap() == p);
|
||||
@@ -328,7 +330,7 @@ fn test_jubjub_params<E: JubjubEngine>(params: &E::Params) {
|
||||
let mut tmp = *params.edwards_d();
|
||||
|
||||
// 1 / d is nonsquare
|
||||
assert!(tmp.inverse().unwrap().legendre() == LegendreSymbol::QuadraticNonResidue);
|
||||
assert!(tmp.invert().unwrap().legendre() == LegendreSymbol::QuadraticNonResidue);
|
||||
|
||||
// tmp = -d
|
||||
tmp = tmp.neg();
|
||||
@@ -337,7 +339,7 @@ fn test_jubjub_params<E: JubjubEngine>(params: &E::Params) {
|
||||
assert!(tmp.legendre() == LegendreSymbol::QuadraticNonResidue);
|
||||
|
||||
// 1 / -d is nonsquare
|
||||
assert!(tmp.inverse().unwrap().legendre() == LegendreSymbol::QuadraticNonResidue);
|
||||
assert!(tmp.invert().unwrap().legendre() == LegendreSymbol::QuadraticNonResidue);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -358,7 +360,7 @@ fn test_jubjub_params<E: JubjubEngine>(params: &E::Params) {
|
||||
// Check the validity of the scaling factor
|
||||
let mut tmp = a;
|
||||
tmp.sub_assign(¶ms.edwards_d());
|
||||
tmp = tmp.inverse().unwrap();
|
||||
tmp = tmp.invert().unwrap();
|
||||
tmp.mul_assign(&E::Fr::from_str("4").unwrap());
|
||||
tmp = tmp.sqrt().unwrap();
|
||||
assert_eq!(&tmp, params.scale());
|
||||
|
Reference in New Issue
Block a user