Endomorphism G2 (#79)

* Clear cofactor in BN254 G2 testgen and frobenius

* Implement G2 endomorphism acceleration in Sage

* Somewhat working accelerated scalar mul G2 (2.2x) faster
- OK for BN254_Snarks
- Some test failing for BLS12-381

* Fix negative miniscalars by adding an extra bit of encoding

* Cleanup accel params

* Small recoding optimizations
This commit is contained in:
Mamy Ratsimbazafy 2020-09-03 23:10:48 +02:00 committed by GitHub
parent 92939d40fb
commit 85d365359d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 1672 additions and 549 deletions

View File

@ -53,13 +53,13 @@ proc main() =
separator()
scalarMulUnsafeDoubleAddBench(ECP_SWei_Proj[Fp[curve]], MulIters)
separator()
scalarMulGenericBench(ECP_SWei_Proj[Fp[curve]], scratchSpaceSize = 1 shl 2, MulIters)
scalarMulGenericBench(ECP_SWei_Proj[Fp[curve]], window = 2, MulIters)
separator()
scalarMulGenericBench(ECP_SWei_Proj[Fp[curve]], scratchSpaceSize = 1 shl 3, MulIters)
scalarMulGenericBench(ECP_SWei_Proj[Fp[curve]], window = 3, MulIters)
separator()
scalarMulGenericBench(ECP_SWei_Proj[Fp[curve]], scratchSpaceSize = 1 shl 4, MulIters)
scalarMulGenericBench(ECP_SWei_Proj[Fp[curve]], window = 4, MulIters)
separator()
scalarMulGenericBench(ECP_SWei_Proj[Fp[curve]], scratchSpaceSize = 1 shl 5, MulIters)
scalarMulGenericBench(ECP_SWei_Proj[Fp[curve]], window = 5, MulIters)
separator()
scalarMulEndo(ECP_SWei_Proj[Fp[curve]], MulIters)
separator()

View File

@ -54,14 +54,16 @@ proc main() =
separator()
scalarMulUnsafeDoubleAddBench(ECP_SWei_Proj[Fp2[curve]], MulIters)
separator()
scalarMulGenericBench(ECP_SWei_Proj[Fp2[curve]], scratchSpaceSize = 1 shl 2, MulIters)
scalarMulGenericBench(ECP_SWei_Proj[Fp2[curve]], window = 2, MulIters)
separator()
scalarMulGenericBench(ECP_SWei_Proj[Fp2[curve]], scratchSpaceSize = 1 shl 3, MulIters)
scalarMulGenericBench(ECP_SWei_Proj[Fp2[curve]], window = 3, MulIters)
separator()
scalarMulGenericBench(ECP_SWei_Proj[Fp2[curve]], scratchSpaceSize = 1 shl 4, MulIters)
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()
# scalarMulEndo(ECP_SWei_Proj[Fp2[curve]], MulIters)
# separator()
separator()
main()

View File

@ -92,9 +92,12 @@ proc notes*() =
echo "Notes:"
echo " - Compilers:"
echo " Compilers are severely limited on multiprecision arithmetic."
echo " Inline Assembly is used by default (nimble bench_fp)."
echo " Bench without assembly can use \"nimble bench_fp_gcc\" or \"nimble bench_fp_clang\"."
echo " Constantine compile-time assembler is used by default (nimble bench_fp)."
echo " GCC is significantly slower than Clang on multiprecision arithmetic due to catastrophic handling of carries."
echo " GCC also seems to have issues with large temporaries and register spilling."
echo " This is somewhat alleviated by Constantine compile-time assembler."
echo " Bench on specific compiler with assembler: \"nimble bench_ec_g1_gcc\" or \"nimble bench_ec_g1_clang\"."
echo " Bench on specific compiler with assembler: \"nimble bench_ec_g1_gcc_noasm\" or \"nimble bench_ec_g1_clang_noasm\"."
echo " - The simplest operations might be optimized away by the compiler."
echo " - Fast Squaring and Fast Multiplication are possible if there are spare bits in the prime representation (i.e. the prime uses 254 bits out of 256 bits)"
@ -139,7 +142,7 @@ proc doublingBench*(T: typedesc, iters: int) =
bench("EC Double " & G1_or_G2, T, iters):
r.double(P)
proc scalarMulGenericBench*(T: typedesc, scratchSpaceSize: static int, iters: int) =
proc scalarMulGenericBench*(T: typedesc, window: static int, iters: int) =
const bits = T.F.C.getCurveOrderBitwidth()
const G1_or_G2 = when T.F is Fp: "G1" else: "G2"
@ -147,14 +150,10 @@ proc scalarMulGenericBench*(T: typedesc, scratchSpaceSize: static int, iters: in
let P = rng.random_unsafe(T) # TODO: clear cofactor
let exponent = rng.random_unsafe(BigInt[bits])
var exponentCanonical{.noInit.}: array[(bits+7) div 8, byte]
exponentCanonical.exportRawUint(exponent, bigEndian)
var scratchSpace{.noInit.}: array[scratchSpaceSize, T]
bench("EC ScalarMul Generic " & G1_or_G2 & " (scratchsize = " & $scratchSpaceSize & ')', T, iters):
bench("EC ScalarMul Generic " & G1_or_G2 & " (window = " & $window & ", scratchsize = " & $(1 shl window) & ')', T, iters):
r = P
r.scalarMulGeneric(exponentCanonical, scratchSpace)
r.scalarMulGeneric(exponent, window)
proc scalarMulEndo*(T: typedesc, iters: int) =
const bits = T.F.C.getCurveOrderBitwidth()
@ -167,10 +166,7 @@ proc scalarMulEndo*(T: typedesc, iters: int) =
bench("EC ScalarMul " & G1_or_G2 & " (endomorphism accelerated)", T, iters):
r = P
when T.F is Fp:
r.scalarMulGLV(exponent)
else:
{.error: "Not implemented".}
r.scalarMulEndo(exponent)
proc scalarMulEndoWindow*(T: typedesc, iters: int) =
const bits = T.F.C.getCurveOrderBitwidth()
@ -196,9 +192,7 @@ proc scalarMulUnsafeDoubleAddBench*(T: typedesc, iters: int) =
let P = rng.random_unsafe(T) # TODO: clear cofactor
let exponent = rng.random_unsafe(BigInt[bits])
var exponentCanonical{.noInit.}: array[(bits+7) div 8, byte]
exponentCanonical.exportRawUint(exponent, bigEndian)
bench("EC ScalarMul " & G1_or_G2 & " (unsafe reference DoubleAdd)", T, iters):
r = P
r.unsafe_ECmul_double_add(exponentCanonical)
r.unsafe_ECmul_double_add(exponent)

View File

@ -89,9 +89,12 @@ proc notes*() =
echo "Notes:"
echo " - Compilers:"
echo " Compilers are severely limited on multiprecision arithmetic."
echo " Inline Assembly is used by default (nimble bench_fp)."
echo " Bench without assembly can use \"nimble bench_fp_gcc\" or \"nimble bench_fp_clang\"."
echo " Constantine compile-time assembler is used by default (nimble bench_fp)."
echo " GCC is significantly slower than Clang on multiprecision arithmetic due to catastrophic handling of carries."
echo " GCC also seems to have issues with large temporaries and register spilling."
echo " This is somewhat alleviated by Constantine compile-time assembler."
echo " Bench on specific compiler with assembler: \"nimble bench_fp_gcc\" or \"nimble bench_fp_clang\"."
echo " Bench on specific compiler with assembler: \"nimble bench_fp_gcc_noasm\" or \"nimble bench_fp_clang_noasm\"."
echo " - The simplest operations might be optimized away by the compiler."
echo " - Fast Squaring and Fast Multiplication are possible if there are spare bits in the prime representation (i.e. the prime uses 254 bits out of 256 bits)"

View File

@ -95,10 +95,12 @@ func cswap*(a, b: var BigInt, ctl: CTBool) =
func copyTruncatedFrom*[dBits, sBits: static int](dst: var BigInt[dBits], src: BigInt[sBits]) =
## Copy `src` into `dst`
## if `dst` is not big enough, only the low words are copied
## if `src` is smaller than `dst` the higher words of `dst` will NOT be overwritten
## if `src` is smaller than `dst` the higher words of `dst` will be overwritten
for wordIdx in 0 ..< min(dst.limbs.len, src.limbs.len):
dst.limbs[wordIdx] = src.limbs[wordIdx]
for wordIdx in min(dst.limbs.len, src.limbs.len) ..< dst.limbs.len:
dst.limbs[wordIdx] = SecretWord(0)
# Comparison
# ------------------------------------------------------------
@ -128,6 +130,23 @@ func isOdd*(a: BigInt): SecretBool =
## Returns true if a is odd
a.limbs.isOdd
func isMsbSet*(a: BigInt): SecretBool =
## Returns true if MSB is set
## i.e. if a BigInt is interpreted
## as signed AND the full bitwidth
## is not used by construction
## 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)
func eq*(a: BigInt, n: SecretWord): SecretBool =
## Returns true if ``a`` is equal
## to the specified small word
a.limbs.eq n
# Arithmetic
# ------------------------------------------------------------

View File

@ -131,12 +131,17 @@ func isZero*(a: Limbs): SecretBool =
accum = accum or a[i]
result = accum.isZero()
func isOne*(a: Limbs): SecretBool =
## Returns true if ``a`` is equal to one
result = a[0] == SecretWord(1)
func eq*(a: Limbs, n: SecretWord): SecretBool =
## Returns true if ``a`` is equal
## to the specified small word
result = a[0] == n
for i in 1 ..< a.len:
result = result and a[i].isZero()
func isOne*(a: Limbs): SecretBool =
## Returns true if ``a`` is equal to one
a.eq(SecretWord(1))
func isOdd*(a: Limbs): SecretBool =
## Returns true if a is odd
SecretBool(a[0] and SecretWord(1))

View File

@ -15,6 +15,7 @@ import
../arithmetic,
../io/io_bigints,
../towers,
../isogeny/frobenius,
./ec_weierstrass_affine,
./ec_weierstrass_projective,
./ec_endomorphism_params
@ -192,7 +193,7 @@ func secretLookup[T](dst: var T, table: openArray[T], index: SecretWord) =
let selector = SecretWord(i) == index
dst.ccopy(table[i], selector)
func scalarMulGLV*[scalBits](
func scalarMulEndo*[scalBits](
P: var ECP_SWei_Proj,
scalar: BigInt[scalBits]
) =
@ -201,35 +202,51 @@ func scalarMulGLV*[scalBits](
## P <- [k] P
##
## This is a scalar multiplication accelerated by an endomorphism
## via the GLV (Gallant-lambert-Vanstone) decomposition.
## - via the GLV (Gallant-lambert-Vanstone) decomposition on G1
## - via the GLS (Galbraith-Lin-Scott) decomposition on G2
##
## Requires:
## - Cofactor to be cleared
## - 0 <= scalar < curve order
const C = P.F.C # curve
static: doAssert: scalBits == C.getCurveOrderBitwidth()
static: doAssert scalBits <= C.getCurveOrderBitwidth(), "Do not use endomorphism to multiply beyond the curve order"
when P.F is Fp:
const M = 2
# 1. Compute endomorphisms
var endomorphisms {.noInit.}: array[M-1, typeof(P)]
endomorphisms[0] = P
endomorphisms[0].x *= C.getCubicRootOfUnity_mod_p()
# 1. Compute endomorphisms
var endomorphisms {.noInit.}: array[M-1, typeof(P)]
endomorphisms[0] = P
endomorphisms[0].x *= C.getCubicRootOfUnity_mod_p()
elif P.F is Fp2:
const M = 4
# 1. Compute endomorphisms
var endomorphisms {.noInit.}: array[M-1, typeof(P)]
endomorphisms[0].frobenius_psi(P)
endomorphisms[1].frobenius_psi2(P)
endomorphisms[2].frobenius_psi(endomorphisms[1])
else:
{.error: "Unconfigured".}
# 2. Decompose scalar into mini-scalars
const L = (C.getCurveOrderBitwidth() + M - 1) div M + 1
const L = (scalBits + M - 1) div M + 1 + 1 # A "+1" to handle negative
var miniScalars {.noInit.}: array[M, BigInt[L]]
when C == BN254_Snarks:
scalar.decomposeScalar_BN254_Snarks_G1(
miniScalars
)
elif C == BLS12_381:
scalar.decomposeScalar_BLS12_381_G1(
miniScalars
)
else:
{.error: "Unsupported curve for GLV acceleration".}
miniScalars.decomposeEndo(scalar, P.F)
# 3. TODO: handle negative mini-scalars
# Either negate the associated base and the scalar (in the `endomorphisms` array)
# Or use Algorithm 3 from Faz et al which can encode the sign
# in the GLV representation at the low low price of 1 bit
# 3. Handle negative mini-scalars
# A scalar decomposition might lead to negative miniscalar.
# For proper handling it requires either:
# 1. Negating it and then negating the corresponding curve point P
# 2. Adding an extra bit to the recoding, which will do the right thing™
#
# For implementation solution 1 is faster:
# - Double + Add is about 5000~8000 cycles on 6 64-bits limbs (BLS12-381)
# - Conditional negate is about 10 cycles per Fp, on G2 projective we have 3 (coords) * 2 (Fp2) * 10 (cycles) ~= 60 cycles
# We need to test the mini scalar, which is 65 bits so 2 Fp so about 2 cycles
# and negate it as well.
#
# However solution 1 seems to cause issues (TODO)
# with some of the BLS12-381 test cases (6 and 9)
# - 0x5668a2332db27199dcfb7cbdfca6317c2ff128db26d7df68483e0a095ec8e88f
# - 0x644dc62869683f0c93f38eaef2ba6912569dc91ec2806e46b4a3dd6a4421dad1
# 4. Precompute lookup table
var lut {.noInit.}: array[1 shl (M-1), ECP_SWei_Proj]
@ -358,8 +375,8 @@ func w2TableIndex(glv: GLV_SAC, bit2: int, isNeg: var SecretBool): SecretWord {.
func computeRecodedLength(bitWidth, window: int): int =
# Strangely in the paper this doesn't depend
# "m", the GLV decomposition dimension.
# lw = ⌈log2 r/w⌉+1
let lw = ((bitWidth + window - 1) div window + 1)
# lw = ⌈log2 r/w⌉+1+1 (a "+1" to handle negative mini scalars)
let lw = (bitWidth + window - 1) div window + 1 + 1
result = (lw mod window) + lw
func scalarMulGLV_m2w2*[scalBits](
@ -374,6 +391,10 @@ func scalarMulGLV_m2w2*[scalBits](
## via the GLV (Gallant-lambert-Vanstone) decomposition.
##
## For 2-dimensional decomposition with window 2
##
## Requires:
## - Cofactor to be cleared
## - 0 <= scalar < curve order
const C = P0.F.C # curve
static: doAssert: scalBits == C.getCurveOrderBitwidth()
@ -384,16 +405,7 @@ func scalarMulGLV_m2w2*[scalBits](
# 2. Decompose scalar into mini-scalars
const L = computeRecodedLength(C.getCurveOrderBitwidth(), 2)
var miniScalars {.noInit.}: array[2, BigInt[L]]
when C == BN254_Snarks:
scalar.decomposeScalar_BN254_Snarks_G1(
miniScalars
)
elif C == BLS12_381:
scalar.decomposeScalar_BLS12_381_G1(
miniScalars
)
else:
{.error: "Unsupported curve for GLV acceleration".}
miniScalars.decomposeEndo(scalar, P0.F)
# 3. TODO: handle negative mini-scalars
# Either negate the associated base and the scalar (in the `endomorphisms` array)
@ -553,7 +565,7 @@ when isMainModule:
)
var decomp: MultiScalar[M, L]
decomposeScalar_BN254_Snarks_G1(scalar, decomp)
decomp.decomposeEndo(scalar, Fp[BN254_Snarks])
doAssert: bool(decomp[0] == BigInt[L].fromHex"14928105460c820ccc9a25d0d953dbfe")
doAssert: bool(decomp[1] == BigInt[L].fromHex"13a2f911eb48a578844b901de6f41660")
@ -564,7 +576,7 @@ when isMainModule:
)
var decomp: MultiScalar[M, L]
decomposeScalar_BN254_Snarks_G1(scalar, decomp)
decomp.decomposeEndo(scalar, Fp[BN254_Snarks])
doAssert: bool(decomp[0] == BigInt[L].fromHex"28cf7429c3ff8f7e82fc419e90cc3a2")
doAssert: bool(decomp[1] == BigInt[L].fromHex"457efc201bdb3d2e6087df36430a6db6")
@ -575,7 +587,7 @@ when isMainModule:
)
var decomp: MultiScalar[M, L]
decomposeScalar_BN254_Snarks_G1(scalar, decomp)
decomp.decomposeEndo(scalar, Fp[BN254_Snarks])
doAssert: bool(decomp[0] == BigInt[L].fromHex"4da8c411566c77e00c902eb542aaa66b")
doAssert: bool(decomp[1] == BigInt[L].fromHex"5aa8f2f15afc3217f06677702bd4e41a")

View File

@ -7,6 +7,7 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
std/macros,
# Internal
../primitives,
../config/[common, curves, type_bigint],
@ -15,122 +16,219 @@ import
../towers,
./ec_weierstrass_projective
# Parameters for GLV endomorphisms acceleration
# ----------------------------------------------------------------------------------------
# ############################################################
#
# Endomorphism acceleration decomposition parameters
# for Scalar Multiplication
#
# ############################################################
#
# TODO: cleanup, those should be derived in the config folder
# and stored in a constant
# or generated from sage into a config file read at compile-time
type
MultiScalar*[M, LengthInBits: static int] = array[M, BigInt[LengthInBits]]
## Decomposition of a secret scalar in multiple scalars
# BN254 Snarks G1
# ----------------------------------------------------------------------------------------
# Chapter 6.3.1 - Guide to Pairing-based Cryptography
const Lattice_BN254_Snarks_G1: array[2, array[2, tuple[b: BigInt[127], isNeg: bool]]] = [
const Lattice_BN254_Snarks_G1 = (
# Curve of order 254 -> mini scalars of size 127
# u = 0x44E992B44A6909F1
[(BigInt[127].fromHex"0x89d3256894d213e3", false), # 2u + 1
(BigInt[127].fromHex"0x6f4d8248eeb859fd0be4e1541221250b", false)], # 6u² + 4u + 1
[(BigInt[127].fromHex"0x6f4d8248eeb859fc8211bbeb7d4f1128", false), # 6u² + 2u
(BigInt[127].fromHex"0x89d3256894d213e3", true)] # -2u - 1
]
# (BigInt, isNeg)
((BigInt[64].fromHex"0x89d3256894d213e3", false), # 2u + 1
(BigInt[127].fromHex"0x6f4d8248eeb859fd0be4e1541221250b", false)), # 6u² + 4u + 1
((BigInt[127].fromHex"0x6f4d8248eeb859fc8211bbeb7d4f1128", false), # 6u² + 2u
(BigInt[64].fromHex"0x89d3256894d213e3", true)) # -2u - 1
)
const Babai_BN254_Snarks_G1 = [
const Babai_BN254_Snarks_G1 = (
# Vector for Babai rounding
BigInt[127].fromHex"0x89d3256894d213e3", # 2u + 1
BigInt[127].fromHex"0x6f4d8248eeb859fd0be4e1541221250b" # 6u² + 4u + 1
]
# (BigInt, isNeg)
(BigInt[66].fromHex"0x2d91d232ec7e0b3d7", false), # (2u + 1) << 2^256 // r
(BigInt[130].fromHex"0x24ccef014a773d2d25398fd0300ff6565", false) # (6u² + 4u + 1) << 2^256 // r
)
func decomposeScalar_BN254_Snarks_G1*[M, scalBits, L: static int](
scalar: BigInt[scalBits],
miniScalars: var MultiScalar[M, L]
) =
## Decompose a secret scalar into mini-scalar exploiting
## BN254_Snarks specificities.
##
## TODO: Generalize to all BN curves
## - needs a Lattice type
## - needs to better support negative bigints, (extra bit for sign?)
# BLS12-381 G1
# ----------------------------------------------------------------------------------------
static: doAssert L == (scalBits + M - 1) div M + 1
# 𝛼0 = (0x2d91d232ec7e0b3d7 * s) >> 256
# 𝛼1 = (0x24ccef014a773d2d25398fd0300ff6565 * s) >> 256
const
w = BN254_Snarks.getCurveOrderBitwidth().wordsRequired()
alphaHats = (BigInt[66].fromHex"0x2d91d232ec7e0b3d7",
BigInt[130].fromHex"0x24ccef014a773d2d25398fd0300ff6565")
const Lattice_BLS12_381_G1 = (
# (BigInt, isNeg)
((BigInt[128].fromHex"0xac45a4010001a40200000000ffffffff", false), # u² - 1
(BigInt[1].fromHex"0x1", true)), # -1
((BigInt[1].fromHex"0x1", false), # 1
(BigInt[128].fromHex"0xac45a4010001a4020000000100000000", false)) # u²
)
var alphas{.noInit.}: array[M, BigInt[scalBits]] # TODO size 66+254 and 130+254
staticFor i, 0, M:
alphas[i].prod_high_words(alphaHats[i], scalar, w)
# We have k0 = s - 𝛼0 b00 - 𝛼1 b10
# and kj = 0 - 𝛼j b0j - 𝛼1 b1j
var k: array[M, BigInt[scalBits]]
k[0] = scalar
for miniScalarIdx in 0 ..< M:
for basisIdx in 0 ..< M:
var alphaB {.noInit.}: BigInt[scalBits]
alphaB.prod(alphas[basisIdx], Lattice_BN254_Snarks_G1[basisIdx][miniScalarIdx].b) # TODO small lattice size
if Lattice_BN254_Snarks_G1[basisIdx][miniScalarIdx].isNeg:
k[miniScalarIdx] += alphaB
else:
k[miniScalarIdx] -= alphaB
miniScalars[miniScalarIdx].copyTruncatedFrom(k[miniScalarIdx])
const Lattice_BLS12_381_G1: array[2, array[2, tuple[b: BigInt[128], isNeg: bool]]] = [
# Curve of order 254 -> mini scalars of size 127
# u = 0x44E992B44A6909F1
[(BigInt[128].fromHex"0xac45a4010001a40200000000ffffffff", false), # u² - 1
(BigInt[128].fromHex"0x1", true)], # -1
[(BigInt[128].fromHex"0x1", false), # 1
(BigInt[128].fromHex"0xac45a4010001a4020000000100000000", false)] # u²
]
const Babai_BLS12_381_G1 = [
const Babai_BLS12_381_G1 = (
# Vector for Babai rounding
BigInt[128].fromHex"0xac45a4010001a4020000000100000000",
BigInt[128].fromHex"0x1"
]
# (BigInt, isNeg)
(BigInt[129].fromHex"0x17c6becf1e01faadd63f6e522f6cfee30", false),
(BigInt[2].fromHex"0x2", false)
)
func decomposeScalar_BLS12_381_G1*[M, scalBits, L: static int](
# BN254 Snarks G2
# ----------------------------------------------------------------------------------------
const Lattice_BN254_Snarks_G2 = (
# Curve of order 254 -> mini scalars of size 65
# x = 0x44E992B44A6909F1
# Value, isNeg
((BigInt[63].fromHex"0x44e992b44a6909f2", false), # x+1
(BigInt[63].fromHex"0x44e992b44a6909f1", false), # x
(BigInt[63].fromHex"0x44e992b44a6909f1", false), # x
(BigInt[64].fromHex"0x89d3256894d213e2", true)), # -2x
((BigInt[64].fromHex"0x89d3256894d213e3", false), # 2x+1
(BigInt[63].fromHex"0x44e992b44a6909f1", true), # -x
(BigInt[63].fromHex"0x44e992b44a6909f2", true), # -x-1
(BigInt[63].fromHex"0x44e992b44a6909f1", true)), # -x
((BigInt[64].fromHex"0x89d3256894d213e2", false), # 2x
(BigInt[64].fromHex"0x89d3256894d213e3", false), # 2x+1
(BigInt[64].fromHex"0x89d3256894d213e3", false), # 2x+1
(BigInt[64].fromHex"0x89d3256894d213e3", false)), # 2x+1
((BigInt[63].fromHex"0x44e992b44a6909f0", false), # x-1
(BigInt[65].fromHex"0x113a64ad129a427c6", false), # 4x+2
(BigInt[64].fromHex"0x89d3256894d213e1", true), # -2x+1
(BigInt[63].fromHex"0x44e992b44a6909f0", false)), # x-1
)
const Babai_BN254_Snarks_G2 = (
# Vector for Babai rounding
# Value, isNeg
(BigInt[128].fromHex"0xc444fab18d269b9dd0cb46fd51906254", false), # 2x²+3x+1 << 2^256 // r
(BigInt[193].fromHex"0x13d00631561b2572922df9f942d7d77c7001378f5ee78976d", false), # 3x³+8x²+x << 2^256 // r
(BigInt[192].fromhex"0x9e80318ab0d92b94916fcfca16bebbe436510546a93478ab", false), # 6x³+4x²+x << 2^256 // r
(BigInt[128].fromhex"0xc444fab18d269b9af7ae23ce89afae7d", true) # -2x²-x << 2^256 // r
)
# BLS12-381 G2
# ----------------------------------------------------------------------------------------
const Lattice_BLS12_381_G2 = (
# Curve of order 254 -> mini scalars of size 65
# x = -0xd201000000010000
# Value, isNeg
((BigInt[64].fromHex"0xd201000000010000", false), # -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"0xd201000000010000", false), # -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"0xd201000000010000", false), # -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"0xd201000000010000", false)) # -x
)
const Babai_BLS12_381_G2 = (
# Vector for Babai rounding
# Value, isNeg
(BigInt[193].fromHex"0x1381204ca56cd56b533cfcc0d3e76ec2892078a5e8573b29c", false),
(BigInt[129].fromHex"0x17c6becf1e01faadd63f6e522f6cfee2f", true),
(BigInt[65].fromhex"0x1cfbe4f7bd0027db0", false),
(BigInt[1].fromhex"0x0", false)
)
# Decomposition routine
# ----------------------------------------------------------------------------------------
{.experimental: "dynamicbindsym".}
macro dispatch(prefix: static string, C: static Curve, G: static string): untyped =
result = bindSym(prefix & $C & "_" & G)
template babai(F: typedesc[Fp or Fp2]): untyped =
const G = if F is Fp: "G1"
else: "G2"
dispatch("Babai_", F.C, G)
template lattice(F: typedesc[Fp or Fp2]): untyped =
const G = if F is Fp: "G1"
else: "G2"
dispatch("Lattice_", F.C, G)
func decomposeEndo*[M, scalBits, L: static int](
miniScalars: var MultiScalar[M, L],
scalar: BigInt[scalBits],
miniScalars: var MultiScalar[M, L]
F: typedesc[Fp or Fp2]
) =
## Decompose a secret scalar into mini-scalar exploiting
## BLS12_381 specificities.
## Decompose a secret scalar into M mini-scalars
## using a curve endomorphism(s) characteristics.
##
## TODO: Generalize to all BLS curves
## - needs a Lattice type
## - needs to better support negative bigints, (extra bit for sign?)
## A scalar decomposition might lead to negative miniscalar(s).
## For proper handling it requires either:
## 1. Negating it and then negating the corresponding curve point P
## 2. Adding an extra bit to the recoding, which will do the right thing™
##
## For implementation solution 1 is faster:
## - Double + Add is about 5000~8000 cycles on 6 64-bits limbs (BLS12-381)
## - Conditional negate is about 10 cycles per Fp, on G2 projective we have 3 (coords) * 2 (Fp2) * 10 (cycles) ~= 60 cycles
## We need to test the mini scalar, which is 65 bits so 2 Fp so about 2 cycles
## and negate it as well.
##
## However solution 1 seems to cause issues (TODO)
## with some of the BLS12-381 test cases (6 and 9)
## - 0x5668a2332db27199dcfb7cbdfca6317c2ff128db26d7df68483e0a095ec8e88f
## - 0x644dc62869683f0c93f38eaef2ba6912569dc91ec2806e46b4a3dd6a4421dad1
# Equal when no window, greater otherwise
# Equal when no window or no negative handling, greater otherwise
static: doAssert L >= (scalBits + M - 1) div M + 1
const w = F.C.getCurveOrderBitwidth().wordsRequired()
# 𝛼0 = (0x2d91d232ec7e0b3d7 * s) >> 256
# 𝛼1 = (0x24ccef014a773d2d25398fd0300ff6565 * s) >> 256
const
w = BLS12_381.getCurveOrderBitwidth().wordsRequired()
alphaHats = (BigInt[129].fromHex"0x17c6becf1e01faadd63f6e522f6cfee30",
BigInt[2].fromHex"0x2")
var alphas{.noInit.}: array[M, BigInt[scalBits]] # TODO size 256+255 and 132+255
when F is Fp:
var alphas{.noInit.}: (
BigInt[scalBits + babai(F)[0][0].bits],
BigInt[scalBits + babai(F)[1][0].bits]
)
else:
var alphas{.noInit.}: (
BigInt[scalBits + babai(F)[0][0].bits],
BigInt[scalBits + babai(F)[1][0].bits],
BigInt[scalBits + babai(F)[2][0].bits],
BigInt[scalBits + babai(F)[3][0].bits]
)
staticFor i, 0, M:
alphas[i].prod_high_words(alphaHats[i], scalar, w)
when bool babai(F)[i][0].isZero():
alphas[i].setZero()
else:
alphas[i].prod_high_words(babai(F)[i][0], scalar, w)
when babai(F)[i][1]:
# prod_high_words works like logical right shift
# When negative, we should add 1 to properly round toward -infinity
alphas[i] += SecretWord(1)
# We have k0 = s - 𝛼0 b00 - 𝛼1 b10
# and kj = 0 - 𝛼j b0j - 𝛼1 b1j
var k: array[M, BigInt[scalBits]]
# We have k0 = s - 𝛼0 b00 - 𝛼1 b10 ... - 𝛼m bm0
# and kj = 0 - 𝛼j b0j - 𝛼1 b1j ... - 𝛼m bmj
var
k: array[M, BigInt[scalBits]] # zero-init required
alphaB {.noInit.}: BigInt[scalBits]
k[0] = scalar
for miniScalarIdx in 0 ..< M:
for basisIdx in 0 ..< M:
var alphaB {.noInit.}: BigInt[scalBits]
alphaB.prod(alphas[basisIdx], Lattice_BLS12_381_G1[basisIdx][miniScalarIdx].b) # TODO small lattice size
if Lattice_BLS12_381_G1[basisIdx][miniScalarIdx].isNeg:
k[miniScalarIdx] += alphaB
else:
k[miniScalarIdx] -= alphaB
staticFor miniScalarIdx, 0, M:
staticFor basisIdx, 0, M:
when not bool lattice(F)[basisIdx][miniScalarIdx][0].isZero():
when bool lattice(F)[basisIdx][miniScalarIdx][0].isOne():
alphaB.copyTruncatedFrom(alphas[basisIdx])
else:
alphaB.prod(alphas[basisIdx], lattice(F)[basisIdx][miniScalarIdx][0])
when lattice(F)[basisIdx][miniScalarIdx][1] xor babai(F)[basisIdx][1]:
k[miniScalarIdx] += alphaB
else:
k[miniScalarIdx] -= alphaB
miniScalars[miniScalarIdx].copyTruncatedFrom(k[miniScalarIdx])

View File

@ -11,6 +11,7 @@ import
../config/[common, curves],
../arithmetic,
../towers,
../io/io_bigints,
./ec_weierstrass_projective,
./ec_endomorphism_accel
@ -127,7 +128,7 @@ func scalarMulDoubling(
return (k, bits)
func scalarMulGeneric*(
func scalarMulGeneric(
P: var ECP_SWei_Proj,
scalar: openArray[byte],
scratchspace: var openArray[ECP_SWei_Proj]
@ -209,6 +210,23 @@ func scalarMulGeneric*(
scratchspace[0].sum(P, scratchspace[1])
P.ccopy(scratchspace[0], SecretWord(bits).isNonZero())
func scalarMulGeneric*(P: var ECP_SWei_Proj, scalar: BigInt, window: static int = 5) =
## Elliptic Curve Scalar Multiplication
##
## P <- [k] P
##
## This scalar multiplication can handle edge cases:
## - When a cofactor is not cleared
## - Multiplying by a number beyond curve order.
##
## A window size will reserve 2^window of scratch space to accelerate
## the scalar multiplication.
var
scratchSpace: array[1 shl window, ECP_SWei_Proj]
scalarCanonicalBE: array[(scalar.bits+7) div 8, byte] # canonical big endian representation
scalarCanonicalBE.exportRawUint(scalar, bigEndian) # Export is constant-time
P.scalarMulGeneric(scalarCanonicalBE, scratchSpace)
func scalarMul*(
P: var ECP_SWei_Proj,
scalar: BigInt
@ -216,14 +234,19 @@ func scalarMul*(
## Elliptic Curve Scalar Multiplication
##
## P <- [k] P
# This calls endomorphism accelerated scalar mul if available
# or the generic scalar mul otherwise
when ECP_SWei_Proj.F is Fp and ECP_SWei_Proj.F.C in {BN254_Snarks, BLS12_381}:
# ⚠️ This requires the cofactor to be cleared
scalarMulGLV(P, scalar)
##
## This use endomorphism acceleration by default if available
## Endomorphism acceleration requires:
## - Cofactor to be cleared
## - 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}:
when ECP_SWei_Proj.F is Fp:
P.scalarMulGLV_m2w2(scalar)
elif ECP_SWei_Proj.F is Fp2:
P.scalarMulEndo(scalar)
else: # Curves defined on Fp^m with m > 2
{.error: "Unreachable".}
else:
var
scratchSpace: array[1 shl 4, ECP_SWei_Proj]
scalarCanonicalBE: array[(scalar.bits+7)div 8, byte] # canonical big endian representation
scalarCanonicalBE.exportRawUint(scalar, bigEndian) # Export is constant-time
P.scalarMulGeneric(scalarCanonicalBE, scratchSpace)
scalarMulGeneric(P, scalar)

View File

@ -22,15 +22,12 @@ t = x + 1
print('p : ' + p.hex())
print('r : ' + r.hex())
print('t : ' + t.hex())
print('p (mod r) == t-1 (mod r) == 0x' + (p % r).hex())
# Finite fields
Fp = GF(p)
K2.<u> = PolynomialRing(Fp)
Fp2.<beta> = Fp.extension(u^2+1)
# K6.<v> = PolynomialRing(F2)
# Fp6.<eta> = Fp2.extension(v^3-Fp2([1, 1])
# K12.<w> = PolynomialRing(Fp6)
# Fp12.<gamma> = Fp6.extension(w^2-eta)
# Curves
b = 4
@ -38,6 +35,16 @@ SNR = Fp2([1, 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('')
# Utilities
def fp2_to_hex(a):
v = vector(a)
@ -116,6 +123,9 @@ def psi2(P):
# Pz - Always 1 after extract
])
def clearCofactorG2(P):
return cofactorG2 * P
# Test generator
set_random_seed(1337)
@ -123,6 +133,7 @@ set_random_seed(1337)
print('\nTest vectors:')
for i in range(4):
P = G2.random_point()
P = clearCofactorG2(P)
(Px, Py, Pz) = P
vPx = vector(Px)
@ -134,17 +145,18 @@ for i in range(4):
# Galbraith-Lin-Scott, 2008, Theorem 1
# Fuentes-Castaneda et al, 2011, Equation (2)
assert psi(psi(P)) - t*psi(P) + p*P == G2([0, 1, 0])
assert psi(psi(P)) - t*psi(P) + p*P == G2([0, 1, 0]), "Always true"
# Galbraith-Scott, 2008, Lemma 1
# k-th cyclotomic polynomial with k = 12
assert psi2(psi2(P)) - psi2(P) + P == G2([0, 1, 0])
assert psi(psi(P)) == psi2(P)
assert psi2(psi2(P)) - psi2(P) + P == G2([0, 1, 0]), "Always true"
assert psi(psi(P)) == psi2(P), "Always true"
(Qx, Qy, Qz) = psi(P)
vQx = vector(Qx)
vQy = vector(Qy)
print(' Qx: ' + Integer(vQx[0]).hex() + ' + β * ' + Integer(vQx[1]).hex())
print(' Qy: ' + Integer(vQy[0]).hex() + ' + β * ' + Integer(vQy[1]).hex())
assert psi(P) == (p % r) * P, "Can be false if the cofactor was not cleared"

View File

@ -0,0 +1,166 @@
# 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
# Frobenius Endomorphism
# Untwist-Frobenius-Twist isogeny
#
# ############################################################
# Parameters
x = -(2^62 + 2^55 + 1)
p = 36*x^4 + 36*x^3 + 24*x^2 + 6*x + 1
r = 36*x^4 + 36*x^3 + 18*x^2 + 6*x + 1
t = 6*x^2 + 1
print('p : ' + p.hex())
print('r : ' + r.hex())
print('t : ' + t.hex())
print('p (mod r) == t-1 (mod r) == 0x' + (p % r).hex())
# Finite fields
Fp = GF(p)
K2.<u> = PolynomialRing(Fp)
Fp2.<beta> = Fp.extension(u^2+1)
# Curves
b = 2
SNR = Fp2([1, 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('')
# Utilities
def fp2_to_hex(a):
v = vector(a)
return Integer(v[0]).hex() + ' + β * ' + Integer(v[1]).hex()
# 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
print('FrobConst_psi : ' + fp2_to_hex(FrobConst_psi))
print('FrobConst_psi_2 : ' + fp2_to_hex(FrobConst_psi_2))
print('FrobConst_psi_3 : ' + fp2_to_hex(FrobConst_psi_3))
print('')
FrobConst_psi2_2 = FrobConst_psi_2 * FrobConst_psi_2^p
FrobConst_psi2_3 = FrobConst_psi_3 * FrobConst_psi_3^p
print('FrobConst_psi2_2 : ' + fp2_to_hex(FrobConst_psi2_2))
print('FrobConst_psi2_3 : ' + fp2_to_hex(FrobConst_psi2_3))
print('')
FrobConst_psi3_2 = FrobConst_psi_2 * FrobConst_psi2_2^p
FrobConst_psi3_3 = FrobConst_psi_3 * FrobConst_psi2_3^p
print('FrobConst_psi3_2 : ' + fp2_to_hex(FrobConst_psi3_2))
print('FrobConst_psi3_3 : ' + fp2_to_hex(FrobConst_psi3_3))
# Recap, with ξ (xi) the sextic non-residue
# psi_2 = (ξ^((p-1)/6))^2 = ξ^((p-1)/3)
# psi_3 = psi_2 * ξ^((p-1)/6) = ξ^((p-1)/3) * ξ^((p-1)/6) = ξ^((p-1)/2)
#
# Reminder, in 𝔽p2, frobenius(a) = a^p = conj(a)
# psi2_2 = psi_2 * psi_2^p = ξ^((p-1)/3) * ξ^((p-1)/3)^p = ξ^((p-1)/3) * frobenius(ξ)^((p-1)/3)
# = norm(ξ)^((p-1)/3)
# psi2_3 = psi_3 * psi_3^p = ξ^((p-1)/2) * ξ^((p-1)/2)^p = ξ^((p-1)/2) * frobenius(ξ)^((p-1)/2)
# = norm(ξ)^((p-1)/2)
#
# In Fp²:
# - quadratic non-residues respect the equation a^((p²-1)/2) ≡ -1 (mod p²) by the Legendre symbol
# - sextic non-residues are also quadratic non-residues so ξ^((p²-1)/2) ≡ -1 (mod p²)
#
# We have norm(ξ)^((p-1)/2) = (ξ*frobenius(ξ))^((p-1)/2) = (ξ*(ξ^p))^((p-1)/2) = ξ^(p+1)^(p-1)/2
# = ξ^((p²-1)/2)
# And ξ^((p²-1)/2) ≡ -1 (mod p²)
# So psi2_3 ≡ -1 (mod p²)
# Frobenius Fp2
A = Fp2([5, 7])
Aconj = Fp2([5, -7])
AF = A.frobenius(1) # or pth_power(1)
AF2 = A.frobenius(2)
AF3 = A.frobenius(3)
print('')
print('A : ' + fp2_to_hex(A))
print('A conjugate: ' + fp2_to_hex(Aconj))
print('')
print('AF1 : ' + fp2_to_hex(AF))
print('AF2 : ' + fp2_to_hex(AF2))
print('AF3 : ' + fp2_to_hex(AF3))
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)
# Vectors
print('\nTest vectors:')
for i in range(4):
P = G2.random_point()
P = clearCofactorG2(P)
(Px, Py, Pz) = P
vPx = vector(Px)
vPy = vector(Py)
# vPz = vector(Pz)
print(f'\nTest {i}')
print(' Px: ' + Integer(vPx[0]).hex() + ' + β * ' + Integer(vPx[1]).hex())
print(' Py: ' + Integer(vPy[0]).hex() + ' + β * ' + Integer(vPy[1]).hex())
# print(' Pz: ' + Integer(vPz[0]).hex() + ' + β * ' + Integer(vPz[1]).hex())
# Galbraith-Lin-Scott, 2008, Theorem 1
# Fuentes-Castaneda et al, 2011, Equation (2)
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 psi(psi(P)) == psi2(P)
(Qx, Qy, Qz) = psi(P)
vQx = vector(Qx)
vQy = vector(Qy)
print(' Qx: ' + Integer(vQx[0]).hex() + ' + β * ' + Integer(vQx[1]).hex())
print(' Qy: ' + Integer(vQy[0]).hex() + ' + β * ' + Integer(vQy[1]).hex())
(Rx, Ry, Rz) = (p % r) * P
vRx = vector(Rx)
vRy = vector(Ry)
print(' Rx: ' + Integer(vRx[0]).hex() + ' + β * ' + Integer(vRx[1]).hex())
print(' Ry: ' + Integer(vRy[0]).hex() + ' + β * ' + Integer(vRy[1]).hex())
assert psi(P) == (p % r) * P, "Can be false if the cofactor was not cleared"

View File

@ -23,15 +23,12 @@ cofactor = 1
print('p : ' + p.hex())
print('r : ' + r.hex())
print('t : ' + t.hex())
print('p (mod r) == t-1 (mod r) == 0x' + (p % r).hex())
# Finite fields
Fp = GF(p)
K2.<u> = PolynomialRing(Fp)
Fp2.<beta> = Fp.extension(u^2+1)
# K6.<v> = PolynomialRing(F2)
# Fp6.<eta> = Fp2.extension(v^3-Fp2([9, 1]))
# K12.<w> = PolynomialRing(Fp6)
# K12.<gamma> = F6.extension(w^2-eta)
# Curves
b = 3
@ -39,6 +36,16 @@ SNR = Fp2([9, 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('')
# Utilities
def fp2_to_hex(a):
v = vector(a)
@ -100,8 +107,8 @@ print('AF3 : ' + fp2_to_hex(AF3))
def psi(P):
(Px, Py, Pz) = P
return G2([
FrobConst_psi_2 * Px.frobenius(),
FrobConst_psi_3 * Py.frobenius()
FrobConst_psi_2 * Px.frobenius(1),
FrobConst_psi_3 * Py.frobenius(1)
# Pz.frobenius() - Always 1 after extract
])
@ -113,6 +120,9 @@ def psi2(P):
# Pz - Always 1 after extract
])
def clearCofactorG2(P):
return cofactorG2 * P
# Test generator
set_random_seed(1337)
@ -120,6 +130,7 @@ set_random_seed(1337)
print('\nTest vectors:')
for i in range(4):
P = G2.random_point()
P = clearCofactorG2(P)
(Px, Py, Pz) = P
vPx = vector(Px)
@ -145,3 +156,5 @@ for i in range(4):
vQy = vector(Qy)
print(' Qx: ' + Integer(vQx[0]).hex() + ' + β * ' + Integer(vQx[1]).hex())
print(' Qy: ' + Integer(vQy[0]).hex() + ' + β * ' + Integer(vQy[1]).hex())
assert psi(P) == (p % r) * P, "Can be false if the cofactor was not cleared"

View File

@ -8,42 +8,40 @@
# ############################################################
#
# BN254 GLV Endomorphism
# BLS12-381 GLS Endomorphism
# Lattice Decomposition
#
# ############################################################
# Parameters
u = -(2^63 + 2^62 + 2^60 + 2^57 + 2^48 + 2^16)
p = (u - 1)^2 * (u^4 - u^2 + 1)//3 + u
r = u^4 - u^2 + 1
cofactor = Integer('0x396c8c005555e1568c00aaab0000aaab')
x = -(2^63 + 2^62 + 2^60 + 2^57 + 2^48 + 2^16)
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 = u^2 - 1
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 = u^4
lambda2_r = x^4
assert lambda2_r^3 % r == 1
print('λᵩ2 : ' + lambda2_r.hex())
# Finite fields
F = GF(p)
# K2.<u> = PolynomialRing(F)
# F2.<beta> = F.extension(u^2+9)
# K6.<v> = PolynomialRing(F2)
# F6.<eta> = F2.extension(v^3-beta)
# K12.<w> = PolynomialRing(F6)
# K12.<gamma> = F6.extension(w^2-eta)
# Curves
b = 4
G1 = EllipticCurve(F, [0, b])
# G2 = EllipticCurve(F2, [0, b*beta])
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())
@ -51,16 +49,17 @@ 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()
assert Prand != G1([0, 1, 0]) # Infinity
# Clear cofactor
P = Prand * cofactor
P = clearCofactorG1(Prand)
assert P != G1([0, 1, 0]) # Infinity
(Px, Py, Pz) = P
Qendo1 = G1([Px*phi1 % p, Py, Pz])
@ -82,30 +81,45 @@ def checkEndo():
checkEndo()
# Lattice
b = [
[u^2-1, -1],
[1, u^2]
# 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]
]
# Babai rounding
ahat = [u^2, 1]
v = int(r).bit_length()
v = int(((v + 64 - 1) // 64) * 64) # round to next multiple of 64
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]
l = [Integer(a << v) // r for a in ahat]
print('𝛼\u03051: ' + l[0].hex())
print('𝛼\u03052: ' + l[1].hex())
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('')
def getGLV2_decomp(scalar):
a0 = (l[0] * scalar) >> v
a1 = (l[1] * scalar) >> v
maxLen = (int(r).bit_length() + 1) // 2 + 1
k0 = scalar - a0 * b[0][0] - a1 * b[1][0]
k1 = 0 - a0 * b[0][1] - a1 * b[1][1]
a0 = (v[0] * scalar) >> n
a1 = (v[1] * scalar) >> n
assert int(k0).bit_length() <= (int(r).bit_length() + 1) // 2
assert int(k1).bit_length() <= (int(r).bit_length() + 1) // 2
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
@ -138,7 +152,7 @@ def pointToString(P):
(Px, Py, Pz) = P
return '(x: ' + Integer(Px).hex() + ', y: ' + Integer(Py).hex() + ', z: ' + Integer(Pz).hex() + ')'
def scalarMulGLV(scalar, P0):
def scalarMulEndo(scalar, P0):
m = 2
L = ((int(r).bit_length() + m-1) // m) + 1 # l = ⌈log2 r/m⌉ + 1
@ -193,10 +207,11 @@ for i in range(1):
print('---------------------------------------')
# scalar = randrange(r) # Pick an integer below curve order
# P = G1.random_point()
# P = clearCofactorG1(P)
scalar = Integer('0xf7e60a832eb77ac47374bc93251360d6c81c21add62767ff816caf11a20d8db')
P = G1([
Integer('0xf9679bb02ee7f352fff6a6467a5e563ec8dd38c86a48abd9e8f7f241f1cdd29d54bc3ddea3a33b62e0d7ce22f3d244a'),
Integer('0x50189b992cf856846b30e52205ff9ef72dc081e9680726586231cbc29a81a162120082585f401e00382d5c86fb1083f'),
Integer(1)
])
scalarMulGLV(scalar, P)
scalarMulEndo(scalar, P)

View 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 = -(2^63 + 2^62 + 2^60 + 2^57 + 2^48 + 2^16)
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+1)
SNR = Fp2([1, 1]) # Sextic Non-Residue for Sextic Twist
# Curves
b = 4
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 = (1/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()
# 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('')
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
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
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
L = ((int(r).bit_length() + m-1) // m) + 1 # l = ⌈log2 r/m⌉ + 1
L += 1 # Deal with negative miniscalars
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
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('0x1f7bef2a74f3bf8ac0225a9edfa514bb5666b15e7be3e929059f2ef75f0035a6')
# P = G2([
# Fp2([Integer('0x989f16bcb9da60ef72383e6134ba194f57e30109806304336c0c995e2857ed20bf5b6e03d6fe1424332e9c666cbd10a'),
# Integer('0x16692643cb5e7466e3730d3ea775c7741ac34d670b3be685761a7d6ab722a2673ce374ddab87b7c4d2675ba2199f9121')]),
# Fp2([Integer('0x931e416488bef7cb4a053e4bd86ef44818bc03a5be5b04606b2a4dc1d139a3a452f5f7172f24eeaad84702b73b155bb'),
# Integer('0x192c3e2a6619473216b7bb2447448cdbeb9f7e3c9486b0a05aadf6dcd91d7cb275a5d84c1a362628efffbc8711a62a67')])
# ])
# This integer leads to negative miniscalar for proper handling it requires either:
# 1. Negating it and then negating the corresponding curve point P
# 2. Adding an extra bit to the recoding, which will do the right thing™
#
# For implementation solution 1 is faster:
# - Double + Add is about 5000~8000 cycles on 6 64-bits limbs (BLS12-381)
# - Conditional negate is about 10 cycles per Fp, on G2 projective we have 3 (coords) * 2 (Fp2) * 10 (cycles) ~= 60 cycles
# We need to test the mini scalar, which is 65 bits so 2 Fp so about 2 cycles
# and negate it as well.
# scalar = Integer('0x6448f296d9b1a8d81319a0b789df04c587c6165776ccf39f50a354204aabe0da')
# P = G2([
# Fp2([Integer('0x5adc112fb04bf4ca642d5a7d7343ccd6b93546442d2fff5b9d32c15e456d54884cba49dd7f94ce4ddaad4018e55d0f2'),
# Integer('0x5d1c5bbf5d7a833dc76ba206bfa99c281fc37941be050e18f8c6d267b2376b3634d8ad6eb951e52a6d096315abd17d6')]),
# Fp2([Integer('0x15a959e54981fab9ac3c6f5bfd6fb60a50a916bd43d96a09922a54309b84812736581bfa728670cba864b08b9e391bb9'),
# Integer('0xf5d6d74f1dd3d9c07451340b8f6990fe93a28fe5e176564eb920bf17eb02df8b6f1e626eda5542ff415f89d51943001')])
# ])
scalar = Integer('0x5668a2332db27199dcfb7cbdfca6317c2ff128db26d7df68483e0a095ec8e88f')
P = G2([
Fp2([Integer('0xa8c5649d2df1bae84fd9e8bfcde5113937b3acea22d67ddfedaf1fb8de8c1ef4c70591cf505c24c31e54020c2c510c3'),
Integer('0xa0553f98229a6a067489c3ee204161c11e96f421b3e9c145dc3865b03e9d4ff6cab14c5b5308ecd31173f954463690c')]),
Fp2([Integer('0xb29d8dfe18dc41b4826c3a102c1bf8f306cb42433cc36ee38080f47a324c02a678f9daed0a2bc577c18b9865de029f0'),
Integer('0x558cdabf11e37c5c5e8abd668bbdd71bb3f07f320948ccaac8a207359fffe38424bfd9b1ef1d24b28b2fbb9f76faff1')])
])
scalarMulEndo(scalar, P)

View File

@ -8,40 +8,39 @@
# ############################################################
#
# BN254 GLV Endomorphism
# BN254-Snarks GLV Endomorphism
# Lattice Decomposition
#
# ############################################################
# Parameters
u = Integer('0x44E992B44A6909F1')
p = 36*u^4 + 36*u^3 + 24*u^2 + 6*u + 1
r = 36*u^4 + 36*u^3 + 18*u^2 + 6*u + 1
cofactor = 1
x = Integer('0x44E992B44A6909F1')
p = 36*x^4 + 36*x^3 + 24*x^2 + 6*x + 1
r = 36*x^4 + 36*x^3 + 18*x^2 + 6*x + 1
# Cube root of unity (mod r) formula for any BN curves
lambda1_r = (-(36*u^3+18*u^2+6*u+2))
lambda1_r = (-(36*x^3+18*x^2+6*x+2))
assert lambda1_r^3 % r == 1
print('λᵩ1 : ' + lambda1_r.hex())
print('λᵩ1+r: ' + (lambda1_r+r).hex())
lambda2_r = (36*u^4-1)
lambda2_r = (36*x^4-1)
assert lambda2_r^3 % r == 1
print('λᵩ2 : ' + lambda2_r.hex())
# Finite fields
F = GF(p)
# K2.<u> = PolynomialRing(F)
# F2.<beta> = F.extension(u^2+9)
# K6.<v> = PolynomialRing(F2)
# F6.<eta> = F2.extension(v^3-beta)
# K12.<w> = PolynomialRing(F6)
# K12.<gamma> = F6.extension(w^2-eta)
# Curves
b = 3
G1 = EllipticCurve(F, [0, b])
# G2 = EllipticCurve(F2, [0, b/beta])
cofactorG1 = G1.order() // r
assert cofactorG1 == 1, "BN curve have a prime order"
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())
@ -75,28 +74,45 @@ def checkEndo():
checkEndo()
# Lattice
b = [
[2*u+1, 6*u^2+4*u+1],
[6*u^2+2*u, -2*u-1]
# Decomposition generated by LLL-algorithm and Babai rounding
# to solve the Shortest (Basis) Vector Problem
# Lattice from Guide to Pairing-Based Cryptography
Lat = [
[2*x+1, 6*x^2+4*x+1],
[6*x^2+2*x, -2*x-1]
]
# Babai rounding
ahat = [2*u+1, 6*u^2+4*u+1]
v = int(r).bit_length()
v = int(((v + 64 - 1) // 64) * 64) # round to next multiple of 64
ahat = [2*x+1, 6*x^2+4*x+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]
l = [Integer(a << v) // 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('')
def getGLV2_decomp(scalar):
a0 = (l[0] * scalar) >> v
a1 = (l[1] * scalar) >> v
maxLen = (int(r).bit_length() + 1) // 2 + 1
k0 = scalar - a0 * b[0][0] - a1 * b[1][0]
k1 = 0 - a0 * b[0][1] - a1 * b[1][1]
a0 = (v[0] * scalar) >> n
a1 = (v[1] * scalar) >> n
assert int(k0).bit_length() <= (int(r).bit_length() + 1) // 2
assert int(k1).bit_length() <= (int(r).bit_length() + 1) // 2
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
@ -129,7 +145,7 @@ def pointToString(P):
(Px, Py, Pz) = P
return '(x: ' + Integer(Px).hex() + ', y: ' + Integer(Py).hex() + ', z: ' + Integer(Pz).hex() + ')'
def scalarMulGLV(scalar, P0):
def scalarMulEndo(scalar, P0):
m = 2
L = ((int(r).bit_length() + m-1) // m) + 1 # l = ⌈log2 r/m⌉ + 1
@ -190,4 +206,4 @@ for i in range(1):
Integer('0x1c994169687886ccd28dd587c29c307fb3cab55d796d73a5be0bbf9aab69912e'),
Integer(1)
])
scalarMulGLV(scalar, P)
scalarMulEndo(scalar, P)

View 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.
# ############################################################
#
# BN254-Snarks GLS Endomorphism
# Lattice Decomposition
#
# ############################################################
# Parameters
x = Integer('0x44E992B44A6909F1')
p = 36*x^4 + 36*x^3 + 24*x^2 + 6*x + 1
r = 36*x^4 + 36*x^3 + 18*x^2 + 6*x + 1
t = 6*x^2 + 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+1)
SNR = Fp2([9, 1]) # Sextic Non-Residue for Sextic Twist
# Curves
b = 3
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()
# Decomposition generated by LLL-algorithm and Babai rounding
# to solve the Shortest (Basis) Vector Problem
# TODO: This Lattice from Guide to Pairing-Based Cryptography
# gives miniscalars bigger than (r/4)+1 = 65 bits
# for unknown reason
# The linear combination is correct though.
# Lattice from Guide to Pairing-Based Cryptography
# Lat = [
# [ - x , 2*x , 2*x+1, - x ],
# [ -2*x-1, x , x+1, x ],
# [ 2*x+1, 0, 2*x , 1],
# [ -1, 2*x+1, 1, 2*x ]
# ]
# ahat = [2*x+1, -(12*x^3+6*x^2+2*x+1), 2*x*(3*x^2+3*x+1), 6*x^2-1]
# Lattice from Galbraith-Scott 2008
Lat = Matrix([
[ x+1, x , x , -2*x ],
[ 2*x+1, -x , -x-1, -x ],
[ 2*x , 2*x+1, 2*x+1, 2*x+1],
[ x-1, 4*x+2, -2*x+1, x-1]
])
ahat = [2*x^2+3*x+1, 12*x^3+8*x^2+x, 6*x^3+4*x^2+x, -2*x^2-x]
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(' 𝛼\u03052: 0x' + v[2].hex())
print(' 𝛼\u03053: 0x' + v[3].hex())
print('')
lambda1 = (t-1) % r
lambda2 = lambda1^2 % r
lambda3 = lambda1^3 % r
def getGLV2_decomp(scalar):
maxLen = (int(r).bit_length() + 3) // 4 + 1
a0 = (v[0] * scalar) >> n
a1 = (v[1] * scalar) >> n
a2 = (v[2] * scalar) >> n
a3 = (v[3] * scalar) >> n
print('𝛼0: ' + a0.hex())
print('𝛼1: ' + a1.hex())
print('𝛼2: ' + a2.hex())
print('𝛼3: ' + a3.hex())
print('𝛼3 unred: ' + (v[3] * scalar).hex())
print('')
print('Lat[3][0]: ' + Lat[3][0].hex())
print('a3 * Lat[3][0]: ' + (a3 * Lat[3][0]).hex())
print('')
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]
k = [scalar, 0, 0, 0]
a = [a0, a1, a2, a3]
for i in range(4):
for j in range(4):
elem = a[j] * Lat[j][i]
print(f'a{j} * Lat[{j}][{i}] = {elem.hex()}')
k[i] -= elem
print(f' k{i} = {k[i].hex()}')
print('k: ' + str([ki.hex() for ki in k]))
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
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
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):
for i in range(len(factors)):
print(f'f{i}: {factors[i].hex()}')
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())
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
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('0x2c02275a71bb41c911faf48cab4f7ac7fc6672a5c15586185c8cff3203181da0')
# P = G2([
# Fp2([Integer('0x2a028c1328bb0abf252edfbf7133b84eef2a5f20163fe61685b4b54229ca585d'),
# Integer('0x8f80ad79e8e7e79bbdc645d9f5b339c52dd99a901b90de2494492656f11a9d5')]),
# Fp2([Integer('0x1f04320578e31ffa2e2b59ad8fcb1aba622b5f307ac540cf2ccdab07dec56503'),
# Integer('0x2973900c0fdf651b64f5b1a990baec7c582e0743d501bdb991374776d6c73b28')])
# ])
scalar = Integer('0x24c5b2ce21615dca82231f5fb0fc8d05aa07c6df4bb5aa7c2381ac7b61a6290c')
P = G2([
Fp2([Integer('0x1132e63c444e1abce6fc4c39bdf5be5caad586837cbf5ca9d3891482bdefe77'),
Integer('0x22b71f598dab789f055fc9669ddf66f0d75f581af0e9e8863d7f95a51ef34862')]),
Fp2([Integer('0x58e39050f64c9948d7238b99ecaee947cb934688a6e9f483c5c36b6e07aa31b'),
Integer('0x2e64b920f498e12992f2a4ae3f9ced43f3f64705c9008169f3b930a760d055fb')])
])
scalarMulEndo(scalar, P)

View File

@ -16,10 +16,6 @@
x = -(2^63 + 2^62 + 2^60 + 2^57 + 2^48 + 2^16)
p = (x - 1)^2 * (x^4 - x^2 + 1)//3 + x
r = x^4 - x^2 + 1
cofactor = Integer('0x396c8c005555e1568c00aaab0000aaab')
# Effective cofactor for the G2 curve (that leads to equivalent hashToG2 when using endomorphisms)
g2_h_eff = Integer('0xbc69f08f2ee75b3584c6a0ea91b352888e2a8e9145ad7689986ff031508ffe1329c2f178731db956d82bf015d1212b02ec0ec69d7477c1ae954cbc06689f6a359894c0adebbf6b4e8020005aaa95551')
# Finite fields
Fp = GF(p)
@ -36,16 +32,31 @@ SNR = Fp2([1, 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()
# Clear cofactor
P = Prand * cofactor
P = clearCofactorG1(Prand)
(Px, Py, Pz) = P
print('Px: ' + Integer(Px).hex())
@ -59,15 +70,13 @@ for i in range(10):
print('Qx: ' + Integer(Qx).hex())
print('Qy: ' + Integer(Qy).hex())
# print('Qz: ' + Integer(Qz).hex())
print('---------------------------------------')
print('=========================================')
print('G2 vectors: ')
for i in range(10):
print(f'--- test {i} ------------------------------')
Prand = G2.random_point()
# Clear cofactor
P = Prand * g2_h_eff
P = clearCofactorG2(Prand)
(Px, Py, Pz) = P
vPx = vector(Px)
@ -85,7 +94,6 @@ for i in range(10):
Qy = vector(Qy)
print('Qx: ' + Integer(Qx[0]).hex() + ' + β * ' + Integer(Qx[1]).hex())
print('Qy: ' + Integer(Qy[0]).hex() + ' + β * ' + Integer(Qy[1]).hex())
print('---------------------------------------')
print('=========================================')
# CurveOrder sanity check

View File

@ -16,7 +16,6 @@
x = Integer('0x44E992B44A6909F1')
p = 36*x^4 + 36*x^3 + 24*x^2 + 6*x + 1
r = 36*x^4 + 36*x^3 + 18*x^2 + 6*x + 1
cofactor = 1
# Finite fields
Fp = GF(p)
@ -33,13 +32,31 @@ SNR = Fp2([9, 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} ------------------------------')
P = G1.random_point()
P = clearCofactorG1(P)
(Px, Py, Pz) = P
print('Px: ' + Integer(Px).hex())
print('Py: ' + Integer(Py).hex())
@ -57,7 +74,9 @@ print('=========================================')
print('G2 vectors: ')
for i in range(10):
print(f'--- test {i} ------------------------------')
P = G2.random_point()
P = clearCofactorG2(P)
(Px, Py, Pz) = P
vPx = vector(Px)
vPy = vector(Py)
@ -67,6 +86,7 @@ for i in range(10):
exponent = randrange(r) # Pick an integer below curve order
print('scalar: ' + Integer(exponent).hex())
assert exponent < r
Q = exponent * P
(Qx, Qy, Qz) = Q

View File

@ -10,7 +10,8 @@ import
# Internals
../../constantine/config/[common, curves],
../../constantine/arithmetic,
../../constantine/elliptic/ec_weierstrass_projective
../../constantine/elliptic/ec_weierstrass_projective,
../../constantine/io/io_bigints
# Support files for testing Elliptic Curve arithmetic
# ------------------------------------------------------------------------------
@ -27,7 +28,7 @@ iterator unpack(scalarByte: byte): bool =
func unsafe_ECmul_double_add*(
P: var ECP_SWei_Proj,
scalar: openArray[byte],
scalar: BigInt,
) =
## **Unsafe** Elliptic Curve Scalar Multiplication
##
@ -37,12 +38,13 @@ func unsafe_ECmul_double_add*(
## This is UNSAFE to use in production and only intended for testing purposes.
##
## This is highly VULNERABLE to timing attacks and power analysis attacks
##
## `scalar` is in canonical representation in BigEndian (octet string)
var scalarCanonical: array[(scalar.bits+7) div 8, byte]
scalarCanonical.exportRawUint(scalar, bigEndian)
var t0{.noInit.}, t1{.noInit.}: typeof(P)
t0.setInf()
t1.setInf()
for scalarByte in scalar:
for scalarByte in scalarCanonical:
for bit in unpack(scalarByte):
t1.double(t0)
if bit:

View File

@ -269,11 +269,11 @@ suite "ψ²(P) - [t]ψ(P) + [p]P = Inf" & " [" & $WordBitwidth & "-bit mode]":
psi2.frobenius_psi2(P)
tpsi.frobenius_psi(P)
tpsi.scalarMul(trace[0]) # Should be valid for GLS scalar mul even if cofactor isn't cleared
tpsi.scalarMulGeneric(trace[0]) # Cofactor not cleared, invalid for GLS
if trace[1]: # negative trace
tpsi.neg()
pP = P
pP.scalarMul(EC.F.C.Mod) # Should be valid for GLS scalar mul even if cofactor isn't cleared
pP.scalarMulGeneric(EC.F.C.Mod) # Multiply beyond curve order, invalid for GLS
# ψ²(P) - [t]ψ(P) + [p]P = InfinityPoint
r.diff(psi2, tpsi)

View File

@ -37,19 +37,16 @@ proc test(
let qOK = Q.fromHex(Qx, Qy)
let exponent = BigInt[EC.F.C.getCurveOrderBitwidth()].fromHex(scalar)
var exponentCanonical: array[(exponent.bits+7) div 8, byte]
exponentCanonical.exportRawUint(exponent, bigEndian)
var
impl = P
reference = P
endo = P
endoW = P
scratchSpace: array[1 shl 4, EC]
impl.scalarMulGeneric(exponentCanonical, scratchSpace)
reference.unsafe_ECmul_double_add(exponentCanonical)
endo.scalarMulGLV(exponent)
impl.scalarMulGeneric(exponent)
reference.unsafe_ECmul_double_add(exponent)
endo.scalarMulEndo(exponent)
endoW.scalarMulGLV_m2w2(exponent)
doAssert: bool(Q == reference)
@ -60,7 +57,7 @@ proc test(
suite "Scalar Multiplication (cofactor cleared): BLS12_381 implementation vs SageMath" & " [" & $WordBitwidth & "-bit mode]":
# Generated via sage sage/testgen_bls12_381.sage
test(
id = 1,
id = 0,
EC = ECP_SWei_Proj[Fp[BLS12_381]],
Px = "f9679bb02ee7f352fff6a6467a5e563ec8dd38c86a48abd9e8f7f241f1cdd29d54bc3ddea3a33b62e0d7ce22f3d244a",
Py = "50189b992cf856846b30e52205ff9ef72dc081e9680726586231cbc29a81a162120082585f401e00382d5c86fb1083f",
@ -70,7 +67,7 @@ suite "Scalar Multiplication (cofactor cleared): BLS12_381 implementation vs Sag
)
test(
id = 2,
id = 1,
EC = ECP_SWei_Proj[Fp[BLS12_381]],
Px = "17d71835ff84f150fabf5c77ac90bf7f6249143abd1f5d8a46a76f243d424d82e1e258fc7983ba8af97a2462adebe090",
Py = "d3e108ee1332067cbe4f4193eae10381acb69f493b40e53d9dee59506b49c6564c9056494a7f987982eb4069512c1c6",
@ -80,7 +77,7 @@ suite "Scalar Multiplication (cofactor cleared): BLS12_381 implementation vs Sag
)
test(
id = 3,
id = 2,
EC = ECP_SWei_Proj[Fp[BLS12_381]],
Px = "f92c9572692e8f3d450483a7a9bb4694e3b54c9cd09441a4dd7f579b0a6984e47f8090c31c172b33d87f3de186d6b58",
Py = "286ede4cb2ae19ead4932d5550c5d3ec8ce3a3ada5e1ed6d202e93dd1b16d3513f0f9b62adc6323f18e272a426ee955",
@ -90,7 +87,7 @@ suite "Scalar Multiplication (cofactor cleared): BLS12_381 implementation vs Sag
)
test(
id = 4,
id = 3,
EC = ECP_SWei_Proj[Fp[BLS12_381]],
Px = "ec23ff3435b8ebd5e8e0a879d432e11eb974161664b1341fd28f1ffc4c228bf6ada2ae4a565f18c9b66f67a7573502d",
Py = "10c4b647be08db0b49b75320ae891f9f9c5d7bb7c798947e800d681d205d1b24b12e4dfa993d1bd16851b00356627cc1",
@ -100,7 +97,7 @@ suite "Scalar Multiplication (cofactor cleared): BLS12_381 implementation vs Sag
)
test(
id = 5,
id = 4,
EC = ECP_SWei_Proj[Fp[BLS12_381]],
Px = "df127083c2a5ef2388b02af913c0e4002a52a82db9e5ecbf23ee4f557d3b61c91ebcfe9d4973070b46bc5ea6897bca1",
Py = "318960aeea262ec23ffdd42ec1ba72ae6fa2186a1e2a0fc2659073fb7b5adfb50d581a4d998a94d1accf78b1b3a0163",
@ -110,7 +107,7 @@ suite "Scalar Multiplication (cofactor cleared): BLS12_381 implementation vs Sag
)
test(
id = 6,
id = 5,
EC = ECP_SWei_Proj[Fp[BLS12_381]],
Px = "101123de23c0f240c583c2368c4118dc942db219c55f58cf54acd500c1fcfa06f651ad75319ebf840cbdb6bddea7fde4",
Py = "5268587d4b844b0708e0336d1bbf48da185aaf5b948eccc3b565d00a856dd55882b9bb31c52af0e275b168cb35eb7b0",
@ -120,7 +117,7 @@ suite "Scalar Multiplication (cofactor cleared): BLS12_381 implementation vs Sag
)
test(
id = 7,
id = 6,
EC = ECP_SWei_Proj[Fp[BLS12_381]],
Px = "1457ba1bae6eb3afae3261941c65c93e3ae7d784907d15b8d559100da5e13fd29e4a4d6e3103b781a95237b7b2d80a8e",
Py = "6a869a47cb48d01e7d29660932afd7617720262b55de5f430b8aa3d74f9fd2b9d3a07ce192425da58014764fc9532cd",
@ -130,7 +127,7 @@ suite "Scalar Multiplication (cofactor cleared): BLS12_381 implementation vs Sag
)
test(
id = 8,
id = 7,
EC = ECP_SWei_Proj[Fp[BLS12_381]],
Px = "2615f843e8fe68d4c337bcf83b2cf13cbae638edd0740f1eac520dc2146afa3b8d36c540878c1d207ef913634b1e593",
Py = "1787d6eeeceb6e7793073f0bbe7bae522529c126b650c43d5d41e732c581a57df1bfb818061b7b4e6c9145da5df2c43e",
@ -140,7 +137,7 @@ suite "Scalar Multiplication (cofactor cleared): BLS12_381 implementation vs Sag
)
test(
id = 9,
id = 8,
EC = ECP_SWei_Proj[Fp[BLS12_381]],
Px = "10bc0c4e1ed87246a9d4d7d38546369f275a245f6e1d3b882e8c9a7f05bc6ee8ff97a96a54084c2bef15ed8bfefb1465",
Py = "1782377e5f588576b5ab42fea224e88873dda957202f0c6d72ce8728c2d58dc654be77226fbda385d5f269354e4a176a",
@ -150,7 +147,7 @@ suite "Scalar Multiplication (cofactor cleared): BLS12_381 implementation vs Sag
)
test(
id = 10,
id = 9,
EC = ECP_SWei_Proj[Fp[BLS12_381]],
Px = "be4f9f721d98a761a5562bd80ea06f369e9cbb7d33bbb2f0191d4b77d0fd2a10c4083b54157b525f36c522ca3a6ca09",
Py = "166c315ecdd20acb3c5efcc7e038b17d0b37a06ffbf77873f15fc0cd091a1e4102a8b8bf5507919453759e744391b04d",
@ -177,161 +174,158 @@ proc test(
let qOK = Q.fromHex(Qx0, Qx1, Qy0, Qy1)
let exponent = BigInt[EC.F.C.getCurveOrderBitwidth()].fromHex(scalar)
var exponentCanonical: array[(exponent.bits+7) div 8, byte]
exponentCanonical.exportRawUint(exponent, bigEndian)
var
impl = P
reference = P
endo = P
scratchSpace: array[1 shl 4, EC]
impl.scalarMulGeneric(exponentCanonical, scratchSpace)
reference.unsafe_ECmul_double_add(exponentCanonical)
# endo.scalarMulGLV(exponent) # TODO GLV+GLS on G2
impl.scalarMulGeneric(exponent)
reference.unsafe_ECmul_double_add(exponent)
endo.scalarMulEndo(exponent)
doAssert: bool(Q == reference)
doAssert: bool(Q == impl)
# doAssert: bool(Q == endo)
doAssert: bool(Q == endo)
suite "Scalar Multiplication G2: BLS12-381 implementation vs SageMath" & " [" & $WordBitwidth & "-bit mode]":
# Generated via sage sage/testgen_bls12_381.sage
test(
id = 0,
EC = ECP_SWei_Proj[Fp2[BLS12_381]],
Px0 = "10fbddd49246ac4b0faa489e3474507ebc96a5da194b2f7a706fad6bf8435021e1598700088abfe0ae7343296c1b7f52",
Px1 = "324102fa5bd71d9048c3c6a6c62d1f35195d7067bf00dc5eaedd14eecc688383446aba4e8fda059d3f619f00be7890",
Py0 = "f3e974aafa7a3fb3a1209f3af4492c9d9c52f1ae738e1e08309dd0f438f131f8ddd8b934eb8ff2cb078b8c524c11fab",
Py1 = "15e75704edffe7b975cf1d3f27f1dceb89d02e5660650195e0288e5d26c5e9087c241d1bd3263c991d10e2695d1611f1",
scalar = "1f7bef2a74f3bf8ac0225a9edfa514bb5666b15e7be3e929059f2ef75f0035a6",
Qx0 = "1328e4ac12458f9e0c22e925d2fc5593eaf0c126154b484de986895030f830262f54493edcc26aa39c5ab8e714b784b3",
Qx1 = "14d375c704f9187984fe2d64d5517ce7c6eb09981cee68cd48370df21d7f4d0b19431347be3b9eae9d46605cb229e293",
Qy0 = "1580cb4fceea4e71e222945825d4352c97a02d3118ffbd0e006e467b1ff6d4207acd2bb58a197894e9870cbd1bfb369c",
Qy1 = "5df16d4223c040dffcd1912359cfae1f3a99e7f519b2aedaaeb6d77115c63acf309f9effc69d4d9a0d7de420dbf9f1a"
)
test(
id = 1,
EC = ECP_SWei_Proj[Fp2[BLS12_381]],
Px0 = "989f16bcb9da60ef72383e6134ba194f57e30109806304336c0c995e2857ed20bf5b6e03d6fe1424332e9c666cbd10a",
Px1 = "16692643cb5e7466e3730d3ea775c7741ac34d670b3be685761a7d6ab722a2673ce374ddab87b7c4d2675ba2199f9121",
Py0 = "931e416488bef7cb4a053e4bd86ef44818bc03a5be5b04606b2a4dc1d139a3a452f5f7172f24eeaad84702b73b155bb",
Py1 = "192c3e2a6619473216b7bb2447448cdbeb9f7e3c9486b0a05aadf6dcd91d7cb275a5d84c1a362628efffbc8711a62a67",
scalar = "1f7bef2a74f3bf8ac0225a9edfa514bb5666b15e7be3e929059f2ef75f0035a6",
Qx0 = "110c9c96525cccb2c9045ab5bc1d2c5629b06e225cd63802d2f81c7ee7920483a74653e44289dfe5e69b979b049badad",
Qx1 = "189cb0e86e6b8886911107969afc8e807f9abbe20ab7063bddefa2fbfc85bc4e851598daeada33b8e1be6dd430fb9c0c",
Qy0 = "91d2176e98a4122466ac0d6c19c6de17b5b27f30dd760b8f644b17291f050ccfe8c1d47462052e95fd9a7aca90a78e1",
Qy1 = "25c5b7fe0fbf854f5e3af9146dfc706113de79eee220ede03491fcbadb47a89ee7179b6db8b06619e0369ec2a2e80fd"
Px0 = "4b472ab5d0995a22c95f0805eb459b147d2033b8c13c1c07c857a97e66952d4df3e3b5f346997cc7bd19886492fae83",
Px1 = "12228345592511c7327176258097c70dffad1ff53b37163cbd4747d0085ed0bcfe90b9150d2f7e49580a42110b1d9c6b",
Py0 = "19220ed4e423d3274a8e9a58624b8762d7831d6f65fcaf6718b933bf77a0c41d3bb713a2224dbc448cfc735101a5bb1e",
Py1 = "1d4c7565e4831130ae0ddd95aaad4033e57daab2518b3f0d934c7abc7db8614adbf39beaef5ebb85d34f5add8a6d341",
scalar = "b500f1fa8ffa8d1c0aa7d65054a9aaa0d9ed2fff83b40516def10b03cc80026",
Qx0 = "aaf3fc4e3c92df5b76863d64d19d26213757173c1a8ef4beddbee7c22f5a6a00418527f83c9ab5b17797cd2212d3d73",
Qx1 = "1643ce8e3a0f0997e949c04cb28aa77b7184033dd0056caee2488311e0a3567a0cd9d051ee531d6a83be8ac516c3f075",
Qy0 = "12923eb157d8ee0a940e100408250beded20e4185cbec484431f705ab605c452122c66d6f08e1b06305677dc872a024f",
Qy1 = "460b65406a6e3935dfd75977b12c02971d2673b9c23b032494e6684e08727f35da0407be6708e98cd309ff0c09be00d"
)
test(
id = 2,
EC = ECP_SWei_Proj[Fp2[BLS12_381]],
Px0 = "1677db8e7f654fa225ad1fae79177a9ebf72dd2c1c731a4abd0ab80b2225bf0bb99cc998bc43dd3b1f75f8ff977111ba",
Px1 = "b1b3cd03ed2e49475c371fbe24e64bd97a89b28a4e7103c0203951e12763497d1c1d142de1a018fe5bd7edb57b9a88f",
Py0 = "a8ec2afc4424099cef745c6636a9db54ed11f83e98a3e7537349bea1146733d0d2d223e09492912e9b866c88c76a6b7",
Py1 = "33f45142e67aaa076379eb6c09b35509279f8b68d545729c22a51593db1d08ae1874a9b29b85a968af279c752f5b73a",
scalar = "b500f1fa8ffa8d1c0aa7d65054a9aaa0d9ed2fff83b40516def10b03cc80026",
Qx0 = "75d6b24552b5d3ae5937d17717c492160a32b01245e1351caca5e8ab9f973b356bdc1776c06c86d50924574cbcbcd04",
Qx1 = "c35b4d49b562727eda2212c6740a5a0c39343c213338c97cc9a9b3cf75624e45fb02830863f32cea6d37ddcea3dcabb",
Qy0 = "9c4107fce5315f306a70658e74f470b86ac863314efd118dca3c0c4ddd7391a5eef6999d024d927b1b1c690205a171d",
Qy1 = "124fc330b4e7d361c4c90d9e39933b3c635e6a5d7a98e73ace3a2c55cc6360e5e80b5828b3fb17aaba4bd18da5316a7a"
Px0 = "11c9f03cd130f7b4d6675b902d9b4dddfa41577b7673c31a508760675ca083abedfe3f6c1c69eb46737d4877adb527c6",
Px1 = "c64be8d22d478378784c4f38e386635a8ab606d2b35101ebecfe97b3bb5132d26e9a7ea9690d07a78a22f458045a8c5",
Py0 = "6253c05b48fde95024644efd87cdf0cf15414c36c35625e383ea7b5ab839eaa783563918cd9e5e391ef1512a6ac28e0",
Py1 = "2d214172d2a0326ed45b60945c424ac30f416fa8c6e11f243034a9de26f4aaa69c0d4cc8405227f26c6ee4085ea5bd4",
scalar = "3638a1f09b542c9c14706bddf9bd411747489f3d398a5c286d28f3a950e33406",
Qx0 = "a6e22837968f191d848297f60b511acf4cc375e53161e7869b8d98455375d8ee69367513b3439b6c4ee66f9232badbb",
Qx1 = "10f95cc0c70943519c30c04a8625bb56c1656da634018b1e6e35786b610785f41978983c61e78d4be003074d7fd76660",
Qy0 = "bae732b1dd39cb84c7e0649ed641f9d275bfc45721e3ac35d6cd35faa356235ce0b69fa0f2882d6d2762ed8846368d6",
Qy1 = "bcab3bee59706e0f8c381d2a15a55ce6b2c38d1639b43852af2a62f1366d1cdb8433240cbb237750f2f3e0435b23141"
)
test(
id = 3,
EC = ECP_SWei_Proj[Fp2[BLS12_381]],
Px0 = "55d8f0f0af28084eb7050b403b1d10513931ca3245c047f21c51f9648b7573237f75c8617750cf8d3aea96b858893ad",
Px1 = "125272cdaeac32cb5f255c3ef073630ae80c43c3413e8a22ede153368b44b1e8cb98100304ebc621c49096b26f6f9c11",
Py0 = "67dce3c5bf2a9ff812436f25920d207f7b32b6392ea5b2288f759c813486ff0b97f95bbdb2a6efb487bc369ee1f4a4",
Py1 = "118ded28f46124289428802b5d97f7445d7efdd74db9b3a89597718c34b6433916110c86aa025a45d7e34e056ecad1dc",
scalar = "3638a1f09b542c9c14706bddf9bd411747489f3d398a5c286d28f3a950e33406",
Qx0 = "8c045276467c6ecbc26816d7639f196cdd129d7a7e8cee8d3c787aabc95959360b2e2f9f8f63e2db8b76ed228fb6e83",
Qx1 = "181b41e88accd20a6a05deef15997b14c1cdd17ad0ac75ce0f95e48b773095853aee0808f8fb4dd5c29757656c521232",
Qy0 = "178b8034e50e6bffbbac00717d7ef96f027e1c0e07b850174e20034c3a89f0e1f41108449f2c3e7be0d637936f84202a",
Qy1 = "19dae3659e65fcae08b2fd620178b4d78e9099a78f5fc9db941a9456de9eed2e4a208570adc296b43e4297b238aadbf9"
Px0 = "5adc112fb04bf4ca642d5a7d7343ccd6b93546442d2fff5b9d32c15e456d54884cba49dd7f94ce4ddaad4018e55d0f2",
Px1 = "5d1c5bbf5d7a833dc76ba206bfa99c281fc37941be050e18f8c6d267b2376b3634d8ad6eb951e52a6d096315abd17d6",
Py0 = "15a959e54981fab9ac3c6f5bfd6fb60a50a916bd43d96a09922a54309b84812736581bfa728670cba864b08b9e391bb9",
Py1 = "f5d6d74f1dd3d9c07451340b8f6990fe93a28fe5e176564eb920bf17eb02df8b6f1e626eda5542ff415f89d51943001",
scalar = "6448f296d9b1a8d81319a0b789df04c587c6165776ccf39f50a354204aabe0da",
Qx0 = "689e9532c287233d12c3d7196361a06479ecb762a8542f6ba4003a5863d5731f3a0cd3fe4e1405aa3c6f9bd87c08b26",
Qx1 = "b74b46e87a05b7413282d63d63a2b1eeb23e8c482fa8097623888d4be2b90c7cdb49fcb93c74f2b98da15aa9382e285",
Qy0 = "44250df4c1e38bfd7fef6c61649afe7ab5d5252b98589d71f1a64efac5ae8ecf0ccafa173acaafc30a0b2a1616b08e4",
Qy1 = "173b324bb2e0596d0ab560c2a447a1867fea87c5712bd06f2503385b0e145509036c119440973396fc263a02838c8433"
)
test(
id = 4,
EC = ECP_SWei_Proj[Fp2[BLS12_381]],
Px0 = "13b3476e05a61f9c273046d68dc0aff412695115189caa49964c3d794fb99dd153ec357bb553150ce7a3d6a5d3fa89fb",
Px1 = "3453cfc3fff2d649db5141e76b0248acd9e1593138b44e5e4da516a3c0ee5f1cef8bbc2611fa8969eaa5bb3534544ed",
Py0 = "1054487252835dfc8ce3e8845ab420e6b746353f70e2447e32d92b6816f94ad2e245d3484cf2fb984e6e3f7c8c786d07",
Py1 = "14c4f0b21d5381d7c50f8b02f5bad3af23b76729fe12b42b33bdfae87cb0fa452a447096a29c9ca8f951d24f02d9fdd7",
scalar = "6448f296d9b1a8d81319a0b789df04c587c6165776ccf39f50a354204aabe0da",
Qx0 = "15201587a5a6644f04c8818e70da24f8ed94403bc8f69e4a26923646c86faaea7e5538079ad870e4928484f682101e49",
Qx1 = "17eec094b4ffa3b28e2a07069107b1ca3efb458d09fa719baf7229d1ed30cd137ca7c1897f84b4ba26aac99b7a5628fe",
Qy0 = "19d32d85db24064e48ee7d1348df69e4f409f8f43e34f9d2f37077929975199049bcc5a1cf86358f539f034d14c882f2",
Qy1 = "122226ac41a6f2de2347a59657d707092c6becb7fc4991695631cfa9f2caaed9cee9701562fb99c2a85bd56122305bd3"
Px0 = "99f8b62e82892d323847f64ab2b422478d207b780fbb097cdca6a1a89e70ff09213dee8534eaf63dac9f7f7feff2548",
Px1 = "12e7b53d802fa9cd897b5470614d57b1620bfe53f36158466f83e7cc6a6cccb1ac7557a8d5a2208c7c1366835c2cba59",
Py0 = "115d6b7a8bc5628690ec750207b3252d4121c20c2106d0277cd41dee7b1d4ed1ff856883719bccb545054b9a745a53e2",
Py1 = "4b55174d273b358a72687d52956af3e94d97db8d2cc508b2a4ec5b0c0b4073b8fcc52eadaea35e3eae9a441b3f86cbc",
scalar = "150caebc321c53c0658c5cecb45e564620b57bfbad0f5d5a277be71a184937b7",
Qx0 = "3f474d7fa1ff31949c7b61f1ff71a7ccd282201ef88ef12fcd1fd3fef6a3ca18d8bb31fa7e9f4abc713f37e02abef3",
Qx1 = "cb94daed6079cbe5a628ae4c27e8ad31a17f14e68050e8ce1b03d5a3c0e6a6cc5a34b3115034349b2ebe8de6a2c441e",
Qy0 = "15b09aac63ac527ad719ee2bcebda6bb646044d9060c4f72280d41186798912b6e29b2f7782744b8cdce927a9c1b9340",
Qy1 = "5204820d6336aade860fb1cb983bcc66e10cfb352ea645b4cdf74e643e9fdf545609bd7181a67daac891f551a6ce566"
)
test(
id = 5,
EC = ECP_SWei_Proj[Fp2[BLS12_381]],
Px0 = "a16c8f0c124b0a76e53900b8e1b0cb0aa7b4280656ecebbd4cfdd8a09a40a06be8844081b3d359791391d7985a73078",
Px1 = "a639f83aa058b7a2f8e8c6272c1d03d72d37061b9586e965dd3cf2839baf2575caa276871aa8263cb56a27fc02c209d",
Py0 = "1c722e4e7e4784327296624871326f84de14e034f6a523eaaaa6e4352e22b9956ce2fede2d771a4d29b13d24742850b",
Py1 = "109fe8ea84c839a4877120495cef7c054f37192fd87b4ff44ec9581230904ec7e4b90dbd767040c74137ab9963d005b6",
scalar = "150caebc321c53c0658c5cecb45e564620b57bfbad0f5d5a277be71a184937b7",
Qx0 = "121d86aa00cec80f3e00437247902d97ef5d4867d3ae725170c1f29a9f2837c07f0fe778a9ff30f8911b84b3203cb8c2",
Qx1 = "f08808972239bde0fd4b8d498a92d08fa1d5f5b87d8a3f5f5b2c9a467745784e01defc5b55325f5707c532639b81d4c",
Qy0 = "19b484b4a366edfb6e7c3fde7e607928ed0b8a138568bbcdfd55bce47baf070759229df7a7a2b386693f23a4664cb478",
Qy1 = "92527a036c8ce2f4a592681a2f35db20917b5709c42da7c6e987c8f592837a3dfbc596667d033c8a313ac1ac28be8e1"
Px0 = "7f56aa6111f341d6381090f058835b3d60200032b382108194188c40afe4225eb6fecaba734084283771923e004b5ca",
Px1 = "18abca4d9eca6d3ef3c720ca6b27f824fdd12dcac72c167f0212f707fa22752f291c9c20a4b92417d05c64207b8e6da6",
Py0 = "10e08fc323d2ef92c5fd9a0ba38e32e16068ac5a4a0f95b0390c2e8ad6caa446adebe16bbf628a0c2de007bfa1218317",
Py1 = "6394379cc76d50b41865b619007de5a7cda3bb7ae6fc696bf2f83e2de562039dab890a9e2b4d61045bac606f224ba42",
scalar = "138ecc47a9d5b6cf2a052731b8f016734614949862a9f2be703935a5e0cd43bd",
Qx0 = "19ec61da69ffdbf79411f041276d4e9fe02df31caa79406135a7522a12965364bb3549b7310393208082465feca7c9eb",
Qx1 = "457ddcc53f5338d34713a91ca4298896bfa7ede3b939cd1c8b320a4c9ba4c1038ac09740e5b428f5c15bdf18ca6af0b",
Qy0 = "9e334e01a81dc6da2004d2f13eec7c15ba0ab8fb8a9628ce24a0de073b251f955ee758f5b0a298c4fadd641610c7fe0",
Qy1 = "935bf9c37ed2ce17b9e5f8014816d8a1a3d8debfc72e39ea9121018f3a990561cfbc64c25621d16a921f458d7f40344"
)
test(
id = 6,
EC = ECP_SWei_Proj[Fp2[BLS12_381]],
Px0 = "9315bb59e19f21e198f30d32a876b0f922ac4c444977f4ca0a6f8f07f7a6c6353d197ff0213cb835d2685675e931250",
Px1 = "4c3ce432c9e6f542d0a54593eeccfe923f679e448fd81b98aa28f542b5cef88ea4c822ddc8f78fb64a2e9695ea7c85",
Py0 = "e0027be110ad0f7e1d2415fffb1ded7567cb37f106c9ad5eced2aa38b9e44742c8cb5f3b1c73d935bb51a86df733ac8",
Py1 = "1275ba7b78e3063c0efe7b9a9ce5b64c713530071c52da0ec8496f47cf8546f83e50faddea736a1d495ee324ec2c290b",
scalar = "138ecc47a9d5b6cf2a052731b8f016734614949862a9f2be703935a5e0cd43bd",
Qx0 = "7a7508613526ef10773c9bb6b61310647a0401988ca80bb116748e7db49148db455c5032abc985545d8e082f3445e72",
Qx1 = "15b0a9ebfb75288e5efd5defc731bbff2d90722dfd6560284078d2886fd2254e27329be5cbe85276dcfa653f2c768337",
Qy0 = "134961d307616db7353ceb6ad61dee0fb579ebfc4256dccc8c0da35af93db90a84f87cc8cb8c14a8cb524e3dbc8efa46",
Qy1 = "d672894cc2a42dab3fc420de576c96f3585eb77e037fc3b0b2cc74c6a0e4a197b8a55dc525022289f6cad381c9adadf"
Px0 = "a8c5649d2df1bae84fd9e8bfcde5113937b3acea22d67ddfedaf1fb8de8c1ef4c70591cf505c24c31e54020c2c510c3",
Px1 = "a0553f98229a6a067489c3ee204161c11e96f421b3e9c145dc3865b03e9d4ff6cab14c5b5308ecd31173f954463690c",
Py0 = "b29d8dfe18dc41b4826c3a102c1bf8f306cb42433cc36ee38080f47a324c02a678f9daed0a2bc577c18b9865de029f0",
Py1 = "558cdabf11e37c5c5e8abd668bbdd71bb3f07f320948ccaac8a207359fffe38424bfd9b1ef1d24b28b2fbb9f76faff1",
scalar = "5668a2332db27199dcfb7cbdfca6317c2ff128db26d7df68483e0a095ec8e88f",
Qx0 = "490e99b9a27cb49b9446a7bedb8c22ef802cfdc3609cbecd8de4e227fdb72aeafb27c53d74361008ce9ea806e25dc85",
Qx1 = "1624f7ed9ca1fcfda7651608be2acb1d76cb37ab989c1aecf06a6401ee66afdddf283039496c2320dca4d720e8b8a337",
Qy0 = "c6fef37a864fa1602824a2128d4a62e3221413e1ded862f11347576f43c27d69b1957385ad6ba7a9168c05e6fc2ab85",
Qy1 = "11d4a696f8e366929fe0b7eae94702a89aef43725218f95e0bfe7d7abd726e884604838e4d7d670c9579431f9d8012e0"
)
test(
id = 7,
EC = ECP_SWei_Proj[Fp2[BLS12_381]],
Px0 = "17d02c0921a613211e805a053056f3c32ca517c60220f2a71ff5fb4db0a2645b2fc75edbcc224ec6cd0935074e1a665",
Px1 = "3ca6158f4cff81eec3a4ce63c3250f6e7dffb9657d7d6b12c49e011ba983e425a9543a4cfda9bf27e57237d7ef2aa28",
Py0 = "11997fbd203fd972944490bec60ddb023f16e9895fffb93548ad284b3593dd70bcfd6f1efe665f30efd102339f6e59b6",
Py1 = "84336a1eb009f9173d1c4d2d74b3df6bdd05194208a81a068f1296d26049974014d8b39f5d66ea97ed8cffe177bcfe1",
scalar = "5668a2332db27199dcfb7cbdfca6317c2ff128db26d7df68483e0a095ec8e88f",
Qx0 = "1690a2bd0da82c969a43f71ec908921a65b87459eb516e16f27bc43118acb1b5bb5b91c9eeca7afacb4aa2ff5097160f",
Qx1 = "16ae56ed58e06176173246ecb865f797219f420675e49a789979be0a032108e159fa72a885222f18a4142ee808cab4e1",
Qy0 = "a44729556da9f1d4adf297e4f4b8de53274bf2d42257ea387185ea245bdabc7fc554b866c14b5100dcd0faf4ddf4079",
Qy1 = "e94f2652d0bc97087a04e4d9ab638f8b43e99b214f120d08ebb64a401ad6ab1c538d1524540fc37c76601ecc3425529"
Px0 = "eb79d9e425feb105ec09ce60949721b12dac5e6721c8a6b505aa2d83270d2a3e6c6bcce16a1b510f6822504c5e86416",
Px1 = "9f5d2dc403a2e96c3f59c7bb98a36cc8be68500510fd88b09f55938efd192d9653f4bcfd1451518c535e9d1996a924",
Py0 = "114825899129828ee0b946811ff98a79af1b53c4511bc45a8e41a07a7d9600c824ed7c5cd608781d0a98a13e69b0c002",
Py1 = "57600cfce779277faf31d1b18a39c752c179a76b301cbdc317263c7e8770df0d5896c9dec84958bf558c16b5ec5869c",
scalar = "45b4bca2f783bba42b2d41a8d00f4c5f18a78738a5678fc3707523e7c62dafcb",
Qx0 = "71bee2c58caace914434607aff9c1730af2da8bd78d1a44215d9c7cc422623e9b65ba1fc7207de9d0af96f8211eda2b",
Qx1 = "80ea7a46f9352599f67bbbed1253b142422cb79af32b1a544e77ff02c25057396569182341e5492c4a520a756568a41",
Qy0 = "73470439d1202c87e54d9de94121f5e36371fe527eca699f48caba867111707a250b2b0357e79a8263babb907c9f43a",
Qy1 = "cf2f2cca752462c5fc2827b7871d517e50d7ecd819e0b9419f9b1e357874acca15c8f0a1f611aa243b9d21ce1962d3a"
)
test(
id = 8,
EC = ECP_SWei_Proj[Fp2[BLS12_381]],
Px0 = "7fd385cc4200ad11b1983a9399007cec30ea7de4aebaa3d65347f0566634372d89c0744c95639db223c72d994de816",
Px1 = "83ce6c36676aa56e2cd615d5617df16514cdf0d2ca11eea21f429649cf65010acd466c93e51e68088bdc16aef189472",
Py0 = "6407f7f0da8ebf27813e1f1b7339f694d0d134a281aa58855d7d102e2364900b89033904799e3b4d0dcda7946dc9f5a",
Py1 = "14d1947a7c6ce9e4f146637ba5cab3aa7a50579288f2df221dce1c0ee930c48a9f164b405b72b44c45c971608b1b97d",
scalar = "45b4bca2f783bba42b2d41a8d00f4c5f18a78738a5678fc3707523e7c62dafcb",
Qx0 = "b961dddb0fc6b6bdec4119fbfa6c7be6e568fe2ab1ec9d52442ac947f5e99172eef578af5001bcffc293d35b95a5958",
Qx1 = "13f2abd7f650404eaceeff3f834b3b8f20ed0fb05a4962d9e5798cddae5d107231655b61af5fce76403ff80dc7efebf3",
Qy0 = "2e45d6e716abe3daed8dba4fd2c030cecd2a3aadf33902d01492d221ac7704d095857a70397aedea783c0cda1624c4f",
Qy1 = "99785f9572b79e79defdf4cba35e248bd454521227dc7fdaf34d3bfb124a179b219dc69d1521f2c2af9ec0a7b82b0df"
Px0 = "17ce8a849a475599245ad6b84cf5284cd15d6346ae52a833954ec50d5cf7f0be1cd8473fdc9dfd500d7f1d80bf6fa6ca",
Px1 = "15d8128bc60c8e83846bf6748982a7188df6393a9379b2959fa7e1cb72f1c1da066fe3a6d927f97ecec3725fac65eb10",
Py0 = "a05421595b36750e134b91500962201e9f57ac068732b9fb34ec50ff22f274d395d34d133e131e6dc7bc42d66149767",
Py1 = "178cb2541ce0c60b8860d59762daa6a5b55a0ca034aa18b1fcc6deb23aaa093fb2a6129db0d58de044c4bbb23f1fa298",
scalar = "6083671fcc66dc084ad73eba100830555fcfcc5eccaa6acb27cda0d3fa8d6f64",
Qx0 = "175a067a5cca1e18c2c60c1ed84a2d0fdad20ad8e2b9b67a13d8a173d6c097bd2bb8fa6d83ff5ab3668d56d39f34cbe0",
Qx1 = "14bdf8d62a088a6a71e5ea236bd3caa07934da726fc1f0b7ae462272c0778615734fac562a0293ae5da759dcfc78480c",
Qy0 = "59ead353a2a4189dd9dc0d93ac86d0f3f8fd60bf3db10fd77e4295b1dfaeccb28aa481b1efce7198093a7f2e8ba3617",
Qy1 = "a4661ce0260e251df9d2b01b5af29262e019b8ea12b3a0a2c4c125c09cadd906dcd8a25b7876e76892acfd74dfab8c5"
)
test(
id = 9,
EC = ECP_SWei_Proj[Fp2[BLS12_381]],
Px0 = "1874fee7c4d207403afb01b5a2d15260e3eb1edd6ee5c3dff6c36dd652c1c34dd2e387671c8663204667e0334699cd43",
Px1 = "8abd6d2f87e58696a5c1a1d59c852d5a38c4c862f14d5bbf7db139dc7f9d6b19301fdae3c237fe77114b53c2b5a38b0",
Py0 = "14d126e9f035c55dedf47bb9c784e32866effc223fd450b8415c55b1d7b082ebbcd984d5329d3a3ab400955b908447b0",
Py1 = "7d41e011283732bdbc704f90d2f41735a7b17362ca0e0b12ca9eacd6a2f1f478bc09922aa7c3c77ef00a5e975a31bc4",
scalar = "6083671fcc66dc084ad73eba100830555fcfcc5eccaa6acb27cda0d3fa8d6f64",
Qx0 = "9f62bc139137190c2a04b8334679cd8830a6f9a7fde746397129b5518ef61ac907b39a39200834870dab6f579766cc3",
Qx1 = "f24da37709b5949766a6fccb28a316ed885c47be935044367fd7418a672e3193f68cffbf7052a33227faa8e453c5e00",
Qy0 = "410ddc02ae4c5f4cd264252a3f3ee720c9cfce1dd1b844076fa9fa55e4f9802a95e8fd130010b6d9a5ed6011239e9ce",
Qy1 = "15004542a02d1439838bdb579cba1a4f3258b14d998e4c3e198b9e27c5799c8e1b5a059f731d6380510bb9ff4d6bce00"
)
test(
id = 10,
EC = ECP_SWei_Proj[Fp2[BLS12_381]],
Px0 = "171597b604848dac1eea6908b849ed35ab1ea768683ff4cbf299c64da208953c7b95db0e7a514a31b3ea64ffd6bd634",
Px1 = "1679d3ec65d141fd38c8eeffdce9200d250f38e1dd3a714685d4b01ae677d7ac5c7dd452c768cdfc55249e8352c9f27e",
Py0 = "c16d445bf20a50baa06f0dba778a013e02c3c619a8b434ec463f79a9c5ec1a069c66bded745420bc5ebccc46ba0cc3e",
Py1 = "158281e1c97208e3d1388bf85b96fe75072b9ff5f628cb84e8f837ad587b414be3900be35e4df46fd0b4d517ed73a144",
Px0 = "13eb9e9906446be49345c08de406cd104d6bb9901ee12af0b80a8351027152d6f6d158d5a906e4a58c5602e97347cfd5",
Px1 = "1218df3f2a9cd7685325a4a7bb6a3636a458a52ea7f1e1d73c2429acb74a2a9beb838c109541120b095118c90868eb0f",
Py0 = "3ac16edac6898f11ff8ddb48fad6f59f4842cd427d72fa964171801be172b8ecd2fdffb4882d4aa6f1e730f6e53f8c5",
Py1 = "b2d251295859b1be1854b1db06eae2ff8e3879d8ba5d9f86f4bb805c65696b48d0771cf10150983e322bdf9eb659af1",
scalar = "644dc62869683f0c93f38eaef2ba6912569dc91ec2806e46b4a3dd6a4421dad1",
Qx0 = "e4b186929f609e5d36451d6b62736c0bd7d4e6ed9d7ed14d33cfd715cec150e2f27224756d9f70cf78e72bb41b0ee12",
Qx1 = "1290bc5038c3ed9ed393e27edc9202751c3533ad2320802f951573f0d6da960572ce57481ab4bcf18c1525e52fd4f4e1",
Qy0 = "46260daa3ab7f1a855223127450b1963bb594930b59fd627b17275aca76533e9aab01253b5b9d5be2ac18eafbe39ea8",
Qy1 = "8b2a1941c24f3c0d7a9920f14d4e3066ed0d36839d524494db34e31f386b2523b33dfe1455e04018ef46e44e2ee6886"
Qx0 = "1888f703e7525e4ac29788eb6e3afde14e4c8b36f74a3058ab7e991630cdb8332fb164766db6d14186ac7fa2e593f513",
Qx1 = "1496076ec35db3760a3cfe22a1759b01ac89b7ae21ccb9c4d7553a7881f4a610ef88a56b2e5a027ab49507fd710dd9ea",
Qy0 = "1357e8db4a105bf81a94e9b9130a892b1ec78d564f77b2717451cce777c16a409cd19f450247c75882f1b84678d7c46d",
Qy1 = "14dd91161426cd5d831914706ee9f427512a789f4953f82538f3fb17553840eae31c992de2ed91a6695d291b5ef4c204"
)

View File

@ -37,19 +37,16 @@ proc test(
let qOK = Q.fromHex(Qx, Qy)
let exponent = BigInt[EC.F.C.getCurveOrderBitwidth()].fromHex(scalar)
var exponentCanonical: array[(exponent.bits+7) div 8, byte]
exponentCanonical.exportRawUint(exponent, bigEndian)
var
impl = P
reference = P
endo = P
endoW = P
scratchSpace: array[1 shl 4, EC]
impl.scalarMulGeneric(exponentCanonical, scratchSpace)
reference.unsafe_ECmul_double_add(exponentCanonical)
endo.scalarMulGLV(exponent)
impl.scalarMulGeneric(exponent)
reference.unsafe_ECmul_double_add(exponent)
endo.scalarMulEndo(exponent)
endoW.scalarMulGLV_m2w2(exponent)
doAssert: bool(Q == reference)
@ -60,7 +57,7 @@ proc test(
suite "Scalar Multiplication G1: BN254 implementation vs SageMath" & " [" & $WordBitwidth & "-bit mode]":
# Generated via sage sage/testgen_bn254_snarks.sage
test(
id = 1,
id = 0,
EC = ECP_SWei_Proj[Fp[BN254_Snarks]],
Px = "22d3af0f3ee310df7fc1a2a204369ac13eb4a48d969a27fcd2861506b2dc0cd7",
Py = "1c994169687886ccd28dd587c29c307fb3cab55d796d73a5be0bbf9aab69912e",
@ -70,7 +67,7 @@ suite "Scalar Multiplication G1: BN254 implementation vs SageMath" & " [" & $Wor
)
test(
id = 2,
id = 1,
EC = ECP_SWei_Proj[Fp[BN254_Snarks]],
Px = "2724750abe620fce759b6f18729e40f891a514160d477811a44b222372cc4ea3",
Py = "105cdcbe363921790a56bf2696e73642447c60b814827ca4dba86c814912c98a",
@ -80,7 +77,7 @@ suite "Scalar Multiplication G1: BN254 implementation vs SageMath" & " [" & $Wor
)
test(
id = 3,
id = 2,
EC = ECP_SWei_Proj[Fp[BN254_Snarks]],
Px = "39bc19c41835082f86ca046b71875b051575072e4d6a4aeedac31eee34b07df",
Py = "1fdbf42fc20421e1e775fd93ed1888d614f7e39067e7443f21b6a4817481c346",
@ -90,7 +87,7 @@ suite "Scalar Multiplication G1: BN254 implementation vs SageMath" & " [" & $Wor
)
test(
id = 4,
id = 3,
EC = ECP_SWei_Proj[Fp[BN254_Snarks]],
Px = "157a3e1ff9dabccced9746e19855a9438098be6d734f07d1c069aa1bd05b8d87",
Py = "1c96bf3e48bc1a6635d93d4f1302a0eba39bd907c5d861f2a9d0c714ee60f04d",
@ -100,7 +97,7 @@ suite "Scalar Multiplication G1: BN254 implementation vs SageMath" & " [" & $Wor
)
test(
id = 5,
id = 4,
EC = ECP_SWei_Proj[Fp[BN254_Snarks]],
Px = "2f260967d4cd5d15f98c0a0a9d5abaae0c70d3b8d83e1e884586cd6ece395fe7",
Py = "2a102c7aebdfaa999d5a99984148ada142f72f5d4158c10368a2e13dded886f6",
@ -110,7 +107,7 @@ suite "Scalar Multiplication G1: BN254 implementation vs SageMath" & " [" & $Wor
)
test(
id = 6,
id = 5,
EC = ECP_SWei_Proj[Fp[BN254_Snarks]],
Px = "1b4ccef57f4411360a02b8228e4251896c9492ff93a69ba3720da0cd46a04e83",
Py = "1fabcb215bd7c06ead2e6b0167497efc2cdd3dbacf69bcb0244142fd63c1e405",
@ -120,7 +117,7 @@ suite "Scalar Multiplication G1: BN254 implementation vs SageMath" & " [" & $Wor
)
test(
id = 7,
id = 6,
EC = ECP_SWei_Proj[Fp[BN254_Snarks]],
Px = "2807c88d6759280d6bd83a54d349a533d1a66dc32f72cab8114ab707f10e829b",
Py = "dbf0d486aeed3d303880f324faa2605aa0219e35661bc88150470c7df1c0b61",
@ -130,7 +127,7 @@ suite "Scalar Multiplication G1: BN254 implementation vs SageMath" & " [" & $Wor
)
test(
id = 8,
id = 7,
EC = ECP_SWei_Proj[Fp[BN254_Snarks]],
Px = "2754a174a33a55f2a31573767e9bf5381b47dca1cbebc8b68dd4df58b3f1cc2",
Py = "f222f59c8893ad87c581dacb3f8b6e7c20e7a13bc5fb6e24262a3436d663b1",
@ -140,7 +137,7 @@ suite "Scalar Multiplication G1: BN254 implementation vs SageMath" & " [" & $Wor
)
test(
id = 9,
id = 8,
EC = ECP_SWei_Proj[Fp[BN254_Snarks]],
Px = "273bf6c679d8e880034590d16c007bbabc6c65ed870a263b5d1ce7375c18fd7",
Py = "2904086cb9e33657999229b082558a74c19b2b619a0499afb2e21d804d8598ee",
@ -150,7 +147,7 @@ suite "Scalar Multiplication G1: BN254 implementation vs SageMath" & " [" & $Wor
)
test(
id = 10,
id = 9,
EC = ECP_SWei_Proj[Fp[BN254_Snarks]],
Px = "ec892c09a5f1c68c1bfec7780a1ebd279739383f2698eeefbba745b3e717fd5",
Py = "23d273a1b9750fe1d4ebd4b7c25f4a8d7d94f6662c436305cca8ff2cdbd3f736",
@ -183,154 +180,153 @@ proc test(
impl = P
reference = P
endo = P
scratchSpace: array[1 shl 4, EC]
impl.scalarMulGeneric(exponentCanonical, scratchSpace)
reference.unsafe_ECmul_double_add(exponentCanonical)
# endo.scalarMulGLV(exponent) # TODO GLV+GLS on G2
impl.scalarMulGeneric(exponent)
reference.unsafe_ECmul_double_add(exponent)
endo.scalarMulEndo(exponent)
doAssert: bool(Q == reference)
doAssert: bool(Q == impl)
# doAssert: bool(Q == endo)
doAssert: bool(Q == endo)
suite "Scalar Multiplication G2: BN254 implementation vs SageMath" & " [" & $WordBitwidth & "-bit mode]":
# Generated via sage sage/testgen_bn254_snarks.sage
test(
id = 0,
EC = ECP_SWei_Proj[Fp2[BN254_Snarks]],
Px0 = "1dcee2242ae85da43d02d38032b85836660f9a0a8777ab66c84ffbbde3ac3b25",
Px1 = "1e2eb4c305e3b6c36a4081888b7a953eb44804b8b5120306331f8c89a3bb950",
Py0 = "1db75f495edd522cae161ceeb86ca466ca2efd80ef979028d7aa39679de675fd",
Py1 = "b1b6edeb6a7689595098a58a916657dcc09f53f5fc1a1a64b34a2b80447692e",
scalar = "3075e23caee5579e5c96f1ca7b206862c2cf3ce21d79182d58b140074b7bd34",
Qx0 = "8d63bb4368f94f1629f33ef0c970b3a6fcec6979e423f54ce657f0493c08fde",
Qx1 = "29d1af77bb890dcb27e685198f23dffd5ae5733bd6dd55757dcb44ce8c396742",
Qy0 = "13c24efab7e517c2337ba9cbb8cfb2166666a44550fc4d314f4c81a4812fb8a",
Qy1 = "c3ad1f7a175fa21e1d18595f8fc793688e1a33feda902805a52569ea7a787bb"
)
test(
id = 1,
EC = ECP_SWei_Proj[Fp2[BN254_Snarks]],
Px0 = "d4ff42fc6d0febc88c9e1bc568d72c80c58438f6295dc598d798c1285f974ed",
Px1 = "3845ad0148f76bdf14f752268eafb065c7272721784a8c6bd3a5fa736332b94",
Py0 = "13fea1d73f8e06ea57a110f9156a8c876ba42251c7dcf9f203f90839bea3e462",
Py1 = "1b722e9557c77e1a74a2ad7236b9b0194dbf80a5c03021ce55649e3082c0cbaf",
scalar = "3075e23caee5579e5c96f1ca7b206862c2cf3ce21d79182d58b140074b7bd34",
Qx0 = "1811e020b970e8c87c63acc020a27e99e97236f9dd01475ece959fb679c3e2d",
Qx1 = "2e7c501387b25ab6fc9b45c8e0944d9685364f5c448b954f370ac80751a25de5",
Qy0 = "8d73969c1c49878b450c829a7574d7df69fb0f44f158f1a84a8dda940453f30",
Qy1 = "14eda105095dd606285a3b7a2aa9bd269b9193c22726d5a4d8708e02ae217807"
Px0 = "5ed8c937273562944e0f1ebfb40e6511202188c1cabf588ed38735016d37b32",
Px1 = "23f75e8322c4b540cd5b8fd144a89ab5206a040f498b7b59385770bc841cf012",
Py0 = "2150beef17f5c22a65a4129390f47eece8f0c7d0c516790ea2632e7fd594ed8",
Py1 = "78281fb396e2b923453fae943af96b57e8b283fc40b0be4be3a1750d0daf121",
scalar = "1eac341ad699cba0cb13ae35b8215bfe0f34e931f8e51e33bf90d9849767bb",
Qx0 = "1bb6e8c1be4d9da9ef294ab066c82bb6dd805efa0c73f289e25f5cc6fc4f12e4",
Qx1 = "8ca44ff91e6484ecadc2a866ec64031e71c4a9d7a902f4280bef3db4dbf1bc9",
Qy0 = "39d151a22c49d4c71c8258e9664ead46ddccd49c596056509d9f9e6055def62",
Qy1 = "b37a08b453b96880f435d9fb2555960571a76e72a9d0d0ec622b055b7c97cdb"
)
test(
id = 2,
EC = ECP_SWei_Proj[Fp2[BN254_Snarks]],
Px0 = "c4bff378e0e78a9094bc8cb224ad7d89266c28d1289098f03226fa84e7905b0",
Px1 = "208afbcdfa4243045ad02aea93f275c60e9f838d6a933e9ad5732235e93cec84",
Py0 = "178fd343e358c869df8c3b2e2e90c68cb2352c1ce6a6e51516a2ccab5bc191e3",
Py1 = "23d122142470d7a5b9a9b456dcd1898ab5130f2274e010a67c0b59d8a06c98a3",
scalar = "1eac341ad699cba0cb13ae35b8215bfe0f34e931f8e51e33bf90d9849767bb",
Qx0 = "7d2b09ccebc6ea3ab685a2938c3b594bd1e500eb2ab2a4e0337e7f6587026fb",
Qx1 = "ac5a99b924aebdbe4ff277ff5c8e1a209059c646fcac221917fbcbf738039ca",
Qy0 = "2be1aebafff712ffd677fe1ac78eb2e838fe3bfc0051afb4e1b446b9aecb5939",
Qy1 = "16bf0803d6e1d68be0e3e10d25e358e1f89a28c211cdc61def5ef10ea3abec94"
Px0 = "2ac4b3d0a70d6686c63d9d63cce1370eafd4dc67f616c13582c6c64f6670513e",
Px1 = "f1daeb6a2581ba6c8027a645ab5c10e303db4aee85c70c8fe11f4c1adcc7029",
Py0 = "25807ff21967759cab64844741d006e2aa0221d9836613b1239da1a167d15131",
Py1 = "148dae725e508dbb43616972e2945f4868b2a193d828ed2efcf9c8a37b6b83f5",
scalar = "b29535765163b5d5f2d01c8fcde010d11f3f0f250a2cc84b8bc13dd9083356c",
Qx0 = "5f4b3a8a5fe74bf3f2ddc0bc2024f18c7958b2846dab1c602b8256a6035361a",
Qx1 = "ba3dad609b1ba8c78cbbfb7fae2d2f9398ef02265e3b4c0f3c8c18d8d0e59d6",
Qy0 = "2c226aee4621895d63df069c4b6951e2201ee1508d5d54e6ee860533b73b534a",
Qy1 = "2aa5384592339bff0a6e4664c931c0ec9f5a3d2fb2fff87a52245c0d95d3d130"
)
test(
id = 3,
EC = ECP_SWei_Proj[Fp2[BN254_Snarks]],
Px0 = "15dd63cf4d0c2d0e21368a72b93f72ed172c413782db489f5d7b4dfcdee061c7",
Px1 = "26e5ca7f4b418fc9eb7d7b7f44ed1c4357fa71695ad59299d4404c55a295d64",
Py0 = "df8c4bcbb5518b1ea51967f69f61b743be8e58bc9b597b398b51ca7820940af",
Py1 = "8a36e75e7058969f4aef0724d9f6317b8b6028870f0e7412baece8073be3477",
scalar = "b29535765163b5d5f2d01c8fcde010d11f3f0f250a2cc84b8bc13dd9083356c",
Qx0 = "1c329c496b4cb95ee511277fd514a07fb98e313c61f256116d9c071ecc9d9a3a",
Qx1 = "11d64f0b3301b18b969f58664801c0de67a295943034e5946b27065ac56581a0",
Qy0 = "54787e9bdec726f06896ed90b12a346a2f92e44688b1663911931cd225a1cf3",
Qy1 = "1303456cc596e033f1f32f2041bd83fabb8566744c0b4a358097270baa734a48"
Px0 = "2a028c1328bb0abf252edfbf7133b84eef2a5f20163fe61685b4b54229ca585d",
Px1 = "8f80ad79e8e7e79bbdc645d9f5b339c52dd99a901b90de2494492656f11a9d5",
Py0 = "1f04320578e31ffa2e2b59ad8fcb1aba622b5f307ac540cf2ccdab07dec56503",
Py1 = "2973900c0fdf651b64f5b1a990baec7c582e0743d501bdb991374776d6c73b28",
scalar = "2c02275a71bb41c911faf48cab4f7ac7fc6672a5c15586185c8cff3203181da0",
Qx0 = "2f39a0772a0bd75db3e6ec0745b18824118e534fdefec811d0a4ca2ca3ce7606",
Qx1 = "23e1601a4338502dbc619a8bde251114272ca87662a851a56035c81d46877d81",
Qy0 = "1f0ee85e7c590124f89319f2450f7b8421d9f6d6414fd3b5cc18d781b69b30c9",
Qy1 = "29e4ff75afecaf732419409a5e0d8e94df6acec29fb5b298c7aad5ceef63e5f9"
)
test(
id = 4,
EC = ECP_SWei_Proj[Fp2[BN254_Snarks]],
Px0 = "9a26b213edf4d6b8b8026e934436d2a99d5cc23a9153abdb101a9bc67ab0b74",
Px1 = "1654d9658fb77c7836ef3b41431282834c348d922d424ec4205cc62599b1cff4",
Py0 = "13359cc29af8ed4d2a8b3acdc2e1c257bb738a365b020075a0cf387fadc9ee96",
Py1 = "16dd9e23d0e5a92a98c57eeb0438412185e602bfb87c464e088933fd418e83fb",
scalar = "2c02275a71bb41c911faf48cab4f7ac7fc6672a5c15586185c8cff3203181da0",
Qx0 = "263a3327dbcd1d29dc43c428f6f03638a146ae40e06974f2a2bdc97c2239adcc",
Qx1 = "21d7f34d76f4b71b3e35138f219af27709c0337d1bcd3a680de34ad191a2ddab",
Qy0 = "1b0f2d2d9be7fc91bec9dad3294159834e506cf0d24d319b8282bfde26aa4268",
Qy1 = "1bceb12af58dd453e801b6036fad5cf63ee511b00c6b8c5cee7bb3846ef3eb05"
Px0 = "1132e63c444e1abce6fc4c39bdf5be5caad586837cbf5ca9d3891482bdefe77",
Px1 = "22b71f598dab789f055fc9669ddf66f0d75f581af0e9e8863d7f95a51ef34862",
Py0 = "58e39050f64c9948d7238b99ecaee947cb934688a6e9f483c5c36b6e07aa31b",
Py1 = "2e64b920f498e12992f2a4ae3f9ced43f3f64705c9008169f3b930a760d055fb",
scalar = "24c5b2ce21615dca82231f5fb0fc8d05aa07c6df4bb5aa7c2381ac7b61a6290c",
Qx0 = "25a0637163a40813529e8a22a3e8be6db96f6dc1cdb8e1844729cad6be11668e",
Qx1 = "16de42461c4db7f9f72eb28bb16da850a42fc153dec64baf021f67f5564f36d8",
Qy0 = "27f2d743d3ce0c1f92c51110a6b9ca93a95693161f1d1cd85a0cf5a2492b4901",
Qy1 = "2c5a8df4fe93e31e374595c0605b1a8b93bb429232cf40f45c847739790c191e"
)
test(
id = 5,
EC = ECP_SWei_Proj[Fp2[BN254_Snarks]],
Px0 = "1a4fb241cdcd2415acca073eaae81ea2e75fbe3122d91a113ee60d6a1f2a882c",
Px1 = "1cfac3eb7f51ef5c90fe33469dd55b0641eaf4597cfde95f01fe8d0c16613599",
Py0 = "112e05efd8fae9654a20c4a53cb31207176bb6ea7c5ed4c8464a9846e4c6bd56",
Py1 = "2b9b15b98d8a2116ffea8886e9399fadf6998f89e2037c423d78c6145beaaed8",
scalar = "24c5b2ce21615dca82231f5fb0fc8d05aa07c6df4bb5aa7c2381ac7b61a6290c",
Qx0 = "27c16e9546b4383b7d7df55ccc33737866e1e9d12d4f5135bcdbc95514bc5b23",
Qx1 = "2e451f8f8f5163dbbd1bf48dce686204511d8cea5bc504a4fcb13d76490589f2",
Qy0 = "1c66b04bb04c139b5a6bd40a2a5b20706620b5b54aa69ffc9075dfe14fbbba70",
Qy1 = "139f9a895e3e68e57a15b0d6cb01c4101317b4554e196f305f88212ce5cef640"
Px0 = "6a20c456e80e2bfe37d8631d41ffed31cba5d1c411e816d64014d0088d16554",
Px1 = "9d1555c77222abd79e17e2806386c53aba9609375276e817f52f03dc3f75676",
Py0 = "127e76f384726e56dfaa46e6fde6dc644f5fd494d056191059e2bebc525ce835",
Py1 = "2d80f2570da62adc61d794ac17c251f9f9f3de2b45f39c8ede5a9e215e60363",
scalar = "263e44e282fe22216cc054a39e40d4e38e71780bdc84563c340bdaaf4562534b",
Qx0 = "6d7e15b98352e445d1bdacb48826e5a7e8cf854fb9bc18839b30a164a2a9c53",
Qx1 = "12aa3294f6a9bb17f91d5875a5a1aa559c29b06134c6d165d83c8e9e02947568",
Qy0 = "271b8b552e52bdd310c46e07327a18861c91a739da178be909f0a4fe53ae0d05",
Qy1 = "1f4f200de96541e826f0bd536b1401e05e2a7c5a96c567b6dff21a21119bbf7"
)
test(
id = 6,
EC = ECP_SWei_Proj[Fp2[BN254_Snarks]],
Px0 = "249d33d9b24b0b9d72753345239bc59ae80557dfb0c86a1f86ec92e749c8722a",
Px1 = "cfba4d7f339870b12f9f83eb31a791ae3333d1e984919f5a128f72377f70756",
Py0 = "1cc869e4e50855a0c09d6da00687007702f5d8fd9c1b1abc17dc643d5dd40825",
Py1 = "19a0e1f64ae604d4591905d73cbeae6e644ddda04628a035d941dd0f94e8a33",
scalar = "263e44e282fe22216cc054a39e40d4e38e71780bdc84563c340bdaaf4562534b",
Qx0 = "2534d84cba98b2aa589b912f5be6dca6f8bf5fc0538fb0a3bc126c109af36aa8",
Qx1 = "13921f40b39312b5a62dd8c2b49f153c331c32fa1d1d5cf31e71e1111ffdc947",
Qy0 = "2a61adb49770d50ccc0e84b3561746cd3672a292e4d8e2dc8cb0a48dfc678adc",
Qy1 = "d2564831641fd45cd073146cc061b2811d1d1b56289887eeed4ce07827dd3cc"
Px0 = "4c591d080257375d6189340185c6fe4c1fa796722d07e1bec0b17080b6b1154",
Px1 = "241e2f2eb2a58afc2b5410d4ccbf75b53744ca1ac0bb28d7c60449f8d06204a4",
Py0 = "eaddea52f2e884a5e2635965ca4146b12127fe8a8883e07def8e8720f410781",
Py1 = "cc60dec6ad90e38c851924cf39ddd11b70eeb3fac95731eafd984e9aba2cae",
scalar = "1471f378000d557f0c33c1f8c8313edd53422479440cbd0fdc4cc55f1163deec",
Qx0 = "2a86b4867d7f63afdc09048210a3ef6d363c7896ccc1bb248f3aad4174e1f8fa",
Qx1 = "84c200018461c84ef9ce6de2c691b95cc2c41edc87107331f107ac49de76656",
Qy0 = "2ea1b6d71adb183d9a8dd319a21a679cb0b4e06bc96583d3a786f82b88b5e3ba",
Qy1 = "834e2ff738dcb5e8db7e4dae9336dede51524313b476019ea29ebadbb4ba12d"
)
test(
id = 7,
EC = ECP_SWei_Proj[Fp2[BN254_Snarks]],
Px0 = "210e0d4ae81d5a5108ecc70c5ea0317455f6d5ae6853938a8fd832b055fb8d4d",
Px1 = "d1a120ec549f63e2b67043d5c6a3b7a9a7682ebac87cfda91dcc696c425eee8",
Py0 = "830c793ad790d61b9b0cbc83bc63869a1c6dc629e7d8c3bec7049ebe68fbad9",
Py1 = "129a312b5e866a67ab15ba01fabbb533dd5a7fd5ba976cad0d0e44743d6efb15",
scalar = "1471f378000d557f0c33c1f8c8313edd53422479440cbd0fdc4cc55f1163deec",
Qx0 = "2c16f3ee75dcdad425ee694342de2ef1c4f07b29c1b5366173d93013ef426692",
Qx1 = "3b7d1258cb99bb20857605d9cd5132c82189f98d78a267e80c583bf840c6eeb",
Qy0 = "2afabef1030af27bc3ba6cc378b0f7dcb84a09cae301e580d9103daad28ba71f",
Qy1 = "117a37aee6704e3fc36d0dba37822658350c48bde5a0968d9ecf45e346caff22"
Px0 = "115e772198e3f0003e31c0a1c48b6ac870d96020a4d634b7f14c15422d001cfe",
Px1 = "1913447ff41836e0b6a3b01be670a2457e6119e02ae35903fb71be4369e269f7",
Py0 = "14cb779c640aad2731b93b07c623c621a5585d0374f0394e5332f20ac28ca49d",
Py1 = "13a4c4b67f5976431158be8137b5017127fdbc312dc49825dae218a9a7878817",
scalar = "411315458c0c0cb27a058faf15dcbf2e467128f9c958d92de37902e1912f81",
Qx0 = "243a8808a891428d01ef28a77d0766488a98272a5dd394b2992599ff522f264",
Qx1 = "1baebf873402812e4345a8b1783fd25272c64d6194bd9f50b32b8e67ee737dc7",
Qy0 = "1f1001ba8b8b27159568f72e80662e352adfc00c9341d8b4fb8ef6f75ff628d2",
Qy1 = "169af215aa2456c6a65e13ac4df1ba1c982ca791058612679ef26dcb8fb0a989"
)
test(
id = 8,
EC = ECP_SWei_Proj[Fp2[BN254_Snarks]],
Px0 = "116f9cd5018206c9e0c20bfd684995d42941ba7d4eff87aec228d5fc593e8893",
Px1 = "211a34b8228f4bc48f0849e2e721cdaeb416e5be421e942339b751c5edaed7e7",
Py0 = "1a888b9355886760acab22c5f35de566d9f521e28cfde8ef5c6cd771b4c19716",
Py1 = "4935e0ab136c85ede2a70c3a4a2429b10e1ee9b259d0ffc5ccd0cbcdcba1351",
scalar = "411315458c0c0cb27a058faf15dcbf2e467128f9c958d92de37902e1912f81",
Qx0 = "175bcd9b7ac109968b88118e93aac3e44446b8abb9e9a2d50eacc2475f245106",
Qx1 = "295dd179211b165f3096be9c44248a525976d9f3757c56083a9f0f69cd9eb75",
Qy0 = "f67730f5ced93a2a7dbcd57b073505b496a7eba5eb5b1f6170cfea145ce2f15",
Qy1 = "903a6681d15626728d7e36af65fe5d96ae314433de84321410579cba5e5dbec"
Px0 = "13faa1f28e6bfe89765c284e67face6ce0a29006ebc1551d4243e754c59f88ad",
Px1 = "640cebb80938dfcb998d84a8e5aafd47ffbcba0aa2f8c9b1c585baf119a8942",
Py0 = "1de793a9ef8f4dea5dad12fb09ddefa07ce197d4d7389a29ad3d8c6484582afe",
Py1 = "fc6e1f8bf75d1b7e48fdb6b2869c2de553663742c151b390cede6712da5a084",
scalar = "111e6f761ce48e22970752bd56cab93d47caa252d52948367b21163591f7b7b1",
Qx0 = "1c25702bf3b6f5fb453c6473b6dc2d67cd3cc21c65b589df1bfde254d50cffdd",
Qx1 = "14d03eb2075d6b5995240cc54a01ebe50b43933863f0316760226fbfa3a0280",
Qy0 = "1a075c9f2f6afa6e07e54b143a33c17e81d0ac8b91e0c8e0fdd5082bd6b9a72d",
Qy1 = "8e5ef57bb0f95fb6538dfaeac677977e3f3d6f49142c09584d8ec5c2ccd9b2d"
)
test(
id = 9,
EC = ECP_SWei_Proj[Fp2[BN254_Snarks]],
Px0 = "16181913b3c03bd61b7e3ba2e05541b492626046533440bced33420cb1d0cfc2",
Px1 = "3d505402f6d6eab342473ed2b07313c5b02e2c63f2218e5773df0aa839ce9ba",
Py0 = "8b40ff9ba82fbf42f02628600894d112640223759570e87bb721a93da0c2c22",
Py1 = "2d8df108c6cb25384b748480f99b9c3e72c256839e227fb22eadc4148e6398eb",
scalar = "111e6f761ce48e22970752bd56cab93d47caa252d52948367b21163591f7b7b1",
Qx0 = "2a8ea2288308fd73ffa423dbe971e45e4cbadfc977d75cd4ea015adf80f25bac",
Qx1 = "491f281ad2faf5b41cb5da93b114310222c6356469b7fb51a8166e8ccc4ab01",
Qy0 = "386ae4175f00ba59c45b07f1f47fbeb0359e8fa52f70cc7396d58f2ef06abd9",
Qy1 = "525877a41155f9dbd541f5833b0d1543a07089cb4a1842990d01dbb3068e8db"
)
test(
id = 10,
EC = ECP_SWei_Proj[Fp2[BN254_Snarks]],
Px0 = "1ea8eb841a242b478d5ed96da30eb78ac5588964dd0f3405b419747d44795ae8",
Px1 = "ee64b54258e687fc9887ca2362b71c50539c881d43097a0578b58c487fd26ca",
Py0 = "2ab3b56d071b0ca9934fc031e26dd0ef777b42018e9afa632ba5af8fec4ddeb8",
Py1 = "cdf8de134912bb9e9b1e9deec26066028ef099def9c4f3e157cec48f5919295",
Px0 = "2fc3da947b78ac524a57670cef36ca89f1dad71b337bc3c18305c582a59648ad",
Px1 = "2f7cc845d8c1ef0613f919d8c47f3c62f83608a45b1e186748ac5dcccd4c6baf",
Py0 = "18ddc4718a4161f72f8d188fc61a609a3d592e186a65f4158483b719ffb05b8f",
Py1 = "45b9c3629ed709784db63ff090e2e891c5b5c6b2222cb6afc56638d7389d689",
scalar = "6223903d4bc2adea7b0a0db92822b6c2638691e4388df93f567e11edd6f23",
Qx0 = "1f30a3adabf28b22f0ca4088fb9cd48688c7c360098d33d0a93800d5b22433db",
Qx1 = "e436556e8cf709b4cceb314bf387326f824afdfdc13638dcd5212822543fb1d",
Qy0 = "28329f3dff9158be7d166e6063ee6964f2d04810a46ef1e05732fa377b6302b4",
Qy1 = "dea3c3263a5914c54be5abcbf9d1aad995dac6a82b88ff46f0a314e8a0c2925"
Qx0 = "9ec612ab0cf4a48e1c15d22284bce8e34619bfb9afb688a9a7930afcc1bd0f3",
Qx1 = "d796e5f5ae1a15622d2284ada34166b9e7c717bd2ff9b2cf2c6e48c33db5ff2",
Qy0 = "2a8ecb09a01cd2f89b316e7569331e9da3bfbd8a40114913b3e5477442c0e4ef",
Qy1 = "282b14bc00df2dd1733e3187a9845ef3a123c17ce4f6154e5cad26c3b48d1b98"
)

View File

@ -224,16 +224,12 @@ proc run_EC_mul_sanity_tests*(
for _ in 0 ..< ItersMul:
let a = rng.random_point(EC, randZ, gen)
# zeroInit
var exponentCanonical: array[(bits+7) div 8, byte]
var
impl = a
reference = a
scratchSpace{.noInit.}: array[1 shl 4, EC]
impl.scalarMulGeneric(exponentCanonical, scratchSpace)
reference.unsafe_ECmul_double_add(exponentCanonical)
impl.scalarMulGeneric(BigInt[bits]())
reference.unsafe_ECmul_double_add(BigInt[bits]())
check:
bool(impl.isInf())
@ -253,16 +249,13 @@ proc run_EC_mul_sanity_tests*(
var exponent{.noInit.}: BigInt[bits]
exponent.setOne()
var exponentCanonical{.noInit.}: array[(bits+7) div 8, byte]
exponentCanonical.exportRawUint(exponent, bigEndian)
var
impl = a
reference = a
scratchSpace{.noInit.}: array[1 shl 4, EC]
impl.scalarMulGeneric(exponentCanonical, scratchSpace)
reference.unsafe_ECmul_double_add(exponentCanonical)
impl.scalarMulGeneric(exponent)
reference.unsafe_ECmul_double_add(exponent)
check:
bool(impl == a)
@ -284,16 +277,13 @@ proc run_EC_mul_sanity_tests*(
doubleA.double(a)
let exponent = BigInt[bits].fromUint(2)
var exponentCanonical{.noInit.}: array[(bits+7) div 8, byte]
exponentCanonical.exportRawUint(exponent, bigEndian)
var
impl = a
reference = a
scratchSpace{.noInit.}: array[1 shl 4, EC]
impl.scalarMulGeneric(exponentCanonical, scratchSpace)
reference.unsafe_ECmul_double_add(exponentCanonical)
impl.scalarMulGeneric(exponent)
reference.unsafe_ECmul_double_add(exponent)
check:
bool(impl == doubleA)
@ -335,33 +325,30 @@ proc run_EC_mul_distributive_tests*(
let b = rng.random_point(EC, randZ, gen)
let exponent = rng.random_unsafe(BigInt[bits])
var exponentCanonical{.noInit.}: array[(bits+7) div 8, byte]
exponentCanonical.exportRawUint(exponent, bigEndian)
# [k](a + b) - Factorized
var
fImpl{.noInit.}: EC
fReference{.noInit.}: EC
scratchSpace{.noInit.}: array[1 shl 4, EC]
fImpl.sum(a, b)
fReference.sum(a, b)
fImpl.scalarMulGeneric(exponentCanonical, scratchSpace)
fReference.unsafe_ECmul_double_add(exponentCanonical)
fImpl.scalarMulGeneric(exponent)
fReference.unsafe_ECmul_double_add(exponent)
# [k]a + [k]b - Distributed
var kaImpl = a
var kaRef = a
kaImpl.scalarMulGeneric(exponentCanonical, scratchSpace)
kaRef.unsafe_ECmul_double_add(exponentCanonical)
kaImpl.scalarMulGeneric(exponent)
kaRef.unsafe_ECmul_double_add(exponent)
var kbImpl = b
var kbRef = b
kbImpl.scalarMulGeneric(exponentCanonical, scratchSpace)
kbRef.unsafe_ECmul_double_add(exponentCanonical)
kbImpl.scalarMulGeneric(exponent)
kbRef.unsafe_ECmul_double_add(exponent)
var kakbImpl{.noInit.}, kakbRef{.noInit.}: EC
kakbImpl.sum(kaImpl, kbImpl)
@ -406,16 +393,13 @@ proc run_EC_mul_vs_ref_impl*(
let a = rng.random_point(EC, randZ, gen)
let exponent = rng.random_unsafe(BigInt[bits])
var exponentCanonical{.noInit.}: array[(bits+7) div 8, byte]
exponentCanonical.exportRawUint(exponent, bigEndian)
var
impl = a
reference = a
scratchSpace{.noInit.}: array[1 shl 4, EC]
impl.scalarMulGeneric(exponentCanonical, scratchSpace)
reference.unsafe_ECmul_double_add(exponentCanonical)
impl.scalarMulGeneric(exponent)
reference.unsafe_ECmul_double_add(exponent)
check: bool(impl == reference)

View File

@ -44,16 +44,13 @@ suite "Order checks on BN254_Snarks":
let a = rng.random_unsafe(EC)
let exponent = EC.F.C.getCurveOrder()
var exponentCanonical{.noInit.}: array[(bits+7) div 8, byte]
exponentCanonical.exportRawUint(exponent, bigEndian)
var
impl = a
reference = a
scratchSpace{.noInit.}: array[1 shl 4, EC]
impl.scalarMulGeneric(exponentCanonical, scratchSpace)
reference.unsafe_ECmul_double_add(exponentCanonical)
impl.scalarMulGeneric(exponent)
reference.unsafe_ECmul_double_add(exponent)
check:
bool(impl.isInf())

View File

@ -46,16 +46,13 @@ run_EC_mul_sanity_tests(
# let a = rng.random_unsafe(EC)
#
# let exponent = F.C.getCurveOrder()
# var exponentCanonical{.noInit.}: array[(bits+7) div 8, byte]
# exponentCanonical.exportRawUint(exponent, bigEndian)
#
# var
# impl = a
# reference = a
# scratchSpace{.noInit.}: array[1 shl 4, EC]
#
# impl.scalarMulGeneric(exponentCanonical, scratchSpace)
# reference.unsafe_ECmul_double_add(exponentCanonical)
# impl.scalarMulGeneric(exponent)
# reference.unsafe_ECmul_double_add(exponent)
#
# check:
# bool(impl.isInf())

View File

@ -46,16 +46,13 @@ run_EC_mul_sanity_tests(
# let a = rng.random_unsafe(EC)
#
# let exponent = F.C.getCurveOrder()
# var exponentCanonical{.noInit.}: array[(bits+7) div 8, byte]
# exponentCanonical.exportRawUint(exponent, bigEndian)
#
# var
# impl = a
# reference = a
# scratchSpace{.noInit.}: array[1 shl 4, EC]
#
# impl.scalarMulGeneric(exponentCanonical, scratchSpace)
# reference.unsafe_ECmul_double_add(exponentCanonical)
# impl.scalarMulGeneric(exponent)
# reference.unsafe_ECmul_double_add(exponent)
#
# check:
# bool(impl.isInf())