diff --git a/benchmarks/bench_ec_g1.nim b/benchmarks/bench_ec_g1.nim index 37fe364..e3454e0 100644 --- a/benchmarks/bench_ec_g1.nim +++ b/benchmarks/bench_ec_g1.nim @@ -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() diff --git a/benchmarks/bench_ec_g2.nim b/benchmarks/bench_ec_g2.nim index 7207fa9..37f5f41 100644 --- a/benchmarks/bench_ec_g2.nim +++ b/benchmarks/bench_ec_g2.nim @@ -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() diff --git a/benchmarks/bench_elliptic_template.nim b/benchmarks/bench_elliptic_template.nim index 8501fcb..586a10a 100644 --- a/benchmarks/bench_elliptic_template.nim +++ b/benchmarks/bench_elliptic_template.nim @@ -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) diff --git a/benchmarks/bench_fields_template.nim b/benchmarks/bench_fields_template.nim index afbd5be..4bafa5b 100644 --- a/benchmarks/bench_fields_template.nim +++ b/benchmarks/bench_fields_template.nim @@ -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)" diff --git a/constantine/arithmetic/bigints.nim b/constantine/arithmetic/bigints.nim index 9ff2656..0f3c9aa 100644 --- a/constantine/arithmetic/bigints.nim +++ b/constantine/arithmetic/bigints.nim @@ -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 # ------------------------------------------------------------ diff --git a/constantine/arithmetic/limbs.nim b/constantine/arithmetic/limbs.nim index e7cee3d..cb2264a 100644 --- a/constantine/arithmetic/limbs.nim +++ b/constantine/arithmetic/limbs.nim @@ -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)) diff --git a/constantine/elliptic/ec_endomorphism_accel.nim b/constantine/elliptic/ec_endomorphism_accel.nim index ed3b78a..6e5ac02 100644 --- a/constantine/elliptic/ec_endomorphism_accel.nim +++ b/constantine/elliptic/ec_endomorphism_accel.nim @@ -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") diff --git a/constantine/elliptic/ec_endomorphism_params.nim b/constantine/elliptic/ec_endomorphism_params.nim index 626fa83..cec7002 100644 --- a/constantine/elliptic/ec_endomorphism_params.nim +++ b/constantine/elliptic/ec_endomorphism_params.nim @@ -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]) diff --git a/constantine/elliptic/ec_scalar_mul.nim b/constantine/elliptic/ec_scalar_mul.nim index 9ba6529..475da2c 100644 --- a/constantine/elliptic/ec_scalar_mul.nim +++ b/constantine/elliptic/ec_scalar_mul.nim @@ -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) diff --git a/sage/frobenius_bls12_381.sage b/sage/frobenius_bls12_381.sage index e3ad1a3..178fac7 100644 --- a/sage/frobenius_bls12_381.sage +++ b/sage/frobenius_bls12_381.sage @@ -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. = PolynomialRing(Fp) Fp2. = Fp.extension(u^2+1) -# K6. = PolynomialRing(F2) -# Fp6. = Fp2.extension(v^3-Fp2([1, 1]) -# K12. = PolynomialRing(Fp6) -# Fp12. = 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" diff --git a/sage/frobenius_bn254_nogami.sage b/sage/frobenius_bn254_nogami.sage new file mode 100644 index 0000000..2f138e1 --- /dev/null +++ b/sage/frobenius_bn254_nogami.sage @@ -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. = PolynomialRing(Fp) +Fp2. = 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" diff --git a/sage/frobenius_bn254_snarks.sage b/sage/frobenius_bn254_snarks.sage index 8f4c384..a63d69d 100644 --- a/sage/frobenius_bn254_snarks.sage +++ b/sage/frobenius_bn254_snarks.sage @@ -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. = PolynomialRing(Fp) Fp2. = Fp.extension(u^2+1) -# K6. = PolynomialRing(F2) -# Fp6. = Fp2.extension(v^3-Fp2([9, 1])) -# K12. = PolynomialRing(Fp6) -# K12. = 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" diff --git a/sage/lattice_decomposition_bls12_381_g1.sage b/sage/lattice_decomposition_bls12_381_g1.sage index 0a328b1..eaf580f 100644 --- a/sage/lattice_decomposition_bls12_381_g1.sage +++ b/sage/lattice_decomposition_bls12_381_g1.sage @@ -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. = PolynomialRing(F) -# F2. = F.extension(u^2+9) -# K6. = PolynomialRing(F2) -# F6. = F2.extension(v^3-beta) -# K12. = PolynomialRing(F6) -# K12. = 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) diff --git a/sage/lattice_decomposition_bls12_381_g2.sage b/sage/lattice_decomposition_bls12_381_g2.sage new file mode 100644 index 0000000..8c39431 --- /dev/null +++ b/sage/lattice_decomposition_bls12_381_g2.sage @@ -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. = PolynomialRing(Fp) +Fp2. = 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) diff --git a/sage/lattice_decomposition_bn254_snarks_g1.sage b/sage/lattice_decomposition_bn254_snarks_g1.sage index 5d2f999..6065aec 100644 --- a/sage/lattice_decomposition_bn254_snarks_g1.sage +++ b/sage/lattice_decomposition_bn254_snarks_g1.sage @@ -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. = PolynomialRing(F) -# F2. = F.extension(u^2+9) -# K6. = PolynomialRing(F2) -# F6. = F2.extension(v^3-beta) -# K12. = PolynomialRing(F6) -# K12. = 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) diff --git a/sage/lattice_decomposition_bn254_snarks_g2.sage b/sage/lattice_decomposition_bn254_snarks_g2.sage new file mode 100644 index 0000000..11f71a6 --- /dev/null +++ b/sage/lattice_decomposition_bn254_snarks_g2.sage @@ -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. = PolynomialRing(Fp) +Fp2. = 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) diff --git a/sage/testgen_bls12_381.sage b/sage/testgen_bls12_381.sage index 4ec2f94..f414302 100644 --- a/sage/testgen_bls12_381.sage +++ b/sage/testgen_bls12_381.sage @@ -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 diff --git a/sage/testgen_bn254_snarks.sage b/sage/testgen_bn254_snarks.sage index 34b49f4..75e0b7d 100644 --- a/sage/testgen_bn254_snarks.sage +++ b/sage/testgen_bn254_snarks.sage @@ -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 diff --git a/tests/support/ec_reference_scalar_mult.nim b/tests/support/ec_reference_scalar_mult.nim index 6c1d0bb..4778a02 100644 --- a/tests/support/ec_reference_scalar_mult.nim +++ b/tests/support/ec_reference_scalar_mult.nim @@ -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: diff --git a/tests/t_ec_frobenius.nim b/tests/t_ec_frobenius.nim index a8c5f26..8f23cd2 100644 --- a/tests/t_ec_frobenius.nim +++ b/tests/t_ec_frobenius.nim @@ -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) diff --git a/tests/t_ec_sage_bls12_381.nim b/tests/t_ec_sage_bls12_381.nim index 8214daa..a2f4559 100644 --- a/tests/t_ec_sage_bls12_381.nim +++ b/tests/t_ec_sage_bls12_381.nim @@ -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" ) diff --git a/tests/t_ec_sage_bn254.nim b/tests/t_ec_sage_bn254.nim index ef981d0..12701f7 100644 --- a/tests/t_ec_sage_bn254.nim +++ b/tests/t_ec_sage_bn254.nim @@ -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" ) diff --git a/tests/t_ec_template.nim b/tests/t_ec_template.nim index bb8fdd6..33d4883 100644 --- a/tests/t_ec_template.nim +++ b/tests/t_ec_template.nim @@ -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) diff --git a/tests/t_ec_wstrass_prj_g1_mul_sanity.nim b/tests/t_ec_wstrass_prj_g1_mul_sanity.nim index df8ff99..8a5f582 100644 --- a/tests/t_ec_wstrass_prj_g1_mul_sanity.nim +++ b/tests/t_ec_wstrass_prj_g1_mul_sanity.nim @@ -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()) diff --git a/tests/t_ec_wstrass_prj_g2_mul_sanity_bls12_381.nim b/tests/t_ec_wstrass_prj_g2_mul_sanity_bls12_381.nim index fff3415..3d51d15 100644 --- a/tests/t_ec_wstrass_prj_g2_mul_sanity_bls12_381.nim +++ b/tests/t_ec_wstrass_prj_g2_mul_sanity_bls12_381.nim @@ -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()) diff --git a/tests/t_ec_wstrass_prj_g2_mul_sanity_bn254_snarks.nim b/tests/t_ec_wstrass_prj_g2_mul_sanity_bn254_snarks.nim index 2eb856e..386d49c 100644 --- a/tests/t_ec_wstrass_prj_g2_mul_sanity_bn254_snarks.nim +++ b/tests/t_ec_wstrass_prj_g2_mul_sanity_bn254_snarks.nim @@ -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())