mirror of
https://github.com/codex-storage/constantine.git
synced 2025-01-11 19:44:10 +00:00
Pairing optim (#85)
* Fix fp12 Frobenius map * Implement cyclotomic subgroup acceleration * make cyclotomic squaring in-place * Add back out-place cycl squaring and add cyclotomic inverse * Implement state-of-the-art BLS12-381 final exponentiation * save a cyclotomic squaring * Accelerate sparse line multiplication in Miller loop * Add pairing bench * fix comments
This commit is contained in:
parent
0c18f4436c
commit
f78ed23dad
51
benchmarks/bench_pairing_bls12_381.nim
Normal file
51
benchmarks/bench_pairing_bls12_381.nim
Normal file
@ -0,0 +1,51 @@
|
||||
# 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/curves,
|
||||
../constantine/arithmetic,
|
||||
../constantine/towers,
|
||||
# Helpers
|
||||
../helpers/static_for,
|
||||
./bench_pairing_template,
|
||||
# Standard library
|
||||
std/strutils
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
# Benchmark of pairings
|
||||
# for BLS12-381
|
||||
#
|
||||
# ############################################################
|
||||
|
||||
|
||||
const Iters = 50
|
||||
const AvailableCurves = [
|
||||
BLS12_381,
|
||||
]
|
||||
|
||||
proc main() =
|
||||
separator()
|
||||
staticFor i, 0, AvailableCurves.len:
|
||||
const curve = AvailableCurves[i]
|
||||
lineDoubleBench(curve, Iters)
|
||||
lineAddBench(curve, Iters)
|
||||
mulFp12byLineBench(curve, Iters)
|
||||
separator()
|
||||
finalExpEasyBench(curve, Iters)
|
||||
finalExpHardBLS12Bench(curve, Iters)
|
||||
separator()
|
||||
millerLoopBLS12Bench(curve, Iters)
|
||||
finalExpBLS12Bench(curve, Iters)
|
||||
separator()
|
||||
pairingBLS12Bench(curve, Iters)
|
||||
separator()
|
||||
|
||||
main()
|
||||
notes()
|
206
benchmarks/bench_pairing_template.nim
Normal file
206
benchmarks/bench_pairing_template.nim
Normal file
@ -0,0 +1,206 @@
|
||||
# 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.
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
# Benchmark of pairings
|
||||
#
|
||||
# ############################################################
|
||||
|
||||
import
|
||||
# Internals
|
||||
../constantine/config/[curves, common],
|
||||
../constantine/arithmetic,
|
||||
../constantine/io/io_bigints,
|
||||
../constantine/towers,
|
||||
../constantine/elliptic/[ec_weierstrass_projective, ec_weierstrass_affine],
|
||||
../constantine/hash_to_curve/cofactors,
|
||||
../constantine/pairing/[
|
||||
cyclotomic_fp12,
|
||||
lines_projective,
|
||||
mul_fp12_by_lines,
|
||||
pairing_bls12
|
||||
],
|
||||
# Helpers
|
||||
../helpers/[prng_unsafe, static_for],
|
||||
./platforms,
|
||||
# Standard library
|
||||
std/[monotimes, times, strformat, strutils, macros]
|
||||
|
||||
var rng: RngState
|
||||
let seed = uint32(getTime().toUnix() and (1'i64 shl 32 - 1)) # unixTime mod 2^32
|
||||
rng.seed(seed)
|
||||
echo "bench xoshiro512** seed: ", seed
|
||||
|
||||
# warmup
|
||||
proc warmup*() =
|
||||
# Warmup - make sure cpu is on max perf
|
||||
let start = cpuTime()
|
||||
var foo = 123
|
||||
for i in 0 ..< 300_000_000:
|
||||
foo += i*i mod 456
|
||||
foo = foo mod 789
|
||||
|
||||
# Compiler shouldn't optimize away the results as cpuTime rely on sideeffects
|
||||
let stop = cpuTime()
|
||||
echo &"Warmup: {stop - start:>4.4f} s, result {foo} (displayed to avoid compiler optimizing warmup away)\n"
|
||||
|
||||
warmup()
|
||||
|
||||
when defined(gcc):
|
||||
echo "\nCompiled with GCC"
|
||||
elif defined(clang):
|
||||
echo "\nCompiled with Clang"
|
||||
elif defined(vcc):
|
||||
echo "\nCompiled with MSVC"
|
||||
elif defined(icc):
|
||||
echo "\nCompiled with ICC"
|
||||
else:
|
||||
echo "\nCompiled with an unknown compiler"
|
||||
|
||||
echo "Optimization level => "
|
||||
echo " no optimization: ", not defined(release)
|
||||
echo " release: ", defined(release)
|
||||
echo " danger: ", defined(danger)
|
||||
echo " inline assembly: ", UseASM_X86_64
|
||||
|
||||
when (sizeof(int) == 4) or defined(Constantine32):
|
||||
echo "⚠️ Warning: using Constantine with 32-bit limbs"
|
||||
else:
|
||||
echo "Using Constantine with 64-bit limbs"
|
||||
|
||||
when SupportsCPUName:
|
||||
echo "Running on ", cpuName(), ""
|
||||
|
||||
when SupportsGetTicks:
|
||||
echo "\n⚠️ Cycles measurements are approximate and use the CPU nominal clock: Turbo-Boost and overclocking will skew them."
|
||||
echo "i.e. a 20% overclock will be about 20% off (assuming no dynamic frequency scaling)"
|
||||
|
||||
echo "\n=================================================================================================================\n"
|
||||
|
||||
proc separator*() =
|
||||
echo "-".repeat(177)
|
||||
|
||||
proc report(op, curve: string, start, stop: MonoTime, startClk, stopClk: int64, iters: int) =
|
||||
let ns = inNanoseconds((stop-start) div iters)
|
||||
let throughput = 1e9 / float64(ns)
|
||||
when SupportsGetTicks:
|
||||
echo &"{op:<60} {curve:<15} {throughput:>15.3f} ops/s {ns:>9} ns/op {(stopClk - startClk) div iters:>9} CPU cycles (approx)"
|
||||
else:
|
||||
echo &"{op:<60} {curve:<15} {throughput:>15.3f} ops/s {ns:>9} ns/op"
|
||||
|
||||
proc notes*() =
|
||||
echo "Notes:"
|
||||
echo " - Compilers:"
|
||||
echo " Compilers are severely limited on multiprecision arithmetic."
|
||||
echo " Constantine compile-time assembler is used by default (nimble bench_fp)."
|
||||
echo " GCC is significantly slower than Clang on multiprecision arithmetic due to catastrophic handling of carries."
|
||||
echo " GCC also seems to have issues with large temporaries and register spilling."
|
||||
echo " This is somewhat alleviated by Constantine compile-time assembler."
|
||||
echo " Bench on specific compiler with assembler: \"nimble bench_ec_g1_gcc\" or \"nimble bench_ec_g1_clang\"."
|
||||
echo " Bench on specific compiler with assembler: \"nimble bench_ec_g1_gcc_noasm\" or \"nimble bench_ec_g1_clang_noasm\"."
|
||||
echo " - The simplest operations might be optimized away by the compiler."
|
||||
echo " - Fast Squaring and Fast Multiplication are possible if there are spare bits in the prime representation (i.e. the prime uses 254 bits out of 256 bits)"
|
||||
|
||||
template bench(op: string, C: static Curve, iters: int, body: untyped): untyped =
|
||||
let start = getMonotime()
|
||||
when SupportsGetTicks:
|
||||
let startClk = getTicks()
|
||||
for _ in 0 ..< iters:
|
||||
body
|
||||
when SupportsGetTicks:
|
||||
let stopClk = getTicks()
|
||||
let stop = getMonotime()
|
||||
|
||||
when not SupportsGetTicks:
|
||||
let startClk = -1'i64
|
||||
let stopClk = -1'i64
|
||||
|
||||
report(op, $C, start, stop, startClk, stopClk, iters)
|
||||
|
||||
func random_point*(rng: var RngState, EC: typedesc): EC {.noInit.} =
|
||||
result = rng.random_unsafe(EC)
|
||||
result.clearCofactorReference()
|
||||
|
||||
proc lineDoubleBench*(C: static Curve, iters: int) =
|
||||
var line: Line[Fp2[C], C.getSexticTwist()]
|
||||
var T = rng.random_point(ECP_SWei_Proj[Fp2[C]])
|
||||
let P = rng.random_point(ECP_SWei_Proj[Fp[C]])
|
||||
var Paff: ECP_SWei_Aff[Fp[C]]
|
||||
Paff.affineFromProjective(P)
|
||||
bench("Line double", C, iters):
|
||||
line.line_double(T, Paff)
|
||||
|
||||
proc lineAddBench*(C: static Curve, iters: int) =
|
||||
var line: Line[Fp2[C], C.getSexticTwist()]
|
||||
var T = rng.random_point(ECP_SWei_Proj[Fp2[C]])
|
||||
let
|
||||
P = rng.random_point(ECP_SWei_Proj[Fp[C]])
|
||||
Q = rng.random_point(ECP_SWei_Proj[Fp2[C]])
|
||||
var
|
||||
Paff: ECP_SWei_Aff[Fp[C]]
|
||||
Qaff: ECP_SWei_Aff[Fp2[C]]
|
||||
Paff.affineFromProjective(P)
|
||||
Qaff.affineFromProjective(Q)
|
||||
bench("Line add", C, iters):
|
||||
line.line_add(T, Qaff, Paff)
|
||||
|
||||
proc mulFp12byLineBench*(C: static Curve, iters: int) =
|
||||
var line: Line[Fp2[C], C.getSexticTwist()]
|
||||
var T = rng.random_point(ECP_SWei_Proj[Fp2[C]])
|
||||
let P = rng.random_point(ECP_SWei_Proj[Fp[C]])
|
||||
var Paff: ECP_SWei_Aff[Fp[C]]
|
||||
Paff.affineFromProjective(P)
|
||||
|
||||
line.line_double(T, Paff)
|
||||
var f = rng.random_unsafe(Fp12[C])
|
||||
|
||||
bench("Mul 𝔽p12 by line xy000z", C, iters):
|
||||
f.mul_sparse_by_line_xy000z(line)
|
||||
|
||||
proc millerLoopBLS12Bench*(C: static Curve, iters: int) =
|
||||
let
|
||||
P = rng.random_point(ECP_SWei_Proj[Fp[C]])
|
||||
Q = rng.random_point(ECP_SWei_Proj[Fp2[C]])
|
||||
var
|
||||
Paff: ECP_SWei_Aff[Fp[C]]
|
||||
Qaff: ECP_SWei_Aff[Fp2[C]]
|
||||
Paff.affineFromProjective(P)
|
||||
Qaff.affineFromProjective(Q)
|
||||
|
||||
var f: Fp12[C]
|
||||
|
||||
bench("Miller Loop BLS12", C, iters):
|
||||
f.millerLoopGenericBLS12(Paff, Qaff)
|
||||
|
||||
proc finalExpEasyBench*(C: static Curve, iters: int) =
|
||||
var r = rng.random_unsafe(Fp12[C])
|
||||
bench("Final Exponentiation Easy", C, iters):
|
||||
r.finalExpEasy()
|
||||
|
||||
proc finalExpHardBLS12Bench*(C: static Curve, iters: int) =
|
||||
var r = rng.random_unsafe(Fp12[C])
|
||||
r.finalExpEasy()
|
||||
bench("Final Exponentiation Hard BLS12", C, iters):
|
||||
r.finalExpHard_BLS12()
|
||||
|
||||
proc finalExpBLS12Bench*(C: static Curve, iters: int) =
|
||||
var r = rng.random_unsafe(Fp12[C])
|
||||
bench("Final Exponentiation BLS12", C, iters):
|
||||
r.finalExpEasy()
|
||||
r.finalExpHard_BLS12()
|
||||
|
||||
proc pairingBLS12Bench*(C: static Curve, iters: int) =
|
||||
let
|
||||
P = rng.random_point(ECP_SWei_Proj[Fp[C]])
|
||||
Q = rng.random_point(ECP_SWei_Proj[Fp2[C]])
|
||||
|
||||
var f: Fp12[C]
|
||||
|
||||
bench("Pairing BLS12", C, iters):
|
||||
f.pairing_bls12(P, Q)
|
@ -71,7 +71,8 @@ const testDesc: seq[tuple[path: string, useGMP: bool]] = @[
|
||||
# Edge cases highlighted by past bugs
|
||||
("tests/t_ec_wstrass_prj_edge_cases.nim", false),
|
||||
# Pairing
|
||||
("tests/t_pairing_fp12_sparse.nim", false),
|
||||
("tests/t_pairing_mul_fp12_by_lines.nim", false),
|
||||
("tests/t_pairing_cyclotomic_fp12.nim", false),
|
||||
("tests/t_pairing_bn254_nogami_optate.nim", false),
|
||||
("tests/t_pairing_bn254_snarks_optate.nim", false),
|
||||
("tests/t_pairing_bls12_381_optate.nim", false)
|
||||
@ -417,7 +418,7 @@ task bench_fp12_gcc_noasm, "Run benchmark 𝔽p12 with gcc - no Assembly":
|
||||
task bench_fp12_clang_noasm, "Run benchmark 𝔽p12 with clang - no Assembly":
|
||||
runBench("bench_fp12", "clang", useAsm = false)
|
||||
|
||||
task bench_ec_g1, "Run benchmark on Elliptic Curve group 𝔾1 - Short Weierstrass with Projective Coordinates - GCC":
|
||||
task bench_ec_g1, "Run benchmark on Elliptic Curve group 𝔾1 - Short Weierstrass with Projective Coordinates - Default compiler":
|
||||
runBench("bench_ec_g1")
|
||||
|
||||
task bench_ec_g1_gcc, "Run benchmark on Elliptic Curve group 𝔾1 - Short Weierstrass with Projective Coordinates - GCC":
|
||||
@ -432,7 +433,7 @@ task bench_ec_g1_gcc_noasm, "Run benchmark on Elliptic Curve group 𝔾1 - Short
|
||||
task bench_ec_g1_clang_noasm, "Run benchmark on Elliptic Curve group 𝔾1 - Short Weierstrass with Projective Coordinates - Clang no Assembly":
|
||||
runBench("bench_ec_g1", "clang", useAsm = false)
|
||||
|
||||
task bench_ec_g2, "Run benchmark on Elliptic Curve group 𝔾2 - Short Weierstrass with Projective Coordinates - GCC":
|
||||
task bench_ec_g2, "Run benchmark on Elliptic Curve group 𝔾2 - Short Weierstrass with Projective Coordinates - Default compiler":
|
||||
runBench("bench_ec_g2")
|
||||
|
||||
task bench_ec_g2_gcc, "Run benchmark on Elliptic Curve group 𝔾2 - Short Weierstrass with Projective Coordinates - GCC":
|
||||
@ -446,3 +447,18 @@ task bench_ec_g2_gcc_noasm, "Run benchmark on Elliptic Curve group 𝔾2 - Short
|
||||
|
||||
task bench_ec_g2_clang_noasm, "Run benchmark on Elliptic Curve group 𝔾2 - Short Weierstrass with Projective Coordinates - Clang no Assembly":
|
||||
runBench("bench_ec_g2", "clang", useAsm = false)
|
||||
|
||||
task bench_pairing_bls12_381, "Run pairings benchmarks for BLS12-381 - Default compiler":
|
||||
runBench("bench_pairing_bls12_381")
|
||||
|
||||
task bench_pairing_bls12_381_gcc, "Run benchmark on Elliptic Curve group 𝔾2 - Short Weierstrass with Projective Coordinates - GCC":
|
||||
runBench("bench_pairing_bls12_381", "gcc")
|
||||
|
||||
task bench_pairing_bls12_381_clang, "Run benchmark on Elliptic Curve group 𝔾2 - Short Weierstrass with Projective Coordinates - Clang":
|
||||
runBench("bench_pairing_bls12_381", "clang")
|
||||
|
||||
task bench_pairing_bls12_381_gcc_noasm, "Run benchmark on Elliptic Curve group 𝔾2 - Short Weierstrass with Projective Coordinates - GCC no Assembly":
|
||||
runBench("bench_pairing_bls12_381", "gcc", useAsm = false)
|
||||
|
||||
task bench_pairing_bls12_381_clang_noasm, "Run benchmark on Elliptic Curve group 𝔾2 - Short Weierstrass with Projective Coordinates - Clang no Assembly":
|
||||
runBench("bench_pairing_bls12_381", "clang", useAsm = false)
|
||||
|
@ -79,28 +79,28 @@ const FrobMapConst_BLS12_381 = [
|
||||
"0x1904d3bf02bb0667c231beb4202c0d1f0fd603fd3cbd5f4f7b2443d784bab9c4f67ea53d63e7813d8d0775ed92235fb8",
|
||||
"0xfc3e2b36c4e03288e9e902231f9fb854a14787b6c7b36fec0c8ec971f63c5f282d5ac14d6c7ec22cf78a126ddc4af3"
|
||||
),
|
||||
Fp2[BLS12_381].fromHex(
|
||||
Fp2[BLS12_381].fromHex( # SNR^((p-1)/6)^2 = SNR^((p-1)/3)
|
||||
"0x0",
|
||||
"0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac"
|
||||
),
|
||||
Fp2[BLS12_381].fromHex(
|
||||
Fp2[BLS12_381].fromHex( # SNR^((p-1)/6)^3 = SNR^((p-1)/2)
|
||||
"0x6af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09",
|
||||
"0x6af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09"
|
||||
),
|
||||
Fp2[BLS12_381].fromHex(
|
||||
Fp2[BLS12_381].fromHex( # SNR^((p-1)/6)^4 = SNR^(2(p-1)/3)
|
||||
"0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaad",
|
||||
"0x0"
|
||||
),
|
||||
Fp2[BLS12_381].fromHex(
|
||||
Fp2[BLS12_381].fromHex( # SNR^((p-1)/6)^5
|
||||
"0x5b2cfd9013a5fd8df47fa6b48b1e045f39816240c0b8fee8beadf4d8e9c0566c63a3e6e257f87329b18fae980078116",
|
||||
"0x144e4211384586c16bd3ad4afa99cc9170df3560e77982d0db45f3536814f0bd5871c1908bd478cd1ee605167ff82995"
|
||||
)],
|
||||
# frobenius(2)
|
||||
[Fp2[BLS12_381].fromHex(
|
||||
[Fp2[BLS12_381].fromHex( # norm(SNR)^((p-1)/6)^1
|
||||
"0x1",
|
||||
"0x0"
|
||||
),
|
||||
Fp2[BLS12_381].fromHex(
|
||||
Fp2[BLS12_381].fromHex( # norm(SNR)^((p-1)/6)^2
|
||||
"0x5f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffeffff",
|
||||
"0x0"
|
||||
),
|
||||
@ -151,15 +151,24 @@ func frobenius_map*(r: var Fp4, a: Fp4, k: static int = 1) {.inline.} =
|
||||
## The p-power frobenius automorphism on 𝔽p4
|
||||
r.c0.frobenius_map(a.c0, k)
|
||||
r.c1.frobenius_map(a.c1, k)
|
||||
r.c1.mulCheckSparse FrobMapConst_BLS12_381[k-1][4-1]
|
||||
r.c1.mulCheckSparse FrobMapConst_BLS12_381[k-1][3]
|
||||
|
||||
func frobenius_map*(r: var Fp6, a: Fp6, k: static int = 1) {.inline.} =
|
||||
## Computes a^(p^k)
|
||||
## The p-power frobenius automorphism on 𝔽p6
|
||||
r.c0.frobenius_map(a.c0, k)
|
||||
r.c1.frobenius_map(a.c1, k)
|
||||
r.c2.frobenius_map(a.c2, k)
|
||||
r.c1.mulCheckSparse FrobMapConst_BLS12_381[k-1][2]
|
||||
r.c2.mulCheckSparse FrobMapConst_BLS12_381[k-1][4]
|
||||
|
||||
func frobenius_map*(r: var Fp12, a: Fp12, k: static int = 1) {.inline.} =
|
||||
## Computes a^(p^k)
|
||||
## The p-power frobenius automorphism on 𝔽p4
|
||||
## The p-power frobenius automorphism on 𝔽p12
|
||||
static: doAssert r.c0 is Fp4
|
||||
for r_fp4, a_fp4 in fields(r, a):
|
||||
for r_fp2, a_fp2 in fields(r_fp4, a_fp4):
|
||||
r_fp2.frobenius_map(a_fp2)
|
||||
r_fp2.frobenius_map(a_fp2, k)
|
||||
|
||||
r.c0.c0.mulCheckSparse FrobMapConst_BLS12_381[k-1][0]
|
||||
r.c0.c1.mulCheckSparse FrobMapConst_BLS12_381[k-1][3]
|
||||
|
@ -24,7 +24,7 @@
|
||||
Scott, Benger, Charlemagne, Perez, Kachisa, 2008\
|
||||
https://eprint.iacr.org/2008/490.pdf
|
||||
|
||||
- Faster Squaring in the Cyclotomic Subgroup ofSixth Degree Extensions\
|
||||
- Faster Squaring in the Cyclotomic Subgroup of Sixth Degree Extensions\
|
||||
Granger, Scott, 2009\
|
||||
https://eprint.iacr.org/2009/565.pdf
|
||||
|
||||
@ -87,6 +87,11 @@
|
||||
Aurore Guillevic, 2019\
|
||||
https://eprint.iacr.org/2019/1371.pdf
|
||||
|
||||
- Improving the computation of the optimal ate pairing
|
||||
for a high security level.
|
||||
Loubna Ghammam, Emmanuel Fouotsa
|
||||
J. Appl. Math. Comput.59, 21–36 (2019)
|
||||
|
||||
- Efficient Final Exponentiation
|
||||
via Cyclotomic Structure for Pairings
|
||||
over Families of Elliptic Curves
|
||||
|
226
constantine/pairing/cyclotomic_fp12.nim
Normal file
226
constantine/pairing/cyclotomic_fp12.nim
Normal file
@ -0,0 +1,226 @@
|
||||
# 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
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
# Gϕ₁₂, Cyclotomic subgroup of Fp12
|
||||
# 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
|
||||
|
||||
# 𝔽p12 -> Gϕ₁₂ - Mapping to Cyclotomic group
|
||||
# ----------------------------------------------------------------
|
||||
func finalExpEasy*[C: static Curve](f: var Fp12[C]) =
|
||||
## 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^12 --> (fexp easy) --> Gϕ₁₂ --> (fexp hard) --> Gₜ
|
||||
##
|
||||
## The final exponentiation is fexp = f^((p^12 - 1) / r)
|
||||
## It is separated into:
|
||||
## f^((p^12 - 1) / r) = (p^12 - 1) / ϕ₁₂(p) * ϕ₁₂(p) / r
|
||||
##
|
||||
## with the cyclotomic polynomial ϕ₁₂(p) = (p⁴-p²+1)
|
||||
##
|
||||
## With an embedding degree of 12, the easy part of final exponentiation is
|
||||
##
|
||||
## f^(p⁶−1)(p²+1)
|
||||
##
|
||||
## And properties are
|
||||
## 0. f^(p⁶) ≡ conj(f) (mod p^12) for all f in Fp12
|
||||
##
|
||||
## After g = f^(p⁶−1) the result g is on the cyclotomic subgroup
|
||||
## 1. g^(-1) ≡ g^(p⁶) (mod p^12)
|
||||
## 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:
|
||||
#
|
||||
# Fp12 can be defined as a quadratic extension over Fp⁶
|
||||
# with g = g₀ + x g₁ with x a quadratic non-residue
|
||||
#
|
||||
# with 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^12)
|
||||
# Q.E.D. of (0)
|
||||
#
|
||||
# ----------------
|
||||
#
|
||||
# p^12 - 1 = (p⁶−1)(p⁶+1) = (p⁶−1)(p²+1)(p⁴-p²+1)
|
||||
# by Fermat's little theorem we have
|
||||
# f^(p^12 - 1) ≡ 1 (mod p^12)
|
||||
#
|
||||
# Hence f^(p⁶−1)(p⁶+1) ≡ 1 (mod p^12)
|
||||
#
|
||||
# We call g = f^(p⁶−1) we have
|
||||
# g^(p⁶+1) ≡ 1 (mod p^12) <=> g^(p⁶) * g ≡ 1 (mod p^12)
|
||||
# hence g^(-1) ≡ g^(p⁶) (mod p^12)
|
||||
# Q.E.D. of (1)
|
||||
#
|
||||
# --
|
||||
#
|
||||
# From (1) g^(-1) ≡ g^(p⁶) (mod p^12) for g = f^(p⁶−1)
|
||||
# and (0) f^p⁶ ≡ conj(f) (mod p^12) for all f in fp12
|
||||
#
|
||||
# so g^(-1) ≡ conj(g) (mod p^12) for g = f^(p⁶−1)
|
||||
# Q.E.D. of (2)
|
||||
#
|
||||
# --
|
||||
#
|
||||
# f^(p^12 - 1) ≡ 1 (mod p^12) by Fermat's Little Theorem
|
||||
# f^(p⁶−1)(p⁶+1) ≡ 1 (mod p^12)
|
||||
# g^(p⁶+1) ≡ 1 (mod p^12)
|
||||
# g * g^p⁶ ≡ 1 (mod p^12)
|
||||
# g * conj(g) ≡ 1 (mod p^12)
|
||||
# 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, 2) # 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 Fp12) =
|
||||
## Fast inverse for a
|
||||
## `a` MUST be in the cyclotomic subgroup
|
||||
## consequently `a` MUST be unitary
|
||||
a.conj()
|
||||
|
||||
func cyclotomic_inv*(r: var Fp12, a: Fp12) =
|
||||
## 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 Fp12[C], a: Fp12[C]) =
|
||||
## 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 Fp4:
|
||||
# Cubic over quadratic
|
||||
# A = 3a² − 2 ̄a
|
||||
# B = 3 √i c² + 2 ̄b
|
||||
# C = 3b² − 2 ̄c
|
||||
var A{.noinit.}, B{.noinit.}, C{.noinit.}, D{.noinit.}: Fp4[C]
|
||||
|
||||
A = a.c0
|
||||
|
||||
r.c0.square(a.c0) # r0 = a²
|
||||
D.double(r.c0) # D = 2a²
|
||||
r.c0 += D # r0 = 3a²
|
||||
|
||||
A.conjneg() # A = − ̄a
|
||||
A.double() # A = − 2 ̄a
|
||||
r.c0 += A # r0 = 3a² − 2 ̄a
|
||||
|
||||
B.square(a.c2) # B = c²
|
||||
B *= NonResidue # B = √i c²
|
||||
D.double(B) # B = 2 √i c²
|
||||
B += D # B = 3 √i c²
|
||||
|
||||
r.c1.conj(a.c1) # r1 = ̄b
|
||||
r.c1.double() # r1 = 2 ̄b
|
||||
r.c1 += B # r1 = 3 √i c² + 2 ̄b
|
||||
|
||||
C.square(a.c1) # C = b²
|
||||
D.double(C) # D = 2b²
|
||||
C += D # C = 3b²
|
||||
|
||||
r.c2.conjneg(a.c2) # r2 = - ̄c
|
||||
r.c2.double() # r2 = - 2 ̄c
|
||||
r.c2 += C # r2 = 3b² - 2 ̄c
|
||||
|
||||
else:
|
||||
{.error: "Not implemented".}
|
||||
|
||||
func cyclotomic_square*[C](a: var Fp12[C]) =
|
||||
## 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 Fp4:
|
||||
# Cubic over quadratic
|
||||
# A = 3a² − 2 ̄a
|
||||
# B = 3 √i c² + 2 ̄b
|
||||
# C = 3b² − 2 ̄c
|
||||
var A{.noinit.}, B{.noinit.}, C{.noinit.}, D{.noinit.}: Fp4[C]
|
||||
|
||||
A = a.c0
|
||||
|
||||
a.c0.square() # r0 = a²
|
||||
D.double(a.c0) # D = 2a²
|
||||
a.c0 += D # r0 = 3a²
|
||||
|
||||
A.conjneg() # A = − ̄a
|
||||
A.double() # A = − 2 ̄a
|
||||
a.c0 += A # r0 = 3a² − 2 ̄a
|
||||
|
||||
B.square(a.c2) # B = c²
|
||||
B *= NonResidue # B = √i c²
|
||||
D.double(B) # B = 2 √i c²
|
||||
B += D # B = 3 √i c²
|
||||
|
||||
A = a.c1
|
||||
|
||||
a.c1.conj() # r1 = ̄b
|
||||
a.c1.double() # r1 = 2 ̄b
|
||||
a.c1 += B # r1 = 3 √i c² + 2 ̄b
|
||||
|
||||
C.square(A) # C = b²
|
||||
D.double(C) # D = 2b²
|
||||
C += D # C = 3b²
|
||||
|
||||
a.c2.conjneg() # r2 = - ̄c
|
||||
a.c2.double() # r2 = - 2 ̄c
|
||||
a.c2 += C # r2 = 3b² - 2 ̄c
|
||||
|
||||
else:
|
||||
{.error: "Not implemented".}
|
@ -13,11 +13,11 @@ import
|
||||
../towers,
|
||||
./lines_projective
|
||||
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
# Sparse Multiplication
|
||||
# and cyclotomic squaring
|
||||
# for elements of Gₜ = E(Fp¹²)
|
||||
# by lines
|
||||
#
|
||||
# ############################################################
|
||||
|
||||
@ -38,16 +38,16 @@ import
|
||||
# Craig Costello, Tanja Lange, and Michael Naehrig, 2009
|
||||
# https://eprint.iacr.org/2009/615.pdf
|
||||
|
||||
# TODO: we assume an embedding degree k of 12 and a sextic twist.
|
||||
# -> Generalize to KSS (k=18), BLS24 and BLS48 curves
|
||||
#
|
||||
# TODO: we assume a 2-3-2 towering scheme
|
||||
#
|
||||
# TODO: merge that in the quadratic/cubic files
|
||||
|
||||
# 𝔽p12 - Sparse functions
|
||||
# 𝔽p12 by line - Sparse functions
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
func mul_sparse_by_0y*[C: static Curve](r: var Fp4[C], a: Fp4[C], b: Fp2[C]) =
|
||||
## Sparse multiplication of an Fp4 element
|
||||
## with coordinates (a₀, a₁) by (0, b₁, 0)
|
||||
r.c0.prod(a.c1, b)
|
||||
r.c0 *= NonResidue
|
||||
r.c1.prod(a.c0, b)
|
||||
|
||||
func mul_sparse_by_0y0*[C: static Curve](r: var Fp6[C], a: Fp6[C], b: Fp2[C]) =
|
||||
## Sparse multiplication of an Fp6 element
|
||||
## with coordinates (a₀, a₁, a₂) by (0, b₁, 0)
|
||||
@ -60,7 +60,7 @@ func mul_sparse_by_0y0*[C: static Curve](r: var Fp6[C], a: Fp6[C], b: Fp2[C]) =
|
||||
# r0 = ξ ((a1 + a2) * (b1 + b2) - v1 - v2) + v0
|
||||
# = ξ (a1 b1 + a2 b1 - v1)
|
||||
# = ξ a2 b1
|
||||
# r1 = (a0 + a1) * (b0 + b1) - v0 - v1 + β v2
|
||||
# r1 = (a0 + a1) * (b0 + b1) - v0 - v1 + ξ v2
|
||||
# = a0 b1 + a1 b1 - v1
|
||||
# = a0 b1
|
||||
# r2 = (a0 + a2) * (b0 + b2) - v0 - v2 + v1
|
||||
@ -152,17 +152,47 @@ func mul_sparse_by_line_xy000z*[C: static Curve, Tw: static SexticTwist](
|
||||
|
||||
static: doAssert f.c0.typeof is Fp4, "This assumes 𝔽p12 as a cubic extension of 𝔽p4"
|
||||
|
||||
var v: Fp12[C]
|
||||
v.c0.c0 = l.x
|
||||
v.c0.c1 = l.y
|
||||
v.c2.c1 = l.z
|
||||
# In the following equations (taken from cubic extension implementation)
|
||||
# a = f
|
||||
# b0 = (x, y)
|
||||
# b1 = (0, 0)
|
||||
# b2 = (0, z)
|
||||
#
|
||||
# v0 = a0 b0 = (f00, f01).(x, y)
|
||||
# v1 = a1 b1 = (f10, f11).(0, 0)
|
||||
# v2 = a2 b2 = (f20, f21).(0, z)
|
||||
#
|
||||
# r0 = ξ ((a1 + a2) * (b1 + b2) - v1 - v2) + v0
|
||||
# = ξ (a1 b2 + a2 b2 - v2) + v0
|
||||
# = ξ a1 b2 + v0
|
||||
# r1 = (a0 + a1) * (b0 + b1) - v0 - v1 + ξ v2
|
||||
# = a0 b0 + a1 b0 - v0 + ξ v2
|
||||
# = a1 b0 + ξ v2
|
||||
# r2 = (a0 + a2) * (b0 + b2) - v0 - v2 + v1
|
||||
# = (a0 + a2) * (b0 + b2) - v0 - v2
|
||||
|
||||
f *= v
|
||||
var b0 {.noInit.}, v0{.noInit.}, v2{.noInit.}, t{.noInit.}: Fp4[C]
|
||||
|
||||
# Gₜ = 𝔽p12 - 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
|
||||
b0.c0 = l.x
|
||||
b0.c1 = l.y
|
||||
|
||||
v0.prod(f.c0, b0)
|
||||
v2.mul_sparse_by_0y(f.c2, l.z)
|
||||
|
||||
# r2 = (a0 + a2) * (b0 + b2) - v0 - v2
|
||||
f.c2 += f.c0 # r2 = a0 + a2
|
||||
t = b0
|
||||
t.c1 += l.z # t = b0 + b2
|
||||
f.c2 *= t # r2 = (a0 + a2)(b0 + b2)
|
||||
f.c2 -= v0
|
||||
f.c2 -= v2 # r2 = (a0 + a2)(b0 + b2) - v0 - v2
|
||||
|
||||
# r0 = ξ a1 b2 + v0
|
||||
f.c0.mul_sparse_by_0y(f.c1, l.z)
|
||||
f.c0 *= NonResidue
|
||||
f.c0 += v0
|
||||
|
||||
# r1 = a1 b0 + ξ v2
|
||||
f.c1 *= b0
|
||||
v2 *= NonResidue
|
||||
f.c1 += v2
|
@ -16,8 +16,10 @@ import
|
||||
ec_weierstrass_affine,
|
||||
ec_weierstrass_projective
|
||||
],
|
||||
../isogeny/frobenius,
|
||||
./lines_projective,
|
||||
./gt_fp12
|
||||
./mul_fp12_by_lines,
|
||||
./cyclotomic_fp12
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
@ -33,6 +35,11 @@ import
|
||||
# and Tadanori Teruya, 2020
|
||||
# https://eprint.iacr.org/2020/875.pdf
|
||||
#
|
||||
# - Improving the computation of the optimal ate pairing
|
||||
# for a high security level.
|
||||
# Loubna Ghammam, Emmanuel Fouotsa
|
||||
# J. Appl. Math. Comput.59, 21–36 (2019)
|
||||
#
|
||||
# - Faster Pairing Computations on Curves with High-Degree Twists
|
||||
# Craig Costello, Tanja Lange, and Michael Naehrig, 2009
|
||||
# https://eprint.iacr.org/2009/615.pdf
|
||||
@ -48,13 +55,16 @@ const BLS12_381_param = block:
|
||||
|
||||
const BLS12_381_param_isNeg = true
|
||||
|
||||
# Generic slow pairing implementation
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
const BLS12_381_finalexponent = block:
|
||||
# (p^12 - 1) / r
|
||||
# BigInt[4314].fromHex"0x2ee1db5dcc825b7e1bda9c0496a1c0a89ee0193d4977b3f7d4507d07363baa13f8d14a917848517badc3a43d1073776ab353f2c30698e8cc7deada9c0aadff5e9cfee9a074e43b9a660835cc872ee83ff3a0f0f1c0ad0d6106feaf4e347aa68ad49466fa927e7bb9375331807a0dce2630d9aa4b113f414386b0e8819328148978e2b0dd39099b86e1ab656d2670d93e4d7acdd350da5359bc73ab61a0c5bf24c374693c49f570bcd2b01f3077ffb10bf24dde41064837f27611212596bc293c8d4c01f25118790f4684d0b9c40a68eb74bb22a40ee7169cdc1041296532fef459f12438dfc8e2886ef965e61a474c5c85b0129127a1b5ad0463434724538411d1676a53b5a62eb34c05739334f46c02c3f0bd0c55d3109cd15948d0a1fad20044ce6ad4c6bec3ec03ef19592004cedd556952c6d8823b19dadd7c2498345c6e5308f1c511291097db60b1749bf9b71a9f9e0100418a3ef0bc627751bbd81367066bca6a4c1b6dcfc5cceb73fc56947a403577dfa9e13c24ea820b09c1d9f7c31759c3635de3f7a3639991708e88adce88177456c49637fd7961be1a4c7e79fb02faa732e2f3ec2bea83d196283313492caa9d4aff1c910e9622d2a73f62537f2701aaef6539314043f7bbce5b78c7869aeb2181a67e49eeed2161daf3f881bd88592d767f67c4717489119226c2f011d4cab803e9d71650a6f80698e2f8491d12191a04406fbc8fbd5f48925f98630e68bfb24c0bcb9b55df57510"
|
||||
# (p^12 - 1) / r * 3
|
||||
BigInt[4316].fromHex"0x8ca592196587127a538fd40dc3e541f9dca04bb7dc671be77cf17715a2b2fe3bea73dfb468d8f473094aecb7315a664019fbd84913caba6579c08fd42009fe1bd6fcbce15eacb2cf3218a165958cb8bfdae2d2d54207282314fc0dea9d6ff3a07dbd34efb77b732ba5f994816e296a72928cfee133bdc3ca9412b984b9783d9c6aa81297ab1cd294a502304773528bbae8706979f28efa0d355b0224e2513d6e4a5d3bb4dde0523678105d9167ff1323d6e99ac312d8a7d762336370c4347bb5a7e405d6f3496b2dd38e722d4c1f3ac25e3167ec2cb543d69430c37c2f98fcdd0dd36caa9f5aa7994cec31b24ed5e515911037b376e521070d29c9d56cfa8c3574363efb20f28c19e4105ab99edd44084bd23725017931d6740bda71e5f07600ce6b407e543c4bc40bcd4c0b600e6c98003bf8548986b14d9098746dc89d154af91ad54f337b31c79222145dd3ed254fdeda0300c49ebcd2352765f533883a3513435f3ee452496f5166c25bf503bd6ec0a0679efda3b46ebf86211d458de749460d4a2a19abe6ea2accb451ab9a096b98465d044dc2a7f86c253a4ee57b6df108eff598a8dbc483bf8b74c2789939db85ffd7e0fd55b32bc26877f5be26fa7d750500ce2fab93c0cbe7336b126a5693d0c16484f37addccc7642590dbe98538990b88637e374d545d9b34b67448d0357e60280bbd8542f1f4e813caa8e8db57364b4e0cc14f35af381dd9b71ec9292b3a3f16e42362d2019e05f30"
|
||||
|
||||
func millerLoopGenericBLS12[C: static Curve](
|
||||
func millerLoopGenericBLS12*[C: static Curve](
|
||||
f: var Fp12[C],
|
||||
P: ECP_SWei_Aff[Fp[C]],
|
||||
Q: ECP_SWei_Aff[Fp2[C]]
|
||||
@ -140,13 +150,104 @@ func pairing_bls12_reference*[C](gt: var Fp12[C], P: ECP_SWei_Proj[Fp[C]], Q: EC
|
||||
gt.millerLoopGenericBLS12(Paff, Qaff)
|
||||
gt.finalExpGeneric()
|
||||
|
||||
func finalExpEasy[C: static Curve](f: var Fp12[C]) =
|
||||
## Easy part of the final exponentiation
|
||||
## We need to clear the GT cofactor to obtain
|
||||
## an unique GT representation
|
||||
## (reminder, GT is a multiplicative group hence we exponentiate by the cofactor)
|
||||
# Optimized pairing implementation
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
func cycl_sqr_repeated(f: var Fp12, num: int) =
|
||||
## Repeated cyclotomic squarings
|
||||
for _ in 0 ..< num:
|
||||
f.cyclotomic_square()
|
||||
|
||||
func pow_xdiv2(r: var Fp12[BLS12_381], a: Fp12[BLS12_381], invert = BLS12_381_param_isNeg) =
|
||||
## f^(x/2) with x the curve parameter
|
||||
## For BLS12_381 f^-0xd201000000010000
|
||||
|
||||
r.cyclotomic_square(a)
|
||||
r *= a
|
||||
r.cycl_sqr_repeated(2)
|
||||
r *= a
|
||||
r.cycl_sqr_repeated(3)
|
||||
r *= a
|
||||
r.cycl_sqr_repeated(9)
|
||||
r *= a
|
||||
r.cycl_sqr_repeated(32) # TODO: use Karabina?
|
||||
r *= a
|
||||
r.cycl_sqr_repeated(16-1) # Don't do the last iteration
|
||||
|
||||
if invert:
|
||||
r.cyclotomic_inv()
|
||||
|
||||
func pow_x(r: var Fp12[BLS12_381], a: Fp12[BLS12_381], invert = BLS12_381_param_isNeg) =
|
||||
## f^x with x the curve parameter
|
||||
## For BLS12_381 f^-0xd201000000010000
|
||||
r.pow_xdiv2(a, invert)
|
||||
r.cyclotomic_square()
|
||||
|
||||
func finalExpHard_BLS12*[C: static Curve](f: var Fp12[C]) =
|
||||
## Hard part of the final exponentiation
|
||||
## Specialized for BLS12 curves
|
||||
##
|
||||
## With an embedding degree of 12, the easy part of final exponentiation is
|
||||
# - Efficient Final Exponentiation
|
||||
# via Cyclotomic Structure for Pairings
|
||||
# over Families of Elliptic Curves
|
||||
# Daiki Hayashida and Kenichiro Hayasaka
|
||||
# and Tadanori Teruya, 2020
|
||||
# https://eprint.iacr.org/2020/875.pdf
|
||||
#
|
||||
# p14: 3 Φ₁₂(p(x))/r(x) = (x−1)² (x+p) (x²+p²−1) + 3
|
||||
#
|
||||
# TODO: paper costs are 4Eₓ+Eₓ/₂+7M₁₂+S₁₂+F₁+F₂
|
||||
# so we have an extra cyclotomic squaring since we use 5Eₓ
|
||||
#
|
||||
# with
|
||||
# - Eₓ being f^x
|
||||
# - Eₓ/₂ being f^(x/2)
|
||||
# - M₁₂ being mul in Fp12
|
||||
# - S₁₂ being cyclotomic squaring
|
||||
# - Fₙ being n Frobenius applications
|
||||
|
||||
var v0 {.noInit.}, v1 {.noInit.}, v2 {.noInit.}: Fp12[C]
|
||||
|
||||
# Save for f³ and (x−1)²
|
||||
v2.cyclotomic_square(f) # v2 = f²
|
||||
|
||||
# (x−1)²
|
||||
v0.pow_xdiv2(v2) # v0 = (f²)^(x/2) = f^x
|
||||
v1.cyclotomic_inv(f) # v1 = f^-1
|
||||
v0 *= v1 # v0 = f^(x-1)
|
||||
v1.pow_x(v0) # v1 = (f^(x-1))^x
|
||||
v0.cyclotomic_inv() # v0 = (f^(x-1))^-1
|
||||
v0 *= v1 # v0 = (f^(x-1))^(x-1) = f^((x-1)*(x-1)) = f^((x-1)²)
|
||||
|
||||
# (x+p)
|
||||
v1.pow_x(v0) # v1 = f^((x-1)².x)
|
||||
v0.frobenius_map(v0) # v0 = f^((x-1)².p)
|
||||
v0 *= v1 # v0 = f^((x-1)².(x+p))
|
||||
|
||||
# + 3
|
||||
f *= v2 # f = f³
|
||||
|
||||
# (x²+p²−1)
|
||||
v2.pow_x(v0, invert = false)
|
||||
v1.pow_x(v2, invert = false) # v1 = f^((x-1)².(x+p).x²)
|
||||
v2.frobenius_map(v0, 2) # v2 = f^((x-1)².(x+p).p²)
|
||||
v0.cyclotomic_inv() # v0 = f^((x-1)².(x+p).-1)
|
||||
v0 *= v1 # v0 = f^((x-1)².(x+p).(x²-1))
|
||||
v0 *= v2 # v0 = f^((x-1)².(x+p).(x²+p²-1))
|
||||
|
||||
# (x−1)².(x+p).(x²+p²−1) + 3
|
||||
f *= v0
|
||||
|
||||
func pairing_bls12*[C](gt: var Fp12[C], P: ECP_SWei_Proj[Fp[C]], Q: ECP_SWei_Proj[Fp2[C]]) =
|
||||
## Compute the optimal Ate Pairing for BLS12 curves
|
||||
## Input: P ∈ G1, Q ∈ G2
|
||||
## Output: e(P, Q) ∈ Gt
|
||||
##
|
||||
## f^(p⁶−1)(p²+1)
|
||||
discard
|
||||
## Reference implementation
|
||||
var Paff {.noInit.}: ECP_SWei_Aff[Fp[C]]
|
||||
var Qaff {.noInit.}: ECP_SWei_Aff[Fp2[C]]
|
||||
Paff.affineFromProjective(P)
|
||||
Qaff.affineFromProjective(Q)
|
||||
gt.millerLoopGenericBLS12(Paff, Qaff)
|
||||
gt.finalExpEasy()
|
||||
gt.finalExpHard_BLS12()
|
||||
|
@ -18,7 +18,8 @@ import
|
||||
ec_weierstrass_projective
|
||||
],
|
||||
./lines_projective,
|
||||
./gt_fp12,
|
||||
./mul_fp12_by_lines,
|
||||
./cyclotomic_fp12,
|
||||
../isogeny/frobenius
|
||||
|
||||
# ############################################################
|
||||
|
@ -214,6 +214,13 @@ func conj*(a: var CubicExt) {.inline.} =
|
||||
a.c1.conjneg()
|
||||
a.c2.conj()
|
||||
|
||||
func conj*(r: var CubicExt, a: CubicExt) {.inline.} =
|
||||
## Computes the conjugate out-of-place
|
||||
mixin conj, conjneg
|
||||
r.c0.conj(a.c0)
|
||||
r.c1.conjneg(a.c1)
|
||||
r.c2.conj(a.c2)
|
||||
|
||||
func square*(a: var CubicExt) {.inline.} =
|
||||
## In-place squaring
|
||||
let t = a
|
||||
|
@ -259,6 +259,11 @@ func conjneg*(a: var QuadraticExt) {.inline.} =
|
||||
## Computes the negated conjugate in-place
|
||||
a.c0.neg()
|
||||
|
||||
func conjneg*(r: var QuadraticExt, a: QuadraticExt) {.inline.} =
|
||||
## Computes the negated conjugate out-of-place
|
||||
r.c0.neg(a.c0)
|
||||
r.c1 = a.c1
|
||||
|
||||
func square*(r: var QuadraticExt, a: QuadraticExt) {.inline.} =
|
||||
mixin fromComplexExtension
|
||||
when r.fromComplexExtension():
|
||||
|
33
tests/t_fp6_frobenius.nim
Normal file
33
tests/t_fp6_frobenius.nim
Normal file
@ -0,0 +1,33 @@
|
||||
# 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/towers,
|
||||
../constantine/config/curves,
|
||||
# Test utilities
|
||||
./t_fp_tower_frobenius_template
|
||||
|
||||
const TestCurves = [
|
||||
# BN254_Nogami
|
||||
# BN254_Snarks,
|
||||
# BLS12_377,
|
||||
BLS12_381,
|
||||
# BN446
|
||||
# FKM12_447
|
||||
# BLS12_461
|
||||
# BN462
|
||||
]
|
||||
|
||||
runFrobeniusTowerTests(
|
||||
ExtDegree =6,
|
||||
Iters = 8,
|
||||
TestCurves = TestCurves,
|
||||
moduleName = "test_fp6_frobenius",
|
||||
testSuiteDesc = "𝔽p6 Frobenius map: Frobenius(a, k) = a^(p^k) (mod p⁶)"
|
||||
)
|
@ -65,7 +65,7 @@ func random_point*(rng: var RngState, EC: typedesc, randZ: bool, gen: RandomGen)
|
||||
else:
|
||||
result = rng.random_long01Seq_with_randZ(EC)
|
||||
|
||||
suite "Pairing - Line Functions on BLS12-381":
|
||||
suite "Pairing - Line Functions on BLS12-381" & " [" & $WordBitwidth & "-bit mode]":
|
||||
test "Line double - lt,t(P)":
|
||||
proc test_line_double(C: static Curve, randZ: bool, gen: RandomGen) =
|
||||
for _ in 0 ..< Iters:
|
||||
|
@ -7,9 +7,11 @@
|
||||
# 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
|
||||
|
||||
runPairingTests(4, BLS12_381, pairing_bls12_reference)
|
||||
# runPairingTests(4, BLS12_381, pairing_bls12_reference)
|
||||
runPairingTests(4, BLS12_381, pairing_bls12)
|
||||
|
@ -7,6 +7,7 @@
|
||||
# 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
|
||||
|
@ -7,6 +7,7 @@
|
||||
# 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
|
||||
|
127
tests/t_pairing_cyclotomic_fp12.nim
Normal file
127
tests/t_pairing_cyclotomic_fp12.nim
Normal file
@ -0,0 +1,127 @@
|
||||
# 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
|
||||
# Standard library
|
||||
std/[tables, unittest, times],
|
||||
# Internals
|
||||
../constantine/config/common,
|
||||
../constantine/[arithmetic, primitives],
|
||||
../constantine/towers,
|
||||
../constantine/config/curves,
|
||||
../constantine/io/io_towers,
|
||||
../constantine/pairing/cyclotomic_fp12,
|
||||
../constantine/isogeny/frobenius,
|
||||
# Test utilities
|
||||
../helpers/[prng_unsafe, static_for]
|
||||
|
||||
const
|
||||
Iters = 4
|
||||
TestCurves = [
|
||||
# BN254_Nogami,
|
||||
# BN254_Snarks,
|
||||
# BLS12_377,
|
||||
BLS12_381
|
||||
]
|
||||
|
||||
type
|
||||
RandomGen = enum
|
||||
Uniform
|
||||
HighHammingWeight
|
||||
Long01Sequence
|
||||
|
||||
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 "test_pairing_fp12_sparse xoshiro512** seed: ", seed
|
||||
|
||||
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)
|
||||
|
||||
suite "Pairing - Cyclotomic subgroup - GΦ₁₂(p) = {α ∈ Fp¹² : α^Φ₁₂(p) ≡ 1 (mod p¹²)}" & " [" & $WordBitwidth & "-bit mode]":
|
||||
test "Easy part of the final exponentiation maps to the cyclotomic subgroup":
|
||||
proc test_final_exp_easy_cycl(C: static Curve, gen: static RandomGen) =
|
||||
for _ in 0 ..< Iters:
|
||||
var f = rng.random_elem(Fp12[C], gen)
|
||||
|
||||
f.finalExpEasy()
|
||||
|
||||
var f4, minus_f2: typeof(f)
|
||||
minus_f2.frobenius_map(f, 2) # f^p²
|
||||
f4.frobenius_map(minus_f2, 2) # f^p⁴
|
||||
minus_f2.conj() # f^⁻²p
|
||||
|
||||
f *= f4
|
||||
f *= minus_f2 # f^(p⁴-p²+1) = f^Φ₁₂(p)
|
||||
|
||||
check: bool(f.isOne())
|
||||
|
||||
staticFor(curve, TestCurves):
|
||||
test_final_exp_easy_cycl(curve, gen = Uniform)
|
||||
test_final_exp_easy_cycl(curve, gen = HighHammingWeight)
|
||||
test_final_exp_easy_cycl(curve, gen = Long01Sequence)
|
||||
|
||||
test "Cyclotomic inverse":
|
||||
proc test_cycl_inverse(C: static Curve, gen: static RandomGen) =
|
||||
for _ in 0 ..< Iters:
|
||||
var f = rng.random_elem(Fp12[C], gen)
|
||||
|
||||
f.finalExpEasy()
|
||||
var g = f
|
||||
|
||||
f.cyclotomic_inv()
|
||||
f *= g
|
||||
|
||||
check: bool(f.isOne())
|
||||
|
||||
staticFor(curve, TestCurves):
|
||||
test_cycl_inverse(curve, gen = Uniform)
|
||||
test_cycl_inverse(curve, gen = HighHammingWeight)
|
||||
test_cycl_inverse(curve, gen = Long01Sequence)
|
||||
|
||||
test "Cyclotomic squaring":
|
||||
proc test_cycl_squaring_in_place(C: static Curve, gen: static RandomGen) =
|
||||
for _ in 0 ..< Iters:
|
||||
var f = rng.random_elem(Fp12[C], gen)
|
||||
|
||||
f.finalExpEasy()
|
||||
var g = f
|
||||
|
||||
f.square()
|
||||
g.cyclotomic_square()
|
||||
|
||||
check: bool(f == g)
|
||||
|
||||
staticFor(curve, TestCurves):
|
||||
test_cycl_squaring_in_place(curve, gen = Uniform)
|
||||
test_cycl_squaring_in_place(curve, gen = HighHammingWeight)
|
||||
test_cycl_squaring_in_place(curve, gen = Long01Sequence)
|
||||
|
||||
proc test_cycl_squaring_out_place(C: static Curve, gen: static RandomGen) =
|
||||
for _ in 0 ..< Iters:
|
||||
var f = rng.random_elem(Fp12[C], gen)
|
||||
|
||||
f.finalExpEasy()
|
||||
var g = f
|
||||
var r: typeof(f)
|
||||
|
||||
f.square()
|
||||
r.cyclotomic_square(g)
|
||||
|
||||
check: bool(f == r)
|
||||
|
||||
staticFor(curve, TestCurves):
|
||||
test_cycl_squaring_out_place(curve, gen = Uniform)
|
||||
test_cycl_squaring_out_place(curve, gen = HighHammingWeight)
|
||||
test_cycl_squaring_out_place(curve, gen = Long01Sequence)
|
@ -17,7 +17,7 @@ import
|
||||
../constantine/io/io_towers,
|
||||
../constantine/pairing/[
|
||||
lines_projective,
|
||||
gt_fp12
|
||||
mul_fp12_by_lines
|
||||
],
|
||||
# Test utilities
|
||||
../helpers/[prng_unsafe, static_for]
|
||||
@ -50,6 +50,25 @@ func random_elem(rng: var RngState, F: typedesc, gen: RandomGen): F {.inline, no
|
||||
result = rng.random_long01Seq(F)
|
||||
|
||||
suite "Pairing - Sparse 𝔽p12 multiplication by line function is consistent with dense 𝔽p12 mul":
|
||||
test "Dense 𝔽p4 by Sparse 0y":
|
||||
proc test_fp4_0y(C: static Curve, gen: static RandomGen) =
|
||||
for _ in 0 ..< Iters:
|
||||
let a = rng.random_elem(Fp4[C], gen)
|
||||
let y = rng.random_elem(Fp2[C], gen)
|
||||
let b = Fp4[C](c1: y)
|
||||
|
||||
var r {.noInit.}, r2 {.noInit.}: Fp4[C]
|
||||
|
||||
r.prod(a, b)
|
||||
r2.mul_sparse_by_0y(a, y)
|
||||
|
||||
check: bool(r == r2)
|
||||
|
||||
staticFor(curve, TestCurves):
|
||||
test_fp4_0y(curve, gen = Uniform)
|
||||
test_fp4_0y(curve, gen = HighHammingWeight)
|
||||
test_fp4_0y(curve, gen = Long01Sequence)
|
||||
|
||||
test "Dense 𝔽p6 by Sparse 0y0":
|
||||
proc test_fp6_0y0(C: static Curve, gen: static RandomGen) =
|
||||
for _ in 0 ..< Iters:
|
||||
@ -91,7 +110,7 @@ suite "Pairing - Sparse 𝔽p12 multiplication by line function is consistent wi
|
||||
test_fp6_0y0(curve, gen = Long01Sequence)
|
||||
|
||||
when Fp12[BN254_Snarks]().c0.typeof is Fp6:
|
||||
test "Sparse 𝔽p12 resulting from xy00z0 line function":
|
||||
test "Sparse 𝔽p12/𝔽p6 resulting from xy00z0 line function":
|
||||
proc test_fp12_xy00z0(C: static Curve, gen: static RandomGen) =
|
||||
for _ in 0 ..< Iters:
|
||||
var a = rng.random_elem(Fp12[C], gen)
|
||||
@ -117,7 +136,7 @@ suite "Pairing - Sparse 𝔽p12 multiplication by line function is consistent wi
|
||||
test_fp12_xy00z0(curve, gen = HighHammingWeight)
|
||||
test_fp12_xy00z0(curve, gen = Long01Sequence)
|
||||
|
||||
test "Sparse 𝔽p12 resulting from xyz000 line function":
|
||||
test "Sparse 𝔽p12/𝔽p6 resulting from xyz000 line function":
|
||||
proc test_fp12_xyz000(C: static Curve, gen: static RandomGen) =
|
||||
for _ in 0 ..< Iters:
|
||||
var a = rng.random_elem(Fp12[C], gen)
|
||||
@ -141,3 +160,32 @@ suite "Pairing - Sparse 𝔽p12 multiplication by line function is consistent wi
|
||||
test_fp12_xyz000(curve, gen = Uniform)
|
||||
test_fp12_xyz000(curve, gen = HighHammingWeight)
|
||||
test_fp12_xyz000(curve, gen = Long01Sequence)
|
||||
else:
|
||||
static: doAssert Fp12[BN254_Snarks]().c0.typeof is Fp4
|
||||
|
||||
test "Sparse 𝔽p12/𝔽p4 resulting from xy000z line function":
|
||||
proc test_fp12_xy000z(C: static Curve, gen: static RandomGen) =
|
||||
for _ in 0 ..< Iters:
|
||||
var a = rng.random_elem(Fp12[C], gen)
|
||||
var a2 = a
|
||||
|
||||
var x = rng.random_elem(Fp2[C], gen)
|
||||
var y = rng.random_elem(Fp2[C], gen)
|
||||
var z = rng.random_elem(Fp2[C], gen)
|
||||
|
||||
let line = Line[Fp2[C], Mtwist](x: x, y: y, z: z)
|
||||
let b = Fp12[C](
|
||||
c0: Fp4[C](c0: x, c1: y),
|
||||
# c1
|
||||
c2: Fp4[C]( c1: z),
|
||||
)
|
||||
|
||||
a *= b
|
||||
a2.mul_sparse_by_line_xy000z(line)
|
||||
|
||||
check: bool(a == a2)
|
||||
|
||||
staticFor(curve, TestCurves):
|
||||
test_fp12_xy000z(curve, gen = Uniform)
|
||||
test_fp12_xy000z(curve, gen = HighHammingWeight)
|
||||
test_fp12_xy000z(curve, gen = Long01Sequence)
|
@ -10,6 +10,7 @@ import
|
||||
# Standard library
|
||||
std/unittest, times,
|
||||
# Internals
|
||||
../constantine/config/common,
|
||||
../constantine/[arithmetic, primitives],
|
||||
../constantine/towers,
|
||||
../constantine/config/curves,
|
||||
@ -75,14 +76,13 @@ template runPairingTests*(Iters: static int, C: static Curve, pairing_fn: untype
|
||||
r2.pairing_fn(P2, Q)
|
||||
r3.pairing_fn(P, Q2)
|
||||
|
||||
check:
|
||||
bool(not r.isZero())
|
||||
bool(not r.isOne())
|
||||
bool(r == r2)
|
||||
bool(r == r3)
|
||||
bool(r2 == r3)
|
||||
doAssert bool(not r.isZero())
|
||||
doAssert bool(not r.isOne())
|
||||
doAssert bool(r == r2)
|
||||
doAssert bool(r == r3)
|
||||
doAssert bool(r2 == r3)
|
||||
|
||||
suite "Pairing - Optimal Ate on " & $C:
|
||||
suite "Pairing - Optimal Ate on " & $C & " [" & $WordBitwidth & "-bit mode]":
|
||||
test "Bilinearity e([2]P, Q) = e(P, [2]Q) = e(P, Q)^2":
|
||||
test_bilinearity_double_impl(randZ = false, gen = Uniform)
|
||||
test_bilinearity_double_impl(randZ = true, gen = Uniform)
|
||||
|
Loading…
x
Reference in New Issue
Block a user