Browse Source

Use Scott's subgroup membership tests for `G1` and `G2` of BLS12-381. (#74)

* implementation of the fast subgroup check for bls12_381

* add a bench

* subgroup check for g1

* subgroup check modifications

* remove useless test

* fmt

* need the last version of arkworks/algebra

* remove Parameters0

* using projective points is more efficient

* use of projective coordinates in G2

* fmt

* documentation on the constants and the psi function

* references for algorithms of eprint 2021/1130

* fmt

* sed ^ **

* minor improvement

* fmt

* fix Cargo toml

* nits

* some cleanup for g1

* add the beta test back

* fmt

* g2

* changelog

* add a  note on the Cargo.toml

* nits

* avoid variable name conflicts

* add the early-out optimization

Co-authored-by: weikeng <w.k@berkeley.edu>
sjoseph/big_integer_documentation
Simon Masson 3 years ago
committed by GitHub
parent
commit
2118e14b6a
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 196 additions and 4 deletions
  1. +2
    -0
      CHANGELOG.md
  2. +6
    -0
      Cargo.toml
  3. +35
    -1
      bls12_381/src/curves/g1.rs
  4. +68
    -1
      bls12_381/src/curves/g2.rs
  5. +58
    -2
      bls12_381/src/curves/tests.rs
  6. +27
    -0
      curve-benches/src/macros/ec.rs

+ 2
- 0
CHANGELOG.md

