Skip to content
This repository has been archived by the owner on Dec 18, 2023. It is now read-only.

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

Merged
merged 28 commits into from
Sep 25, 2021
Merged

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

merged 28 commits into from
Sep 25, 2021

Conversation

simonmasson
Copy link
Contributor

@simonmasson simonmasson commented Sep 8, 2021

Description

Implementation of the fast subgroup check for G1 and G2 using the Scott algorithm from eprint 2021/1130.

The Scott's implementation (eprint 2021/1130) is slightly faster than Bowe's implementation:

Author G1 subgroup check G2 subgroup check
Bowe 50.435ns 68.635ns
Scott 42.917ns 60.793ns

closes: #XXXX


Before we can merge this PR, please make sure that all the following items have been
checked off. If any of the checklist items are not applicable, please leave them but
write a little note why.

  • Targeted PR against correct branch (master)
  • Linked to Github issue with discussion and accepted design OR have an explanation in the PR that describes this work.
  • Wrote unit tests
  • Updated relevant documentation in the code
  • Added a relevant changelog entry to the Pending section in CHANGELOG.md
  • Re-reviewed Files changed in the Github PR explorer

@simonmasson simonmasson mentioned this pull request Sep 8, 2021
6 tasks

pub struct Parameters;
pub struct Parameters0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm why this rename?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because Parameters is also used for the SWModelParameters struct... See below in this file

type G1Parameters = self::g1::Parameters

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe instead of importing crate::*, we can use crate::Parameters wherever necessary? That way we don't have to rename anything.

@@ -51,3 +80,11 @@ pub const G1_GENERATOR_X: Fq = field_new!(Fq, "368541675371338701678108831518307
/// 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569
#[rustfmt::skip]
pub const G1_GENERATOR_Y: Fq = field_new!(Fq, "1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569");

pub const BETA: Fq = field_new!(Fq,"793479390729215512621379701633421447060886740281060493010456487427281649075476305620758731620350");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have a script to calculate this value? Or maybe a comment that can let us check it?


pub const BETA: Fq = field_new!(Fq,"793479390729215512621379701633421447060886740281060493010456487427281649075476305620758731620350");

pub fn sigma(p: &GroupAffine<Parameters>) -> GroupAffine<Parameters> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some documentation for this would be great =)

