Optimized subgroup checks and cofactor clearing (#169)

* Move cofactor clearing to dedicated per-curve subgroups file

* Add BLS12-381 fast subgroup checks

* Implement fast cofactor clearing for BN254_snarks

* Add fast subgroup check to BN254Snarks

* add BLS12_377 optimized cofactor and subgroup functions

* Add BN254_Nogami

* Add GT-subgroup tests

* Use the new subgroup checks for Eth1 EVM precompiles
This commit is contained in:
Mamy Ratsimbazafy 2022-01-03 14:12:58 +01:00 committed by GitHub
parent c42e2a0251
commit f6c02fe075
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 1242 additions and 169 deletions

View File

@ -16,10 +16,9 @@ import
# Internals # Internals
../constantine/config/[curves, common], ../constantine/config/[curves, common],
../constantine/arithmetic, ../constantine/arithmetic,
../constantine/io/io_bigints,
../constantine/towers, ../constantine/towers,
../constantine/elliptic/[ec_shortweierstrass_projective, ec_shortweierstrass_affine], ../constantine/ec_shortweierstrass,
../constantine/hash_to_curve/cofactors, ../constantine/curves/zoo_subgroups,
../constantine/pairing/[ ../constantine/pairing/[
cyclotomic_fp12, cyclotomic_fp12,
lines_projective, 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) measure(iters, startTime, stopTime, startClk, stopClk, body)
report(op, $C, startTime, stopTime, startClk, stopClk, iters) 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]) = ec: var ECP_ShortW_Aff[F, G]) =
# For now we don't have any affine operation defined # For now we don't have any affine operation defined
var t {.noInit.}: ECP_ShortW_Prj[F, G] var t {.noInit.}: ECP_ShortW_Prj[F, G]
t.projectiveFromAffine(ec) t.projectiveFromAffine(ec)
t.clearCofactorReference() t.clearCofactor()
ec.affineFromProjective(t) ec.affineFromProjective(t)
func random_point*(rng: var RngState, EC: typedesc): EC {.noInit.} = func random_point*(rng: var RngState, EC: typedesc): EC {.noInit.} =
result = rng.random_unsafe(EC) result = rng.random_unsafe(EC)
result.clearCofactorReference() result.clearCofactor()
proc lineDoubleBench*(C: static Curve, iters: int) = proc lineDoubleBench*(C: static Curve, iters: int) =
var line: Line[Fp2[C]] var line: Line[Fp2[C]]

View File

@ -21,7 +21,8 @@ import
ec_shortweierstrass_projective, ec_shortweierstrass_projective,
ec_shortweierstrass_jacobian, ec_shortweierstrass_jacobian,
ec_scalar_mul, ec_endomorphism_accel], 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/[ ../constantine/pairing/[
cyclotomic_fp12, cyclotomic_fp12,
pairing_bls12, pairing_bls12,

View File

@ -143,6 +143,19 @@ const testDesc: seq[tuple[path: string, useGMP: bool]] = @[
# Edge cases highlighted by past bugs # Edge cases highlighted by past bugs
# ---------------------------------------------------------- # ----------------------------------------------------------
("tests/t_ec_shortw_prj_edge_cases.nim", false), ("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 # Pairing
# ---------------------------------------------------------- # ----------------------------------------------------------
# ("tests/t_pairing_bls12_377_line_functions.nim", false), # ("tests/t_pairing_bls12_377_line_functions.nim", false),

View File

@ -4,6 +4,8 @@ This folder holds curve-specific constants and procedure in particular:
- Inversion addition chains - Inversion addition chains
- Final exponentiation 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 - Lattice decomposition constants for endomorphism acceleration
- Frobenius endomorphism constants - Frobenius endomorphism constants
- Cofactor clearing
- Subgroup checks

View File

@ -7,11 +7,12 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms. # at your option. This file may not be copied, modified, or distributed except according to those terms.
import import
../config/[curves, type_bigint, type_ff], ../config/[common, curves, type_bigint, type_ff],
../io/io_bigints, ../io/io_bigints,
../towers, ../towers,
../elliptic/[ec_shortweierstrass_affine, ec_shortweierstrass_projective], ../elliptic/[ec_shortweierstrass_affine, ec_shortweierstrass_projective],
../pairing/[cyclotomic_fp12, miller_loops] ../pairing/[cyclotomic_fp12, miller_loops],
../isogeny/frobenius
# Slow generic implementation # 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: if invert:
r.cyclotomic_inv() 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

View File

@ -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

View File

@ -7,11 +7,12 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms. # at your option. This file may not be copied, modified, or distributed except according to those terms.
import import
../config/[curves, type_bigint, type_ff], ../config/[common, curves, type_bigint, type_ff],
../io/io_bigints, ../io/io_bigints,
../towers, ../towers,
../elliptic/[ec_shortweierstrass_affine, ec_shortweierstrass_projective], ../elliptic/[ec_shortweierstrass_affine, ec_shortweierstrass_projective],
../pairing/[cyclotomic_fp12, miller_loops] ../pairing/[cyclotomic_fp12, miller_loops],
../isogeny/frobenius
# Slow generic implementation # 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 ## For BLS12_381 f^-0xd201000000010000
r.pow_xdiv2(a, invert) r.pow_xdiv2(a, invert)
r.cyclotomic_square() 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

View File

@ -8,14 +8,66 @@
import import
# Internals # Internals
../config/common, ../config/[common, curves],
../arithmetic, ../arithmetic,
../primitives, ../primitives,
../towers, ../towers,
../config/curves, ../ec_shortweierstrass,
../io/io_bigints, ../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 # 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" const Cofactor_Eff_BLS12_381_G1 = BigInt[64].fromHex"0xd201000000010001"
## P -> (1 - x) P ## P -> (1 - x) P
const Cofactor_Eff_BLS12_381_G2 = BigInt[636].fromHex"0xbc69f08f2ee75b3584c6a0ea91b352888e2a8e9145ad7689986ff031508ffe1329c2f178731db956d82bf015d1212b02ec0ec69d7477c1ae954cbc06689f6a359894c0adebbf6b4e8020005aaa95551" const Cofactor_Eff_BLS12_381_G2 = BigInt[636].fromHex"0xbc69f08f2ee75b3584c6a0ea91b352888e2a8e9145ad7689986ff031508ffe1329c2f178731db956d82bf015d1212b02ec0ec69d7477c1ae954cbc06689f6a359894c0adebbf6b4e8020005aaa95551"
## P -> (x^2 - x - 1) P + (x - 1) ψ(P) + ψ(ψ(2P)) ## 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.} = func clearCofactorReference*(P: var ECP_ShortW_Prj[Fp[BLS12_381], G1]) {.inline.} =
## Clear the cofactor of BLS12_381 G1 ## Clear the cofactor of BLS12_381 G1
P.scalarMulGeneric(Cofactor_Eff_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 # Endomorphism acceleration cannot be used if cofactor is not cleared
P.scalarMulGeneric(Cofactor_Eff_BLS12_381_G2) 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 # 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 # BLS12 G2
# ------------------------------------------------------------ # ------------------------------------------------------------
# From any point on the elliptic curve E2 of a BLS12 curve # 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 # with Psi (ψ) - untwist-Frobenius-Twist function
# and x the curve BLS parameter # 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]) = func clearCofactorFast*(P: var ECP_ShortW_Prj[Fp2[BLS12_381], G2]) =
## Clear the cofactor of BLS12_381 G2 ## Clear the cofactor of BLS12_381 G2
## Optimized using endomorphisms ## 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) var xP{.noInit.}, x2P{.noInit.}: typeof(P)
xP.pow_x(P) # 1. xP = [x]P xP.pow_bls12_381_x(P) # 1. xP = [x]P
x2P.pow_x(xP) # 2. x2P = [x²]P x2P.pow_bls12_381_x(xP) # 2. x2P = [x²]P
x2P.diff(x2P, xP) # 3. x2P = [x²-x]P x2P.diff(x2P, xP) # 3. x2P = [x²-x]P
x2P.diff(x2P, P) # 4. x2P = [x²-x-1]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, x2P) # 9. P = [x²-x-1]P + ψ²([2]P)
P.sum(P, xP) # 10. P = [x²-x-1]P + [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

View File

@ -7,11 +7,12 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms. # at your option. This file may not be copied, modified, or distributed except according to those terms.
import import
../config/[curves, type_bigint, type_ff], ../config/[common, curves, type_bigint, type_ff],
../io/io_bigints, ../io/io_bigints,
../towers, ../towers,
../elliptic/[ec_shortweierstrass_affine, ec_shortweierstrass_projective], ../elliptic/[ec_shortweierstrass_affine, ec_shortweierstrass_projective],
../pairing/[cyclotomic_fp12, miller_loops] ../pairing/[cyclotomic_fp12, miller_loops],
../isogeny/frobenius
# Slow generic implementation # Slow generic implementation
# ------------------------------------------------------------ # ------------------------------------------------------------
@ -68,3 +69,21 @@ func pow_u*(r: var Fp12[BN254_Nogami], a: Fp12[BN254_Nogami], invert = BN254_Nog
if invert: if invert:
r.cyclotomic_inv() 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

View File

@ -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

View File

@ -7,10 +7,12 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms. # at your option. This file may not be copied, modified, or distributed except according to those terms.
import import
../config/[curves, type_bigint], ../config/[common, curves, type_bigint, type_ff],
../io/io_bigints, ../io/io_bigints,
../towers, ../towers,
../pairing/cyclotomic_fp12 ../elliptic/[ec_shortweierstrass_affine, ec_shortweierstrass_projective],
../pairing/[cyclotomic_fp12, miller_loops],
../isogeny/frobenius
# Slow generic implementation # Slow generic implementation
# ------------------------------------------------------------ # ------------------------------------------------------------
@ -121,3 +123,21 @@ func pow_u*(r: var Fp12[BN254_Snarks], a: Fp12[BN254_Snarks], invert = BN254_Sna
if invert: if invert:
r.cyclotomic_inv() 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

View File

@ -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

View File

@ -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)

View File

@ -21,4 +21,4 @@ macro pairing*(C: static Curve, value: untyped): untyped =
## Get pairing related constants ## Get pairing related constants
return bindSym($C & "_pairing_" & $value) return bindSym($C & "_pairing_" & $value)
export pow_x, pow_xdiv2, pow_u, millerLoopAddchain export pow_x, pow_xdiv2, pow_u, millerLoopAddchain, isInPairingSubgroup

View File

@ -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()

View File

@ -22,6 +22,8 @@ import
export ec_shortweierstrass_affine, ec_shortweierstrass_jacobian, ec_shortweierstrass_projective, ec_scalar_mul 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]( func projectiveFromJacobian*[F; G](
prj: var ECP_ShortW_Prj[F, G], prj: var ECP_ShortW_Prj[F, G],
jac: ECP_ShortW_Jac[F, G]) {.inline.} = jac: ECP_ShortW_Jac[F, G]) {.inline.} =
@ -30,3 +32,7 @@ func projectiveFromJacobian*[F; G](
prj.z.square(jac.z) prj.z.square(jac.z)
prj.z *= jac.z prj.z *= jac.z
func double_repeated*(P: var ECP_ShortW, num: int) {.inline.} =
## Repeated doublings
for _ in 0 ..< num:
P.double()

View File

@ -10,11 +10,10 @@ import
# Internals # Internals
../config/[common, curves], ../config/[common, curves],
../primitives, ../arithmetic, ../towers, ../primitives, ../arithmetic, ../towers,
../curves/zoo_hash_to_curve, ../curves/[zoo_hash_to_curve, zoo_subgroups],
../ec_shortweierstrass, ../ec_shortweierstrass,
./h2c_hash_to_field, ./h2c_hash_to_field,
./h2c_map_to_isocurve_swu, ./h2c_map_to_isocurve_swu,
./cofactors,
../isogeny/h2c_isogeny_maps, ../isogeny/h2c_isogeny_maps,
../hashes ../hashes
@ -166,4 +165,4 @@ func hashToCurve*[
Pjac.mapToCurve_fusedAdd(u[0], u[1]) Pjac.mapToCurve_fusedAdd(u[0], u[1])
output.projectiveFromJacobian(Pjac) output.projectiveFromJacobian(Pjac)
output.clearCofactorFast() output.clearCofactor()

View File

@ -220,3 +220,14 @@ func cyclotomic_exp*[C](r: var Fp12[C], a: Fp12[C], exponent: BigInt, invert: bo
r *= a r *= a
if invert: if invert:
r.cyclotomic_inv() 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

View File

@ -12,6 +12,7 @@ import
../arithmetic/limbs_montgomery, ../arithmetic/limbs_montgomery,
../ec_shortweierstrass, ../ec_shortweierstrass,
../pairing/[pairing_bn, miller_loops, cyclotomic_fp12], ../pairing/[pairing_bn, miller_loops, cyclotomic_fp12],
../curves/zoo_subgroups,
../io/[io_bigints, io_fields] ../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 ## 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. ## that point may not be in the correct cyclic subgroup.
## If we are on the subgroup of order r then [r]P = 0 ## 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] 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) Q.projectiveFromAffine(P)
let Q0 = Q return bool(Q.isInSubgroup())
Q.scalarMul(rm1)
Q += Q0
return bool(Q.isInf())
func fromRawCoords( func fromRawCoords(
dst: var ECP_ShortW_Aff[Fp[BN254_Snarks], G1], dst: var ECP_ShortW_Aff[Fp[BN254_Snarks], G1],

View File

@ -67,12 +67,12 @@ The optimizations can be of algebraic, algorithmic or "implementation details" n
- [x] FIPS (Finely Integrated Operand Scanning) - [x] FIPS (Finely Integrated Operand Scanning)
- Montgomery Squaring - Montgomery Squaring
- [ ] Dedicated squaring functions - [x] Dedicated squaring functions
- [ ] Fused multiply + reduce - [x] Fused multiply + reduce
- [ ] int128 - [ ] int128
- [ ] loop unrolling - [ ] loop unrolling
- [ ] x86: Full Assembly implementation - [x] x86: Full Assembly implementation
- [ ] x86: MULX, ADCX, ADOX instructions - [x] x86: MULX, ADCX, ADOX instructions
- [ ] no-carry optimization for CIOS (Coarsely Integrated Operand Scanning) - [ ] no-carry optimization for CIOS (Coarsely Integrated Operand Scanning)
- Addition chains - Addition chains
@ -97,7 +97,7 @@ The optimizations can be of algebraic, algorithmic or "implementation details" n
- Square Root (constant-time) - Square Root (constant-time)
- [x] baseline sqrt via Little-Fermat for `p ≡ 3 (mod 4)` - [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)` - [ ] baseline sqrt via Little-Fermat for `p ≡ 9 (mod 16)`
- [x] baseline sqrt via Tonelli-Shanks for any prime. - [x] baseline sqrt via Tonelli-Shanks for any prime.
- [x] sqrt via addition-chain - [x] sqrt via addition-chain
@ -211,3 +211,5 @@ The optimizations can be of algebraic, algorithmic or "implementation details" n
- Subgroup check - Subgroup check
- [ ] BLS G1: Bowe, endomorphism accelerated - [ ] BLS G1: Bowe, endomorphism accelerated
- [ ] BLS G2: Bowe, endomorphism accelerated - [ ] BLS G2: Bowe, endomorphism accelerated
- [x] BLS G1: Scott, endomorphism accelerated
- [x] BLS G2: Scott, endomorphism accelerated

View File

@ -30,7 +30,7 @@ echo "bench xoshiro512** seed: ", seed
func random_point*(rng: var RngState, EC: typedesc): EC {.noInit.} = func random_point*(rng: var RngState, EC: typedesc): EC {.noInit.} =
result = rng.random_unsafe(EC) result = rng.random_unsafe(EC)
result.clearCofactorReference() result.clearCofactor()
proc pairingBLS12Meter*(C: static Curve) = proc pairingBLS12Meter*(C: static Curve) =
let let

View File

@ -12,7 +12,7 @@ import
../constantine/config/[common, curves], ../constantine/config/[common, curves],
../constantine/[arithmetic, towers], ../constantine/[arithmetic, towers],
../constantine/elliptic/ec_shortweierstrass_projective, ../constantine/elliptic/ec_shortweierstrass_projective,
../constantine/hash_to_curve/cofactors, ../constantine/curves/zoo_subgroups,
../constantine/pairing/pairing_bls12, ../constantine/pairing/pairing_bls12,
# Helpers # Helpers
../helpers/prng_unsafe ../helpers/prng_unsafe
@ -24,7 +24,7 @@ echo "bench xoshiro512** seed: ", seed
func random_point*(rng: var RngState, EC: typedesc): EC {.noInit.} = func random_point*(rng: var RngState, EC: typedesc): EC {.noInit.} =
result = rng.random_unsafe(EC) result = rng.random_unsafe(EC)
result.clearCofactorReference() result.clearCofactor()
proc pairingBLS12Meter*(C: static Curve) = proc pairingBLS12Meter*(C: static Curve) =
let let

View File

@ -33,14 +33,14 @@ import
func millerLoopBW6_761_naive[C]( func millerLoopBW6_761_naive[C](
f: var Fp6[C], f: var Fp6[C],
P: ECP_ShortW_Aff[Fp[C], NotOnTwist], P: ECP_ShortW_Aff[Fp[C], G1],
Q: ECP_ShortW_Aff[Fp[C], OnTwist] Q: ECP_ShortW_Aff[Fp[C], G2]
) = ) =
## Miller Loop for BW6_761 curve ## Miller Loop for BW6_761 curve
## Computes f_{u+1,Q}(P)*Frobenius(f_{u*(u^2-u-1),Q}(P)) ## Computes f_{u+1,Q}(P)*Frobenius(f_{u*(u^2-u-1),Q}(P))
var var
T {.noInit.}: ECP_ShortW_Prj[Fp[C], OnTwist] T {.noInit.}: ECP_ShortW_Prj[Fp[C], G2]
line {.noInit.}: Line[Fp[C]] line {.noInit.}: Line[Fp[C]]
nQ{.noInit.}: typeof(Q) nQ{.noInit.}: typeof(Q)
@ -76,15 +76,15 @@ func finalExpGeneric[C: static Curve](f: var Fp6[C]) =
func millerLoopBW6_761_opt_to_debug[C]( func millerLoopBW6_761_opt_to_debug[C](
f: var Fp6[C], f: var Fp6[C],
P: ECP_ShortW_Aff[Fp[C], NotOnTwist], P: ECP_ShortW_Aff[Fp[C], G1],
Q: ECP_ShortW_Aff[Fp[C], OnTwist] Q: ECP_ShortW_Aff[Fp[C], G2]
) {.used.} = ) {.used.} =
## Miller Loop Otpimized for BW6_761 curve ## Miller Loop Otpimized for BW6_761 curve
# 1st part: f_{u,Q}(P) # 1st part: f_{u,Q}(P)
# ------------------------------ # ------------------------------
var var
T {.noInit.}: ECP_ShortW_Prj[Fp[C], OnTwist] T {.noInit.}: ECP_ShortW_Prj[Fp[C], G2]
line {.noInit.}: Line[Fp[C]] line {.noInit.}: Line[Fp[C]]
T.projectiveFromAffine(Q) T.projectiveFromAffine(Q)
@ -150,15 +150,15 @@ func millerLoopBW6_761_opt_to_debug[C](
func pairing_bw6_761_reference*[C]( func pairing_bw6_761_reference*[C](
gt: var Fp6[C], gt: var Fp6[C],
P: ECP_ShortW_Prj[Fp[C], NotOnTwist], P: ECP_ShortW_Prj[Fp[C], G1],
Q: ECP_ShortW_Prj[Fp[C], OnTwist]) = Q: ECP_ShortW_Prj[Fp[C], G2]) =
## Compute the optimal Ate Pairing for BW6 curves ## Compute the optimal Ate Pairing for BW6 curves
## Input: P ∈ G1, Q ∈ G2 ## Input: P ∈ G1, Q ∈ G2
## Output: e(P, Q) ∈ Gt ## Output: e(P, Q) ∈ Gt
## ##
## Reference implementation ## Reference implementation
var Paff {.noInit.}: ECP_ShortW_Aff[Fp[C], NotOnTwist] var Paff {.noInit.}: ECP_ShortW_Aff[Fp[C], G1]
var Qaff {.noInit.}: ECP_ShortW_Aff[Fp[C], OnTwist] var Qaff {.noInit.}: ECP_ShortW_Aff[Fp[C], G2]
Paff.affineFromProjective(P) Paff.affineFromProjective(P)
Qaff.affineFromProjective(Q) Qaff.affineFromProjective(Q)
gt.millerLoopBW6_761_naive(Paff, Qaff) gt.millerLoopBW6_761_naive(Paff, Qaff)

View File

@ -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
)

View File

@ -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
)

View File

@ -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
)

View File

@ -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
)

View File

@ -26,7 +26,8 @@ import
ec_twistededwards_affine, ec_twistededwards_affine,
ec_twistededwards_projective, ec_twistededwards_projective,
ec_scalar_mul], ec_scalar_mul],
../constantine/io/[io_bigints, io_fields], ../constantine/io/[io_bigints, io_fields, io_ec],
../constantine/curves/zoo_subgroups,
# Test utilities # Test utilities
../helpers/prng_unsafe, ../helpers/prng_unsafe,
./support/ec_reference_scalar_mult ./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 = true, gen = HighHammingWeight)
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = false, gen = Long01Sequence) test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = false, gen = Long01Sequence)
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = true, 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)"

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -15,7 +15,10 @@ import
../constantine/towers, ../constantine/towers,
../constantine/config/curves, ../constantine/config/curves,
../constantine/elliptic/[ec_shortweierstrass_affine, ec_shortweierstrass_projective], ../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 # Test utilities
../helpers/prng_unsafe ../helpers/prng_unsafe
@ -23,7 +26,8 @@ export
prng_unsafe, times, unittest, prng_unsafe, times, unittest,
ec_shortweierstrass_affine, ec_shortweierstrass_projective, ec_shortweierstrass_affine, ec_shortweierstrass_projective,
arithmetic, towers, arithmetic, towers,
primitives primitives, io_towers,
cyclotomic_fp12
type type
RandomGen* = enum RandomGen* = enum
@ -35,24 +39,24 @@ template affineType[F; G: static Subgroup](
ec: ECP_ShortW_Prj[F, G]): type = ec: ECP_ShortW_Prj[F, G]): type =
ECP_ShortW_Aff[F, G] ECP_ShortW_Aff[F, G]
func clearCofactorReference[F; G: static Subgroup]( func clearCofactor[F; G: static Subgroup](
ec: var ECP_ShortW_Aff[F, G]) = ec: var ECP_ShortW_Aff[F, G]) =
# For now we don't have any affine operation defined # For now we don't have any affine operation defined
var t {.noInit.}: ECP_ShortW_Prj[F, G] var t {.noInit.}: ECP_ShortW_Prj[F, G]
t.projectiveFromAffine(ec) t.projectiveFromAffine(ec)
t.clearCofactorReference() t.clearCofactor()
ec.affineFromProjective(t) ec.affineFromProjective(t)
func random_point*(rng: var RngState, EC: typedesc, randZ: bool, gen: RandomGen): EC {.noInit.} = func random_point*(rng: var RngState, EC: typedesc, randZ: bool, gen: RandomGen): EC {.noInit.} =
if gen == Uniform: if gen == Uniform:
result = rng.random_unsafe(EC) result = rng.random_unsafe(EC)
result.clearCofactorReference() result.clearCofactor()
elif gen == HighHammingWeight: elif gen == HighHammingWeight:
result = rng.random_highHammingWeight(EC) result = rng.random_highHammingWeight(EC)
result.clearCofactorReference() result.clearCofactor()
else: else:
result = rng.random_long01Seq(EC) 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.}= template runPairingTests*(Iters: static int, C: static Curve, G1, G2, GT: typedesc, pairing_fn: untyped): untyped {.dirty.}=
bind affineType 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 = Uniform)
test_bilinearity_double_impl(randZ = false, gen = HighHammingWeight) test_bilinearity_double_impl(randZ = false, gen = HighHammingWeight)
test_bilinearity_double_impl(randZ = false, gen = Long01Sequence) 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)

View File

@ -19,7 +19,8 @@ import
../constantine/elliptic/ec_scalar_mul, ../constantine/elliptic/ec_scalar_mul,
../constantine/io/[io_fields, io_towers, io_ec], ../constantine/io/[io_fields, io_towers, io_ec],
../constantine/config/curves, ../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, ../constantine/pairing/pairing_bls12,
# Test utilities # Test utilities
../helpers/prng_unsafe ../helpers/prng_unsafe