Skip to content

Commit

Permalink
Merge pull request #128 from HorizenOfficial/endo
Browse files Browse the repository at this point in the history
Endo
  • Loading branch information
DanieleDiBenedetto authored Nov 3, 2021
2 parents e10cb02 + 42470dc commit 1d9e99b
Show file tree
Hide file tree
Showing 28 changed files with 709 additions and 42 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ Cargo.lock
*.orig
coeffs_*
msm_bases_*
*.py
2 changes: 1 addition & 1 deletion algebra/src/curves/bls12_377/g1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::{
biginteger::{BigInteger256, BigInteger384},
curves::models::{ModelParameters, SWModelParameters},
fields::{
bls12_377::{Fq, Fr},
bls12_377::*,
Field,
},
};
Expand Down
2 changes: 1 addition & 1 deletion algebra/src/curves/bls12_377/g2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::{
biginteger::{BigInteger256, BigInteger384},
curves::models::{ModelParameters, SWModelParameters},
fields::{
bls12_377::{Fq, Fq2, Fr},
bls12_377::*,
Field,
},
};
Expand Down
2 changes: 1 addition & 1 deletion algebra/src/curves/bls12_381/g1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::{
models::{ModelParameters, SWModelParameters},
},
fields::{
bls12_381::{Fq, Fr},
bls12_381::*,
Field,
},
};
Expand Down
2 changes: 1 addition & 1 deletion algebra/src/curves/bls12_381/g2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::{
models::{ModelParameters, SWModelParameters},
},
fields::{
bls12_381::{Fq, Fq2, Fr},
bls12_381::*,
Field,
},
};
Expand Down
72 changes: 68 additions & 4 deletions algebra/src/curves/check_curve_parameters.sage
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
# The following Sage script check the consistency of the following curves parameters:
#
# 1) P=(GENERATOR_X,GENERATOR_Y) must belongs to the curve of equation E: y^2 = x^3 + Ax + B
# 1) P=(GENERATOR_X,GENERATOR_Y) must belongs to the curve of equation E: y^2 = x^3 + Ax + B
# 2) P must have order equal to the MODULUS of the scalar field
# 3) COFACTOR must be equal to Order(E)/Order(P)
# 4) COFACTOR_INV must be the inverse of COFACTOR in the scalar Field
# 5) ENDO_COEFF must be a cube root in the base field.
# 6) ENDO_SCALAR must be a cube root in the scalar field and satisfy ENDO_SCALAR * (X, Y) == (ENDO_COEFF * X, Y)
# 7) The intersection of the plane lattice spanned by {(1, ENDO_SCALAR), (0, SCALAR_FIELD_MODULUS)} with the square [-A,A]^2 must be empty,
# where A = 2^(LAMBDA/2 + 1) + 2^(LAMBDA/2) + 1.
# Open Sage Shell in the corresponding folder and run the command
# "sage check_curve_paramaters sage [file_path_curve] [file_path_basefield] [file_path_scalarfield]".

Expand Down Expand Up @@ -65,10 +69,10 @@ scalar_field_name = re.findall(pattern, readfile)[0]
fn = "(?:" + base_field_name + "|" + scalar_field_name + ")" #fn = field name = "(:?Fr|Fq)". Useful declaration for the pattern

#### Reading the big integers list and extracting names and values
pattern = "const\s+(\w+):\s*" + fn + "\s*=\s*field_new!\(\s*" + fn + "\s*,\s*BigInteger\d*\s*\(\s*\[" + "([0-9a-fA-Fxu\s,]+)\s*" + "\]\s*\)"
pattern = "const\s+(\w+)[:\w\s]*=\s*field_new!\([\s\w,]*\(\s*\[" + "([0-9a-fA-Fxu\s,]+)\s*" + "\]\s*\)"
big_int_ls = re.findall(pattern,readfile) #####list of couples of the form ('[VARIABLE_NAME]',"[u64],..,[u64]")

big_int_names = [b[0] for b in big_int_ls]
big_int_names = [b[0] for b in big_int_ls]
big_int_values = [BigInteger_to_number(b[1]) for b in big_int_ls]

BigIntegerLen = BigInteger_len(big_int_ls[0][1])
Expand All @@ -87,6 +91,10 @@ for s in big_int_names:
pattern = "const\s+COFACTOR:\s*&'static\s*\[u64\]\s*=\s*&\[([0-9a-fA-Fxu\s,]+)\]\s*;"
COFACTOR = BigInteger_to_number(re.findall(pattern,readfile)[0])

####Reading the value of LAMBDA
pattern = "const\s+LAMBDA[:\w\s]*=\s*([\d]+)\s*;"
LAMBDA = int(re.findall(pattern,readfile)[0])

#######################################Reading the values from the file containing the Base Field parameters########################
filename = sys.argv[2]

Expand Down Expand Up @@ -163,4 +171,60 @@ else:
if Fr(COFACTOR) * Fr(COFACTOR_INV) == Fr(SCALAR_FIELD_R):
print("Correct. COFACTOR_INV is the inverse of COFACTOR in the the scalar field.")
else:
print("WARNING! COFACTOR_INV IS NOT THE INVERSE OF COFACTOR IN THE SCALAR FIELD!")
print("WARNING! COFACTOR_INV IS NOT THE INVERSE OF COFACTOR IN THE SCALAR FIELD!")
####### Checking the correctness of ENDO_COEFF and ENDO_FACTOR ############
endo_mul_is_used = False
if 'ENDO_COEFF' in locals() and 'ENDO_SCALAR' in locals():
zeta_q = Fq(ENDO_COEFF) * Fq(BASE_FIELD_R)**(-1)
if zeta_q**2 + zeta_q == Fq(-1):
endo_mul_is_used = True
print("Correct. ENDO_COEFF is a primitive cube root of unity.")
else:
print("WARNING! ENDO_COEFF IS NOT A PRIMITIVE CUBE ROOT OF UNITY.")
zeta_r = Fr(ENDO_SCALAR) * Fr(SCALAR_FIELD_R)**(-1)
if zeta_r**2 + zeta_r == Fr(-1):
print("Correct. ENDO_SCALAR is a primitive cube root of unity.")
else:
print("WARNING! ENDO_SCALAR IS NOT A PRIMITIVE CUBE ROOT OF UNITY.")


####### Checking the consistency of ENDO_COEFF and ENDO_SCALAR #############
if endo_mul_is_used:
Q = int(zeta_r) * P
if Q == E([zeta_q * X, Y]):
print("Correct. ENDO_COEFF and ENDO_SCALAR are consistent.")
else:
print("WARNING! ENDO_COEFF AND ENDO_SCALAR ARE NOT CONSISTENT!")


########## Checking that shortest vector in the lattice ([1,zeta_r),[0,r]) is long enough #########
## The Halo paper (https://eprint.iacr.org/2019/1021.pdf) proves the injectivity of the endo_mul map.
## The injectivity of the map (a,b) |-> a\zeta + b for a,b in [0,A] (essential for using add_unsafe)
## is equivalent the lattice condition below.
## a*zeta + b = a'*zeta_r + b' mod r for a,a',b,b' in [0,A]
## is equivalent to the fact that there are non-zero solutions to
## a * zeta_r = b mod r for a,b in [-A,A].
## Then it would exists c such that
## b = a * zeta_r + c * r.
## Any such solution correspond to a point of the lattice spanned by (1, zeta_r) and (0, r).
## (a, b) = (a, c) * (1 zeta_r)
## (0 r )
## The injectivity is equivalent to the fact that the intersection between this lattice and [-A, A]^2
## is trivial. To verify this we first compute a LLL reduced basis {v,w} and
## then check if at least one of v, w, v + w, v - w is belongs to such a square.
## If not, there can't be other lattice points in the square.
if endo_mul_is_used:
A = 2**(LAMBDA//2 + 1) + 2**(LAMBDA//2) + 1
L = Matrix([[1,Integer(zeta_r)],[0,SCALAR_FIELD_MODULUS]])
Lred = L.LLL()
set = [Lred.row(0), Lred.row(1), Lred.row(0) - Lred.row(1), Lred.row(0) + Lred.row(1)]
add_unsafe = True
for v in set:
if abs(v[0]) <= A and abs(v[1]) <= A:
add_unsafe = False
if add_unsafe:
print("We can use add_unsafe for endo_mul.")
else:
print("WARNING! WE CAN'T USE add_unsafe FOR endo_mul!")
else:
print("endo_mul is not used for this curve.")
2 changes: 1 addition & 1 deletion algebra/src/curves/mnt6/g1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::{
short_weierstrass_projective::{GroupAffine, GroupProjective},
AffineCurve,
},
fields::mnt6::{Fq, Fq3, Fr},
fields::mnt6::*,
};
use crate::{field_new, FromBytes};
use serde::{Deserialize, Serialize};
Expand Down
2 changes: 1 addition & 1 deletion algebra/src/curves/mnt6/g2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::{
short_weierstrass_projective::{GroupAffine, GroupProjective},
AffineCurve,
},
fields::mnt6::{Fq, Fq3, Fr},
fields::mnt6::*,
};
use crate::{field_new, FromBytes};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
Expand Down
15 changes: 15 additions & 0 deletions algebra/src/curves/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,21 @@ pub trait AffineCurve:
fn mul_by_cofactor_inv(&self) -> Self;
}

/// The `EndoMulCurve` trait for curves that have a non-trivial endomorphism
/// `Phi` of the form `Phi(x,y) = (zeta*x,y)`.
pub trait EndoMulCurve: AffineCurve {
/// Apply `Phi`
fn apply_endomorphism(&self) -> Self;

/// Conversion of a bit sequence used in `endo_mul()` into its equivalent
/// scalar
fn endo_rep_to_scalar(bits: Vec<bool>) -> Result<Self::ScalarField, Error>;

/// Endomorphism-based multiplication of `&self` with `bits`, a little-endian
/// endomorphism representation.
fn endo_mul(&self, bits: Vec<bool>) -> Result<Self::Projective, Error>;
}

impl<C: ProjectiveCurve> Group for C {
type ScalarField = C::ScalarField;
#[must_use]
Expand Down
19 changes: 19 additions & 0 deletions algebra/src/curves/models/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,22 @@ pub trait MontgomeryModelParameters: ModelParameters {

type TEModelParameters: TEModelParameters<BaseField = Self::BaseField>;
}

pub trait EndoMulParameters: SWModelParameters {
/// Parameters for endomorphism-based scalar multiplication [Halo](https://eprint.iacr.org/2019/1021).
/// A non-trivial cubic root of unity `ENDO_COEFF` for a curve endomorphism of the form
/// (x, y) -> (ENDO_COEFF * x, y).
const ENDO_COEFF: Self::BaseField;

/// The scalar representation `zeta_r` of `ENDO_COEFF`.
/// NOTE : If one wants to use the endo mul circuit with `lambda` many bits,
/// then `zeta_r` MUST satisfy the minimal distance property
/// D = min { d(n*zeta_r, m*zeta_r) : n,m in [0, T] } >= R + 1,
/// where `T = 2^{lambda/2 + 1} + 2^{lambda/2} - 1` is the output
/// bound for the coefficients a, b of the equivalent scalar
/// representation `a*zeta_r + b`.
const ENDO_SCALAR: Self::ScalarField;

/// Maximum number of bits for which security of endo mul is proven. MUST be an even number.
const LAMBDA: usize;
}
98 changes: 97 additions & 1 deletion algebra/src/curves/models/short_weierstrass_jacobian.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use crate::{
bytes::{FromBytes, ToBytes},
curves::{models::SWModelParameters as Parameters, AffineCurve, ProjectiveCurve},
curves::{models::{
SWModelParameters as Parameters,
EndoMulParameters as EndoParameters,
}, AffineCurve, ProjectiveCurve, EndoMulCurve},
fields::{BitIterator, Field, PrimeField, SquareRootField},
BitSerializationError, CanonicalDeserialize, CanonicalDeserializeWithFlags, CanonicalSerialize,
CanonicalSerializeWithFlags, Error, FromBytesChecked, FromCompressedBits, SWFlags,
Expand Down Expand Up @@ -295,6 +298,99 @@ impl<P: Parameters> AffineCurve for GroupAffine<P> {
}
}

impl<P: EndoParameters> EndoMulCurve for GroupAffine<P> {

fn apply_endomorphism(&self) -> Self {
let mut self_e = self.clone();
self_e.x.mul_assign(P::ENDO_COEFF);
self_e
}

fn endo_rep_to_scalar(bits: Vec<bool>) -> Result<Self::ScalarField, Error> {

let mut a : P::ScalarField = 2u64.into();
let mut b : P::ScalarField = 2u64.into();

let one = P::ScalarField::one();
let one_neg = one.neg();

let mut bits = bits;
if bits.len() % 2 == 1 {
bits.push(false);
}

if bits.len() > P::LAMBDA {
Err("Endo mul bits length exceeds LAMBDA")?
}

for i in (0..(bits.len() / 2)).rev() {
a.double_in_place();
b.double_in_place();

let s =
if bits[i * 2] {
&one
} else {
&one_neg
};

if bits[i * 2 + 1] {
a.add_assign(s);
} else {
b.add_assign(s);
}
}

Ok(a.mul(P::ENDO_SCALAR) + &b)
}

/// Endomorphism-based multiplication of a curve point
/// with a scalar in little-endian endomorphism representation.
fn endo_mul(&self, bits: Vec<bool>) -> Result<Self::Projective, Error> {

let self_neg = self.neg();

let self_e = self.apply_endomorphism();
let self_e_neg = self_e.neg();

let mut acc = self_e.into_projective();
acc.add_assign_mixed(&self);
acc.double_in_place();

let mut bits = bits;
if bits.len() % 2 == 1 {
bits.push(false);
}

if bits.len() > P::LAMBDA {
Err("Endo mul bits length exceeds LAMBDA")?
}

for i in (0..(bits.len() / 2)).rev() {

let s =
if bits[i * 2 + 1] {
if bits[i * 2] {
&self_e
} else {
&self_e_neg
}
} else {
if bits[i * 2] {
&self
} else {
&self_neg
}
};

acc.double_in_place();
acc.add_assign_mixed(s);
}

Ok(acc)
}
}

impl<P: Parameters> SemanticallyValid for GroupAffine<P> {
fn is_valid(&self) -> bool {
self.x.is_valid() && self.y.is_valid() && self.group_membership_test()
Expand Down
Loading

0 comments on commit 1d9e99b

Please sign in to comment.