if Parameters0::X_IS_NEGATIVE {
x_times_p = -x_times_p;
}
if (-*p + x_times_p).is_zero() {
Copy link
Member

@Pratyush Pratyush Sep 8, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (-*p + x_times_p).is_zero() {
if (x_times_p - p).is_zero() {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not working, and neither x_times_p - *p. The only way I found is to write x_times_p + (-*p)... Any suggestion?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was because x_times_p was GroupAffine. The following code should work:

Suggested change
if (-*p + x_times_p).is_zero() {
if x_times_p.add_mixed(-*p).is_zero() {

fn is_in_correct_subgroup_assuming_on_curve(point: &GroupAffine<Parameters>) -> bool {
// check that [p]P = [X]P
let mut x_times_point: GroupAffine<_> =
point.mul(BigInteger([Parameters0::X[0], 0, 0, 0])).into();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm if you'd like, we can add a mul_limbs (or some other name) method similar to this one on GroupProjective: https://docs.rs/ark-ec/0.3.0/ark_ec/trait.ProjectiveCurve.html#method.mul. That way you can directly use point.mul_limbs(Parameters::X).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively, you can also just use the existing point.mul_bits(BitIteratorBE::without_leading_zeros(Parameters::X))

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the mul_bits function is private and so I was not able to use it...

} else {
let sigma_p = sigma(p);
let mut x_times_p: GroupAffine<_> =
p.mul(BigInteger([Parameters0::X[0], 0, 0, 0])).into();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
p.mul(BigInteger([Parameters0::X[0], 0, 0, 0])).into();
p.mul_bits(BitIteratorBE::without_leading_zeros(Parameters::X).into();

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the mul_bits function is private and so I was not able to use it...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be much cleaner like that, I agree :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm I'll make a PR to expose that API, but for the time being we can use the existing code. If you can leave a TODO then i'll refactor to use that later.

Comment on lines 54 to 55
let mut x_times_p: GroupAffine<_> =
p.mul(BigInteger([Parameters0::X[0], 0, 0, 0])).into();
Copy link
Member

@Pratyush Pratyush Sep 9, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's avoid the inversion here:

Suggested change
let mut x_times_p: GroupAffine<_> =
p.mul(BigInteger([Parameters0::X[0], 0, 0, 0])).into();
let mut x_times_p = p.mul(BigInteger([Parameters0::X[0], 0, 0, 0]));

Comment on lines 62 to 64
let mut x2_times_p: GroupAffine<_> = x_times_p
.mul(BigInteger([Parameters0::X[0], 0, 0, 0]))
.into();
Copy link
Member

@Pratyush Pratyush Sep 9, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let mut x2_times_p: GroupAffine<_> = x_times_p
.mul(BigInteger([Parameters0::X[0], 0, 0, 0]))
.into();
let mut x2_times_p = x_times_p.mul(BigInteger([Parameters0::X[0], 0, 0, 0]));

if Parameters0::X_IS_NEGATIVE {
x2_times_p = -x2_times_p;
}
(x2_times_p + sigma_p).is_zero()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
(x2_times_p + sigma_p).is_zero()
x2_times_p.add_mixed(sigma_p).is_zero()

Comment on lines 60 to 61
let mut x_times_point: GroupAffine<_> =
point.mul(BigInteger([Parameters0::X[0], 0, 0, 0])).into();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let mut x_times_point: GroupAffine<_> =
point.mul(BigInteger([Parameters0::X[0], 0, 0, 0])).into();
let mut x_times_point =
point.mul(BigInteger([Parameters0::X[0], 0, 0, 0]));

Comment on lines 93 to 111
// psi(x,y) = (x^p * PSI_X, y^p * PSI_Y) is the Frobenius composed
// with the quadratic twist and its inverse
pub const PSI_X:Fq2 = field_new!(
Fq2,
FQ_ZERO,
field_new!(
Fq,
"4002409555221667392624310435006688643935503118305586438271171395842971157480381377015405980053539358417135540939437"
)
);
pub const PSI_Y: Fq2 = field_new!(
Fq2,
field_new!(
Fq,
"2973677408986561043442465346520108879172042883009249989176415018091420807192182638567116318576472649347015917690530"),
field_new!(
Fq,
"1028732146235106349975324479215795277384839936929757896155643118032610843298655225875571310552543014690878354869257")
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some documentation on how these constants are derived would be great.

Fq,
"1028732146235106349975324479215795277384839936929757896155643118032610843298655225875571310552543014690878354869257")
);
pub fn psi(p: &GroupAffine<Parameters>) -> GroupAffine<Parameters> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto here.

Comment on lines 62 to 66
if Parameters0::X_IS_NEGATIVE {
x_times_point = -x_times_point;
}
let p_times_point = psi(point);
(-x_times_point + p_times_point).is_zero()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if Parameters0::X_IS_NEGATIVE {
x_times_point = -x_times_point;
}
let p_times_point = psi(point);
(-x_times_point + p_times_point).is_zero()
// The check is `p_times_point - x_times_point == 0`?
// If `x` is negative, then the LHS becomes `p_times_point + x_times_point`.
// If `x` is positive, then it remains `p_times_point - x_times_point`.
// So, we negate if `x` is positive, and add the result.
if !Parameters0::X_IS_NEGATIVE {
x_times_point = -x_times_point;
}
let p_times_point = psi(point);
x_times_point.add_mixed(p_times_point).is_zero()

@Pratyush
Copy link
Member

Pratyush commented Sep 9, 2021

Left another round of reviews, focusing on adding some more documentation (eg: links to descriptions of the algorithms in papers or blogposts etc) and on not doing unnecessary affine operations. Thanks for your patience @simonmasson!

@simonmasson
Copy link
Contributor Author

Thank you for all the comments/suggestions! I add some changes, let me know if something else is needed.

pub fn psi(p: &GroupAffine<Parameters>) -> GroupAffine<Parameters> {
// Psi is an endomorphism of the curve defined with the sextic
// twists and the frobenius endomorphism on the G1 curve
// (x,y) -> (x^p / (u+1)**((p-1)//3), y^p / (u+1)**((p-1)//2))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there an intended distinction between ^ and ** here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. I will change ^ into ** here :-)

@weikengchen
Copy link
Member

I will need to double check the paper.

Based on the paper, it seems that the following is not expected:

BETA^2 + BETA + 1 = 0

Rather, it is expected:

lambda^2 + lambda + 1 = 0

where for BLS-12, lambda = -u^2.

And, if beta is the cubic root of unity, then:

BETA^3 = 1

@weikengchen
Copy link
Member

weikengchen commented Sep 18, 2021

Let me correct my previous post.

For the beta we are finding,

BETA^3  = 1 (mod Fq)
BETA^2 + BETA + 1 = 0 (mod Fq)

Both are satisfied.

Because x^3 - 1 = (x - 1)(x^2 + x + 1).

@weikengchen
Copy link
Member

I added some documents and renamed some method names. Let me ping @Pratyush to do a final pass.

@weikengchen
Copy link
Member

There is some offline discussion with Simon. I will update this PR soon to reflect the discussion.

@weikengchen weikengchen changed the title Scott subgroup checks Add the Scott subgroup checks for BLS12-381 Sep 25, 2021
@weikengchen weikengchen changed the title Add the Scott subgroup checks for BLS12-381 Use Scott's subgroup membership tests for G1 and G2 of BLS12-381. Sep 25, 2021
@weikengchen weikengchen merged commit 2118e14 into arkworks-rs:master Sep 25, 2021
@yelhousni
Copy link

So it seems there was an error in Scott preprint for G2 membership test proof. However, the result is still correct (more on that here: https://eprint.iacr.org/2022/352.pdf).

@weikengchen
Copy link
Member

I think one todo is to add additional comment to reference to the new paper for the proof.

It is a relief that the original method still works. (not common)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants