mirror of
https://github.com/logos-storage/constantine.git
synced 2026-01-02 13:13:07 +00:00
BLS12-377 (#91)
* add Sage for constant time tonelli shanks * Fused sqrt and invsqrt via Tonelli Shanks * isolate sqrt in their own folder * Implement constant-time Tonelli Shanks for any prime * Implement Fp2 sqrt for any non-residue * Add tests for BLS12_377 * Lattice decomposition script for BLS12_377 G1 * BLS12-377 G1 GLV ok, G2 GLV issue * Proper endomorphism acceleration support for BLS12-377 * Add naive pairing support for BLS12-377 * Activate more bench for BLS12-377 * Fix MSB computation * Optimize final exponentiation + add benches
This commit is contained in:
parent
ac37b55aa1
commit
0e4dbfe400
@ -35,7 +35,7 @@ const AvailableCurves = [
|
||||
# Curve25519,
|
||||
# P256,
|
||||
# Secp256k1,
|
||||
# BLS12_377,
|
||||
BLS12_377,
|
||||
BLS12_381,
|
||||
# BN446,
|
||||
# FKM12_447,
|
||||
@ -48,23 +48,17 @@ proc main() =
|
||||
staticFor i, 0, AvailableCurves.len:
|
||||
const curve = AvailableCurves[i]
|
||||
addBench(ECP_SWei_Proj[Fp[curve]], Iters)
|
||||
separator()
|
||||
mixedAddBench(ECP_SWei_Proj[Fp[curve]], Iters)
|
||||
separator()
|
||||
doublingBench(ECP_SWei_Proj[Fp[curve]], Iters)
|
||||
separator()
|
||||
scalarMulUnsafeDoubleAddBench(ECP_SWei_Proj[Fp[curve]], MulIters)
|
||||
separator()
|
||||
scalarMulGenericBench(ECP_SWei_Proj[Fp[curve]], window = 2, MulIters)
|
||||
separator()
|
||||
scalarMulGenericBench(ECP_SWei_Proj[Fp[curve]], window = 3, MulIters)
|
||||
separator()
|
||||
scalarMulGenericBench(ECP_SWei_Proj[Fp[curve]], window = 4, MulIters)
|
||||
separator()
|
||||
scalarMulGenericBench(ECP_SWei_Proj[Fp[curve]], window = 5, MulIters)
|
||||
separator()
|
||||
scalarMulEndo(ECP_SWei_Proj[Fp[curve]], MulIters)
|
||||
separator()
|
||||
scalarMulEndoWindow(ECP_SWei_Proj[Fp[curve]], MulIters)
|
||||
separator()
|
||||
separator()
|
||||
|
||||
@ -36,7 +36,7 @@ const AvailableCurves = [
|
||||
# Curve25519,
|
||||
# P256,
|
||||
# Secp256k1,
|
||||
# BLS12_377,
|
||||
BLS12_377,
|
||||
BLS12_381,
|
||||
# BN446,
|
||||
# FKM12_447,
|
||||
@ -49,23 +49,19 @@ proc main() =
|
||||
staticFor i, 0, AvailableCurves.len:
|
||||
const curve = AvailableCurves[i]
|
||||
addBench(ECP_SWei_Proj[Fp2[curve]], Iters)
|
||||
separator()
|
||||
mixedAddBench(ECP_SWei_Proj[Fp2[curve]], Iters)
|
||||
separator()
|
||||
doublingBench(ECP_SWei_Proj[Fp2[curve]], Iters)
|
||||
separator()
|
||||
scalarMulUnsafeDoubleAddBench(ECP_SWei_Proj[Fp2[curve]], MulIters)
|
||||
separator()
|
||||
scalarMulGenericBench(ECP_SWei_Proj[Fp2[curve]], window = 2, MulIters)
|
||||
separator()
|
||||
scalarMulGenericBench(ECP_SWei_Proj[Fp2[curve]], window = 3, MulIters)
|
||||
separator()
|
||||
scalarMulGenericBench(ECP_SWei_Proj[Fp2[curve]], window = 4, MulIters)
|
||||
separator()
|
||||
scalarMulGenericBench(ECP_SWei_Proj[Fp2[curve]], window = 5, MulIters)
|
||||
separator()
|
||||
scalarMulEndo(ECP_SWei_Proj[Fp2[curve]], MulIters)
|
||||
separator()
|
||||
separator()
|
||||
separator()
|
||||
|
||||
main()
|
||||
|
||||
@ -28,12 +28,12 @@ const Iters = 1_000_000
|
||||
const ExponentIters = 1000
|
||||
const AvailableCurves = [
|
||||
# P224,
|
||||
# BN254_Nogami,
|
||||
BN254_Nogami,
|
||||
BN254_Snarks,
|
||||
# Curve25519,
|
||||
# P256,
|
||||
# Secp256k1,
|
||||
# BLS12_377,
|
||||
BLS12_377,
|
||||
BLS12_381,
|
||||
# BN446,
|
||||
# FKM12_447,
|
||||
@ -52,7 +52,8 @@ proc main() =
|
||||
sqrBench(Fp[curve], Iters)
|
||||
invEuclidBench(Fp[curve], ExponentIters)
|
||||
invPowFermatBench(Fp[curve], ExponentIters)
|
||||
invAddChainBench(Fp[curve], ExponentIters)
|
||||
when curve in {BN254_Snarks, BLS12_381}:
|
||||
invAddChainBench(Fp[curve], ExponentIters)
|
||||
sqrtBench(Fp[curve], ExponentIters)
|
||||
# Exponentiation by a "secret" of size ~the curve order
|
||||
powBench(Fp[curve], ExponentIters)
|
||||
|
||||
@ -27,7 +27,7 @@ const Iters = 10_000
|
||||
const InvIters = 1000
|
||||
const AvailableCurves = [
|
||||
# Pairing-Friendly curves
|
||||
# BN254_Nogami,
|
||||
BN254_Nogami,
|
||||
BN254_Snarks,
|
||||
BLS12_377,
|
||||
BLS12_381
|
||||
|
||||
@ -27,9 +27,9 @@ const Iters = 1_000_000
|
||||
const InvIters = 1000
|
||||
const AvailableCurves = [
|
||||
# Pairing-Friendly curves
|
||||
# BN254_Nogami,
|
||||
BN254_Nogami,
|
||||
BN254_Snarks,
|
||||
# BLS12_377,
|
||||
BLS12_377,
|
||||
BLS12_381
|
||||
# BN446,
|
||||
# FKM12_447,
|
||||
|
||||
@ -27,7 +27,7 @@ const Iters = 100_000
|
||||
const InvIters = 1000
|
||||
const AvailableCurves = [
|
||||
# Pairing-Friendly curves
|
||||
# BN254_Nogami,
|
||||
BN254_Nogami,
|
||||
BN254_Snarks,
|
||||
BLS12_377,
|
||||
BLS12_381
|
||||
|
||||
51
benchmarks/bench_pairing_bls12_377.nim
Normal file
51
benchmarks/bench_pairing_bls12_377.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_377,
|
||||
]
|
||||
|
||||
proc main() =
|
||||
separator()
|
||||
staticFor i, 0, AvailableCurves.len:
|
||||
const curve = AvailableCurves[i]
|
||||
lineDoubleBench(curve, Iters)
|
||||
lineAddBench(curve, Iters)
|
||||
mulFp12byLine_xyz000_Bench(curve, Iters)
|
||||
separator()
|
||||
finalExpEasyBench(curve, Iters)
|
||||
finalExpHardBLS12Bench(curve, Iters)
|
||||
separator()
|
||||
millerLoopBLS12Bench(curve, Iters)
|
||||
finalExpBLS12Bench(curve, Iters)
|
||||
separator()
|
||||
pairingBLS12Bench(curve, Iters)
|
||||
separator()
|
||||
|
||||
main()
|
||||
notes()
|
||||
@ -65,9 +65,14 @@ const testDesc: seq[tuple[path: string, useGMP: bool]] = @[
|
||||
("tests/t_ec_wstrass_prj_g2_mul_sanity_bls12_381.nim", false),
|
||||
("tests/t_ec_wstrass_prj_g2_mul_distri_bls12_381.nim", false),
|
||||
("tests/t_ec_wstrass_prj_g2_mul_vs_ref_bls12_381.nim", false),
|
||||
("tests/t_ec_wstrass_prj_g2_add_double_bls12_377.nim", false),
|
||||
("tests/t_ec_wstrass_prj_g2_mul_sanity_bls12_377.nim", false),
|
||||
("tests/t_ec_wstrass_prj_g2_mul_distri_bls12_377.nim", false),
|
||||
("tests/t_ec_wstrass_prj_g2_mul_vs_ref_bls12_377.nim", false),
|
||||
# Elliptic curve arithmetic vs Sagemath
|
||||
("tests/t_ec_frobenius.nim", false),
|
||||
("tests/t_ec_sage_bn254.nim", false),
|
||||
("tests/t_ec_sage_bls12_377.nim", false),
|
||||
("tests/t_ec_sage_bls12_381.nim", false),
|
||||
# Edge cases highlighted by past bugs
|
||||
("tests/t_ec_wstrass_prj_edge_cases.nim", false),
|
||||
@ -76,7 +81,8 @@ const testDesc: seq[tuple[path: string, useGMP: bool]] = @[
|
||||
("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)
|
||||
("tests/t_pairing_bls12_377_optate.nim", false),
|
||||
("tests/t_pairing_bls12_381_optate.nim", false),
|
||||
]
|
||||
|
||||
# For temporary (hopefully) investigation that can only be reproduced in CI
|
||||
@ -152,11 +158,16 @@ task test, "Run all tests":
|
||||
# Ensure benchmarks stay relevant. Ignore Windows 32-bit at the moment
|
||||
if not defined(windows) or not (existsEnv"UCPU" or getEnv"UCPU" == "i686"):
|
||||
runBench("bench_fp")
|
||||
runBench("bench_fp_double_width")
|
||||
runBench("bench_fp2")
|
||||
runBench("bench_fp6")
|
||||
runBench("bench_fp12")
|
||||
runBench("bench_ec_g1")
|
||||
runBench("bench_ec_g2")
|
||||
runBench("bench_pairing_bls12_377")
|
||||
runBench("bench_pairing_bls12_381")
|
||||
runBench("bench_pairing_bn254_nogami")
|
||||
runBench("bench_pairing_bn254_snarks")
|
||||
|
||||
task test_no_gmp, "Run tests that don't require GMP":
|
||||
# -d:testingCurves is configured in a *.nim.cfg for convenience
|
||||
@ -449,6 +460,21 @@ 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_377, "Run pairings benchmarks for BLS12-377 - Default compiler":
|
||||
runBench("bench_pairing_bls12_377")
|
||||
|
||||
task bench_pairing_bls12_377_gcc, "Run pairings benchmarks for BLS12-377 - GCC":
|
||||
runBench("bench_pairing_bls12_377", "gcc")
|
||||
|
||||
task bench_pairing_bls12_377_clang, "Run pairings benchmarks for BLS12-377 - Clang":
|
||||
runBench("bench_pairing_bls12_377", "clang")
|
||||
|
||||
task bench_pairing_bls12_377_gcc_noasm, "Run pairings benchmarks for BLS12-377 - GCC no Assembly":
|
||||
runBench("bench_pairing_bls12_377", "gcc", useAsm = false)
|
||||
|
||||
task bench_pairing_bls12_377_clang_noasm, "Run pairings benchmarks for BLS12-377 - Clang no Assembly":
|
||||
runBench("bench_pairing_bls12_377", "clang", useAsm = false)
|
||||
|
||||
task bench_pairing_bls12_381, "Run pairings benchmarks for BLS12-381 - Default compiler":
|
||||
runBench("bench_pairing_bls12_381")
|
||||
|
||||
|
||||
@ -8,8 +8,16 @@
|
||||
|
||||
import
|
||||
arithmetic/bigints,
|
||||
arithmetic/[finite_fields, finite_fields_inversion, finite_fields_double_width]
|
||||
arithmetic/[
|
||||
finite_fields,
|
||||
finite_fields_inversion,
|
||||
finite_fields_square_root,
|
||||
finite_fields_double_width
|
||||
]
|
||||
|
||||
export
|
||||
bigints,
|
||||
finite_fields, finite_fields_inversion, finite_fields_double_width
|
||||
finite_fields,
|
||||
finite_fields_inversion,
|
||||
finite_fields_square_root,
|
||||
finite_fields_double_width
|
||||
|
||||
@ -130,6 +130,10 @@ func isOdd*(a: BigInt): SecretBool =
|
||||
## Returns true if a is odd
|
||||
a.limbs.isOdd
|
||||
|
||||
func isEven*(a: BigInt): SecretBool =
|
||||
## Returns true if a is even
|
||||
a.limbs.isEven
|
||||
|
||||
func isMsbSet*(a: BigInt): SecretBool =
|
||||
## Returns true if MSB is set
|
||||
## i.e. if a BigInt is interpreted
|
||||
@ -138,9 +142,9 @@ func isMsbSet*(a: BigInt): SecretBool =
|
||||
## This is equivalent to checking
|
||||
## if the number is negative
|
||||
|
||||
# MSB is at announced bits - (wordsRequired - 1)
|
||||
const msb_pos = BigInt.bits-1 - (BigInt.bits.wordsRequired - 1)
|
||||
SecretBool((BaseType(a.limbs[a.limbs.len-1]) shr msb_pos) and 1)
|
||||
# MSB is at announced bits - (wordsRequired-1)*WordBitWidth - 1
|
||||
const msb_in_msw = BigInt.bits - (BigInt.bits.wordsRequired-1)*WordBitWidth - 1
|
||||
SecretBool((BaseType(a.limbs[a.limbs.len-1]) shr msb_in_msw) and 1)
|
||||
|
||||
func eq*(a: BigInt, n: SecretWord): SecretBool =
|
||||
## Returns true if ``a`` is equal
|
||||
@ -546,6 +550,9 @@ func montyPowUnsafeExponent*[mBits: static int](
|
||||
var scratchSpace {.noInit.}: array[scratchLen, Limbs[mBits.wordsRequired]]
|
||||
montyPowUnsafeExponent(a.limbs, exponent, M.limbs, one.limbs, negInvModWord, scratchSpace, canUseNoCarryMontyMul, canUseNoCarryMontySquare)
|
||||
|
||||
from ../io/io_bigints import exportRawUint
|
||||
# Workaround recursive dependencies
|
||||
|
||||
func montyPow*[mBits, eBits: static int](
|
||||
a: var BigInt[mBits], exponent: BigInt[eBits],
|
||||
M, one: BigInt[mBits], negInvModWord: static BaseType, windowSize: static int,
|
||||
@ -560,8 +567,6 @@ func montyPow*[mBits, eBits: static int](
|
||||
##
|
||||
## This is constant-time: the window optimization does
|
||||
## not reveal the exponent bits or hamming weight
|
||||
mixin exportRawUint # exported in io_bigints which depends on this module ...
|
||||
|
||||
var expBE {.noInit.}: array[(ebits + 7) div 8, byte]
|
||||
expBE.exportRawUint(exponent, bigEndian)
|
||||
|
||||
@ -585,8 +590,6 @@ func montyPowUnsafeExponent*[mBits, eBits: static int](
|
||||
##
|
||||
## This uses fixed window optimization
|
||||
## A window size in the range [1, 5] must be chosen
|
||||
mixin exportRawUint # exported in io_bigints which depends on this module ...
|
||||
|
||||
var expBE {.noInit.}: array[(ebits + 7) div 8, byte]
|
||||
expBE.exportRawUint(exponent, bigEndian)
|
||||
|
||||
|
||||
@ -27,6 +27,7 @@
|
||||
import
|
||||
../primitives,
|
||||
../config/[common, type_fp, curves],
|
||||
../io/io_bigints,
|
||||
./bigints, ./limbs_montgomery
|
||||
|
||||
when UseASM_X86_64:
|
||||
@ -307,153 +308,6 @@ func powUnsafeExponent*(a: var Fp, exponent: openarray[byte]) {.inline.} =
|
||||
Fp.C.canUseNoCarryMontySquare()
|
||||
)
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
# Field arithmetic square roots
|
||||
#
|
||||
# ############################################################
|
||||
|
||||
func isSquare*[C](a: Fp[C]): SecretBool {.inline.} =
|
||||
## Returns true if ``a`` is a square (quadratic residue) in 𝔽p
|
||||
##
|
||||
## Assumes that the prime modulus ``p`` is public.
|
||||
# Implementation: we use exponentiation by (p-1)/2 (Euler's criterion)
|
||||
# as it can reuse the exponentiation implementation
|
||||
# Note that we don't care about leaking the bits of p
|
||||
# as we assume that
|
||||
var xi {.noInit.} = a # TODO: is noInit necessary? see https://github.com/mratsim/constantine/issues/21
|
||||
xi.powUnsafeExponent(C.getPrimeMinus1div2_BE())
|
||||
result = not(xi.mres == C.getMontyPrimeMinus1())
|
||||
# xi can be:
|
||||
# - 1 if a square
|
||||
# - 0 if 0
|
||||
# - -1 if a quadratic non-residue
|
||||
debug:
|
||||
doAssert: bool(
|
||||
xi.isZero or
|
||||
xi.isOne or
|
||||
xi.mres == C.getMontyPrimeMinus1()
|
||||
)
|
||||
|
||||
func sqrt_p3mod4[C](a: var Fp[C]) {.inline.} =
|
||||
## Compute the square root of ``a``
|
||||
##
|
||||
## This requires ``a`` to be a square
|
||||
## and the prime field modulus ``p``: p ≡ 3 (mod 4)
|
||||
##
|
||||
## The result is undefined otherwise
|
||||
##
|
||||
## The square root, if it exist is multivalued,
|
||||
## i.e. both x² == (-x)²
|
||||
## This procedure returns a deterministic result
|
||||
static: doAssert BaseType(C.Mod.limbs[0]) mod 4 == 3
|
||||
a.powUnsafeExponent(C.getPrimePlus1div4_BE())
|
||||
|
||||
func sqrt_invsqrt_p3mod4[C](sqrt, invsqrt: var Fp[C], a: Fp[C]) {.inline.} =
|
||||
## If ``a`` is a square, compute the square root of ``a`` in sqrt
|
||||
## and the inverse square root of a in invsqrt
|
||||
##
|
||||
## This assumes that the prime field modulus ``p``: p ≡ 3 (mod 4)
|
||||
# TODO: deterministic sign
|
||||
#
|
||||
# Algorithm
|
||||
#
|
||||
#
|
||||
# From Euler's criterion: a^((p-1)/2)) ≡ 1 (mod p) if square
|
||||
# a^((p-1)/2)) * a^-1 ≡ 1/a (mod p)
|
||||
# a^((p-3)/2)) ≡ 1/a (mod p)
|
||||
# a^((p-3)/4)) ≡ 1/√a (mod p) # Requires p ≡ 3 (mod 4)
|
||||
static: doAssert BaseType(C.Mod.limbs[0]) mod 4 == 3
|
||||
|
||||
invsqrt = a
|
||||
invsqrt.powUnsafeExponent(C.getPrimeMinus3div4_BE())
|
||||
# √a ≡ a * 1/√a ≡ a^((p+1)/4) (mod p)
|
||||
sqrt.prod(invsqrt, a)
|
||||
|
||||
func sqrt_invsqrt_if_square_p3mod4[C](sqrt, invsqrt: var Fp[C], a: Fp[C]): SecretBool {.inline.} =
|
||||
## If ``a`` is a square, compute the square root of ``a`` in sqrt
|
||||
## and the inverse square root of a in invsqrt
|
||||
##
|
||||
## If a is not square, sqrt and invsqrt are undefined
|
||||
##
|
||||
## This assumes that the prime field modulus ``p``: p ≡ 3 (mod 4)
|
||||
sqrt_invsqrt_p3mod4(sqrt, invsqrt, a)
|
||||
var euler {.noInit.}: Fp[C]
|
||||
euler.prod(sqrt, invsqrt)
|
||||
result = not(euler.mres == C.getMontyPrimeMinus1())
|
||||
|
||||
func sqrt_if_square_p3mod4[C](a: var Fp[C]): SecretBool {.inline.} =
|
||||
## If ``a`` is a square, compute the square root of ``a``
|
||||
## if not, ``a`` is unmodified.
|
||||
##
|
||||
## This assumes that the prime field modulus ``p``: p ≡ 3 (mod 4)
|
||||
##
|
||||
## The result is undefined otherwise
|
||||
##
|
||||
## The square root, if it exist is multivalued,
|
||||
## i.e. both x² == (-x)²
|
||||
## This procedure returns a deterministic result
|
||||
var sqrt {.noInit.}, invsqrt {.noInit.}: Fp[C]
|
||||
result = sqrt_invsqrt_if_square_p3mod4(sqrt, invsqrt, a)
|
||||
a.ccopy(sqrt, result)
|
||||
|
||||
func sqrt*[C](a: var Fp[C]) {.inline.} =
|
||||
## Compute the square root of ``a``
|
||||
##
|
||||
## This requires ``a`` to be a square
|
||||
##
|
||||
## The result is undefined otherwise
|
||||
##
|
||||
## The square root, if it exist is multivalued,
|
||||
## i.e. both x² == (-x)²
|
||||
## This procedure returns a deterministic result
|
||||
when BaseType(C.Mod.limbs[0]) mod 4 == 3:
|
||||
sqrt_p3mod4(a)
|
||||
else:
|
||||
{.error: "Square root is only implemented for p ≡ 3 (mod 4)".}
|
||||
|
||||
func sqrt_if_square*[C](a: var Fp[C]): SecretBool {.inline.} =
|
||||
## If ``a`` is a square, compute the square root of ``a``
|
||||
## if not, ``a`` is unmodified.
|
||||
##
|
||||
## The square root, if it exist is multivalued,
|
||||
## i.e. both x² == (-x)²
|
||||
## This procedure returns a deterministic result
|
||||
when BaseType(C.Mod.limbs[0]) mod 4 == 3:
|
||||
result = sqrt_if_square_p3mod4(a)
|
||||
else:
|
||||
{.error: "Square root is only implemented for p ≡ 3 (mod 4)".}
|
||||
|
||||
func sqrt_invsqrt*[C](sqrt, invsqrt: var Fp[C], a: Fp[C]) {.inline.} =
|
||||
## Compute the square root and inverse square root of ``a``
|
||||
##
|
||||
## This requires ``a`` to be a square
|
||||
##
|
||||
## The result is undefined otherwise
|
||||
##
|
||||
## The square root, if it exist is multivalued,
|
||||
## i.e. both x² == (-x)²
|
||||
## This procedure returns a deterministic result
|
||||
when BaseType(C.Mod.limbs[0]) mod 4 == 3:
|
||||
sqrt_invsqrt_p3mod4(sqrt, invsqrt, a)
|
||||
else:
|
||||
{.error: "Square root is only implemented for p ≡ 3 (mod 4)".}
|
||||
|
||||
func sqrt_invsqrt_if_square*[C](sqrt, invsqrt: var Fp[C], a: Fp[C]): SecretBool {.inline.} =
|
||||
## Compute the square root and ivnerse square root of ``a``
|
||||
##
|
||||
## This returns true if ``a`` is square and sqrt/invsqrt contains the square root/inverse square root
|
||||
##
|
||||
## The result is undefined otherwise
|
||||
##
|
||||
## The square root, if it exist is multivalued,
|
||||
## i.e. both x² == (-x)²
|
||||
## This procedure returns a deterministic result
|
||||
when BaseType(C.Mod.limbs[0]) mod 4 == 3:
|
||||
result = sqrt_invsqrt_if_square_p3mod4(sqrt, invsqrt, a)
|
||||
else:
|
||||
{.error: "Square root is only implemented for p ≡ 3 (mod 4)".}
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
# Field arithmetic ergonomic primitives
|
||||
|
||||
269
constantine/arithmetic/finite_fields_square_root.nim
Normal file
269
constantine/arithmetic/finite_fields_square_root.nim
Normal file
@ -0,0 +1,269 @@
|
||||
# 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
|
||||
std/macros,
|
||||
../primitives,
|
||||
../config/[common, type_fp, curves],
|
||||
../io/[io_bigints, io_fields],
|
||||
./bigints, ./finite_fields, ./limbs_montgomery
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
# Field arithmetic square roots
|
||||
#
|
||||
# ############################################################
|
||||
|
||||
# Legendre symbol / Euler's Criterion / Kronecker's symbol
|
||||
# ------------------------------------------------------------
|
||||
|
||||
func isSquare*[C](a: Fp[C]): SecretBool {.inline.} =
|
||||
## Returns true if ``a`` is a square (quadratic residue) in 𝔽p
|
||||
##
|
||||
## Assumes that the prime modulus ``p`` is public.
|
||||
# Implementation: we use exponentiation by (p-1)/2 (Euler's criterion)
|
||||
# as it can reuse the exponentiation implementation
|
||||
# Note that we don't care about leaking the bits of p
|
||||
# as we assume that
|
||||
var xi {.noInit.} = a # TODO: is noInit necessary? see https://github.com/mratsim/constantine/issues/21
|
||||
xi.powUnsafeExponent(C.getPrimeMinus1div2_BE())
|
||||
result = not(xi.mres == C.getMontyPrimeMinus1())
|
||||
# xi can be:
|
||||
# - 1 if a square
|
||||
# - 0 if 0
|
||||
# - -1 if a quadratic non-residue
|
||||
debug:
|
||||
doAssert: bool(
|
||||
xi.isZero or
|
||||
xi.isOne or
|
||||
xi.mres == C.getMontyPrimeMinus1()
|
||||
)
|
||||
|
||||
# Specialized routine for p ≡ 3 (mod 4)
|
||||
# ------------------------------------------------------------
|
||||
|
||||
func sqrt_p3mod4[C](a: var Fp[C]) {.inline.} =
|
||||
## Compute the square root of ``a``
|
||||
##
|
||||
## This requires ``a`` to be a square
|
||||
## and the prime field modulus ``p``: p ≡ 3 (mod 4)
|
||||
##
|
||||
## The result is undefined otherwise
|
||||
##
|
||||
## The square root, if it exist is multivalued,
|
||||
## i.e. both x² == (-x)²
|
||||
## This procedure returns a deterministic result
|
||||
static: doAssert BaseType(C.Mod.limbs[0]) mod 4 == 3
|
||||
a.powUnsafeExponent(C.getPrimePlus1div4_BE())
|
||||
|
||||
func sqrt_invsqrt_p3mod4[C](sqrt, invsqrt: var Fp[C], a: Fp[C]) {.inline.} =
|
||||
## If ``a`` is a square, compute the square root of ``a`` in sqrt
|
||||
## and the inverse square root of a in invsqrt
|
||||
##
|
||||
## This assumes that the prime field modulus ``p``: p ≡ 3 (mod 4)
|
||||
# TODO: deterministic sign
|
||||
#
|
||||
# Algorithm
|
||||
#
|
||||
#
|
||||
# From Euler's criterion: a^((p-1)/2)) ≡ 1 (mod p) if square
|
||||
# a^((p-1)/2)) * a^-1 ≡ 1/a (mod p)
|
||||
# a^((p-3)/2)) ≡ 1/a (mod p)
|
||||
# a^((p-3)/4)) ≡ 1/√a (mod p) # Requires p ≡ 3 (mod 4)
|
||||
static: doAssert BaseType(C.Mod.limbs[0]) mod 4 == 3
|
||||
|
||||
invsqrt = a
|
||||
invsqrt.powUnsafeExponent(C.getPrimeMinus3div4_BE())
|
||||
# √a ≡ a * 1/√a ≡ a^((p+1)/4) (mod p)
|
||||
sqrt.prod(invsqrt, a)
|
||||
|
||||
func sqrt_invsqrt_if_square_p3mod4[C](sqrt, invsqrt: var Fp[C], a: Fp[C]): SecretBool {.inline.} =
|
||||
## If ``a`` is a square, compute the square root of ``a`` in sqrt
|
||||
## and the inverse square root of a in invsqrt
|
||||
##
|
||||
## If a is not square, sqrt and invsqrt are undefined
|
||||
##
|
||||
## This assumes that the prime field modulus ``p``: p ≡ 3 (mod 4)
|
||||
sqrt_invsqrt_p3mod4(sqrt, invsqrt, a)
|
||||
var euler {.noInit.}: Fp[C]
|
||||
euler.prod(sqrt, invsqrt)
|
||||
result = not(euler.mres == C.getMontyPrimeMinus1())
|
||||
|
||||
func sqrt_if_square_p3mod4[C](a: var Fp[C]): SecretBool {.inline.} =
|
||||
## If ``a`` is a square, compute the square root of ``a``
|
||||
## if not, ``a`` is unmodified.
|
||||
##
|
||||
## This assumes that the prime field modulus ``p``: p ≡ 3 (mod 4)
|
||||
##
|
||||
## The result is undefined otherwise
|
||||
##
|
||||
## The square root, if it exist is multivalued,
|
||||
## i.e. both x² == (-x)²
|
||||
## This procedure returns a deterministic result
|
||||
var sqrt {.noInit.}, invsqrt {.noInit.}: Fp[C]
|
||||
result = sqrt_invsqrt_if_square_p3mod4(sqrt, invsqrt, a)
|
||||
a.ccopy(sqrt, result)
|
||||
|
||||
# Tonelli Shanks for any prime
|
||||
# ------------------------------------------------------------
|
||||
|
||||
const
|
||||
# with e = 2adicity
|
||||
# p == s * 2^e + 1
|
||||
# root_of_unity = smallest_quadratic_nonresidue^s
|
||||
# exponent = (p-1-2^e)/2^e / 2
|
||||
TonelliShanks_exponent_BLS12_377 = BigInt[330].fromHex"0x35c748c2f8a21d58c760b80d94292763445b3e601ea271e3de6c45f741290002e16ba88600000010a11"
|
||||
TonelliShanks_twoAdicity_BLS12_377 = 46
|
||||
TonelliShanks_root_of_unity_BLS12_377 = Fp[BLS12_377].fromHex"0x382d3d99cdbc5d8fe9dee6aa914b0ad14fcaca7022110ec6eaa2bc56228ac41ea03d28cc795186ba6b5ef26b00bbe8"
|
||||
|
||||
{.experimental: "dynamicBindSym".}
|
||||
|
||||
macro tsGet(C: static Curve, value: untyped): untyped =
|
||||
return bindSym("TonelliShanks_" & $value & "_" & $C)
|
||||
|
||||
func precompute_tonelli_shanks[C](
|
||||
a_pre_exp: var Fp[C],
|
||||
a: Fp[C]) =
|
||||
a_pre_exp = a
|
||||
a_pre_exp.powUnsafeExponent(C.tsGet(exponent))
|
||||
|
||||
func isSquare_tonelli_shanks[C](
|
||||
a, a_pre_exp: Fp[C]): SecretBool =
|
||||
## Returns if `a` is a quadratic residue
|
||||
## This uses common precomputation for
|
||||
## Tonelli-Shanks based square root and inverse square root
|
||||
##
|
||||
## a^((p-1-2^e)/(2*2^e))
|
||||
const e = C.tsGet(twoAdicity)
|
||||
var r {.noInit.}: Fp[C]
|
||||
r.square(a_pre_exp) # a^(2(q-1-2^e)/(2*2^e)) = a^((q-1)/2^e - 1)
|
||||
r *= a # a^((q-1)/2^e)
|
||||
for _ in 0 ..< e-1:
|
||||
r.square() # a^((q-1)/2)
|
||||
|
||||
result = not(r.mres == C.getMontyPrimeMinus1())
|
||||
# r can be:
|
||||
# - 1 if a square
|
||||
# - 0 if 0
|
||||
# - -1 if a quadratic non-residue
|
||||
debug:
|
||||
doAssert: bool(
|
||||
r.isZero or
|
||||
r.isOne or
|
||||
r.mres == C.getMontyPrimeMinus1()
|
||||
)
|
||||
|
||||
func sqrt_invsqrt_tonelli_shanks[C](
|
||||
sqrt, invsqrt: var Fp[C],
|
||||
a, a_pre_exp: Fp[C]) =
|
||||
## Compute the square_root and inverse_square_root
|
||||
## of `a` via constant-time Tonelli-Shanks
|
||||
##
|
||||
## a_pre_exp is a precomputation a^((p-1-2^e)/(2*2^e))
|
||||
## ThItat is shared with the simultaneous isSquare routine
|
||||
template z: untyped = a_pre_exp
|
||||
template r: untyped = invsqrt
|
||||
var t {.noInit.}: Fp[C]
|
||||
const e = C.tsGet(twoAdicity)
|
||||
|
||||
t.square(z)
|
||||
t *= a
|
||||
r = z
|
||||
var b = t
|
||||
var root = C.tsGet(root_of_unity)
|
||||
|
||||
var buf {.noInit.}: Fp[C]
|
||||
|
||||
for i in countdown(e, 2, 1):
|
||||
for j in 1 .. i-2:
|
||||
b.square()
|
||||
|
||||
let bNotOne = not b.isOne()
|
||||
buf.prod(r, root)
|
||||
r.ccopy(buf, bNotOne)
|
||||
root.square()
|
||||
buf.prod(t, root)
|
||||
t.ccopy(buf, bNotOne)
|
||||
b = t
|
||||
|
||||
sqrt.prod(invsqrt, a)
|
||||
|
||||
# Public routines
|
||||
# ------------------------------------------------------------
|
||||
|
||||
func sqrt*[C](a: var Fp[C]) {.inline.} =
|
||||
## Compute the square root of ``a``
|
||||
##
|
||||
## This requires ``a`` to be a square
|
||||
##
|
||||
## The result is undefined otherwise
|
||||
##
|
||||
## The square root, if it exist is multivalued,
|
||||
## i.e. both x² == (-x)²
|
||||
## This procedure returns a deterministic result
|
||||
## This procedure is constant-time
|
||||
when (BaseType(C.Mod.limbs[0]) and 3) == 3:
|
||||
sqrt_p3mod4(a)
|
||||
else:
|
||||
var a_pre_exp{.noInit.}, sqrt{.noInit.}, invsqrt{.noInit.}: Fp[C]
|
||||
a_pre_exp.precompute_tonelli_shanks(a)
|
||||
sqrt_invsqrt_tonelli_shanks(sqrt, invsqrt, a, a_pre_exp)
|
||||
a = sqrt
|
||||
|
||||
func sqrt_if_square*[C](a: var Fp[C]): SecretBool {.inline.} =
|
||||
## If ``a`` is a square, compute the square root of ``a``
|
||||
## if not, ``a`` is unmodified.
|
||||
##
|
||||
## The square root, if it exist is multivalued,
|
||||
## i.e. both x² == (-x)²
|
||||
## This procedure returns a deterministic result
|
||||
## This procedure is constant-time
|
||||
when (BaseType(C.Mod.limbs[0]) and 3) == 3:
|
||||
result = sqrt_if_square_p3mod4(a)
|
||||
else:
|
||||
var a_pre_exp{.noInit.}, sqrt{.noInit.}, invsqrt{.noInit.}: Fp[C]
|
||||
a_pre_exp.precompute_tonelli_shanks(a)
|
||||
result = isSquare_tonelli_shanks(a, a_pre_exp)
|
||||
sqrt_invsqrt_tonelli_shanks(sqrt, invsqrt, a, a_pre_exp)
|
||||
a = sqrt
|
||||
|
||||
func sqrt_invsqrt*[C](sqrt, invsqrt: var Fp[C], a: Fp[C]) {.inline.} =
|
||||
## Compute the square root and inverse square root of ``a``
|
||||
##
|
||||
## This requires ``a`` to be a square
|
||||
##
|
||||
## The result is undefined otherwise
|
||||
##
|
||||
## The square root, if it exist is multivalued,
|
||||
## i.e. both x² == (-x)²
|
||||
## This procedure returns a deterministic result
|
||||
when (BaseType(C.Mod.limbs[0]) and 3) == 3:
|
||||
sqrt_invsqrt_p3mod4(sqrt, invsqrt, a)
|
||||
else:
|
||||
var a_pre_exp{.noInit.}: Fp[C]
|
||||
a_pre_exp.precompute_tonelli_shanks(a)
|
||||
sqrt_invsqrt_tonelli_shanks(sqrt, invsqrt, a, a_pre_exp)
|
||||
|
||||
func sqrt_invsqrt_if_square*[C](sqrt, invsqrt: var Fp[C], a: Fp[C]): SecretBool {.inline.} =
|
||||
## Compute the square root and ivnerse square root of ``a``
|
||||
##
|
||||
## This returns true if ``a`` is square and sqrt/invsqrt contains the square root/inverse square root
|
||||
##
|
||||
## The result is undefined otherwise
|
||||
##
|
||||
## The square root, if it exist is multivalued,
|
||||
## i.e. both x² == (-x)²
|
||||
## This procedure returns a deterministic result
|
||||
when (BaseType(C.Mod.limbs[0]) and 3) == 3:
|
||||
result = sqrt_invsqrt_if_square_p3mod4(sqrt, invsqrt, a)
|
||||
else:
|
||||
var a_pre_exp{.noInit.}: Fp[C]
|
||||
a_pre_exp.precompute_tonelli_shanks(a)
|
||||
result = isSquare_tonelli_shanks(a, a_pre_exp)
|
||||
sqrt_invsqrt_tonelli_shanks(sqrt, invsqrt, a, a_pre_exp)
|
||||
a = sqrt
|
||||
@ -146,6 +146,10 @@ func isOdd*(a: Limbs): SecretBool =
|
||||
## Returns true if a is odd
|
||||
SecretBool(a[0] and SecretWord(1))
|
||||
|
||||
func isEven*(a: Limbs): SecretBool =
|
||||
## Returns true if a is even
|
||||
not SecretBool(a[0] and SecretWord(1))
|
||||
|
||||
# Bit manipulation
|
||||
# ------------------------------------------------------------
|
||||
|
||||
|
||||
@ -140,9 +140,12 @@ declareCurves:
|
||||
family: BarretoLynnScott
|
||||
# u: 3 * 2^46 * (7 * 13 * 499) + 1
|
||||
# u: 0x8508c00000000001
|
||||
cubicRootOfUnity_mod_p: "0x9b3af05dd14f6ec619aaf7d34594aabc5ed1347970dec00452217cc900000008508c00000000001"
|
||||
|
||||
# G1 Equation: y² = x³ + 1
|
||||
# G2 Equation: y² = x³ + 1/ with 𝑗 = √-5
|
||||
order: "0x12ab655e9a2ca55660b44d1e5c37b00159aa76fed00000010a11800000000001"
|
||||
orderBitwidth: 253
|
||||
eq_form: ShortWeierstrass
|
||||
coef_a: 0
|
||||
coef_b: 1
|
||||
|
||||
@ -244,9 +244,15 @@ func scalarMulEndo*[scalBits](
|
||||
# and negate it as well.
|
||||
#
|
||||
# However solution 1 seems to cause issues (TODO)
|
||||
# with some of the BLS12-381 test cases (6 and 9)
|
||||
# with some of the BLS12-381 G2 test cases (6 and 9) as one of the miniscalars is 65 bits
|
||||
# instead of the expected maximum of 64.
|
||||
# - 0x5668a2332db27199dcfb7cbdfca6317c2ff128db26d7df68483e0a095ec8e88f
|
||||
# - 0x644dc62869683f0c93f38eaef2ba6912569dc91ec2806e46b4a3dd6a4421dad1
|
||||
#
|
||||
# Furthermore solution 2 isn't enough on BLS12-377 G2 as test fails when miniScalars[0] is negative
|
||||
let isNeg0 = miniscalars[0].isMsbSet()
|
||||
miniscalars[0].cneg(isNeg0)
|
||||
P.cneg(isNeg0)
|
||||
|
||||
# 4. Precompute lookup table
|
||||
var lut {.noInit.}: array[1 shl (M-1), ECP_SWei_Proj]
|
||||
|
||||
@ -52,6 +52,24 @@ const Babai_BN254_Snarks_G1 = (
|
||||
(BigInt[130].fromHex"0x24ccef014a773d2d25398fd0300ff6565", false) # (6u² + 4u + 1) << 2^256 // r
|
||||
)
|
||||
|
||||
# BLS12-377 G1
|
||||
# ----------------------------------------------------------------------------------------
|
||||
|
||||
const Lattice_BLS12_377_G1 = (
|
||||
# (BigInt, isNeg)
|
||||
((BigInt[127].fromHex"0x452217cc900000010a11800000000000", false), # u² - 1
|
||||
(BigInt[1].fromHex"0x1", true)), # -1
|
||||
((BigInt[1].fromHex"0x1", false), # 1
|
||||
(BigInt[127].fromHex"0x452217cc900000010a11800000000001", false)) # u²
|
||||
)
|
||||
|
||||
const Babai_BLS12_377_G1 = (
|
||||
# Vector for Babai rounding
|
||||
# (BigInt, isNeg)
|
||||
(BigInt[130].fromHex"0x3b3f7aa969fd371607f72ed32af90182c", false),
|
||||
(BigInt[4].fromHex"0xd", false)
|
||||
)
|
||||
|
||||
# BLS12-381 G1
|
||||
# ----------------------------------------------------------------------------------------
|
||||
|
||||
@ -107,6 +125,43 @@ const Babai_BN254_Snarks_G2 = (
|
||||
(BigInt[128].fromhex"0xc444fab18d269b9af7ae23ce89afae7d", true) # -2x²-x << 2^256 // r
|
||||
)
|
||||
|
||||
# BLS12-377 G2
|
||||
# ----------------------------------------------------------------------------------------
|
||||
|
||||
const Lattice_BLS12_377_G2 = (
|
||||
# Curve of order 254 -> mini scalars of size 65
|
||||
# x = -0xd201000000010000
|
||||
# Value, isNeg
|
||||
((BigInt[64].fromHex"0x8508c00000000001", true), # -x
|
||||
(BigInt[1].fromHex"0x1", false), # 1
|
||||
(BigInt[1].fromHex"0x0", false), # 0
|
||||
(BigInt[1].fromHex"0x0", false)), # 0
|
||||
|
||||
((BigInt[1].fromHex"0x0", false), # 0
|
||||
(BigInt[64].fromHex"0x8508c00000000001", true), # -x
|
||||
(BigInt[1].fromHex"0x1", false), # 1
|
||||
(BigInt[1].fromHex"0x0", false)), # 0
|
||||
|
||||
((BigInt[1].fromHex"0x0", false), # 0
|
||||
(BigInt[1].fromHex"0x0", false), # 0
|
||||
(BigInt[64].fromHex"0x8508c00000000001", true), # -x
|
||||
(BigInt[1].fromHex"0x1", false)), # 1
|
||||
|
||||
((BigInt[1].fromHex"0x1", false), # 1
|
||||
(BigInt[1].fromHex"0x0", false), # 0
|
||||
(BigInt[1].fromHex"0x1", true), # -1
|
||||
(BigInt[64].fromHex"0x8508c00000000001", true)) # -x
|
||||
)
|
||||
|
||||
const Babai_BLS12_377_G2 = (
|
||||
# Vector for Babai rounding
|
||||
# Value, isNeg
|
||||
(BigInt[193].fromHex"0x1eca0125755aed064f63abaff9084ce152979759b442f60d1", true),
|
||||
(BigInt[130].fromHex"0x3b3f7aa969fd371607f72ed32af90181f", true),
|
||||
(BigInt[67].fromhex"0x72030ba8ee9c06415", true),
|
||||
(BigInt[1].fromhex"0x0", false)
|
||||
)
|
||||
|
||||
# BLS12-381 G2
|
||||
# ----------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
@ -241,7 +241,7 @@ func scalarMul*(
|
||||
## - 0 <= scalar < curve order
|
||||
## this will not automatically
|
||||
when BigInt.bits <= ECP_SWei_Proj.F.C.getCurveOrderBitwidth() and
|
||||
ECP_SWei_Proj.F.C in {BN254_Snarks, BLS12_381}:
|
||||
ECP_SWei_Proj.F.C in {BN254_Snarks, BLS12_377, BLS12_381}:
|
||||
when ECP_SWei_Proj.F is Fp:
|
||||
P.scalarMulGLV_m2w2(scalar)
|
||||
elif ECP_SWei_Proj.F is Fp2:
|
||||
|
||||
@ -32,6 +32,12 @@ const Cofactor_Eff_BN254_Snarks_G1 = BigInt[1].fromHex"0x1"
|
||||
const Cofactor_Eff_BN254_Snarks_G2 = BigInt[254].fromHex"0x30644e72e131a029b85045b68181585e06ceecda572a2489345f2299c0f9fa8d"
|
||||
## G2.order // r
|
||||
|
||||
# TODO effective cofactors as per H2C draft like BLS12-381 curve
|
||||
const Cofactor_Eff_BLS12_377_G1 = BigInt[125].fromHex"0x170b5d44300000000000000000000000"
|
||||
## P -> (1 - x) P
|
||||
const Cofactor_Eff_BLS12_377_G2 = BigInt[502].fromHex"0x26ba558ae9562addd88d99a6f6a829fbb36b00e1dcc40c8c505634fae2e189d693e8c36676bd09a0f3622fba094800452217cc900000000000000000000001"
|
||||
## P -> (x^2 - x - 1) P + (x - 1) psi(P) + psi(psi(2P))
|
||||
|
||||
# https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-09#section-8.8
|
||||
const Cofactor_Eff_BLS12_381_G1 = BigInt[64].fromHex"0xd201000000010001"
|
||||
## P -> (1 - x) P
|
||||
@ -58,9 +64,17 @@ func clearCofactorReference*(P: var ECP_SWei_Proj[Fp2[BN254_Snarks]]) {.inline.}
|
||||
# Endomorphism acceleration cannot be used if cofactor is not cleared
|
||||
P.scalarMulGeneric(Cofactor_Eff_BN254_Snarks_G2)
|
||||
|
||||
func clearCofactorReference*(P: var ECP_SWei_Proj[Fp[BLS12_377]]) {.inline.} =
|
||||
## Clear the cofactor of BLS12_377 G1
|
||||
P.scalarMulGeneric(Cofactor_Eff_BLS12_377_G1)
|
||||
|
||||
func clearCofactorReference*(P: var ECP_SWei_Proj[Fp2[BLS12_377]]) {.inline.} =
|
||||
## Clear the cofactor of BLS12_377 G2
|
||||
# Endomorphism acceleration cannot be used if cofactor is not cleared
|
||||
P.scalarMulGeneric(Cofactor_Eff_BLS12_377_G2)
|
||||
|
||||
func clearCofactorReference*(P: var ECP_SWei_Proj[Fp[BLS12_381]]) {.inline.} =
|
||||
## Clear the cofactor of BLS12_381 G1
|
||||
## BN curve have a G1 cofactor of 1 so this is a no-op
|
||||
P.scalarMulGeneric(Cofactor_Eff_BLS12_381_G1)
|
||||
|
||||
func clearCofactorReference*(P: var ECP_SWei_Proj[Fp2[BLS12_381]]) {.inline.} =
|
||||
|
||||
@ -64,7 +64,11 @@ func toHex*(f: Fp, order: static Endianness = bigEndian): string =
|
||||
## - no leaks
|
||||
result.appendHex(f, order)
|
||||
|
||||
func fromHex*(dst: var Fp, s: string) {.raises: [ValueError].}=
|
||||
func fromHex*(dst: var Fp, hexString: string) {.raises: [ValueError].}=
|
||||
## Convert a hex string to a element of Fp
|
||||
let raw {.noinit.} = fromHex(dst.mres.typeof, s)
|
||||
let raw {.noinit.} = fromHex(dst.mres.typeof, hexString)
|
||||
dst.fromBig(raw)
|
||||
|
||||
func fromHex*(T: type Fp, hexString: string): T {.noInit, raises: [ValueError].}=
|
||||
## Convert a hex string to a element of Fp
|
||||
result.fromHex(hexString)
|
||||
|
||||
@ -66,6 +66,86 @@ template mulCheckSparse[Fp2](a: var Fp2, b: Fp2) =
|
||||
# Frobenius map - on extension fields
|
||||
# -----------------------------------------------------------------
|
||||
|
||||
# c = (SNR^((p-1)/6)^coef).
|
||||
# Then for frobenius(2): c * conjugate(c)
|
||||
# And for frobenius(3): c² * conjugate(c)
|
||||
const FrobMapConst_BLS12_377 = [
|
||||
# frobenius(1)
|
||||
[Fp2[BLS12_377].fromHex( # SNR^((p-1)/6)^0
|
||||
"0x1",
|
||||
"0x0"
|
||||
),
|
||||
Fp2[BLS12_377].fromHex( # SNR^((p-1)/6)^1
|
||||
"0x9a9975399c019633c1e30682567f915c8a45e0f94ebc8ec681bf34a3aa559db57668e558eb0188e938a9d1104f2031",
|
||||
"0x0"
|
||||
),
|
||||
Fp2[BLS12_377].fromHex( # SNR^((p-1)/6)^2 = SNR^((p-1)/3)
|
||||
"0x9b3af05dd14f6ec619aaf7d34594aabc5ed1347970dec00452217cc900000008508c00000000002",
|
||||
"0x0"
|
||||
),
|
||||
Fp2[BLS12_377].fromHex( # SNR^((p-1)/6)^3 = SNR^((p-1)/2)
|
||||
"0x1680a40796537cac0c534db1a79beb1400398f50ad1dec1bce649cf436b0f6299588459bff27d8e6e76d5ecf1391c63",
|
||||
"0x0"
|
||||
),
|
||||
Fp2[BLS12_377].fromHex( # SNR^((p-1)/6)^4 = SNR^(2(p-1)/3)
|
||||
"0x9b3af05dd14f6ec619aaf7d34594aabc5ed1347970dec00452217cc900000008508c00000000001",
|
||||
"0x0"
|
||||
),
|
||||
Fp2[BLS12_377].fromHex( # SNR^((p-1)/6)^5
|
||||
"0xcd70cb3fc936348d0351d498233f1fe379531411832232f6648a9a9fc0b9c4e3e21b7467077c05853e2c1be0e9fc32",
|
||||
"0x0"
|
||||
)],
|
||||
# frobenius(2)
|
||||
[Fp2[BLS12_377].fromHex( # norm(SNR)^((p-1)/6)^1
|
||||
"0x1",
|
||||
"0x0"
|
||||
),
|
||||
Fp2[BLS12_377].fromHex( # norm(SNR)^((p-1)/6)^2
|
||||
"0x9b3af05dd14f6ec619aaf7d34594aabc5ed1347970dec00452217cc900000008508c00000000002",
|
||||
"0x0"
|
||||
),
|
||||
Fp2[BLS12_377].fromHex(
|
||||
"0x9b3af05dd14f6ec619aaf7d34594aabc5ed1347970dec00452217cc900000008508c00000000001",
|
||||
"0x0"
|
||||
),
|
||||
Fp2[BLS12_377].fromHex(
|
||||
"0x1ae3a4617c510eac63b05c06ca1493b1a22d9f300f5138f1ef3622fba094800170b5d44300000008508c00000000000",
|
||||
"0x0"
|
||||
),
|
||||
Fp2[BLS12_377].fromHex(
|
||||
"0x1ae3a4617c510eabc8756ba8f8c524eb8882a75cc9bc8e359064ee822fb5bffd1e945779fffffffffffffffffffffff",
|
||||
"0x0"
|
||||
),
|
||||
Fp2[BLS12_377].fromHex(
|
||||
"0x1ae3a4617c510eabc8756ba8f8c524eb8882a75cc9bc8e359064ee822fb5bffd1e94577a00000000000000000000000",
|
||||
"0x0"
|
||||
)],
|
||||
# frobenius(3)
|
||||
[Fp2[BLS12_377].fromHex(
|
||||
"0x1",
|
||||
"0x0"
|
||||
),
|
||||
Fp2[BLS12_377].fromHex(
|
||||
"0x1680a40796537cac0c534db1a79beb1400398f50ad1dec1bce649cf436b0f6299588459bff27d8e6e76d5ecf1391c63",
|
||||
"0x0"
|
||||
),
|
||||
Fp2[BLS12_377].fromHex(
|
||||
"0x1ae3a4617c510eac63b05c06ca1493b1a22d9f300f5138f1ef3622fba094800170b5d44300000008508c00000000000",
|
||||
"0x0"
|
||||
),
|
||||
Fp2[BLS12_377].fromHex(
|
||||
"0x4630059e5fd9200575d0e552278a89da1f40fdf62334cd620d1860769e389d7db2d8ea700d82721691ea130ec6e39e",
|
||||
"0x0"
|
||||
),
|
||||
Fp2[BLS12_377].fromHex(
|
||||
"0x1",
|
||||
"0x0"
|
||||
),
|
||||
Fp2[BLS12_377].fromHex(
|
||||
"0x1680a40796537cac0c534db1a79beb1400398f50ad1dec1bce649cf436b0f6299588459bff27d8e6e76d5ecf1391c63",
|
||||
"0x0"
|
||||
)]]
|
||||
|
||||
# c = (SNR^((p-1)/6)^coef).
|
||||
# Then for frobenius(2): c * conjugate(c)
|
||||
# And for frobenius(3): c² * conjugate(c)
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import
|
||||
std/macros,
|
||||
../primitives,
|
||||
../config/[common, curves],
|
||||
../arithmetic,
|
||||
@ -45,21 +46,39 @@ import
|
||||
# https://eprint.iacr.org/2009/615.pdf
|
||||
|
||||
# TODO: should be part of curve parameters
|
||||
const BLS12_381_param = block:
|
||||
# The bit count must be exact for the Miller loop
|
||||
const BLS12_377_ate_param = block:
|
||||
# BLS Miller loop is parametrized by u
|
||||
BigInt[64+1].fromHex("0x8508c00000000001") # +1 so that we can take *3 and NAF encode it
|
||||
|
||||
const BLS12_377_ate_param_isNeg = false
|
||||
|
||||
const BLS12_381_ate_param = block:
|
||||
# BLS Miller loop is parametrized by u
|
||||
BigInt[64+2].fromHex("0xd201000000010000") # +2 so that we can take *3 and NAF encode it
|
||||
|
||||
const BLS12_381_param_isNeg = true
|
||||
const BLS12_381_ate_param_isNeg = true
|
||||
|
||||
# Generic slow pairing implementation
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
const BLS12_377_finalexponent = block:
|
||||
# (p^12 - 1) / r
|
||||
# BigInt[4269].fromHex"0x1b2ff68c1abdc48ab4f04ed12cc8f9b2f161b41c7eb8865b9ad3c9bb0571dd94c6bde66548dc13624d9d741024ceb315f46a89cc2482605eb6afc6d8977e5e2ccbec348dd362d59ec2b5bc62a1b467ae44572215548abc98bb4193886ed89cceaedd0221aba84fb33e5584ac29619a87a00c315178155496857c995eab4a8a9af95f4015db27955ae408d6927d0ab37d52f3917c4ddec88f8159f7bcba7eb65f1aae4eeb4e70cb20227159c08a7fdfea9b62bb308918eac3202569dd1bcdd86b431e3646356fc3fb79f89b30775e006993adb629586b6c874b7688f86f11ef7ad94a40eb020da3c532b317232fa56dc564637b331a8e8832eab84269f00b506602c8594b7f7da5a5d8d851fff6ab1d38a354fc8e0b8958e2a9e5ce2d7e50ec36d761d9505fe5e1f317257e2df2952fcd4c93b85278c20488b4ccaee94db3fec1ce8283473e4b493843fa73abe99af8bafce29170b2b863b9513b5a47312991f60c5a4f6872b5d574212bf00d797c0bea3c0f7dfd748e63679fda9b1c50f2df74de38f38e004ae0df997a10db31d209cacbf58ba0678bfe7cd0985bc43258d72d8d5106c21635ae1e527eb01fca3032d50d97756ec9ee756eaba7f21652a808a4e2539e838ef7ec4b178b29e3b976c46bd0ecdd32c1fb75e6e0aef2d8b5661f595a98023f3520381aba8da6cce785dbb0a0bba025478d75ee749619cdb7c42a21098ece86a00c6c2046c1e00000063c69000000000000"
|
||||
# (p^12 - 1) / r * 3
|
||||
BigInt[4271].fromHex"0x518fe3a450394da01ed0ec73865aed18d4251c557c299312d07b5d31105598be5439b32fda943a26e8d85c306e6c1941dd3f9d646d87211c240f5489c67b1a8663c49da97a2880dc48213527e51d370acd05663ffda035ca31c4ba994c89d66c0c97066502f8ef19bb008e047c24cf96e02493f4683ffdc39075cc1c01df9fd0ec1dc0419176c010ac1a83b777201a77f8dab474e99c59ae840de7362f7c231d500aecc1eb52616067540d419f7f9fbfd22831919b4ac04960703d9753698941c95aa2d2a04f4bf26de9d191661a013cbb09227c09424595e2639ae94d35ce708bdec2c10628eb4f981945698ef049502d2a71994fab9898c028c73dd021f13208590be27e78f0f18a88f5ffe40157a9e9fef5aa229c0aa7fdb16a887af2c4a486258bf11fb1a5d945707a89d7bf8f67e5bb28f76a460d9a1e660cbbe91bfc456b8789d5bae1dba8cbef5b03bcd0ea30f6a7b45218292b2bf3b20ed5937cb5e2250eee395821805c6383d0286c7423beb42e79f85dab2a36df8fd154f2d89e5e9aaadaaa00e0a29ecc6e329195761d6063e0a2e136a3fb7671c9134c970a8588a7f3144642a10a5af77c105f5e90987f28c6604c5dcb604c02f7d642f7f819eea6fadb8aace7c4e146a17dab2c644d4372c6979845f261b4a20cd88a20325e0c0fc806bd9f60a8502fa8f466b6919311e232e06fd6a861cb5dc24d69274c7e631cac6b93e0254460d445a0000012b53b000000000000"
|
||||
|
||||
const BLS12_381_finalexponent = block:
|
||||
# (p^12 - 1) / r
|
||||
# BigInt[4314].fromHex"0x2ee1db5dcc825b7e1bda9c0496a1c0a89ee0193d4977b3f7d4507d07363baa13f8d14a917848517badc3a43d1073776ab353f2c30698e8cc7deada9c0aadff5e9cfee9a074e43b9a660835cc872ee83ff3a0f0f1c0ad0d6106feaf4e347aa68ad49466fa927e7bb9375331807a0dce2630d9aa4b113f414386b0e8819328148978e2b0dd39099b86e1ab656d2670d93e4d7acdd350da5359bc73ab61a0c5bf24c374693c49f570bcd2b01f3077ffb10bf24dde41064837f27611212596bc293c8d4c01f25118790f4684d0b9c40a68eb74bb22a40ee7169cdc1041296532fef459f12438dfc8e2886ef965e61a474c5c85b0129127a1b5ad0463434724538411d1676a53b5a62eb34c05739334f46c02c3f0bd0c55d3109cd15948d0a1fad20044ce6ad4c6bec3ec03ef19592004cedd556952c6d8823b19dadd7c2498345c6e5308f1c511291097db60b1749bf9b71a9f9e0100418a3ef0bc627751bbd81367066bca6a4c1b6dcfc5cceb73fc56947a403577dfa9e13c24ea820b09c1d9f7c31759c3635de3f7a3639991708e88adce88177456c49637fd7961be1a4c7e79fb02faa732e2f3ec2bea83d196283313492caa9d4aff1c910e9622d2a73f62537f2701aaef6539314043f7bbce5b78c7869aeb2181a67e49eeed2161daf3f881bd88592d767f67c4717489119226c2f011d4cab803e9d71650a6f80698e2f8491d12191a04406fbc8fbd5f48925f98630e68bfb24c0bcb9b55df57510"
|
||||
# (p^12 - 1) / r * 3
|
||||
BigInt[4316].fromHex"0x8ca592196587127a538fd40dc3e541f9dca04bb7dc671be77cf17715a2b2fe3bea73dfb468d8f473094aecb7315a664019fbd84913caba6579c08fd42009fe1bd6fcbce15eacb2cf3218a165958cb8bfdae2d2d54207282314fc0dea9d6ff3a07dbd34efb77b732ba5f994816e296a72928cfee133bdc3ca9412b984b9783d9c6aa81297ab1cd294a502304773528bbae8706979f28efa0d355b0224e2513d6e4a5d3bb4dde0523678105d9167ff1323d6e99ac312d8a7d762336370c4347bb5a7e405d6f3496b2dd38e722d4c1f3ac25e3167ec2cb543d69430c37c2f98fcdd0dd36caa9f5aa7994cec31b24ed5e515911037b376e521070d29c9d56cfa8c3574363efb20f28c19e4105ab99edd44084bd23725017931d6740bda71e5f07600ce6b407e543c4bc40bcd4c0b600e6c98003bf8548986b14d9098746dc89d154af91ad54f337b31c79222145dd3ed254fdeda0300c49ebcd2352765f533883a3513435f3ee452496f5166c25bf503bd6ec0a0679efda3b46ebf86211d458de749460d4a2a19abe6ea2accb451ab9a096b98465d044dc2a7f86c253a4ee57b6df108eff598a8dbc483bf8b74c2789939db85ffd7e0fd55b32bc26877f5be26fa7d750500ce2fab93c0cbe7336b126a5693d0c16484f37addccc7642590dbe98538990b88637e374d545d9b34b67448d0357e60280bbd8542f1f4e813caa8e8db57364b4e0cc14f35af381dd9b71ec9292b3a3f16e42362d2019e05f30"
|
||||
|
||||
{.experimental: "dynamicBindSym".}
|
||||
|
||||
macro get(C: static Curve, value: untyped): untyped =
|
||||
return bindSym($C & "_" & $value)
|
||||
|
||||
func millerLoopGenericBLS12*[C: static Curve](
|
||||
f: var Fp12[C],
|
||||
P: ECP_SWei_Aff[Fp[C]],
|
||||
@ -95,9 +114,6 @@ func millerLoopGenericBLS12*[C: static Curve](
|
||||
# or we special case line addition of T and -T (it's a vertical line)
|
||||
# or we ensure the loop is done for a number of iterations strictly less
|
||||
# than the curve order which is the case for BLS12 curves
|
||||
|
||||
static: doAssert C == BLS12_381, "Only BLS12-381 is supported at the moment"
|
||||
|
||||
var
|
||||
T {.noInit.}: ECP_SWei_Proj[Fp2[C]]
|
||||
line {.noInit.}: Line[Fp2[C], C.getSexticTwist()]
|
||||
@ -107,22 +123,29 @@ func millerLoopGenericBLS12*[C: static Curve](
|
||||
nQ.neg(Q)
|
||||
f.setOne()
|
||||
|
||||
template u: untyped = BLS12_381_param
|
||||
let u3 = 3*BLS12_381_param
|
||||
template mul(f, line): untyped =
|
||||
when C.getSexticTwist() == D_Twist:
|
||||
f.mul_sparse_by_line_xyz000(line)
|
||||
else:
|
||||
f.mul_sparse_by_line_xy000z(line)
|
||||
|
||||
template u: untyped = C.get(ate_param)
|
||||
let u3 = 3*C.get(ate_param)
|
||||
for i in countdown(u3.bits - 2, 1):
|
||||
f.square()
|
||||
line.line_double(T, P)
|
||||
f.mul_sparse_by_line_xy000z(line)
|
||||
|
||||
f.mul(line)
|
||||
|
||||
let naf = u3.bit(i).int8 - u.bit(i).int8 # This can throw exception
|
||||
if naf == 1:
|
||||
line.line_add(T, Q, P)
|
||||
f.mul_sparse_by_line_xy000z(line)
|
||||
f.mul(line)
|
||||
elif naf == -1:
|
||||
line.line_add(T, nQ, P)
|
||||
f.mul_sparse_by_line_xy000z(line)
|
||||
f.mul(line)
|
||||
|
||||
when BLS12_381_param_isNeg: # TODO generic
|
||||
when C.get(ate_param_isNeg):
|
||||
# In GT, x^-1 == conjugate(x)
|
||||
# Remark 7.1, chapter 7.1.1 of Guide to Pairing-Based Cryptography, El Mrabet, 2017
|
||||
f.conj()
|
||||
@ -130,8 +153,7 @@ func millerLoopGenericBLS12*[C: static Curve](
|
||||
func finalExpGeneric[C: static Curve](f: var Fp12[C]) =
|
||||
## A generic and slow implementation of final exponentiation
|
||||
## for sanity checks purposes.
|
||||
static: doAssert C == BLS12_381, "Only BLS12-381 is supported at the moment"
|
||||
f.powUnsafeExponent(BLS12_381_finalexponent, window = 3)
|
||||
f.powUnsafeExponent(C.get(finalexponent), window = 3)
|
||||
|
||||
func pairing_bls12_reference*[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
|
||||
@ -154,7 +176,7 @@ func cycl_sqr_repeated(f: var Fp12, num: int) =
|
||||
for _ in 0 ..< num:
|
||||
f.cyclotomic_square()
|
||||
|
||||
func pow_xdiv2(r: var Fp12[BLS12_381], a: Fp12[BLS12_381], invert = BLS12_381_param_isNeg) =
|
||||
func pow_xdiv2(r: var Fp12[BLS12_381], a: Fp12[BLS12_381], invert = BLS12_381_ate_param_isNeg) =
|
||||
## f^(x/2) with x the curve parameter
|
||||
## For BLS12_381 f^-0xd201000000010000
|
||||
|
||||
@ -173,12 +195,41 @@ func pow_xdiv2(r: var Fp12[BLS12_381], a: Fp12[BLS12_381], invert = BLS12_381_pa
|
||||
if invert:
|
||||
r.cyclotomic_inv()
|
||||
|
||||
func pow_x(r: var Fp12[BLS12_381], a: Fp12[BLS12_381], invert = BLS12_381_param_isNeg) =
|
||||
func pow_x(r: var Fp12[BLS12_381], a: Fp12[BLS12_381], invert = BLS12_381_ate_param_isNeg) =
|
||||
## f^x with x the curve parameter
|
||||
## For BLS12_381 f^-0xd201000000010000
|
||||
r.pow_xdiv2(a, invert)
|
||||
r.cyclotomic_square()
|
||||
|
||||
func pow_x(r: var Fp12[BLS12_377], a: Fp12[BLS12_377], invert = BLS12_377_ate_param_isNeg) =
|
||||
## f^x with x the curve parameter
|
||||
## For BLS12_377 f^-0x8508c00000000001
|
||||
## Warning: The parameter is odd and needs a correction
|
||||
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)
|
||||
r *= a
|
||||
|
||||
if invert:
|
||||
r.cyclotomic_inv()
|
||||
|
||||
func finalExpHard_BLS12*[C: static Curve](f: var Fp12[C]) =
|
||||
## Hard part of the final exponentiation
|
||||
## Specialized for BLS12 curves
|
||||
@ -208,7 +259,10 @@ func finalExpHard_BLS12*[C: static Curve](f: var Fp12[C]) =
|
||||
v2.cyclotomic_square(f) # v2 = f²
|
||||
|
||||
# (x−1)²
|
||||
v0.pow_xdiv2(v2) # v0 = (f²)^(x/2) = f^x
|
||||
when C.get(ate_param).isEven.bool:
|
||||
v0.pow_xdiv2(v2) # v0 = (f²)^(x/2) = f^x
|
||||
else:
|
||||
v0.pow_x(f)
|
||||
v1.cyclotomic_inv(f) # v1 = f^-1
|
||||
v0 *= v1 # v0 = f^(x-1)
|
||||
v1.pow_x(v0) # v1 = (f^(x-1))^x
|
||||
|
||||
@ -43,15 +43,16 @@ import
|
||||
# https://eprint.iacr.org/2009/615.pdf
|
||||
|
||||
# TODO: should be part of curve parameters
|
||||
# The bit count must be exact for the Miller loop
|
||||
const BN254_Snarks_ate_param = block:
|
||||
# BN Miller loop is parametrized by 6u+2
|
||||
BigInt[67].fromHex"0x19d797039be763ba8"
|
||||
BigInt[65+2].fromHex"0x19d797039be763ba8"
|
||||
|
||||
const BN254_Snarks_ate_param_isNeg = false
|
||||
|
||||
const BN254_Nogami_ate_param = block:
|
||||
# BN Miller loop is parametrized by 6u+2
|
||||
BigInt[67].fromHex"0x18300000000000004" # 65+2 bit for NAF x3 encoding
|
||||
BigInt[65+2].fromHex"0x18300000000000004" # 65+2 bit for NAF x3 encoding
|
||||
|
||||
const BN254_Nogami_ate_param_isNeg = true
|
||||
|
||||
@ -116,22 +117,28 @@ func millerLoopGenericBN*[C: static Curve](
|
||||
nQ.neg(Q)
|
||||
f.setOne()
|
||||
|
||||
template mul(f, line): untyped =
|
||||
when C.getSexticTwist() == D_Twist:
|
||||
f.mul_sparse_by_line_xyz000(line)
|
||||
else:
|
||||
f.mul_sparse_by_line_xy000z(line)
|
||||
|
||||
template u: untyped = C.get(ate_param)
|
||||
let u3 = 3*C.get(ate_param)
|
||||
for i in countdown(u3.bits - 2, 1):
|
||||
f.square()
|
||||
line.line_double(T, P)
|
||||
f.mul_sparse_by_line_xyz000(line)
|
||||
f.mul(line)
|
||||
|
||||
let naf = u3.bit(i).int8 - u.bit(i).int8 # This can throw exception
|
||||
if naf == 1:
|
||||
line.line_add(T, Q, P)
|
||||
f.mul_sparse_by_line_xyz000(line)
|
||||
f.mul(line)
|
||||
elif naf == -1:
|
||||
line.line_add(T, nQ, P)
|
||||
f.mul_sparse_by_line_xyz000(line)
|
||||
f.mul(line)
|
||||
|
||||
when C.get(ate_param_isNeg): # TODO generic
|
||||
when C.get(ate_param_isNeg):
|
||||
# In GT, x^-1 == conjugate(x)
|
||||
# Remark 7.1, chapter 7.1.1 of Guide to Pairing-Based Cryptography, El Mrabet, 2017
|
||||
f.conj()
|
||||
|
||||
@ -221,16 +221,18 @@ func isSquare*(a: QuadraticExt): SecretBool =
|
||||
#
|
||||
# https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-08#appendix-G.5
|
||||
# https://eprint.iacr.org/2012/685
|
||||
|
||||
mixin fromComplexExtension # TODO: relax this
|
||||
static: doAssert a.fromComplexExtension()
|
||||
mixin fromComplexExtension
|
||||
|
||||
var tv1{.noInit.}, tv2{.noInit.}: typeof(a.c0)
|
||||
|
||||
tv1.square(a.c0) # a0²
|
||||
tv2.square(a.c1) # - β a1² with β = 𝑖² in a complex extension field
|
||||
when a.fromComplexExtension():
|
||||
tv1 += tv2 # a0 - (-1) a1²
|
||||
else:
|
||||
tv2 *= NonResidue
|
||||
tv1 -= tv2
|
||||
|
||||
tv1 += tv2 # a0 - (-1) a1²
|
||||
result = tv1.isSquare()
|
||||
|
||||
func sqrt_if_square*(a: var QuadraticExt): SecretBool =
|
||||
@ -243,16 +245,18 @@ func sqrt_if_square*(a: var QuadraticExt): SecretBool =
|
||||
#
|
||||
# Implementation via the complex method (which confusingly does not require a complex field)
|
||||
# We make it constant-time via conditional copies
|
||||
|
||||
mixin fromComplexExtension # TODO: relax this
|
||||
static: doAssert a.fromComplexExtension()
|
||||
mixin fromComplexExtension
|
||||
|
||||
var t1{.noInit.}, t2{.noInit.}, t3{.noInit.}: typeof(a.c0)
|
||||
|
||||
t1.square(a.c0) # a0²
|
||||
t2.square(a.c1) # - β a1² with β = 𝑖² in a complex extension field
|
||||
when a.fromComplexExtension():
|
||||
t1 += t2 # a0 - (-1) a1²
|
||||
else:
|
||||
t2 *= NonResidue
|
||||
t1 -= t2
|
||||
|
||||
t1 += t2 # a0 - (-1) a1²
|
||||
result = t1.sqrt_if_square()
|
||||
|
||||
t2.sum(a.c0, t1)
|
||||
|
||||
@ -92,7 +92,10 @@ def compute_curve_characteristic(x_str):
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Usage
|
||||
# BLS12-381
|
||||
# sage sage/curve_family_bls12.sage '-(2^63 + 2^62 + 2^60 + 2^57 + 2^48 + 2^16)'
|
||||
# BLS12-377
|
||||
# sage sage/curve_family_bls12.sage '3 * 2^46 * (7 * 13 * 499) + 1'
|
||||
|
||||
from argparse import ArgumentParser
|
||||
|
||||
|
||||
@ -41,9 +41,27 @@ G2 = EllipticCurve(Fp2, [0, b/SNR])
|
||||
# Utilities
|
||||
def fp2_to_hex(a):
|
||||
v = vector(a)
|
||||
return Integer(v[0]).hex() + ' + β * ' + Integer(v[1]).hex()
|
||||
return '0x' + Integer(v[0]).hex() + ' + β * ' + '0x' + Integer(v[1]).hex()
|
||||
|
||||
# Frobenius constants (D type: use SNR, M type use 1/SNR)
|
||||
# Frobenius map constants
|
||||
print('\nFrobenius extension field constants')
|
||||
FrobConst_map = SNR^((p-1)/6)
|
||||
FrobConst_map_list = []
|
||||
cur = Fp2([1, 0])
|
||||
|
||||
for i in range(6):
|
||||
FrobConst_map_list.append(cur)
|
||||
print(f'FrobConst_map_{i} : {fp2_to_hex(cur)}')
|
||||
cur *= FrobConst_map
|
||||
print('')
|
||||
for i in range(6):
|
||||
print(f'FrobConst_map_{i}_pow2 : {fp2_to_hex(FrobConst_map_list[i]*conjugate(FrobConst_map_list[i]))}')
|
||||
print('')
|
||||
for i in range(6):
|
||||
print(f'FrobConst_map_{i}_pow3 : {fp2_to_hex(FrobConst_map_list[i]**2 * conjugate(FrobConst_map_list[i]))}')
|
||||
|
||||
# Frobenius psi constants (D type: use SNR, M type use 1/SNR)
|
||||
print('\nψ (Psi) - Untwist-Frobenius-Twist constants')
|
||||
FrobConst_psi = SNR^((p-1)/6)
|
||||
FrobConst_psi_2 = FrobConst_psi * FrobConst_psi
|
||||
FrobConst_psi_3 = FrobConst_psi_2 * FrobConst_psi
|
||||
|
||||
223
sage/lattice_decomposition_bls12_377_g1.sage
Normal file
223
sage/lattice_decomposition_bls12_377_g1.sage
Normal file
@ -0,0 +1,223 @@
|
||||
# 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.
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
# BLS12-381 GLS Endomorphism
|
||||
# Lattice Decomposition
|
||||
#
|
||||
# ############################################################
|
||||
|
||||
# Parameters
|
||||
x = 3 * 2^46 * (7 * 13 * 499) + 1
|
||||
p = (x - 1)^2 * (x^4 - x^2 + 1)//3 + x
|
||||
r = x^4 - x^2 + 1
|
||||
print('p : ' + p.hex())
|
||||
print('r : ' + r.hex())
|
||||
|
||||
# Cube root of unity (mod r) formula for any BLS12 curves
|
||||
lambda1_r = x^2 - 1
|
||||
assert lambda1_r^3 % r == 1
|
||||
print('λᵩ1 : ' + lambda1_r.hex())
|
||||
print('λᵩ1+r: ' + (lambda1_r+r).hex())
|
||||
|
||||
lambda2_r = x^4
|
||||
assert lambda2_r^3 % r == 1
|
||||
print('λᵩ2 : ' + lambda2_r.hex())
|
||||
|
||||
# Finite fields
|
||||
F = GF(p)
|
||||
|
||||
# Curves
|
||||
b = 1
|
||||
G1 = EllipticCurve(F, [0, b])
|
||||
|
||||
cofactorG1 = G1.order() // r
|
||||
|
||||
print('')
|
||||
print('cofactor G1: ' + cofactorG1.hex())
|
||||
print('')
|
||||
|
||||
(phi1, phi2) = (root for root in GF(p)(1).nth_root(3, all=True) if root != 1)
|
||||
print('𝜑1 :' + Integer(phi1).hex())
|
||||
print('𝜑2 :' + Integer(phi2).hex())
|
||||
assert phi1^3 % p == 1
|
||||
assert phi2^3 % p == 1
|
||||
|
||||
def clearCofactorG1(P):
|
||||
return cofactorG1 * P
|
||||
|
||||
# Test generator
|
||||
set_random_seed(1337)
|
||||
|
||||
# Check
|
||||
def checkEndo():
|
||||
Prand = G1.random_point()
|
||||
P = clearCofactorG1(Prand)
|
||||
assert P != G1([0, 1, 0]) # Infinity
|
||||
|
||||
(Px, Py, Pz) = P
|
||||
Qendo1 = G1([Px*phi1 % p, Py, Pz])
|
||||
Qendo2 = G1([Px*phi2 % p, Py, Pz])
|
||||
|
||||
Q1 = lambda1_r * P
|
||||
Q2 = lambda2_r * P
|
||||
|
||||
assert P != Q1
|
||||
assert P != Q2
|
||||
|
||||
assert (F(Px)*F(phi1))^3 == F(Px)^3
|
||||
assert (F(Px)*F(phi2))^3 == F(Px)^3
|
||||
|
||||
assert Q1 == Qendo1
|
||||
assert Q2 == Qendo1
|
||||
|
||||
print('Endomorphism OK with 𝜑1')
|
||||
|
||||
checkEndo()
|
||||
|
||||
# Decomposition generated by LLL-algorithm and Babai rounding
|
||||
# to solve the Shortest (Basis) Vector Problem
|
||||
# Lattice from Guide to Pairing-Based Cryptography
|
||||
Lat = [
|
||||
[x^2-1, -1],
|
||||
[1, x^2]
|
||||
]
|
||||
ahat = [x^2, 1]
|
||||
n = int(r).bit_length()
|
||||
n = int(((n + 64 - 1) // 64) * 64) # round to next multiple of 64
|
||||
v = [Integer(a << n) // r for a in ahat]
|
||||
|
||||
def pretty_print_lattice(Lat):
|
||||
latHex = [['0x' + x.hex() if x >= 0 else '-0x' + (-x).hex() for x in vec] for vec in Lat]
|
||||
maxlen = max([len(cell) for row in latHex for cell in row])
|
||||
for row in latHex:
|
||||
row = ' '.join(cell.rjust(maxlen + 2) for cell in row)
|
||||
print(row)
|
||||
|
||||
print('\nLattice')
|
||||
pretty_print_lattice(Lat)
|
||||
|
||||
print('\nbasis:')
|
||||
print(' 𝛼\u03050: 0x' + v[0].hex())
|
||||
print(' 𝛼\u03051: 0x' + v[1].hex())
|
||||
print('')
|
||||
|
||||
maxInfNorm = abs(x^2 + 1)
|
||||
print('\nmax infinity norm:')
|
||||
print(' ||(a0 , a1)||∞ ≤ 0x' + str(maxInfNorm.hex()))
|
||||
print(' infinity norm bitlength: ' + str(int(maxInfNorm).bit_length()))
|
||||
|
||||
# Contrary to Faz2013 paper, we use the max infinity norm
|
||||
# to properly dimension our recoding instead of ⌈log2 r/m⌉ + 1
|
||||
# which fails for some inputs
|
||||
#
|
||||
# +1 for signed column
|
||||
# Optional +1 for handling negative miniscalars
|
||||
L = int(maxInfNorm).bit_length() + 1
|
||||
L += 1
|
||||
|
||||
def getGLV1_decomp(scalar):
|
||||
|
||||
maxLen = (int(r).bit_length() + 1) // 2 + 1
|
||||
|
||||
a0 = (v[0] * scalar) >> n
|
||||
a1 = (v[1] * scalar) >> n
|
||||
|
||||
k0 = scalar - a0 * Lat[0][0] - a1 * Lat[1][0]
|
||||
k1 = 0 - a0 * Lat[0][1] - a1 * Lat[1][1]
|
||||
|
||||
assert int(k0).bit_length() <= maxLen
|
||||
assert int(k1).bit_length() <= maxLen
|
||||
|
||||
assert scalar == (k0 + k1 * (lambda1_r % r)) % r
|
||||
assert scalar == (k0 + k1 * (lambda2_r % r)) % r
|
||||
|
||||
return k0, k1
|
||||
|
||||
def recodeScalars(k):
|
||||
m = 2
|
||||
|
||||
b = [[0] * L, [0] * L]
|
||||
b[0][L-1] = 0
|
||||
for i in range(0, L-1): # l-2 inclusive
|
||||
b[0][i] = 1 - ((k[0] >> (i+1)) & 1)
|
||||
for j in range(1, m):
|
||||
for i in range(0, L):
|
||||
b[j][i] = k[j] & 1
|
||||
k[j] = k[j]//2 + (b[j][i] & b[0][i])
|
||||
|
||||
return b
|
||||
|
||||
def buildLut(P0, P1):
|
||||
m = 2
|
||||
lut = [0] * (1 << (m-1))
|
||||
lut[0] = P0
|
||||
lut[1] = P0 + P1
|
||||
return lut
|
||||
|
||||
def pointToString(P):
|
||||
(Px, Py, Pz) = P
|
||||
return '(x: ' + Integer(Px).hex() + ', y: ' + Integer(Py).hex() + ', z: ' + Integer(Pz).hex() + ')'
|
||||
|
||||
def scalarMulEndo(scalar, P0):
|
||||
m = 2
|
||||
|
||||
print('L: ' + str(L))
|
||||
|
||||
print('scalar: ' + Integer(scalar).hex())
|
||||
|
||||
k0, k1 = getGLV1_decomp(scalar)
|
||||
print('k0: ' + k0.hex())
|
||||
print('k1: ' + k1.hex())
|
||||
|
||||
P1 = (lambda1_r % r) * P0
|
||||
(Px, Py, Pz) = P0
|
||||
P1_endo = G1([Px*phi1 % p, Py, Pz])
|
||||
assert P1 == P1_endo
|
||||
|
||||
expected = scalar * P0
|
||||
decomp = k0*P0 + k1*P1
|
||||
assert expected == decomp
|
||||
|
||||
print('------ recode scalar -----------')
|
||||
even = k0 & 1 == 0
|
||||
if even:
|
||||
k0 += 1
|
||||
|
||||
b = recodeScalars([k0, k1])
|
||||
print('b0: ' + str(list(reversed(b[0]))))
|
||||
print('b1: ' + str(list(reversed(b[1]))))
|
||||
|
||||
print('------------ lut ---------------')
|
||||
|
||||
lut = buildLut(P0, P1)
|
||||
|
||||
print('------------ mul ---------------')
|
||||
# b[0][L-1] is always 0
|
||||
Q = lut[b[1][L-1]]
|
||||
for i in range(L-2, -1, -1):
|
||||
Q *= 2
|
||||
Q += (1 - 2 * b[0][i]) * lut[b[1][i]]
|
||||
|
||||
if even:
|
||||
Q -= P0
|
||||
|
||||
print('final Q: ' + pointToString(Q))
|
||||
print('expected: ' + pointToString(expected))
|
||||
assert Q == expected
|
||||
|
||||
# Test generator
|
||||
set_random_seed(1337)
|
||||
|
||||
for i in range(1):
|
||||
print('---------------------------------------')
|
||||
scalar = randrange(r) # Pick an integer below curve order
|
||||
P = G1.random_point()
|
||||
P = clearCofactorG1(P)
|
||||
scalarMulEndo(scalar, P)
|
||||
375
sage/lattice_decomposition_bls12_377_g2.sage
Normal file
375
sage/lattice_decomposition_bls12_377_g2.sage
Normal file
@ -0,0 +1,375 @@
|
||||
# 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.
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
# BLS12-381 GLS Endomorphism
|
||||
# Lattice Decomposition
|
||||
#
|
||||
# ############################################################
|
||||
|
||||
# Parameters
|
||||
x = 3 * 2^46 * (7 * 13 * 499) + 1
|
||||
p = (x - 1)^2 * (x^4 - x^2 + 1)//3 + x
|
||||
r = x^4 - x^2 + 1
|
||||
t = x+1
|
||||
print(' Prime modulus p: 0x' + p.hex())
|
||||
print(' Curve order r: 0x' + r.hex())
|
||||
print(' trace t: 0x' + t.hex())
|
||||
|
||||
# Finite fields
|
||||
Fp = GF(p)
|
||||
K2.<u> = PolynomialRing(Fp)
|
||||
Fp2.<beta> = Fp.extension(u^2+5)
|
||||
|
||||
SNR = Fp2([0, 1]) # Sextic Non-Residue for Sextic Twist
|
||||
|
||||
# Curves
|
||||
b = 1
|
||||
G1 = EllipticCurve(Fp, [0, b])
|
||||
G2 = EllipticCurve(Fp2, [0, b/SNR])
|
||||
|
||||
# https://crypto.stackexchange.com/questions/64064/order-of-twisted-curve-in-pairings
|
||||
# https://math.stackexchange.com/questions/144194/how-to-find-the-order-of-elliptic-curve-over-finite-field-extension
|
||||
cofactorG1 = G1.order() // r
|
||||
cofactorG2 = G2.order() // r
|
||||
|
||||
print('')
|
||||
print('cofactor G1: ' + cofactorG1.hex())
|
||||
print('cofactor G2: ' + cofactorG2.hex())
|
||||
print('')
|
||||
|
||||
# Frobenius constants (D type: use SNR, M type use 1/SNR)
|
||||
FrobConst_psi = SNR^((p-1)/6)
|
||||
FrobConst_psi_2 = FrobConst_psi * FrobConst_psi
|
||||
FrobConst_psi_3 = FrobConst_psi_2 * FrobConst_psi
|
||||
FrobConst_psi2_2 = FrobConst_psi_2 * FrobConst_psi_2^p
|
||||
FrobConst_psi2_3 = FrobConst_psi_3 * FrobConst_psi_3^p
|
||||
|
||||
def psi(P):
|
||||
(Px, Py, Pz) = P
|
||||
return G2([
|
||||
FrobConst_psi_2 * Px.frobenius(1),
|
||||
FrobConst_psi_3 * Py.frobenius(1)
|
||||
# Pz.frobenius() - Always 1 after extract
|
||||
])
|
||||
|
||||
def psi2(P):
|
||||
(Px, Py, Pz) = P
|
||||
return G2([
|
||||
FrobConst_psi2_2 * Px.frobenius(2),
|
||||
FrobConst_psi2_3 * Py.frobenius(2)
|
||||
# Pz - Always 1 after extract
|
||||
])
|
||||
|
||||
def clearCofactorG2(P):
|
||||
return cofactorG2 * P
|
||||
|
||||
# Test generator
|
||||
set_random_seed(1337)
|
||||
|
||||
# Check
|
||||
def checkEndo():
|
||||
P = G2.random_point()
|
||||
P = clearCofactorG2(P)
|
||||
|
||||
(Px, Py, Pz) = P
|
||||
|
||||
# Galbraith-Lin-Scott, 2008, Theorem 1
|
||||
assert psi(psi(P)) - t*psi(P) + p*P == G2([0, 1, 0])
|
||||
# Galbraith-Scott, 2008, Lemma 1
|
||||
# k-th cyclotomic polynomial with k = 12
|
||||
assert psi2(psi2(P)) - psi2(P) + P == G2([0, 1, 0])
|
||||
|
||||
assert p % r == (t-1) % r
|
||||
# assert (p^4 - p^2 + 1) % r == 0
|
||||
assert ((t-1)^4 - (t-1)^2 + 1) % r == 0
|
||||
assert (t-1)*P == (p % r)*P
|
||||
assert (t-1)*P == psi(P)
|
||||
|
||||
print('Endomorphism OK')
|
||||
|
||||
checkEndo()
|
||||
|
||||
def subgroup_check(P):
|
||||
ppP = psi2(P)
|
||||
assert x * psi(ppP) - ppP + P == G2([0,1,0])
|
||||
|
||||
# Decomposition generated by LLL-algorithm and Babai rounding
|
||||
# to solve the Shortest (Basis) Vector Problem
|
||||
#
|
||||
# TODO: This lattice is generating wrong result
|
||||
# Lattice from Guide to Pairing-Based Cryptography
|
||||
# Lat = [
|
||||
# [ x, 1, 0, 0],
|
||||
# [ 0, x, 1, 0],
|
||||
# [ 0, 0, x, 1],
|
||||
# [ 1, 0,-1, x]
|
||||
# ]
|
||||
# ahat = [x*(x^2+1), -(x^2+1), x, -1]
|
||||
|
||||
# Lattice from my own LLL+Babai rounding routines
|
||||
Lat = Matrix([
|
||||
[-x, 1, 0, 0],
|
||||
[ 0,-x, 1, 0],
|
||||
[ 0, 0,-x, 1],
|
||||
[ 1, 0,-1,-x]
|
||||
])
|
||||
# print('Lat: ' + str(Lat))
|
||||
ahat = vector([r, 0, 0, 0]) * Lat.inverse()
|
||||
# print('ahat: ' + str(ahat))
|
||||
|
||||
n = int(r).bit_length()
|
||||
n = int(((n + 64 - 1) // 64) * 64) # round to next multiple of 64
|
||||
v = [Integer(int(a) << n) // r for a in ahat]
|
||||
|
||||
def pretty_print_lattice(Lat):
|
||||
latHex = [['0x' + x.hex() if x >= 0 else '-0x' + (-x).hex() for x in vec] for vec in Lat]
|
||||
maxlen = max([len(cell) for row in latHex for cell in row])
|
||||
for row in latHex:
|
||||
row = ' '.join(cell.rjust(maxlen + 2) for cell in row)
|
||||
print(row)
|
||||
|
||||
print('\nLattice')
|
||||
pretty_print_lattice(Lat)
|
||||
|
||||
print('\nbasis:')
|
||||
print(' 𝛼\u03050: 0x' + v[0].hex())
|
||||
print(' 𝛼\u03051: 0x' + v[1].hex())
|
||||
print(' 𝛼\u03052: 0x' + v[2].hex())
|
||||
print(' 𝛼\u03053: 0x' + v[3].hex())
|
||||
print('')
|
||||
|
||||
maxInfNorm = abs(x + 2)
|
||||
print('\nmax infinity norm:')
|
||||
print(' ||(a0, a1, a2, a3)||∞ ≤ 0x' + str(maxInfNorm.hex()))
|
||||
print(' infinity norm bitlength: ' + str(int(maxInfNorm).bit_length()))
|
||||
|
||||
# Contrary to Faz2013 paper, we use the max infinity norm
|
||||
# to properly dimension our recoding instead of ⌈log2 r/m⌉ + 1
|
||||
# which fails for some inputs
|
||||
# +1 for signed column
|
||||
# Optional +1 for handling negative miniscalars
|
||||
L = int(maxInfNorm).bit_length() + 1
|
||||
L += 1
|
||||
|
||||
lambda1 = (t-1) % r
|
||||
lambda2 = lambda1^2 % r
|
||||
lambda3 = lambda1^3 % r
|
||||
|
||||
def getGLV2_decomp(scalar):
|
||||
|
||||
maxLen = (int(r).bit_length() + 3) // 4 + 1
|
||||
maxLen += 1 # Deal with negative miniscalars
|
||||
|
||||
a0 = (v[0] * scalar) >> n
|
||||
a1 = (v[1] * scalar) >> n
|
||||
a2 = (v[2] * scalar) >> n
|
||||
a3 = (v[3] * scalar) >> n
|
||||
|
||||
k0 = scalar - a0 * Lat[0][0] - a1 * Lat[1][0] - a2 * Lat[2][0] - a3 * Lat[3][0]
|
||||
k1 = 0 - a0 * Lat[0][1] - a1 * Lat[1][1] - a2 * Lat[2][1] - a3 * Lat[3][1]
|
||||
k2 = 0 - a0 * Lat[0][2] - a1 * Lat[1][2] - a2 * Lat[2][2] - a3 * Lat[3][2]
|
||||
k3 = 0 - a0 * Lat[0][3] - a1 * Lat[1][3] - a2 * Lat[2][3] - a3 * Lat[3][3]
|
||||
|
||||
print("k0.bitlength(): " + str(int(k0).bit_length()))
|
||||
print("k1.bitlength(): " + str(int(k1).bit_length()))
|
||||
print("k2.bitlength(): " + str(int(k2).bit_length()))
|
||||
print("k3.bitlength(): " + str(int(k3).bit_length()))
|
||||
|
||||
print('k0: ' + k0.hex())
|
||||
print('k1: ' + k1.hex())
|
||||
print('k2: ' + k2.hex())
|
||||
print('k3: ' + k3.hex())
|
||||
|
||||
assert scalar == (k0 + k1*lambda1 + k2*lambda2 + k3*lambda3) % r
|
||||
|
||||
assert int(k0).bit_length() <= maxLen
|
||||
assert int(k1).bit_length() <= maxLen
|
||||
assert int(k2).bit_length() <= maxLen
|
||||
assert int(k3).bit_length() <= maxLen
|
||||
|
||||
return k0, k1, k2, k3
|
||||
|
||||
def recodeScalars(k):
|
||||
m = 4
|
||||
|
||||
b = [[0] * L, [0] * L, [0] * L, [0] * L]
|
||||
b[0][L-1] = 0
|
||||
for i in range(0, L-1): # l-2 inclusive
|
||||
b[0][i] = 1 - ((k[0] >> (i+1)) & 1)
|
||||
for j in range(1, m):
|
||||
for i in range(0, L):
|
||||
b[j][i] = k[j] & 1
|
||||
k[j] = k[j]//2 + (b[j][i] & b[0][i])
|
||||
|
||||
return b
|
||||
|
||||
def clearBit(v, bit):
|
||||
return v & ~int(1 << bit)
|
||||
|
||||
def buildLut(P0, P_endos):
|
||||
m = 4
|
||||
assert len(P_endos) == m-1
|
||||
lut = [0] * (1 << (m-1))
|
||||
lut[0] = P0
|
||||
|
||||
lutS = [''] * (1 << (m-1))
|
||||
lutS[0] = 'P0'
|
||||
endoS = ['P1', 'P2', 'P3']
|
||||
|
||||
for u in range(1, 1 << (m-1)):
|
||||
msb = u.bit_length() - 1
|
||||
idx = clearBit(u, msb)
|
||||
lut[u] = lut[clearBit(u, msb)] + P_endos[msb]
|
||||
|
||||
lutS[u] = lutS[clearBit(u, msb)] + ' + ' + endoS[msb]
|
||||
|
||||
print('LUT: ' + str(lutS))
|
||||
return lut
|
||||
|
||||
def pointToString(P):
|
||||
(Px, Py, Pz) = P
|
||||
vPx = vector(Px)
|
||||
vPy = vector(Py)
|
||||
result = 'Point(\n'
|
||||
result += ' Px: ' + Integer(vPx[0]).hex() + ' + β * ' + Integer(vPx[1]).hex() + '\n'
|
||||
result += ' Py: ' + Integer(vPy[0]).hex() + ' + β * ' + Integer(vPy[1]).hex() + '\n'
|
||||
result += ')'
|
||||
return result
|
||||
|
||||
def getIndex(glvRecoding, bit):
|
||||
m = 4
|
||||
index = 0
|
||||
for k in range(1, m):
|
||||
index |= ((glvRecoding[k][bit] & 1) << (k-1))
|
||||
return index
|
||||
|
||||
def updateFactors(factors, recoded, bit):
|
||||
index = getIndex(recoded, bit)
|
||||
if recoded[0][bit] == 0: # Positive
|
||||
factors[0] += 1
|
||||
factors[1] += (index >> 0) & 1
|
||||
factors[2] += (index >> 1) & 1
|
||||
factors[3] += (index >> 2) & 1
|
||||
else:
|
||||
factors[0] -= 1
|
||||
factors[1] -= (index >> 0) & 1
|
||||
factors[2] -= (index >> 1) & 1
|
||||
factors[3] -= (index >> 2) & 1
|
||||
|
||||
def doubleFactors(factors):
|
||||
for i in range(len(factors)):
|
||||
factors[i] *= 2
|
||||
|
||||
def printFactors(factors):
|
||||
print('Multiplication done: ')
|
||||
for i in range(len(factors)):
|
||||
print(f' f{i}: {factors[i].hex()}')
|
||||
|
||||
def scalarMulEndo(scalar, P0):
|
||||
m = 4
|
||||
|
||||
print('L: ' + str(L))
|
||||
|
||||
print('scalar: ' + Integer(scalar).hex())
|
||||
|
||||
k0, k1, k2, k3 = getGLV2_decomp(scalar)
|
||||
|
||||
P1 = psi(P0)
|
||||
P2 = psi2(P0)
|
||||
P3 = psi(P2)
|
||||
|
||||
expected = scalar * P0
|
||||
decomp = k0*P0 + k1*P1 + k2*P2 + k3*P3
|
||||
print('expected: ' + pointToString(expected))
|
||||
print('decomp: ' + pointToString(decomp))
|
||||
assert expected == decomp
|
||||
|
||||
# Alternative to adding an extra bit
|
||||
# to deal with miniscalars
|
||||
# if k0 < 0: k0 = -k0; P0 = -P0
|
||||
# if k1 < 0: k1 = -k1; P1 = -P1
|
||||
# if k2 < 0: k2 = -k2; P2 = -P2
|
||||
# if k3 < 0: k3 = -k3; P3 = -P3
|
||||
# assert expected == k0*P0 + k1*P1 + k2*P2 + k3*P3
|
||||
|
||||
# Somehow the recoding doesn't cope with first scalar being negative
|
||||
if k0 < 0:
|
||||
k0 = -k0
|
||||
P0 = -P0
|
||||
|
||||
print('------ recode scalar -----------')
|
||||
even = k0 & 1 == 0
|
||||
print('was even: ' + str(even))
|
||||
if even:
|
||||
k0 += 1
|
||||
|
||||
b = recodeScalars([k0, k1, k2, k3])
|
||||
print('b0: ' + str(list(reversed(b[0]))))
|
||||
print('b1: ' + str(list(reversed(b[1]))))
|
||||
print('b2: ' + str(list(reversed(b[2]))))
|
||||
print('b3: ' + str(list(reversed(b[3]))))
|
||||
print('------------ lut ---------------')
|
||||
|
||||
lut = buildLut(P0, [P1, P2, P3])
|
||||
|
||||
print('------------ mul ---------------')
|
||||
# b[0][L-1] is always 0
|
||||
print(f'L-1: {getIndex(b, L-1)}')
|
||||
print(f'L-2: {getIndex(b, L-2)}')
|
||||
print(f'L-3: {getIndex(b, L-3)}')
|
||||
print(f'L-4: {getIndex(b, L-4)}')
|
||||
print(f'L-5: {getIndex(b, L-5)}')
|
||||
print(f'L-6: {getIndex(b, L-6)}')
|
||||
|
||||
factors = [0, 0, 0, 0] # Track the decomposed scalar applied (debugging)
|
||||
updateFactors(factors, b, L-1)
|
||||
|
||||
Q = lut[getIndex(b, L-1)]
|
||||
for bit in range(L-2, -1, -1):
|
||||
Q *= 2
|
||||
Q += (1 - 2 * b[0][bit]) * lut[getIndex(b, bit)]
|
||||
|
||||
doubleFactors(factors)
|
||||
updateFactors(factors, b, bit)
|
||||
|
||||
if even:
|
||||
Q -= P0
|
||||
print('----')
|
||||
print('final Q: ' + pointToString(Q))
|
||||
print('expected: ' + pointToString(expected))
|
||||
print('----')
|
||||
printFactors(factors)
|
||||
print('Mul expected:')
|
||||
print(' k0: ' + k0.hex())
|
||||
print(' k1: ' + k1.hex())
|
||||
print(' k2: ' + k2.hex())
|
||||
print(' k3: ' + k3.hex())
|
||||
|
||||
assert Q == expected
|
||||
|
||||
# Test generator
|
||||
set_random_seed(1337)
|
||||
|
||||
for i in range(1):
|
||||
print('---------------------------------------')
|
||||
scalar = randrange(r) # Pick an integer below curve order
|
||||
# P = G2.random_point()
|
||||
# P = clearCofactorG2(P)
|
||||
|
||||
scalar = Integer('0x9d432eb58ec68bbc09d10961451d99c7796fb2f795eca603d6feaf3e2a1634b')
|
||||
P = G2([
|
||||
Fp2([Integer('0x267401f3ef554fe74ae131d56a10edf14ae40192654901b4618d2bf7af22e77c2a9b79e407348dbd4aad13ca73b33a'),
|
||||
Integer('0x12dcca838f46a3e0418e5dd8b978362757a16bfd78f0b77f4a1916ace353938389ae3ea228d0eb5020a0aaa58884aec')]),
|
||||
Fp2([Integer('0x11799118d2e054aabd9f74c0843fecbdc1c0d56f61c61c5854c2507ae2416e48a6b2cd3bc8bf7495a4d3d8270eafe2b'),
|
||||
Integer('0x823b9f8fb9f8297734a14359fa2c2a0de275e7e638197eaaaa7cff28f9cb3101bdabb570016672455f1ecae625e294')])
|
||||
])
|
||||
|
||||
subgroup_check(P)
|
||||
scalarMulEndo(scalar, P)
|
||||
@ -108,7 +108,20 @@ print(' 𝛼\u03050: 0x' + v[0].hex())
|
||||
print(' 𝛼\u03051: 0x' + v[1].hex())
|
||||
print('')
|
||||
|
||||
def getGLV2_decomp(scalar):
|
||||
maxInfNorm = abs(x + 2)
|
||||
print('\nmax infinity norm:')
|
||||
print(' ||(a0, a1)||∞ ≤ 0x' + str(maxInfNorm.hex()))
|
||||
print(' infinity norm bitlength: ' + str(int(maxInfNorm).bit_length()))
|
||||
|
||||
# Contrary to Faz2013 paper, we use the max infinity norm
|
||||
# to properly dimension our recoding instead of ⌈log2 r/m⌉ + 1
|
||||
# which fails for some inputs
|
||||
# +1 for signed column
|
||||
# Optional +1 for handling negative miniscalars
|
||||
L = int(maxInfNorm).bit_length() + 1
|
||||
L += 1
|
||||
|
||||
def getGLV1_decomp(scalar):
|
||||
|
||||
maxLen = (int(r).bit_length() + 1) // 2 + 1
|
||||
|
||||
@ -128,7 +141,6 @@ def getGLV2_decomp(scalar):
|
||||
|
||||
def recodeScalars(k):
|
||||
m = 2
|
||||
L = ((int(r).bit_length() + m-1) // m) + 1 # l = ⌈log2 r/m⌉ + 1
|
||||
|
||||
b = [[0] * L, [0] * L]
|
||||
b[0][L-1] = 0
|
||||
@ -154,13 +166,11 @@ def pointToString(P):
|
||||
|
||||
def scalarMulEndo(scalar, P0):
|
||||
m = 2
|
||||
L = ((int(r).bit_length() + m-1) // m) + 1 # l = ⌈log2 r/m⌉ + 1
|
||||
|
||||
print('L: ' + str(L))
|
||||
|
||||
print('scalar: ' + Integer(scalar).hex())
|
||||
|
||||
k0, k1 = getGLV2_decomp(scalar)
|
||||
k0, k1 = getGLV1_decomp(scalar)
|
||||
print('k0: ' + k0.hex())
|
||||
print('k1: ' + k1.hex())
|
||||
|
||||
|
||||
@ -96,6 +96,10 @@ def checkEndo():
|
||||
|
||||
checkEndo()
|
||||
|
||||
def subgroup_check(P):
|
||||
ppP = psi2(P)
|
||||
assert x * psi(ppP) - ppP + P == G2([0,1,0])
|
||||
|
||||
# Decomposition generated by LLL-algorithm and Babai rounding
|
||||
# to solve the Shortest (Basis) Vector Problem
|
||||
#
|
||||
@ -141,6 +145,19 @@ print(' 𝛼\u03052: 0x' + v[2].hex())
|
||||
print(' 𝛼\u03053: 0x' + v[3].hex())
|
||||
print('')
|
||||
|
||||
maxInfNorm = abs(x + 2)
|
||||
print('\nmax infinity norm:')
|
||||
print(' ||(a0 , a1 , a2 , a3)||∞ ≤ 0x' + str(maxInfNorm.hex()))
|
||||
print(' infinity norm bitlength: ' + str(int(maxInfNorm).bit_length()))
|
||||
|
||||
# Contrary to Faz2013 paper, we use the max infinity norm
|
||||
# to properly dimension our recoding instead of ⌈log2 r/m⌉ + 1
|
||||
# which fails for some inputs
|
||||
# +1 for signed column
|
||||
# Optional +1 for handling negative miniscalars
|
||||
L = int(maxInfNorm).bit_length() + 1
|
||||
L += 1
|
||||
|
||||
lambda1 = (t-1) % r
|
||||
lambda2 = lambda1^2 % r
|
||||
lambda3 = lambda1^3 % r
|
||||
@ -181,8 +198,6 @@ def getGLV2_decomp(scalar):
|
||||
|
||||
def recodeScalars(k):
|
||||
m = 4
|
||||
L = ((int(r).bit_length() + m-1) // m) + 1 # l = ⌈log2 r/m⌉ + 1
|
||||
L += 1 # Deal with negative miniscalars
|
||||
|
||||
b = [[0] * L, [0] * L, [0] * L, [0] * L]
|
||||
b[0][L-1] = 0
|
||||
@ -259,9 +274,8 @@ def printFactors(factors):
|
||||
|
||||
def scalarMulEndo(scalar, P0):
|
||||
m = 4
|
||||
L = ((int(r).bit_length() + m-1) // m) + 1 # l = ⌈log2 r/m⌉ + 1
|
||||
L += 1 # Deal with negative miniscalars
|
||||
|
||||
print('r bits: ' + str(int(r).bit_length()))
|
||||
print('L: ' + str(L))
|
||||
|
||||
print('scalar: ' + Integer(scalar).hex())
|
||||
@ -278,6 +292,16 @@ def scalarMulEndo(scalar, P0):
|
||||
print('decomp: ' + pointToString(decomp))
|
||||
assert expected == decomp
|
||||
|
||||
# Alternative to adding an extra bit
|
||||
# to deal with miniscalars, unfortunately broken
|
||||
# for some input
|
||||
# for example 0x5668a2332db27199dcfb7cbdfca6317c2ff128db26d7df68483e0a095ec8e88f
|
||||
# which is missing bits for b[2]
|
||||
# if k0 < 0: k0 = -k0; P0 = -P0
|
||||
# if k1 < 0: k1 = -k1; P1 = -P1
|
||||
# if k2 < 0: k2 = -k2; P2 = -P2
|
||||
# if k3 < 0: k3 = -k3; P3 = -P3
|
||||
|
||||
print('------ recode scalar -----------')
|
||||
even = k0 & 1 == 0
|
||||
print('was even: ' + str(even))
|
||||
@ -363,13 +387,29 @@ for i in range(1):
|
||||
# Integer('0xf5d6d74f1dd3d9c07451340b8f6990fe93a28fe5e176564eb920bf17eb02df8b6f1e626eda5542ff415f89d51943001')])
|
||||
# ])
|
||||
|
||||
scalar = Integer('0x5668a2332db27199dcfb7cbdfca6317c2ff128db26d7df68483e0a095ec8e88f')
|
||||
# The following input fails in Constantine when negating the base point
|
||||
# but not when adding an extra bit
|
||||
|
||||
# scalar = Integer('0x5668a2332db27199dcfb7cbdfca6317c2ff128db26d7df68483e0a095ec8e88f')
|
||||
# P = G2([
|
||||
# Fp2([Integer('0xa8c5649d2df1bae84fd9e8bfcde5113937b3acea22d67ddfedaf1fb8de8c1ef4c70591cf505c24c31e54020c2c510c3'),
|
||||
# Integer('0xa0553f98229a6a067489c3ee204161c11e96f421b3e9c145dc3865b03e9d4ff6cab14c5b5308ecd31173f954463690c')]),
|
||||
# Fp2([Integer('0xb29d8dfe18dc41b4826c3a102c1bf8f306cb42433cc36ee38080f47a324c02a678f9daed0a2bc577c18b9865de029f0'),
|
||||
# Integer('0x558cdabf11e37c5c5e8abd668bbdd71bb3f07f320948ccaac8a207359fffe38424bfd9b1ef1d24b28b2fbb9f76faff1')])
|
||||
# ])
|
||||
|
||||
# The following fails when we have both extra bit and negation of the first
|
||||
# scalar if it is negative.
|
||||
# it also uses 65 bits instead of teh expected max of 64
|
||||
# And triggers an off by 1 when negating
|
||||
|
||||
scalar = Integer('0x6448f296d9b1a8d81319a0b789df04c587c6165776ccf39f50a354204aabe0da')
|
||||
P = G2([
|
||||
Fp2([Integer('0xa8c5649d2df1bae84fd9e8bfcde5113937b3acea22d67ddfedaf1fb8de8c1ef4c70591cf505c24c31e54020c2c510c3'),
|
||||
Integer('0xa0553f98229a6a067489c3ee204161c11e96f421b3e9c145dc3865b03e9d4ff6cab14c5b5308ecd31173f954463690c')]),
|
||||
Fp2([Integer('0xb29d8dfe18dc41b4826c3a102c1bf8f306cb42433cc36ee38080f47a324c02a678f9daed0a2bc577c18b9865de029f0'),
|
||||
Integer('0x558cdabf11e37c5c5e8abd668bbdd71bb3f07f320948ccaac8a207359fffe38424bfd9b1ef1d24b28b2fbb9f76faff1')])
|
||||
Fp2([Integer('0x5adc112fb04bf4ca642d5a7d7343ccd6b93546442d2fff5b9d32c15e456d54884cba49dd7f94ce4ddaad4018e55d0f2'),
|
||||
Integer('0x5d1c5bbf5d7a833dc76ba206bfa99c281fc37941be050e18f8c6d267b2376b3634d8ad6eb951e52a6d096315abd17d6')]),
|
||||
Fp2([Integer('0x15a959e54981fab9ac3c6f5bfd6fb60a50a916bd43d96a09922a54309b84812736581bfa728670cba864b08b9e391bb9'),
|
||||
Integer('0xf5d6d74f1dd3d9c07451340b8f6990fe93a28fe5e176564eb920bf17eb02df8b6f1e626eda5542ff415f89d51943001')])
|
||||
])
|
||||
|
||||
|
||||
subgroup_check(P)
|
||||
scalarMulEndo(scalar, P)
|
||||
|
||||
@ -101,6 +101,19 @@ print(' 𝛼\u03050: 0x' + v[0].hex())
|
||||
print(' 𝛼\u03051: 0x' + v[1].hex())
|
||||
print('')
|
||||
|
||||
maxInfNorm = abs(6*x^2+6*x+2)
|
||||
print('\nmax infinity norm:')
|
||||
print(' ||(a0, a1)||∞ ≤ 0x' + str(maxInfNorm.hex()))
|
||||
print(' infinity norm bitlength: ' + str(int(maxInfNorm).bit_length()))
|
||||
|
||||
# Contrary to Faz2013 paper, we use the max infinity norm
|
||||
# to properly dimension our recoding instead of ⌈log2 r/m⌉ + 1
|
||||
# which fails for some inputs
|
||||
# +1 for signed column
|
||||
# Optional +1 for handling negative miniscalars
|
||||
L = int(maxInfNorm).bit_length() + 1
|
||||
L += 1
|
||||
|
||||
def getGLV2_decomp(scalar):
|
||||
|
||||
maxLen = (int(r).bit_length() + 1) // 2 + 1
|
||||
@ -121,7 +134,6 @@ def getGLV2_decomp(scalar):
|
||||
|
||||
def recodeScalars(k):
|
||||
m = 2
|
||||
L = ((int(r).bit_length() + m-1) // m) + 1 # l = ⌈log2 r/m⌉ + 1
|
||||
|
||||
b = [[0] * L, [0] * L]
|
||||
b[0][L-1] = 0
|
||||
@ -147,8 +159,6 @@ def pointToString(P):
|
||||
|
||||
def scalarMulEndo(scalar, P0):
|
||||
m = 2
|
||||
L = ((int(r).bit_length() + m-1) // m) + 1 # l = ⌈log2 r/m⌉ + 1
|
||||
|
||||
print('L: ' + str(L))
|
||||
|
||||
print('scalar: ' + Integer(scalar).hex())
|
||||
|
||||
@ -142,6 +142,19 @@ print(' 𝛼\u03052: 0x' + v[2].hex())
|
||||
print(' 𝛼\u03053: 0x' + v[3].hex())
|
||||
print('')
|
||||
|
||||
maxInfNorm = abs(5*x + 3)
|
||||
print('\nmax infinity norm:')
|
||||
print(' ||(a0 , a1 , a2 , a3)||∞ ≤ 0x' + str(maxInfNorm.hex()))
|
||||
print(' infinity norm bitlength: ' + str(int(maxInfNorm).bit_length()))
|
||||
|
||||
# Contrary to Faz2013 paper, we use the max infinity norm
|
||||
# to properly dimension our recoding instead of ⌈log2 r/m⌉ + 1
|
||||
# which fails for some inputs
|
||||
# +1 for signed column
|
||||
# Optional +1 for handling negative miniscalars
|
||||
L = int(maxInfNorm).bit_length() + 1
|
||||
L += 1
|
||||
|
||||
lambda1 = (t-1) % r
|
||||
lambda2 = lambda1^2 % r
|
||||
lambda3 = lambda1^3 % r
|
||||
@ -204,7 +217,6 @@ def getGLV2_decomp(scalar):
|
||||
|
||||
def recodeScalars(k):
|
||||
m = 4
|
||||
L = ((int(r).bit_length() + m-1) // m) + 1 # l = ⌈log2 r/m⌉ + 1
|
||||
|
||||
b = [[0] * L, [0] * L, [0] * L, [0] * L]
|
||||
b[0][L-1] = 0
|
||||
@ -280,8 +292,6 @@ def printFactors(factors):
|
||||
|
||||
def scalarMulEndo(scalar, P0):
|
||||
m = 4
|
||||
L = ((int(r).bit_length() + m-1) // m) + 1 # l = ⌈log2 r/m⌉ + 1
|
||||
|
||||
print('L: ' + str(L))
|
||||
|
||||
print('scalar: ' + Integer(scalar).hex())
|
||||
|
||||
263
sage/square_root_bls12_377.sage
Normal file
263
sage/square_root_bls12_377.sage
Normal file
@ -0,0 +1,263 @@
|
||||
# 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.
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
# BLS12-377
|
||||
# Constant-time Square Root
|
||||
#
|
||||
# ############################################################
|
||||
|
||||
# Parameters
|
||||
x = 3 * 2^46 * (7 * 13 * 499) + 1
|
||||
p = (x - 1)^2 * (x^4 - x^2 + 1)//3 + x
|
||||
r = x^4 - x^2 + 1
|
||||
t = x + 1
|
||||
print('x : ' + x.hex())
|
||||
print('p : ' + p.hex())
|
||||
print('r : ' + r.hex())
|
||||
print('t : ' + t.hex())
|
||||
|
||||
def modCheck(p, pow):
|
||||
## Find q mod 2^s != 1
|
||||
q = p^pow
|
||||
s = 4
|
||||
while q % s == 1:
|
||||
s *= 2
|
||||
if s > q:
|
||||
raise ValueError('Uh Oh')
|
||||
if pow == 1:
|
||||
print(f'Found: p mod {s} = {q % s}')
|
||||
else:
|
||||
print(f'Found: p^{pow} mod {s} = {q % s}')
|
||||
|
||||
modCheck(p, 1) # Found: p mod 140737488355328 = 70368744177665
|
||||
modCheck(p, 2) # Found: p^2 mod 281474976710656 = 140737488355329
|
||||
|
||||
# On Fp
|
||||
# a^((p-70368744177665+140737488355328)/140737488355328)
|
||||
# would lead to a square root but there would be
|
||||
# log2(140737488355328)-1 candidates
|
||||
# which must be checked constant time
|
||||
|
||||
def precomp_tonelli_shanks(p):
|
||||
## Precompute constants for
|
||||
## constant-time Tonelli Shanks algorithm
|
||||
## with q = p^pow returns:
|
||||
## 1. c1, the largest integer such that 2^c1 divides q - 1.
|
||||
## 2. c2 = (q - 1) / (2^c1) in ℕ
|
||||
## 3. c3 = (c2 - 1) / 2 in ℕ
|
||||
## 4. c4, a non-square value in Fq
|
||||
## 5. c5 = c4^c2 in Fq
|
||||
q = p
|
||||
c1 = 0
|
||||
c2 = q-1
|
||||
while c2 & 1 == 0:
|
||||
c2 >>= 1
|
||||
c1 += 1
|
||||
c3 = (c2 - 1) // 2
|
||||
c4 = 1
|
||||
while kronecker(c4, q) == 1:
|
||||
c4 += 1
|
||||
c5 = GF(p)(c4)^c2
|
||||
return (c1,c2,c3,c4,c5)
|
||||
|
||||
def ccopy(a, b, ctl):
|
||||
## `b` if `ctl` is true, `a` if false
|
||||
return int(not(bool(ctl)))*a + int(bool(ctl))*b
|
||||
|
||||
def sqrt_tonelli_shanks(x, p):
|
||||
## Returns z = x² (p^pow)
|
||||
(c1, c2, c3, c4, c5) = precomp_tonelli_shanks(p)
|
||||
|
||||
x = GF(p)(x)
|
||||
|
||||
z = x^c3
|
||||
t = z*z*x
|
||||
z *= x
|
||||
b = t
|
||||
c = c5
|
||||
for i in range(c1, 1, -1): # c1 ... 2
|
||||
for j in range(1, i-1): # 1 ... i-2
|
||||
b *= b
|
||||
z = ccopy(z, z*c, b != 1)
|
||||
c *= c
|
||||
t = ccopy(t, t*c, b != 1)
|
||||
b = t
|
||||
return z
|
||||
|
||||
# for a in range(2, 30):
|
||||
# if kronecker(a, p) != 1:
|
||||
# continue
|
||||
# # print(f'{a}^(p-1)/2 = ' + str(GF(p)(a)^((p-1)/2)))
|
||||
# print(f'{a} is a quadratic residue mod p')
|
||||
# b = sqrt_tonelli_shanks(a, p)
|
||||
# # print(f'{b}² = {a} mod p')
|
||||
# # print('b*b = ' + str(b*b))
|
||||
# assert b*b == a
|
||||
|
||||
# Optimized Tonelli Shanks
|
||||
# --------------------------------------------------------
|
||||
|
||||
# Finite fields
|
||||
Fp = GF(p)
|
||||
K2.<u> = PolynomialRing(Fp)
|
||||
Fp2.<beta> = Fp.extension(u^2+5)
|
||||
|
||||
def precomp_ts(Fq):
|
||||
## From q = p^m with p the prime characteristic of the field Fp^m
|
||||
##
|
||||
## Returns (s, e) such as
|
||||
## q == s * 2^e + 1
|
||||
s = Fq.order() - 1
|
||||
e = 0
|
||||
while s & 1 == 0:
|
||||
s >>= 1
|
||||
e += 1
|
||||
return s, e
|
||||
|
||||
def find_any_qnr(Fq):
|
||||
## Find a quadratic Non-Residue
|
||||
## in GF(p^m)
|
||||
qnr = Fq(Fq.gen())
|
||||
r = Fq.order()
|
||||
while qnr.is_square():
|
||||
qnr += 1
|
||||
return qnr
|
||||
|
||||
def sqrt_exponent_precomp(Fq, e):
|
||||
## Returns precomputation a^((q-1-2^e)/(2*2^e))
|
||||
##
|
||||
## With 2^e the largest power of 2 that divides q-1
|
||||
##
|
||||
## For all sqrt related functions
|
||||
## - legendre symbol
|
||||
## - SQRT
|
||||
## - inverse SQRT
|
||||
r = Fq.order()
|
||||
precomp = (r - 1) >> e # (q-1) / 2^e
|
||||
precomp = (precomp - 1) >> 1 # ((q-1) / 2^e) - 1) / 2 = (q-1-2^e)/2^e / 2
|
||||
return precomp
|
||||
|
||||
s, e = precomp_ts(Fp)
|
||||
qnr = find_any_qnr(Fp)
|
||||
root_unity = qnr^s
|
||||
exponent = sqrt_exponent_precomp(Fp, e)
|
||||
|
||||
# print('tonelli s: 0x' + Integer(s).hex())
|
||||
print('tonelli e (2-adicity): ' + str(e))
|
||||
print('tonelli root: 0x' + Integer(root_unity).hex())
|
||||
print('tonelli exponent: 0x' + Integer(exponent).hex())
|
||||
|
||||
def legendre_symbol_impl(a, e, a_pre_exp):
|
||||
## Legendre symbol χ(a) = a^(q-1)/2
|
||||
## -1 if a is non-square
|
||||
## 0 if a is 0
|
||||
## 1 if a is square
|
||||
##
|
||||
## a_pre_exp = a^((q-1-2^e)/(2*2^e))
|
||||
## with
|
||||
## s and e, precomputed values
|
||||
## such as q == s * 2^e + 1
|
||||
##
|
||||
## a_pre_exp is used in square root
|
||||
## and or inverse square root computation
|
||||
##
|
||||
## for fused operations
|
||||
r = a_pre_exp * a_pre_exp # a^((q-1-2^e)/2^e) = a^((q-1)/2^e - 1)
|
||||
r *= a # a^((q-1)/2^e)
|
||||
for i in range(0, e-1):
|
||||
r *= r # a^((q-1)/2)
|
||||
|
||||
return r
|
||||
|
||||
def legendre_symbol(a):
|
||||
a_pre_exp = a^exponent
|
||||
return legendre_symbol_impl(a, e, a_pre_exp)
|
||||
|
||||
for a in range(20):
|
||||
assert kronecker(a, p) == legendre_symbol(GF(p)(a))
|
||||
|
||||
def sqrt_tonelli_shanks_impl(a, a_pre_exp, s, e, root_of_unity):
|
||||
## Square root for any `a` in a field of prime characteristic p
|
||||
##
|
||||
## a_pre_exp = a^((q-1-2^e)/(2*2^e))
|
||||
## with
|
||||
## s and e, precomputed values
|
||||
## such as q == s * 2^e + 1
|
||||
z = a_pre_exp
|
||||
t = z*z*a
|
||||
r = z * a
|
||||
b = t
|
||||
root = root_of_unity
|
||||
for i in range(e, 1, -1): # e .. 2
|
||||
for j in range(1, i-1): # 1 .. i-2
|
||||
b *= b
|
||||
doCopy = b != 1
|
||||
r = ccopy(r, r * root, doCopy)
|
||||
root *= root
|
||||
t = ccopy(t, t * root, doCopy)
|
||||
b = t
|
||||
return r
|
||||
|
||||
def sqrt_tonelli_shanks_opt(a):
|
||||
a_pre_exp = a^exponent
|
||||
return sqrt_tonelli_shanks_impl(a, a_pre_exp, s, e, root_unity)
|
||||
|
||||
# for a in range(2, 30):
|
||||
# if kronecker(a, p) != 1:
|
||||
# continue
|
||||
# # print(f'{a}^(p-1)/2 = ' + str(GF(p)(a)^((p-1)/2)))
|
||||
# print(f'{a} is a quadratic residue mod p')
|
||||
# b = sqrt_tonelli_shanks_opt(GF(p)(a))
|
||||
# # print(f'{b}² = {a} mod p')
|
||||
# # print('b*b = ' + str(b*b))
|
||||
# assert b*b == a
|
||||
|
||||
def sqrt_inv_sqrt_tonelli_shanks_impl(a, a_pre_exp, s, e, root_of_unity):
|
||||
## Square root and inverse square root for any `a` in a field of prime characteristic p
|
||||
##
|
||||
## a_pre_exp = a^((q-1-2^e)/(2*2^e))
|
||||
## with
|
||||
## s and e, precomputed values
|
||||
## such as q == s * 2^e + 1
|
||||
|
||||
# Implementation
|
||||
# 1/√a * a = √a
|
||||
# Notice that in Tonelli Shanks, the result `r` is bootstrapped by "z*a"
|
||||
# We bootstrap it instead by just z to get invsqrt for free
|
||||
|
||||
z = a_pre_exp
|
||||
t = z*z*a
|
||||
r = z
|
||||
b = t
|
||||
root = root_of_unity
|
||||
for i in range(e, 1, -1): # e .. 2
|
||||
for j in range(1, i-1): # 1 .. i-2
|
||||
b *= b
|
||||
doCopy = b != 1
|
||||
r = ccopy(r, r * root, doCopy)
|
||||
root *= root
|
||||
t = ccopy(t, t * root, doCopy)
|
||||
b = t
|
||||
return r*a, r
|
||||
|
||||
def sqrt_invsqrt_tonelli_shanks_opt(a):
|
||||
a_pre_exp = a^exponent
|
||||
return sqrt_inv_sqrt_tonelli_shanks_impl(a, a_pre_exp, s, e, root_unity)
|
||||
|
||||
for a in range(2, 30):
|
||||
if kronecker(a, p) != 1:
|
||||
continue
|
||||
# print(f'{a}^(p-1)/2 = ' + str(GF(p)(a)^((p-1)/2)))
|
||||
print(f'{a} is a quadratic residue mod p')
|
||||
b, invb = sqrt_invsqrt_tonelli_shanks_opt(GF(p)(a))
|
||||
# print(f'{b}² = {a} mod p')
|
||||
# print('b*b = ' + str(b*b))
|
||||
assert b*b == a
|
||||
assert invb*a == b
|
||||
109
sage/testgen_bls12_377.sage
Normal file
109
sage/testgen_bls12_377.sage
Normal file
@ -0,0 +1,109 @@
|
||||
# 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.
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
# BN254 test generator
|
||||
#
|
||||
# ############################################################
|
||||
|
||||
# Parameters
|
||||
x = 3 * 2^46 * (7 * 13 * 499) + 1
|
||||
p = (x - 1)^2 * (x^4 - x^2 + 1)//3 + x
|
||||
r = x^4 - x^2 + 1
|
||||
|
||||
# Finite fields
|
||||
Fp = GF(p)
|
||||
K2.<u> = PolynomialRing(Fp)
|
||||
Fp2.<beta> = Fp.extension(u^2+5)
|
||||
|
||||
# Curves
|
||||
b = 1
|
||||
SNR = Fp2([0, 1])
|
||||
G1 = EllipticCurve(Fp, [0, b])
|
||||
G2 = EllipticCurve(Fp2, [0, b/SNR])
|
||||
|
||||
# https://crypto.stackexchange.com/questions/64064/order-of-twisted-curve-in-pairings
|
||||
# https://math.stackexchange.com/questions/144194/how-to-find-the-order-of-elliptic-curve-over-finite-field-extension
|
||||
cofactorG1 = G1.order() // r
|
||||
cofactorG2 = G2.order() // r
|
||||
|
||||
print('')
|
||||
print('cofactor G1: ' + cofactorG1.hex())
|
||||
print('cofactor G2: ' + cofactorG2.hex())
|
||||
print('')
|
||||
|
||||
def clearCofactorG1(P):
|
||||
return cofactorG1 * P
|
||||
|
||||
def clearCofactorG2(P):
|
||||
return cofactorG2 * P
|
||||
|
||||
# Test generator
|
||||
set_random_seed(1337)
|
||||
|
||||
print('=========================================')
|
||||
print('G1 vectors: ')
|
||||
for i in range(10):
|
||||
print(f'--- test {i} ------------------------------')
|
||||
Prand = G1.random_point()
|
||||
P = clearCofactorG1(Prand)
|
||||
|
||||
(Px, Py, Pz) = P
|
||||
print('Px: ' + Integer(Px).hex())
|
||||
print('Py: ' + Integer(Py).hex())
|
||||
# print('Pz: ' + Integer(Pz).hex())
|
||||
exponent = randrange(r) # Pick an integer below curve order
|
||||
print('scalar: ' + Integer(exponent).hex())
|
||||
|
||||
Q = exponent * P
|
||||
(Qx, Qy, Qz) = Q
|
||||
print('Qx: ' + Integer(Qx).hex())
|
||||
print('Qy: ' + Integer(Qy).hex())
|
||||
# print('Qz: ' + Integer(Qz).hex())
|
||||
print('=========================================')
|
||||
print('G2 vectors: ')
|
||||
|
||||
for i in range(10):
|
||||
print(f'--- test {i} ------------------------------')
|
||||
Prand = G2.random_point()
|
||||
P = clearCofactorG2(Prand)
|
||||
|
||||
(Px, Py, Pz) = P
|
||||
vPx = vector(Px)
|
||||
vPy = vector(Py)
|
||||
# Pz = vector(Pz)
|
||||
print('Px: ' + Integer(vPx[0]).hex() + ' + β * ' + Integer(vPx[1]).hex())
|
||||
print('Py: ' + Integer(vPy[0]).hex() + ' + β * ' + Integer(vPy[1]).hex())
|
||||
|
||||
exponent = randrange(r) # Pick an integer below curve order
|
||||
print('scalar: ' + Integer(exponent).hex())
|
||||
|
||||
Q = exponent * P
|
||||
(Qx, Qy, Qz) = Q
|
||||
Qx = vector(Qx)
|
||||
Qy = vector(Qy)
|
||||
print('Qx: ' + Integer(Qx[0]).hex() + ' + β * ' + Integer(Qx[1]).hex())
|
||||
print('Qy: ' + Integer(Qy[0]).hex() + ' + β * ' + Integer(Qy[1]).hex())
|
||||
print('=========================================')
|
||||
|
||||
# CurveOrder sanity check
|
||||
#
|
||||
# P = G1.random_point()
|
||||
# (Px, Py, Pz) = P
|
||||
# print('Px: ' + Integer(Px).hex())
|
||||
# print('Py: ' + Integer(Py).hex())
|
||||
# print('Pz: ' + Integer(Pz).hex())
|
||||
#
|
||||
# print('order: ' + Integer(r).hex())
|
||||
#
|
||||
# Q = (r * cofactor) * P
|
||||
# (Qx, Qy, Qz) = Q
|
||||
# print('Qx: ' + Integer(Qx).hex())
|
||||
# print('Qy: ' + Integer(Qy).hex())
|
||||
# print('Qz: ' + Integer(Qz).hex())
|
||||
331
tests/t_ec_sage_bls12_377.nim
Normal file
331
tests/t_ec_sage_bls12_377.nim
Normal file
@ -0,0 +1,331 @@
|
||||
# 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/[unittest, times],
|
||||
# Internals
|
||||
../constantine/config/[common, curves],
|
||||
../constantine/arithmetic,
|
||||
../constantine/towers,
|
||||
../constantine/io/[io_bigints, io_ec],
|
||||
../constantine/elliptic/[ec_weierstrass_projective, ec_scalar_mul, ec_endomorphism_accel],
|
||||
# Test utilities
|
||||
./support/ec_reference_scalar_mult
|
||||
|
||||
echo "\n------------------------------------------------------\n"
|
||||
|
||||
proc test(
|
||||
id: int,
|
||||
EC: typedesc[ECP_SWei_Proj],
|
||||
Px, Py: string,
|
||||
scalar: string,
|
||||
Qx, Qy: string
|
||||
) =
|
||||
|
||||
test "test " & $id:
|
||||
var P: EC
|
||||
let pOK = P.fromHex(Px, Py)
|
||||
doAssert pOK
|
||||
|
||||
var Q: EC
|
||||
let qOK = Q.fromHex(Qx, Qy)
|
||||
|
||||
let exponent = BigInt[EC.F.C.getCurveOrderBitwidth()].fromHex(scalar)
|
||||
|
||||
var
|
||||
impl = P
|
||||
reference = P
|
||||
endo = P
|
||||
endoW = P
|
||||
|
||||
impl.scalarMulGeneric(exponent)
|
||||
reference.unsafe_ECmul_double_add(exponent)
|
||||
endo.scalarMulEndo(exponent)
|
||||
endoW.scalarMulGLV_m2w2(exponent)
|
||||
|
||||
doAssert: bool(Q == reference)
|
||||
doAssert: bool(Q == impl)
|
||||
doAssert: bool(Q == endo)
|
||||
doAssert: bool(Q == endoW)
|
||||
|
||||
suite "Scalar Multiplication (cofactor cleared): BLS12_377 implementation vs SageMath" & " [" & $WordBitwidth & "-bit mode]":
|
||||
# Generated via sage sage/testgen_bls12_377.sage
|
||||
test(
|
||||
id = 0,
|
||||
EC = ECP_SWei_Proj[Fp[BLS12_377]],
|
||||
Px = "4e7e6dfa01ed0ceb6e66708b07cb5c6cd30a42eeb13d7b76f8d103a0a4d491450be6f526fc12f15209b792220c041e",
|
||||
Py = "c782515159b7e7b9371e0e0caa387951317e993b1625d91869d4346621058a0960ef1b8b6eabb33cd5719694908a05",
|
||||
scalar = "cf815cb4d44d3d691b7c82a40b4b70caa9b0e8fe9586648abf3f1e2e639ca1b",
|
||||
Qx = "4a1203db4af8f0efc18c7ceb24999eb6e0dbdfc8f44a9edd5ba2f9eced38e81ecae287ab1c184eea8e8753d1178604",
|
||||
Qy = "f7509cdce5de473e37f36c69d93ff1b2ab04c3ae25a3b5e41f2c138cdcc69ed5eaf4ac95a7a857eef336ebac19bcd3"
|
||||
)
|
||||
|
||||
test(
|
||||
id = 1,
|
||||
EC = ECP_SWei_Proj[Fp[BLS12_377]],
|
||||
Px = "13d735b28405253dcc0bc60bcdc13633475ffc187d38a9b97655b0d0fa1d56c4548f11ea0a795391ee85c953aaf9b83",
|
||||
Py = "1693101123fd13a20f9c0569c52c29507ba1c8b6dd412660bc82e7974022f1a10f9137b4ba59d3f0aab67027cefec19",
|
||||
scalar = "913aa7b9fa2f940b70b6dcf538cc08da1a369809ab86a8ee49cead0ed6bfef6",
|
||||
Qx = "10e0e8582ec3456f7569473892c23997d004f2542d914fa75db8f1798ed8ce505836e8b7af5cf1503e14d85fadd65ee",
|
||||
Qy = "d5151ae60e43100e20aad7eaf8659e690e8034910d7717078031520fcbf6f9a00b22c6a9894aec88c9182f13335639"
|
||||
)
|
||||
|
||||
test(
|
||||
id = 2,
|
||||
EC = ECP_SWei_Proj[Fp[BLS12_377]],
|
||||
Px = "6dd2211113cada09d3b96848cbf21ed14f6fc238b581c0afd49aa776980101e4ee279a256ce8f1428d3ed3f70afd85",
|
||||
Py = "3b406b4433a3f44f8e196012f50c520e876412fbcae2651916f133c1fd3899c79f676e1abba01d84bab7ad100c9295",
|
||||
scalar = "4cf47669aeb0f30b6c6c5aa02808a87dc787fba22da32875e614a54a50b6a0c",
|
||||
Qx = "3caf55e1f82d746df2bf47defbee127a3e6f7e79a9575929704b25489ffb801dbce07999acbddfd79352b0633a2708",
|
||||
Qy = "3fc275c136b07456f0b6ee814508876037add5e95357fae6b4195744ba5ccccaf93581e34feb2babe652a9a704731b"
|
||||
)
|
||||
|
||||
test(
|
||||
id = 3,
|
||||
EC = ECP_SWei_Proj[Fp[BLS12_377]],
|
||||
Px = "18648abbe3261f93cbf679777eac66e419035041a967d1e6a0f0afdb92810d7122e0984f1d6efc8fe518500464ee803",
|
||||
Py = "7b6f518d4d06309aad4d60d29118310b6c8b17c7bf5db2251f4701b13b89a8c0f04bb5d0386785e55ffbbd7ecc445e",
|
||||
scalar = "69498486a06c18f836a8e9ed507bbb563d6d03545e03e08f628e8fbd2e5d098",
|
||||
Qx = "6a871d872673879fd23ec8c150f8d63e8130dc60343fc0c2ec9ff1b02e769e20eeec0288102ccec6ff2f6ac6973b4",
|
||||
Qy = "cd95a31afa4f0bbcc063a1585fec4a6c51812b2baaee42a04525fd55b3a9db3bdb7cbc77f7cd32f42d2e0eb26ddfa2"
|
||||
)
|
||||
|
||||
test(
|
||||
id = 4,
|
||||
EC = ECP_SWei_Proj[Fp[BLS12_377]],
|
||||
Px = "745e9944549522486f3446676cc62fb666682e10c661a13b8110b42cb9a37676b6f33a46f9495f4fafb342d5809db5",
|
||||
Py = "bc595854bc42ccec60dd9ec573608d736aa59996cef1c2e8f6c5d424f525a6f3e3d4beeedfac6b959dbd71ced95b13",
|
||||
scalar = "6e08d8714102a5aa3e9f46e33c70a759c27253c7b0196c3e46c7cb42671197e",
|
||||
Qx = "18a967a80785de8ec6ac9d98cffa06a8c633b5fa0f36431a32f7bd955946edc3d55f79bfdf5335db405560a6cbe2415",
|
||||
Qy = "4552ff2eb6ade0c6b33c3603460d9d62099201d842c9883b33b7ed1147cb17268338a77a9417776ddbd774e91a8d2"
|
||||
)
|
||||
|
||||
test(
|
||||
id = 5,
|
||||
EC = ECP_SWei_Proj[Fp[BLS12_377]],
|
||||
Px = "f7b56cf212a8906ed77a758164c0bd05ce1fbd3ee3c4357e7a09b3aedc748a29ace254f1f6df35b8cb74361060337f",
|
||||
Py = "2640ef641d20fea19b28947833e53faceeafa57a8761b807049f3d707d70c01f1c69a57edd993d301a64517bf47f77",
|
||||
scalar = "a5c46d7a52938fed7f3093d5867f65361dc8b48c83bd7db490c26736196e20e",
|
||||
Qx = "434f024c4afd7b9a44375011d663af0ae0fe79442e9caf36518e053bb13d49998ec1d2da4bc1c4a812812119b3221f",
|
||||
Qy = "32c959e2a678cf93f77e621fdf887454a5f9cb7a67f29065669f48234c25e321ceb5758dfba987431a0c2caec94616"
|
||||
)
|
||||
|
||||
test(
|
||||
id = 6,
|
||||
EC = ECP_SWei_Proj[Fp[BLS12_377]],
|
||||
Px = "43b387c12e4cc91fb1a5593b7671356dceb0fe6e6ac666d5bac94f2a44c8db54976b649a678aae038b21144de04e23",
|
||||
Py = "1a6366a50f1f9ba64eef73d82bec86177bf184be048a9d66326ccb0122203569ddcb8cf74445cadaff7f47a66d1b1a2",
|
||||
scalar = "bddd07231bc7fe89ee4a859a00ea1f9d236be9e7fd561303d566904c1b0a07c",
|
||||
Qx = "8ddb7a959483b51a1471de988146b7d5b166f660734b4d55166c5a23d781e923261927b1012dce73b822bb6e56bfd2",
|
||||
Qy = "dc7178bf5597ef31209a5b0409fc42c64b81260a0046a562ca08c7a9dc67b444c25e94fb97b9a6bb4c137cadd81021"
|
||||
)
|
||||
|
||||
test(
|
||||
id = 7,
|
||||
EC = ECP_SWei_Proj[Fp[BLS12_377]],
|
||||
Px = "6984c9385a67081f97b3d33444077466cca1d0442a4da8c083a0957578e0b21011435b126a5ab143da9da1cf5b216f",
|
||||
Py = "18a87c7f5f6c5a8101773e63956b9addd4becf5177acc560d548e5331638121934842fdc9f654b3f456a7df5a2e471a",
|
||||
scalar = "b72c41a6ffaff0aacb5d62e3dcb16acfec66b6a9639e19d3128fd43c18e7dbe",
|
||||
Qx = "19279201c3c7a9d50b546aa99d3e1a6625fe2a7bc64a09625b683534638b9e87b9102d4dba6684956b6be7668a658c6",
|
||||
Qy = "195f15ad1edc05f8b289c7eee7bd8f78116a2d5ba8b83643d9e7cb2cdc6550bcf8c2145008e900ca9cba4d5040e9f4"
|
||||
)
|
||||
|
||||
test(
|
||||
id = 8,
|
||||
EC = ECP_SWei_Proj[Fp[BLS12_377]],
|
||||
Px = "efc34595823e7333616f7768bc82f407268df6f029bf4c94d15f1785dc7ccc08f22e7301dfc48dadc0ea383c8bb3e",
|
||||
Py = "2459bd9f71977ef122d2102e8bfd07a5737066075058cfa8bcaa9f9690ed065919c844363ceaea6f9bb650906a535f",
|
||||
scalar = "8f90f6ab0ffa6e4acc601b44c062745f2935b3dc153d0da07977470080d5c18",
|
||||
Qx = "35d08b33e02579581905941975c8b1cc5be1c9670a7f7ef390daa363b0abd3571a802d8c27f156fba40573094f6c7a",
|
||||
Qy = "111ba3bdfa4260dc2b636479edb1fcf2fc9478aa722da0118908e1db1551cf7e131c8521b2f3708ef670684dbe8d181"
|
||||
)
|
||||
|
||||
test(
|
||||
id = 9,
|
||||
EC = ECP_SWei_Proj[Fp[BLS12_377]],
|
||||
Px = "3eec93c2a9c5fd03f0de5ede2fdac9e361090fbaea38e4a0f1828745f1d14a057d9fd7c46b9168bd95a45a182a3a62",
|
||||
Py = "e912dc7e95f90d91e3274ec5639edacb88be1b092c47c13d31a29ecd579885cc09f197f8207d23b2260ab10c94d5f5",
|
||||
scalar = "203300e949aff816d084a388f07c74b9152bb11b523543afd65c805a389980",
|
||||
Qx = "8c23ae9c51e7c92c6a59f0ed07a59f148b4d79394fc026931c264612041eedd3782e4f249bbfad1799212788c1a00d",
|
||||
Qy = "191628b702ebef397bb7c52102ee4522f864ff594b24dc385ece081e8066bcde20aa5c7dfd00fb1f3cea221b3ad4ea2"
|
||||
)
|
||||
|
||||
|
||||
proc test(
|
||||
id: int,
|
||||
EC: typedesc[ECP_SWei_Proj],
|
||||
Px0, Px1, Py0, Py1: string,
|
||||
scalar: string,
|
||||
Qx0, Qx1, Qy0, Qy1: string
|
||||
) =
|
||||
|
||||
test "test " & $id:
|
||||
var P: EC
|
||||
let pOK = P.fromHex(Px0, Px1, Py0, Py1)
|
||||
doAssert pOK
|
||||
|
||||
var Q: EC
|
||||
let qOK = Q.fromHex(Qx0, Qx1, Qy0, Qy1)
|
||||
|
||||
let exponent = BigInt[EC.F.C.getCurveOrderBitwidth()].fromHex(scalar)
|
||||
|
||||
var
|
||||
impl = P
|
||||
reference = P
|
||||
endo = P
|
||||
|
||||
impl.scalarMulGeneric(exponent)
|
||||
reference.unsafe_ECmul_double_add(exponent)
|
||||
endo.scalarMulEndo(exponent)
|
||||
|
||||
doAssert: bool(Q == reference)
|
||||
doAssert: bool(Q == impl)
|
||||
doAssert: bool(Q == endo)
|
||||
|
||||
suite "Scalar Multiplication G2: BLS12-381 implementation vs SageMath" & " [" & $WordBitwidth & "-bit mode]":
|
||||
# Generated via sage sage/testgen_bls12_377.sage
|
||||
test(
|
||||
id = 0,
|
||||
EC = ECP_SWei_Proj[Fp2[BLS12_377]],
|
||||
Px0 = "267401f3ef554fe74ae131d56a10edf14ae40192654901b4618d2bf7af22e77c2a9b79e407348dbd4aad13ca73b33a",
|
||||
Px1 = "12dcca838f46a3e0418e5dd8b978362757a16bfd78f0b77f4a1916ace353938389ae3ea228d0eb5020a0aaa58884aec",
|
||||
Py0 = "11799118d2e054aabd9f74c0843fecbdc1c0d56f61c61c5854c2507ae2416e48a6b2cd3bc8bf7495a4d3d8270eafe2b",
|
||||
Py1 = "823b9f8fb9f8297734a14359fa2c2a0de275e7e638197eaaaa7cff28f9cb3101bdabb570016672455f1ecae625e294",
|
||||
scalar = "9d432eb58ec68bbc09d10961451d99c7796fb2f795eca603d6feaf3e2a1634b",
|
||||
Qx0 = "12cfcb50345d43271d2a20e8208789c8ca82f2b8732fae4e7cccd87eb0883741d5e77166971c38c54170bf7635ca2f3",
|
||||
Qx1 = "17467c786368f2f6eabd78f1c24aa668eae9cd00f6045bfd86b1b3c40966f023a71927026ae5a1281b432ac980d2b6b",
|
||||
Qy0 = "6f5f1068b6e6a8fafd96894d8b0a8548acee24319d8e2d8b6e6982b1ced8970d9fe33155e74b33d6c2a7835196ca9",
|
||||
Qy1 = "dc3c7c91c712f9a2631d2b5e49497d8cdf2ea4b30859b43edd716e84cd6a8b61e63cf708d263a7845913cdfb9c7f3b"
|
||||
)
|
||||
|
||||
test(
|
||||
id = 1,
|
||||
EC = ECP_SWei_Proj[Fp2[BLS12_377]],
|
||||
Px0 = "3a3055d6a46901c1b2227a0e334ffa9f654e62a6d3608f3a672e5816f9e9b04c0e668c3e9f8c807b269422afdc7de9",
|
||||
Px1 = "25803bd55f37b254865d5fc7ac9843fb306c2eb09d34ee0c4ecb705b5e10f6911f07fd707a2e28681a421f45b1a4d",
|
||||
Py0 = "15a0594a3c9dddc535472c4827aa443774a06f77bec2d20837c6574aa5fac35a279bac756531fa75f979a7a97f297d6",
|
||||
Py1 = "15e8c3e013cbc64110f075295f39a4f85c9591a52b8e4903047b1f4b44bb5216b6339788c82fd90e82b1027756e7987",
|
||||
scalar = "a0067f5b4294fbacd24730fed25c936a08b5f1a77824149ad6c2ce476518d17",
|
||||
Qx0 = "6280dbf019f8d94acac4a39213e03f221be732849b45437edef7617460e83f972883c20791a86670a8a4c6c0125629",
|
||||
Qx1 = "ab3a185addbd6c7db907170d75bd2f1997666e93d8cedbef9e10a103110d4ea09d9a889f505d7026e5c498b0355c1",
|
||||
Qy0 = "1a6dd15b4c2bd6bd97858e5a75ac4365f8becaa885c4e3267d0ae256655cd057061d61b2b9acbd10bdf678a9c1e7f8c",
|
||||
Qy1 = "16204b5825eec903cb7798cb62ccadb9004032d72bb815958fcd2f613c77e147656449fdaa210994338978676ad6b0e"
|
||||
)
|
||||
|
||||
test(
|
||||
id = 2,
|
||||
EC = ECP_SWei_Proj[Fp2[BLS12_377]],
|
||||
Px0 = "18ebf549cd24a64badff4ec177205be121757b68589020434aba816756d2e6fa95fdb389c8639d4bf77575122e1c04e",
|
||||
Px1 = "11cd0f0976658a6f2ec113034428ef1605befdaa5642944f5c4e571b24fc166c368c30473e25ab148209be4c0b4e37",
|
||||
Py0 = "1190d818317495201732feb2cfc8f507adac6273debff46bb6aea4a3f3e3fe7d28d893c90b21a6f28d2fbc72d9fc528",
|
||||
Py1 = "df5fcb2daa302a5c64aeef96835e0a6b39f5d7bf0e70cc10401f966745a6b3fa682b7e5b45d9295e744e1dd7855fd4",
|
||||
scalar = "7a49802ba58c87c30b631b2f90a3b876c7143e09b542c9c14706bddf9bd4117",
|
||||
Qx0 = "6cdff80576d8695a646f915caba5bc6748eee1707bb0d4ebcabaf8d236b780aef6a953a07f48ef4696f02db4906c71",
|
||||
Qx1 = "6b64ca5f0d46702e7e9e7beb1993b698c9b3e9991f545241d5fd44a27dc692d5f5a4c2fe6871af0653128f960307d4",
|
||||
Qy0 = "cfad02b664495d42d1c6b598306229c67bcf76cc923abb13ae038c90e959a7611161a98d607729577d55ec18bf1e74",
|
||||
Qy1 = "e433d9dd7d47d94e9f61a5bc27a688c63efa05408d6fd40a5c4e2711a37d011dc80f5dbaaafd939a07235c770feead"
|
||||
)
|
||||
|
||||
test(
|
||||
id = 3,
|
||||
EC = ECP_SWei_Proj[Fp2[BLS12_377]],
|
||||
Px0 = "52b54c30c1dcadbcb698d0cb7fd65de1eb8f7590c7afe46e019bdc9e0ef8bc4c060339220d3615e4b1f2b12ffa6d83",
|
||||
Px1 = "dd53b483c2ab1aaa7ed22ef619b5e979237ae95476436f2c51c8b70da39a4e54a989f10f6d12ee098154911aa052f6",
|
||||
Py0 = "2a8c96662a7c76cb7d8ca6571a5b99abfd2d7343dd668425e5fa4a8c880b313b70f09a15a825fac63ae7065c0c51d",
|
||||
Py1 = "7bd93bb9d5bfdd15636a34a13385e9abbc1088ecc02c86cbc733122ccbbb657ec8d75e47102ce46d35a9612c01fc3d",
|
||||
scalar = "e0c564e69ad68343fd4ec3d4dafdf6a92f44c13fc70a9aad95d10b2c96ee747",
|
||||
Qx0 = "126f6039b7031b73a7f926bd443ace416f62e56f03864d660bbe1f1a2a994672d408f9936293f367d3fb5d8e275ad40",
|
||||
Qx1 = "1654a2c26479ace1eb68e279eceac6aa10680549b09f42d288767f6296c98ba4299fa6df37a946b88823a5deebc231c",
|
||||
Qy0 = "23026839059191f71e2abcf582234aa2ce4b4225c3d503e8fc6c119df1168192e894f33ed48bc571e5527365dd92b5",
|
||||
Qy1 = "11f8fcce53f5e3211290e6c72736eb1c9d2e159dd243f3e493c430f0411864e109af09a3b9379c4b332815e012caa80"
|
||||
)
|
||||
|
||||
test(
|
||||
id = 4,
|
||||
EC = ECP_SWei_Proj[Fp2[BLS12_377]],
|
||||
Px0 = "fb6dd822e327a60c9e55d760c4f5d26783617a06f868835c2f46e902108eaca5d60fd64c0a0c8a6dc12f9352dfef33",
|
||||
Px1 = "85e97eef3b4e42a93b8421644e9334f4543a25a36ccd41d448c385146baf3b1efbaeac49c202b04cdbf8b50f3fd962",
|
||||
Py0 = "8db78b315a524f17ac3d69333604e6bc8aa0b2f138f9adf7edb19f49c847eda6b64df9fe2576b7687e7b55cb5f0bd0",
|
||||
Py1 = "d4a0c7e5e7aaacab0e0e60c6c49624971a161de4f0daa7968f80998fb7b4761b1196964b26fefa9337fd133784e3d9",
|
||||
scalar = "1abce9e9c079c686864304d479d68087db33f9edb3f7b2e4655fe0c1da4993f",
|
||||
Qx0 = "1238814ea497bc1b65ba1c0fbfc394427b10b2d8783bf6a04e01505bc165b577cb2366c19c24c555235ff9b16e7233a",
|
||||
Qx1 = "b156205cad5d2d97d24c45fe218cd412f47248f26d579fb368af5ba2d80b8c95e2a68f4c84540a8b78b2f0a33a48a4",
|
||||
Qy0 = "12154ce4a569b39ccfe464a7559b8baf03d3fb6462e67dc0c4ae79a3c33b7756e0c558eff64f9558ebd6500086684f8",
|
||||
Qy1 = "74571a18fc091f4d77cc7d363bf7a34ab4d0dca86c160b261b383e416f68fa553d7b8b8f6317c8e340ce454daffb5f"
|
||||
)
|
||||
|
||||
test(
|
||||
id = 5,
|
||||
EC = ECP_SWei_Proj[Fp2[BLS12_377]],
|
||||
Px0 = "b56e23f2558c3c124f024952e1858067223541f51a8885d161133beb7bf8d64f22163769e604afbf7fbfb02e1fcb43",
|
||||
Px1 = "e10c4366d667b40488a9ae32daf3e2a5cc8ddf25ed407afe1a38b855f3ac4f7ea20455924e71369eed07114613b633",
|
||||
Py0 = "f42d3ab4716d10384bcdfec38cba997c2bafb8d8de32a47225a3e2d2835be1ad02a63323d3dd4145db21e3cbf5cc7a",
|
||||
Py1 = "15f48a901f3dfd3b812a455f1297e8311c4608869ea02d3d16e9aafdf610d7e72f34b02830de8cc0d0f4e909af5827d",
|
||||
scalar = "401e70df8ccaa504da72b1d649c067f3b752156dcea5b48dcb601710ab0baa3",
|
||||
Qx0 = "949099ea1ece1be6fa3f3537e8ba39587b40003fb409a4beb831bb1d9d999b7c621b9a1ced3223f710e0bd05f4a018",
|
||||
Qx1 = "cdfc0d47788bca3672126e0facc05c19a4fb24a43eee32c8e08a6e5152f6af1d7c2efa48907046f90a721ed28fcf7",
|
||||
Qy0 = "11d608a01cfe1a40806b9951f85a7bb1b6df6e4c4e2c7c50c17e8fad6ed397a430aff9b4742408367d6536fe503692a",
|
||||
Qy1 = "19c25d8a782b9d8d9f067d345fd03afe50439c82f99a06abca790f2fae944677d8095d2e276fcb7a75cfb2cbccf89d7"
|
||||
)
|
||||
|
||||
test(
|
||||
id = 6,
|
||||
EC = ECP_SWei_Proj[Fp2[BLS12_377]],
|
||||
Px0 = "137001541ab8a479362ee39f818fc38788231e5288ceea5ebe0c08d0bbca3be9519aa415bde98428df26d361b7311ea",
|
||||
Px1 = "411b1a4f0abb9fd255a7ae26ac39c1f2c88a48f82c7623b6f225aec9755206e27084b23cbc98f31399405a6599dc54",
|
||||
Py0 = "833ef097986116cab669c4fddff9b831535d100644f732fb0da0a2dce17d69beaeed67230dd66392e840679afbae1e",
|
||||
Py1 = "f63e809bd55617c31570d686dbc9b94b3cb96c154f9983181cb913a0671c0c16143332087e44a0283ebf01eea6d73b",
|
||||
scalar = "10697220c755c800861d377c55b22ae48c25dc144e1fa7a1e5bbdf82993aa33c",
|
||||
Qx0 = "10fc7772bb2bc946e3ff659c01a0452ebf5dd1472bd1aa71e198597fd05361bf0d173817f91fcb8a1b96b46c35d2675",
|
||||
Qx1 = "d7b00ddf32f9d8e549c05dd605931ef737d8a4bc62795f73044482957b8093b1cdad0f55c7d4edf6f59594a13f9b67",
|
||||
Qy0 = "148756a592dec0bf2fda41ac219c4a891ef427665ba1cf47b58bed4996cf8937fb07929c041f10e8a55b2238944fd30",
|
||||
Qy1 = "e4a240f5c32dab76575ace4d035cbd1e2b87d7585fee7bfde9c88e918a7ad2cd8eb4982acfacfac58c33c8a54ddac7"
|
||||
)
|
||||
|
||||
test(
|
||||
id = 7,
|
||||
EC = ECP_SWei_Proj[Fp2[BLS12_377]],
|
||||
Px0 = "1580ffccc21b057dfe4831f950cbb8f1436f999df657404ecec20225a929a5d56920e8662abc426de23402643087308",
|
||||
Px1 = "8bf8ff20713a9054aa1cce9635eb3d6ac8371fc052b747a8414595708ff9462d64a0a11ff2c1c5121f4ecc5f22df5e",
|
||||
Py0 = "e7a5f7df0cec03de25da415fdda485ecc1443b95352e47b23e5506b820c9bc9ea9c2d9e99dd48e1f74e1befe58ce80",
|
||||
Py1 = "12b5638a0f7e508016fddd2dae2d84736efa59c53f2a5d979b9a29fc7c9c406407bb5f5788bcea3529ac1a763c72e8b",
|
||||
scalar = "bd936c7066316d3f86b077419a0c0fb9867d4612208241fc004b548f1f54fad",
|
||||
Qx0 = "69e3411725062858ca7e5fc4d648037baf10e34ba6830ff0090e7188932112b9458c4f4b82f2f0f06c6501fcdaa86c",
|
||||
Qx1 = "127b75c55c90a872c1d749d05609d2499065ac3c9eb96bd6a9816bd7a2315795ddf40944baef1907676395df306860e",
|
||||
Qy0 = "13fd0e314159909d3091e382017b35ec21ee2004e9082cd24338817046ed2202ffed52e7714cf436b953f95f49f9856",
|
||||
Qy1 = "5a8ccdf4a1600fafcc86c646d83199c41cbfebf7a1fa2b1cb9c408352be46a33d0f411b175348f42382d170e17ec2b"
|
||||
)
|
||||
|
||||
test(
|
||||
id = 8,
|
||||
EC = ECP_SWei_Proj[Fp2[BLS12_377]],
|
||||
Px0 = "10898a074b6931cc4ada152b64dd1c6b2bb47912e6502b9e88f638e489c144f8aa9b9f915bb428e082ec84676607f40",
|
||||
Px1 = "18484823648fe1699468e2d265cd2f2e381a0e67f35f8d192259e2a14573692b4e1fbdeed639e9b6eb0731be820b166",
|
||||
Py0 = "1840bc6fcb224efe00e32f827fa4f9694cd4186493089c66a936e912b50346b75542b6edbe51ba95c88d3b0fcac34ed",
|
||||
Py1 = "18cb4201510933776a28ff1c44d356ceab065880f5242d8cc7cdf86874568df679b20f34b6a216d0047d2e1c1a16b85",
|
||||
scalar = "bdb46c0eece9c8bac8a382def90b522b1d5197f09cf9f7cd98b710845ddb7b4",
|
||||
Qx0 = "102779766b0068b176e118348e4163e89e3171e6eaad0e47f00cad626f8db025ec3ea990363813882c3dcfcbb642240",
|
||||
Qx1 = "131eb563d2b0145eee52ee7586acd0254f103f02a20de3bf0202d1cffec2c3628501ec20f9f2ae5f461b59604afc04c",
|
||||
Qy0 = "1463527db149d2c76f30c082786edb3fe1a9c7f4e04f9496e7adb40aab89340d9792f7d75cf3f5f81b0a28f09625175",
|
||||
Qy1 = "f3190261db082c207ccd88a3ba762d2c0ba330548444afca03c8535ff11c7c90659a867dda3712a83f02ad184eb24a"
|
||||
)
|
||||
|
||||
test(
|
||||
id = 9,
|
||||
EC = ECP_SWei_Proj[Fp2[BLS12_377]],
|
||||
Px0 = "13c3a392307124afb5f219ba0f8062fa9b75654d3fff12bc924592d284af550f039b6ac58880d2c6fea146b3982f03c",
|
||||
Px1 = "188169bc937fcc20cc9c289adef30580188f64ecb126faadb5b888f31b813727ff7046d1a19b81abeea6609b8b208c6",
|
||||
Py0 = "2f9bde8fdd43c5f4de30f335a480dca3bf0858464d8368984406f10ddc1ecabb15fcfd11cebd4fef426e7ca9411221",
|
||||
Py1 = "25505653e199c354f2da8a13ed9de47f9b51a06ad2fa8826c4b8a9c61c6e75268807d7053e06bfc2899d1a3d6deeb4",
|
||||
scalar = "91c6249ee16ef94d0b905575982419a7cf31d125775e7ede5c0ab4f86defd33",
|
||||
Qx0 = "1967547943b197311a7d6723dcc37c3e649f173cec1c88ffca27df86518b3392ae0ee13dbca375c928b94d129226852",
|
||||
Qx1 = "18b793916195f2d7d2a47c3f8243e5bdc239e78a3eb26971dac01b5f4ce14081a06529ab66cda8e8d5537808223fd00",
|
||||
Qy0 = "a784902a14ad39adcfdc52bc30d8b8711b93dd869af2375a1f408ba8610b5c558bbebfe9f8f9875954780f4b4262c0",
|
||||
Qy1 = "fb1c766957c89e4d67747549f650070983e6c19d0208b7e65478e4dd4f72fc54fca450d96329182e3f00d16ae0f3a"
|
||||
)
|
||||
@ -32,3 +32,9 @@ run_EC_addition_tests(
|
||||
Iters = Iters,
|
||||
moduleName = "test_ec_weierstrass_projective_g1_add_double_" & $BLS12_381
|
||||
)
|
||||
|
||||
run_EC_addition_tests(
|
||||
ec = ECP_SWei_Proj[Fp[BLS12_377]],
|
||||
Iters = Iters,
|
||||
moduleName = "test_ec_weierstrass_projective_g1_add_double_" & $BLS12_377
|
||||
)
|
||||
|
||||
@ -28,3 +28,9 @@ run_EC_mixed_add_impl(
|
||||
Iters = Iters,
|
||||
moduleName = "test_ec_weierstrass_projective_mixed_add_" & $BLS12_381
|
||||
)
|
||||
|
||||
run_EC_mixed_add_impl(
|
||||
ec = ECP_SWei_Proj[Fp[BLS12_377]],
|
||||
Iters = Iters,
|
||||
moduleName = "test_ec_weierstrass_projective_mixed_add_" & $BLS12_377
|
||||
)
|
||||
|
||||
@ -34,3 +34,9 @@ run_EC_mul_distributive_tests(
|
||||
ItersMul = ItersMul,
|
||||
moduleName = "test_ec_weierstrass_projective_g1_mul_distributive_" & $BLS12_381
|
||||
)
|
||||
|
||||
run_EC_mul_distributive_tests(
|
||||
ec = ECP_SWei_Proj[Fp[BLS12_377]],
|
||||
ItersMul = ItersMul,
|
||||
moduleName = "test_ec_weierstrass_projective_g1_mul_distributive_" & $BLS12_377
|
||||
)
|
||||
|
||||
@ -78,3 +78,9 @@ run_EC_mul_sanity_tests(
|
||||
ItersMul = ItersMul,
|
||||
moduleName = "test_ec_weierstrass_projective_g1_mul_sanity_" & $BLS12_381
|
||||
)
|
||||
|
||||
run_EC_mul_sanity_tests(
|
||||
ec = ECP_SWei_Proj[Fp[BLS12_377]],
|
||||
ItersMul = ItersMul,
|
||||
moduleName = "test_ec_weierstrass_projective_g1_mul_sanity_" & $BLS12_377
|
||||
)
|
||||
|
||||
@ -34,3 +34,9 @@ run_EC_mul_vs_ref_impl(
|
||||
ItersMul = ItersMul,
|
||||
moduleName = "test_ec_weierstrass_projective_g1_mul_vs_ref_" & $BLS12_381
|
||||
)
|
||||
|
||||
run_EC_mul_vs_ref_impl(
|
||||
ec = ECP_SWei_Proj[Fp[BLS12_377]],
|
||||
ItersMul = ItersMul,
|
||||
moduleName = "test_ec_weierstrass_projective_g1_mul_vs_ref_" & $BLS12_377
|
||||
)
|
||||
|
||||
29
tests/t_ec_wstrass_prj_g2_add_double_bls12_377.nim
Normal file
29
tests/t_ec_wstrass_prj_g2_add_double_bls12_377.nim
Normal file
@ -0,0 +1,29 @@
|
||||
# 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/[unittest, times],
|
||||
# Internals
|
||||
../constantine/config/[common, curves],
|
||||
../constantine/arithmetic,
|
||||
../constantine/towers,
|
||||
../constantine/io/io_bigints,
|
||||
../constantine/elliptic/[ec_weierstrass_affine, ec_weierstrass_projective],
|
||||
# Test utilities
|
||||
../helpers/prng_unsafe,
|
||||
./t_ec_template
|
||||
|
||||
const
|
||||
Iters = 8
|
||||
|
||||
run_EC_addition_tests(
|
||||
ec = ECP_SWei_Proj[Fp2[BLS12_377]],
|
||||
Iters = Iters,
|
||||
moduleName = "test_ec_weierstrass_projective_g2_add_double_" & $BLS12_377
|
||||
)
|
||||
24
tests/t_ec_wstrass_prj_g2_mixed_add_bls12_377.nim
Normal file
24
tests/t_ec_wstrass_prj_g2_mixed_add_bls12_377.nim
Normal file
@ -0,0 +1,24 @@
|
||||
# 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/elliptic/ec_weierstrass_projective,
|
||||
../constantine/towers,
|
||||
# Test utilities
|
||||
./t_ec_template
|
||||
|
||||
const
|
||||
Iters = 12
|
||||
|
||||
run_EC_mixed_add_impl(
|
||||
ec = ECP_SWei_Proj[Fp2[BLS12_377]],
|
||||
Iters = Iters,
|
||||
moduleName = "test_ec_weierstrass_projective_mixed_add_" & $BLS12_377
|
||||
)
|
||||
31
tests/t_ec_wstrass_prj_g2_mul_distri_bls12_377.nim
Normal file
31
tests/t_ec_wstrass_prj_g2_mul_distri_bls12_377.nim
Normal file
@ -0,0 +1,31 @@
|
||||
# Constantine
|
||||
# Copyright (c) 2018-2019 Status Research & Development GmbH
|
||||
# Copyright (c) 2020-Present Mamy André-Ratsimbazafy
|
||||
# Licensed and distributed under either of
|
||||
# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT).
|
||||
# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0).
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import
|
||||
# Standard library
|
||||
std/[unittest, times],
|
||||
# Internals
|
||||
../constantine/config/[common, curves],
|
||||
../constantine/arithmetic,
|
||||
../constantine/towers,
|
||||
../constantine/io/io_bigints,
|
||||
../constantine/elliptic/[ec_weierstrass_affine, ec_weierstrass_projective, ec_scalar_mul],
|
||||
# Test utilities
|
||||
../helpers/prng_unsafe,
|
||||
./support/ec_reference_scalar_mult,
|
||||
./t_ec_template
|
||||
|
||||
const
|
||||
Iters = 12
|
||||
ItersMul = Iters div 4
|
||||
|
||||
run_EC_mul_distributive_tests(
|
||||
ec = ECP_SWei_Proj[Fp2[BLS12_377]],
|
||||
ItersMul = ItersMul,
|
||||
moduleName = "test_ec_weierstrass_projective_g2_mul_distributive_" & $BLS12_377
|
||||
)
|
||||
62
tests/t_ec_wstrass_prj_g2_mul_sanity_bls12_377.nim
Normal file
62
tests/t_ec_wstrass_prj_g2_mul_sanity_bls12_377.nim
Normal file
@ -0,0 +1,62 @@
|
||||
# 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/[unittest, times],
|
||||
# Internals
|
||||
../constantine/config/[common, curves],
|
||||
../constantine/arithmetic,
|
||||
../constantine/towers,
|
||||
../constantine/io/io_bigints,
|
||||
../constantine/elliptic/[ec_weierstrass_affine, ec_weierstrass_projective, ec_scalar_mul],
|
||||
# Test utilities
|
||||
../helpers/prng_unsafe,
|
||||
./support/ec_reference_scalar_mult,
|
||||
./t_ec_template
|
||||
|
||||
const
|
||||
Iters = 12
|
||||
ItersMul = Iters div 4
|
||||
|
||||
run_EC_mul_sanity_tests(
|
||||
ec = ECP_SWei_Proj[Fp2[BLS12_377]],
|
||||
ItersMul = ItersMul,
|
||||
moduleName = "test_ec_weierstrass_projective_g2_mul_sanity_" & $BLS12_377
|
||||
)
|
||||
|
||||
# TODO: the order on E'(Fp2) for BLS curves is ??? with r the order on E(Fp)
|
||||
#
|
||||
# test "EC mul [Order]P == Inf":
|
||||
# var rng: RngState
|
||||
# let seed = uint32(getTime().toUnix() and (1'i64 shl 32 - 1)) # unixTime mod 2^32
|
||||
# rng.seed(seed)
|
||||
# echo "test_ec_weierstrass_projective_g1_mul_sanity_extra_curve_order_mul_sanity xoshiro512** seed: ", seed
|
||||
#
|
||||
# proc test(EC: typedesc, bits: static int, randZ: static bool) =
|
||||
# for _ in 0 ..< ItersMul:
|
||||
# when randZ:
|
||||
# let a = rng.random_unsafe_with_randZ(EC)
|
||||
# else:
|
||||
# let a = rng.random_unsafe(EC)
|
||||
#
|
||||
# let exponent = F.C.getCurveOrder()
|
||||
#
|
||||
# var
|
||||
# impl = a
|
||||
# reference = a
|
||||
#
|
||||
# impl.scalarMulGeneric(exponent)
|
||||
# reference.unsafe_ECmul_double_add(exponent)
|
||||
#
|
||||
# check:
|
||||
# bool(impl.isInf())
|
||||
# bool(reference.isInf())
|
||||
#
|
||||
# test(ECP_SWei_Proj[Fp2[BLS12_377]], bits = BLS12_377.getCurveOrderBitwidth(), randZ = false)
|
||||
# test(ECP_SWei_Proj[Fp2[BLS12_377]], bits = BLS12_377.getCurveOrderBitwidth(), randZ = true)
|
||||
31
tests/t_ec_wstrass_prj_g2_mul_vs_ref_bls12_377.nim
Normal file
31
tests/t_ec_wstrass_prj_g2_mul_vs_ref_bls12_377.nim
Normal file
@ -0,0 +1,31 @@
|
||||
# Constantine
|
||||
# Copyright (c) 2018-2019 Status Research & Development GmbH
|
||||
# Copyright (c) 2020-Present Mamy André-Ratsimbazafy
|
||||
# Licensed and distributed under either of
|
||||
# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT).
|
||||
# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0).
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import
|
||||
# Standard library
|
||||
std/[unittest, times],
|
||||
# Internals
|
||||
../constantine/config/[common, curves],
|
||||
../constantine/arithmetic,
|
||||
../constantine/towers,
|
||||
../constantine/io/io_bigints,
|
||||
../constantine/elliptic/[ec_weierstrass_affine, ec_weierstrass_projective, ec_scalar_mul],
|
||||
# Test utilities
|
||||
../helpers/prng_unsafe,
|
||||
./support/ec_reference_scalar_mult,
|
||||
./t_ec_template
|
||||
|
||||
const
|
||||
Iters = 12
|
||||
ItersMul = Iters div 4
|
||||
|
||||
run_EC_mul_vs_ref_impl(
|
||||
ec = ECP_SWei_Proj[Fp2[BLS12_377]],
|
||||
ItersMul = ItersMul,
|
||||
moduleName = "test_ec_weierstrass_projective_g2_mul_vs_ref_" & $BLS12_377
|
||||
)
|
||||
@ -27,8 +27,8 @@ echo "test_finite_fields_sqrt xoshiro512** seed: ", seed
|
||||
|
||||
static: doAssert defined(testingCurves), "This modules requires the -d:testingCurves compile option"
|
||||
|
||||
proc exhaustiveCheck_p3mod4(C: static Curve, modulus: static int) =
|
||||
test "Exhaustive square root check for p ≡ 3 (mod 4) on " & $Curve(C):
|
||||
proc exhaustiveCheck(C: static Curve, modulus: static int) =
|
||||
test "Exhaustive square root check for " & $Curve(C):
|
||||
var squares_to_roots: Table[uint16, set[uint16]]
|
||||
|
||||
# Create all squares
|
||||
@ -102,9 +102,9 @@ template testImpl(a: untyped): untyped {.dirty.} =
|
||||
bool(r == s)
|
||||
bool(r == a or r == na)
|
||||
|
||||
proc randomSqrtCheck_p3mod4(C: static Curve) =
|
||||
test "Random square root check for p ≡ 3 (mod 4) on " & $Curve(C):
|
||||
for _ in 0 ..< Iters:
|
||||
proc randomSqrtCheck(C: static Curve) =
|
||||
test "Random square root check for " & $Curve(C):
|
||||
for _ in 0 ..< 1: # Iters:
|
||||
let a = rng.random_unsafe(Fp[C])
|
||||
testImpl(a)
|
||||
|
||||
@ -118,20 +118,21 @@ proc randomSqrtCheck_p3mod4(C: static Curve) =
|
||||
|
||||
proc main() =
|
||||
suite "Modular square root" & " [" & $WordBitwidth & "-bit mode]":
|
||||
exhaustiveCheck_p3mod4 Fake103, 103
|
||||
exhaustiveCheck_p3mod4 Fake10007, 10007
|
||||
exhaustiveCheck_p3mod4 Fake65519, 65519
|
||||
randomSqrtCheck_p3mod4 Mersenne61
|
||||
randomSqrtCheck_p3mod4 Mersenne127
|
||||
randomSqrtCheck_p3mod4 BN254_Nogami
|
||||
randomSqrtCheck_p3mod4 BN254_Snarks
|
||||
randomSqrtCheck_p3mod4 P256
|
||||
randomSqrtCheck_p3mod4 Secp256k1
|
||||
randomSqrtCheck_p3mod4 BLS12_381
|
||||
randomSqrtCheck_p3mod4 BN446
|
||||
randomSqrtCheck_p3mod4 FKM12_447
|
||||
randomSqrtCheck_p3mod4 BLS12_461
|
||||
randomSqrtCheck_p3mod4 BN462
|
||||
exhaustiveCheck Fake103, 103
|
||||
exhaustiveCheck Fake10007, 10007
|
||||
exhaustiveCheck Fake65519, 65519
|
||||
randomSqrtCheck Mersenne61
|
||||
randomSqrtCheck Mersenne127
|
||||
randomSqrtCheck BN254_Nogami
|
||||
randomSqrtCheck BN254_Snarks
|
||||
randomSqrtCheck P256
|
||||
randomSqrtCheck Secp256k1
|
||||
randomSqrtCheck BLS12_377 # p ≢ 3 (mod 4)
|
||||
randomSqrtCheck BLS12_381
|
||||
randomSqrtCheck BN446
|
||||
randomSqrtCheck FKM12_447
|
||||
randomSqrtCheck BLS12_461
|
||||
randomSqrtCheck BN462
|
||||
|
||||
suite "Modular square root - 32-bit bugs highlighted by property-based testing " & " [" & $WordBitwidth & "-bit mode]":
|
||||
test "FKM12_447 - #30":
|
||||
|
||||
@ -16,7 +16,7 @@ import
|
||||
const TestCurves = [
|
||||
BN254_Nogami,
|
||||
BN254_Snarks,
|
||||
# BLS12_377,
|
||||
BLS12_377,
|
||||
BLS12_381,
|
||||
# BN446
|
||||
# FKM12_447
|
||||
|
||||
@ -21,7 +21,9 @@ import
|
||||
const
|
||||
Iters = 8
|
||||
TestCurves = [
|
||||
BN254_Nogami,
|
||||
BN254_Snarks,
|
||||
BLS12_377,
|
||||
BLS12_381
|
||||
]
|
||||
|
||||
@ -45,7 +47,7 @@ func random_elem(rng: var RngState, F: typedesc, gen: RandomGen): F {.inline, no
|
||||
else:
|
||||
result = rng.random_long01Seq(F)
|
||||
|
||||
proc randomSqrtCheck_p3mod4(C: static Curve, gen: RandomGen) =
|
||||
proc randomSqrtCheck(C: static Curve, gen: RandomGen) =
|
||||
for _ in 0 ..< Iters:
|
||||
let a = rng.random_elem(Fp2[C], gen)
|
||||
var na{.noInit.}: Fp2[C]
|
||||
@ -70,10 +72,10 @@ proc randomSqrtCheck_p3mod4(C: static Curve, gen: RandomGen) =
|
||||
proc main() =
|
||||
suite "Modular square root" & " [" & $WordBitwidth & "-bit mode]":
|
||||
staticFor(curve, TestCurves):
|
||||
test "[𝔽p2] Random square root check for p ≡ 3 (mod 4) on " & $curve:
|
||||
randomSqrtCheck_p3mod4(curve, gen = Uniform)
|
||||
randomSqrtCheck_p3mod4(curve, gen = HighHammingWeight)
|
||||
randomSqrtCheck_p3mod4(curve, gen = Long01Sequence)
|
||||
test "[𝔽p2] Random square root check for " & $curve:
|
||||
randomSqrtCheck(curve, gen = Uniform)
|
||||
randomSqrtCheck(curve, gen = HighHammingWeight)
|
||||
randomSqrtCheck(curve, gen = Long01Sequence)
|
||||
|
||||
suite "Modular square root - 32-bit bugs highlighted by property-based testing " & " [" & $WordBitwidth & "-bit mode]":
|
||||
test "sqrt_if_square invalid square BLS12_381 - #64":
|
||||
|
||||
16
tests/t_pairing_bls12_377_optate.nim
Normal file
16
tests/t_pairing_bls12_377_optate.nim
Normal file
@ -0,0 +1,16 @@
|
||||
# Constantine
|
||||
# Copyright (c) 2018-2019 Status Research & Development GmbH
|
||||
# Copyright (c) 2020-Present Mamy André-Ratsimbazafy
|
||||
# Licensed and distributed under either of
|
||||
# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT).
|
||||
# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0).
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import
|
||||
../constantine/config/common,
|
||||
../constantine/config/curves,
|
||||
../constantine/pairing/pairing_bls12,
|
||||
# Test utilities
|
||||
./t_pairing_template
|
||||
|
||||
runPairingTests(4, BLS12_377, pairing_bls12)
|
||||
@ -23,9 +23,9 @@ import
|
||||
const
|
||||
Iters = 4
|
||||
TestCurves = [
|
||||
# BN254_Nogami,
|
||||
# BN254_Snarks,
|
||||
# BLS12_377,
|
||||
BN254_Nogami,
|
||||
BN254_Snarks,
|
||||
BLS12_377,
|
||||
BLS12_381
|
||||
]
|
||||
|
||||
|
||||
@ -27,6 +27,7 @@ const
|
||||
TestCurves = [
|
||||
BN254_Nogami,
|
||||
BN254_Snarks,
|
||||
BLS12_377,
|
||||
BLS12_381
|
||||
]
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user