mirror of
https://github.com/arnaucube/ark-curves-cherry-picked.git
synced 2026-01-07 14:31:34 +01:00
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>
This commit is contained in:
@@ -8,6 +8,8 @@
|
||||
|
||||
### 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
|
||||
|
||||
## v0.3.0
|
||||
|
||||
@@ -56,3 +56,9 @@ lto = "thin"
|
||||
incremental = true
|
||||
debug-assertions = 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" }
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
use crate::*;
|
||||
use ark_ec::{
|
||||
bls12,
|
||||
bls12::Bls12Parameters,
|
||||
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 G1Projective = bls12::G1Projective<crate::Parameters>;
|
||||
@@ -40,6 +44,25 @@ impl SWModelParameters for Parameters {
|
||||
fn mul_by_a(_: &Self::BaseField) -> Self::BaseField {
|
||||
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 =
|
||||
@@ -51,3 +74,14 @@ pub const G1_GENERATOR_X: Fq = field_new!(Fq, "368541675371338701678108831518307
|
||||
/// 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569
|
||||
#[rustfmt::skip]
|
||||
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
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
use crate::*;
|
||||
use ark_ec::bls12::Bls12Parameters;
|
||||
use ark_ec::{
|
||||
bls12,
|
||||
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 G2Projective = bls12::G2Projective<crate::Parameters>;
|
||||
@@ -51,6 +54,21 @@ impl SWModelParameters for Parameters {
|
||||
fn mul_by_a(_: &Self::BaseField) -> Self::BaseField {
|
||||
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);
|
||||
@@ -75,3 +93,52 @@ pub const G2_GENERATOR_Y_C0: Fq = field_new!(Fq, "198515060228729193556805452117
|
||||
/// 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582
|
||||
#[rustfmt::skip]
|
||||
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
|
||||
}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
#![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::{
|
||||
fields::{Field, FpParameters, PrimeField, SquareRootField},
|
||||
One, Zero,
|
||||
BitIteratorBE, One, UniformRand, Zero,
|
||||
};
|
||||
use ark_serialize::CanonicalSerialize;
|
||||
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 ark_algebra_test_templates::{curves::*, groups::*};
|
||||
use ark_ec::group::Group;
|
||||
|
||||
#[test]
|
||||
fn test_g1_projective_curve() {
|
||||
@@ -115,3 +120,54 @@ fn test_g1_generator_raw() {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
|
||||
const SAMPLES: usize = 131072;
|
||||
@@ -224,6 +250,7 @@ macro_rules! ec_bench {
|
||||
deser,
|
||||
ser_unchecked,
|
||||
deser_unchecked,
|
||||
deser_uncompressed,
|
||||
msm_131072,
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user