diff --git a/benchmarks/bench_ec_g1.nim b/benchmarks/bench_ec_g1.nim index 175e226..6389353 100644 --- a/benchmarks/bench_ec_g1.nim +++ b/benchmarks/bench_ec_g1.nim @@ -59,7 +59,7 @@ proc main() = separator() scalarMulGenericBench(ECP_SWei_Proj[Fp[curve]], scratchSpaceSize = 1 shl 4, MulIters) separator() - scalarMulGLV(ECP_SWei_Proj[Fp[curve]], MulIters) + scalarMulEndo(ECP_SWei_Proj[Fp[curve]], MulIters) separator() separator() diff --git a/benchmarks/bench_ec_g2.nim b/benchmarks/bench_ec_g2.nim new file mode 100644 index 0000000..6fb16f2 --- /dev/null +++ b/benchmarks/bench_ec_g2.nim @@ -0,0 +1,72 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + # Internals + ../constantine/config/curves, + ../constantine/arithmetic, + ../constantine/towers, + ../constantine/elliptic/ec_weierstrass_projective, + # Helpers + ../helpers/static_for, + ./bench_elliptic_template, + # Standard library + std/strutils + +# ############################################################ +# +# Benchmark of the G1 group of +# Short Weierstrass elliptic curves +# in (homogeneous) projective coordinates +# +# ############################################################ + + +const Iters = 500_000 +const MulIters = 500 +const AvailableCurves = [ + # P224, + # BN254_Nogami, + BN254_Snarks, + # Curve25519, + # P256, + # Secp256k1, + # BLS12_377, + BLS12_381, + # BN446, + # FKM12_447, + # BLS12_461, + # BN462 +] + +proc main() = + separator() + staticFor i, 0, AvailableCurves.len: + const curve = AvailableCurves[i] + addBench(ECP_SWei_Proj[Fp2[curve]], Iters) + separator() + doublingBench(ECP_SWei_Proj[Fp2[curve]], Iters) + separator() + scalarMulUnsafeDoubleAddBench(ECP_SWei_Proj[Fp2[curve]], MulIters) + separator() + scalarMulGenericBench(ECP_SWei_Proj[Fp2[curve]], scratchSpaceSize = 1 shl 2, MulIters) + separator() + scalarMulGenericBench(ECP_SWei_Proj[Fp2[curve]], scratchSpaceSize = 1 shl 3, MulIters) + separator() + scalarMulGenericBench(ECP_SWei_Proj[Fp2[curve]], scratchSpaceSize = 1 shl 4, MulIters) + separator() + # scalarMulEndo(ECP_SWei_Proj[Fp2[curve]], MulIters) + # separator() + separator() + +main() + +echo "\nNotes:" +echo " - GCC is significantly slower than Clang on multiprecision arithmetic." +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/benchmarks/bench_elliptic_template.nim b/benchmarks/bench_elliptic_template.nim index a654b5b..3728acf 100644 --- a/benchmarks/bench_elliptic_template.nim +++ b/benchmarks/bench_elliptic_template.nim @@ -111,20 +111,23 @@ template bench(op: string, T: typedesc, iters: int, body: untyped): untyped = report(op, fixEllipticDisplay(T), start, stop, startClk, stopClk, iters) proc addBench*(T: typedesc, iters: int) = + const G1_or_G2 = when T.F is Fp: "G1" else: "G2" var r {.noInit.}: T let P = rng.random_unsafe(T) let Q = rng.random_unsafe(T) - bench("EC Add G1", T, iters): + bench("EC Add " & G1_or_G2, T, iters): r.sum(P, Q) proc doublingBench*(T: typedesc, iters: int) = + const G1_or_G2 = when T.F is Fp: "G1" else: "G2" var r {.noInit.}: T let P = rng.random_unsafe(T) - bench("EC Double G1", T, iters): + bench("EC Double " & G1_or_G2, T, iters): r.double(P) proc scalarMulGenericBench*(T: typedesc, scratchSpaceSize: static int, iters: int) = const bits = T.F.C.getCurveOrderBitwidth() + const G1_or_G2 = when T.F is Fp: "G1" else: "G2" var r {.noInit.}: T let P = rng.random_unsafe(T) # TODO: clear cofactor @@ -135,24 +138,29 @@ proc scalarMulGenericBench*(T: typedesc, scratchSpaceSize: static int, iters: in var scratchSpace{.noInit.}: array[scratchSpaceSize, T] - bench("EC ScalarMul Generic G1 (scratchsize = " & $scratchSpaceSize & ')', T, iters): + bench("EC ScalarMul Generic " & G1_or_G2 & " (scratchsize = " & $scratchSpaceSize & ')', T, iters): r = P r.scalarMulGeneric(exponentCanonical, scratchSpace) -proc scalarMulGLV*(T: typedesc, iters: int) = +proc scalarMulEndo*(T: typedesc, iters: int) = const bits = T.F.C.getCurveOrderBitwidth() + const G1_or_G2 = when T.F is Fp: "G1" else: "G2" var r {.noInit.}: T let P = rng.random_unsafe(T) # TODO: clear cofactor let exponent = rng.random_unsafe(BigInt[bits]) - bench("EC ScalarMul G1 (GLV endomorphism accelerated)", T, iters): + bench("EC ScalarMul " & G1_or_G2 & " (endomorphism accelerated)", T, iters): r = P - r.scalarMulGLV(exponent) + when T.F is Fp: + r.scalarMulGLV(exponent) + else: + {.error: "Not implemented".} proc scalarMulUnsafeDoubleAddBench*(T: typedesc, iters: int) = const bits = T.F.C.getCurveOrderBitwidth() + const G1_or_G2 = when T.F is Fp: "G1" else: "G2" var r {.noInit.}: T let P = rng.random_unsafe(T) # TODO: clear cofactor @@ -161,6 +169,6 @@ proc scalarMulUnsafeDoubleAddBench*(T: typedesc, iters: int) = var exponentCanonical{.noInit.}: array[(bits+7) div 8, byte] exponentCanonical.exportRawUint(exponent, bigEndian) - bench("EC ScalarMul G1 (unsafe reference DoubleAdd)", T, iters): + bench("EC ScalarMul " & G1_or_G2 & " (unsafe reference DoubleAdd)", T, iters): r = P r.unsafe_ECmul_double_add(exponentCanonical) diff --git a/constantine.nimble b/constantine.nimble index d21c12f..1799b6c 100644 --- a/constantine.nimble +++ b/constantine.nimble @@ -5,12 +5,67 @@ description = "This library provides constant time big int primitives." license = "MIT or Apache License 2.0" srcDir = "src" -### Dependencies +# Dependencies +# ---------------------------------------------------------------- + requires "nim >= 1.1.0" +# Test config +# ---------------------------------------------------------------- + const buildParallel = "test_parallel.txt" -### Helper functions +const testDesc: seq[tuple[path: string, useGMP: bool]] = @[ + # Primitives + ("tests/test_primitives.nim", false), + # Big ints + ("tests/test_io_bigints.nim", false), + ("tests/test_bigints.nim", false), + ("tests/test_bigints_multimod.nim", false), + ("tests/test_bigints_mod_vs_gmp.nim", true), + ("tests/test_bigints_mul_vs_gmp.nim", true), + ("tests/test_bigints_mul_high_words_vs_gmp.nim", true), + # Field + ("tests/test_io_fields", false), + ("tests/test_finite_fields.nim", false), + ("tests/test_finite_fields_mulsquare.nim", false), + ("tests/test_finite_fields_sqrt.nim", false), + ("tests/test_finite_fields_powinv.nim", false), + ("tests/test_finite_fields_vs_gmp.nim", true), + # Precompute + ("tests/test_precomputed", false), + # Towers of extension fields + ("tests/test_fp2.nim", false), + ("tests/test_fp2_sqrt.nim", false), + ("tests/test_fp6_bn254_snarks.nim", false), + ("tests/test_fp6_bls12_377.nim", false), + ("tests/test_fp6_bls12_381.nim", false), + ("tests/test_fp12_bn254_snarks.nim", false), + ("tests/test_fp12_bls12_377.nim", false), + ("tests/test_fp12_bls12_381.nim", false), + # Elliptic curve arithmetic G1 + ("tests/test_ec_weierstrass_projective_g1_add_double.nim", false), + ("tests/test_ec_weierstrass_projective_g1_mul_sanity.nim", false), + ("tests/test_ec_weierstrass_projective_g1_mul_distributive.nim", false), + ("tests/test_ec_weierstrass_projective_g1_mul_vs_ref.nim", false), + # Elliptic curve arithmetic G2 + ("tests/test_ec_weierstrass_projective_g2_add_double_bn254_snarks.nim", false), + ("tests/test_ec_weierstrass_projective_g2_mul_sanity_bn254_snarks.nim", false), + ("tests/test_ec_weierstrass_projective_g2_mul_distributive_bn254_snarks.nim", false), + ("tests/test_ec_weierstrass_projective_g2_mul_vs_ref_bn254_snarks.nim", false), + + ("tests/test_ec_weierstrass_projective_g2_add_double_bls12_381.nim", false), + ("tests/test_ec_weierstrass_projective_g2_mul_sanity_bls12_381.nim", false), + ("tests/test_ec_weierstrass_projective_g2_mul_distributive_bls12_381.nim", false), + ("tests/test_ec_weierstrass_projective_g2_mul_vs_ref_bls12_381.nim", false), + # Elliptic curve arithmetic vs Sagemath + ("tests/test_ec_sage_bn254.nim", false), + ("tests/test_ec_sage_bls12_381.nim", false) +] + +# Helper functions +# ---------------------------------------------------------------- + proc test(flags, path: string, commandFile = false) = # commandFile should be a "file" but Nimscript doesn't support IO # TODO: use a proper runner @@ -28,9 +83,9 @@ proc test(flags, path: string, commandFile = false) = let command = "nim " & lang & cc & " " & flags & " --verbosity:0 --outdir:build -r --hints:off --warnings:off " & path if not commandFile: - echo "\n========================================================================================" + echo "\n==============================================================================================" echo "Running [flags: ", flags, "] ", path - echo "========================================================================================" + echo "==============================================================================================" exec command else: # commandFile.writeLine command @@ -47,74 +102,18 @@ proc runBench(benchName: string, compiler = "") = " -d:danger --verbosity:0 -o:build/" & benchName & "_" & compiler & " -r --hints:off --warnings:off benchmarks/" & benchName & ".nim" -### tasks +# Tasks +# ---------------------------------------------------------------- + task test, "Run all tests": # -d:testingCurves is configured in a *.nim.cfg for convenience - # Primitives - test "", "tests/test_primitives.nim" - - # Big ints - test "", "tests/test_io_bigints.nim" - test "", "tests/test_bigints.nim" - test "", "tests/test_bigints_multimod.nim" - - test "", "tests/test_bigints_mod_vs_gmp.nim" - - # Field - test "", "tests/test_io_fields" - test "", "tests/test_finite_fields.nim" - test "", "tests/test_finite_fields_mulsquare.nim" - test "", "tests/test_finite_fields_sqrt.nim" - test "", "tests/test_finite_fields_powinv.nim" - - test "", "tests/test_finite_fields_vs_gmp.nim" - - # Precompute - test "", "tests/test_precomputed" - - # Towers of extension fields - test "", "tests/test_fp2.nim" - test "", "tests/test_fp6.nim" - test "", "tests/test_fp12.nim" - - # Elliptic curve arithmetic - test "", "tests/test_ec_weierstrass_projective_g1.nim" - test "", "tests/test_ec_bn254.nim" - test "", "tests/test_ec_bls12_381.nim" + for td in testDesc: + test "", td.path if sizeof(int) == 8: # 32-bit tests on 64-bit arch - # Primitives - test "-d:Constantine32", "tests/test_primitives.nim" - - # Big ints - test "-d:Constantine32", "tests/test_io_bigints.nim" - test "-d:Constantine32", "tests/test_bigints.nim" - test "-d:Constantine32", "tests/test_bigints_multimod.nim" - - test "-d:Constantine32", "tests/test_bigints_mod_vs_gmp.nim" - - # Field - test "-d:Constantine32", "tests/test_io_fields" - test "-d:Constantine32", "tests/test_finite_fields.nim" - test "-d:Constantine32", "tests/test_finite_fields_mulsquare.nim" - test "-d:Constantine32", "tests/test_finite_fields_sqrt.nim" - test "-d:Constantine32", "tests/test_finite_fields_powinv.nim" - - test "-d:Constantine32", "tests/test_finite_fields_vs_gmp.nim" - - # Precompute - test "-d:Constantine32", "tests/test_precomputed" - - # Towers of extension fields - test "-d:Constantine32", "tests/test_fp2.nim" - test "-d:Constantine32", "tests/test_fp6.nim" - test "-d:Constantine32", "tests/test_fp12.nim" - - # Elliptic curve arithmetic - test "-d:Constantine32", "tests/test_ec_weierstrass_projective_g1.nim" - test "-d:Constantine32", "tests/test_ec_bn254.nim" - test "-d:Constantine32", "tests/test_ec_bls12_381.nim" + for td in testDesc: + test "-d:Constantine32", td.path # Benchmarks compile and run # ignore Windows 32-bit for the moment @@ -125,66 +124,18 @@ task test, "Run all tests": runBench("bench_fp6") runBench("bench_fp12") runBench("bench_ec_g1") + runBench("bench_ec_g2") task test_no_gmp, "Run tests that don't require GMP": # -d:testingCurves is configured in a *.nim.cfg for convenience + for td in testDesc: + if not td.useGMP: + test "", td.path - # Primitives - test "", "tests/test_primitives.nim" - - # Big ints - test "", "tests/test_io_bigints.nim" - test "", "tests/test_bigints.nim" - test "", "tests/test_bigints_multimod.nim" - - # Field - test "", "tests/test_io_fields" - test "", "tests/test_finite_fields.nim" - test "", "tests/test_finite_fields_mulsquare.nim" - test "", "tests/test_finite_fields_sqrt.nim" - test "", "tests/test_finite_fields_powinv.nim" - - # Precompute - test "", "tests/test_precomputed" - - # Towers of extension fields - test "", "tests/test_fp2.nim" - test "", "tests/test_fp6.nim" - test "", "tests/test_fp12.nim" - - # Elliptic curve arithmetic - test "", "tests/test_ec_weierstrass_projective_g1.nim" - test "", "tests/test_ec_bn254.nim" - test "", "tests/test_ec_bls12_381.nim" - - if sizeof(int) == 8: # 32-bit tests - # Primitives - test "-d:Constantine32", "tests/test_primitives.nim" - - # Big ints - test "-d:Constantine32", "tests/test_io_bigints.nim" - test "-d:Constantine32", "tests/test_bigints.nim" - test "-d:Constantine32", "tests/test_bigints_multimod.nim" - - # Field - test "-d:Constantine32", "tests/test_io_fields" - test "-d:Constantine32", "tests/test_finite_fields.nim" - test "-d:Constantine32", "tests/test_finite_fields_mulsquare.nim" - test "-d:Constantine32", "tests/test_finite_fields_sqrt.nim" - test "-d:Constantine32", "tests/test_finite_fields_powinv.nim" - - # Precompute - test "-d:Constantine32", "tests/test_precomputed" - - # Towers of extension fields - test "-d:Constantine32", "tests/test_fp2.nim" - test "-d:Constantine32", "tests/test_fp6.nim" - test "-d:Constantine32", "tests/test_fp12.nim" - - # Elliptic curve arithmetic - test "-d:Constantine32", "tests/test_ec_weierstrass_projective_g1.nim" - test "-d:Constantine32", "tests/test_ec_bn254.nim" - test "-d:Constantine32", "tests/test_ec_bls12_381.nim" + if sizeof(int) == 8: # 32-bit tests on 64-bit arch + for td in testDesc: + if not td.useGMP: + test "-d:Constantine32", td.path # Benchmarks compile and run # ignore Windows 32-bit for the moment @@ -195,79 +146,24 @@ task test_no_gmp, "Run tests that don't require GMP": runBench("bench_fp6") runBench("bench_fp12") runBench("bench_ec_g1") + runBench("bench_ec_g2") task test_parallel, "Run all tests in parallel (via GNU parallel)": # -d:testingCurves is configured in a *.nim.cfg for convenience let cmdFile = true # open(buildParallel, mode = fmWrite) # Nimscript doesn't support IO :/ exec "> " & buildParallel - # Primitives - test "", "tests/test_primitives.nim", cmdFile - - # Big ints - test "", "tests/test_io_bigints.nim", cmdFile - test "", "tests/test_bigints.nim", cmdFile - test "", "tests/test_bigints_multimod.nim", cmdFile - - test "", "tests/test_bigints_mul_vs_gmp.nim", cmdFile - test "", "tests/test_bigints_mod_vs_gmp.nim", cmdFile - - # Field - test "", "tests/test_io_fields", cmdFile - test "", "tests/test_finite_fields.nim", cmdFile - test "", "tests/test_finite_fields_mulsquare.nim", cmdFile - test "", "tests/test_finite_fields_sqrt.nim", cmdFile - test "", "tests/test_finite_fields_powinv.nim", cmdFile - - test "", "tests/test_finite_fields_vs_gmp.nim", cmdFile - - # Towers of extension fields - test "", "tests/test_fp2.nim", cmdFile - test "", "tests/test_fp6.nim", cmdFile - test "", "tests/test_fp12.nim", cmdFile - - # Elliptic curve arithmetic - test "", "tests/test_ec_weierstrass_projective_g1.nim", cmdFile - test "", "tests/test_ec_bn254.nim", cmdFile - test "", "tests/test_ec_bls12_381.nim", cmdFile + for td in testDesc: + test "", td.path, cmdFile # cmdFile.close() # Execute everything in parallel with GNU parallel exec "parallel --keep-order --group < " & buildParallel exec "> " & buildParallel - if sizeof(int) == 8: # 32-bit tests on 64-bit arch - # Primitives - test "-d:Constantine32", "tests/test_primitives.nim", cmdFile - - # Big ints - test "-d:Constantine32", "tests/test_io_bigints.nim", cmdFile - test "-d:Constantine32", "tests/test_bigints.nim", cmdFile - test "-d:Constantine32", "tests/test_bigints_multimod.nim", cmdFile - - test "-d:Constantine32", "tests/test_bigints_mul_vs_gmp.nim", cmdFile - test "-d:Constantine32", "tests/test_bigints_mod_vs_gmp.nim", cmdFile - - # Field - test "-d:Constantine32", "tests/test_io_fields", cmdFile - test "-d:Constantine32", "tests/test_finite_fields.nim", cmdFile - test "-d:Constantine32", "tests/test_finite_fields_mulsquare.nim", cmdFile - test "-d:Constantine32", "tests/test_finite_fields_sqrt.nim", cmdFile - test "-d:Constantine32", "tests/test_finite_fields_powinv.nim", cmdFile - - test "-d:Constantine32", "tests/test_finite_fields_vs_gmp.nim", cmdFile - - # Towers of extension fields - test "-d:Constantine32", "tests/test_fp2.nim", cmdFile - test "-d:Constantine32", "tests/test_fp6.nim", cmdFile - test "-d:Constantine32", "tests/test_fp12.nim", cmdFile - - # Elliptic curve arithmetic - test "-d:Constantine32", "tests/test_ec_weierstrass_projective_g1.nim", cmdFile - test "-d:Constantine32", "tests/test_ec_bn254.nim", cmdFile - test "-d:Constantine32", "tests/test_ec_bls12_381.nim", cmdFile - + for td in testDesc: + test "-d:Constantine32", td.path, cmdFile # cmdFile.close() # Execute everything in parallel with GNU parallel exec "parallel --keep-order --group < " & buildParallel @@ -283,6 +179,7 @@ task test_parallel, "Run all tests in parallel (via GNU parallel)": runBench("bench_fp6") runBench("bench_fp12") runBench("bench_ec_g1") + runBench("bench_ec_g2") task bench_fp, "Run benchmark 𝔽p with your default compiler": runBench("bench_fp") @@ -323,8 +220,17 @@ task bench_fp12_clang, "Run benchmark 𝔽p12 with clang": task bench_ec_g1, "Run benchmark on Elliptic Curve group 𝔾1 - Short Weierstrass with Projective Coordinates - GCC": runBench("bench_ec_g1") -task bench_ec_gcc, "Run benchmark on Elliptic Curve group 𝔾1 - Short Weierstrass with Projective Coordinates - GCC": +task bench_ec_g1_gcc, "Run benchmark on Elliptic Curve group 𝔾1 - Short Weierstrass with Projective Coordinates - GCC": runBench("bench_ec_g1", "gcc") task bench_ec_g1_clang, "Run benchmark on Elliptic Curve group 𝔾1 - Short Weierstrass with Projective Coordinates - Clang": runBench("bench_ec_g1", "clang") + +task bench_ec_g2, "Run benchmark on Elliptic Curve group 𝔾2 - Short Weierstrass with Projective Coordinates - GCC": + runBench("bench_ec_g2") + +task bench_ec_g2_gcc, "Run benchmark on Elliptic Curve group 𝔾2 - Short Weierstrass with Projective Coordinates - GCC": + runBench("bench_ec_g2", "gcc") + +task bench_ec_g2_clang, "Run benchmark on Elliptic Curve group 𝔾2 - Short Weierstrass with Projective Coordinates - Clang": + runBench("bench_ec_g2", "clang") diff --git a/constantine/arithmetic/bigints.nim b/constantine/arithmetic/bigints.nim index 9b682ea..88654ff 100644 --- a/constantine/arithmetic/bigints.nim +++ b/constantine/arithmetic/bigints.nim @@ -301,7 +301,7 @@ func reduce*[aBits, mBits](r: var BigInt[mBits], a: BigInt[aBits], M: BigInt[mBi # pass a pointer+length to a fixed session of the BSS. reduce(r.limbs, a.limbs, aBits, M.limbs, mBits) -func div2mod*[bits](a: var BigInt[bits], mp1div2: BigInt[bits]) = +func div2_modular*[bits](a: var BigInt[bits], mp1div2: BigInt[bits]) = ## Compute a <- a/2 (mod M) ## `mp1div2` is the modulus (M+1)/2 ## @@ -313,7 +313,7 @@ func div2mod*[bits](a: var BigInt[bits], mp1div2: BigInt[bits]) = ## overflowing the "Limbs" by dividing by 2 first ## and add 1 ## Otherwise `mp1div2` should be M/2 - a.limbs.div2mod(mp1div2.limbs) + a.limbs.div2_modular(mp1div2.limbs) func steinsGCD*[bits](r: var BigInt[bits], a, F, M, mp1div2: BigInt[bits]) = ## Compute F multiplied the modular inverse of ``a`` modulo M diff --git a/constantine/arithmetic/finite_fields.nim b/constantine/arithmetic/finite_fields.nim index ecab650..4ea0d3d 100644 --- a/constantine/arithmetic/finite_fields.nim +++ b/constantine/arithmetic/finite_fields.nim @@ -45,7 +45,7 @@ func fromBig*[C: static Curve](T: type Fp[C], src: BigInt): Fp[C] {.noInit.} = ## Convert a BigInt to its Montgomery form result.mres.montyResidue(src, C.Mod, C.getR2modP(), C.getNegInvModWord(), C.canUseNoCarryMontyMul()) -func fromBig*[C: static Curve](dst: var Fp[C], src: BigInt) {.noInit.} = +func fromBig*[C: static Curve](dst: var Fp[C], src: BigInt) = ## Convert a BigInt to its Montgomery form dst.mres.montyResidue(src, C.Mod, C.getR2modP(), C.getNegInvModWord(), C.canUseNoCarryMontyMul()) @@ -168,7 +168,7 @@ func neg*(r: var Fp, a: Fp) = func div2*(a: var Fp) = ## Modular division by 2 - a.mres.div2mod(Fp.C.getPrimePlus1div2()) + a.mres.div2_modular(Fp.C.getPrimePlus1div2()) # ############################################################ # @@ -247,11 +247,13 @@ func isSquare*[C](a: Fp[C]): SecretBool = # as we assume that var xi {.noInit.} = a # TODO: is noInit necessary? see https://github.com/mratsim/constantine/issues/21 xi.powUnsafeExponent(C.getPrimeMinus1div2_BE()) - result = xi.isOne() - # 0 is also a square - result = result or xi.isZero() + result = not(xi.mres == C.getMontyPrimeMinus1()) + # xi can be: + # - 1 if a square + # - 0 if 0 + # - -1 if a quadratic non-residue -func sqrt_p3mod4*[C](a: var Fp[C]) = +func sqrt_p3mod4[C](a: var Fp[C]) = ## Compute the square root of ``a`` ## ## This requires ``a`` to be a square @@ -262,10 +264,10 @@ func sqrt_p3mod4*[C](a: var Fp[C]) = ## The square root, if it exist is multivalued, ## i.e. both x² == (-x)² ## This procedure returns a deterministic result - static: doAssert C.Mod.limbs[0].BaseType mod 4 == 3 + static: doAssert BaseType(C.Mod.limbs[0]) mod 4 == 3 a.powUnsafeExponent(C.getPrimePlus1div4_BE()) -func sqrt_if_square_p3mod4*[C](a: var Fp[C]): SecretBool = +func sqrt_if_square_p3mod4[C](a: var Fp[C]): SecretBool = ## If ``a`` is a square, compute the square root of ``a`` ## if not, ``a`` is unmodified. ## @@ -290,6 +292,33 @@ func sqrt_if_square_p3mod4*[C](a: var Fp[C]): SecretBool = result = not(a0.mres == C.getMontyPrimeMinus1()) a.ccopy(a1a, result) +func sqrt*[C](a: var Fp[C]) = + ## Compute the square root of ``a`` + ## + ## This requires ``a`` to be a square + ## + ## The result is undefined otherwise + ## + ## The square root, if it exist is multivalued, + ## i.e. both x² == (-x)² + ## This procedure returns a deterministic result + when BaseType(C.Mod.limbs[0]) mod 4 == 3: + sqrt_p3mod4(a) + else: + {.error: "Square root is only implemented for p ≡ 3 (mod 4)".} + +func sqrt_if_square*[C](a: var Fp[C]): SecretBool = + ## If ``a`` is a square, compute the square root of ``a`` + ## if not, ``a`` is unmodified. + ## + ## The square root, if it exist is multivalued, + ## i.e. both x² == (-x)² + ## This procedure returns a deterministic result + when BaseType(C.Mod.limbs[0]) mod 4 == 3: + result = sqrt_if_square_p3mod4(a) + else: + {.error: "Square root is only implemented for p ≡ 3 (mod 4)".} + # ############################################################ # # Field arithmetic ergonomic primitives diff --git a/constantine/arithmetic/finite_fields_inversion.nim b/constantine/arithmetic/finite_fields_inversion.nim index a59706b..99440ce 100644 --- a/constantine/arithmetic/finite_fields_inversion.nim +++ b/constantine/arithmetic/finite_fields_inversion.nim @@ -162,3 +162,18 @@ func inv*(r: var Fp, a: Fp) = # Performance is slower than GCD # To be revisited with faster squaring/multiplications r.mres.steinsGCD(a.mres, Fp.C.getR2modP(), Fp.C.Mod, Fp.C.getPrimePlus1div2()) + +func inv*(a: var Fp) = + ## Inversion modulo p + ## + ## The inverse of 0 is 0. + ## Incidentally this avoids extra check + ## to convert Jacobian and Projective coordinates + ## to affine for elliptic curve + # For now we don't activate the addition chains + # neither for Secp256k1 nor BN curves + # Performance is slower than GCD + # To be revisited with faster squaring/multiplications + var t: typeof(a) # TODO: zero-init needed? + t.mres.steinsGCD(a.mres, Fp.C.getR2modP(), Fp.C.Mod, Fp.C.getPrimePlus1div2()) + a = t diff --git a/constantine/arithmetic/limbs_modular.nim b/constantine/arithmetic/limbs_modular.nim index 894f0c0..d4f89aa 100644 --- a/constantine/arithmetic/limbs_modular.nim +++ b/constantine/arithmetic/limbs_modular.nim @@ -20,7 +20,7 @@ import # # ############################################################ -func div2mod*(a: var Limbs, mp1div2: Limbs) {.inline.}= +func div2_modular*(a: var Limbs, mp1div2: Limbs) {.inline.}= ## Modular Division by 2 ## `a` will be divided in-place ## `mp1div2` is the modulus (M+1)/2 @@ -39,7 +39,7 @@ func div2mod*(a: var Limbs, mp1div2: Limbs) {.inline.}= # a = a shr 1 let wasOdd = a.isOdd() a.shiftRight(1) - let carry = a.cadd(mp1div2, wasOdd) + let carry {.used.} = a.cadd(mp1div2, wasOdd) debug: doAssert not carry.bool # ############################################################ @@ -133,10 +133,10 @@ func steinsGCD*(v: var Limbs, a: Limbs, F, M: Limbs, bits: int, mp1div2: Limbs) u.cswap(v, aLessThanB) # if isOddA: u -= v (mod M) let neg = isOddA and (SecretBool) u.csub(v, isOddA) - let corrected = u.cadd(M, neg) + discard u.cadd(M, neg) # u = u/2 (mod M) - u.div2mod(mp1div2) + u.div2_modular(mp1div2) debug: doAssert bool a.isZero() diff --git a/constantine/arithmetic/limbs_montgomery.nim b/constantine/arithmetic/limbs_montgomery.nim index 76edde4..ce5574a 100644 --- a/constantine/arithmetic/limbs_montgomery.nim +++ b/constantine/arithmetic/limbs_montgomery.nim @@ -596,7 +596,7 @@ func montyPowUnsafeExponent*( acc, acc_len: uint e = 0 while acc_len > 0 or e < exponent.len: - let (k, bits) = montyPowSquarings( + let (_, bits) = montyPowSquarings( a, exponent, M, m0ninv, scratchspace[0], window, acc, acc_len, e, diff --git a/constantine/elliptic/ec_scalar_mul.nim b/constantine/elliptic/ec_scalar_mul.nim index 3fc8cfd..d6531ca 100644 --- a/constantine/elliptic/ec_scalar_mul.nim +++ b/constantine/elliptic/ec_scalar_mul.nim @@ -65,7 +65,7 @@ func scalarMulPrologue( P: var ECP_SWei_Proj, scratchspace: var openarray[ECP_SWei_Proj] ): uint = - ## Setup the scratchspace + ## Setup the scratchspace then set P to infinity ## Returns the fixed-window size for scalar mul with window optimization result = scratchspace.len.getWindowLen() # Precompute window content, special case for window = 1 @@ -218,7 +218,7 @@ func scalarMul*( ## P <- [k] P # This calls endomorphism accelerated scalar mul if available # or the generic scalar mul otherwise - when ECP_SWei_Proj.F.C in {BN254_Snarks, BLS12_381}: + 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) else: diff --git a/constantine/elliptic/ec_weierstrass_affine.nim b/constantine/elliptic/ec_weierstrass_affine.nim index e54d75a..a25c167 100644 --- a/constantine/elliptic/ec_weierstrass_affine.nim +++ b/constantine/elliptic/ec_weierstrass_affine.nim @@ -23,15 +23,29 @@ func curve_eq_rhs*[F](y2: var F, x: F) = t.square(x) t *= x - # No need to precompute `b` in 𝔽p or 𝔽p² or `b/µ` `µ b` - # This procedure is not use in perf critcal situation like signing/verification + # This procedure is not use in perf critical situation like signing/verification # but for testing to quickly create points on a curve. - y2 = F.fromBig F.C.matchingBigInt().fromUint F.C.getCoefB() + # That said D-Twists require an inversion + # and we could avoid doing `b/µ` or `µ*b` at runtime on 𝔽p² + # which would accelerate random point generation + # + # This is preferred to generating random point + # via random scalar multiplication of the curve generator + # as the latter assumes: + # - point addition, doubling work + # - scalar multiplication works + # - a generator point is defined + # i.e. you can't test unless everything is already working + # + # TODO: precomputation needed when deserializing points + # to check if a point is on-curve and prevent denial-of-service + # using slow inversion. + y2.fromBig F.C.matchingBigInt().fromUint F.C.getCoefB() when F is Fp2: when F.C.getSexticTwist() == D_Twist: - y2 /= F.C.get_SNR_Fp2() + y2 /= SexticNonResidue elif F.C.getSexticTwist() == M_Twist: - y2 *= F.C.get_SNR_Fp2() + y2 *= SexticNonResidue else: {.error: "Only twisted curves are supported on extension field 𝔽p²".} diff --git a/constantine/elliptic/ec_weierstrass_projective.nim b/constantine/elliptic/ec_weierstrass_projective.nim index d818b8a..7c27fcf 100644 --- a/constantine/elliptic/ec_weierstrass_projective.nim +++ b/constantine/elliptic/ec_weierstrass_projective.nim @@ -81,7 +81,7 @@ func trySetFromCoordsXandZ*[F](P: var ECP_SWei_Proj[F], x, z: F): SecretBool = ## will be provided, this is intended for testing purposes. P.y.curve_eq_rhs(x) # TODO: supports non p ≡ 3 (mod 4) modulus like BLS12-377 - result = sqrt_if_square_p3mod4(P.y) + result = sqrt_if_square(P.y) P.x.prod(x, z) P.y *= z @@ -100,7 +100,7 @@ func trySetFromCoordX*[F](P: var ECP_SWei_Proj[F], x: F): SecretBool = ## will be provided, this is intended for testing purposes. P.y.curve_eq_rhs(x) # TODO: supports non p ≡ 3 (mod 4) modulus like BLS12-377 - result = sqrt_if_square_p3mod4(P.y) + result = sqrt_if_square(P.y) P.x = x P.z.setOne() @@ -178,32 +178,32 @@ func sum*[F]( t4.sum(t0, t1) # 7. t4 <- t0 + t1 t3 -= t4 # 8. t3 <- t3 - t4 t3 = (X1 + Y1)(X2 + Y2) - (X1 X2 + Y1 Y2) = X1.Y2 + X2.Y1 when F is Fp2 and F.C.getSexticTwist() == D_Twist: - t3 *= F.sexticNonResidue() + t3 *= SexticNonResidue t4.sum(P.y, P.z) # 9. t4 <- Y1 + Z1 r.x.sum(Q.y, Q.z) # 10. X3 <- Y2 + Z2 t4 *= r.x # 11. t4 <- t4 X3 r.x.sum(t1, t2) # 12. X3 <- t1 + t2 X3 = Y1 Y2 + Z1 Z2 t4 -= r.x # 13. t4 <- t4 - X3 t4 = (Y1 + Z1)(Y2 + Z2) - (Y1 Y2 + Z1 Z2) = Y1 Z2 + Y2 Z1 when F is Fp2 and F.C.getSexticTwist() == D_Twist: - t4 *= F.sexticNonResidue() + t4 *= SexticNonResidue r.x.sum(P.x, P.z) # 14. X3 <- X1 + Z1 r.y.sum(Q.x, Q.z) # 15. Y3 <- X2 + Z2 r.x *= r.y # 16. X3 <- X3 Y3 X3 = (X1 Z1)(X2 Z2) r.y.sum(t0, t2) # 17. Y3 <- t0 + t2 Y3 = X1 X2 + Z1 Z2 r.y.diff(r.x, r.y) # 18. Y3 <- X3 - Y3 Y3 = (X1 + Z1)(X2 + Z2) - (X1 X2 + Z1 Z2) = X1 Z2 + X2 Z1 when F is Fp2 and F.C.getSexticTwist() == D_Twist: - t0 *= F.sexticNonResidue() - t1 *= F.sexticNonResidue() + t0 *= SexticNonResidue + t1 *= SexticNonResidue r.x.double(t0) # 19. X3 <- t0 + t0 X3 = 2 X1 X2 t0 += r.x # 20. t0 <- X3 + t0 t0 = 3 X1 X2 t2 *= b3 # 21. t2 <- b3 t2 t2 = 3b Z1 Z2 when F is Fp2 and F.C.getSexticTwist() == M_Twist: - t2 *= F.sexticNonResidue() + t2 *= SexticNonResidue r.z.sum(t1, t2) # 22. Z3 <- t1 + t2 Z3 = Y1 Y2 + 3b Z1 Z2 t1 -= t2 # 23. t1 <- t1 - t2 t1 = Y1 Y2 - 3b Z1 Z2 r.y *= b3 # 24. Y3 <- b3 Y3 Y3 = 3b(X1 Z2 + X2 Z1) when F is Fp2 and F.C.getSexticTwist() == M_Twist: - r.y *= F.sexticNonResidue() + r.y *= SexticNonResidue r.x.prod(t4, r.y) # 25. X3 <- t4 Y3 X3 = 3b(Y1 Z2 + Y2 Z1)(X1 Z2 + X2 Z1) t2.prod(t3, t1) # 26. t2 <- t3 t1 t2 = (X1 Y2 + X2 Y1) (Y1 Y2 - 3b Z1 Z2) r.x.diff(t2, r.x) # 27. X3 <- t2 - X3 X3 = (X1 Y2 + X2 Y1) (Y1 Y2 - 3b Z1 Z2) - 3b(Y1 Z2 + Y2 Z1)(X1 Z2 + X2 Z1) @@ -252,7 +252,7 @@ func double*[F]( # Cost: 8M + 3S + 3 mul(a) + 2 mul(3b) + 15a when F.C.getCoefA() == 0: - var t0 {.noInit.}, t1 {.noInit.}, t2 {.noInit.}: F + var t0 {.noInit.}, t1 {.noInit.}, t2 {.noInit.}, snrY {.noInit.}: F const b3 = 3 * F.C.getCoefB() # Algorithm 9 for curves: @@ -261,14 +261,21 @@ func double*[F]( # X3 = 2XY(Y² - 9bZ²) # Y3 = (Y² - 9bZ²)(Y² + 3bZ²) + 24bY²Z² # Z3 = 8Y³Z - - t0.square(P.y) # 1. t0 <- Y Y + snrY = P.y + when F is Fp2 and F.C.getSexticTwist() == D_Twist: + snrY *= SexticNonResidue + t0.square(P.y) + t0 *= SexticNonResidue + else: + t0.square(P.y) # 1. t0 <- Y Y r.z.double(t0) # 2. Z3 <- t0 + t0 r.z.double() # 3. Z3 <- Z3 + Z3 r.z.double() # 4. Z3 <- Z3 + Z3 Z3 = 8Y² - t1.prod(P.y, P.z) # 5. t1 <- Y Z + t1.prod(snrY, P.z) # 5. t1 <- Y Z t2.square(P.z) # 6. t2 <- Z Z t2 *= b3 # 7. t2 <- b3 t2 + when F is Fp2 and F.C.getSexticTwist() == M_Twist: + t2 *= SexticNonResidue r.x.prod(t2, r.z) # 8. X3 <- t2 Z3 r.y.sum(t0, t2) # 9. Y3 <- t0 + t2 r.z *= t1 # 10. Z3 <- t1 Z3 @@ -277,7 +284,7 @@ func double*[F]( t0 -= t2 # 13. t0 <- t0 - t2 r.y *= t0 # 14. Y3 <- t0 Y3 r.y += r.x # 15. Y3 <- X3 + Y3 - t1.prod(P.x, P.y) # 16. t1 <- X Y + t1.prod(P.x, snrY) # 16. t1 <- X Y r.x.prod(t0, t1) # 17. X3 <- t0 t1 r.x.double() # 18. X3 <- X3 + X3 else: diff --git a/constantine/io/io_bigints.nim b/constantine/io/io_bigints.nim index 47b2075..889fb78 100644 --- a/constantine/io/io_bigints.nim +++ b/constantine/io/io_bigints.nim @@ -428,6 +428,27 @@ func fromHex*(T: type BigInt, s: string): T {.noInit.} = # 2. Convert canonical uint to Big Int result.fromRawUint(bytes, bigEndian) +func appendHex*(dst: var string, big: BigInt, order: static Endianness = bigEndian) = + ## Append the BigInt hex into an accumulator + ## Note. Leading zeros are not removed. + ## Result is prefixed with 0x + ## + ## Output will be padded with 0s to maintain constant-time. + ## + ## CT: + ## - no leaks + ## + ## This is useful to reduce the number of allocations when serializing + ## Fp towers + + # 1. Convert Big Int to canonical uint + const canonLen = (big.bits + 8 - 1) div 8 + var bytes: array[canonLen, byte] + exportRawUint(bytes, big, cpuEndian) + + # 2 Convert canonical uint to hex + dst.add bytes.nativeEndianToHex(order) + func toHex*(big: BigInt, order: static Endianness = bigEndian): string = ## Stringify an int to hex. ## Note. Leading zeros are not removed. @@ -437,11 +458,4 @@ func toHex*(big: BigInt, order: static Endianness = bigEndian): string = ## ## CT: ## - no leaks - - # 1. Convert Big Int to canonical uint - const canonLen = (big.bits + 8 - 1) div 8 - var bytes: array[canonLen, byte] - exportRawUint(bytes, big, cpuEndian) - - # 2 Convert canonical uint to hex - result = bytes.nativeEndianToHex(order) + result.appendHex(big, order) diff --git a/constantine/io/io_ec.nim b/constantine/io/io_ec.nim index d487a5e..de3c578 100644 --- a/constantine/io/io_ec.nim +++ b/constantine/io/io_ec.nim @@ -7,7 +7,7 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - ./io_bigints, ./io_fields, + ./io_bigints, ./io_fields, ./io_towers, ../config/curves, ../elliptic/[ ec_weierstrass_affine, @@ -41,11 +41,11 @@ func toHex*(P: ECP_SWei_Proj): string = var aff {.noInit.}: typeof(P) aff.affineFromProjective(P) - result = $aff.F.C & "(x: " - result &= aff.x.tohex(bigEndian) - result &= ", y: " - result &= aff.y.tohex(bigEndian) - result &= ')' + result = "ECP[" & $aff.F & "](\n x: " + result.appendHex(aff.x, bigEndian) + result &= ",\n y: " + result.appendHex(aff.y, bigEndian) + result &= "\n)" func fromHex*(dst: var ECP_SWei_Proj, x, y: string): bool {.raises: [ValueError].}= ## Convert hex strings to a curve point diff --git a/constantine/io/io_fields.nim b/constantine/io/io_fields.nim index cf132a2..aa7c799 100644 --- a/constantine/io/io_fields.nim +++ b/constantine/io/io_fields.nim @@ -42,6 +42,17 @@ func exportRawUint*(dst: var openarray[byte], ## I.e least significant bit is aligned to buffer boundary exportRawUint(dst, src.toBig(), dstEndianness) +func appendHex*(dst: var string, f: Fp, order: static Endianness = bigEndian) = + ## Stringify a finite field element to hex. + ## Note. Leading zeros are not removed. + ## Result is prefixed with 0x + ## + ## Output will be padded with 0s to maintain constant-time. + ## + ## CT: + ## - no leaks + dst.appendHex(f.toBig(), order) + func toHex*(f: Fp, order: static Endianness = bigEndian): string = ## Stringify a finite field element to hex. ## Note. Leading zeros are not removed. @@ -51,7 +62,7 @@ func toHex*(f: Fp, order: static Endianness = bigEndian): string = ## ## CT: ## - no leaks - result = f.toBig().toHex(order) + result.appendHex(f, order) func fromHex*(dst: var Fp, s: string) {.raises: [ValueError].}= ## Convert a hex string to a element of Fp diff --git a/constantine/io/io_towers.nim b/constantine/io/io_towers.nim new file mode 100644 index 0000000..66a4c11 --- /dev/null +++ b/constantine/io/io_towers.nim @@ -0,0 +1,48 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + + +import + # Standard library + std/typetraits, + # Internal + ./io_bigints, ./io_fields, + ../config/curves, + ../arithmetic/finite_fields, + ../towers + +# No exceptions allowed +{.push raises: [].} +{.push inline.} + +# ############################################################ +# +# Parsing from canonical inputs to internal representation +# +# ############################################################ + +func appendHex*(accum: var string, f: Fp2 or Fp6 or Fp12, order: static Endianness = bigEndian) = + ## Hex accumulator + accum.add static($f.typeof.genericHead() & '(') + for fieldName, fieldValue in fieldPairs(f): + when fieldName != "c0": + accum.add ", " + accum.add fieldName & ": " + accum.appendHex(fieldValue, order) + accum.add ")" + +func toHex*(f: Fp2 or Fp6 or Fp12, order: static Endianness = bigEndian): string = + ## Stringify a tower field element to hex. + ## Note. Leading zeros are not removed. + ## Result is prefixed with 0x + ## + ## Output will be padded with 0s to maintain constant-time. + ## + ## CT: + ## - no leaks + result.appendHex(f, order) diff --git a/constantine/tower_field_extensions/cubic_extensions.nim b/constantine/tower_field_extensions/cubic_extensions.nim index a4da8a8..0681420 100644 --- a/constantine/tower_field_extensions/cubic_extensions.nim +++ b/constantine/tower_field_extensions/cubic_extensions.nim @@ -50,7 +50,7 @@ func square_Chung_Hasan_SQR2(r: var CubicExt, a: CubicExt) = v4.prod(a.c0, a.c1) v4.double() v5.square(a.c2) - r.c1 = β * v5 + r.c1 = NonResidue * v5 r.c1 += v4 r.c2.diff(v4, v5) v3.square(a.c0) @@ -59,7 +59,7 @@ func square_Chung_Hasan_SQR2(r: var CubicExt, a: CubicExt) = v5.prod(a.c1, a.c2) v5.double() v4.square() - r.c0 = β * v5 + r.c0 = NonResidue * v5 r.c0 += v3 r.c2 += v4 r.c2 += v5 @@ -70,29 +70,29 @@ func square_Chung_Hasan_SQR3(r: var CubicExt, a: CubicExt) = mixin prod, square, sum var v0{.noInit.}, v2{.noInit.}: typeof(r.c0) - r.c1.sum(a.c0, a.c2) # r1 = a0 + a2 - v2.diff(r.c1, a.c1) # v2 = a0 - a1 + a2 - r.c1 += a.c1 # r1 = a0 + a1 + a2 - r.c1.square() # r1 = (a0 + a1 + a2)² - v2.square() # v2 = (a0 - a1 + a2)² + r.c1.sum(a.c0, a.c2) # r1 = a0 + a2 + v2.diff(r.c1, a.c1) # v2 = a0 - a1 + a2 + r.c1 += a.c1 # r1 = a0 + a1 + a2 + r.c1.square() # r1 = (a0 + a1 + a2)² + v2.square() # v2 = (a0 - a1 + a2)² - r.c2.sum(r.c1, v2) # r2 = (a0 + a1 + a2)² + (a0 - a1 + a2)² - r.c2.div2() # r2 = ((a0 + a1 + a2)² + (a0 - a1 + a2)²)/2 + r.c2.sum(r.c1, v2) # r2 = (a0 + a1 + a2)² + (a0 - a1 + a2)² + r.c2.div2() # r2 = ((a0 + a1 + a2)² + (a0 - a1 + a2)²)/2 - r.c0.prod(a.c1, a.c2) # r0 = a1 a2 - r.c0.double() # r0 = 2 a1 a2 + r.c0.prod(a.c1, a.c2) # r0 = a1 a2 + r.c0.double() # r0 = 2 a1 a2 - v2.square(a.c2) # v2 = a2² - r.c1 += β * v2 # r1 = (a0 + a1 + a2)² + β a2² - r.c1 -= r.c0 # r1 = (a0 + a1 + a2)² - 2 a1 a2 + β a2² - r.c1 -= r.c2 # r1 = (a0 + a1 + a2)² - 2 a1 a2 - ((a0 + a1 + a2)² + (a0 - a1 + a2)²)/2 + β a2² + v2.square(a.c2) # v2 = a2² + r.c1 += NonResidue * v2 # r1 = (a0 + a1 + a2)² + β a2² + r.c1 -= r.c0 # r1 = (a0 + a1 + a2)² - 2 a1 a2 + β a2² + r.c1 -= r.c2 # r1 = (a0 + a1 + a2)² - 2 a1 a2 - ((a0 + a1 + a2)² + (a0 - a1 + a2)²)/2 + β a2² - v0.square(a.c0) # v0 = a0² - r.c0 *= β # r0 = β 2 a1 a2 - r.c0 += v0 # r0 = a0² + β 2 a1 a2 + v0.square(a.c0) # v0 = a0² + r.c0 *= NonResidue # r0 = β 2 a1 a2 + r.c0 += v0 # r0 = a0² + β 2 a1 a2 - r.c2 -= v0 # r2 = ((a0 + a1 + a2)² + (a0 - a1 + a2)²)/2 - a0² - r.c2 -= v2 # r2 = ((a0 + a1 + a2)² + (a0 - a1 + a2)²)/2 - a0² - a2² + r.c2 -= v0 # r2 = ((a0 + a1 + a2)² + (a0 - a1 + a2)²)/2 - a0² + r.c2 -= v2 # r2 = ((a0 + a1 + a2)² + (a0 - a1 + a2)²)/2 - a0² - a2² func square*(r: var CubicExt, a: CubicExt) {.inline.} = ## Returns r = a² @@ -115,7 +115,7 @@ func prod*(r: var CubicExt, a, b: CubicExt) = r.c0 *= t r.c0 -= v1 r.c0 -= v2 - r.c0 *= β + r.c0 *= NonResidue r.c0 += v0 # r.c1 = (a.c0 + a.c1) * (b.c0 + b.c1) - v0 - v1 + β v2 @@ -124,7 +124,7 @@ func prod*(r: var CubicExt, a, b: CubicExt) = r.c1 *= t r.c1 -= v0 r.c1 -= v1 - r.c1 += β * v2 + r.c1 += NonResidue * v2 # r.c2 = (a.c0 + a.c2) * (b.c0 + b.c2) - v0 - v2 + v1 r.c2.sum(a.c0, a.c2) @@ -159,13 +159,13 @@ func inv*(r: var CubicExt, a: CubicExt) = # A <- a0² - β a1 a2 r.c0.square(a.c0) v1.prod(a.c1, a.c2) - v1 *= β + v1 *= NonResidue r.c0 -= v1 # B in v1 # B <- β a2² - a0 a1 v1.square(a.c2) - v1 *= β + v1 *= NonResidue v2.prod(a.c0, a.c1) v1 -= v2 @@ -177,8 +177,8 @@ func inv*(r: var CubicExt, a: CubicExt) = # F in v3 # F <- β a1 C + a0 A + β a2 B - r.c1.prod(v1, β * a.c2) - r.c2.prod(v2, β * a.c1) + r.c1.prod(v1, NonResidue * a.c2) + r.c2.prod(v2, NonResidue * a.c1) v3.prod(r.c0, a.c0) v3 += r.c1 v3 += r.c2 diff --git a/constantine/tower_field_extensions/exponentiations.nim b/constantine/tower_field_extensions/exponentiations.nim new file mode 100644 index 0000000..2c8c7ea --- /dev/null +++ b/constantine/tower_field_extensions/exponentiations.nim @@ -0,0 +1,223 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + + +import + ../arithmetic, + ../config/[common, curves], + ../primitives, + ./tower_common, + ./quadratic_extensions, + ./cubic_extensions + +# ############################################################ +# +# Exponentiations (pow and square roots) in extension fields +# +# ############################################################ + +# Square root should be implemented in constant-time for hash-to-curve: +# https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-05#section-4 +# +# Further non-constant-time optimization may be used +# - Square Root Computation over Even Extension Fields +# Gora Adj, Francisco Rodríguez-Henríquez, 2012 +# https://eprint.iacr.org/2012/685 + +# No exceptions allowed +{.push raises: [].} + +# Pow +# ----------------------------------------------------------- + +template checkPowScratchSpaceLen(len: int) = + ## Checks that there is a minimum of scratchspace to hold the temporaries + debug: + assert len >= 2, "Internal Error: the scratchspace for powmod should be equal or greater than 2" + +func getWindowLen(bufLen: int): uint = + ## Compute the maximum window size that fits in the scratchspace buffer + checkPowScratchSpaceLen(bufLen) + result = 4 + while (1 shl result) + 1 > bufLen: + dec result + +func powPrologue[F](a: var F, scratchspace: var openarray[F]): uint = + ## Setup the scratchspace, then set a to 1. + ## Returns the fixed-window size for exponentiation with window optimization + result = scratchspace.len.getWindowLen + # Precompute window content, special case for window = 1 + # (i.e scratchspace has only space for 2 temporaries) + # The content scratchspace[2+k] is set at [k]P + # with scratchspace[0] untouched + if result == 1: + scratchspace[1] = a + else: + scratchspace[2] = a + for k in 2 ..< 1 shl result: + scratchspace[k+1] + a.setOne() + +func powSquarings[F]( + a: var F, + exponent: openArray[byte], + tmp: var F, + window: uint, + acc, acc_len: var uint, + e: var uint + ): tuple[k, bits: uint] {.inline.}= + ## Squaring step of exponentiation by squaring + ## Get the next k bits in range [1, window) + ## Square k times + ## Returns the number of squarings done and the corresponding bits + ## + ## Updates iteration variables and accumulators + # Due to the high number of parameters, + # forcing this inline actually reduces the code size + # + # ⚠️: Extreme care should be used to not leak + # the exponent bits nor its real bitlength + # i.e. if the exponent is zero but encoded in a + # 256-bit integer, only "256" should leak + # as for some application like RSA + # the exponent might be the user secret key. + + # Get the next bits + # acc/acc_len must be uint to avoid Nim runtime checks leaking bits + # acc/acc_len must be uint to avoid Nim runtime checks leaking bits + # e is public + var k = window + if acc_len < window: + if e < exponent.len: + acc = (acc shl 8) or exponent[e].uint + inc e + acc_len += 8 + else: # Drained all exponent bits + k = acc_len + + let bits = (acc shr (acc_len - k)) and ((1'u32 shl k) - 1) + acc_len -= k + + # We have k bits and can do k squaring + for i in 0 ..< k: + a.square() + + return (k, bits) + +func powUnsafeExponent( + a: var ExtensionField, + exponent: openArray[byte], + scratchspace: var openArray[byte] + ) = + ## Extension field exponentiation r = a^exponent (mod p^m) + ## + ## Warning ⚠️ : + ## This is an optimization for public exponent + ## Otherwise bits of the exponent can be retrieved with: + ## - memory access analysis + ## - power analysis + ## - timing analysis + + # TODO: scratchspace[1] is unused when window > 1 + let window = powPrologue(a, scratchspace) + + var + acc, acc_len: uint + e = 0 + while acc_len > 0 or e < exponent.len: + let (_, bits) = powSquarings( + a, exponent, + scratchspace[0], window, + acc, acc_len, e + ) + + ## Warning ⚠️: Exposes the exponent bits + if bits != 0: + if window > 1: + scratchspace[0].prod(a, scratchspace[1+bits]) + else: + # scratchspace[1] holds the original `a` + scratchspace[0].prod(a, scratchspace[1]) + a = scratchspace[0] + +# Square root +# ----------------------------------------------------------- +# +# Warning ⚠️: +# p the characteristic, i.e. the prime modulus of the base field +# in extension field we require q = p^m be of special form +# i.e. q ≡ 3 (mod 4) or q ≡ 9 (mod 16) +# +# In Fp2 in particular p² ≡ 1 (mod 4) always hold +# and p² ≡ 5 (mod 8) is not possible +# if Fp2 = Fp[v]/(v² − β) with β a quadratic non-residue in Fp + +func isSquare*(a: QuadraticExt): SecretBool = + ## Returns true if ``a`` is a square (quadratic residue) in 𝔽p2 + ## + ## Assumes that the prime modulus ``p`` is public. + # Implementation: + # + # (a0, a1) = a in F(p^2) + # is_square(a) = is_square(|a|) over F(p) + # where |a| = a0^2 + a1^2 + # + # This can be done recursively in an extension tower + # + # https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-08#appendix-G.5 + # https://eprint.iacr.org/2012/685 + + mixin fromComplexExtension # TODO: relax this + static: doAssert a.fromComplexExtension() + + var tv1{.noInit.}, tv2{.noInit.}: typeof(a.c0) + + tv1.square(a.c0) # a0² + tv2.square(a.c1) # - β a1² with β = 𝑖² in a complex extension field + + tv1 += tv2 # a0 - (-1) a1² + result = tv1.isSquare() + +func sqrt_if_square*(a: var QuadraticExt): SecretBool = + ## If ``a`` is a square, compute the square root of ``a`` + ## if not, ``a`` is unmodified. + ## + ## The square root, if it exist is multivalued, + ## i.e. both x² == (-x)² + ## This procedure returns a deterministic result + # + # Implementation via the complex method (which confusingly does not require a complex field) + # We make it constant-time via conditional copies + + mixin fromComplexExtension # TODO: relax this + static: doAssert a.fromComplexExtension() + + var t1{.noInit.}, t2{.noInit.}, t3{.noInit.}: typeof(a.c0) + + t1.square(a.c0) # a0² + t2.square(a.c1) # - β a1² with β = 𝑖² in a complex extension field + + t1 += t2 # a0 - (-1) a1² + result = t1.sqrt_if_square() + + t2.sum(a.c0, t1) + t2.div2() + + t3.diff(a.c0, t1) + t3.div2() + + let quadResidTest = t2.isSquare() + t2.ccopy(t3, not quadResidTest) + + t2.sqrt() + a.c0.ccopy(t2, result) + + t2.double() + t1.inv(t2) + t1 *= a.c1 + a.c1.ccopy(t1, result) diff --git a/constantine/tower_field_extensions/quadratic_extensions.nim b/constantine/tower_field_extensions/quadratic_extensions.nim index f0b1538..48b5892 100644 --- a/constantine/tower_field_extensions/quadratic_extensions.nim +++ b/constantine/tower_field_extensions/quadratic_extensions.nim @@ -126,14 +126,14 @@ func square_generic(r: var QuadraticExt, a: QuadraticExt) = # r0 <- (c0 + c1)(c0 + β c1) r.c0.sum(a.c0, a.c1) - r.c1.sum(a.c0, β * a.c1) + r.c1.sum(a.c0, NonResidue * a.c1) r.c0 *= r.c1 # r1 <- c0 c1 r.c1.prod(a.c0, a.c1) # r0 = (c0 + c1)(c0 + β c1) - β c0c1 - c0c1 - r.c0 -= β * r.c1 + r.c0 -= NonResidue * r.c1 r.c0 -= r.c1 # r1 = 2 c0c1 @@ -161,7 +161,7 @@ func prod_generic(r: var QuadraticExt, a, b: QuadraticExt) = r.c1 -= t # r0 <- a0 b0 + β a1 b1 - r.c0 += β * t + r.c0 += NonResidue * t # Exported symbols # ------------------------------------------------------------------- @@ -204,15 +204,15 @@ func inv*(r: var QuadraticExt, a: QuadraticExt) = when r.fromComplexExtension(): v0 += v1 else: - v0 -= β * v1 # v0 = a0² - β a1² (the norm / squared magnitude of a) + v0 -= NonResidue * v1 # v0 = a0² - β a1² (the norm / squared magnitude of a) # [1 Inv, 2 Sqr, 1 Add] - v1.inv(v0) # v1 = 1 / (a0² - β a1²) + v1.inv(v0) # v1 = 1 / (a0² - β a1²) # [1 Inv, 2 Mul, 2 Sqr, 1 Add, 1 Neg] - r.c0.prod(a.c0, v1) # r0 = a0 / (a0² - β a1²) - v0.neg(v1) # v0 = -1 / (a0² - β a1²) - r.c1.prod(a.c1, v0) # r1 = -a1 / (a0² - β a1²) + r.c0.prod(a.c0, v1) # r0 = a0 / (a0² - β a1²) + v0.neg(v1) # v0 = -1 / (a0² - β a1²) + r.c1.prod(a.c1, v0) # r1 = -a1 / (a0² - β a1²) func `*=`*(a: var QuadraticExt, b: QuadraticExt) {.inline.} = ## In-place multiplication diff --git a/constantine/tower_field_extensions/tower_common.nim b/constantine/tower_field_extensions/tower_common.nim index 5803409..88716cd 100644 --- a/constantine/tower_field_extensions/tower_common.nim +++ b/constantine/tower_field_extensions/tower_common.nim @@ -18,10 +18,10 @@ import # ------------------------------------------------------------------- type - β* = object - ## Non-Residue β + NonResidue* = object + ## Non-Residue ## - ## Placeholder for the appropriate quadratic or cubic non-residue + ## Placeholder for the appropriate quadratic, cubic or sectic non-residue CubicExt* = concept x ## Cubic Extension field concept @@ -38,7 +38,7 @@ type x.c0 is BaseField x.c1 is BaseField - ExtensionField = QuadraticExt or CubicExt + ExtensionField* = QuadraticExt or CubicExt # Initialization # ------------------------------------------------------------------- @@ -56,6 +56,14 @@ func setOne*(a: var ExtensionField) = else: fA.setZero() +func fromBig*(a: var ExtensionField, src: BigInt) = + ## Set ``a`` to the bigint value int eh extension field + for fieldName, fA in fieldPairs(a): + when fieldName == "c0": + fA.fromBig(src) + else: + fA.setZero() + # Comparison # ------------------------------------------------------------------- @@ -145,3 +153,77 @@ func diff*(r: var CubicExt, a, b: CubicExt) = r.c0.diff(a.c0, b.c0) r.c1.diff(a.c1, b.c1) r.c2.diff(a.c2, b.c2) + +# Multiplication by a small integer known at compile-time +# ------------------------------------------------------------------- + +func `*=`*(a: var ExtensionField, b: static int) {.inline.} = + ## Multiplication by a small integer known at compile-time + + const negate = b < 0 + const b = if negate: -b + else: b + when negate: + a.neg(a) + when b == 0: + a.setZero() + elif b == 1: + return + elif b == 2: + a.double() + elif b == 3: + let t1 = a + a.double() + a += t1 + elif b == 4: + a.double() + a.double() + elif b == 5: + let t1 = a + a.double() + a.double() + a += t1 + elif b == 6: + a.double() + let t2 = a + a.double() # 4 + a += t2 + elif b == 7: + let t1 = a + a.double() + let t2 = a + a.double() # 4 + a += t2 + a += t1 + elif b == 8: + a.double() + a.double() + a.double() + elif b == 9: + let t1 = a + a.double() + a.double() + a.double() # 8 + a += t1 + elif b == 10: + a.double() + let t2 = a + a.double() + a.double() # 8 + a += t2 + elif b == 11: + let t1 = a + a.double() + let t2 = a + a.double() + a.double() # 8 + a += t2 + a += t1 + elif b == 12: + a.double() + a.double() # 4 + let t4 = a + a.double() # 8 + a += t4 + else: + {.error: "Multiplication by this small int not implemented".} diff --git a/constantine/towers.nim b/constantine/towers.nim index b2e1692..13cdcb4 100644 --- a/constantine/towers.nim +++ b/constantine/towers.nim @@ -9,13 +9,15 @@ import ./arithmetic, ./config/curves, + ./io/io_fields, ./tower_field_extensions/[ tower_common, quadratic_extensions, - cubic_extensions + cubic_extensions, + exponentiations ] -export tower_common, quadratic_extensions, cubic_extensions +export tower_common, quadratic_extensions, cubic_extensions, exponentiations # 𝔽p2 # ---------------------------------------------------------------- @@ -24,6 +26,8 @@ type Fp2*[C: static Curve] = object c0*, c1*: Fp[C] + β = NonResidue + template fromComplexExtension*[F](elem: F): static bool = ## Returns true if the input is a complex extension ## i.e. the irreducible polynomial chosen is @@ -46,6 +50,73 @@ func `*`*(_: typedesc[β], a: Fp): Fp {.inline, noInit.} = result = a result *= β +type + SexticNonResidue* = object + +func `*=`*(a: var Fp2, _: typedesc[SexticNonResidue]) {.inline.} = + ## Multiply an element of 𝔽p2 by the sextic non-residue + ## chosen to construct the sextic twist + # Yet another const tuple unpacking bug + const u = Fp2.C.get_SNR_Fp2()[0] # Sextic non-residue to construct 𝔽p12 + const v = Fp2.C.get_SNR_Fp2()[1] + const Beta = Fp2.C.get_QNR_Fp() # Quadratic non-residue to construct 𝔽p2 + # ξ = u + v x + # and x² = β + # + # (c0 + c1 x) (u + v x) => u c0 + (u c0 + u c1)x + v c1 x² + # => u c0 + β v c1 + (v c0 + u c1) x + when a.fromComplexExtension() and u == 1 and v == 1: + let t = a.c0 + a.c0 -= a.c1 + a.c1 += t + else: + let a0 = a.c0 + let a1 = a.c1 + when a.fromComplexExtension(): + a.c0.diff(u * a0, v * a1) + else: + a.c0.sum(u * a0, (Beta * v) * a1) + a.c1.sum(v * a0, u * a1) + +func `/=`*(a: var Fp2, _: typedesc[SexticNonResidue]) {.inline.} = + ## Multiply an element of 𝔽p by the quadratic non-residue + ## chosen to construct sextic twist + # Yet another const tuple unpacking bug + const u = Fp2.C.get_SNR_Fp2()[0] # Sextic non-residue to construct 𝔽p12 + const v = Fp2.C.get_SNR_Fp2()[1] + const Beta = Fp2.C.get_QNR_Fp() # Quadratic non-residue to construct 𝔽p2 + # ξ = u + v x + # and x² = β + # + # (c0 + c1 x) / (u + v x) => (c0 + c1 x)(u - v x) / ((u + vx)(u-vx)) + # => u c0 - v c1 x² + (u c1 - v c0) x / (u² - x²v²) + # => 1/(u² - βv²) * (uc0 - β v c1, u c1 - v c0) + # With β = 𝑖 = √-1 + # 1/(u² + v²) * (u c0 + v c1, u c1 - v c0) + # + # With β = 𝑖 = √-1 and ξ = 1 + 𝑖 + # 1/2 * (c0 + c1, c1 - c0) + + when a.fromComplexExtension() and u == 1 and v == 1: + let t = a.c0 + a.c0 += a.c1 + a.c1 -= t + a.div2() + else: + let a0 = a.c0 + let a1 = a.c1 + const u2v2 = u*u - Beta*v*v # (u² - βv²) + # TODO can be precomputed (or precompute b/µ the twist coefficient) + # and use faster non-constant-time inversion in the VM + var u2v2inv {.noInit.}: a.c0.typeof + u2v2inv.fromUint(u2v2) + u2v2inv.inv() + + a.c0.diff(u * a0, (Beta * v) * a1) + a.c1.diff(u * a1, v * a0) + a.c0 *= u2v2inv + a.c1 *= u2v2inv + # 𝔽p6 # ---------------------------------------------------------------- @@ -82,8 +153,8 @@ func `*`*(_: typedesc[β], a: Fp2): Fp2 {.inline, noInit.} = result.c1.sum(v * a.c0, u * a.c1 ) func `*=`*(a: var Fp2, _: typedesc[ξ]) {.inline.} = - ## Multiply an element of 𝔽p by the quadratic non-residue - ## chosen to construct 𝔽p2 + ## Multiply an element of 𝔽p2 by the quadratic non-residue + ## chosen to construct 𝔽p6 # Yet another const tuple unpacking bug const u = Fp2.C.get_CNR_Fp2()[0] # Cubic non-residue to construct 𝔽p6 const v = Fp2.C.get_CNR_Fp2()[1] diff --git a/sage/curve_family_bls12.sage b/sage/curve_family_bls12.sage index bda3566..6f34e46 100644 --- a/sage/curve_family_bls12.sage +++ b/sage/curve_family_bls12.sage @@ -30,6 +30,8 @@ def compute_curve_characteristic(u_str): else: print(' Parameter u (hex): 0x' + u.hex()) + print() + print(f' p mod 3: ' + str(p % 3)) print(f' p mod 4: ' + str(p % 4)) print(f' p mod 8: ' + str(p % 8)) @@ -38,6 +40,14 @@ def compute_curve_characteristic(u_str): print() + print(f' p^2 mod 3: ' + str(p^2 % 3)) + print(f' p^2 mod 4: ' + str(p^2 % 4)) + print(f' p^2 mod 8: ' + str(p^2 % 8)) + print(f' p^2 mod 12: ' + str(p^2 % 12)) + print(f' p^2 mod 16: ' + str(p^2 % 16)) + + print() + print(f' Endomorphism-based acceleration when p mod 3 == 1') print(f' Endomorphism can be field multiplication by one of the non-trivial cube root of unity 𝜑') print(f' Rationale:') diff --git a/sage/curve_family_bn.sage b/sage/curve_family_bn.sage index a546487..1b3a006 100644 --- a/sage/curve_family_bn.sage +++ b/sage/curve_family_bn.sage @@ -38,6 +38,14 @@ def compute_curve_characteristic(u_str): print() + print(f' p^2 mod 3: ' + str(p^2 % 3)) + print(f' p^2 mod 4: ' + str(p^2 % 4)) + print(f' p^2 mod 8: ' + str(p^2 % 8)) + print(f' p^2 mod 12: ' + str(p^2 % 12)) + print(f' p^2 mod 16: ' + str(p^2 % 16)) + + print() + print(f' Endomorphism-based acceleration when p mod 3 == 1') print(f' Endomorphism can be field multiplication by one of the non-trivial cube root of unity 𝜑') print(f' Rationale:') diff --git a/tests/support/ec_reference_scalar_mult.nim b/tests/support/ec_reference_scalar_mult.nim index 9220020..6c1d0bb 100644 --- a/tests/support/ec_reference_scalar_mult.nim +++ b/tests/support/ec_reference_scalar_mult.nim @@ -10,7 +10,7 @@ import # Internals ../../constantine/config/[common, curves], ../../constantine/arithmetic, - ../../constantine/elliptic/[ec_weierstrass_affine, ec_weierstrass_projective] + ../../constantine/elliptic/ec_weierstrass_projective # Support files for testing Elliptic Curve arithmetic # ------------------------------------------------------------------------------ diff --git a/tests/test_bigints.nim b/tests/test_bigints.nim index 45726bb..11ae074 100644 --- a/tests/test_bigints.nim +++ b/tests/test_bigints.nim @@ -12,8 +12,10 @@ import std/unittest, ../constantine/config/common, ../constantine/primitives +echo "\n------------------------------------------------------\n" + proc mainArith() = - suite "isZero": + suite "isZero" & " [" & $WordBitwidth & "-bit mode]": test "isZero for zero": var x: BigInt[128] check: x.isZero().bool @@ -28,7 +30,7 @@ proc mainArith() = var x = fromHex(BigInt[128], "0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF") check: not x.isZero().bool - suite "Arithmetic operations - Addition": + suite "Arithmetic operations - Addition" & " [" & $WordBitwidth & "-bit mode]": test "Adding 2 zeros": var a = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000000") let b = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000000") @@ -128,7 +130,7 @@ proc mainArith() = bool(a == c) not bool(carry) - suite "BigInt + SecretWord": + suite "BigInt + SecretWord" & " [" & $WordBitwidth & "-bit mode]": test "Addition limbs carry": block: # P256 / 2 var a = BigInt[256].fromhex"0x7fffffff800000008000000000000000000000007fffffffffffffffffffffff" @@ -138,7 +140,7 @@ proc mainArith() = discard a.add(SecretWord 1) check: bool(a == expected) - suite "Multi-precision multiplication": + suite "Multi-precision multiplication" & " [" & $WordBitwidth & "-bit mode]": test "Same size operand into double size result": block: var r: BigInt[256] @@ -178,7 +180,7 @@ proc mainArith() = r.prod(b, a) check: bool(r == expected) - suite "Multi-precision multiplication keeping only high words": + suite "Multi-precision multiplication keeping only high words" & " [" & $WordBitwidth & "-bit mode]": test "Same size operand into double size result - discard first word": block: var r: BigInt[256] @@ -263,7 +265,7 @@ proc mainArith() = r.prod_high_words(b, a, 2) check: bool(r == expected) - suite "Modular operations - small modulus": + suite "Modular operations - small modulus" & " [" & $WordBitwidth & "-bit mode]": # Vectors taken from Stint - https://github.com/status-im/nim-stint test "100 mod 13": # Test 1 word and more than 1 word @@ -312,7 +314,7 @@ proc mainArith() = check: bool(r == BigInt[8].fromUint(0'u8)) - suite "Modular operations - small modulus - Stint specific failures highlighted by property-based testing": + suite "Modular operations - small modulus - Stint specific failures highlighted by property-based testing" & " [" & $WordBitwidth & "-bit mode]": # Vectors taken from Stint - https://github.com/status-im/nim-stint test "Modulo: 65696211516342324 mod 174261910798982": let u = 65696211516342324'u64 @@ -341,7 +343,7 @@ proc mainArith() = bool(r == BigInt[40].fromUint(u mod v)) proc mainNeg() = - suite "Conditional negation": + suite "Conditional negation" & " [" & $WordBitwidth & "-bit mode]": test "Conditional negation": block: var a = fromHex(BigInt[128], "0x12345678_FF11FFAA_00321321_CAFECAFE") @@ -439,7 +441,7 @@ proc mainNeg() = bool(b == b2) proc mainCopySwap() = - suite "Copy and Swap": + suite "Copy and Swap" & " [" & $WordBitwidth & "-bit mode]": test "Conditional copy": block: var a = fromHex(BigInt[128], "0x12345678_FF11FFAA_00321321_CAFECAFE") @@ -485,7 +487,7 @@ proc mainCopySwap() = bool(eB == b) proc mainModularInverse() = - suite "Modular Inverse (with odd modulus)": + suite "Modular Inverse (with odd modulus)" & " [" & $WordBitwidth & "-bit mode]": # Note: We don't define multi-precision multiplication # because who needs it when you have Montgomery? # ¯\_(ツ)_/¯ diff --git a/tests/test_bigints_mod_vs_gmp.nim b/tests/test_bigints_mod_vs_gmp.nim index de7fb01..d8fb058 100644 --- a/tests/test_bigints_mod_vs_gmp.nim +++ b/tests/test_bigints_mod_vs_gmp.nim @@ -16,6 +16,7 @@ import ../constantine/arithmetic, ../constantine/primitives +echo "\n------------------------------------------------------\n" # We test up to 1024-bit, more is really slow var bitSizeRNG {.compileTime.} = initRand(1234) diff --git a/tests/test_bigints_mul_high_words_vs_gmp.nim b/tests/test_bigints_mul_high_words_vs_gmp.nim index 85c48f2..b44f8e1 100644 --- a/tests/test_bigints_mul_high_words_vs_gmp.nim +++ b/tests/test_bigints_mul_high_words_vs_gmp.nim @@ -17,6 +17,7 @@ import ../constantine/primitives, ../constantine/config/[common, type_bigint] +echo "\n------------------------------------------------------\n" # We test up to 1024-bit, more is really slow var bitSizeRNG {.compileTime.} = initRand(1234) diff --git a/tests/test_bigints_mul_vs_gmp.nim b/tests/test_bigints_mul_vs_gmp.nim index 9b34fb6..f777a52 100644 --- a/tests/test_bigints_mul_vs_gmp.nim +++ b/tests/test_bigints_mul_vs_gmp.nim @@ -17,6 +17,7 @@ import ../constantine/primitives, ../constantine/config/[common, type_bigint] +echo "\n------------------------------------------------------\n" # We test up to 1024-bit, more is really slow var bitSizeRNG {.compileTime.} = initRand(1234) diff --git a/tests/test_bigints_multimod.nim b/tests/test_bigints_multimod.nim index 3c5e5e8..94154b8 100644 --- a/tests/test_bigints_multimod.nim +++ b/tests/test_bigints_multimod.nim @@ -10,12 +10,15 @@ import # Standard library std/unittest, # Third-party + ../constantine/config/common, ../constantine/io/io_bigints, ../constantine/arithmetic, ../constantine/primitives +echo "\n------------------------------------------------------\n" + proc main() = - suite "Bigints - Multiprecision modulo": + suite "Bigints - Multiprecision modulo" & " [" & $WordBitwidth & "-bit mode]": test "bitsize 237 mod bitsize 192": let a = BigInt[237].fromHex("0x123456789012345678901234567890123456789012345678901234567890") let m = BigInt[192].fromHex("0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB") diff --git a/tests/test_ec_bls12_381.nim b/tests/test_ec_sage_bls12_381.nim similarity index 98% rename from tests/test_ec_bls12_381.nim rename to tests/test_ec_sage_bls12_381.nim index ffe8740..9b38f8e 100644 --- a/tests/test_ec_bls12_381.nim +++ b/tests/test_ec_sage_bls12_381.nim @@ -17,6 +17,8 @@ import # Test utilities ./support/ec_reference_scalar_mult +echo "\n------------------------------------------------------\n" + proc test( id: int, EC: typedesc[ECP_SWei_Proj], @@ -51,7 +53,7 @@ proc test( doAssert: bool(Q == impl) doAssert: bool(Q == endo) -suite "Scalar Multiplication (cofactor cleared): BLS12_381 implementation (and unsafe reference impl) vs SageMath": +suite "Scalar Multiplication (cofactor cleared): BLS12_381 implementation vs SageMath" & " [" & $WordBitwidth & "-bit mode]": # Generated via sage sage/testgen_bls12_381.sage test( id = 1, diff --git a/tests/test_ec_bn254.nim b/tests/test_ec_sage_bn254.nim similarity index 97% rename from tests/test_ec_bn254.nim rename to tests/test_ec_sage_bn254.nim index 6044477..6b2568b 100644 --- a/tests/test_ec_bn254.nim +++ b/tests/test_ec_sage_bn254.nim @@ -17,6 +17,8 @@ import # Test utilities ./support/ec_reference_scalar_mult +echo "\n------------------------------------------------------\n" + proc test( id: int, EC: typedesc[ECP_SWei_Proj], @@ -51,7 +53,7 @@ proc test( doAssert: bool(Q == impl) doAssert: bool(Q == endo) -suite "Scalar Multiplication: BN254 implementation (and unsafe reference impl) vs SageMath": +suite "Scalar Multiplication: BN254 implementation vs SageMath" & " [" & $WordBitwidth & "-bit mode]": # Generated via sage sage/testgen_bn254_snarks.sage test( id = 1, diff --git a/tests/test_ec_template.nim b/tests/test_ec_template.nim new file mode 100644 index 0000000..6c652b9 --- /dev/null +++ b/tests/test_ec_template.nim @@ -0,0 +1,399 @@ +# 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. + +# ############################################################ +# +# Template tests for elliptic curve operations +# +# ############################################################ + +import + # Standard library + std/[unittest, times], + # Internals + ../constantine/config/[common, curves], + ../constantine/arithmetic, + ../constantine/towers, + ../constantine/io/io_bigints, + ../constantine/elliptic/[ec_weierstrass_projective, ec_scalar_mul], + # Test utilities + ../helpers/prng_unsafe, + ./support/ec_reference_scalar_mult + +proc run_EC_addition_tests*( + ec: typedesc, + Iters: static int, + moduleName: string + ) = + + # Random seed for reproducibility + var rng: RngState + let seed = uint32(getTime().toUnix() and (1'i64 shl 32 - 1)) # unixTime mod 2^32 + rng.seed(seed) + echo "\n------------------------------------------------------\n" + echo moduleName, " xoshiro512** seed: ", seed + + when ec.F is Fp: + const G1_or_G2 = "G1" + else: + const G1_or_G2 = "G2" + + const testSuiteDesc = "Elliptic curve in Short Weierstrass form with projective coordinates" + + suite testSuiteDesc & " - " & $ec & " - [" & $WordBitwidth & "-bit mode]": + test "The infinity point is the neutral element w.r.t. to EC " & G1_or_G2 & " addition": + proc test(EC: typedesc, randZ: static bool) = + var inf {.noInit.}: EC + inf.setInf() + check: bool inf.isInf() + + for _ in 0 ..< Iters: + var r{.noInit.}: EC + when randZ: + let P = rng.random_unsafe_with_randZ(EC) + else: + let P = rng.random_unsafe(EC) + + r.sum(P, inf) + check: bool(r == P) + + r.sum(inf, P) + check: bool(r == P) + + test(ec, randZ = false) + test(ec, randZ = true) + + test "Adding opposites gives an infinity point": + proc test(EC: typedesc, randZ: static bool) = + for _ in 0 ..< Iters: + var r{.noInit.}: EC + when randZ: + let P = rng.random_unsafe_with_randZ(EC) + else: + let P = rng.random_unsafe(EC) + var Q = P + Q.neg() + + r.sum(P, Q) + check: bool r.isInf() + + r.sum(Q, P) + check: bool r.isInf() + + test(ec, randZ = false) + test(ec, randZ = true) + + test "EC " & G1_or_G2 & " add is commutative": + proc test(EC: typedesc, randZ: static bool) = + for _ in 0 ..< Iters: + var r0{.noInit.}, r1{.noInit.}: EC + when randZ: + let P = rng.random_unsafe_with_randZ(EC) + let Q = rng.random_unsafe_with_randZ(EC) + else: + let P = rng.random_unsafe(EC) + let Q = rng.random_unsafe(EC) + + r0.sum(P, Q) + r1.sum(Q, P) + check: bool(r0 == r1) + + test(ec, randZ = false) + test(ec, randZ = true) + + test "EC " & G1_or_G2 & " add is associative": + proc test(EC: typedesc, randZ: static bool) = + for _ in 0 ..< Iters: + when randZ: + let a = rng.random_unsafe_with_randZ(EC) + let b = rng.random_unsafe_with_randZ(EC) + let c = rng.random_unsafe_with_randZ(EC) + else: + let a = rng.random_unsafe(EC) + let b = rng.random_unsafe(EC) + let c = rng.random_unsafe(EC) + + var tmp1{.noInit.}, tmp2{.noInit.}: EC + + # r0 = (a + b) + c + tmp1.sum(a, b) + tmp2.sum(tmp1, c) + let r0 = tmp2 + + # r1 = a + (b + c) + tmp1.sum(b, c) + tmp2.sum(a, tmp1) + let r1 = tmp2 + + # r2 = (a + c) + b + tmp1.sum(a, c) + tmp2.sum(tmp1, b) + let r2 = tmp2 + + # r3 = a + (c + b) + tmp1.sum(c, b) + tmp2.sum(a, tmp1) + let r3 = tmp2 + + # r4 = (c + a) + b + tmp1.sum(c, a) + tmp2.sum(tmp1, b) + let r4 = tmp2 + + # ... + + check: + bool(r0 == r1) + bool(r0 == r2) + bool(r0 == r3) + bool(r0 == r4) + + test(ec, randZ = false) + test(ec, randZ = true) + + test "EC " & G1_or_G2 & " double and EC " & G1_or_G2 & " add are consistent": + proc test(EC: typedesc, randZ: static bool) = + for _ in 0 ..< Iters: + when randZ: + let a = rng.random_unsafe_with_randZ(EC) + else: + let a = rng.random_unsafe(EC) + + var r0{.noInit.}, r1{.noInit.}: EC + + r0.double(a) + r1.sum(a, a) + + check: bool(r0 == r1) + + test(ec, randZ = false) + test(ec, randZ = true) + +proc run_EC_mul_sanity_tests*( + ec: typedesc, + ItersMul: static int, + moduleName: string + ) = + + # Random seed for reproducibility + var rng: RngState + let seed = uint32(getTime().toUnix() and (1'i64 shl 32 - 1)) # unixTime mod 2^32 + rng.seed(seed) + echo "\n------------------------------------------------------\n" + echo moduleName, " xoshiro512** seed: ", seed + + when ec.F is Fp: + const G1_or_G2 = "G1" + else: + const G1_or_G2 = "G2" + + const testSuiteDesc = "Elliptic curve in Short Weierstrass form with projective coordinates" + + suite testSuiteDesc & " - " & $ec & " - [" & $WordBitwidth & "-bit mode]": + test "EC " & G1_or_G2 & " mul [0]P == Inf": + proc test(EC: typedesc, bits: static int, randZ: static bool) = + for _ in 0 ..< ItersMul: + when randZ: + let a = rng.random_unsafe_with_randZ(EC) + else: + let a = rng.random_unsafe(EC) + + # 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) + + check: + bool(impl.isInf()) + bool(reference.isInf()) + + test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = false) + test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = true) + + test "EC " & G1_or_G2 & " mul [1]P == P": + proc test(EC: typedesc, bits: static int, randZ: static bool) = + for _ in 0 ..< ItersMul: + when randZ: + let a = rng.random_unsafe_with_randZ(EC) + else: + let a = rng.random_unsafe(EC) + + 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) + + check: + bool(impl == a) + bool(reference == a) + + test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = false) + test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = true) + + test "EC " & G1_or_G2 & " mul [2]P == P.double()": + proc test(EC: typedesc, bits: static int, randZ: static bool) = + for _ in 0 ..< ItersMul: + when randZ: + let a = rng.random_unsafe_with_randZ(EC) + else: + let a = rng.random_unsafe(EC) + + var doubleA{.noInit.}: EC + 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) + + check: + bool(impl == doubleA) + bool(reference == doubleA) + + test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = false) + test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = true) + +proc run_EC_mul_distributive_tests*( + ec: typedesc, + ItersMul: static int, + moduleName: string + ) = + + # Random seed for reproducibility + var rng: RngState + let seed = uint32(getTime().toUnix() and (1'i64 shl 32 - 1)) # unixTime mod 2^32 + rng.seed(seed) + echo "\n------------------------------------------------------\n" + echo moduleName, " xoshiro512** seed: ", seed + + when ec.F is Fp: + const G1_or_G2 = "G1" + else: + const G1_or_G2 = "G2" + + const testSuiteDesc = "Elliptic curve in Short Weierstrass form with projective coordinates" + + suite testSuiteDesc & " - " & $ec & " - [" & $WordBitwidth & "-bit mode]": + + test "EC " & G1_or_G2 & " mul is distributive over EC add": + proc test(EC: typedesc, bits: static int, randZ: static bool) = + for _ in 0 ..< ItersMul: + when randZ: + let a = rng.random_unsafe_with_randZ(EC) + let b = rng.random_unsafe_with_randZ(EC) + else: + let a = rng.random_unsafe(EC) + let b = rng.random_unsafe_with_randZ(EC) + + 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) + + # [k]a + [k]b - Distributed + var kaImpl = a + var kaRef = a + + kaImpl.scalarMulGeneric(exponentCanonical, scratchSpace) + kaRef.unsafe_ECmul_double_add(exponentCanonical) + + var kbImpl = b + var kbRef = b + + kbImpl.scalarMulGeneric(exponentCanonical, scratchSpace) + kbRef.unsafe_ECmul_double_add(exponentCanonical) + + var kakbImpl{.noInit.}, kakbRef{.noInit.}: EC + kakbImpl.sum(kaImpl, kbImpl) + kakbRef.sum(kaRef, kbRef) + + check: + bool(fImpl == kakbImpl) + bool(fReference == kakbRef) + bool(fImpl == fReference) + + test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = false) + test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = true) + +proc run_EC_mul_vs_ref_impl*( + ec: typedesc, + ItersMul: static int, + moduleName: string + ) = + + # Random seed for reproducibility + var rng: RngState + let seed = uint32(getTime().toUnix() and (1'i64 shl 32 - 1)) # unixTime mod 2^32 + rng.seed(seed) + echo "\n------------------------------------------------------\n" + echo moduleName, " xoshiro512** seed: ", seed + + when ec.F is Fp: + const G1_or_G2 = "G1" + else: + const G1_or_G2 = "G2" + + const testSuiteDesc = "Elliptic curve in Short Weierstrass form with projective coordinates" + + suite testSuiteDesc & " - " & $ec & " - [" & $WordBitwidth & "-bit mode]": + test "EC " & G1_or_G2 & " mul constant-time is equivalent to a simple double-and-add algorithm": + proc test(EC: typedesc, bits: static int, randZ: static bool) = + for _ in 0 ..< ItersMul: + when randZ: + let a = rng.random_unsafe_with_randZ(EC) + else: + let a = rng.random_unsafe(EC) + + let exponent = 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) + + check: bool(impl == reference) + + test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = false) + test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = true) diff --git a/tests/test_ec_weierstrass_projective_g1.nim b/tests/test_ec_weierstrass_projective_g1.nim deleted file mode 100644 index d901aa8..0000000 --- a/tests/test_ec_weierstrass_projective_g1.nim +++ /dev/null @@ -1,381 +0,0 @@ -# Constantine -# Copyright (c) 2018-2019 Status Research & Development GmbH -# Copyright (c) 2020-Present Mamy André-Ratsimbazafy -# Licensed and distributed under either of -# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). -# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). -# at your option. This file may not be copied, modified, or distributed except according to those terms. - -import - # Standard library - std/[unittest, times], - # Internals - ../constantine/config/[common, curves], - ../constantine/arithmetic, - ../constantine/io/io_bigints, - ../constantine/elliptic/[ec_weierstrass_affine, ec_weierstrass_projective, ec_scalar_mul], - # Test utilities - ../helpers/prng_unsafe, - ./support/ec_reference_scalar_mult - -const - Iters = 128 - ItersMul = Iters div 4 - -var rng: RngState -let seed = uint32(getTime().toUnix() and (1'i64 shl 32 - 1)) # unixTime mod 2^32 -rng.seed(seed) -echo "test_ec_weierstrass_projective_g1 xoshiro512** seed: ", seed - -# Import: wrap in elliptic curve tests in small procedures -# otherwise they will become globals, -# and will create binary size issues. -# Also due to Nim stack scanning, -# having too many elements on the stack (a couple kB) -# will significantly slow down testing (100x is possible) - -suite "Elliptic curve in Short Weierstrass form y² = x³ + a x + b with projective coordinates (X, Y, Z): Y²Z = X³ + aXZ² + bZ³ i.e. X = xZ, Y = yZ": - test "The infinity point is the neutral element w.r.t. to EC addition": - proc test(F: typedesc, randZ: static bool) = - var inf {.noInit.}: ECP_SWei_Proj[F] - inf.setInf() - check: bool inf.isInf() - - for _ in 0 ..< Iters: - var r{.noInit.}: ECP_SWei_Proj[F] - when randZ: - let P = rng.random_unsafe_with_randZ(ECP_SWei_Proj[F]) - else: - let P = rng.random_unsafe(ECP_SWei_Proj[F]) - - r.sum(P, inf) - check: bool(r == P) - - r.sum(inf, P) - check: bool(r == P) - - - test(Fp[BN254_Snarks], randZ = false) - test(Fp[BN254_Snarks], randZ = true) - test(Fp[BLS12_381], randZ = false) - test(Fp[BLS12_381], randZ = true) - - test "Adding opposites gives an infinity point": - proc test(F: typedesc, randZ: static bool) = - for _ in 0 ..< Iters: - var r{.noInit.}: ECP_SWei_Proj[F] - when randZ: - let P = rng.random_unsafe_with_randZ(ECP_SWei_Proj[F]) - else: - let P = rng.random_unsafe(ECP_SWei_Proj[F]) - var Q = P - Q.neg() - - r.sum(P, Q) - check: bool r.isInf() - - r.sum(Q, P) - check: bool r.isInf() - - test(Fp[BN254_Snarks], randZ = false) - test(Fp[BN254_Snarks], randZ = true) - test(Fp[BLS12_381], randZ = false) - test(Fp[BLS12_381], randZ = true) - - test "EC add is commutative": - proc test(F: typedesc, randZ: static bool) = - for _ in 0 ..< Iters: - var r0{.noInit.}, r1{.noInit.}: ECP_SWei_Proj[F] - when randZ: - let P = rng.random_unsafe_with_randZ(ECP_SWei_Proj[F]) - let Q = rng.random_unsafe_with_randZ(ECP_SWei_Proj[F]) - else: - let P = rng.random_unsafe(ECP_SWei_Proj[F]) - let Q = rng.random_unsafe(ECP_SWei_Proj[F]) - - r0.sum(P, Q) - r1.sum(Q, P) - check: bool(r0 == r1) - - test(Fp[BN254_Snarks], randZ = false) - test(Fp[BN254_Snarks], randZ = true) - test(Fp[BLS12_381], randZ = false) - test(Fp[BLS12_381], randZ = true) - - test "EC add is associative": - proc test(F: typedesc, randZ: static bool) = - for _ in 0 ..< Iters: - when randZ: - let a = rng.random_unsafe_with_randZ(ECP_SWei_Proj[F]) - let b = rng.random_unsafe_with_randZ(ECP_SWei_Proj[F]) - let c = rng.random_unsafe_with_randZ(ECP_SWei_Proj[F]) - else: - let a = rng.random_unsafe(ECP_SWei_Proj[F]) - let b = rng.random_unsafe(ECP_SWei_Proj[F]) - let c = rng.random_unsafe(ECP_SWei_Proj[F]) - - var tmp1{.noInit.}, tmp2{.noInit.}: ECP_SWei_Proj[F] - - # r0 = (a + b) + c - tmp1.sum(a, b) - tmp2.sum(tmp1, c) - let r0 = tmp2 - - # r1 = a + (b + c) - tmp1.sum(b, c) - tmp2.sum(a, tmp1) - let r1 = tmp2 - - # r2 = (a + c) + b - tmp1.sum(a, c) - tmp2.sum(tmp1, b) - let r2 = tmp2 - - # r3 = a + (c + b) - tmp1.sum(c, b) - tmp2.sum(a, tmp1) - let r3 = tmp2 - - # r4 = (c + a) + b - tmp1.sum(c, a) - tmp2.sum(tmp1, b) - let r4 = tmp2 - - # ... - - check: - bool(r0 == r1) - bool(r0 == r2) - bool(r0 == r3) - bool(r0 == r4) - - test(Fp[BN254_Snarks], randZ = false) - test(Fp[BN254_Snarks], randZ = true) - test(Fp[BLS12_381], randZ = false) - test(Fp[BLS12_381], randZ = true) - - test "EC double and EC add are consistent": - proc test(F: typedesc, randZ: static bool) = - for _ in 0 ..< Iters: - when randZ: - let a = rng.random_unsafe_with_randZ(ECP_SWei_Proj[F]) - else: - let a = rng.random_unsafe(ECP_SWei_Proj[F]) - - var r0{.noInit.}, r1{.noInit.}: ECP_SWei_Proj[F] - - r0.double(a) - r1.sum(a, a) - - check: bool(r0 == r1) - - test(Fp[BN254_Snarks], randZ = false) - test(Fp[BN254_Snarks], randZ = true) - test(Fp[BLS12_381], randZ = false) - test(Fp[BLS12_381], randZ = true) - - - const BN254_Snarks_order_bits = BN254_Snarks.getCurveOrderBitwidth() - const BLS12_381_order_bits = BLS12_381.getCurveOrderBitwidth() - - test "EC mul [0]P == Inf": - proc test(F: typedesc, bits: static int, randZ: static bool) = - for _ in 0 ..< ItersMul: - when randZ: - let a = rng.random_unsafe_with_randZ(ECP_SWei_Proj[F]) - else: - let a = rng.random_unsafe(ECP_SWei_Proj[F]) - - # zeroInit - var exponentCanonical: array[(bits+7) div 8, byte] - - var - impl = a - reference = a - scratchSpace{.noInit.}: array[1 shl 4, ECP_SWei_Proj[F]] - - impl.scalarMulGeneric(exponentCanonical, scratchSpace) - reference.unsafe_ECmul_double_add(exponentCanonical) - - check: - bool(impl.isInf()) - bool(reference.isInf()) - - test(Fp[BN254_Snarks], bits = BN254_Snarks_order_bits, randZ = false) - test(Fp[BN254_Snarks], bits = BN254_Snarks_order_bits, randZ = true) - test(Fp[BLS12_381], bits = BLS12_381_order_bits, randZ = false) - test(Fp[BLS12_381], bits = BLS12_381_order_bits, randZ = true) - - test "EC mul [Order]P == Inf": - proc test(F: typedesc, bits: static int, randZ: static bool) = - for _ in 0 ..< ItersMul: - when randZ: - let a = rng.random_unsafe_with_randZ(ECP_SWei_Proj[F]) - else: - let a = rng.random_unsafe(ECP_SWei_Proj[F]) - - 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, ECP_SWei_Proj[F]] - - impl.scalarMulGeneric(exponentCanonical, scratchSpace) - reference.unsafe_ECmul_double_add(exponentCanonical) - - check: - bool(impl.isInf()) - bool(reference.isInf()) - - test(Fp[BN254_Snarks], bits = BN254_Snarks_order_bits, randZ = false) - test(Fp[BN254_Snarks], bits = BN254_Snarks_order_bits, randZ = true) - # TODO: BLS12 is using a subgroup of order "r" such as r*h = CurveOrder - # with h the curve cofactor - # instead of the full group - # test(Fp[BLS12_381], bits = BLS12_381_order_bits, randZ = false) - # test(Fp[BLS12_381], bits = BLS12_381_order_bits, randZ = true) - - test "EC mul [1]P == P": - proc test(F: typedesc, bits: static int, randZ: static bool) = - for _ in 0 ..< ItersMul: - when randZ: - let a = rng.random_unsafe_with_randZ(ECP_SWei_Proj[F]) - else: - let a = rng.random_unsafe(ECP_SWei_Proj[F]) - - 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, ECP_SWei_Proj[F]] - - impl.scalarMulGeneric(exponentCanonical, scratchSpace) - reference.unsafe_ECmul_double_add(exponentCanonical) - - check: - bool(impl == a) - bool(reference == a) - - test(Fp[BN254_Snarks], bits = BN254_Snarks_order_bits, randZ = false) - test(Fp[BN254_Snarks], bits = BN254_Snarks_order_bits, randZ = true) - test(Fp[BLS12_381], bits = BLS12_381_order_bits, randZ = false) - test(Fp[BLS12_381], bits = BLS12_381_order_bits, randZ = true) - - test "EC mul [2]P == P.double()": - proc test(F: typedesc, bits: static int, randZ: static bool) = - for _ in 0 ..< ItersMul: - when randZ: - let a = rng.random_unsafe_with_randZ(ECP_SWei_Proj[F]) - else: - let a = rng.random_unsafe(ECP_SWei_Proj[F]) - - var doubleA{.noInit.}: ECP_SWei_Proj[F] - 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, ECP_SWei_Proj[F]] - - impl.scalarMulGeneric(exponentCanonical, scratchSpace) - reference.unsafe_ECmul_double_add(exponentCanonical) - - check: - bool(impl == doubleA) - bool(reference == doubleA) - - test(Fp[BN254_Snarks], bits = BN254_Snarks_order_bits, randZ = false) - test(Fp[BN254_Snarks], bits = BN254_Snarks_order_bits, randZ = true) - test(Fp[BLS12_381], bits = BLS12_381_order_bits, randZ = false) - test(Fp[BLS12_381], bits = BLS12_381_order_bits, randZ = true) - - test "EC mul is distributive over EC add": - proc test(F: typedesc, bits: static int, randZ: static bool) = - for _ in 0 ..< ItersMul: - when randZ: - let a = rng.random_unsafe_with_randZ(ECP_SWei_Proj[F]) - let b = rng.random_unsafe_with_randZ(ECP_SWei_Proj[F]) - else: - let a = rng.random_unsafe(ECP_SWei_Proj[F]) - let b = rng.random_unsafe_with_randZ(ECP_SWei_Proj[F]) - - 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.}: ECP_SWei_Proj[F] - fReference{.noInit.}: ECP_SWei_Proj[F] - scratchSpace{.noInit.}: array[1 shl 4, ECP_SWei_Proj[F]] - - fImpl.sum(a, b) - fReference.sum(a, b) - - fImpl.scalarMulGeneric(exponentCanonical, scratchSpace) - fReference.unsafe_ECmul_double_add(exponentCanonical) - - # [k]a + [k]b - Distributed - var kaImpl = a - var kaRef = a - - kaImpl.scalarMulGeneric(exponentCanonical, scratchSpace) - kaRef.unsafe_ECmul_double_add(exponentCanonical) - - var kbImpl = b - var kbRef = b - - kbImpl.scalarMulGeneric(exponentCanonical, scratchSpace) - kbRef.unsafe_ECmul_double_add(exponentCanonical) - - var kakbImpl{.noInit.}, kakbRef{.noInit.}: ECP_SWei_Proj[F] - kakbImpl.sum(kaImpl, kbImpl) - kakbRef.sum(kaRef, kbRef) - - check: - bool(fImpl == kakbImpl) - bool(fReference == kakbRef) - bool(fImpl == fReference) - - test(Fp[BN254_Snarks], bits = BN254_Snarks_order_bits, randZ = false) - test(Fp[BN254_Snarks], bits = BN254_Snarks_order_bits, randZ = true) - test(Fp[BLS12_381], bits = BLS12_381_order_bits, randZ = false) - test(Fp[BLS12_381], bits = BLS12_381_order_bits, randZ = true) - - test "EC mul constant-time is equivalent to a simple double-and-add algorithm": - proc test(F: typedesc, bits: static int, randZ: static bool) = - for _ in 0 ..< ItersMul: - when randZ: - let a = rng.random_unsafe_with_randZ(ECP_SWei_Proj[F]) - else: - let a = rng.random_unsafe(ECP_SWei_Proj[F]) - - 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, ECP_SWei_Proj[F]] - - impl.scalarMulGeneric(exponentCanonical, scratchSpace) - reference.unsafe_ECmul_double_add(exponentCanonical) - - check: bool(impl == reference) - - test(Fp[BN254_Snarks], bits = BN254_Snarks_order_bits, randZ = false) - test(Fp[BN254_Snarks], bits = BN254_Snarks_order_bits, randZ = true) - test(Fp[BLS12_381], bits = BLS12_381_order_bits, randZ = false) - test(Fp[BLS12_381], bits = BLS12_381_order_bits, randZ = true) diff --git a/tests/test_ec_weierstrass_projective_g1_add_double.nim b/tests/test_ec_weierstrass_projective_g1_add_double.nim new file mode 100644 index 0000000..e6cb194 --- /dev/null +++ b/tests/test_ec_weierstrass_projective_g1_add_double.nim @@ -0,0 +1,34 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + # Standard library + std/[unittest, times], + # Internals + ../constantine/config/[common, curves], + ../constantine/arithmetic, + ../constantine/io/io_bigints, + ../constantine/elliptic/[ec_weierstrass_affine, ec_weierstrass_projective], + # Test utilities + ../helpers/prng_unsafe, + ./test_ec_template + +const + Iters = 128 + +run_EC_addition_tests( + ec = ECP_SWei_Proj[Fp[BN254_Snarks]], + Iters = Iters, + moduleName = "test_ec_weierstrass_projective_g1_add_double_" & $BN254_Snarks + ) + +run_EC_addition_tests( + ec = ECP_SWei_Proj[Fp[BLS12_381]], + Iters = Iters, + moduleName = "test_ec_weierstrass_projective_g1_add_double_" & $BLS12_381 + ) diff --git a/tests/test_ec_weierstrass_projective_g1_mul_distributive.nim b/tests/test_ec_weierstrass_projective_g1_mul_distributive.nim new file mode 100644 index 0000000..24385e6 --- /dev/null +++ b/tests/test_ec_weierstrass_projective_g1_mul_distributive.nim @@ -0,0 +1,36 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + # Standard library + std/[unittest, times], + # Internals + ../constantine/config/[common, curves], + ../constantine/arithmetic, + ../constantine/io/io_bigints, + ../constantine/elliptic/[ec_weierstrass_affine, ec_weierstrass_projective, ec_scalar_mul], + # Test utilities + ../helpers/prng_unsafe, + ./support/ec_reference_scalar_mult, + ./test_ec_template + +const + Iters = 128 + ItersMul = Iters div 4 + +run_EC_mul_distributive_tests( + ec = ECP_SWei_Proj[Fp[BN254_Snarks]], + ItersMul = ItersMul, + moduleName = "test_ec_weierstrass_projective_g1_mul_distributive_" & $BN254_Snarks + ) + +run_EC_mul_distributive_tests( + ec = ECP_SWei_Proj[Fp[BLS12_381]], + ItersMul = ItersMul, + moduleName = "test_ec_weierstrass_projective_g1_mul_distributive_" & $BLS12_381 + ) diff --git a/tests/test_ec_weierstrass_projective_g1_mul_sanity.nim b/tests/test_ec_weierstrass_projective_g1_mul_sanity.nim new file mode 100644 index 0000000..1450791 --- /dev/null +++ b/tests/test_ec_weierstrass_projective_g1_mul_sanity.nim @@ -0,0 +1,74 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + # Standard library + std/[unittest, times], + # Internals + ../constantine/config/[common, curves], + ../constantine/arithmetic, + ../constantine/io/io_bigints, + ../constantine/elliptic/[ec_weierstrass_affine, ec_weierstrass_projective, ec_scalar_mul], + # Test utilities + ../helpers/prng_unsafe, + ./support/ec_reference_scalar_mult, + ./test_ec_template + +const + Iters = 128 + ItersMul = Iters div 4 + +run_EC_mul_sanity_tests( + ec = ECP_SWei_Proj[Fp[BN254_Snarks]], + ItersMul = ItersMul, + moduleName = "test_ec_weierstrass_projective_g1_mul_sanity_" & $BN254_Snarks + ) + +run_EC_mul_sanity_tests( + ec = ECP_SWei_Proj[Fp[BLS12_381]], + ItersMul = ItersMul, + moduleName = "test_ec_weierstrass_projective_g1_mul_sanity_" & $BLS12_381 + ) + + +test "EC mul [Order]P == Inf": + var rng: RngState + let seed = uint32(getTime().toUnix() and (1'i64 shl 32 - 1)) # unixTime mod 2^32 + rng.seed(seed) + echo "test_ec_weierstrass_projective_g1_mul_sanity_extra_curve_order_mul_sanity xoshiro512** seed: ", seed + + proc test(EC: typedesc, bits: static int, randZ: static bool) = + for _ in 0 ..< ItersMul: + when randZ: + let a = rng.random_unsafe_with_randZ(EC) + else: + let a = rng.random_unsafe(EC) + + let exponent = 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) + + check: + bool(impl.isInf()) + bool(reference.isInf()) + + test(ECP_SWei_Proj[Fp[BN254_Snarks]], bits = BN254_Snarks.getCurveOrderBitwidth(), randZ = false) + test(ECP_SWei_Proj[Fp[BN254_Snarks]], bits = BN254_Snarks.getCurveOrderBitwidth(), randZ = true) + # TODO: BLS12 is using a subgroup of order "r" such as r*h = CurveOrder + # with h the curve cofactor + # instead of the full group + # test(Fp[BLS12_381], bits = BLS12_381.getCurveOrderBitwidth(), randZ = false) + # test(Fp[BLS12_381], bits = BLS12_381.getCurveOrderBitwidth(), randZ = true) diff --git a/tests/test_ec_weierstrass_projective_g1_mul_vs_ref.nim b/tests/test_ec_weierstrass_projective_g1_mul_vs_ref.nim new file mode 100644 index 0000000..515accc --- /dev/null +++ b/tests/test_ec_weierstrass_projective_g1_mul_vs_ref.nim @@ -0,0 +1,36 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + # Standard library + std/[unittest, times], + # Internals + ../constantine/config/[common, curves], + ../constantine/arithmetic, + ../constantine/io/io_bigints, + ../constantine/elliptic/[ec_weierstrass_affine, ec_weierstrass_projective, ec_scalar_mul], + # Test utilities + ../helpers/prng_unsafe, + ./support/ec_reference_scalar_mult, + ./test_ec_template + +const + Iters = 128 + ItersMul = Iters div 4 + +run_EC_mul_vs_ref_impl( + ec = ECP_SWei_Proj[Fp[BN254_Snarks]], + ItersMul = ItersMul, + moduleName = "test_ec_weierstrass_projective_g1_mul_vs_ref_" & $BN254_Snarks + ) + +run_EC_mul_vs_ref_impl( + ec = ECP_SWei_Proj[Fp[BLS12_381]], + ItersMul = ItersMul, + moduleName = "test_ec_weierstrass_projective_g1_mul_vs_ref_" & $BLS12_381 + ) diff --git a/tests/test_ec_weierstrass_projective_g2_add_double_bls12_381.nim b/tests/test_ec_weierstrass_projective_g2_add_double_bls12_381.nim new file mode 100644 index 0000000..efda932 --- /dev/null +++ b/tests/test_ec_weierstrass_projective_g2_add_double_bls12_381.nim @@ -0,0 +1,29 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + # Standard library + std/[unittest, times], + # Internals + ../constantine/config/[common, curves], + ../constantine/arithmetic, + ../constantine/towers, + ../constantine/io/io_bigints, + ../constantine/elliptic/[ec_weierstrass_affine, ec_weierstrass_projective], + # Test utilities + ../helpers/prng_unsafe, + ./test_ec_template + +const + Iters = 128 + +run_EC_addition_tests( + ec = ECP_SWei_Proj[Fp2[BLS12_381]], + Iters = Iters, + moduleName = "test_ec_weierstrass_projective_g2_add_double_" & $BLS12_381 + ) diff --git a/tests/test_ec_weierstrass_projective_g2_add_double_bn254_snarks.nim b/tests/test_ec_weierstrass_projective_g2_add_double_bn254_snarks.nim new file mode 100644 index 0000000..44807f8 --- /dev/null +++ b/tests/test_ec_weierstrass_projective_g2_add_double_bn254_snarks.nim @@ -0,0 +1,29 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + # Standard library + std/[unittest, times], + # Internals + ../constantine/config/[common, curves], + ../constantine/arithmetic, + ../constantine/towers, + ../constantine/io/io_bigints, + ../constantine/elliptic/[ec_weierstrass_affine, ec_weierstrass_projective], + # Test utilities + ../helpers/prng_unsafe, + ./test_ec_template + +const + Iters = 128 + +run_EC_addition_tests( + ec = ECP_SWei_Proj[Fp2[BN254_Snarks]], + Iters = Iters, + moduleName = "test_ec_weierstrass_projective_g2_add_double_" & $BN254_Snarks + ) diff --git a/tests/test_ec_weierstrass_projective_g2_mul_distributive_bls12_381.nim b/tests/test_ec_weierstrass_projective_g2_mul_distributive_bls12_381.nim new file mode 100644 index 0000000..7ebb5d2 --- /dev/null +++ b/tests/test_ec_weierstrass_projective_g2_mul_distributive_bls12_381.nim @@ -0,0 +1,31 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + # Standard library + std/[unittest, times], + # Internals + ../constantine/config/[common, curves], + ../constantine/arithmetic, + ../constantine/towers, + ../constantine/io/io_bigints, + ../constantine/elliptic/[ec_weierstrass_affine, ec_weierstrass_projective, ec_scalar_mul], + # Test utilities + ../helpers/prng_unsafe, + ./support/ec_reference_scalar_mult, + ./test_ec_template + +const + Iters = 128 + ItersMul = Iters div 4 + +run_EC_mul_distributive_tests( + ec = ECP_SWei_Proj[Fp2[BLS12_381]], + ItersMul = ItersMul, + moduleName = "test_ec_weierstrass_projective_g2_mul_distributive_" & $BLS12_381 + ) diff --git a/tests/test_ec_weierstrass_projective_g2_mul_distributive_bn254_snarks.nim b/tests/test_ec_weierstrass_projective_g2_mul_distributive_bn254_snarks.nim new file mode 100644 index 0000000..fcba086 --- /dev/null +++ b/tests/test_ec_weierstrass_projective_g2_mul_distributive_bn254_snarks.nim @@ -0,0 +1,31 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + # Standard library + std/[unittest, times], + # Internals + ../constantine/config/[common, curves], + ../constantine/arithmetic, + ../constantine/towers, + ../constantine/io/io_bigints, + ../constantine/elliptic/[ec_weierstrass_affine, ec_weierstrass_projective, ec_scalar_mul], + # Test utilities + ../helpers/prng_unsafe, + ./support/ec_reference_scalar_mult, + ./test_ec_template + +const + Iters = 128 + ItersMul = Iters div 4 + +run_EC_mul_distributive_tests( + ec = ECP_SWei_Proj[Fp2[BN254_Snarks]], + ItersMul = ItersMul, + moduleName = "test_ec_weierstrass_projective_g2_mul_distributive_" & $BN254_Snarks + ) diff --git a/tests/test_ec_weierstrass_projective_g2_mul_sanity_bls12_381.nim b/tests/test_ec_weierstrass_projective_g2_mul_sanity_bls12_381.nim new file mode 100644 index 0000000..d3d847c --- /dev/null +++ b/tests/test_ec_weierstrass_projective_g2_mul_sanity_bls12_381.nim @@ -0,0 +1,65 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + # Standard library + std/[unittest, times], + # Internals + ../constantine/config/[common, curves], + ../constantine/arithmetic, + ../constantine/towers, + ../constantine/io/io_bigints, + ../constantine/elliptic/[ec_weierstrass_affine, ec_weierstrass_projective, ec_scalar_mul], + # Test utilities + ../helpers/prng_unsafe, + ./support/ec_reference_scalar_mult, + ./test_ec_template + +const + Iters = 128 + ItersMul = Iters div 4 + +run_EC_mul_sanity_tests( + ec = ECP_SWei_Proj[Fp2[BLS12_381]], + ItersMul = ItersMul, + moduleName = "test_ec_weierstrass_projective_g2_mul_sanity_" & $BLS12_381 + ) + +# TODO: the order on E'(Fp2) for BLS curves is ??? with r the order on E(Fp) +# +# test "EC mul [Order]P == Inf": +# var rng: RngState +# let seed = uint32(getTime().toUnix() and (1'i64 shl 32 - 1)) # unixTime mod 2^32 +# rng.seed(seed) +# echo "test_ec_weierstrass_projective_g1_mul_sanity_extra_curve_order_mul_sanity xoshiro512** seed: ", seed +# +# proc test(EC: typedesc, bits: static int, randZ: static bool) = +# for _ in 0 ..< ItersMul: +# when randZ: +# let a = rng.random_unsafe_with_randZ(EC) +# else: +# let a = rng.random_unsafe(EC) +# +# let exponent = F.C.getCurveOrder() +# var 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) +# +# check: +# bool(impl.isInf()) +# bool(reference.isInf()) +# +# test(ECP_SWei_Proj[Fp2[BLS12_381]], bits = BLS12_381.getCurveOrderBitwidth(), randZ = false) +# test(ECP_SWei_Proj[Fp2[BLS12_381]], bits = BLS12_381.getCurveOrderBitwidth(), randZ = true) diff --git a/tests/test_ec_weierstrass_projective_g2_mul_sanity_bn254_snarks.nim b/tests/test_ec_weierstrass_projective_g2_mul_sanity_bn254_snarks.nim new file mode 100644 index 0000000..7825bcc --- /dev/null +++ b/tests/test_ec_weierstrass_projective_g2_mul_sanity_bn254_snarks.nim @@ -0,0 +1,65 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + # Standard library + std/[unittest, times], + # Internals + ../constantine/config/[common, curves], + ../constantine/arithmetic, + ../constantine/towers, + ../constantine/io/io_bigints, + ../constantine/elliptic/[ec_weierstrass_affine, ec_weierstrass_projective, ec_scalar_mul], + # Test utilities + ../helpers/prng_unsafe, + ./support/ec_reference_scalar_mult, + ./test_ec_template + +const + Iters = 128 + ItersMul = Iters div 4 + +run_EC_mul_sanity_tests( + ec = ECP_SWei_Proj[Fp2[BN254_Snarks]], + ItersMul = ItersMul, + moduleName = "test_ec_weierstrass_projective_g2_mul_sanity_" & $BN254_Snarks + ) + +# TODO: the order on E'(Fp2) for BN curve is r∗(2p−r) with r the order on E(Fp) +# +# test "EC mul [Order]P == Inf": +# var rng: RngState +# let seed = uint32(getTime().toUnix() and (1'i64 shl 32 - 1)) # unixTime mod 2^32 +# rng.seed(seed) +# echo "test_ec_weierstrass_projective_g1_mul_sanity_extra_curve_order_mul_sanity xoshiro512** seed: ", seed +# +# proc test(EC: typedesc, bits: static int, randZ: static bool) = +# for _ in 0 ..< ItersMul: +# when randZ: +# let a = rng.random_unsafe_with_randZ(EC) +# else: +# let a = rng.random_unsafe(EC) +# +# let exponent = F.C.getCurveOrder() +# var 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) +# +# check: +# bool(impl.isInf()) +# bool(reference.isInf()) +# +# test(ECP_SWei_Proj[Fp2[BN254_Snarks]], bits = BN254_Snarks.getCurveOrderBitwidth(), randZ = false) +# test(ECP_SWei_Proj[Fp2[BN254_Snarks]], bits = BN254_Snarks.getCurveOrderBitwidth(), randZ = true) diff --git a/tests/test_ec_weierstrass_projective_g2_mul_vs_ref_bls12_381.nim b/tests/test_ec_weierstrass_projective_g2_mul_vs_ref_bls12_381.nim new file mode 100644 index 0000000..b5e2516 --- /dev/null +++ b/tests/test_ec_weierstrass_projective_g2_mul_vs_ref_bls12_381.nim @@ -0,0 +1,31 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + # Standard library + std/[unittest, times], + # Internals + ../constantine/config/[common, curves], + ../constantine/arithmetic, + ../constantine/towers, + ../constantine/io/io_bigints, + ../constantine/elliptic/[ec_weierstrass_affine, ec_weierstrass_projective, ec_scalar_mul], + # Test utilities + ../helpers/prng_unsafe, + ./support/ec_reference_scalar_mult, + ./test_ec_template + +const + Iters = 128 + ItersMul = Iters div 4 + +run_EC_mul_vs_ref_impl( + ec = ECP_SWei_Proj[Fp2[BLS12_381]], + ItersMul = ItersMul, + moduleName = "test_ec_weierstrass_projective_g2_mul_vs_ref_" & $BLS12_381 + ) diff --git a/tests/test_ec_weierstrass_projective_g2_mul_vs_ref_bn254_snarks.nim b/tests/test_ec_weierstrass_projective_g2_mul_vs_ref_bn254_snarks.nim new file mode 100644 index 0000000..9a4a9f6 --- /dev/null +++ b/tests/test_ec_weierstrass_projective_g2_mul_vs_ref_bn254_snarks.nim @@ -0,0 +1,31 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + # Standard library + std/[unittest, times], + # Internals + ../constantine/config/[common, curves], + ../constantine/arithmetic, + ../constantine/towers, + ../constantine/io/io_bigints, + ../constantine/elliptic/[ec_weierstrass_affine, ec_weierstrass_projective, ec_scalar_mul], + # Test utilities + ../helpers/prng_unsafe, + ./support/ec_reference_scalar_mult, + ./test_ec_template + +const + Iters = 128 + ItersMul = Iters div 4 + +run_EC_mul_vs_ref_impl( + ec = ECP_SWei_Proj[Fp2[BN254_Snarks]], + ItersMul = ItersMul, + moduleName = "test_ec_weierstrass_projective_g2_mul_vs_ref_" & $BN254_Snarks + ) diff --git a/tests/test_finite_fields.nim b/tests/test_finite_fields.nim index fd169a4..a06d247 100644 --- a/tests/test_finite_fields.nim +++ b/tests/test_finite_fields.nim @@ -13,6 +13,8 @@ import std/unittest, static: doAssert defined(testingCurves), "This modules requires the -d:testingCurves compile option" +echo "\n------------------------------------------------------\n" + proc main() = suite "Basic arithmetic over finite fields": test "Addition mod 101": diff --git a/tests/test_finite_fields_mulsquare.nim b/tests/test_finite_fields_mulsquare.nim index d04923c..f2c2800 100644 --- a/tests/test_finite_fields_mulsquare.nim +++ b/tests/test_finite_fields_mulsquare.nim @@ -21,6 +21,7 @@ const Iters = 128 var rng: RngState let seed = uint32(getTime().toUnix() and (1'i64 shl 32 - 1)) # unixTime mod 2^32 rng.seed(seed) +echo "\n------------------------------------------------------\n" echo "test_finite_fields_mulsquare xoshiro512** seed: ", seed static: doAssert defined(testingCurves), "This modules requires the -d:testingCurves compile option" @@ -76,7 +77,7 @@ proc sanity(C: static Curve) = bool(n == expected) proc mainSanity() = - suite "Modular squaring is consistent with multiplication on special elements": + suite "Modular squaring is consistent with multiplication on special elements" & " [" & $WordBitwidth & "-bit mode]": sanity Fake101 sanity Mersenne61 sanity Mersenne127 @@ -87,7 +88,7 @@ proc mainSanity() = mainSanity() proc mainSelectCases() = - suite "Modular Squaring: selected tricky cases": + suite "Modular Squaring: selected tricky cases" & " [" & $WordBitwidth & "-bit mode]": test "P-256 [FastSquaring = " & $P256.canUseNoCarryMontySquare & "]": block: # Triggered an issue in the (t[N+1], t[N]) = t[N] + (A1, A0) @@ -114,7 +115,7 @@ proc randomCurve(C: static Curve) = doAssert bool(r_mul == r_sqr) -suite "Random Modular Squaring is consistent with Modular Multiplication": +suite "Random Modular Squaring is consistent with Modular Multiplication" & " [" & $WordBitwidth & "-bit mode]": test "Random squaring mod P-224 [FastSquaring = " & $P224.canUseNoCarryMontySquare & "]": for _ in 0 ..< Iters: randomCurve(P224) diff --git a/tests/test_finite_fields_powinv.nim b/tests/test_finite_fields_powinv.nim index 9d29980..a0f7133 100644 --- a/tests/test_finite_fields_powinv.nim +++ b/tests/test_finite_fields_powinv.nim @@ -10,6 +10,7 @@ import # Standard library std/[unittest, times], # Internal + ../constantine/config/common, ../constantine/arithmetic, ../constantine/io/[io_bigints, io_fields], ../constantine/config/curves, @@ -24,10 +25,11 @@ const Iters = 512 var rng: RngState let seed = uint32(getTime().toUnix() and (1'i64 shl 32 - 1)) # unixTime mod 2^32 rng.seed(seed) +echo "\n------------------------------------------------------\n" echo "test_finite_fields_powinv xoshiro512** seed: ", seed proc main() = - suite "Modular exponentiation over finite fields": + suite "Modular exponentiation over finite fields" & " [" & $WordBitwidth & "-bit mode]": test "n² mod 101": let exponent = BigInt[64].fromUint(2'u64) @@ -181,7 +183,7 @@ proc main() = testRandomDiv2 BLS12_461 testRandomDiv2 BN462 - suite "Modular inversion over prime fields": + suite "Modular inversion over prime fields" & " [" & $WordBitwidth & "-bit mode]": test "Specific tests on Fp[BLS12_381]": block: # No inverse exist for 0 --> should return 0 for projective/jacobian to affine coordinate conversion var r, x: Fp[BLS12_381] diff --git a/tests/test_finite_fields_sqrt.nim b/tests/test_finite_fields_sqrt.nim index 4bffb71..498721d 100644 --- a/tests/test_finite_fields_sqrt.nim +++ b/tests/test_finite_fields_sqrt.nim @@ -22,6 +22,7 @@ const Iters = 128 var rng: RngState let seed = uint32(getTime().toUnix() and (1'i64 shl 32 - 1)) # unixTime mod 2^32 rng.seed(seed) +echo "\n------------------------------------------------------\n" echo "test_finite_fields_sqrt xoshiro512** seed: ", seed static: doAssert defined(testingCurves), "This modules requires the -d:testingCurves compile option" @@ -59,11 +60,11 @@ proc exhaustiveCheck_p3mod4(C: static Curve, modulus: static int) = var a2 = a check: bool a.isSquare() - bool a.sqrt_if_square_p3mod4() + bool a.sqrt_if_square() # 2 different code paths have the same result # (despite 2 square roots existing per square) - a2.sqrt_p3mod4() + a2.sqrt() check: bool(a == a2) var r_bytes: array[8, byte] @@ -78,7 +79,7 @@ proc exhaustiveCheck_p3mod4(C: static Curve, modulus: static int) = check: bool not a.isSquare() - bool not a.sqrt_if_square_p3mod4() + bool not a.sqrt_if_square() bool (a == a2) # a shouldn't be modified proc randomSqrtCheck_p3mod4(C: static Curve) = @@ -97,15 +98,15 @@ proc randomSqrtCheck_p3mod4(C: static Curve) = bool a2.isSquare() var r, s = a2 - r.sqrt_p3mod4() - let ok = s.sqrt_if_square_p3mod4() + r.sqrt() + let ok = s.sqrt_if_square() check: bool ok bool(r == s) bool(r == a or r == na) proc main() = - suite "Modular square root": + suite "Modular square root" & " [" & $WordBitwidth & "-bit mode]": exhaustiveCheck_p3mod4 Fake103, 103 exhaustiveCheck_p3mod4 Fake10007, 10007 exhaustiveCheck_p3mod4 Fake65519, 65519 diff --git a/tests/test_finite_fields_vs_gmp.nim b/tests/test_finite_fields_vs_gmp.nim index 63fa2ff..1880336 100644 --- a/tests/test_finite_fields_vs_gmp.nim +++ b/tests/test_finite_fields_vs_gmp.nim @@ -17,6 +17,8 @@ import ../constantine/primitives, ../constantine/config/curves +echo "\n------------------------------------------------------\n" + var RNG {.compileTime.} = initRand(1234) const CurveParams = [ P224, diff --git a/tests/test_fp12_bls12_377.nim b/tests/test_fp12_bls12_377.nim new file mode 100644 index 0000000..a17d7f3 --- /dev/null +++ b/tests/test_fp12_bls12_377.nim @@ -0,0 +1,26 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + # Internals + ../constantine/towers, + ../constantine/config/curves, + # Test utilities + ./test_fp_tower_template + +const TestCurves = [ + BLS12_377, + ] + +runTowerTests( + ExtDegree = 12, + Iters = 128, + TestCurves = TestCurves, + moduleName = "test_fp12_" & $BLS12_377, + testSuiteDesc = "𝔽p12 = 𝔽p6[w] " & $BLS12_377 +) diff --git a/tests/test_fp12_bls12_381.nim b/tests/test_fp12_bls12_381.nim new file mode 100644 index 0000000..3c294a3 --- /dev/null +++ b/tests/test_fp12_bls12_381.nim @@ -0,0 +1,26 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + # Internals + ../constantine/towers, + ../constantine/config/curves, + # Test utilities + ./test_fp_tower_template + +const TestCurves = [ + BLS12_381 + ] + +runTowerTests( + ExtDegree = 12, + Iters = 128, + TestCurves = TestCurves, + moduleName = "test_fp12_" & $BLS12_381, + testSuiteDesc = "𝔽p12 = 𝔽p6[w] " & $BLS12_381 +) diff --git a/tests/test_fp12.nim b/tests/test_fp12_bn254_snarks.nim similarity index 71% rename from tests/test_fp12.nim rename to tests/test_fp12_bn254_snarks.nim index 1561eb4..1dee22b 100644 --- a/tests/test_fp12.nim +++ b/tests/test_fp12_bn254_snarks.nim @@ -14,20 +14,13 @@ import ./test_fp_tower_template const TestCurves = [ - # BN254_Nogami BN254_Snarks, - BLS12_377, - BLS12_381, - # BN446 - # FKM12_447 - # BLS12_461 - # BN462 ] runTowerTests( ExtDegree = 12, Iters = 128, TestCurves = TestCurves, - moduleName = "test_fp12", - testSuiteDesc = "𝔽p12 = 𝔽p6[w] (irreducible polynomial w²-γ = 0) -> 𝔽p12 point (a, b) with coordinate a + bw and γ quadratic non-residue in 𝔽p6" + moduleName = "test_fp12_" & $BN254_Snarks, + testSuiteDesc = "𝔽p12 = 𝔽p6[w] " & $BN254_Snarks ) diff --git a/tests/test_fp2_sqrt.nim b/tests/test_fp2_sqrt.nim new file mode 100644 index 0000000..12b7865 --- /dev/null +++ b/tests/test_fp2_sqrt.nim @@ -0,0 +1,56 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + # Standard library + std/[tables, unittest, times], + # Internals + ../constantine/config/common, + ../constantine/[arithmetic, primitives], + ../constantine/towers, + ../constantine/config/curves, + # Test utilities + ../helpers/prng_unsafe + +const Iters = 128 + +var rng: RngState +let seed = uint32(getTime().toUnix() and (1'i64 shl 32 - 1)) # unixTime mod 2^32 +rng.seed(seed) +echo "\n------------------------------------------------------\n" +echo "test_fp2_sqrt xoshiro512** seed: ", seed + +proc randomSqrtCheck_p3mod4(C: static Curve) = + test "[𝔽p2] Random square root check for p ≡ 3 (mod 4) on " & $Curve(C): + for _ in 0 ..< Iters: + let a = rng.random_unsafe(Fp2[C]) + var na{.noInit.}: Fp2[C] + na.neg(a) + + var a2 = a + var na2 = na + a2.square() + na2.square() + check: + bool a2 == na2 + bool a2.isSquare() + + var r, s = a2 + # r.sqrt() + let ok = s.sqrt_if_square() + check: + bool ok + # bool(r == s) + bool(s == a or s == na) + +proc main() = + suite "Modular square root" & " [" & $WordBitwidth & "-bit mode]": + randomSqrtCheck_p3mod4 BN254_Snarks + randomSqrtCheck_p3mod4 BLS12_381 + +main() diff --git a/tests/test_fp6_bls12_377.nim b/tests/test_fp6_bls12_377.nim new file mode 100644 index 0000000..9c37563 --- /dev/null +++ b/tests/test_fp6_bls12_377.nim @@ -0,0 +1,26 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + # Internals + ../constantine/towers, + ../constantine/config/curves, + # Test utilities + ./test_fp_tower_template + +const TestCurves = [ + BLS12_377, + ] + +runTowerTests( + ExtDegree = 6, + Iters = 128, + TestCurves = TestCurves, + moduleName = "test_fp6_" & $BLS12_377, + testSuiteDesc = "𝔽p6 = 𝔽p2[v] " & $BLS12_377 +) diff --git a/tests/test_fp6_bls12_381.nim b/tests/test_fp6_bls12_381.nim new file mode 100644 index 0000000..26feae0 --- /dev/null +++ b/tests/test_fp6_bls12_381.nim @@ -0,0 +1,26 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + # Internals + ../constantine/towers, + ../constantine/config/curves, + # Test utilities + ./test_fp_tower_template + +const TestCurves = [ + BLS12_381 + ] + +runTowerTests( + ExtDegree = 6, + Iters = 128, + TestCurves = TestCurves, + moduleName = "test_fp6_" & $BLS12_381, + testSuiteDesc = "𝔽p6 = 𝔽p2[v] " & $BLS12_381 +) diff --git a/tests/test_fp6.nim b/tests/test_fp6_bn254_snarks.nim similarity index 71% rename from tests/test_fp6.nim rename to tests/test_fp6_bn254_snarks.nim index bc535e0..863f685 100644 --- a/tests/test_fp6.nim +++ b/tests/test_fp6_bn254_snarks.nim @@ -14,20 +14,13 @@ import ./test_fp_tower_template const TestCurves = [ - # BN254_Nogami BN254_Snarks, - BLS12_377, - BLS12_381, - # BN446 - # FKM12_447 - # BLS12_461 - # BN462 ] runTowerTests( ExtDegree = 6, Iters = 128, TestCurves = TestCurves, - moduleName = "test_fp6", - testSuiteDesc = "𝔽p6 = 𝔽p2[v] (irreducible polynomial v³-ξ = 0) -> 𝔽p6 point (a, b, c) with coordinate a + bv + cv² and ξ cubic non-residue in 𝔽p2" + moduleName = "test_fp6_" & $BN254_Snarks, + testSuiteDesc = "𝔽p6 = 𝔽p2[w] " & $BN254_Snarks ) diff --git a/tests/test_fp_tower_template.nim b/tests/test_fp_tower_template.nim index e21172b..d41af7f 100644 --- a/tests/test_fp_tower_template.nim +++ b/tests/test_fp_tower_template.nim @@ -23,6 +23,8 @@ import # Test utilities ../helpers/[prng_unsafe, static_for] +echo "\n------------------------------------------------------\n" + template ExtField(degree: static int, curve: static Curve): untyped = when degree == 2: Fp2[curve] @@ -47,7 +49,7 @@ proc runTowerTests*[N]( rng.seed(seed) echo moduleName, " xoshiro512** seed: ", seed - suite testSuiteDesc: + suite testSuiteDesc & " [" & $WordBitwidth & "-bit mode]": test "Comparison sanity checks": proc test(Field: typedesc) = var z, o {.noInit.}: Field diff --git a/tests/test_io_bigints.nim b/tests/test_io_bigints.nim index dd4c44c..3f66984 100644 --- a/tests/test_io_bigints.nim +++ b/tests/test_io_bigints.nim @@ -16,12 +16,13 @@ import std/[unittest,times], var rng: RngState let seed = uint32(getTime().toUnix() and (1'i64 shl 32 - 1)) # unixTime mod 2^32 rng.seed(seed) +echo "\n------------------------------------------------------\n" echo "test_io_bigints xoshiro512** seed: ", seed type T = BaseType proc main() = - suite "IO": + suite "IO - BigInt" & " [" & $WordBitwidth & "-bit mode]": test "Parsing raw integers": block: # Sanity check let x = 0'u64 diff --git a/tests/test_io_fields.nim b/tests/test_io_fields.nim index 405c294..bfdaa68 100644 --- a/tests/test_io_fields.nim +++ b/tests/test_io_fields.nim @@ -17,10 +17,11 @@ import std/[unittest, times], var rng: RngState let seed = uint32(getTime().toUnix() and (1'i64 shl 32 - 1)) # unixTime mod 2^32 rng.seed(seed) +echo "\n------------------------------------------------------\n" echo "test_io_fields xoshiro512** seed: ", seed proc main() = - suite "IO - Finite fields": + suite "IO - Finite fields" & " [" & $WordBitwidth & "-bit mode]": test "Parsing and serializing round-trip on uint64": # 101 --------------------------------- block: diff --git a/tests/test_precomputed.nim b/tests/test_precomputed.nim index b787af7..01c4591 100644 --- a/tests/test_precomputed.nim +++ b/tests/test_precomputed.nim @@ -7,10 +7,13 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import std/unittest, + ../constantine/config/common, ../constantine/arithmetic, ../constantine/config/curves, ../constantine/io/[io_bigints, io_fields] +echo "\n------------------------------------------------------\n" + proc checkCubeRootOfUnity(curve: static Curve) = test $curve & " cube root of unity (mod p)": var cru = curve.getCubicRootOfUnity_mod_p() @@ -30,7 +33,7 @@ proc checkCubeRootOfUnity(curve: static Curve) = check: bool r.isOne() proc main() = - suite "Sanity checks on precomputed values": + suite "Sanity checks on precomputed values" & " [" & $WordBitwidth & "-bit mode]": checkCubeRootOfUnity(BN254_Snarks) # checkCubeRootOfUnity(BLS12_381) diff --git a/tests/test_primitives.nim b/tests/test_primitives.nim index 68cfdfd..9631510 100644 --- a/tests/test_primitives.nim +++ b/tests/test_primitives.nim @@ -7,6 +7,7 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import std/[unittest, times, math], + ../constantine/config/common, ../constantine/primitives, ../helpers/prng_unsafe @@ -14,13 +15,14 @@ import std/[unittest, times, math], var rng: RngState let seed = uint32(getTime().toUnix() and (1'i64 shl 32 - 1)) # unixTime mod 2^32 rng.seed(seed) +echo "\n------------------------------------------------------\n" echo "test_primitives xoshiro512** seed: ", seed template undistinct[T](x: Ct[T]): T = T(x) proc main() = - suite "Constant-time unsigned integers": + suite "Constant-time unsigned integers" & " [" & $WordBitwidth & "-bit mode]": test "High - getting the biggest representable number": check: high(Ct[byte]).undistinct == 0xFF.byte