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:
Mamy Ratsimbazafy 2020-09-24 17:18:23 +02:00 committed by GitHub
parent 0c18f4436c
commit f78ed23dad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 928 additions and 59 deletions

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

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

View File

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

View File

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

View File

@ -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, 2136 (2019)
- Efficient Final Exponentiation
via Cyclotomic Structure for Pairings
over Families of Elliptic Curves

View 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".}

View File

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

View File

@ -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, 2136 (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) = (x1)² (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 (x1)²
v2.cyclotomic_square(f) # v2 = f²
# (x1)²
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))
# (x1)².(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()

View File

@ -18,7 +18,8 @@ import
ec_weierstrass_projective
],
./lines_projective,
./gt_fp12,
./mul_fp12_by_lines,
./cyclotomic_fp12,
../isogeny/frobenius
# ############################################################

View File

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

View File

@ -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
View 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⁶)"
)

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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