diff --git a/benchmarks/bench_pairing_template.nim b/benchmarks/bench_pairing_template.nim index 2f66548..a4c8a10 100644 --- a/benchmarks/bench_pairing_template.nim +++ b/benchmarks/bench_pairing_template.nim @@ -16,10 +16,9 @@ import # Internals ../constantine/config/[curves, common], ../constantine/arithmetic, - ../constantine/io/io_bigints, ../constantine/towers, - ../constantine/elliptic/[ec_shortweierstrass_projective, ec_shortweierstrass_affine], - ../constantine/hash_to_curve/cofactors, + ../constantine/ec_shortweierstrass, + ../constantine/curves/zoo_subgroups, ../constantine/pairing/[ cyclotomic_fp12, lines_projective, @@ -48,17 +47,17 @@ template bench(op: string, C: static Curve, iters: int, body: untyped): untyped measure(iters, startTime, stopTime, startClk, stopClk, body) report(op, $C, startTime, stopTime, startClk, stopClk, iters) -func clearCofactorReference[F; G: static Subgroup]( +func clearCofactor[F; G: static Subgroup]( ec: var ECP_ShortW_Aff[F, G]) = # For now we don't have any affine operation defined var t {.noInit.}: ECP_ShortW_Prj[F, G] t.projectiveFromAffine(ec) - t.clearCofactorReference() + t.clearCofactor() ec.affineFromProjective(t) func random_point*(rng: var RngState, EC: typedesc): EC {.noInit.} = result = rng.random_unsafe(EC) - result.clearCofactorReference() + result.clearCofactor() proc lineDoubleBench*(C: static Curve, iters: int) = var line: Line[Fp2[C]] diff --git a/benchmarks/bench_summary_template.nim b/benchmarks/bench_summary_template.nim index e1392a2..06f3611 100644 --- a/benchmarks/bench_summary_template.nim +++ b/benchmarks/bench_summary_template.nim @@ -21,7 +21,8 @@ import ec_shortweierstrass_projective, ec_shortweierstrass_jacobian, ec_scalar_mul, ec_endomorphism_accel], - ../constantine/hash_to_curve/[cofactors, hash_to_curve], + ../constantine/curves/zoo_subgroups, + ../constantine/hash_to_curve/hash_to_curve, ../constantine/pairing/[ cyclotomic_fp12, pairing_bls12, diff --git a/constantine.nimble b/constantine.nimble index 6e1b808..18a4d06 100644 --- a/constantine.nimble +++ b/constantine.nimble @@ -143,6 +143,19 @@ const testDesc: seq[tuple[path: string, useGMP: bool]] = @[ # Edge cases highlighted by past bugs # ---------------------------------------------------------- ("tests/t_ec_shortw_prj_edge_cases.nim", false), + + # Subgroups and cofactors + # ---------------------------------------------------------- + ("tests/t_ec_subgroups_bn254_nogami.nim", false), + ("tests/t_ec_subgroups_bn254_snarks.nim", false), + ("tests/t_ec_subgroups_bls12_377.nim", false), + ("tests/t_ec_subgroups_bls12_381.nim", false), + + ("tests/t_pairing_bn254_nogami_gt_subgroup.nim", false), + ("tests/t_pairing_bn254_snarks_gt_subgroup.nim", false), + ("tests/t_pairing_bls12_377_gt_subgroup.nim", false), + ("tests/t_pairing_bls12_381_gt_subgroup.nim", false), + # Pairing # ---------------------------------------------------------- # ("tests/t_pairing_bls12_377_line_functions.nim", false), diff --git a/constantine/curves/README.md b/constantine/curves/README.md index 7a84038..a009f85 100644 --- a/constantine/curves/README.md +++ b/constantine/curves/README.md @@ -4,6 +4,8 @@ This folder holds curve-specific constants and procedure in particular: - Inversion addition chains - Final exponentiation addition chains -- Square root constants for Tonelli Shanks +- Square root addition chains and constants for Tonelli Shanks - Lattice decomposition constants for endomorphism acceleration - Frobenius endomorphism constants +- Cofactor clearing +- Subgroup checks \ No newline at end of file diff --git a/constantine/curves/bls12_377_pairing.nim b/constantine/curves/bls12_377_pairing.nim index cc4be02..8e184ca 100644 --- a/constantine/curves/bls12_377_pairing.nim +++ b/constantine/curves/bls12_377_pairing.nim @@ -7,11 +7,12 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - ../config/[curves, type_bigint, type_ff], + ../config/[common, curves, type_bigint, type_ff], ../io/io_bigints, ../towers, ../elliptic/[ec_shortweierstrass_affine, ec_shortweierstrass_projective], - ../pairing/[cyclotomic_fp12, miller_loops] + ../pairing/[cyclotomic_fp12, miller_loops], + ../isogeny/frobenius # Slow generic implementation # ------------------------------------------------------------ @@ -84,3 +85,16 @@ func pow_x*(r: var Fp12[BLS12_377], a: Fp12[BLS12_377], invert = BLS12_377_pairi if invert: r.cyclotomic_inv() + +func isInPairingSubgroup*(a: Fp12[BLS12_377]): SecretBool = + ## Returns true if a is in GT subgroup, i.e. a is an element of order r + ## Warning ⚠: Assumes that a is in the cyclotomic subgroup + # Implementation: Scott, https://eprint.iacr.org/2021/1130.pdf + # A note on group membership tests for G1, G2 and GT + # on BLS pairing-friendly curves + # P is in the G1 subgroup iff a^p == a^u + var t0{.noInit.}, t1{.noInit.}: Fp12[BLS12_377] + t0.frobenius_map(a) + t1.pow_x(a) + + return t0 == t1 \ No newline at end of file diff --git a/constantine/curves/bls12_377_subgroups.nim b/constantine/curves/bls12_377_subgroups.nim new file mode 100644 index 0000000..1e13c7c --- /dev/null +++ b/constantine/curves/bls12_377_subgroups.nim @@ -0,0 +1,212 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + # Internals + ../config/[common, curves], + ../arithmetic, + ../primitives, + ../towers, + ../ec_shortweierstrass, + ../io/io_bigints, + ../isogeny/frobenius, + ../curves/zoo_endomorphisms + +func pow_bls12_377_abs_x[ECP: ECP_ShortW[Fp[BLS12_377], G1] or + ECP_ShortW[Fp2[BLS12_377], G2]]( + r{.noalias.}: var ECP, + P{.noalias.}: ECP + ) = + ## Does the scalar multiplication [x]P + ## with x the absolute value of the BLS12 curve parameter + ## For BLS12_377 [0x8508c00000000001]P + ## Requires r and P to not alias + r.double(P) + r += P + r.double() + r += P + let t111 = r + + r.double_repeated(2) + let t111000 = r + + r += t111 + let t100011 = r + + r.double() + r += t100011 + r += t111000 + + r.double_repeated(10) + r += t100011 + + r.double_repeated(46) + r += P + +func pow_bls12_377_x[ECP: ECP_ShortW[Fp[BLS12_377], G1] or + ECP_ShortW[Fp2[BLS12_377], G2]]( + r{.noalias.}: var ECP, + P{.noalias.}: ECP + ) {.inline.}= + ## Does the scalar multiplication [x]P + ## with x the BLS12 curve parameter + ## For BLS12_377 [0x8508c00000000001]P + ## Requires r and P to not alias + pow_bls12_377_abs_x(r, P) + +func pow_bls12_377_minus_x[ECP: ECP_ShortW[Fp[BLS12_377], G1] or + ECP_ShortW[Fp2[BLS12_377], G2]]( + r{.noalias.}: var ECP, + P{.noalias.}: ECP + ) {.inline.}= + ## Does the scalar multiplication [-x]P + ## with x the BLS12 curve parameter + ## For BLS12_377 [-0x8508c00000000001]P + ## Requires r and P to not alias + pow_bls12_377_abs_x(r, P) + r.neg() + +# ############################################################ +# +# Clear Cofactor - Naive +# +# ############################################################ + +const Cofactor_Eff_BLS12_377_G1 = BigInt[64].fromHex"0x8508c00000000000" + ## P -> (1 - x) P +const Cofactor_Eff_BLS12_377_G2 = BigInt[629].fromHex"0x1f60243677e30653648d3d9502abfba951764c46f4edd28f6ade35a5c7d769f7ee7c4b03103b45b85860aaaad2927678ba2796373885598e8e73ad8a538800cf664765b00000031e34800000000000" + ## P -> (x^2 - x - 1) P + (x - 1) ψ(P) + ψ(ψ(2P)) + ## + ## Effective cofactor from Budroni et al https://eprint.iacr.org/2017/419.pdf + ## (3x² − 3)*cofactor + +func clearCofactorReference*(P: var ECP_ShortW_Prj[Fp[BLS12_377], G1]) {.inline.} = + ## Clear the cofactor of BLS12_377 G1 + # Endomorphism acceleration cannot be used if cofactor is not cleared + P.scalarMulGeneric(Cofactor_Eff_BLS12_377_G1) + P.neg() + +func clearCofactorReference*(P: var ECP_ShortW_Prj[Fp2[BLS12_377], G2]) {.inline.} = + ## Clear the cofactor of BLS12_377 G2 + # Endomorphism acceleration cannot be used if cofactor is not cleared + P.scalarMulGeneric(Cofactor_Eff_BLS12_377_G2) + +# ############################################################ +# +# Clear Cofactor - Optimized +# +# ############################################################ + +# BLS12 G1 +# ------------------------------------------------------------ + +func clearCofactorFast*(P: var ECP_ShortW_Prj[Fp[BLS12_377], G1]) = + ## Clear the cofactor of BLS12_377 G1 + ## + ## Wahby et al "Fast and simple constant-time hashing to the BLS12-377 elliptic curve", https://eprint.iacr.org/2019/403 + ## Optimized using endomorphisms + ## P -> (1 - x) P + var t{.noInit.}: typeof(P) + t.pow_bls12_377_minus_x(P) # [-x]P + P += t # [1-x]P + +# BLS12 G2 +# ------------------------------------------------------------ +# From any point on the elliptic curve E2 of a BLS12 curve +# Obtain a point in the G2 prime-order subgroup +# +# Described in https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-G.4 +# +# Implementations, multiple implementations are possible in increasing order of speed: +# +# - The default, canonical, implementation is h_eff * P +# - Scott et al, "Fast Hashing to G2 on Pairing-Friendly Curves", https://doi.org/10.1007/978-3-642-03298-1_8 +# - Fuentes-Castaneda et al, "Fast Hashing to G2 on Pairing-Friendly Curves", https://doi.org/10.1007/978-3-642-28496-0_25 +# - Budroni et al, "Hashing to G2 on BLS pairing-friendly curves", https://doi.org/10.1145/3313880.3313884 +# - Wahby et al "Fast and simple constant-time hashing to the BLS12-377 elliptic curve", https://eprint.iacr.org/2019/403 +# - IETF "Hashing to Elliptic Curves", https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-G.4 +# +# In summary, the elliptic curve point multiplication is very expensive, +# the fast methods uses endomorphism acceleration instead. +# +# The method described in Wahby et al is implemented by Riad Wahby +# in C at: https://github.com/kwantam/bls12-377_hash/blob/23c1930039f58606138459557677668fabc8ce39/src/curve2/ops2.c#L106-L204 +# following Budroni et al, "Efficient hash maps to G2 on BLS curves" +# https://eprint.iacr.org/2017/419 +# +# "P -> [x² - x - 1] P + [x - 1] ψ(P) + ψ(ψ([2]P))" +# +# with Psi (ψ) - untwist-Frobenius-Twist function +# and x the curve BLS parameter + +func clearCofactorFast*(P: var ECP_ShortW_Prj[Fp2[BLS12_377], G2]) = + ## Clear the cofactor of BLS12_377 G2 + ## Optimized using endomorphisms + ## P -> [x²-x-1]P + [x-1] ψ(P) + ψ²([2]P) + + var xP{.noInit.}, x2P{.noInit.}: typeof(P) + + xP.pow_bls12_377_x(P) # 1. xP = [x]P + x2P.pow_bls12_377_x(xP) # 2. x2P = [x²]P + + x2P.diff(x2P, xP) # 3. x2P = [x²-x]P + x2P.diff(x2P, P) # 4. x2P = [x²-x-1]P + + xP.diff(xP, P) # 5. xP = [x-1]P + xP.frobenius_psi(xP) # 6. xP = ψ([x-1]P) = [x-1] ψ(P) + + P.double(P) # 7. P = [2]P + P.frobenius_psi(P, k=2) # 8. P = ψ²([2]P) + + P.sum(P, x2P) # 9. P = [x²-x-1]P + ψ²([2]P) + P.sum(P, xP) # 10. P = [x²-x-1]P + [x-1] ψ(P) + ψ²([2]P) + +# ############################################################ +# +# Subgroup checks +# +# ############################################################ + +func isInSubgroup*(P: ECP_ShortW_Prj[Fp[BLS12_377], G1]): SecretBool = + ## Returns true if P is in G1 subgroup, i.e. P is a point of order r. + ## A point may be on a curve but not on the prime order r subgroup. + ## Not checking subgroup exposes a protocol to small subgroup attacks. + ## + ## Warning ⚠: Assumes that P is on curve + # Implementation: Scott, https://eprint.iacr.org/2021/1130.pdf + # A note on group membership tests for G1, G2 and GT + # on BLS pairing-friendly curves + # P is in the G1 subgroup iff phi(P) == [-u²](P) + var t0{.noInit.}, t1{.noInit.}: ECP_ShortW_Prj[Fp[BLS12_377], G1] + + # [-u²]P + t0.pow_bls12_377_x(P) + t1.pow_bls12_377_minus_x(t0) + + # phi(P) + t0.x.prod(P.x, BLS12_377.getCubicRootOfUnity_mod_p()) + t0.y = P.y + t0.z = P.z + + return t0 == t1 + +func isInSubgroup*(P: ECP_ShortW_Prj[Fp2[BLS12_377], G2]): SecretBool = + ## Returns true if P is in G2 subgroup, i.e. P is a point of order r. + ## A point may be on a curve but not on the prime order r subgroup. + ## Not checking subgroup exposes a protocol to small subgroup attacks. + ## + ## Warning ⚠: Assumes that P is on curve + # Implementation: Scott, https://eprint.iacr.org/2021/1130.pdf + # A note on group membership tests for G1, G2 and GT + # on BLS pairing-friendly curves + # P is in the G1 subgroup iff ψ(P) == [u](P) + var t0{.noInit.}, t1{.noInit.}: ECP_ShortW_Prj[Fp2[BLS12_377], G2] + t0.pow_bls12_377_x(P) # [u]P + t1.frobenius_psi(P) # ψ(P) + + return t0 == t1 \ No newline at end of file diff --git a/constantine/curves/bls12_381_pairing.nim b/constantine/curves/bls12_381_pairing.nim index 2a8de04..3effbf3 100644 --- a/constantine/curves/bls12_381_pairing.nim +++ b/constantine/curves/bls12_381_pairing.nim @@ -7,11 +7,12 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - ../config/[curves, type_bigint, type_ff], + ../config/[common, curves, type_bigint, type_ff], ../io/io_bigints, ../towers, ../elliptic/[ec_shortweierstrass_affine, ec_shortweierstrass_projective], - ../pairing/[cyclotomic_fp12, miller_loops] + ../pairing/[cyclotomic_fp12, miller_loops], + ../isogeny/frobenius # Slow generic implementation # ------------------------------------------------------------ @@ -105,3 +106,16 @@ func pow_x*(r: var Fp12[BLS12_381], a: Fp12[BLS12_381], invert = BLS12_381_pairi ## For BLS12_381 f^-0xd201000000010000 r.pow_xdiv2(a, invert) r.cyclotomic_square() + +func isInPairingSubgroup*(a: Fp12[BLS12_381]): SecretBool = + ## Returns true if a is in GT subgroup, i.e. a is an element of order r + ## Warning ⚠: Assumes that a is in the cyclotomic subgroup + # Implementation: Scott, https://eprint.iacr.org/2021/1130.pdf + # A note on group membership tests for G1, G2 and GT + # on BLS pairing-friendly curves + # P is in the G1 subgroup iff a^p == a^u + var t0{.noInit.}, t1{.noInit.}: Fp12[BLS12_381] + t0.frobenius_map(a) + t1.pow_x(a) + + return t0 == t1 \ No newline at end of file diff --git a/constantine/hash_to_curve/cofactors.nim b/constantine/curves/bls12_381_subgroups.nim similarity index 56% rename from constantine/hash_to_curve/cofactors.nim rename to constantine/curves/bls12_381_subgroups.nim index 20f6da2..0d9d247 100644 --- a/constantine/hash_to_curve/cofactors.nim +++ b/constantine/curves/bls12_381_subgroups.nim @@ -8,14 +8,66 @@ import # Internals - ../config/common, + ../config/[common, curves], ../arithmetic, ../primitives, ../towers, - ../config/curves, + ../ec_shortweierstrass, ../io/io_bigints, - ../elliptic/[ec_shortweierstrass_projective, ec_scalar_mul], - ../isogeny/frobenius + ../isogeny/frobenius, + ../curves/zoo_endomorphisms + +func pow_bls12_381_abs_x[ECP: ECP_ShortW[Fp[BLS12_381], G1] or + ECP_ShortW[Fp2[BLS12_381], G2]]( + r{.noalias.}: var ECP, + P{.noalias.}: ECP + ) = + ## Does the scalar multiplication [x]P + ## with x the absolute value of the BLS12 curve parameter + ## For BLS12_381 [0xd201000000010000]P + ## Requires r and P to not alias + + # In binary + # 0b11 + r.double(P) + r += P + # 0b1101 + r.double_repeated(2) + r += P + # 0b1101001 + r.double_repeated(3) + r += P + # 0b1101001000000001 + r.double_repeated(9) + r += P + # 0b110100100000000100000000000000000000000000000001 + r.double_repeated(32) + r += P + # 0b1101001000000001000000000000000000000000000000010000000000000000 + r.double_repeated(16) + +func pow_bls12_381_x[ECP: ECP_ShortW[Fp[BLS12_381], G1] or + ECP_ShortW[Fp2[BLS12_381], G2]]( + r{.noalias.}: var ECP, + P{.noalias.}: ECP + ) {.inline.}= + ## Does the scalar multiplication [x]P + ## with x the BLS12 curve parameter + ## For BLS12_381 [-0xd201000000010000]P + ## Requires r and P to not alias + pow_bls12_381_abs_x(r, P) + r.neg() + +func pow_bls12_381_minus_x[ECP: ECP_ShortW[Fp[BLS12_381], G1] or + ECP_ShortW[Fp2[BLS12_381], G2]]( + r{.noalias.}: var ECP, + P{.noalias.}: ECP + ) {.inline.}= + ## Does the scalar multiplication [-x]P + ## with x the BLS12 curve parameter + ## For BLS12_381 [0xd201000000010000]P + ## Requires r and P to not alias + pow_bls12_381_abs_x(r, P) # ############################################################ # @@ -23,63 +75,12 @@ import # # ############################################################ -const Cofactor_Eff_BN254_Nogami_G1 = BigInt[1].fromHex"0x1" -const Cofactor_Eff_BN254_Nogami_G2 = BigInt[254].fromHex"0x2523648240000001ba344d8000000008c2a2800000000016ad00000000000019" - ## G2.order // r - -const Cofactor_Eff_BN254_Snarks_G1 = BigInt[1].fromHex"0x1" -const Cofactor_Eff_BN254_Snarks_G2 = BigInt[254].fromHex"0x30644e72e131a029b85045b68181585e06ceecda572a2489345f2299c0f9fa8d" - ## G2.order // r - -# TODO effective cofactors as per H2C draft like BLS12-381 curve -const Cofactor_Eff_BLS12_377_G1 = BigInt[125].fromHex"0x170b5d44300000000000000000000000" - ## P -> (1 - x) P -const Cofactor_Eff_BLS12_377_G2 = BigInt[502].fromHex"0x26ba558ae9562addd88d99a6f6a829fbb36b00e1dcc40c8c505634fae2e189d693e8c36676bd09a0f3622fba094800452217cc900000000000000000000001" - ## P -> (x^2 - x - 1) P + (x - 1) ψ(P) + ψ(ψ(2P)) - # https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-09#section-8.8 const Cofactor_Eff_BLS12_381_G1 = BigInt[64].fromHex"0xd201000000010001" ## P -> (1 - x) P const Cofactor_Eff_BLS12_381_G2 = BigInt[636].fromHex"0xbc69f08f2ee75b3584c6a0ea91b352888e2a8e9145ad7689986ff031508ffe1329c2f178731db956d82bf015d1212b02ec0ec69d7477c1ae954cbc06689f6a359894c0adebbf6b4e8020005aaa95551" ## P -> (x^2 - x - 1) P + (x - 1) ψ(P) + ψ(ψ(2P)) -# TODO https://eprint.iacr.org/2020/351.pdf p12 -const Cofactor_Eff_BW6_761_G1 = BigInt[384].fromHex"0xad1972339049ce762c77d5ac34cb12efc856a0853c9db94cc61c554757551c0c832ba4061000003b3de580000000007c" - ## P -> 103([u³]P)− 83([u²]P)−40([u]P)+136P + φ(7([u²]P)+89([u]P)+130P) - -# TODO https://eprint.iacr.org/2020/351.pdf p13 -const Cofactor_Eff_BW6_761_G2 = BigInt[384].fromHex"0xad1972339049ce762c77d5ac34cb12efc856a0853c9db94cc61c554757551c0c832ba4061000003b3de580000000007c" - ## P -> (103([u³]P) − 83([u²]P) − 143([u]P) + 27P) + ψ(7([u²]P) − 117([u]P) − 109P) - -func clearCofactorReference*(P: var ECP_ShortW_Prj[Fp[BN254_Nogami], G1]) {.inline.} = - ## Clear the cofactor of BN254_Nogami G1 - ## BN curve have a G1 cofactor of 1 so this is a no-op - discard - -func clearCofactorReference*(P: var ECP_ShortW_Prj[Fp2[BN254_Nogami], G2]) {.inline.} = - ## Clear the cofactor of BN254_Snarks G2 - # Endomorphism acceleration cannot be used if cofactor is not cleared - P.scalarMulGeneric(Cofactor_Eff_BN254_Nogami_G2) - -func clearCofactorReference*(P: var ECP_ShortW_Prj[Fp[BN254_Snarks], G1]) {.inline.} = - ## Clear the cofactor of BN254_Snarks G1 - ## BN curve have a G1 cofactor of 1 so this is a no-op - discard - -func clearCofactorReference*(P: var ECP_ShortW_Prj[Fp2[BN254_Snarks], G2]) {.inline.} = - ## Clear the cofactor of BN254_Snarks G2 - # Endomorphism acceleration cannot be used if cofactor is not cleared - P.scalarMulGeneric(Cofactor_Eff_BN254_Snarks_G2) - -func clearCofactorReference*(P: var ECP_ShortW_Prj[Fp[BLS12_377], G1]) {.inline.} = - ## Clear the cofactor of BLS12_377 G1 - P.scalarMulGeneric(Cofactor_Eff_BLS12_377_G1) - -func clearCofactorReference*(P: var ECP_ShortW_Prj[Fp2[BLS12_377], G2]) {.inline.} = - ## Clear the cofactor of BLS12_377 G2 - # Endomorphism acceleration cannot be used if cofactor is not cleared - P.scalarMulGeneric(Cofactor_Eff_BLS12_377_G2) - func clearCofactorReference*(P: var ECP_ShortW_Prj[Fp[BLS12_381], G1]) {.inline.} = ## Clear the cofactor of BLS12_381 G1 P.scalarMulGeneric(Cofactor_Eff_BLS12_381_G1) @@ -89,21 +90,25 @@ func clearCofactorReference*(P: var ECP_ShortW_Prj[Fp2[BLS12_381], G2]) {.inline # Endomorphism acceleration cannot be used if cofactor is not cleared P.scalarMulGeneric(Cofactor_Eff_BLS12_381_G2) -func clearCofactorReference*(P: var ECP_ShortW_Prj[Fp[BW6_761], G1]) {.inline.} = - ## Clear the cofactor of BW6_761 G1 - P.scalarMulGeneric(Cofactor_Eff_BW6_761_G1) - -func clearCofactorReference*(P: var ECP_ShortW_Prj[Fp[BW6_761], G2]) {.inline.} = - ## Clear the cofactor of BW6_761 G2 - # Endomorphism acceleration cannot be used if cofactor is not cleared - P.scalarMulGeneric(Cofactor_Eff_BW6_761_G2) - # ############################################################ # # Clear Cofactor - Optimized # # ############################################################ +# BLS12 G1 +# ------------------------------------------------------------ + +func clearCofactorFast*(P: var ECP_ShortW_Prj[Fp[BLS12_381], G1]) = + ## Clear the cofactor of BLS12_381 G1 + ## + ## Wahby et al "Fast and simple constant-time hashing to the BLS12-381 elliptic curve", https://eprint.iacr.org/2019/403 + ## Optimized using endomorphisms + ## P -> (1 - x) P + var t{.noInit.}: typeof(P) + t.pow_bls12_381_minus_x(P) # [-x]P + P += t # [1-x]P + # BLS12 G2 # ------------------------------------------------------------ # From any point on the elliptic curve E2 of a BLS12 curve @@ -133,43 +138,6 @@ func clearCofactorReference*(P: var ECP_ShortW_Prj[Fp[BW6_761], G2]) {.inline.} # with Psi (ψ) - untwist-Frobenius-Twist function # and x the curve BLS parameter -func double_repeated*[EC](P: var EC, num: int) {.inline.} = - ## Repeated doublings - for _ in 0 ..< num: - P.double() - -func pow_x( - r{.noalias.}: var ECP_ShortW_Prj[Fp2[BLS12_381], G2], - P{.noalias.}: ECP_ShortW_Prj[Fp2[BLS12_381], G2], - ) = - ## Does the scalar multiplication [x]P - ## with x the BLS12 curve parameter - ## For BLS12_381 [-0xd201000000010000]P - ## Requires r and P to not alias - - # In binary - # 0b11 - r.double(P) - r += P - # 0b1101 - r.double_repeated(2) - r += P - # 0b1101001 - r.double_repeated(3) - r += P - # 0b1101001000000001 - r.double_repeated(9) - r += P - # 0b110100100000000100000000000000000000000000000001 - r.double_repeated(32) - r += P - # 0b1101001000000001000000000000000000000000000000010000000000000000 - r.double_repeated(16) - - # Negative, x = -0xd201000000010000 - r.neg(r) - - func clearCofactorFast*(P: var ECP_ShortW_Prj[Fp2[BLS12_381], G2]) = ## Clear the cofactor of BLS12_381 G2 ## Optimized using endomorphisms @@ -177,8 +145,8 @@ func clearCofactorFast*(P: var ECP_ShortW_Prj[Fp2[BLS12_381], G2]) = var xP{.noInit.}, x2P{.noInit.}: typeof(P) - xP.pow_x(P) # 1. xP = [x]P - x2P.pow_x(xP) # 2. x2P = [x²]P + xP.pow_bls12_381_x(P) # 1. xP = [x]P + x2P.pow_bls12_381_x(xP) # 2. x2P = [x²]P x2P.diff(x2P, xP) # 3. x2P = [x²-x]P x2P.diff(x2P, P) # 4. x2P = [x²-x-1]P @@ -191,3 +159,48 @@ func clearCofactorFast*(P: var ECP_ShortW_Prj[Fp2[BLS12_381], G2]) = P.sum(P, x2P) # 9. P = [x²-x-1]P + ψ²([2]P) P.sum(P, xP) # 10. P = [x²-x-1]P + [x-1] ψ(P) + ψ²([2]P) + +# ############################################################ +# +# Subgroup checks +# +# ############################################################ + +func isInSubgroup*(P: ECP_ShortW_Prj[Fp[BLS12_381], G1]): SecretBool = + ## Returns true if P is in G1 subgroup, i.e. P is a point of order r. + ## A point may be on a curve but not on the prime order r subgroup. + ## Not checking subgroup exposes a protocol to small subgroup attacks. + ## + ## Warning ⚠: Assumes that P is on curve + # Implementation: Scott, https://eprint.iacr.org/2021/1130.pdf + # A note on group membership tests for G1, G2 and GT + # on BLS pairing-friendly curves + # P is in the G1 subgroup iff phi(P) == [-u²](P) + var t0{.noInit.}, t1{.noInit.}: ECP_ShortW_Prj[Fp[BLS12_381], G1] + + # [-u²]P + t0.pow_bls12_381_x(P) + t1.pow_bls12_381_minus_x(t0) + + # phi(P) + t0.x.prod(P.x, BLS12_381.getCubicRootOfUnity_mod_p()) + t0.y = P.y + t0.z = P.z + + return t0 == t1 + +func isInSubgroup*(P: ECP_ShortW_Prj[Fp2[BLS12_381], G2]): SecretBool = + ## Returns true if P is in G2 subgroup, i.e. P is a point of order r. + ## A point may be on a curve but not on the prime order r subgroup. + ## Not checking subgroup exposes a protocol to small subgroup attacks. + ## + ## Warning ⚠: Assumes that P is on curve + # Implementation: Scott, https://eprint.iacr.org/2021/1130.pdf + # A note on group membership tests for G1, G2 and GT + # on BLS pairing-friendly curves + # P is in the G1 subgroup iff ψ(P) == [u](P) + var t0{.noInit.}, t1{.noInit.}: ECP_ShortW_Prj[Fp2[BLS12_381], G2] + t0.pow_bls12_381_x(P) # [u]P + t1.frobenius_psi(P) # ψ(P) + + return t0 == t1 \ No newline at end of file diff --git a/constantine/curves/bn254_nogami_pairing.nim b/constantine/curves/bn254_nogami_pairing.nim index b2bab48..57245ef 100644 --- a/constantine/curves/bn254_nogami_pairing.nim +++ b/constantine/curves/bn254_nogami_pairing.nim @@ -7,11 +7,12 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - ../config/[curves, type_bigint, type_ff], + ../config/[common, curves, type_bigint, type_ff], ../io/io_bigints, ../towers, ../elliptic/[ec_shortweierstrass_affine, ec_shortweierstrass_projective], - ../pairing/[cyclotomic_fp12, miller_loops] + ../pairing/[cyclotomic_fp12, miller_loops], + ../isogeny/frobenius # Slow generic implementation # ------------------------------------------------------------ @@ -68,3 +69,21 @@ func pow_u*(r: var Fp12[BN254_Nogami], a: Fp12[BN254_Nogami], invert = BN254_Nog if invert: r.cyclotomic_inv() + +func isInPairingSubgroup*(a: Fp12[BN254_Nogami]): SecretBool = + ## Returns true if a is in GT subgroup, i.e. a is an element of order r + ## Warning ⚠: Assumes that a is in the cyclotomic subgroup + # Implementation: Scott, https://eprint.iacr.org/2021/1130.pdf + # A note on group membership tests for G1, G2 and GT + # on BLS pairing-friendly curves + # P is in the G1 subgroup iff a^p == a^(6u²) + var t0{.noInit.}, t1{.noInit.}: Fp12[BN254_Nogami] + t0.pow_u(a) # a^p + t1.pow_u(t0) # a^(p²) + t0.square(t1) # a^(2p²) + t0 *= t1 # a^(3p²) + t0.square() # a^(6p²) + + t1.frobenius_map(a) + + return t0 == t1 \ No newline at end of file diff --git a/constantine/curves/bn254_nogami_subgroups.nim b/constantine/curves/bn254_nogami_subgroups.nim new file mode 100644 index 0000000..d724e91 --- /dev/null +++ b/constantine/curves/bn254_nogami_subgroups.nim @@ -0,0 +1,153 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + # Internals + ../config/[common, curves], + ../arithmetic, + ../primitives, + ../towers, + ../ec_shortweierstrass, + ../io/io_bigints, + ../isogeny/frobenius + +func pow_BN254_Nogami_abs_u*[ECP: ECP_ShortW[Fp[BN254_Nogami], G1] or + ECP_ShortW[Fp2[BN254_Nogami], G2]]( + r{.noalias.}: var ECP, + P{.noalias.}: ECP + ) = + ## [u]P with u the curve parameter + ## For BN254_Nogami [0x4080000000000001]P + r.double(P) + r.double_repeated(6) + r += P + r.double_repeated(55) + r += P + +func pow_BN254_Nogami_u[ECP: ECP_ShortW[Fp[BN254_Nogami], G1] or + ECP_ShortW[Fp2[BN254_Nogami], G2]]( + r{.noalias.}: var ECP, + P{.noalias.}: ECP + ) {.inline.}= + ## Does the scalar multiplication [u]P + ## with u the BN curve parameter + pow_BN254_Nogami_abs_u(r, P) + r.neg() + +func pow_BN254_Nogami_minus_u[ECP: ECP_ShortW[Fp[BN254_Nogami], G1] or + ECP_ShortW[Fp2[BN254_Nogami], G2]]( + r{.noalias.}: var ECP, + P{.noalias.}: ECP + ) {.inline.}= + ## Does the scalar multiplication [-u]P + ## with u the BN curve parameter + pow_BN254_Nogami_abs_u(r, P) + +# ############################################################ +# +# Clear Cofactor - Naive +# +# ############################################################ + +const Cofactor_Eff_BN254_Nogami_G1 = BigInt[1].fromHex"0x1" +const Cofactor_Eff_BN254_Nogami_G2 = BigInt[444].fromHex"0xab11da940a5bd10e25327cb22360008556b23c24080002d6845e3404000009a4f95b60000000145460100000000018544800000000000c8" + # r = 36x⁴ + 36x³ + 18x² + 6x + 1 + # G2.order() = (36x⁴ + 36x³ + 18x² + 6x + 1)(36x⁴ + 36x³ + 30x² + 6x + 1) + # = r * cofactor + # Effective cofactor from Fuentes-Casteneda et al + # −(18x³ + 12x² + 3x + 1)*cofactor + +func clearCofactorReference*(P: var ECP_ShortW_Prj[Fp[BN254_Nogami], G1]) {.inline.} = + ## Clear the cofactor of BN254_Nogami G1 + ## BN curves have a G1 cofactor of 1 so this is a no-op + discard + +func clearCofactorReference*(P: var ECP_ShortW_Prj[Fp2[BN254_Nogami], G2]) {.inline.} = + ## Clear the cofactor of BN254_Nogami G2 + # Endomorphism acceleration cannot be used if cofactor is not cleared + P.scalarMulGeneric(Cofactor_Eff_BN254_Nogami_G2) + +# ############################################################ +# +# Clear Cofactor - Naive +# +# ############################################################ + +# BN G1 +# ------------------------------------------------------------ + +func clearCofactorFast*(P: var ECP_ShortW_Prj[Fp[BN254_Nogami], G1]) {.inline.} = + ## Clear the cofactor of BN254_Nogami G1 + ## BN curves have a prime order r hence all points on curve are in G1 + ## Hence this is a no-op + discard + +# BN G2 +# ------------------------------------------------------------ +# +# Implementation +# Fuentes-Castaneda et al, "Fast Hashing to G2 on Pairing-Friendly Curves", https://doi.org/10.1007/978-3-642-28496-0_25* + +func clearCofactorFast*(P: var ECP_ShortW_Prj[Fp2[BN254_Nogami], G2]) {.inline.} = + ## Clear the cofactor of BN254_Nogami G2 + ## Optimized using endomorphisms + ## P' → [x]P + [3x]ψ(P) + [x]ψ²(P) + ψ³(P) + var xP{.noInit.}, t{.noInit.}: typeof(P) + + xP.pow_BN254_Nogami_u(P) # xP = [x]P + t.frobenius_psi(P, 3) # t = ψ³(P) + P.double(xP) + P += xP + P.frobenius_psi(P) # P = [3x]ψ(P) + P += t # P = [3x]ψ(P) + ψ³(P) + t.frobenius_psi(xP, 2) # t = [x]ψ²(P) + P += xP # P = [x]P + [3x]ψ(P) + ψ³(P) + P += t # P = [x]P + [3x]ψ(P) + [x]ψ²(P) + ψ³(P) + +# ############################################################ +# +# Subgroup checks +# +# ############################################################ + +func isInSubgroup*(P: ECP_ShortW_Prj[Fp[BN254_Nogami], G1]): SecretBool {.inline.} = + ## Returns true if P is in G1 subgroup, i.e. P is a point of order r. + ## A point may be on a curve but not on the prime order r subgroup. + ## Not checking subgroup exposes a protocol to small subgroup attacks. + ## This is a no-op as on G1, all points are in the correct subgroup. + ## + ## Warning ⚠: Assumes that P is on curve + return CtTrue + +func isInSubgroup*(P: ECP_ShortW_Prj[Fp2[BN254_Nogami], G2]): SecretBool = + ## Returns true if P is in G2 subgroup, i.e. P is a point of order r. + ## A point may be on a curve but not on the prime order r subgroup. + ## Not checking subgroup exposes a protocol to small subgroup attacks. + # Implementation: Scott, https://eprint.iacr.org/2021/1130.pdf + # A note on group membership tests for G1, G2 and GT + # on BLS pairing-friendly curves + # + # The condition to apply the optimized endomorphism check on G₂ + # is gcd(h₁, h₂) == 1 with h₁ and h₂ the cofactors on G₁ and G₂. + # In that case [p]Q == [t-1]Q as r = p+1-t and [r]Q = 0 + # For BN curves h₁ = 1, hence Scott group membership tests can be used for BN curves + # + # p the prime modulus: 36u⁴ + 36u³ + 24u² + 6u + 1 + # r the prime order: 36u⁴ + 36u³ + 18u² + 6u + 1 + # t the trace: 6u² + 1 + var t0{.noInit.}, t1{.noInit.}: ECP_ShortW_Prj[Fp2[BN254_Nogami], G2] + + t0.pow_BN254_Nogami_u(P) # [u]P + t1.pow_BN254_Nogami_u(t0) # [u²]P + t0.double(t1) # [2u²]P + t0 += t1 # [3u²]P + t0.double() # [6u²]P + + t1.frobenius_psi(P) # ψ(P) + + return t0 == t1 \ No newline at end of file diff --git a/constantine/curves/bn254_snarks_pairing.nim b/constantine/curves/bn254_snarks_pairing.nim index 3c7cce7..dc681b8 100644 --- a/constantine/curves/bn254_snarks_pairing.nim +++ b/constantine/curves/bn254_snarks_pairing.nim @@ -7,10 +7,12 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - ../config/[curves, type_bigint], + ../config/[common, curves, type_bigint, type_ff], ../io/io_bigints, ../towers, - ../pairing/cyclotomic_fp12 + ../elliptic/[ec_shortweierstrass_affine, ec_shortweierstrass_projective], + ../pairing/[cyclotomic_fp12, miller_loops], + ../isogeny/frobenius # Slow generic implementation # ------------------------------------------------------------ @@ -121,3 +123,21 @@ func pow_u*(r: var Fp12[BN254_Snarks], a: Fp12[BN254_Snarks], invert = BN254_Sna if invert: r.cyclotomic_inv() + +func isInPairingSubgroup*(a: Fp12[BN254_Snarks]): SecretBool = + ## Returns true if a is in GT subgroup, i.e. a is an element of order r + ## Warning ⚠: Assumes that a is in the cyclotomic subgroup + # Implementation: Scott, https://eprint.iacr.org/2021/1130.pdf + # A note on group membership tests for G1, G2 and GT + # on BLS pairing-friendly curves + # P is in the G1 subgroup iff a^p == a^(6u²) + var t0{.noInit.}, t1{.noInit.}: Fp12[BN254_Snarks] + t0.pow_u(a) # a^p + t1.pow_u(t0) # a^(p²) + t0.square(t1) # a^(2p²) + t0 *= t1 # a^(3p²) + t0.square() # a^(6p²) + + t1.frobenius_map(a) + + return t0 == t1 \ No newline at end of file diff --git a/constantine/curves/bn254_snarks_subgroups.nim b/constantine/curves/bn254_snarks_subgroups.nim new file mode 100644 index 0000000..fcff531 --- /dev/null +++ b/constantine/curves/bn254_snarks_subgroups.nim @@ -0,0 +1,218 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + # Internals + ../config/[common, curves], + ../arithmetic, + ../primitives, + ../towers, + ../ec_shortweierstrass, + ../io/io_bigints, + ../isogeny/frobenius + +func pow_bn254_snarks_abs_u*[ECP: ECP_ShortW[Fp[BN254_Snarks], G1] or + ECP_ShortW[Fp2[BN254_Snarks], G2]]( + r{.noalias.}: var ECP, + P{.noalias.}: ECP + ) = + ## [u]P with u the curve parameter + ## For BN254_Snarks [0x44e992b44a6909f1]P + + var # Hopefully the compiler optimizes away unused ECP as those are large + x10 {.noInit.}: ECP + x11 {.noInit.}: ECP + x100 {.noInit.}: ECP + x110 {.noInit.}: ECP + x1100 {.noInit.}: ECP + x1111 {.noInit.}: ECP + x10010 {.noInit.}: ECP + x10110 {.noInit.}: ECP + x11100 {.noInit.}: ECP + x101110 {.noInit.}: ECP + x1001010 {.noInit.}: ECP + x1111000 {.noInit.}: ECP + x10001110 {.noInit.}: ECP + + x10 .double(P) + x11 .sum(x10, P) + x100 .sum(x11, P) + x110 .sum(x10, x100) + x1100 .double(x110) + x1111 .sum(x11, x1100) + x10010 .sum(x11, x1111) + x10110 .sum(x100, x10010) + x11100 .sum(x110, x10110) + x101110 .sum(x10010, x11100) + x1001010 .sum(x11100, x101110) + x1111000 .sum(x101110, x1001010) + x10001110 .sum(x10110, x1111000) + + var + r15 {.noInit.}: ECP + r16 {.noInit.}: ECP + r17 {.noInit.}: ECP + r18 {.noInit.}: ECP + r20 {.noInit.}: ECP + r21 {.noInit.}: ECP + r22 {.noInit.}: ECP + r26 {.noInit.}: ECP + r27 {.noInit.}: ECP + r61 {.noInit.}: ECP + + r15.double(x10001110) + r15 += x1001010 + r16.sum(x10001110, r15) + r17.sum(x1111, r16) + r18.sum(r16, r17) + + r20.double(r18) + r20 += r17 + r21.sum(x1111000, r20) + r22.sum(r15, r21) + + r26.double(r22) + r26.double() + r26 += r22 + r26 += r18 + + r27.sum(r22, r26) + + r61.sum(r26, r27) + r61.double_repeated(17) + r61 += r27 + r61.double_repeated(14) + r61 += r21 + + r = r61 + r.double_repeated(16) + r += r20 + +func pow_bn254_snarks_u[ECP: ECP_ShortW[Fp[BN254_Snarks], G1] or + ECP_ShortW[Fp2[BN254_Snarks], G2]]( + r{.noalias.}: var ECP, + P{.noalias.}: ECP + ) {.inline.}= + ## Does the scalar multiplication [u]P + ## with u the BN curve parameter + pow_bn254_snarks_abs_u(r, P) + +func pow_bn254_snarks_minus_u[ECP: ECP_ShortW[Fp[BN254_Snarks], G1] or + ECP_ShortW[Fp2[BN254_Snarks], G2]]( + r{.noalias.}: var ECP, + P{.noalias.}: ECP + ) {.inline.}= + ## Does the scalar multiplication [-u]P + ## with the BN curve parameter + pow_bn254_snarks_abs_u(r, P) + r.neg() + +# ############################################################ +# +# Clear Cofactor - Naive +# +# ############################################################ + +const Cofactor_Eff_BN254_Snarks_G1 = BigInt[1].fromHex"0x1" +const Cofactor_Eff_BN254_Snarks_G2 = BigInt[445].fromHex"0x10fdac342d9d118eaade453b741519b8e1d63b3400132e99468a9c2b25de5b5f1bf35b43bcc5da2335a0d8a112d43476616edcfabef338ea" + # r = 36x⁴ + 36x³ + 18x² + 6x + 1 + # G2.order() = (36x⁴ + 36x³ + 18x² + 6x + 1)(36x⁴ + 36x³ + 30x² + 6x + 1) + # = r * cofactor + # Effective cofactor from Fuentes-Casteneda et al + # -(18x³ + 12x² + 3x + 1)*cofactor + +func clearCofactorReference*(P: var ECP_ShortW_Prj[Fp[BN254_Snarks], G1]) {.inline.} = + ## Clear the cofactor of BN254_Snarks G1 + ## BN curves have a G1 cofactor of 1 so this is a no-op + discard + +func clearCofactorReference*(P: var ECP_ShortW_Prj[Fp2[BN254_Snarks], G2]) {.inline.} = + ## Clear the cofactor of BN254_Snarks G2 + # Endomorphism acceleration cannot be used if cofactor is not cleared + P.scalarMulGeneric(Cofactor_Eff_BN254_Snarks_G2) + P.neg() + +# ############################################################ +# +# Clear Cofactor - Naive +# +# ############################################################ + +# BN G1 +# ------------------------------------------------------------ + +func clearCofactorFast*(P: var ECP_ShortW_Prj[Fp[BN254_Snarks], G1]) {.inline.} = + ## Clear the cofactor of BN254_Snarks G1 + ## BN curves have a prime order r hence all points on curve are in G1 + ## Hence this is a no-op + discard + +# BN G2 +# ------------------------------------------------------------ +# +# Implementation +# Fuentes-Castaneda et al, "Fast Hashing to G2 on Pairing-Friendly Curves", https://doi.org/10.1007/978-3-642-28496-0_25* + +func clearCofactorFast*(P: var ECP_ShortW_Prj[Fp2[BN254_Snarks], G2]) {.inline.} = + ## Clear the cofactor of BN254_Snarks G2 + ## Optimized using endomorphisms + ## P' → [x]P + [3x]ψ(P) + [x]ψ²(P) + ψ³(P) + var xP{.noInit.}, t{.noInit.}: typeof(P) + + xP.pow_bn254_snarks_u(P) # xP = [x]P + t.frobenius_psi(P, 3) # t = ψ³(P) + P.double(xP) + P += xP + P.frobenius_psi(P) # P = [3x]ψ(P) + P += t # P = [3x]ψ(P) + ψ³(P) + t.frobenius_psi(xP, 2) # t = [x]ψ²(P) + P += xP # P = [x]P + [3x]ψ(P) + ψ³(P) + P += t # P = [x]P + [3x]ψ(P) + [x]ψ²(P) + ψ³(P) + +# ############################################################ +# +# Subgroup checks +# +# ############################################################ + +func isInSubgroup*(P: ECP_ShortW_Prj[Fp[BN254_Snarks], G1]): SecretBool {.inline.} = + ## Returns true if P is in G1 subgroup, i.e. P is a point of order r. + ## A point may be on a curve but not on the prime order r subgroup. + ## Not checking subgroup exposes a protocol to small subgroup attacks. + ## This is a no-op as on G1, all points are in the correct subgroup. + ## + ## Warning ⚠: Assumes that P is on curve + return CtTrue + +func isInSubgroup*(P: ECP_ShortW_Prj[Fp2[BN254_Snarks], G2]): SecretBool = + ## Returns true if P is in G2 subgroup, i.e. P is a point of order r. + ## A point may be on a curve but not on the prime order r subgroup. + ## Not checking subgroup exposes a protocol to small subgroup attacks. + # Implementation: Scott, https://eprint.iacr.org/2021/1130.pdf + # A note on group membership tests for G1, G2 and GT + # on BLS pairing-friendly curves + # + # The condition to apply the optimized endomorphism check on G₂ + # is gcd(h₁, h₂) == 1 with h₁ and h₂ the cofactors on G₁ and G₂. + # In that case [p]Q == [t-1]Q as r = p+1-t and [r]Q = 0 + # For BN curves h₁ = 1, hence Scott group membership tests can be used for BN curves + # + # p the prime modulus: 36u⁴ + 36u³ + 24u² + 6u + 1 + # r the prime order: 36u⁴ + 36u³ + 18u² + 6u + 1 + # t the trace: 6u² + 1 + var t0{.noInit.}, t1{.noInit.}: ECP_ShortW_Prj[Fp2[BN254_Snarks], G2] + + t0.pow_bn254_snarks_u(P) # [u]P + t1.pow_bn254_snarks_u(t0) # [u²]P + t0.double(t1) # [2u²]P + t0 += t1 # [3u²]P + t0.double() # [6u²]P + + t1.frobenius_psi(P) # ψ(P) + + return t0 == t1 \ No newline at end of file diff --git a/constantine/curves/bw6_761_subgroups.nim b/constantine/curves/bw6_761_subgroups.nim new file mode 100644 index 0000000..694c2f2 --- /dev/null +++ b/constantine/curves/bw6_761_subgroups.nim @@ -0,0 +1,40 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + # Internals + ../config/[common, curves], + ../arithmetic, + ../primitives, + ../towers, + ../ec_shortweierstrass, + ../io/io_bigints, + ../isogeny/frobenius + +# ############################################################ +# +# Clear Cofactor - Naive +# +# ############################################################ + +# TODO https://eprint.iacr.org/2020/351.pdf p12 +const Cofactor_Eff_BW6_761_G1 = BigInt[384].fromHex"0xad1972339049ce762c77d5ac34cb12efc856a0853c9db94cc61c554757551c0c832ba4061000003b3de580000000007c" + ## P -> 103([u³]P)− 83([u²]P)−40([u]P)+136P + φ(7([u²]P)+89([u]P)+130P) + +# TODO https://eprint.iacr.org/2020/351.pdf p13 +const Cofactor_Eff_BW6_761_G2 = BigInt[384].fromHex"0xad1972339049ce762c77d5ac34cb12efc856a0853c9db94cc61c554757551c0c832ba4061000003b3de580000000007c" + ## P -> (103([u³]P) − 83([u²]P) − 143([u]P) + 27P) + ψ(7([u²]P) − 117([u]P) − 109P) + +func clearCofactorReference*(P: var ECP_ShortW_Prj[Fp[BW6_761], G1]) {.inline.} = + ## Clear the cofactor of BW6_761 G1 + P.scalarMulGeneric(Cofactor_Eff_BW6_761_G1) + +func clearCofactorReference*(P: var ECP_ShortW_Prj[Fp[BW6_761], G2]) {.inline.} = + ## Clear the cofactor of BW6_761 G2 + # Endomorphism acceleration cannot be used if cofactor is not cleared + P.scalarMulGeneric(Cofactor_Eff_BW6_761_G2) \ No newline at end of file diff --git a/constantine/curves/zoo_pairings.nim b/constantine/curves/zoo_pairings.nim index 0f21921..077f654 100644 --- a/constantine/curves/zoo_pairings.nim +++ b/constantine/curves/zoo_pairings.nim @@ -21,4 +21,4 @@ macro pairing*(C: static Curve, value: untyped): untyped = ## Get pairing related constants return bindSym($C & "_pairing_" & $value) -export pow_x, pow_xdiv2, pow_u, millerLoopAddchain +export pow_x, pow_xdiv2, pow_u, millerLoopAddchain, isInPairingSubgroup diff --git a/constantine/curves/zoo_subgroups.nim b/constantine/curves/zoo_subgroups.nim new file mode 100644 index 0000000..7c1ce68 --- /dev/null +++ b/constantine/curves/zoo_subgroups.nim @@ -0,0 +1,31 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + # Internals + ../config/curves, + ./bls12_377_subgroups, + ./bls12_381_subgroups, + ./bn254_nogami_subgroups, + ./bn254_snarks_subgroups, + ./bw6_761_subgroups + +export + bls12_377_subgroups, + bls12_381_subgroups, + bn254_nogami_subgroups, + bn254_snarks_subgroups, + bw6_761_subgroups + +func clearCofactor*[ECP](P: var ECP) {.inline.} = + ## Clear the cofactor of a point on the curve + ## From a point on the curve, returns a point on the subgroup of order r + when ECP.F.C in {BLS12_381}: + P.clearCofactorFast() + else: + P.clearCofactorReference() diff --git a/constantine/ec_shortweierstrass.nim b/constantine/ec_shortweierstrass.nim index 4b53648..ba3fc97 100644 --- a/constantine/ec_shortweierstrass.nim +++ b/constantine/ec_shortweierstrass.nim @@ -22,6 +22,8 @@ import export ec_shortweierstrass_affine, ec_shortweierstrass_jacobian, ec_shortweierstrass_projective, ec_scalar_mul +type ECP_ShortW*[F; G: static Subgroup] = ECP_ShortW_Aff[F, G] | ECP_ShortW_Jac[F, G] | ECP_ShortW_Prj[F, G] + func projectiveFromJacobian*[F; G]( prj: var ECP_ShortW_Prj[F, G], jac: ECP_ShortW_Jac[F, G]) {.inline.} = @@ -30,3 +32,7 @@ func projectiveFromJacobian*[F; G]( prj.z.square(jac.z) prj.z *= jac.z +func double_repeated*(P: var ECP_ShortW, num: int) {.inline.} = + ## Repeated doublings + for _ in 0 ..< num: + P.double() diff --git a/constantine/hash_to_curve/hash_to_curve.nim b/constantine/hash_to_curve/hash_to_curve.nim index 4af440a..752c7ba 100644 --- a/constantine/hash_to_curve/hash_to_curve.nim +++ b/constantine/hash_to_curve/hash_to_curve.nim @@ -10,11 +10,10 @@ import # Internals ../config/[common, curves], ../primitives, ../arithmetic, ../towers, - ../curves/zoo_hash_to_curve, + ../curves/[zoo_hash_to_curve, zoo_subgroups], ../ec_shortweierstrass, ./h2c_hash_to_field, ./h2c_map_to_isocurve_swu, - ./cofactors, ../isogeny/h2c_isogeny_maps, ../hashes @@ -166,4 +165,4 @@ func hashToCurve*[ Pjac.mapToCurve_fusedAdd(u[0], u[1]) output.projectiveFromJacobian(Pjac) - output.clearCofactorFast() + output.clearCofactor() diff --git a/constantine/pairing/cyclotomic_fp12.nim b/constantine/pairing/cyclotomic_fp12.nim index 6be21ce..8d2a142 100644 --- a/constantine/pairing/cyclotomic_fp12.nim +++ b/constantine/pairing/cyclotomic_fp12.nim @@ -220,3 +220,14 @@ func cyclotomic_exp*[C](r: var Fp12[C], a: Fp12[C], exponent: BigInt, invert: bo r *= a if invert: r.cyclotomic_inv() + +func isInCyclotomicSubgroup*[C](a: Fp12[C]): SecretBool = + ## Check if a ∈ Fpⁿ: a^Φₙ(p) = 1 + ## Φ₁₂(p) = p⁴-p²+1 + var t{.noInit.}, p2{.noInit.}: Fp12[C] + + p2.frobenius_map(a, 2) # a^(p²) + t.frobenius_map(p2, 2) # a^(p⁴) + t *= a # a^(p⁴+1) + + return t == p2 \ No newline at end of file diff --git a/constantine/protocols/ethereum_evm_precompiles.nim b/constantine/protocols/ethereum_evm_precompiles.nim index 6acf275..727469a 100644 --- a/constantine/protocols/ethereum_evm_precompiles.nim +++ b/constantine/protocols/ethereum_evm_precompiles.nim @@ -12,6 +12,7 @@ import ../arithmetic/limbs_montgomery, ../ec_shortweierstrass, ../pairing/[pairing_bn, miller_loops, cyclotomic_fp12], + ../curves/zoo_subgroups, ../io/[io_bigints, io_fields] # ############################################################ @@ -219,27 +220,9 @@ func subgroupCheck(P: ECP_ShortW_Aff[Fp2[BN254_Snarks], G2]): bool = ## A point may be on a curve but in case the curve has a cofactor != 1 ## that point may not be in the correct cyclic subgroup. ## If we are on the subgroup of order r then [r]P = 0 - - # TODO: Generic for any curve - var Q{.noInit.}: ECP_ShortW_Prj[Fp2[BN254_Snarks], G2] - - # TODO: precompute up to the endomorphism decomposition - # or implement fixed base scalar mul - # as subgroup checks are a deserialization bottleneck - var rm1 = Fr[BN254_Snarks].fieldMod() - rm1 -= One - - # We can't use endomorphism acceleration when multiplying - # by the curve order r to check [r]P == 0 - # as it requires the scalar to be < r. - # But we can use it to multiply by [r-1]. Q.projectiveFromAffine(P) - let Q0 = Q - Q.scalarMul(rm1) - Q += Q0 - - return bool(Q.isInf()) + return bool(Q.isInSubgroup()) func fromRawCoords( dst: var ECP_ShortW_Aff[Fp[BN254_Snarks], G1], diff --git a/docs/optimizations.md b/docs/optimizations.md index a5f7f4e..e139436 100644 --- a/docs/optimizations.md +++ b/docs/optimizations.md @@ -67,12 +67,12 @@ The optimizations can be of algebraic, algorithmic or "implementation details" n - [x] FIPS (Finely Integrated Operand Scanning) - Montgomery Squaring - - [ ] Dedicated squaring functions - - [ ] Fused multiply + reduce + - [x] Dedicated squaring functions + - [x] Fused multiply + reduce - [ ] int128 - [ ] loop unrolling - - [ ] x86: Full Assembly implementation - - [ ] x86: MULX, ADCX, ADOX instructions + - [x] x86: Full Assembly implementation + - [x] x86: MULX, ADCX, ADOX instructions - [ ] no-carry optimization for CIOS (Coarsely Integrated Operand Scanning) - Addition chains @@ -97,7 +97,7 @@ The optimizations can be of algebraic, algorithmic or "implementation details" n - Square Root (constant-time) - [x] baseline sqrt via Little-Fermat for `p ≡ 3 (mod 4)` - - [ ] baseline sqrt via Little-Fermat for `p ≡ 5 (mod 8)` + - [x] baseline sqrt via Little-Fermat for `p ≡ 5 (mod 8)` - [ ] baseline sqrt via Little-Fermat for `p ≡ 9 (mod 16)` - [x] baseline sqrt via Tonelli-Shanks for any prime. - [x] sqrt via addition-chain @@ -211,3 +211,5 @@ The optimizations can be of algebraic, algorithmic or "implementation details" n - Subgroup check - [ ] BLS G1: Bowe, endomorphism accelerated - [ ] BLS G2: Bowe, endomorphism accelerated + - [x] BLS G1: Scott, endomorphism accelerated + - [x] BLS G2: Scott, endomorphism accelerated \ No newline at end of file diff --git a/metering/README.md b/metering/README.md index 1d038b5..cba2a6f 100644 --- a/metering/README.md +++ b/metering/README.md @@ -30,7 +30,7 @@ echo "bench xoshiro512** seed: ", seed func random_point*(rng: var RngState, EC: typedesc): EC {.noInit.} = result = rng.random_unsafe(EC) - result.clearCofactorReference() + result.clearCofactor() proc pairingBLS12Meter*(C: static Curve) = let diff --git a/metering/m_pairings.nim b/metering/m_pairings.nim index dcc6911..7a2454a 100644 --- a/metering/m_pairings.nim +++ b/metering/m_pairings.nim @@ -12,7 +12,7 @@ import ../constantine/config/[common, curves], ../constantine/[arithmetic, towers], ../constantine/elliptic/ec_shortweierstrass_projective, - ../constantine/hash_to_curve/cofactors, + ../constantine/curves/zoo_subgroups, ../constantine/pairing/pairing_bls12, # Helpers ../helpers/prng_unsafe @@ -24,7 +24,7 @@ echo "bench xoshiro512** seed: ", seed func random_point*(rng: var RngState, EC: typedesc): EC {.noInit.} = result = rng.random_unsafe(EC) - result.clearCofactorReference() + result.clearCofactor() proc pairingBLS12Meter*(C: static Curve) = let diff --git a/research/bw6_761/pairing_bw6_761.nim b/research/bw6_761/pairing_bw6_761.nim index c0d2625..52e7d29 100644 --- a/research/bw6_761/pairing_bw6_761.nim +++ b/research/bw6_761/pairing_bw6_761.nim @@ -33,14 +33,14 @@ import func millerLoopBW6_761_naive[C]( f: var Fp6[C], - P: ECP_ShortW_Aff[Fp[C], NotOnTwist], - Q: ECP_ShortW_Aff[Fp[C], OnTwist] + P: ECP_ShortW_Aff[Fp[C], G1], + Q: ECP_ShortW_Aff[Fp[C], G2] ) = ## Miller Loop for BW6_761 curve ## Computes f_{u+1,Q}(P)*Frobenius(f_{u*(u^2-u-1),Q}(P)) var - T {.noInit.}: ECP_ShortW_Prj[Fp[C], OnTwist] + T {.noInit.}: ECP_ShortW_Prj[Fp[C], G2] line {.noInit.}: Line[Fp[C]] nQ{.noInit.}: typeof(Q) @@ -76,15 +76,15 @@ func finalExpGeneric[C: static Curve](f: var Fp6[C]) = func millerLoopBW6_761_opt_to_debug[C]( f: var Fp6[C], - P: ECP_ShortW_Aff[Fp[C], NotOnTwist], - Q: ECP_ShortW_Aff[Fp[C], OnTwist] + P: ECP_ShortW_Aff[Fp[C], G1], + Q: ECP_ShortW_Aff[Fp[C], G2] ) {.used.} = ## Miller Loop Otpimized for BW6_761 curve # 1st part: f_{u,Q}(P) # ------------------------------ var - T {.noInit.}: ECP_ShortW_Prj[Fp[C], OnTwist] + T {.noInit.}: ECP_ShortW_Prj[Fp[C], G2] line {.noInit.}: Line[Fp[C]] T.projectiveFromAffine(Q) @@ -150,15 +150,15 @@ func millerLoopBW6_761_opt_to_debug[C]( func pairing_bw6_761_reference*[C]( gt: var Fp6[C], - P: ECP_ShortW_Prj[Fp[C], NotOnTwist], - Q: ECP_ShortW_Prj[Fp[C], OnTwist]) = + P: ECP_ShortW_Prj[Fp[C], G1], + Q: ECP_ShortW_Prj[Fp[C], G2]) = ## Compute the optimal Ate Pairing for BW6 curves ## Input: P ∈ G1, Q ∈ G2 ## Output: e(P, Q) ∈ Gt ## ## Reference implementation - var Paff {.noInit.}: ECP_ShortW_Aff[Fp[C], NotOnTwist] - var Qaff {.noInit.}: ECP_ShortW_Aff[Fp[C], OnTwist] + var Paff {.noInit.}: ECP_ShortW_Aff[Fp[C], G1] + var Qaff {.noInit.}: ECP_ShortW_Aff[Fp[C], G2] Paff.affineFromProjective(P) Qaff.affineFromProjective(Q) gt.millerLoopBW6_761_naive(Paff, Qaff) diff --git a/tests/t_ec_subgroups_bls12_377.nim b/tests/t_ec_subgroups_bls12_377.nim new file mode 100644 index 0000000..43bd398 --- /dev/null +++ b/tests/t_ec_subgroups_bls12_377.nim @@ -0,0 +1,31 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + # Internals + ../constantine/config/[type_ff, curves], + ../constantine/elliptic/ec_shortweierstrass_projective, + ../constantine/towers, + # Test utilities + ./t_ec_template + +const + Iters = 12 + ItersMul = Iters div 4 + +run_EC_subgroups_cofactors_impl( + ec = ECP_ShortW_Prj[Fp[BLS12_377], G1], + ItersMul = ItersMul, + moduleName = "test_ec_subgroups_g1_" & $BLS12_377 + ) + +run_EC_subgroups_cofactors_impl( + ec = ECP_ShortW_Prj[Fp2[BLS12_377], G2], + ItersMul = ItersMul, + moduleName = "test_ec_subgroups_g2_" & $BLS12_377 + ) \ No newline at end of file diff --git a/tests/t_ec_subgroups_bls12_381.nim b/tests/t_ec_subgroups_bls12_381.nim new file mode 100644 index 0000000..a0c86d0 --- /dev/null +++ b/tests/t_ec_subgroups_bls12_381.nim @@ -0,0 +1,31 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + # Internals + ../constantine/config/[type_ff, curves], + ../constantine/elliptic/ec_shortweierstrass_projective, + ../constantine/towers, + # Test utilities + ./t_ec_template + +const + Iters = 12 + ItersMul = Iters div 4 + +run_EC_subgroups_cofactors_impl( + ec = ECP_ShortW_Prj[Fp[BLS12_381], G1], + ItersMul = ItersMul, + moduleName = "test_ec_subgroups_g1_" & $BLS12_381 + ) + +run_EC_subgroups_cofactors_impl( + ec = ECP_ShortW_Prj[Fp2[BLS12_381], G2], + ItersMul = ItersMul, + moduleName = "test_ec_subgroups_g2_" & $BLS12_381 + ) \ No newline at end of file diff --git a/tests/t_ec_subgroups_bn254_nogami.nim b/tests/t_ec_subgroups_bn254_nogami.nim new file mode 100644 index 0000000..a4b6406 --- /dev/null +++ b/tests/t_ec_subgroups_bn254_nogami.nim @@ -0,0 +1,31 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + # Internals + ../constantine/config/[type_ff, curves], + ../constantine/elliptic/ec_shortweierstrass_projective, + ../constantine/towers, + # Test utilities + ./t_ec_template + +const + Iters = 12 + ItersMul = Iters div 4 + +run_EC_subgroups_cofactors_impl( + ec = ECP_ShortW_Prj[Fp[BN254_Nogami], G1], + ItersMul = ItersMul, + moduleName = "test_ec_subgroups_g1_" & $BN254_Nogami + ) + +run_EC_subgroups_cofactors_impl( + ec = ECP_ShortW_Prj[Fp2[BN254_Nogami], G2], + ItersMul = ItersMul, + moduleName = "test_ec_subgroups_g2_" & $BN254_Nogami + ) \ No newline at end of file diff --git a/tests/t_ec_subgroups_bn254_snarks.nim b/tests/t_ec_subgroups_bn254_snarks.nim new file mode 100644 index 0000000..ae137e9 --- /dev/null +++ b/tests/t_ec_subgroups_bn254_snarks.nim @@ -0,0 +1,31 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + # Internals + ../constantine/config/[type_ff, curves], + ../constantine/elliptic/ec_shortweierstrass_projective, + ../constantine/towers, + # Test utilities + ./t_ec_template + +const + Iters = 12 + ItersMul = Iters div 4 + +run_EC_subgroups_cofactors_impl( + ec = ECP_ShortW_Prj[Fp[BN254_Snarks], G1], + ItersMul = ItersMul, + moduleName = "test_ec_subgroups_g1_" & $BN254_Snarks + ) + +run_EC_subgroups_cofactors_impl( + ec = ECP_ShortW_Prj[Fp2[BN254_Snarks], G2], + ItersMul = ItersMul, + moduleName = "test_ec_subgroups_g2_" & $BN254_Snarks + ) \ No newline at end of file diff --git a/tests/t_ec_template.nim b/tests/t_ec_template.nim index bcac647..bbfb4cc 100644 --- a/tests/t_ec_template.nim +++ b/tests/t_ec_template.nim @@ -26,7 +26,8 @@ import ec_twistededwards_affine, ec_twistededwards_projective, ec_scalar_mul], - ../constantine/io/[io_bigints, io_fields], + ../constantine/io/[io_bigints, io_fields, io_ec], + ../constantine/curves/zoo_subgroups, # Test utilities ../helpers/prng_unsafe, ./support/ec_reference_scalar_mult @@ -458,3 +459,80 @@ proc run_EC_mixed_add_impl*( test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = true, gen = HighHammingWeight) test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = false, gen = Long01Sequence) test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = true, gen = Long01Sequence) + +proc run_EC_subgroups_cofactors_impl*( + ec: typedesc, + ItersMul: static int, + moduleName: string + ) = + # Random seed for reproducibility + var rng: RngState + let seed = uint32(getTime().toUnix() and (1'i64 shl 32 - 1)) # unixTime mod 2^32 + rng.seed(seed) + echo "\n------------------------------------------------------\n" + echo moduleName, " xoshiro512** seed: ", seed + + when ec.G == G1: + const G1_or_G2 = "G1" + else: + const G1_or_G2 = "G2" + + const testSuiteDesc = "Elliptic curve subgroup check and cofactor clearing" + + suite testSuiteDesc & " - " & $ec & " - [" & $WordBitwidth & "-bit mode]": + test "Effective cofactor matches accelerated cofactor clearing" & " - " & $ec & " - [" & $WordBitwidth & "-bit mode]": + proc test(EC: typedesc, bits: static int, randZ: bool, gen: RandomGen) = + for _ in 0 ..< Iters: + let P = rng.random_point(EC, randZ, gen) + var cPeff = P + var cPfast = P + + cPeff.clearCofactorReference() + cPfast.clearCofactorFast() + + check: bool(cPeff == cPfast) + + test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = false, gen = Uniform) + test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = true, gen = Uniform) + test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = false, gen = HighHammingWeight) + test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = true, gen = HighHammingWeight) + test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = false, gen = Long01Sequence) + test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = true, gen = Long01Sequence) + + test "Subgroup checks and cofactor clearing consistency": + var inSubgroup = 0 + var offSubgroup = 0 + proc test(EC: typedesc, bits: static int, randZ: bool, gen: RandomGen) = + stdout.write " " + for _ in 0 ..< Iters: + let P = rng.random_point(EC, randZ, gen) + var rP = P + rP.scalarMulGeneric(EC.F.C.getCurveOrder()) + if bool rP.isInf(): + inSubgroup += 1 + doAssert bool P.isInSubgroup(), "Subgroup check issue on " & $EC & " with P: " & P.toHex() + else: + offSubgroup += 1 + doAssert not bool P.isInSubgroup(), "Subgroup check issue on " & $EC & " with P: " & P.toHex() + + var Q = P + var rQ: typeof(rP) + Q.clearCofactor() + rQ = Q + rQ.scalarMulGeneric(EC.F.C.getCurveOrder()) + doAssert bool rQ.isInf(), "Cofactor clearing issue on " & $EC & " with Q: " & Q.toHex() + doAssert bool Q.isInSubgroup(), "Subgroup check issue on " & $EC & " with Q: " & Q.toHex() + + stdout.write '.' + + stdout.write '\n' + + test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = false, gen = Uniform) + test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = true, gen = Uniform) + test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = false, gen = HighHammingWeight) + test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = true, gen = HighHammingWeight) + test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = false, gen = Long01Sequence) + test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = true, gen = Long01Sequence) + + echo " [SUCCESS] Test finished with ", inSubgroup, " points in ", G1_or_G2, " subgroup and ", + offSubgroup, " points on curve but not in subgroup (before cofactor clearing)" \ No newline at end of file diff --git a/tests/t_pairing_bls12_377_gt_subgroup.nim b/tests/t_pairing_bls12_377_gt_subgroup.nim new file mode 100644 index 0000000..120fe58 --- /dev/null +++ b/tests/t_pairing_bls12_377_gt_subgroup.nim @@ -0,0 +1,19 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + ../constantine/config/common, + ../constantine/config/curves, + ../constantine/pairing/pairing_bls12, + # Test utilities + ./t_pairing_template + +runGTsubgroupTests( + Iters = 4, + GT = Fp12[BLS12_377], + finalExpHard_BLS12) diff --git a/tests/t_pairing_bls12_381_gt_subgroup.nim b/tests/t_pairing_bls12_381_gt_subgroup.nim new file mode 100644 index 0000000..3e73f22 --- /dev/null +++ b/tests/t_pairing_bls12_381_gt_subgroup.nim @@ -0,0 +1,19 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + ../constantine/config/common, + ../constantine/config/curves, + ../constantine/pairing/pairing_bls12, + # Test utilities + ./t_pairing_template + +runGTsubgroupTests( + Iters = 4, + GT = Fp12[BLS12_381], + finalExpHard_BLS12) diff --git a/tests/t_pairing_bn254_nogami_gt_subgroup.nim b/tests/t_pairing_bn254_nogami_gt_subgroup.nim new file mode 100644 index 0000000..16c8f7c --- /dev/null +++ b/tests/t_pairing_bn254_nogami_gt_subgroup.nim @@ -0,0 +1,19 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + ../constantine/config/common, + ../constantine/config/curves, + ../constantine/pairing/pairing_bn, + # Test utilities + ./t_pairing_template + +runGTsubgroupTests( + Iters = 4, + GT = Fp12[BN254_Nogami], + finalExpHard_BN) diff --git a/tests/t_pairing_bn254_snarks_gt_subgroup.nim b/tests/t_pairing_bn254_snarks_gt_subgroup.nim new file mode 100644 index 0000000..b163b53 --- /dev/null +++ b/tests/t_pairing_bn254_snarks_gt_subgroup.nim @@ -0,0 +1,19 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + ../constantine/config/common, + ../constantine/config/curves, + ../constantine/pairing/pairing_bn, + # Test utilities + ./t_pairing_template + +runGTsubgroupTests( + Iters = 4, + GT = Fp12[BN254_Snarks], + finalExpHard_BN) diff --git a/tests/t_pairing_template.nim b/tests/t_pairing_template.nim index b8051dc..dc8ceeb 100644 --- a/tests/t_pairing_template.nim +++ b/tests/t_pairing_template.nim @@ -15,7 +15,10 @@ import ../constantine/towers, ../constantine/config/curves, ../constantine/elliptic/[ec_shortweierstrass_affine, ec_shortweierstrass_projective], - ../constantine/hash_to_curve/cofactors, + ../constantine/curves/[zoo_subgroups, zoo_pairings], + ../constantine/pairing/cyclotomic_fp12, + ../constantine/io/io_towers, + # Test utilities ../helpers/prng_unsafe @@ -23,7 +26,8 @@ export prng_unsafe, times, unittest, ec_shortweierstrass_affine, ec_shortweierstrass_projective, arithmetic, towers, - primitives + primitives, io_towers, + cyclotomic_fp12 type RandomGen* = enum @@ -35,24 +39,24 @@ template affineType[F; G: static Subgroup]( ec: ECP_ShortW_Prj[F, G]): type = ECP_ShortW_Aff[F, G] -func clearCofactorReference[F; G: static Subgroup]( +func clearCofactor[F; G: static Subgroup]( ec: var ECP_ShortW_Aff[F, G]) = # For now we don't have any affine operation defined var t {.noInit.}: ECP_ShortW_Prj[F, G] t.projectiveFromAffine(ec) - t.clearCofactorReference() + t.clearCofactor() ec.affineFromProjective(t) func random_point*(rng: var RngState, EC: typedesc, randZ: bool, gen: RandomGen): EC {.noInit.} = if gen == Uniform: result = rng.random_unsafe(EC) - result.clearCofactorReference() + result.clearCofactor() elif gen == HighHammingWeight: result = rng.random_highHammingWeight(EC) - result.clearCofactorReference() + result.clearCofactor() else: result = rng.random_long01Seq(EC) - result.clearCofactorReference() + result.clearCofactor() template runPairingTests*(Iters: static int, C: static Curve, G1, G2, GT: typedesc, pairing_fn: untyped): untyped {.dirty.}= bind affineType @@ -99,3 +103,43 @@ template runPairingTests*(Iters: static int, C: static Curve, G1, G2, GT: typede test_bilinearity_double_impl(randZ = false, gen = Uniform) test_bilinearity_double_impl(randZ = false, gen = HighHammingWeight) test_bilinearity_double_impl(randZ = false, gen = Long01Sequence) + +func random_elem*(rng: var RngState, F: typedesc, gen: RandomGen): F {.inline, noInit.} = + if gen == Uniform: + result = rng.random_unsafe(F) + elif gen == HighHammingWeight: + result = rng.random_highHammingWeight(F) + else: + result = rng.random_long01Seq(F) + +template runGTsubgroupTests*(Iters: static int, GT: typedesc, finalExpHard_fn: untyped): untyped {.dirty.}= + bind affineType + + var rng: RngState + let timeseed = uint32(toUnix(getTime()) and (1'i64 shl 32 - 1)) # unixTime mod 2^32 + seed(rng, timeseed) + echo "\n------------------------------------------------------\n" + echo "test_pairing_",$GT.C,"_gt xoshiro512** seed: ", timeseed + + proc test_gt_impl(gen: RandomGen) = + stdout.write " " + for _ in 0 ..< Iters: + let a = rng.random_elem(GT, gen) + doAssert not bool a.isInCyclotomicSubgroup(), "The odds of generating randomly such an element are too low a: " & a.toHex() + var a2 = a + a2.finalExpEasy() + doAssert bool a2.isInCyclotomicSubgroup() + doAssert not bool a2.isInPairingSubgroup(), "The odds of generating randomly such an element are too low a2: " & a.toHex() + var a3 = a2 + finalExpHard_fn(a3) + doAssert bool a3.isInCyclotomicSubgroup() + doAssert bool a3.isInPairingSubgroup() + stdout.write '.' + + stdout.write '\n' + + suite "Pairing - GT subgroup " & $GT.C & " [" & $WordBitwidth & "-bit mode]": + test "Final Exponentiation and GT-subgroup membership": + test_gt_impl(gen = Uniform) + test_gt_impl(gen = HighHammingWeight) + test_gt_impl(gen = Long01Sequence) \ No newline at end of file diff --git a/tests/t_sig_bls_lowlevel.nim b/tests/t_sig_bls_lowlevel.nim index 3b6844e..51a80f2 100644 --- a/tests/t_sig_bls_lowlevel.nim +++ b/tests/t_sig_bls_lowlevel.nim @@ -19,7 +19,8 @@ import ../constantine/elliptic/ec_scalar_mul, ../constantine/io/[io_fields, io_towers, io_ec], ../constantine/config/curves, - ../constantine/hash_to_curve/[hash_to_curve, cofactors], + ../constantine/curves/zoo_subgroups, + ../constantine/hash_to_curve/hash_to_curve, ../constantine/pairing/pairing_bls12, # Test utilities ../helpers/prng_unsafe