Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add fast subgroup check for is_torsion_free #83

Merged
merged 7 commits into from
May 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 42 additions & 8 deletions src/g1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -399,15 +399,14 @@ impl G1Affine {
/// exists within the $q$-order subgroup $\mathbb{G}_1$. This should always return true
/// unless an "unchecked" API was used.
pub fn is_torsion_free(&self) -> Choice {
const FQ_MODULUS_BYTES: [u8; 32] = [
1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8,
216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115,
];
// Algorithm from Section 6 of https://eprint.iacr.org/2021/1130
// Updated proof of correctness in https://eprint.iacr.org/2022/352
//
// Check that endomorphism_p(P) == -[x^2] P

// Clear the r-torsion from the point and check if it is the identity
G1Projective::from(*self)
.multiply(&FQ_MODULUS_BYTES)
.is_identity()
let minus_x_squared_times_p = G1Projective::from(self).mul_by_x().mul_by_x().neg();
let endomorphism_p = endomorphism(self);
minus_x_squared_times_p.ct_eq(&G1Projective::from(endomorphism_p))
}

/// Returns true if this point is on the curve. This should always return
Expand All @@ -418,6 +417,25 @@ impl G1Affine {
}
}

/// A nontrivial third root of unity in Fp
pub const BETA: Fp = Fp::from_raw_unchecked([
0x30f1_361b_798a_64e8,
0xf3b8_ddab_7ece_5a2a,
0x16a8_ca3a_c615_77f7,
0xc26a_2ff8_74fd_029b,
0x3636_b766_6070_1c6e,
0x051b_a4ab_241b_6160,
]);
Comment on lines +421 to +428
Copy link
Member

Choose a reason for hiding this comment

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

I confirmed that this matches the output of the Sage script.


fn endomorphism(p: &G1Affine) -> G1Affine {
// 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
}

/// This is an element of $\mathbb{G}_1$ represented in the projective coordinate space.
#[cfg_attr(docsrs, doc(cfg(feature = "groups")))]
#[derive(Copy, Clone, Debug)]
Expand Down Expand Up @@ -1054,6 +1072,22 @@ impl UncompressedEncoding for G1Affine {
}
}

#[test]
fn test_beta() {
assert_eq!(
BETA,
Fp::from_bytes(&[
0x00u8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x19, 0x67, 0x2f, 0xdf, 0x76,
0xce, 0x51, 0xba, 0x69, 0xc6, 0x07, 0x6a, 0x0f, 0x77, 0xea, 0xdd, 0xb3, 0xa9, 0x3b,
0xe6, 0xf8, 0x96, 0x88, 0xde, 0x17, 0xd8, 0x13, 0x62, 0x0a, 0x00, 0x02, 0x2e, 0x01,
0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe
])
.unwrap()
);
assert_ne!(BETA, Fp::one());
This conversation was marked as resolved.
Show resolved Hide resolved
assert_ne!(BETA * BETA, Fp::one());
assert_eq!(BETA * BETA * BETA, Fp::one());
}
#[test]
fn test_is_on_curve() {
assert!(bool::from(G1Affine::identity().is_on_curve()));
Expand Down
15 changes: 6 additions & 9 deletions src/g2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -473,15 +473,12 @@ impl G2Affine {
/// exists within the $q$-order subgroup $\mathbb{G}_2$. This should always return true
/// unless an "unchecked" API was used.
pub fn is_torsion_free(&self) -> Choice {
const FQ_MODULUS_BYTES: [u8; 32] = [
1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8,
216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115,
];

// Clear the r-torsion from the point and check if it is the identity
G2Projective::from(*self)
.multiply(&FQ_MODULUS_BYTES)
.is_identity()
// Algorithm from Section 4 of https://eprint.iacr.org/2021/1130
// Updated proof of correctness in https://eprint.iacr.org/2022/352
//
// Check that psi(P) == [x] P
let p = G2Projective::from(self);
ebfull marked this conversation as resolved.
Show resolved Hide resolved
p.psi().ct_eq(&p.mul_by_x())
}

/// Returns true if this point is on the curve. This should always return
Expand Down
46 changes: 46 additions & 0 deletions src/notes/design.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,49 @@
//! print("g2 generator: {}".format(p))
//! break
//! ```
//!
//! ## Nontrivial third root of unity
//!
//! To use the fast subgroup check algorithm for $\mathbb{G_1}$ from https://eprint.iacr.org/2019/814.pdf and
//! https://eprint.iacr.org/2021/1130, it is necessary to find a nontrivial cube root of
//! unity β in Fp to define the endomorphism:
//! $$(x, y) \rightarrow (\beta x, y)$$
//! which is equivalent to
//! $$P \rightarrow \lambda P$$
//! where $\lambda$, a nontrivial cube root of unity in Fr, satisfies $\lambda^2 + \lambda +1 = 0 \pmod{r}.
//!
//! $$\beta = 793479390729215512621379701633421447060886740281060493010456487427281649075476305620758731620350$$
//! can be derived using the following sage commands after running the above sage script:
//!
//! ```text
//! # Prints the given field element in Montgomery form.
//! def print_fq(a):
//! R = 1 << 384
//! tmp = ZZ(Fq(a*R))
//! while tmp > 0:
//! print("0x{:_x}, ".format(tmp % (1 << 64)))
//! tmp >>= 64
//! β = (Fq.multiplicative_generator() ** ((q-1)/3))
//! print_fq(β)
//! ```
//!
//! ## Psi
//!
//! To use the fast subgroup check algorithm for $\mathbb{G_2}$ from https://eprint.iacr.org/2019/814.pdf and
//! https://eprint.iacr.org/2021/1130, it is necessary to find the endomorphism:
//!
//! $$(x, y, z) \rightarrow (x^q \psi_x, y^q \psi_y, z^q)$$
//!
//! where:
//!
//! 1. $\psi_x = 1 / ((i+1) ^ ((q-1)/3)) \in \mathbb{F}_{q^2}$, and
//! 2. $\psi_y = 1 / ((i+1) ^ ((q-1)/2)) \in \mathbb{F}_{q^2}$
//!
//! can be derived using the following sage commands after running the above script and commands:
//! ```text
//! psi_x = (1/((i+1)**((q-1)/3)))
//! psi_y = (1/((i+1)**((q-1)/2)))
//! print_fq(psi_x.polynomial().coefficients()[0])
//! print_fq(psi_y.polynomial().coefficients()[0])
//! print_fq(psi_y.polynomial().coefficients()[1])
This conversation was marked as resolved.
Show resolved Hide resolved
//! ```