Test GT-subgroup for BW6-761 (#171)

This commit is contained in:
Mamy Ratsimbazafy 2022-01-08 17:30:26 +01:00 committed by GitHub
parent f6c02fe075
commit 50717d8de6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 384 additions and 22 deletions

View File

@ -155,6 +155,7 @@ const testDesc: seq[tuple[path: string, useGMP: bool]] = @[
("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),
("tests/t_pairing_bw6_761_gt_subgroup.nim", false),
# Pairing
# ----------------------------------------------------------

View File

@ -60,7 +60,7 @@ func millerLoopAddchain*(
func pow_x*(r: var Fp12[BLS12_377], a: Fp12[BLS12_377], invert = BLS12_377_pairing_ate_param_isNeg) =
## f^x with x the curve parameter
## For BLS12_377 f^-0x8508c00000000001
## For BLS12_377 f^0x8508c00000000001
r.cyclotomic_square(a)
r *= a
r.cyclotomic_square()
@ -92,7 +92,7 @@ func isInPairingSubgroup*(a: Fp12[BLS12_377]): SecretBool =
# 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
# a is in the GT subgroup iff a^p == a^u
var t0{.noInit.}, t1{.noInit.}: Fp12[BLS12_377]
t0.frobenius_map(a)
t1.pow_x(a)

View File

@ -181,14 +181,14 @@ func isInSubgroup*(P: ECP_ShortW_Prj[Fp[BLS12_377], G1]): SecretBool =
# 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)
# P is in the G1 subgroup iff ϕ(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)
# ϕ(P)
t0.x.prod(P.x, BLS12_377.getCubicRootOfUnity_mod_p())
t0.y = P.y
t0.z = P.z

View File

@ -175,14 +175,14 @@ func isInSubgroup*(P: ECP_ShortW_Prj[Fp[BLS12_381], G1]): SecretBool =
# 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)
# P is in the G1 subgroup iff ϕ(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)
# ϕ(P)
t0.x.prod(P.x, BLS12_381.getCubicRootOfUnity_mod_p())
t0.y = P.y
t0.z = P.z

View File

@ -7,8 +7,11 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
../config/[curves, type_bigint],
../io/io_bigints
../config/[common, curves, type_bigint],
../io/io_bigints,
../towers,
../pairing/cyclotomic_fp6,
../isogeny/frobenius
# Slow generic implementation
# ------------------------------------------------------------
@ -53,5 +56,107 @@ const BW6_761_pairing_finalexponent* = block:
# (p^6 - 1) / r * 3*(u^3-u^2+1)
BigInt[4376].fromHex"0x8a168e18d34ff984b8399b649a12265bcdd3023623c45b9a1d38314c4fdd4547f8a0c18b88468482c0ff74c94606e4e5734c43d4e9fa977c1196361496699ea26e4d912e4918fff3cbe177b5d47cd9ba63103cb2a7a1699ef2a48dd77d1f939ca33d35dadabf0aab681703a3340126ab78a2a76c2147cc4f5897f610596fed83ccdcab13b919d48f9365b50ad005a6fbcf41412c73ad8d03f465568acbb86d9b97d5216af6a67fe6d16f12c069cdc44035adc99b54e9e68095349af476057b5bc94bca6e4e23b8de4afd24d6fc655448269a02123b8c4d25115d8d09fc4b2774042d2c744568b132b11cb1fae68e025a6d8c7e405ce52092154a56523f2abeb3ec693419f8402799b08ae023360be4468046e81033e3e1d172d19d5ce5e3441140c26e710015f97bdbbddce57396c565d1a9d4f81d571415dacf2686171f2679797d97a35c59c372cca29eeb8556e2576912edb846235fb723a75a0cc5acc8ace1e5628f8e14c931f0a0d58372a44d0eba074e4fefff61efaf4bde1adf999e6194cf12c73cba39732fe059618901d4c0924b8a5d15ad9bea271be5f6679b6f0148f15d36a9269c4b6a07d08b2aa9b9365ab295a8c6a7eb4088e86fb5e30843798bf1bf426f07c2c39f4b8beef71b3da9c1d656ba3c23bbc8d3b54399d0e6fd1ec64616566ee1471934d0763fe360fb9a02bc3a5d4ccdf6fcaf52be7b67955a89b522a5e0a45e935f1794a038aeca4b9a6d8ae28da00178304c7dfc3d0e13ade8564b78"
const BW6_761_pairing_finalexponent_hard* = block:
# (p^2 - p + 1) / r * 3*(u^3-u^2+1)
BigInt[1335].fromHex"0x52d03a3bd1dd9aa185df830823e31f28dd2231c308bd86210eefcc7623b1c28d6b1e42eabf464f9161e52f11542cbacc962137fe3971d52652188b8ef74af13b0a049a4806e46f50f0c6eda7965e4275a966ebba028d346efe221daebfbbd9ca698a0ed763e9b1b0945bd554b2b8511e18bd7338b3355d3b2419c6fa6d71346b955d466ea17d418f7e444b5c67cd440c20be53ff99df9b79934de2c001a91809a300000000e0cd"
# Addition chain
# ------------------------------------------------------------
# ------------------------------------------------------------
func pow_u*(r: var Fp6[BW6_761], a: Fp6[BW6_761], invert = false) =
## f^u with u the curve parameter
## For BLS12_377/BW6_761 f^0x8508c00000000001
r.cyclotomic_square(a)
r *= a
r.cyclotomic_square()
r *= a
let t111 = r
r.cycl_sqr_repeated(2)
let t111000 = r
r *= t111
let t100011 = r
r.cyclotomic_square()
r *= t100011
r *= t111000
r.cycl_sqr_repeated(10)
r *= t100011
r.cycl_sqr_repeated(46) # TODO: Karabina's compressed squarings
r *= a
if invert:
r.cyclotomic_inv()
func isInPairingSubgroup*(a: Fp6[BW6_761]): 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
# a is in the GT subgroup iff a^(p) == a^(t-1)
# with t the trace of the curve
var u0{.noInit.}, u1{.noInit.}, u3{.noInit.}: Fp6[BW6_761]
var u4{.noInit.}, u5{.noInit.}, u6{.noInit.}: Fp6[BW6_761]
# t-1 = (13u⁶ - 23u⁵ - 9u⁴ + 35u³ + 10u + 19)/3
u0.cyclotomic_square(a) # u0 = 2
u0.cycl_sqr_repeated(2) # u0 = 8
u0 *= a # u0 = 9
u0.cyclotomic_square() # u0 = 18
u0 *= a # u0 = 19
u1.pow_u(a) # u1 = u
u4.pow_u(u1) # u4 = u²
u3.cyclotomic_square(u1) # u3 = 2u
u3.cyclotomic_square() # u3 = 4u
u1 *= u3 # u1 = 5u
u1.cyclotomic_square() # u1 = 10u
u0 *= u1 # u0 = 10u + 19
u1.pow_u(u4) # u1 = u³
u3.cyclotomic_square(u1) # u3 = 2u³
u3.cycl_sqr_repeated(3) # u3 = 16u³
u3 *= u1 # u3 = 17u³
u3.cyclotomic_square() # u3 = 34u³
u3 *= u1 # u3 = 35u³
u0 *= u3 # u0 = 35u³ + 10u + 19
u4.pow_u(u1) # u4 = u⁴
u5.pow_u(u4) # u5 = u⁵
u6.pow_u(u5) # u6 = u⁶
u1.cyclotomic_square(u4) # u1 = 2u⁴
u1.cycl_sqr_repeated(2) # u1 = 8u⁴
u4 *= u1 # u4 = 9u⁴
u4.cyclotomic_inv() # u4 = -9u⁴
u0 *= u4 # u0 = -9u⁴ + 35u³ + 10u + 19
u1.cyclotomic_inv(u5) # u1 = -u⁵
u1.cycl_sqr_repeated(3) # u1 = -8u⁵
u5 *= u1 # u5 = -7u⁵
u1.cyclotomic_square() # u1 = -16u⁵
u5 *= u1 # u5 = -23u⁵
u0 *= u5 # u0 = -23u⁵ - 9u⁴ + 35u³ + 10u + 19
u1.cyclotomic_square(u6) # u1 = 2u⁶
u1 *= u6 # u1 = 3u⁶
u1.cycl_sqr_repeated(2) # u1 = 12u⁶
u6 *= u1 # u6 = 13u⁶
u0 *= u6 # u0 = 3(t-1) = 13u⁶ - 23u⁵ - 9u⁴ + 35u³ + 10u + 19
u1.frobenius_map(a) # u1 = p
u3.cyclotomic_square(u1) # u3 = 2p
u3 *= u1 # u3 = 3p
return u0 == u3

View File

@ -37,4 +37,4 @@ func clearCofactorReference*(P: var ECP_ShortW_Prj[Fp[BW6_761], G1]) {.inline.}
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)
P.scalarMulGeneric(Cofactor_Eff_BW6_761_G2)

View File

@ -0,0 +1,233 @@
# 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
../primitives,
../config/[common, curves],
../arithmetic,
../towers,
../isogeny/frobenius
# No exceptions allowed
{.push raises: [].}
# ############################################################
#
# Gϕ₆, Cyclotomic subgroup of Fp6
# with GΦₙ(p) = {α ∈ Fpⁿ : α^Φₙ(p) ≡ 1 (mod pⁿ)}
#
# ############################################################
# - Faster Squaring in the Cyclotomic Subgroup of Sixth Degree Extensions
# Granger, Scott, 2009
# https://eprint.iacr.org/2009/565.pdf
#
# - On the final exponentiation for calculating
# pairings on ordinary elliptic curves
# Scott, Benger, Charlemagne, Perez, Kachisa, 2008
# https://eprint.iacr.org/2008/490.pdf
# 𝔽p6 -> Gϕ₆ - Mapping to Cyclotomic group
# ----------------------------------------------------------------
func finalExpEasy*[C: static Curve](f: var Fp6[C]) {.meter.} =
## Easy part of the final exponentiation
##
## This maps the result of the Miller loop into the cyclotomic subgroup Gϕ₆
##
## We need to clear the Gₜ cofactor to obtain
## an unique Gₜ representation
## (reminder, Gₜ is a multiplicative group hence we exponentiate by the cofactor)
##
## i.e. Fp⁶ --> (fexp easy) --> Gϕ₆ --> (fexp hard) --> Gₜ
##
## The final exponentiation is fexp = f^((p⁶ - 1) / r)
## It is separated into:
## f^((p⁶ - 1) / r) = (p⁶ - 1) / ϕ₆(p) * ϕ₆(p) / r
##
## with the cyclotomic polynomial ϕ₆(p) = (p²-p+1)
##
## With an embedding degree of 6, the easy part of final exponentiation is
##
## f^(p³1)(p+1)
##
## And properties are
## 0. f^(p³) ≡ conj(f) (mod p⁶) for all f in Fp6
##
## After g = f^(p³1) the result g is on the cyclotomic subgroup
## 1. g^(-1) ≡ g^(p³) (mod p⁶)
## 2. Inversion can be done with conjugate
## 3. g is unitary, its norm |g| (the product of conjugates) is 1
## 4. Squaring has a fast compressed variant.
#
# Proofs:
#
# Fp6 can be defined as a quadratic extension over Fp³
# with g = g₀ + x g₁ with x a quadratic non-residue
#
# with q = p³, q² = p⁶
# The frobenius map f^q ≡ (f₀ + x f₁)^q (mod q²)
# ≡ f₀^q + x^q f₁^q (mod q²)
# ≡ f₀ + x^q f₁ (mod q²)
# ≡ f₀ - x f₁ (mod q²)
# hence
# f^p³ ≡ conj(f) (mod p⁶)
# Q.E.D. of (0)
#
# ----------------
#
# p⁶ - 1 = (p³1)(p³+1) = (p³1)(p+1)(p²-p+1)
# by Fermat's little theorem we have
# f^(p⁶ - 1) ≡ 1 (mod p⁶)
#
# Hence f^(p³1)(p³+1) ≡ 1 (mod p⁶)
#
# We call g = f^(p³1) we have
# g^(p³+1) ≡ 1 (mod p⁶) <=> g^(p³) * g ≡ 1 (mod p⁶)
# hence g^(-1) ≡ g^(p³) (mod p⁶)
# Q.E.D. of (1)
#
# --
#
# From (1) g^(-1) ≡ g^(p³) (mod p⁶) for g = f^(p³1)
# and (0) f^p³ ≡ conj(f) (mod p⁶) for all f in fp12
#
# so g^(-1) ≡ conj(g) (mod p⁶) for g = f^(p³1)
# Q.E.D. of (2)
#
# --
#
# f^(p⁶ - 1) ≡ 1 (mod p⁶) by Fermat's Little Theorem
# f^(p³1)(p³+1) ≡ 1 (mod p⁶)
# g^(p³+1) ≡ 1 (mod p⁶)
# g * g^p³ ≡ 1 (mod p⁶)
# g * conj(g) ≡ 1 (mod p⁶)
# Q.E.D. of (3)
var g {.noinit.}: typeof(f)
g.inv(f) # g = f^-1
conj(f) # f = f^p³
g *= f # g = f^(p³-1)
f.frobenius_map(g) # f = f^((p³-1) p)
f *= g # f = f^((p³-1) (p+1))
# Gϕ₆ - Cyclotomic functions
# ----------------------------------------------------------------
# A cyclotomic group is a subgroup of Fp^n defined by
#
# GΦₙ(p) = {α ∈ Fpⁿ : α^Φₙ(p) = 1}
#
# The result of any pairing is in a cyclotomic subgroup
func cyclotomic_inv*(a: var Fp6) {.meter.} =
## Fast inverse for a
## `a` MUST be in the cyclotomic subgroup
## consequently `a` MUST be unitary
a.conj()
func cyclotomic_inv*(r: var Fp6, a: Fp6) {.meter.} =
## Fast inverse for a
## `a` MUST be in the cyclotomic subgroup
## consequently `a` MUST be unitary
r.conj(a)
func cyclotomic_square*[C](r: var Fp6[C], a: Fp6[C]) {.meter.} =
## Square `a` into `r`
## `a` MUST be in the cyclotomic subgroup
## consequently `a` MUST be unitary
#
# Faster Squaring in the Cyclotomic Subgroup of Sixth Degree Extensions
# Granger, Scott, 2009
# https://eprint.iacr.org/2009/565.pdf
when a.c0 is Fp2:
# Cubic over quadratic
# A = 3a² 2 ̄a
# B = 3 √i c² + 2 ̄b
# C = 3b² 2 ̄c
var t0{.noinit.}, t1{.noinit.}: Fp2[C]
t0.square(a.c0) # t0 = a²
t1.double(t0) # t1 = 2a²
t1 += t0 # t1 = 3a²
t0.conj(a.c0) # t0 = ̄a
t0.double() # t0 = 2 ̄a
r.c0.diff(t1, t0) # r0 = 3a² 2 ̄a
# Aliasing: a.c0 unused
t0.square(a.c2) # t0 = c²
t0 *= NonResidue # t0 = √i c²
t1.double(t0) # t1 = 2 √i c²
t0 += t1 # t0 = 3 √i c²
t1.square(a.c1) # t1 = b²
r.c1.conj(a.c1) # r1 = ̄b
r.c1.double() # r1 = 2 ̄b
r.c1 += t0 # r1 = 3 √i c² + 2 ̄b
# Aliasing: a.c1 unused
t0.double(t1) # t0 = 2b²
t0 += t1 # t0 = 3b²
t1.conj(a.c2) # r2 = ̄c
t1.double() # r2 = 2 ̄c
r.c2.diff(t0, t1) # r2 = 3b² - 2 ̄c
else:
{.error: "Not implemented".}
func cyclotomic_square*[C](a: var Fp6[C]) {.meter.} =
## Square `a` into `r`
## `a` MUST be in the cyclotomic subgroup
## consequently `a` MUST be unitary
#
# Faster Squaring in the Cyclotomic Subgroup of Sixth Degree Extensions
# Granger, Scott, 2009
# https://eprint.iacr.org/2009/565.pdf
a.cyclotomic_square(a)
func cycl_sqr_repeated*(f: var Fp6, num: int) {.inline, meter.} =
## Repeated cyclotomic squarings
for _ in 0 ..< num:
f.cyclotomic_square()
iterator unpack(scalarByte: byte): bool =
yield bool((scalarByte and 0b10000000) shr 7)
yield bool((scalarByte and 0b01000000) shr 6)
yield bool((scalarByte and 0b00100000) shr 5)
yield bool((scalarByte and 0b00010000) shr 4)
yield bool((scalarByte and 0b00001000) shr 3)
yield bool((scalarByte and 0b00000100) shr 2)
yield bool((scalarByte and 0b00000010) shr 1)
yield bool( scalarByte and 0b00000001)
func cyclotomic_exp*[C](r: var Fp6[C], a: Fp6[C], exponent: BigInt, invert: bool) {.meter.} =
var eBytes: array[(exponent.bits+7) div 8, byte]
eBytes.exportRawUint(exponent, bigEndian)
r.setOne()
for b in eBytes:
for bit in unpack(b):
r.cyclotomic_square()
if bit:
r *= a
if invert:
r.cyclotomic_inv()
func isInCyclotomicSubgroup*[C](a: Fp6[C]): SecretBool =
## Check if a ∈ Fpⁿ: a^Φₙ(p) = 1
## Φ₆(p) = p⁴-p²+1
var t{.noInit.}, p{.noInit.}: Fp6[C]
t.frobenius_map(a, 2) # a^(p²)
t *= a # a^(p²+1)
p.frobenius_map(a) # a^(p)
return t == p

View File

@ -20,6 +20,8 @@ import
./mul_fp6_by_lines,
./miller_loops
export zoo_pairings # generic sandwich https://github.com/nim-lang/Nim/issues/11225
# ############################################################
#
# Optimal ATE pairing for
@ -29,7 +31,6 @@ import
# Generic pairing implementation
# ----------------------------------------------------------------
# TODO: debug this
func millerLoopBW6_761_naive[C](
f: var Fp6[C],
@ -59,7 +60,7 @@ func millerLoopBW6_761_naive[C](
basicMillerLoop(
f2, T, line,
P, Q, nQ,
ate_param_1_unopt, ate_param_1_unopt_isNeg
ate_param_2_unopt, ate_param_2_unopt_isNeg
)
let t = f2
@ -71,6 +72,11 @@ func finalExpGeneric[C: static Curve](f: var Fp6[C]) =
## for sanity checks purposes.
f.powUnsafeExponent(C.pairing(finalexponent), window = 3)
func finalExpHard_BW6_761*[C: static Curve](f: var Fp6[C]) =
## A generic and slow implementation of final exponentiation
## for sanity checks purposes.
f.powUnsafeExponent(C.pairing(finalexponent_hard), window = 3)
# Optimized pairing implementation
# ----------------------------------------------------------------
@ -150,16 +156,14 @@ func millerLoopBW6_761_opt_to_debug[C](
func pairing_bw6_761_reference*[C](
gt: var Fp6[C],
P: ECP_ShortW_Prj[Fp[C], G1],
Q: ECP_ShortW_Prj[Fp[C], G2]) =
P: ECP_ShortW_Aff[Fp[C], G1],
Q: ECP_ShortW_Aff[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], G1]
var Qaff {.noInit.}: ECP_ShortW_Aff[Fp[C], G2]
Paff.affineFromProjective(P)
Qaff.affineFromProjective(Q)
gt.millerLoopBW6_761_naive(Paff, Qaff)
gt.finalExpGeneric()
{.error: "BW6_761 Miller loop is not working yet".}
gt.millerLoopBW6_761_naive(P, Q)
gt.finalExpEasy()
gt.finalExpHard_BW6_761()

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_bw6_761,
# Test utilities
./t_pairing_template
runGTsubgroupTests(
Iters = 4,
GT = Fp6[BW6_761],
finalExpHard_BW6_761)

View File

@ -16,7 +16,7 @@ import
../constantine/config/curves,
../constantine/elliptic/[ec_shortweierstrass_affine, ec_shortweierstrass_projective],
../constantine/curves/[zoo_subgroups, zoo_pairings],
../constantine/pairing/cyclotomic_fp12,
../constantine/pairing/[cyclotomic_fp12, cyclotomic_fp6],
../constantine/io/io_towers,
# Test utilities
@ -27,7 +27,7 @@ export
ec_shortweierstrass_affine, ec_shortweierstrass_projective,
arithmetic, towers,
primitives, io_towers,
cyclotomic_fp12
cyclotomic_fp12, cyclotomic_fp6
type
RandomGen* = enum