@ -8,6 +8,8 @@
### Improvements ### Improvements
- [\#74](https://github.com/arkworks-rs/curves/pull/74) Use Scott's subgroup membership tests for `G1` and `G2` of BLS12-381.
### Bug fixes ### Bug fixes
## v0.3.0 ## v0.3.0

+ 6
- 0
Cargo.toml

@ -56,3 +56,9 @@ lto = "thin"
incremental = true incremental = true
debug-assertions = true debug-assertions = true
debug = true debug = true
# To be removed in the new release.
[patch.crates-io]
ark-ec = { git = "https://github.com/arkworks-rs/algebra" }
ark-ff = { git = "https://github.com/arkworks-rs/algebra" }
ark-serialize = { git = "https://github.com/arkworks-rs/algebra" }

+ 35
- 1
bls12_381/src/curves/g1.rs

@ -1,9 +1,13 @@
use crate::*; use crate::*;
use ark_ec::{ use ark_ec::{
bls12, bls12,
bls12::Bls12Parameters,
models::{ModelParameters, SWModelParameters}, models::{ModelParameters, SWModelParameters},
short_weierstrass_jacobian::GroupAffine,
AffineCurve, ProjectiveCurve,
}; };
use ark_ff::{field_new, Zero};
use ark_ff::{biginteger::BigInteger256, field_new, Zero};
use ark_std::ops::Neg;
pub type G1Affine = bls12::G1Affine<crate::Parameters>; pub type G1Affine = bls12::G1Affine<crate::Parameters>;
pub type G1Projective = bls12::G1Projective<crate::Parameters>; pub type G1Projective = bls12::G1Projective<crate::Parameters>;
@ -40,6 +44,25 @@ impl SWModelParameters for Parameters {
fn mul_by_a(_: &Self::BaseField) -> Self::BaseField { fn mul_by_a(_: &Self::BaseField) -> Self::BaseField {
Self::BaseField::zero() Self::BaseField::zero()
} }
fn is_in_correct_subgroup_assuming_on_curve(p: &GroupAffine<Parameters>) -> bool {
// Algorithm from Section 6 of https://eprint.iacr.org/2021/1130.
//
// Check that endomorphism_p(P) == -[X^2]P
let x = BigInteger256::new([crate::Parameters::X[0], 0, 0, 0]);
// An early-out optimization described in Section 6.
// If uP == P but P != point of infinity, then the point is not in the right subgroup.
let x_times_p = p.mul(x);
if x_times_p.eq(p) && !p.infinity {
return false;
}
let minus_x_squared_times_p = x_times_p.mul(x).neg();
let endomorphism_p = endomorphism(p);
minus_x_squared_times_p.eq(&endomorphism_p)
}
} }
/// G1_GENERATOR_X = /// G1_GENERATOR_X =
@ -51,3 +74,14 @@ pub const G1_GENERATOR_X: Fq = field_new!(Fq, "368541675371338701678108831518307
/// 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569 /// 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569
#[rustfmt::skip] #[rustfmt::skip]
pub const G1_GENERATOR_Y: Fq = field_new!(Fq, "1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569"); pub const G1_GENERATOR_Y: Fq = field_new!(Fq, "1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569");
/// BETA is a non-trivial cubic root of unity in Fq.
pub const BETA: Fq = field_new!(Fq, "793479390729215512621379701633421447060886740281060493010456487427281649075476305620758731620350");
pub fn endomorphism(p: &GroupAffine<Parameters>) -> GroupAffine<Parameters> {
// Endomorphism of the points on the curve.
// endomorphism_p(x,y) = (BETA * x, y) where BETA is a non-trivial cubic root of unity in Fq.
let mut res = (*p).clone();
res.x *= BETA;
res
}

+ 68
- 1
bls12_381/src/curves/g2.rs

@ -1,9 +1,12 @@
use crate::*; use crate::*;
use ark_ec::bls12::Bls12Parameters;
use ark_ec::{ use ark_ec::{
bls12, bls12,
models::{ModelParameters, SWModelParameters}, models::{ModelParameters, SWModelParameters},
short_weierstrass_jacobian::GroupAffine,
AffineCurve,
}; };
use ark_ff::{field_new, Zero};
use ark_ff::{biginteger::BigInteger256, field_new, Field, Zero};
pub type G2Affine = bls12::G2Affine<crate::Parameters>; pub type G2Affine = bls12::G2Affine<crate::Parameters>;
pub type G2Projective = bls12::G2Projective<crate::Parameters>; pub type G2Projective = bls12::G2Projective<crate::Parameters>;
@ -51,6 +54,21 @@ impl SWModelParameters for Parameters {
fn mul_by_a(_: &Self::BaseField) -> Self::BaseField { fn mul_by_a(_: &Self::BaseField) -> Self::BaseField {
Self::BaseField::zero() Self::BaseField::zero()
} }
fn is_in_correct_subgroup_assuming_on_curve(point: &GroupAffine<Parameters>) -> bool {
// Algorithm from Section 4 of https://eprint.iacr.org/2021/1130.
//
// Checks that [p]P = [X]P
let mut x_times_point = point.mul(BigInteger256([crate::Parameters::X[0], 0, 0, 0]));
if crate::Parameters::X_IS_NEGATIVE {
x_times_point = -x_times_point;
}
let p_times_point = p_power_endomorphism(point);
x_times_point.eq(&p_times_point)
}
} }
pub const G2_GENERATOR_X: Fq2 = field_new!(Fq2, G2_GENERATOR_X_C0, G2_GENERATOR_X_C1); pub const G2_GENERATOR_X: Fq2 = field_new!(Fq2, G2_GENERATOR_X_C0, G2_GENERATOR_X_C1);
@ -75,3 +93,52 @@ pub const G2_GENERATOR_Y_C0: Fq = field_new!(Fq, "198515060228729193556805452117
/// 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582 /// 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582
#[rustfmt::skip] #[rustfmt::skip]
pub const G2_GENERATOR_Y_C1: Fq = field_new!(Fq, "927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582"); pub const G2_GENERATOR_Y_C1: Fq = field_new!(Fq, "927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582");
// psi(x,y) = (x**p * PSI_X, y**p * PSI_Y) is the Frobenius composed
// with the quadratic twist and its inverse
// PSI_X = 1/(u+1)^((p-1)/3)
pub const P_POWER_ENDOMORPHISM_COEFF_0 : Fq2 = field_new!(
Fq2,
FQ_ZERO,
field_new!(
Fq,
"4002409555221667392624310435006688643935503118305586438271171395842971157480381377015405980053539358417135540939437"
)
);
// PSI_Y = 1/(u+1)^((p-1)/2)
pub const P_POWER_ENDOMORPHISM_COEFF_1: Fq2 = field_new!(
Fq2,
field_new!(
Fq,
"2973677408986561043442465346520108879172042883009249989176415018091420807192182638567116318576472649347015917690530"),
field_new!(
Fq,
"1028732146235106349975324479215795277384839936929757896155643118032610843298655225875571310552543014690878354869257")
);
pub fn p_power_endomorphism(p: &GroupAffine<Parameters>) -> GroupAffine<Parameters> {
// The p-power endomorphism for G2 is defined as follows:
// 1. Note that G2 is defined on curve E': y^2 = x^3 + 4(u+1). To map a point (x, y) in E' to (s, t) in E,
// one set s = x / ((u+1) ^ (1/3)), t = y / ((u+1) ^ (1/2)), because E: y^2 = x^3 + 4.
// 2. Apply the Frobenius endomorphism (s, t) => (s', t'), another point on curve E,
// where s' = s^p, t' = t^p.
// 3. Map the point from E back to E'; that is,
// one set x' = s' * ((u+1) ^ (1/3)), y' = t' * ((u+1) ^ (1/2)).
//
// To sum up, it maps
// (x,y) -> (x^p / ((u+1)^((p-1)/3)), y^p / ((u+1)^((p-1)/2)))
// as implemented in the code as follows.
let mut res = *p;
res.x.frobenius_map(1);
res.y.frobenius_map(1);
let tmp_x = res.x.clone();
res.x.c0 = -P_POWER_ENDOMORPHISM_COEFF_0.c1 * &tmp_x.c1;
res.x.c1 = P_POWER_ENDOMORPHISM_COEFF_0.c1 * &tmp_x.c0;
res.y *= P_POWER_ENDOMORPHISM_COEFF_1;
res
}

+ 58
- 2
bls12_381/src/curves/tests.rs

@ -1,8 +1,12 @@
#![allow(unused_imports)] #![allow(unused_imports)]
use ark_ec::{models::SWModelParameters, AffineCurve, PairingEngine, ProjectiveCurve};
use ark_ec::{
models::SWModelParameters,
short_weierstrass_jacobian::{GroupAffine, GroupProjective},
AffineCurve, PairingEngine, ProjectiveCurve,
};
use ark_ff::{ use ark_ff::{
fields::{Field, FpParameters, PrimeField, SquareRootField}, fields::{Field, FpParameters, PrimeField, SquareRootField},
One, Zero,
BitIteratorBE, One, UniformRand, Zero,
}; };
use ark_serialize::CanonicalSerialize; use ark_serialize::CanonicalSerialize;
use ark_std::rand::Rng; use ark_std::rand::Rng;
@ -11,6 +15,7 @@ use core::ops::{AddAssign, MulAssign};
use crate::{g1, g2, Bls12_381, Fq, Fq12, Fq2, Fr, G1Affine, G1Projective, G2Affine, G2Projective}; use crate::{g1, g2, Bls12_381, Fq, Fq12, Fq2, Fr, G1Affine, G1Projective, G2Affine, G2Projective};
use ark_algebra_test_templates::{curves::*, groups::*}; use ark_algebra_test_templates::{curves::*, groups::*};
use ark_ec::group::Group;
#[test] #[test]
fn test_g1_projective_curve() { fn test_g1_projective_curve() {
@ -115,3 +120,54 @@ fn test_g1_generator_raw() {
x.add_assign(&Fq::one()); x.add_assign(&Fq::one());
} }
} }
#[test]
fn test_g1_endomorphism_beta() {
assert!(g1::BETA.pow(&[3u64]).is_one());
}
#[test]
fn test_g1_subgroup_membership_via_endomorphism() {
let mut rng = test_rng();
let generator = G1Projective::rand(&mut rng).into_affine();
assert!(generator.is_in_correct_subgroup_assuming_on_curve());
}
#[test]
fn test_g1_subgroup_non_membership_via_endomorphism() {
let mut rng = test_rng();
loop {
let x = Fq::rand(&mut rng);
let greatest = rng.gen();
if let Some(p) = G1Affine::get_point_from_x(x, greatest) {
if !p.into_projective().mul(Fr::characteristic()).is_zero() {
assert!(!p.is_in_correct_subgroup_assuming_on_curve());
return;
}
}
}
}
#[test]
fn test_g2_subgroup_membership_via_endomorphism() {
let mut rng = test_rng();
let generator = G2Projective::rand(&mut rng).into_affine();
assert!(generator.is_in_correct_subgroup_assuming_on_curve());
}
#[test]
fn test_g2_subgroup_non_membership_via_endomorphism() {
let mut rng = test_rng();
loop {
let x = Fq2::rand(&mut rng);
let greatest = rng.gen();
if let Some(p) = G2Affine::get_point_from_x(x, greatest) {
if !p.into_projective().mul(Fr::characteristic()).is_zero() {
assert!(!p.is_in_correct_subgroup_assuming_on_curve());
return;
}
}
}
}

