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

Optimized subgroup checks #580

Closed
wants to merge 7 commits into from
Closed
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
2 changes: 1 addition & 1 deletion crypto/src/commitments/kzg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ impl<const N: usize, F: IsPrimeField<RepresentativeType = UnsignedInteger<N>>, P
&(alpha_g2.operate_with(&(g2.operate_with_self(x.representative())).neg())),
),
]);
e == FieldElement::one()
e == Ok(FieldElement::one())
}

fn open_batch(
Expand Down
Binary file added math/bin/act
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1,26 +1,17 @@
use super::field_extension::BLS12381PrimeField;
use super::curve::{BLS12381Curve, BLS12381FieldElement};
use crate::cyclic_group::IsGroup;
use crate::elliptic_curve::short_weierstrass::point::ShortWeierstrassProjectivePoint;
use crate::elliptic_curve::traits::FromAffine;
use crate::field::element::FieldElement;
use crate::unsigned_integer::element::U256;
use crate::{
elliptic_curve::short_weierstrass::curves::bls12_381::curve::BLS12381Curve,
errors::ByteConversionError, traits::ByteConversion,
};
use crate::{errors::ByteConversionError, traits::ByteConversion};
use std::cmp::Ordering;
use std::ops::Neg;

pub type G1Point = ShortWeierstrassProjectivePoint<BLS12381Curve>;
pub type BLS12381FieldElement = FieldElement<BLS12381PrimeField>;
#[allow(dead_code)] // This is done to not let clippy warn. Must be removed when MODULUS is used.
const MODULUS: U256 =
U256::from_hex_unchecked("73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001");

pub fn check_point_is_in_subgroup(point: &G1Point) -> bool {
let inf = G1Point::neutral_element();
let aux_point = point.operate_with_self(MODULUS);
inf == aux_point
}
pub type G1Point = ShortWeierstrassProjectivePoint<BLS12381Curve>;

pub fn decompress_g1_point(input_bytes: &mut [u8; 48]) -> Result<G1Point, ByteConversionError> {
let first_byte = input_bytes.first().unwrap();
Expand Down Expand Up @@ -66,7 +57,8 @@ pub fn decompress_g1_point(input_bytes: &mut [u8; 48]) -> Result<G1Point, ByteCo
let point =
G1Point::from_affine(x, y.clone()).map_err(|_| ByteConversionError::InvalidValue)?;

check_point_is_in_subgroup(&point)
point
.is_in_subgroup()
.then_some(point)
.ok_or(ByteConversionError::PointNotInSubgroup)
}
Expand Down Expand Up @@ -112,13 +104,13 @@ mod tests {
fn test_zero_point() {
let g1 = BLS12381Curve::generator();
Copy link
Contributor

Choose a reason for hiding this comment

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

Please rename this test

Suggested change
fn test_zero_point() {
fn test_point_not_in_subgroup() {


assert!(super::check_point_is_in_subgroup(&g1));
assert!(g1.is_in_subgroup());
let new_x = BLS12381FieldElement::zero();
let new_y = BLS12381FieldElement::one() + BLS12381FieldElement::one();

let false_point2 = G1Point::from_affine(new_x, new_y).unwrap();

Copy link
Contributor

@schouhy schouhy Oct 13, 2023

Choose a reason for hiding this comment

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

Please rename the variable. There's no such thing as a false point. Add please a test that asserts that the point is not in the subgroup by other means. For example asserting that it's order does not divide the order of the subgroup being checked.

Suggested change
let false_point2 = G1Point::from_affine(new_x, new_y).unwrap();
let point_not_in_subgroup = G1Point::from_affine(new_x, new_y).unwrap();

assert!(!super::check_point_is_in_subgroup(&false_point2));
assert!(!false_point2.is_in_subgroup());
}

#[test]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use super::field_extension::{BLS12381PrimeField, Degree2ExtensionField};
use crate::cyclic_group::IsGroup;
use crate::elliptic_curve::short_weierstrass::point::ShortWeierstrassProjectivePoint;
use crate::elliptic_curve::traits::IsEllipticCurve;
use crate::{
Expand All @@ -8,6 +9,28 @@ use crate::{
pub type BLS12381FieldElement = FieldElement<BLS12381PrimeField>;
pub type BLS12381TwistCurveFieldElement = FieldElement<Degree2ExtensionField>;

/// Cube Root of Unity in the base field of BLS12-381.
pub const CUBE_ROOT_OF_UNITY_G1: BLS12381FieldElement = BLS12381FieldElement::from_hex_unchecked(
"5f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe",
);

/// Cube Root of Unity in the degree-2 extension field of BLS12-381
pub const CUBE_ROOT_OF_UNITY_G2: BLS12381TwistCurveFieldElement = BLS12381TwistCurveFieldElement::const_from_raw([FieldElement::<BLS12381PrimeField>::from_hex_unchecked("1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac"), FieldElement::<BLS12381PrimeField>::from_hex_unchecked("0")]);

pub const CUBE_ROOT_OF_UNITY_G2_SQUARE: BLS12381TwistCurveFieldElement =
BLS12381TwistCurveFieldElement::const_from_raw([
FieldElement::<BLS12381PrimeField>::from_hex_unchecked(
"5f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe",
),
FieldElement::<BLS12381PrimeField>::from_hex_unchecked("0"),
]);

/// Seed is −𝑥 where 𝑥 is the parameter of the construction 6.6 for 𝑘 ≡ 0(mod6)
/// given in https://theory.stanford.edu/~dfreeman/papers/taxonomy.pdf (p. 27).
/// Order of prime subgroup: 𝑟(x) = 𝜙(x).
/// Size of the extension field: 𝑞(𝑥) = 1∕3(𝑥 − 1)²(𝑥ᵏᐟ³ − 𝑥ᵏᐟ⁶ + 1) + 𝑥.
pub const SEED: u128 = 0xd201000000010000;

/// The description of the curve.
#[derive(Clone, Debug)]
pub struct BLS12381Curve;
Expand Down Expand Up @@ -35,6 +58,29 @@ impl IsShortWeierstrass for BLS12381Curve {
}
}

impl ShortWeierstrassProjectivePoint<BLS12381Curve> {
/// Returns phi(p) where `phi: (x,y)->(ux,y)` and `u` is the Cube Root of Unity in `G_1`
pub fn phi(&self) -> Self {
let mut a = self.clone();
a.0.value[0] = a.x() * CUBE_ROOT_OF_UNITY_G1;

a
}

/// Returns true if the point is in the prime subgroup $G_1$ of order $r$.
/// Makes use of endomorphism for efficient point multiplication.
// Ref: 4.1, https://eprint.iacr.org/2022/352.pdf
pub fn is_in_subgroup(&self) -> bool {
// -z^2[p]
// As z value is negative, we dont need to negate it. Negating the final result is enough.
let lhs = self.operate_with_self(SEED).operate_with_self(SEED).neg();

let rhs = self.phi();

lhs == rhs
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -117,4 +163,16 @@ mod tests {
g.operate_with_self(3_u16)
);
}

#[test]
fn check_generator_g1_in_subgroup() {
let gen = BLS12381Curve::generator();
assert!(gen.is_in_subgroup());
}

#[test]
fn check_arbitrary_g1_point_in_subgroup() {
let arb_point = BLS12381Curve::generator().operate_with_self(100_u32);
assert!(arb_point.is_in_subgroup());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ impl IsField for Degree2ExtensionField {
let c1 = &v0 + &v0;
[c0, c1]
}

/// Returns the component wise subtraction of `a` and `b`
fn sub(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType {
[&a[0] - &b[0], &a[1] - &b[1]]
Expand Down Expand Up @@ -105,6 +106,13 @@ impl IsField for Degree2ExtensionField {
}
}

impl Degree2ExtensionField {
/// Returns the frobenius map which is always conjugate of a field element.
pub fn frobenius_map(elem: &<Self as IsField>::BaseType) -> <Self as IsField>::BaseType {
[elem[0].clone(), -&elem[1]]
}
}

impl ByteConversion for FieldElement<Degree2ExtensionField> {
fn to_bytes_be(&self) -> Vec<u8> {
let mut byte_slice = ByteConversion::to_bytes_be(&self.value()[0]);
Expand Down Expand Up @@ -139,7 +147,8 @@ impl ByteConversion for FieldElement<Degree2ExtensionField> {
}
}

///////////////
/////////////

#[derive(Debug, Clone)]
pub struct LevelTwoResidue;
impl HasCubicNonResidue for LevelTwoResidue {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::{
curve::BLS12381Curve,
curve::{BLS12381Curve, CUBE_ROOT_OF_UNITY_G2, CUBE_ROOT_OF_UNITY_G2_SQUARE},
field_extension::{Degree12ExtensionField, Degree2ExtensionField},
twist::BLS12381TwistCurve,
};
Expand All @@ -13,6 +13,7 @@ use crate::{
},
traits::IsPairing,
},
errors::PairingError,
field::{element::FieldElement, extensions::cubic::HasCubicNonResidue},
unsigned_integer::element::UnsignedInteger,
};
Expand All @@ -27,16 +28,19 @@ impl IsPairing for BLS12381AtePairing {
/// Compute the product of the ate pairings for a list of point pairs.
fn compute_batch(
pairs: &[(&Self::G1Point, &Self::G2Point)],
) -> FieldElement<Self::OutputField> {
) -> Result<FieldElement<Self::OutputField>, PairingError> {
let mut result = FieldElement::one();
for (p, q) in pairs {
if !p.is_in_subgroup() || !q.is_in_subgroup() {
return Err(PairingError::PointNotInSubgroup);
}
if !p.is_neutral_element() && !q.is_neutral_element() {
let p = p.to_affine();
let q = q.to_affine();
result = result * miller(&q, &p);
}
}
final_exponentiation(&result)
Ok(final_exponentiation(&result))
}
}

Expand Down Expand Up @@ -180,16 +184,20 @@ fn frobenius_square(
) -> FieldElement<Degree12ExtensionField> {
let [a, b] = f.value();
let w_raised_to_p_squared_minus_one = FieldElement::<Degree6ExtensionField>::new_base("1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaad");
let omega_3 = FieldElement::<Degree2ExtensionField>::new_base("1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac");
let omega_3_squared = FieldElement::<Degree2ExtensionField>::new_base(
"5f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe",
);

let [a0, a1, a2] = a.value();
let [b0, b1, b2] = b.value();

let f0 = FieldElement::new([a0.clone(), a1 * &omega_3, a2 * &omega_3_squared]);
let f1 = FieldElement::new([b0.clone(), b1 * omega_3, b2 * omega_3_squared]);
let f0 = FieldElement::new([
a0.clone(),
a1 * CUBE_ROOT_OF_UNITY_G2,
a2 * CUBE_ROOT_OF_UNITY_G2_SQUARE,
]);
let f1 = FieldElement::new([
b0.clone(),
b1 * CUBE_ROOT_OF_UNITY_G2,
b2 * CUBE_ROOT_OF_UNITY_G2_SQUARE,
]);

FieldElement::new([f0, f1 * w_raised_to_p_squared_minus_one])
}
Expand Down Expand Up @@ -260,20 +268,22 @@ mod tests {
&p.operate_with_self(a * b).to_affine(),
&q.neg().to_affine(),
),
]);
])
.unwrap();

assert_eq!(result, FieldElement::one());
}

#[test]
fn ate_pairing_returns_one_when_one_element_is_the_neutral_element() {
let p = BLS12381Curve::generator().to_affine();
let q = ShortWeierstrassProjectivePoint::neutral_element();
let result = BLS12381AtePairing::compute_batch(&[(&p.to_affine(), &q)]);
let result = BLS12381AtePairing::compute_batch(&[(&p.to_affine(), &q)]).unwrap();
assert_eq!(result, FieldElement::one());

let p = ShortWeierstrassProjectivePoint::neutral_element();
let q = BLS12381TwistCurve::generator();
let result = BLS12381AtePairing::compute_batch(&[(&p, &q.to_affine())]);
let result = BLS12381AtePairing::compute_batch(&[(&p, &q.to_affine())]).unwrap();
assert_eq!(result, FieldElement::one());
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
use crate::field::traits::LegendreSymbol;

use super::{curve::BLS12381FieldElement, curve::BLS12381TwistCurveFieldElement};
use crate::field::traits::LegendreSymbol;
use std::cmp::Ordering;

#[must_use]
pub fn select_sqrt_value_from_third_bit(
sqrt_1: BLS12381FieldElement,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
use super::curve::SEED;
use super::field_extension::{Degree12ExtensionField, Degree2ExtensionField};
use crate::cyclic_group::IsGroup;
use crate::elliptic_curve::short_weierstrass::point::ShortWeierstrassProjectivePoint;
use crate::elliptic_curve::traits::IsEllipticCurve;
use crate::field::traits::IsField;
use crate::unsigned_integer::element::U384;
use crate::{
elliptic_curve::short_weierstrass::traits::IsShortWeierstrass, field::element::FieldElement,
};

use super::field_extension::{Degree12ExtensionField, Degree2ExtensionField};

const GENERATOR_X_0: U384 = U384::from_hex_unchecked("024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8");
const GENERATOR_X_1: U384 = U384::from_hex_unchecked("13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e");
const GENERATOR_Y_0: U384 = U384::from_hex_unchecked("0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801");
Expand Down Expand Up @@ -46,7 +47,57 @@ impl IsShortWeierstrass for BLS12381TwistCurve {
}
}

impl BLS12381TwistCurve {
pub fn is_on_curve(point: &<Self as IsEllipticCurve>::PointRepresentation) -> bool {
if point.z() == &FieldElement::zero() {
true
} else {
let point = point.to_affine();
Self::defining_equation(&point.x(), &point.y()) == FieldElement::zero()
}
}
}

impl ShortWeierstrassProjectivePoint<BLS12381TwistCurve> {
/// Returns the "Untwist-Frobenius-Twist" endomorphism
pub fn psi(&self) -> Self {
Comment on lines 61 to +63
Copy link
Contributor

Choose a reason for hiding this comment

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

Please add a test that checks that this endomorphism has minimal polynomial $X^2 - tX + q$. Maybe checking that on a few random points.

// Coefficients borrowed from https://github.com/Consensys/gnark-crypto/blob/55489ac07ca1a88bf0b830a29625fcb0d8879a48/ecc/bls12-381/bls12-381.go#L132C1-L135C138
let psi_coeff_x = FieldElement::<<BLS12381TwistCurve as IsEllipticCurve>::BaseField>::new([
FieldElement::zero(),
FieldElement::new(U384::from_hex_unchecked("1A0111EA397FE699EC02408663D4DE85AA0D857D89759AD4897D29650FB85F9B409427EB4F49FFFD8BFD00000000AAAD"))
]);

let psi_coeff_y = FieldElement::<<BLS12381TwistCurve as IsEllipticCurve>::BaseField>::new([
FieldElement::new(U384::from_hex_unchecked("135203E60180A68EE2E9C448D77A2CD91C3DEDD930B1CF60EF396489F61EB45E304466CF3E67FA0AF1EE7B04121BDEA2")),
FieldElement::new(U384::from_hex_unchecked("6AF0E0437FF400B6831E36D6BD17FFE48395DABC2D3435E77F76E17009241C5EE67992F72EC05F4C81084FBEDE3CC09"))
]);

let frob_map_x = Degree2ExtensionField::frobenius_map(self.x().value());
let frob_map_x_with_psi_coeff_x =
Degree2ExtensionField::mul(&frob_map_x, psi_coeff_x.value());

let frob_map_y = Degree2ExtensionField::frobenius_map(self.y().value());
let frob_map_y_with_psi_coeff_y =
Degree2ExtensionField::mul(&frob_map_y, psi_coeff_y.value());

let frob_map_z = Degree2ExtensionField::frobenius_map(self.z().value());

<BLS12381TwistCurve as IsEllipticCurve>::PointRepresentation::new([
FieldElement::new(frob_map_x_with_psi_coeff_x),
FieldElement::new(frob_map_y_with_psi_coeff_y),
FieldElement::new(frob_map_z),
])
}

/// Returns true if the point is in the prime subgroup `G_2` of order `r`.
/// Makes use of endomorphism for efficient point multiplication.
pub fn is_in_subgroup(&self) -> bool {
let seed_times_p = self.operate_with_self(SEED);
let psi_plus_seed_times_p = self.psi().operate_with(&seed_times_p);

BLS12381TwistCurve::is_on_curve(&psi_plus_seed_times_p)
Copy link
Contributor

Choose a reason for hiding this comment

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

This is not checking that $\psi(P) - uP$ is the neutral element. You probably mean something different here.

Suggested change
BLS12381TwistCurve::is_on_curve(&psi_plus_seed_times_p)
psi_plus_seed_times_p.is_neutral_element()

}

/// This function is related to the map ψ: E_twist(𝔽p²) -> E(𝔽p¹²).
/// Given an affine point G in E_twist(𝔽p²) returns x, y such that
/// ψ(G) = (x', y', 1) with x' = x * x'' and y' = y * y''
Expand Down Expand Up @@ -80,6 +131,7 @@ impl ShortWeierstrassProjectivePoint<BLS12381TwistCurve> {

#[cfg(test)]
mod tests {
use super::BLS12381TwistCurve;
use crate::{
cyclic_group::IsGroup,
elliptic_curve::{
Expand All @@ -94,7 +146,6 @@ mod tests {
unsigned_integer::element::U384,
};

use super::BLS12381TwistCurve;
type Level0FE = FieldElement<BLS12381PrimeField>;
type Level1FE = FieldElement<Degree2ExtensionField>;

Expand Down Expand Up @@ -154,4 +205,16 @@ mod tests {
let expected = BLS12381TwistCurve::create_point_from_affine(expectedx, expectedy).unwrap();
assert_eq!(p.operate_with(&q), expected);
}

#[test]
fn check_generator_g2_in_subgroup() {
let gen = BLS12381TwistCurve::generator();
assert!(gen.is_in_subgroup());
}

#[test]
fn check_arbitrary_g2_point_in_subgroup() {
let arb_point = BLS12381TwistCurve::generator().operate_with_self(420_u32);
assert!(arb_point.is_in_subgroup());
}
Comment on lines +209 to +219
Copy link
Contributor

Choose a reason for hiding this comment

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

Please add a test that checks that is_in_subgroup returns false for a point not in $G_2$.

}
Loading
Loading