diff --git a/README.md b/README.md index befe19dba..1edb80a8f 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,16 @@ This library provides efficient implementation of cryptographic primitives used +## Examples - mini apps + +Below is a list of examples to understand lambdaworks and learn what you can build with the tools provided. + +- [Merkle Tree CLI](./examples/merkle-tree-cli/) +- [Proving Miden](./examples/prove-miden/) +- [Shamir's secret sharing](./examples/shamir_secret_sharing/) +- [BabySNARK](./examples/baby-snark/) +- [Pinocchio](./examples/pinocchio/) + ## Why we built lambdaworks Zero-Knowledge and Validity Proofs have gained a lot of attention over the last few years. We strongly believe in this potential and that is why we decided to start working in this challenging ecosystem, where math, cryptography and distributed systems meet. The main barrier in the beginning was not the cryptography or math but the lack of good libraries which are performant and developer friendly. There are some exceptions, though, like gnark or halo2. Some have nice APIs and are easy to work with, but they are not written in Rust, and some are written in Rust but have poor programming and engineering practices. Most of them don't have support for CUDA, Metal and WebGPU or distributed FFT calculation using schedulers like Dask. @@ -41,14 +51,6 @@ Most of math and crypto crates supports no-std without allocation with `no-defau Both Math and Crypto support wasm with target `wasm32-unknown-unknown`. To see an example of how to use this to deploy a verifier in a browser, check the Cairo Prover wasm-pack verifier. -## Examples - mini apps - -- [Merkle Tree CLI](https://github.com/lambdaclass/lambdaworks/tree/main/examples/merkle-tree-cli) -- [Proving Miden](https://github.com/lambdaclass/lambdaworks/tree/main/examples/prove-miden) -- [Shamir's secret sharing](https://github.com/lambdaclass/lambdaworks/tree/main/examples/shamir_secret_sharing) -- [BabySNARK](https://github.com/lambdaclass/lambdaworks/tree/main/examples/baby-snark) -- [Pinocchio](https://github.com/lambdaclass/lambdaworks/tree/main/examples/pinocchio) - ## Exercises and Challenges - [lambdaworks exercises and challenges](https://github.com/lambdaclass/lambdaworks_exercises/tree/main) diff --git a/math/benches/criterion_elliptic_curve.rs b/math/benches/criterion_elliptic_curve.rs index 3812d74c3..f9bffc781 100644 --- a/math/benches/criterion_elliptic_curve.rs +++ b/math/benches/criterion_elliptic_curve.rs @@ -10,6 +10,6 @@ use elliptic_curves::{ criterion_group!( name = elliptic_curve_benches; config = Criterion::default().with_profiler(PProfProfiler::new(100, Output::Flamegraph(None))); - targets = bn_254_elliptic_curve_benchmarks,bls12_377_elliptic_curve_benchmarks,bls12_381_elliptic_curve_benchmarks + targets = bn_254_elliptic_curve_benchmarks,bls12_381_elliptic_curve_benchmarks,bls12_377_elliptic_curve_benchmarks ); criterion_main!(elliptic_curve_benches); diff --git a/math/benches/elliptic_curves/bls12_381.rs b/math/benches/elliptic_curves/bls12_381.rs index d33e8d2ca..452480697 100644 --- a/math/benches/elliptic_curves/bls12_381.rs +++ b/math/benches/elliptic_curves/bls12_381.rs @@ -4,7 +4,9 @@ use lambdaworks_math::{ elliptic_curve::{ short_weierstrass::{ curves::bls12_381::{ - curve::BLS12381Curve, pairing::BLS12381AtePairing, twist::BLS12381TwistCurve, + curve::BLS12381Curve, + pairing::{final_exponentiation, miller, BLS12381AtePairing}, + twist::BLS12381TwistCurve, }, traits::Compress, }, @@ -24,6 +26,8 @@ pub fn bls12_381_elliptic_curve_benchmarks(c: &mut Criterion) { let a_g2 = BLS12381TwistCurve::generator(); let b_g2 = BLS12381TwistCurve::generator(); + let miller_loop_output = miller(&a_g2, &a_g1); + let mut group = c.benchmark_group("BLS12-381 Ops"); group.significance_level(0.1).sample_size(10000); group.throughput(criterion::Throughput::Elements(1)); @@ -93,4 +97,14 @@ pub fn bls12_381_elliptic_curve_benchmarks(c: &mut Criterion) { )) }); }); + + // Miller + group.bench_function("Miller", |bencher| { + bencher.iter(|| black_box(miller(black_box(&a_g2), black_box(&a_g1)))) + }); + + // Final Exponentiation Optimized + group.bench_function("Final Exponentiation", |bencher| { + bencher.iter(|| black_box(final_exponentiation(black_box(&miller_loop_output)))) + }); } diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/field_extension.rs b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/field_extension.rs index ecf87e2d9..cb41e5aeb 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/field_extension.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/field_extension.rs @@ -21,6 +21,7 @@ impl IsModulus for BLS12381FieldModulus { } pub type BLS12381PrimeField = MontgomeryBackendPrimeField; +type Fp2E = FieldElement; ////////////////// #[derive(Clone, Debug)] @@ -199,6 +200,16 @@ impl HasCubicNonResidue for LevelTwoResidue { } } +impl HasQuadraticNonResidue for LevelTwoResidue { + fn residue() -> FieldElement { + FieldElement::new([ + FieldElement::new(U384::from("1")), + FieldElement::new(U384::from("1")), + ]) + } +} +pub type Degree4ExtensionField = QuadraticExtensionField; + pub type Degree6ExtensionField = CubicExtensionField; #[derive(Debug, Clone)] @@ -284,6 +295,14 @@ impl FieldElement { } } +/// Computes the multiplication of an element of fp2 by the level two non-residue 9+u. +pub fn mul_fp2_by_nonresidue(a: &Fp2E) -> Fp2E { + // (c0 + c1 * u) * (1 + u) = (c0 - c1) + (c1 + c0) * u + let c0 = &a.value()[0] - &a.value()[1]; // c0 - c1 + let c1 = &a.value()[0] + &a.value()[1]; // c1 + c0 + + Fp2E::new([c0, c1]) +} #[cfg(test)] mod tests { use crate::elliptic_curve::{ diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/pairing.rs b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/pairing.rs index b0c0c6cd2..8107f69ec 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/pairing.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/pairing.rs @@ -1,11 +1,12 @@ -use super::curve::MILLER_LOOP_CONSTANT; use super::{ curve::BLS12381Curve, - field_extension::{BLS12381PrimeField, Degree12ExtensionField, Degree2ExtensionField}, + field_extension::{ + mul_fp2_by_nonresidue, BLS12381PrimeField, Degree12ExtensionField, Degree2ExtensionField, + Degree4ExtensionField, + }, twist::BLS12381TwistCurve, }; use crate::{cyclic_group::IsGroup, elliptic_curve::traits::IsPairing, errors::PairingError}; - use crate::{ elliptic_curve::short_weierstrass::{ curves::bls12_381::field_extension::{Degree6ExtensionField, LevelTwoResidue}, @@ -13,11 +14,76 @@ use crate::{ traits::IsShortWeierstrass, }, field::{element::FieldElement, extensions::cubic::HasCubicNonResidue}, - unsigned_integer::element::{UnsignedInteger, U256}, + unsigned_integer::element::U256, }; +type FpE = FieldElement; +type Fp2E = FieldElement; +type Fp4E = FieldElement; +type Fp6E = FieldElement; +type Fp12E = FieldElement; + pub const SUBGROUP_ORDER: U256 = U256::from_hex_unchecked("73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001"); +// value of x in binary + +// We use |x|, then if needed we apply the minus sign in the final exponentiation by applying the inverse (in this case the conjugate because the element is in the cyclotomic subgroup) +pub const X: u64 = 0xd201000000010000; + +// X = 1101001000000001000000000000000000000000000000010000000000000000 +pub const X_BINARY: &[bool] = &[ + true, true, false, true, false, false, true, false, false, false, false, false, false, false, + false, true, false, false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, true, false, false, false, false, false, + false, false, false, false, false, false, false, false, false, false, false, +]; + +// GAMMA constants used to compute the Frobenius morphisms +/// We took these constants from https://github.com/hecmas/zkNotebook/blob/main/src/BLS12381/constants.ts + +pub const GAMMA_11: Fp2E = Fp2E::const_from_raw([ + FpE::from_hex_unchecked("1904D3BF02BB0667C231BEB4202C0D1F0FD603FD3CBD5F4F7B2443D784BAB9C4F67EA53D63E7813D8D0775ED92235FB8"), + FpE::from_hex_unchecked("FC3E2B36C4E03288E9E902231F9FB854A14787B6C7B36FEC0C8EC971F63C5F282D5AC14D6C7EC22CF78A126DDC4AF3"), +]); + +pub const GAMMA_12: Fp2E = Fp2E::const_from_raw([ + FpE::from_hex_unchecked("0"), + FpE::from_hex_unchecked("1A0111EA397FE699EC02408663D4DE85AA0D857D89759AD4897D29650FB85F9B409427EB4F49FFFD8BFD00000000AAAC"), +]); + +pub const GAMMA_13: Fp2E = Fp2E::const_from_raw([ + FpE::from_hex_unchecked("6AF0E0437FF400B6831E36D6BD17FFE48395DABC2D3435E77F76E17009241C5EE67992F72EC05F4C81084FBEDE3CC09"), + FpE::from_hex_unchecked("6AF0E0437FF400B6831E36D6BD17FFE48395DABC2D3435E77F76E17009241C5EE67992F72EC05F4C81084FBEDE3CC09"), +]); + +pub const GAMMA_14: Fp2E = Fp2E::const_from_raw([ + FpE::from_hex_unchecked("1A0111EA397FE699EC02408663D4DE85AA0D857D89759AD4897D29650FB85F9B409427EB4F49FFFD8BFD00000000AAAD"), + FpE::from_hex_unchecked("0"), +]); + +pub const GAMMA_15: Fp2E = Fp2E::const_from_raw([ + FpE::from_hex_unchecked("5B2CFD9013A5FD8DF47FA6B48B1E045F39816240C0B8FEE8BEADF4D8E9C0566C63A3E6E257F87329B18FAE980078116"), + FpE::from_hex_unchecked("144E4211384586C16BD3AD4AFA99CC9170DF3560E77982D0DB45F3536814F0BD5871C1908BD478CD1EE605167FF82995"), +]); + +/// GAMMA_2i = GAMMA_1i * GAMMA_1i.conjugate() +pub const GAMMA_21: FpE = FpE::from_hex_unchecked( + "5F19672FDF76CE51BA69C6076A0F77EADDB3A93BE6F89688DE17D813620A00022E01FFFFFFFEFFFF", +); + +pub const GAMMA_22: FpE = FpE::from_hex_unchecked( + "5F19672FDF76CE51BA69C6076A0F77EADDB3A93BE6F89688DE17D813620A00022E01FFFFFFFEFFFE", +); + +pub const GAMMA_23: FpE = + FpE::from_hex_unchecked("1A0111EA397FE69A4B1BA7B6434BACD764774B84F38512BF6730D2A0F6B0F6241EABFFFEB153FFFFB9FEFFFFFFFFAAAA"); + +pub const GAMMA_24: FpE = + FpE::from_hex_unchecked("1A0111EA397FE699EC02408663D4DE85AA0D857D89759AD4897D29650FB85F9B409427EB4F49FFFD8BFD00000000AAAC"); + +pub const GAMMA_25: FpE = + FpE::from_hex_unchecked("1A0111EA397FE699EC02408663D4DE85AA0D857D89759AD4897D29650FB85F9B409427EB4F49FFFD8BFD00000000AAAD"); #[derive(Clone)] pub struct BLS12381AtePairing; @@ -46,6 +112,26 @@ impl IsPairing for BLS12381AtePairing { } } +/// Implements the miller loop for the ate pairing of the BLS12 381 curve. +/// Based on algorithm 9.2, page 212 of the book +/// "Topics in computational number theory" by W. Bons and K. Lenstra +#[allow(unused)] +pub fn miller( + q: &ShortWeierstrassProjectivePoint, + p: &ShortWeierstrassProjectivePoint, +) -> FieldElement { + let mut r = q.clone(); + let mut f = FieldElement::::one(); + X_BINARY.iter().skip(1).for_each(|bit| { + double_accumulate_line(&mut r, p, &mut f); + if *bit { + add_accumulate_line(&mut r, q, p, &mut f); + } + }); + + f.conjugate() +} + fn double_accumulate_line( t: &mut ShortWeierstrassProjectivePoint, p: &ShortWeierstrassProjectivePoint, @@ -152,69 +238,148 @@ fn add_accumulate_line( ]), ]); } -/// Implements the miller loop for the ate pairing of the BLS12 381 curve. -/// Based on algorithm 9.2, page 212 of the book -/// "Topics in computational number theory" by W. Bons and K. Lenstra -#[allow(unused)] -fn miller( - q: &ShortWeierstrassProjectivePoint, - p: &ShortWeierstrassProjectivePoint, -) -> FieldElement { - let mut r = q.clone(); - let mut f = FieldElement::::one(); - let mut miller_loop_constant = MILLER_LOOP_CONSTANT; - let mut miller_loop_constant_bits: alloc::vec::Vec = alloc::vec![]; - while miller_loop_constant > 0 { - miller_loop_constant_bits.insert(0, (miller_loop_constant & 1) == 1); - miller_loop_constant >>= 1; - } +// To understand more about how to reduce the final exponentiation +// read "Efficient Final Exponentiation via Cyclotomic Structure for +// Pairings over Families of Elliptic Curves" (https://eprint.iacr.org/2020/875.pdf) +// Hard part from https://eprint.iacr.org/2020/875.pdf p14 +// Same implementation as in Constantine https://github.com/mratsim/constantine/blob/master/constantine/math/pairings/pairings_bls12.nim +pub fn final_exponentiation(f: &Fp12E) -> Fp12E { + let f_easy_aux = f.conjugate() * f.inv().unwrap(); + let mut f_easy = frobenius_square(&f_easy_aux) * &f_easy_aux; + + let mut v2 = cyclotomic_square(&f_easy); // v2 = f² + let mut v0 = cyclotomic_pow_x(&f_easy).conjugate(); // v0 = f^x + let mut v1 = f_easy.conjugate(); // v1 = f^-1 + + // (x−1)² + v0 *= v1; // v0 = f^(x-1) + v1 = cyclotomic_pow_x(&v0).conjugate(); // v1 = (f^(x-1))^(x) + + v0 = v0.conjugate(); // v0 = (f^(x-1))^(-1) + v0 *= &v1; // v0 = (f^(x-1))^(-1) * (f^(x-1))^x = (f^(x-1))^(x-1) = f^((x-1)²) + + // (x+p) + v1 = cyclotomic_pow_x(&v0).conjugate(); // v1 = f^((x-1)².x) + v0 = frobenius(&v0); // f^((x-1)².p) + v0 *= &v1; // f^((x-1)².p + (x-1)².x) = f^((x-1)².(x+p)) + + // + 3 + f_easy *= v2; // f^3 + + // (x²+p²−1) + v2 = cyclotomic_pow_x(&v0).conjugate(); + v1 = cyclotomic_pow_x(&v2).conjugate(); // v1 = f^((x-1)².(x+p).x²) + v2 = frobenius_square(&v0); // v2 = f^((x-1)².(x+p).p²) + v0 = v0.conjugate(); // v0 = f^((x-1)².(x+p).-1) + v0 *= &v1; // v0 = f^((x-1)².(x+p).(x²-1)) + v0 *= &v2; // v0 = f^((x-1)².(x+p).(x²+p²-1)) + + f_easy *= &v0; + f_easy +} - for bit in miller_loop_constant_bits[1..].iter() { - double_accumulate_line(&mut r, p, &mut f); - if *bit { - add_accumulate_line(&mut r, q, p, &mut f); - } - } - f.inv().unwrap() +////////////////// FROBENIUS MORPHISIMS ////////////////// +pub fn frobenius(f: &Fp12E) -> Fp12E { + let [a, b] = f.value(); // f = a + bw, where a and b in Fp6. + let [a0, a1, a2] = a.value(); // a = a0 + a1 * v + a2 * v^2, where a0, a1 and a2 in Fp2. + let [b0, b1, b2] = b.value(); // b = b0 + b1 * v + b2 * v^2, where b0, b1 and b2 in Fp2. + + // c1 = a0.conjugate() + a1.conjugate() * GAMMA_12 * v + a2.conjugate() * GAMMA_14 * v^2 + let c1 = Fp6E::new([ + a0.conjugate(), + a1.conjugate() * GAMMA_12, + a2.conjugate() * GAMMA_14, + ]); + + let c2 = Fp6E::new([ + b0.conjugate() * GAMMA_11, + b1.conjugate() * GAMMA_13, + b2.conjugate() * GAMMA_15, + ]); + + Fp12E::new([c1, c2]) //c1 + c2 * w } -/// Auxiliary function for the final exponentiation of the ate pairing. fn frobenius_square( f: &FieldElement, ) -> FieldElement { let [a, b] = f.value(); - let w_raised_to_p_squared_minus_one = FieldElement::::new_base("1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaad"); - let omega_3 = FieldElement::::new_base("1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac"); - let omega_3_squared = FieldElement::::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 c1 = Fp6E::new([a0.clone(), GAMMA_22 * a1, GAMMA_24 * a2]); + let c2 = Fp6E::new([GAMMA_21 * b0, GAMMA_23 * b1, GAMMA_25 * b2]); - FieldElement::new([f0, w_raised_to_p_squared_minus_one * f1]) + Fp12E::new([c1, c2]) } -// To understand more about how to reduce the final exponentiation -// read "Efficient Final Exponentiation via Cyclotomic Structure for -// Pairings over Families of Elliptic Curves" (https://eprint.iacr.org/2020/875.pdf) -// -// TODO: implement optimizations for the hard part of the final exponentiation. -#[allow(unused)] -fn final_exponentiation( - base: &FieldElement, -) -> FieldElement { - const PHI_DIVIDED_BY_R: UnsignedInteger<20> = UnsignedInteger::from_hex_unchecked("f686b3d807d01c0bd38c3195c899ed3cde88eeb996ca394506632528d6a9a2f230063cf081517f68f7764c28b6f8ae5a72bce8d63cb9f827eca0ba621315b2076995003fc77a17988f8761bdc51dc2378b9039096d1b767f17fcbde783765915c97f36c6f18212ed0b283ed237db421d160aeb6a1e79983774940996754c8c71a2629b0dea236905ce937335d5b68fa9912aae208ccf1e516c3f438e3ba79"); - - let f1 = base.conjugate() * base.inv().unwrap(); - let f2 = frobenius_square(&f1) * f1; - f2.pow(PHI_DIVIDED_BY_R) +////////////////// CYCLOTOMIC SUBGROUP OPERATIONS ////////////////// +/// Since the result of the Easy Part of the Final Exponentiation belongs to the cyclotomic +/// subgroup of Fp12, we can optimize the square and pow operations used in the Hard Part. + +/// Computes the square of an element of a cyclotomic subgroup of Fp12. +/// Algorithm from Constantine's cyclotomic_square_quad_over_cube +/// https://github.com/mratsim/constantine/blob/master/constantine/math/pairings/cyclotomic_subgroups.nim#L354 +pub fn cyclotomic_square(a: &Fp12E) -> Fp12E { + // a = g + h * w + let [g, h] = a.value(); + let [b0, b1, b2] = g.value(); + let [b3, b4, b5] = h.value(); + + let v0 = Fp4E::new([b0.clone(), b4.clone()]).square(); + let v1 = Fp4E::new([b3.clone(), b2.clone()]).square(); + let v2 = Fp4E::new([b1.clone(), b5.clone()]).square(); + + // r = r0 + r1 * w + // r0 = r00 + r01 * v + r02 * v^2 + // r1 = r10 + r11 * v + r12 * v^2 + + // r00 = 3v00 - 2b0 + let mut r00 = &v0.value()[0] - b0; + r00 = r00.double(); + r00 += v0.value()[0].clone(); + + // r01 = 3v10 -2b1 + let mut r01 = &v1.value()[0] - b1; + r01 = r01.double(); + r01 += v1.value()[0].clone(); + + // r11 = 3v01 - 2b4 + let mut r11 = &v0.value()[1] + b4; + r11 = r11.double(); + r11 += v0.value()[1].clone(); + + // r12 = 3v11 - 2b5 + let mut r12 = &v1.value()[1] + b5; + r12 = r12.double(); + r12 += v1.value()[1].clone(); + + // 3 * (9 + u) * v21 + 2b3 + let v21 = mul_fp2_by_nonresidue(&v2.value()[1]); + let mut r10 = &v21 + b3; + r10 = r10.double(); + r10 += v21; + + // 3 * (9 + u) * v20 - 2b3 + let mut r02 = &v2.value()[0] - b2; + r02 = r02.double(); + r02 += v2.value()[0].clone(); + + Fp12E::new([Fp6E::new([r00, r01, r02]), Fp6E::new([r10, r11, r12])]) } +#[allow(clippy::needless_range_loop)] +pub fn cyclotomic_pow_x(f: &Fp12E) -> Fp12E { + let mut result = Fp12E::one(); + X_BINARY.iter().for_each(|&bit| { + result = cyclotomic_square(&result); + if bit { + result = &result * f; + } + }); + result +} #[cfg(test)] mod tests { use crate::{ @@ -294,4 +459,58 @@ mod tests { let result = BLS12381AtePairing::compute_batch(&[(&p.to_affine(), &q)]); assert!(result.is_err()) } + #[test] + fn apply_12_times_frobenius_is_identity() { + let f = Fp12E::from_coefficients(&[ + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", + ]); + let mut result = frobenius(&f); + for _ in 1..12 { + result = frobenius(&result); + } + assert_eq!(f, result) + } + + #[test] + fn apply_6_times_frobenius_square_is_identity() { + let f = Fp12E::from_coefficients(&[ + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", + ]); + let mut result = frobenius_square(&f); + for _ in 1..6 { + result = frobenius_square(&result); + } + assert_eq!(f, result) + } + + #[test] + fn cyclotomic_square_equals_square() { + let p = BLS12381Curve::generator(); + let q = BLS12381TwistCurve::generator(); + let f = miller(&q, &p); + let f_easy_aux = f.conjugate() * f.inv().unwrap(); // f ^ (p^6 - 1) because f^(p^6) = f.conjugate(). + let f_easy = &frobenius_square(&f_easy_aux) * f_easy_aux; // (f^{p^6 - 1})^(p^2) * (f^{p^6 - 1}). + assert_eq!(cyclotomic_square(&f_easy), f_easy.square()); + } + + #[test] + fn test_double_accumulate_line_doubles_point_correctl_2() { + let g1 = BLS12381Curve::generator(); + let g2 = BLS12381TwistCurve::generator(); + let mut r = g2.clone(); + let mut f = FieldElement::one(); + double_accumulate_line(&mut r, &g1, &mut f); + let expected_r = g2.operate_with(&g2); + assert_eq!(r.to_affine(), expected_r.to_affine()); + } + + #[test] + fn cyclotomic_pow_x_equals_pow() { + let p = BLS12381Curve::generator(); + let q = BLS12381TwistCurve::generator(); + let f = miller(&q, &p); + let f_easy_aux = f.conjugate() * f.inv().unwrap(); // f ^ (p^6 - 1) because f^(p^6) = f.conjugate(). + let f_easy = &frobenius_square(&f_easy_aux) * f_easy_aux; // (f^{p^6 - 1})^(p^2) * (f^{p^6 - 1}). + assert_eq!(cyclotomic_pow_x(&f_easy), f_easy.pow(X)); + } } diff --git a/math/src/elliptic_curve/short_weierstrass/point.rs b/math/src/elliptic_curve/short_weierstrass/point.rs index 888d3f50a..679edeaea 100644 --- a/math/src/elliptic_curve/short_weierstrass/point.rs +++ b/math/src/elliptic_curve/short_weierstrass/point.rs @@ -52,6 +52,9 @@ impl ShortWeierstrassProjectivePoint { } pub fn double(&self) -> Self { + if self.is_neutral_element() { + return self.clone(); + } let [px, py, pz] = self.coordinates(); let px_square = px * px; @@ -86,11 +89,6 @@ impl ShortWeierstrassProjectivePoint { } // https://hyperelliptic.org/EFD/g1p/data/shortw/projective/addition/madd-1998-cmo pub fn operate_with_affine(&self, other: &Self) -> Self { - let [px, py, pz] = self.coordinates(); - let [qx, qy, _qz] = other.coordinates(); - let u = qy * pz; - let v = qx * pz; - if self.is_neutral_element() { return other.clone(); } @@ -98,6 +96,11 @@ impl ShortWeierstrassProjectivePoint { return self.clone(); } + let [px, py, pz] = self.coordinates(); + let [qx, qy, _qz] = other.coordinates(); + let u = qy * pz; + let v = qx * pz; + if u == *py { if v != *px || *py == FieldElement::zero() { return Self::new([