+ 27
- 0
curve-benches/src/macros/ec.rs

@ -196,6 +196,32 @@ macro_rules! ec_bench {
}); });
} }
fn deser_uncompressed(b: &mut $crate::bencher::Bencher) {
use ark_ec::ProjectiveCurve;
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
const SAMPLES: usize = 1000;
let mut rng = ark_std::test_rng();
let mut num_bytes = 0;
let tmp = <$projective>::rand(&mut rng).into_affine();
let v: Vec<_> = (0..SAMPLES)
.flat_map(|_| {
let mut bytes = Vec::with_capacity(1000);
tmp.serialize_uncompressed(&mut bytes).unwrap();
num_bytes = bytes.len();
bytes
})
.collect();
let mut count = 0;
b.iter(|| {
count = (count + 1) % SAMPLES;
let index = count * num_bytes;
<$affine>::deserialize_uncompressed(&v[index..(index + num_bytes)]).unwrap()
});
}
fn msm_131072(b: &mut $crate::bencher::Bencher) { fn msm_131072(b: &mut $crate::bencher::Bencher) {
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
const SAMPLES: usize = 131072; const SAMPLES: usize = 131072;
@ -224,6 +250,7 @@ macro_rules! ec_bench {
deser, deser,
ser_unchecked, ser_unchecked,
deser_unchecked, deser_unchecked,
deser_uncompressed,
msm_131072, msm_131072,
); );
}; };

Loading…
Cancel
Save