diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bls12_377/curve.rs b/math/src/elliptic_curve/short_weierstrass/curves/bls12_377/curve.rs index c324b35de..efa52bdbc 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bls12_377/curve.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bls12_377/curve.rs @@ -1,10 +1,25 @@ -use super::field_extension::BLS12377PrimeField; +use super::{ + field_extension::{BLS12377PrimeField, Degree2ExtensionField}, + twist::BLS12377TwistCurve, +}; +use crate::cyclic_group::IsGroup; use crate::elliptic_curve::short_weierstrass::point::ShortWeierstrassProjectivePoint; use crate::elliptic_curve::traits::IsEllipticCurve; +use crate::unsigned_integer::element::U256; + use crate::{ elliptic_curve::short_weierstrass::traits::IsShortWeierstrass, field::element::FieldElement, }; +pub const SUBGROUP_ORDER: U256 = + U256::from_hex_unchecked("12ab655e9a2ca55660b44d1e5c37b00159aa76fed00000010a11800000000001"); + +pub const CURVE_COFACTOR: U256 = + U256::from_hex_unchecked("0x30631250834960419227450344600217059328"); + +pub type BLS12377FieldElement = FieldElement; +pub type BLS12377TwistCurveFieldElement = FieldElement; + /// The description of the curve. #[derive(Clone, Debug)] pub struct BLS12377Curve; @@ -32,6 +47,71 @@ impl IsShortWeierstrass for BLS12377Curve { } } +/// This is equal to the frobenius trace of the BLS12 377 curve minus one or seed value z. +pub const MILLER_LOOP_CONSTANT: u64 = 0x8508c00000000001; + +/// 𝛽 : primitive cube root of unity of πΉβ‚š that Β§satisfies the minimal equation +/// 𝛽² + 𝛽 + 1 = 0 mod 𝑝 +pub const CUBE_ROOT_OF_UNITY_G1: BLS12377FieldElement = FieldElement::from_hex_unchecked( + "0x1ae3a4617c510eabc8756ba8f8c524eb8882a75cc9bc8e359064ee822fb5bffd1e945779fffffffffffffffffffffff", +); + +/// x-coordinate of 𝜁 ∘ πœ‹_q ∘ 𝜁⁻¹, where 𝜁 is the isomorphism u:E'(π”½β‚šβ‚†) βˆ’> E(π”½β‚šβ‚β‚‚) from the twist to E +pub const ENDO_U: BLS12377TwistCurveFieldElement = + BLS12377TwistCurveFieldElement::const_from_raw([ + FieldElement::from_hex_unchecked( + "9B3AF05DD14F6EC619AAF7D34594AABC5ED1347970DEC00452217CC900000008508C00000000002", + ), + FieldElement::from_hex_unchecked("0"), + ]); + +/// y-coordinate of 𝜁 ∘ πœ‹_q ∘ 𝜁⁻¹, where 𝜁 is the isomorphism u:E'(π”½β‚šβ‚†) βˆ’> E(π”½β‚šβ‚β‚‚) from the twist to E +pub const ENDO_V: BLS12377TwistCurveFieldElement = + BLS12377TwistCurveFieldElement::const_from_raw([ + FieldElement::from_hex_unchecked("1680A40796537CAC0C534DB1A79BEB1400398F50AD1DEC1BCE649CF436B0F6299588459BFF27D8E6E76D5ECF1391C63"), + FieldElement::from_hex_unchecked("0"), + ]); + +impl ShortWeierstrassProjectivePoint { + /// Returns πœ™(P) = (π‘₯, 𝑦) β‡’ (𝛽π‘₯, 𝑦), where 𝛽 is the Cube Root of Unity in the base prime field + /// https://eprint.iacr.org/2022/352.pdf 2 Preliminaries + fn phi(&self) -> Self { + let mut a = self.clone(); + a.0.value[0] = a.x() * CUBE_ROOT_OF_UNITY_G1; + a + } + + /// πœ™(P) = βˆ’π‘’Β²P + /// https://eprint.iacr.org/2022/352.pdf 4.3 Prop. 4 + pub fn is_in_subgroup(&self) -> bool { + self.operate_with_self(MILLER_LOOP_CONSTANT) + .operate_with_self(MILLER_LOOP_CONSTANT) + .neg() + == self.phi() + } +} + +impl ShortWeierstrassProjectivePoint { + /// πœ“(P) = 𝜁 ∘ πœ‹β‚š ∘ 𝜁⁻¹, where 𝜁 is the isomorphism u:E'(π”½β‚šβ‚†) βˆ’> E(π”½β‚šβ‚β‚‚) from the twist to E,, πœ‹β‚š is the p-power frobenius endomorphism + /// and πœ“ satisifies minmal equation 𝑋² + 𝑑𝑋 + π‘ž = 𝑂 + /// https://eprint.iacr.org/2022/352.pdf 4.2 (7) + /// ψ(P) = (ψ_x * conjugate(x), ψ_y * conjugate(y), conjugate(z)) + fn psi(&self) -> Self { + let [x, y, z] = self.coordinates(); + Self::new([ + x.conjugate() * ENDO_U, + y.conjugate() * ENDO_V, + z.conjugate(), + ]) + } + + /// πœ“(P) = 𝑒P, where 𝑒 = SEED of the curve + /// https://eprint.iacr.org/2022/352.pdf 4.2 + pub fn is_in_subgroup(&self) -> bool { + self.psi() == self.operate_with_self(MILLER_LOOP_CONSTANT) + } +} + #[cfg(test)] mod tests { use super::*; @@ -43,17 +123,18 @@ mod tests { use super::BLS12377Curve; #[allow(clippy::upper_case_acronyms)] - type FEE = FieldElement; + type FpE = FieldElement; + type Fp2 = FieldElement; fn point_1() -> ShortWeierstrassProjectivePoint { - let x = FEE::new_base("134e4cc122cb62a06767fb98e86f2d5f77e2a12fefe23bb0c4c31d1bd5348b88d6f5e5dee2b54db4a2146cc9f249eea"); - let y = FEE::new_base("17949c29effee7a9f13f69b1c28eccd78c1ed12b47068836473481ff818856594fd9c1935e3d9e621901a2d500257a2"); + let x = FpE::new_base("134e4cc122cb62a06767fb98e86f2d5f77e2a12fefe23bb0c4c31d1bd5348b88d6f5e5dee2b54db4a2146cc9f249eea"); + let y = FpE::new_base("17949c29effee7a9f13f69b1c28eccd78c1ed12b47068836473481ff818856594fd9c1935e3d9e621901a2d500257a2"); BLS12377Curve::create_point_from_affine(x, y).unwrap() } fn point_1_times_5() -> ShortWeierstrassProjectivePoint { - let x = FEE::new_base("3c852d5aab73fbb51e57fbf5a0a8b5d6513ec922b2611b7547bfed74cba0dcdfc3ad2eac2733a4f55d198ec82b9964"); - let y = FEE::new_base("a71425e68e55299c64d7eada9ae9c3fb87a9626b941d17128b64685fc07d0e635f3c3a512903b4e0a43e464045967b"); + let x = FpE::new_base("3c852d5aab73fbb51e57fbf5a0a8b5d6513ec922b2611b7547bfed74cba0dcdfc3ad2eac2733a4f55d198ec82b9964"); + let y = FpE::new_base("a71425e68e55299c64d7eada9ae9c3fb87a9626b941d17128b64685fc07d0e635f3c3a512903b4e0a43e464045967b"); BLS12377Curve::create_point_from_affine(x, y).unwrap() } @@ -101,9 +182,9 @@ mod tests { let point_1 = point_1().to_affine(); // Create point 2 - let x = FEE::new_base("134e4cc122cb62a06767fb98e86f2d5f77e2a12fefe23bb0c4c31d1bd5348b88d6f5e5dee2b54db4a2146cc9f249eea") * FEE::from(2); - let y = FEE::new_base("17949c29effee7a9f13f69b1c28eccd78c1ed12b47068836473481ff818856594fd9c1935e3d9e621901a2d500257a2") * FEE::from(2); - let z = FEE::from(2); + let x = FpE::new_base("134e4cc122cb62a06767fb98e86f2d5f77e2a12fefe23bb0c4c31d1bd5348b88d6f5e5dee2b54db4a2146cc9f249eea") * FpE::from(2); + let y = FpE::new_base("17949c29effee7a9f13f69b1c28eccd78c1ed12b47068836473481ff818856594fd9c1935e3d9e621901a2d500257a2") * FpE::from(2); + let z = FpE::from(2); let point_2 = ShortWeierstrassProjectivePoint::::new([x, y, z]); let first_algorithm_result = point_2.operate_with(&point_1).to_affine(); @@ -115,15 +196,15 @@ mod tests { #[test] fn create_valid_point_works() { let p = point_1(); - assert_eq!(*p.x(), FEE::new_base("134e4cc122cb62a06767fb98e86f2d5f77e2a12fefe23bb0c4c31d1bd5348b88d6f5e5dee2b54db4a2146cc9f249eea")); - assert_eq!(*p.y(), FEE::new_base("17949c29effee7a9f13f69b1c28eccd78c1ed12b47068836473481ff818856594fd9c1935e3d9e621901a2d500257a2")); - assert_eq!(*p.z(), FEE::new_base("1")); + assert_eq!(*p.x(), FpE::new_base("134e4cc122cb62a06767fb98e86f2d5f77e2a12fefe23bb0c4c31d1bd5348b88d6f5e5dee2b54db4a2146cc9f249eea")); + assert_eq!(*p.y(), FpE::new_base("17949c29effee7a9f13f69b1c28eccd78c1ed12b47068836473481ff818856594fd9c1935e3d9e621901a2d500257a2")); + assert_eq!(*p.z(), FpE::new_base("1")); } #[test] fn create_invalid_points_panics() { assert_eq!( - BLS12377Curve::create_point_from_affine(FEE::from(1), FEE::from(1)).unwrap_err(), + BLS12377Curve::create_point_from_affine(FpE::from(1), FpE::from(1)).unwrap_err(), EllipticCurveError::InvalidPoint ) } @@ -144,4 +225,36 @@ mod tests { g.operate_with_self(3_u16) ); } + + #[test] + fn generator_g1_is_in_subgroup() { + let g = BLS12377Curve::generator(); + assert!(g.is_in_subgroup()) + } + + #[test] + fn point1_is_in_subgroup() { + let p = point_1(); + assert!(p.is_in_subgroup()) + } + + #[test] + fn arbitrary_g1_point_is_in_subgroup() { + let g = BLS12377Curve::generator().operate_with_self(32u64); + assert!(g.is_in_subgroup()) + } + #[test] + fn generator_g2_is_in_subgroup() { + let g = BLS12377TwistCurve::generator(); + assert!(g.is_in_subgroup()) + } + + #[test] + fn g2_conjugate_works() { + let a = Fp2::zero(); + let mut expected = a.conjugate(); + expected = expected.conjugate(); + + assert_eq!(a, expected); + } } diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bls12_377/field_extension.rs b/math/src/elliptic_curve/short_weierstrass/curves/bls12_377/field_extension.rs index 32f9d465d..96c227ddb 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bls12_377/field_extension.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bls12_377/field_extension.rs @@ -1,10 +1,18 @@ use crate::field::{ element::FieldElement, + errors::FieldError, + extensions::{ + cubic::{CubicExtensionField, HasCubicNonResidue}, + quadratic::{HasQuadraticNonResidue, QuadraticExtensionField}, + }, fields::montgomery_backed_prime_fields::{IsModulus, MontgomeryBackendPrimeField}, + traits::{IsField, IsSubFieldOf}, }; +use crate::traits::ByteConversion; use crate::unsigned_integer::element::U384; pub const BLS12377_PRIME_FIELD_ORDER: U384 = U384::from_hex_unchecked("1ae3a4617c510eac63b05c06ca1493b1a22d9f300f5138f1ef3622fba094800170b5d44300000008508c00000000001"); +pub const FP2_RESIDUE: FieldElement =FieldElement::from_hex_unchecked("0x1ae3a4617c510eac63b05c06ca1493b1a22d9f300f5138f1ef3622fba094800170b5d44300000008508bffffffffffc"); // FPBLS12377 #[derive(Clone, Debug)] @@ -15,8 +23,340 @@ impl IsModulus for BLS12377FieldModulus { pub type BLS12377PrimeField = MontgomeryBackendPrimeField; +#[derive(Clone, Debug)] +pub struct Degree2ExtensionField; + +impl IsField for Degree2ExtensionField { + type BaseType = [FieldElement; 2]; + /// Returns the component wise addition of `a` and `b` + /// Returns the component wise addition of `a` and `b` + fn add(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { + [&a[0] + &b[0], &a[1] + &b[1]] + } + + /// Returns the multiplication of `a` and `b` using the following + /// equation: + /// (a0 + a1 * t) * (b0 + b1 * t) = a0 * b0 + a1 * b1 * Self::residue() + (a0 * b1 + a1 * b0) * t + /// where `t.pow(2)` equals `Q::residue()`. + fn mul(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { + let a0b0 = &a[0] * &b[0]; + let a1b1 = &a[1] * &b[1]; + let z = (&a[0] + &a[1]) * (&b[0] + &b[1]); + [&a0b0 + &a1b1 * FP2_RESIDUE, z - a0b0 - a1b1] + } + + fn square(a: &Self::BaseType) -> Self::BaseType { + let [a0, a1] = a; + let v0 = a0 * a1; + let c0 = (a0 + a1) * (a0 + &FP2_RESIDUE * a1) - &v0 - FP2_RESIDUE * &v0; + 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]] + } + + /// Returns the component wise negation of `a` + fn neg(a: &Self::BaseType) -> Self::BaseType { + [-&a[0], -&a[1]] + } + + /// Returns the multiplicative inverse of `a` + /// This uses the equality `(a0 + a1 * t) * (a0 - a1 * t) = a0.pow(2) - a1.pow(2) * Q::residue()` + fn inv(a: &Self::BaseType) -> Result { + let q: FieldElement = -FieldElement::from(5); + let inv_norm = (a[0].pow(2_u64) - q * a[1].pow(2_u64)).inv()?; + Ok([&a[0] * &inv_norm, -&a[1] * inv_norm]) + } + + /// Returns the division of `a` and `b` + fn div(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { + ::mul(a, &Self::inv(b).unwrap()) + } + + /// Returns a boolean indicating whether `a` and `b` are equal component wise. + fn eq(a: &Self::BaseType, b: &Self::BaseType) -> bool { + a[0] == b[0] && a[1] == b[1] + } + + /// Returns the additive neutral element of the field extension. + fn zero() -> Self::BaseType { + [FieldElement::zero(), FieldElement::zero()] + } + + /// Returns the multiplicative neutral element of the field extension. + fn one() -> Self::BaseType { + [FieldElement::one(), FieldElement::zero()] + } + + /// Returns the element `x * 1` where 1 is the multiplicative neutral element. + fn from_u64(x: u64) -> Self::BaseType { + [FieldElement::from(x), FieldElement::zero()] + } + + /// Takes as input an element of BaseType and returns the internal representation + /// of that element in the field. + /// Note: for this case this is simply the identity, because the components + /// already have correct representations. + fn from_base_type(x: Self::BaseType) -> Self::BaseType { + x + } +} + +impl IsSubFieldOf for BLS12377PrimeField { + fn mul( + a: &Self::BaseType, + b: &::BaseType, + ) -> ::BaseType { + let c0 = FieldElement::from_raw(::mul(a, b[0].value())); + let c1 = FieldElement::from_raw(::mul(a, b[1].value())); + [c0, c1] + } + + fn add( + a: &Self::BaseType, + b: &::BaseType, + ) -> ::BaseType { + let c0 = FieldElement::from_raw(::add(a, b[0].value())); + let c1 = FieldElement::from_raw(*b[1].value()); + [c0, c1] + } + + fn div( + a: &Self::BaseType, + b: &::BaseType, + ) -> ::BaseType { + let b_inv = Degree2ExtensionField::inv(b).unwrap(); + >::mul(a, &b_inv) + } + + fn sub( + a: &Self::BaseType, + b: &::BaseType, + ) -> ::BaseType { + let c0 = FieldElement::from_raw(::sub(a, b[0].value())); + let c1 = FieldElement::from_raw(::neg(b[1].value())); + [c0, c1] + } + + fn embed(a: Self::BaseType) -> ::BaseType { + [FieldElement::from_raw(a), FieldElement::zero()] + } + + #[cfg(feature = "alloc")] + fn to_subfield_vec( + b: ::BaseType, + ) -> alloc::vec::Vec { + b.into_iter().map(|x| x.to_raw()).collect() + } +} + +impl ByteConversion for FieldElement { + #[cfg(feature = "alloc")] + fn to_bytes_be(&self) -> alloc::vec::Vec { + let mut byte_slice = ByteConversion::to_bytes_be(&self.value()[0]); + byte_slice.extend(ByteConversion::to_bytes_be(&self.value()[1])); + byte_slice + } + + #[cfg(feature = "alloc")] + fn to_bytes_le(&self) -> alloc::vec::Vec { + let mut byte_slice = ByteConversion::to_bytes_le(&self.value()[0]); + byte_slice.extend(ByteConversion::to_bytes_le(&self.value()[1])); + byte_slice + } + + fn from_bytes_be(bytes: &[u8]) -> Result + where + Self: core::marker::Sized, + { + const BYTES_PER_FIELD: usize = 48; + let x0 = FieldElement::from_bytes_be(&bytes[0..BYTES_PER_FIELD])?; + let x1 = FieldElement::from_bytes_be(&bytes[BYTES_PER_FIELD..BYTES_PER_FIELD * 2])?; + Ok(Self::new([x0, x1])) + } + + fn from_bytes_le(bytes: &[u8]) -> Result + where + Self: core::marker::Sized, + { + const BYTES_PER_FIELD: usize = 48; + let x0 = FieldElement::from_bytes_le(&bytes[0..BYTES_PER_FIELD])?; + let x1 = FieldElement::from_bytes_le(&bytes[BYTES_PER_FIELD..BYTES_PER_FIELD * 2])?; + Ok(Self::new([x0, x1])) + } +} + +impl FieldElement { + pub fn new_base(a_hex: &str) -> Self { + Self::new([FieldElement::new(U384::from(a_hex)), FieldElement::zero()]) + } + + pub fn conjugate(&self) -> Self { + let [a, b] = self.value(); + Self::new([a.clone(), -b]) + } +} + +#[derive(Debug, Clone)] +pub struct BLS12377Residue; +impl HasQuadraticNonResidue for BLS12377Residue { + fn residue() -> FieldElement { + FP2_RESIDUE + } +} + +#[derive(Debug, Clone)] +pub struct LevelTwoResidue; + +impl HasCubicNonResidue for LevelTwoResidue { + fn residue() -> FieldElement { + FieldElement::new([ + FieldElement::new(U384::from("0")), + -FieldElement::new(U384::from("1")), + ]) + } +} + +pub type Degree6ExtensionField = CubicExtensionField; + +#[derive(Debug, Clone)] +pub struct LevelThreeResidue; +impl HasQuadraticNonResidue for LevelThreeResidue { + fn residue() -> FieldElement { + FieldElement::new([ + FieldElement::zero(), + FieldElement::one(), + FieldElement::zero(), + ]) + } +} + +/// We define Fp12 = Fp6 [w] / (w^2 - v) +pub type Degree12ExtensionField = QuadraticExtensionField; + +impl FieldElement { + pub fn new_base(a_hex: &str) -> Self { + Self::new([ + FieldElement::new([FieldElement::new(U384::from(a_hex)), FieldElement::zero()]), + FieldElement::zero(), + FieldElement::zero(), + ]) + } +} + impl FieldElement { pub fn new_base(a_hex: &str) -> Self { - Self::new(U384::from_hex_unchecked(a_hex)) + Self::new(U384::from(a_hex)) + } +} +impl FieldElement { + pub fn new_base(a_hex: &str) -> Self { + Self::new([ + FieldElement::::new_base(a_hex), + FieldElement::zero(), + ]) + } + + pub fn from_coefficients(coefficients: &[&str; 12]) -> Self { + FieldElement::::new([ + FieldElement::new([ + FieldElement::new([ + FieldElement::new(U384::from(coefficients[0])), + FieldElement::new(U384::from(coefficients[1])), + ]), + FieldElement::new([ + FieldElement::new(U384::from(coefficients[2])), + FieldElement::new(U384::from(coefficients[3])), + ]), + FieldElement::new([ + FieldElement::new(U384::from(coefficients[4])), + FieldElement::new(U384::from(coefficients[5])), + ]), + ]), + FieldElement::new([ + FieldElement::new([ + FieldElement::new(U384::from(coefficients[6])), + FieldElement::new(U384::from(coefficients[7])), + ]), + FieldElement::new([ + FieldElement::new(U384::from(coefficients[8])), + FieldElement::new(U384::from(coefficients[9])), + ]), + FieldElement::new([ + FieldElement::new(U384::from(coefficients[10])), + FieldElement::new(U384::from(coefficients[11])), + ]), + ]), + ]) + } +} + +#[cfg(test)] +mod tests { + + use super::*; + type FpE = FieldElement; + type Fp2E = FieldElement; + type Fp6E = FieldElement; + type Fp12E = FieldElement; + + #[test] + fn embed_base_field_with_degree_2_extension() { + let a = FpE::from(3); + let a_extension = Fp2E::from(3); + assert_eq!(a.to_extension::(), a_extension); + } + #[test] + fn add_base_field_with_degree_2_extension() { + let a = FpE::from(3); + let a_extension = Fp2E::from(3); + let b = Fp2E::from(2); + assert_eq!(a + &b, a_extension + b); + } + #[test] + fn mul_degree_2_with_degree_6_extension() { + let a = Fp2E::new([FpE::from(3), FpE::from(4)]); + let a_extension = a.clone().to_extension::(); + let b = Fp6E::from(2); + assert_eq!(a * &b, a_extension * b); + } + #[test] + fn div_degree_6_degree_12_extension() { + let a = Fp6E::from(3); + let a_extension = Fp12E::from(3); + let b = Fp12E::from(2); + assert_eq!(a / &b, a_extension / b); + } + + #[test] + fn double_equals_sum_two_times() { + let a = FpE::from(3); + assert_eq!(a.double(), a.clone() + a); + } + #[test] + fn base_field_sum_is_asociative() { + let a = FpE::from(3); + let b = FpE::from(2); + let c = &a + &b; + assert_eq!(a.double() + b, a + c); + } + #[test] + fn degree_2_extension_mul_is_conmutative() { + let a = Fp2E::from(3); + let b = Fp2E::new([FpE::from(2), FpE::from(4)]); + assert_eq!(&a * &b, b * a); + } + + #[test] + fn base_field_pow_p_is_identity() { + let a = FpE::from(3); + assert_eq!(a.pow(BLS12377_PRIME_FIELD_ORDER), a); + } + #[test] + fn square_equals_mul_two_times() { + let a = FpE::from(3); + assert_eq!(a.square(), a.clone() * a); } } diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bls12_377/mod.rs b/math/src/elliptic_curve/short_weierstrass/curves/bls12_377/mod.rs index 8fc713a88..5fe6ce8bd 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bls12_377/mod.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bls12_377/mod.rs @@ -1,2 +1,3 @@ pub mod curve; pub mod field_extension; +pub mod twist; diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bls12_377/twist.rs b/math/src/elliptic_curve/short_weierstrass/curves/bls12_377/twist.rs new file mode 100644 index 000000000..e0aa87c59 --- /dev/null +++ b/math/src/elliptic_curve/short_weierstrass/curves/bls12_377/twist.rs @@ -0,0 +1,172 @@ +use super::field_extension::Degree2ExtensionField; +use crate::elliptic_curve::short_weierstrass::point::ShortWeierstrassProjectivePoint; +use crate::elliptic_curve::traits::IsEllipticCurve; +use crate::unsigned_integer::element::U384; +use crate::{ + elliptic_curve::short_weierstrass::traits::IsShortWeierstrass, field::element::FieldElement, +}; + +const GENERATOR_X_0: U384 = U384::from_hex_unchecked("0x018480be71c785fec89630a2a3841d01c565f071203e50317ea501f557db6b9b71889f52bb53540274e3e48f7c005196"); +const GENERATOR_X_1: U384 = U384::from_hex_unchecked("0x00ea6040e700403170dc5a51b1b140d5532777ee6651cecbe7223ece0799c9de5cf89984bff76fe6b26bfefa6ea16afe"); +const GENERATOR_Y_0: U384 = U384::from_hex_unchecked("0x00690d665d446f7bd960736bcbb2efb4de03ed7274b49a58e458c282f832d204f2cf88886d8c7c2ef094094409fd4ddf"); +const GENERATOR_Y_1: U384 = U384::from_hex_unchecked("0x00f8169fd28355189e549da3151a70aa61ef11ac3d591bf12463b01acee304c24279b83f5e52270bd9a1cdd185eb8f93"); + +/// The description of the curve. +#[derive(Clone, Debug)] +pub struct BLS12377TwistCurve; + +impl IsEllipticCurve for BLS12377TwistCurve { + type BaseField = Degree2ExtensionField; + type PointRepresentation = ShortWeierstrassProjectivePoint; + + fn generator() -> Self::PointRepresentation { + Self::PointRepresentation::new([ + FieldElement::new([ + FieldElement::new(GENERATOR_X_0), + FieldElement::new(GENERATOR_X_1), + ]), + FieldElement::new([ + FieldElement::new(GENERATOR_Y_0), + FieldElement::new(GENERATOR_Y_1), + ]), + FieldElement::one(), + ]) + } +} + +impl IsShortWeierstrass for BLS12377TwistCurve { + fn a() -> FieldElement { + FieldElement::zero() + } + + fn b() -> FieldElement { + FieldElement::new([FieldElement::zero(), + FieldElement::from_hex_unchecked("0x10222f6db0fd6f343bd03737460c589dc7b4f91cd5fd889129207b63c6bf8000dd39e5c1ccccccd1c9ed9999999999a")]) + } +} + +#[cfg(test)] +mod tests { + use crate::{ + cyclic_group::IsGroup, + elliptic_curve::{ + short_weierstrass::{ + curves::bls12_377::field_extension::{BLS12377PrimeField, Degree2ExtensionField}, + traits::IsShortWeierstrass, + }, + traits::IsEllipticCurve, + }, + field::element::FieldElement, + unsigned_integer::element::U384, + }; + + use super::BLS12377TwistCurve; + type Level0FE = FieldElement; + type Level1FE = FieldElement; + + #[test] + fn create_generator() { + let g = BLS12377TwistCurve::generator(); + let [x, y, _] = g.coordinates(); + assert_eq!( + BLS12377TwistCurve::defining_equation(x, y), + Level1FE::zero() + ); + } + #[test] + // Values checked in sage + fn add_point_three_times_equals_operate_with_self_3() { + let px = Level1FE::new([ + Level0FE::new(U384::from_hex_unchecked("0x11a87eda97b96e733c2eb833ae35531b87878b416d57b370c7cd13b5f3c413387633b0ca6dfead19305318501376087")), + Level0FE::new(U384::from_hex_unchecked("0xa4a6d842722f2636937acf0e889ab343e121a599b8a3a9bd3be766da7d84f8e060be00f06bb2d29df963bf2d847598")) + ]); + let py = Level1FE::new([ + Level0FE::new(U384::from_hex_unchecked("0x75589c0925d6cf45e715460ea7cb3388d4e21d9b79aa8411567d8de85ba5561bcab80a5c0b363e31817d458e5b2a2a")), + Level0FE::new(U384::from_hex_unchecked("0xcb4e1e4b160cc6c92e7b3dd0b2f4053bc7178d201e7788796a6035c59ccd586635796e97003b1f76eca273576f01ac")) + ]); + + let expectedx = Level1FE::new([ + Level0FE::new(U384::from_hex_unchecked("0x1aba32e70b88834d0cd5fb3a27eda8211eb94b6a191cd287f798145c09dc64dabda377836c31d31cc728635425ccc61")), + Level0FE::new(U384::from_hex_unchecked("0x146b578a9c3d92f64baafa082d27c3446fb197659bbd4ac45e290f5f49ae308599448dc288140a4049bac3c6e3e1aac")) + ]); + let expectedy = Level1FE::new([ + Level0FE::new(U384::from_hex_unchecked("0x1417486a114a5a9074151a3a2c710dc105cce81de69a91067758355cda7049a2ad8077a2343679bba473484ad0cddd6")), + Level0FE::new(U384::from_hex_unchecked("0x195c939995782a07b26e3b44b49d58eb0951d452d0e8928f218a8e63f74f74860cb56265e437da80df67b6254b27cd")) + ]); + let p = BLS12377TwistCurve::create_point_from_affine(px, py).unwrap(); + let expected = BLS12377TwistCurve::create_point_from_affine(expectedx, expectedy).unwrap(); + assert_eq!(p.operate_with_self(3_u16), expected); + } + + #[test] + // Values checked in sage + fn operate_with_self_test() { + let px = Level1FE::new([ + Level0FE::new(U384::from_hex_unchecked("0x2233db786c8cb6a3b6846aebad4ce3f5346961c8bade4c129d920170d1ceeb02d84fd4e12b592f0cba64e083d75167")), + Level0FE::new(U384::from_hex_unchecked("0x12d1c7ac03cb1991993cdb9d38c50588b67c18ed9b9db5f84ac5b59c201f6493a42f690169b96b7a9f12fae4718b6cb")) + ]); + + let py = Level1FE::new([ + Level0FE::new(U384::from_hex_unchecked("0x160cc59bb3929b1073996aa370c880e002b81f3e4f7275636caf55754bbfcfe1d43c5d91ee7f3cb49254be2366d5d0")), + Level0FE::new(U384::from_hex_unchecked("0xe66460970bf2fc79831983744d7c83fad910266fd56f08b4a8f737e7609d88930503091e06228a184627b57c02352")) + ]); + + let qx = Level1FE::new([ + Level0FE::new(U384::from_hex_unchecked("0x1124128cf7166bb67207327079f8a3e75d876e3b6dd54cc05c766951c5d832aa202c57ed2308d78283e4c859be8fee3")), + Level0FE::new(U384::from_hex_unchecked("0x1889e19d4f67d120d367c15f7bc23529fe4e335627e0eb16ec2bafe6199f0e7d393c5413fc7157ec03d5d56e1eb333")) + ]); + + let qy = Level1FE::new([ + Level0FE::new(U384::from_hex_unchecked("0x16db05c371bf6f28e92faa77d3abf5c66c4feea84d334807f46ee69227a2144a827b00f8bcf4179b97922a85ebd5776")), + Level0FE::new(U384::from_hex_unchecked("0x105def68752ac5cb73f3b692b3c877f2febe6cd8a679584f4b1c64f9886f12b15dd7909251bc1e90d559fadd6b1f8f5")) + ]); + + let scalar = U384::from_hex_unchecked( + "0x3061aa3679b1865fa09dac7339a87511147f015aa8997fae59b475d751bc16f", + ); + + let p = BLS12377TwistCurve::create_point_from_affine(px, py).unwrap(); + let q = BLS12377TwistCurve::create_point_from_affine(qx, qy).unwrap(); + + assert_eq!(p.operate_with_self(scalar), q); + } + + #[test] + // Values checked in sage + fn add_two_points() { + let px = Level1FE::new([ + Level0FE::new(U384::from_hex_unchecked("0x11a87eda97b96e733c2eb833ae35531b87878b416d57b370c7cd13b5f3c413387633b0ca6dfead19305318501376087")), + Level0FE::new(U384::from_hex_unchecked("0xa4a6d842722f2636937acf0e889ab343e121a599b8a3a9bd3be766da7d84f8e060be00f06bb2d29df963bf2d847598")) + ]); + + let py = Level1FE::new([ + Level0FE::new(U384::from_hex_unchecked("0x75589c0925d6cf45e715460ea7cb3388d4e21d9b79aa8411567d8de85ba5561bcab80a5c0b363e31817d458e5b2a2a")), + Level0FE::new(U384::from_hex_unchecked("0xcb4e1e4b160cc6c92e7b3dd0b2f4053bc7178d201e7788796a6035c59ccd586635796e97003b1f76eca273576f01ac")) + ]); + + let qx = Level1FE::new([ + Level0FE::new(U384::from_hex_unchecked("0xa27279ea48d8e812223956c84ae89c5de09e67c7052c056d936da4578dcf0b30799f2212af0017fe50e146c5c2ce05")), + Level0FE::new(U384::from_hex_unchecked("0xaed1579f3de386644e0ffac471e98f8f1d97a6be4d88a0c516bc9fd4d7780649424c3d51bb85d074d66560e2c2bb07")) + ]); + + let qy = Level1FE::new([ + Level0FE::new(U384::from_hex_unchecked("0x6e7e249e0d7c81d0fec62a3f96d6fefd7d6c87019559b03664a015a2ed536d98e6432b93ec219426f9729f119c7982")), + Level0FE::new(U384::from_hex_unchecked("0x104c4e4adb48273511a0473d742724f48042b967591ab9b86731bb902debfd44d89571af704340614f6618863fc5841")) + ]); + + let expectedx = Level1FE::new([ + Level0FE::new(U384::from_hex_unchecked("0x19d74f8769a27aae05c5b50b2d051849f6feab554d0fd4f51c7fe43d918fac1d966c97dd6e61b2f7fc13694495dc259")), + Level0FE::new(U384::from_hex_unchecked("0x11f4194886ad75e3baff9853734e9ef4fe5f20d333951ba126bca7dd54ebc4998b17d4d315d29147dc22124c5464a64")) + ]); + let expectedy = Level1FE::new([ + Level0FE::new(U384::from_hex_unchecked("0x14faa941c0cdf5567c294bc9e73e83c4602f331a7400222c8475fba4c6a688b12ce8f7cc20e54eaac1af6046aec58bd")), + Level0FE::new(U384::from_hex_unchecked("0x7cee0c4760e2bf76047cda31141e6242c5893ba5c2fba5f23c0048545912e309dc647f4cfe2db17b23aa2f57c3d8c3")) + ]); + + let p = BLS12377TwistCurve::create_point_from_affine(px, py).unwrap(); + let q = BLS12377TwistCurve::create_point_from_affine(qx, qy).unwrap(); + let expected = BLS12377TwistCurve::create_point_from_affine(expectedx, expectedy).unwrap(); + + assert_eq!(p.operate_with(&q), expected); + } +}