From 638cb71e167780a1a09f81a5c9d87168dbe38ae7 Mon Sep 17 00:00:00 2001 From: Mamy Ratsimbazafy Date: Fri, 22 Jan 2021 00:09:52 +0100 Subject: [PATCH] Fr: Finite Field parametrized by the curve order (#115) * Introduce Fr type: finite field over curve order. Need workaround for https://github.com/nim-lang/Nim/issues/16774 * Split curve properties into core and derived * Attach field properties to an instantiated field instead of the curve enum * Workaround https://github.com/nim-lang/Nim/issues/14021, yet another "working with types in macros" is difficult https://github.com/nim-lang/RFCs/issues/44 * Implement finite field over prime order of a curve subgroup * skip OpenSSL tests on windows --- benchmarks/bench_fields_template.nim | 2 +- constantine.nimble | 9 +- constantine/arithmetic/finite_fields.nim | 198 +++++++++--------- .../arithmetic/finite_fields_double_width.nim | 6 +- .../arithmetic/finite_fields_inversion.nim | 4 +- .../arithmetic/finite_fields_square_root.nim | 56 ++--- constantine/config/curves.nim | 190 +---------------- constantine/config/curves_derived.nim | 56 +++-- constantine/config/curves_parser.nim | 24 +++ constantine/config/curves_prop_core.nim | 116 ++++++++++ constantine/config/curves_prop_derived.nim | 139 ++++++++++++ .../config/{type_fp.nim => type_ff.nim} | 17 +- constantine/curves/bls12_377_glv.nim | 2 +- constantine/curves/bls12_377_sqrt.nim | 2 +- constantine/curves/bls12_381_glv.nim | 2 +- constantine/curves/bn254_nogami_glv.nim | 2 +- constantine/curves/bn254_snarks_glv.nim | 2 +- constantine/curves/bw6_761_frobenius.nim | 2 +- constantine/curves/bw6_761_glv.nim | 2 +- constantine/curves/zoo_glv.nim | 2 +- constantine/io/io_fields.nim | 24 +-- constantine/pairing/pairing_bls12.nim | 2 +- constantine/pairing/pairing_bn.nim | 2 +- .../quadratic_extensions.nim | 2 +- helpers/prng_unsafe.nim | 94 +++++---- sage/derive_endomorphisms.sage | 2 +- sage/derive_frobenius.sage | 2 +- tests/t_ec_sage_bls12_377.nim | 2 +- tests/t_ec_sage_bls12_381.nim | 2 +- tests/t_ec_sage_bn254_nogami.nim | 2 +- tests/t_ec_sage_bn254_snarks.nim | 2 +- tests/t_ec_sage_bw6_761_g1.nim | 2 +- tests/t_ec_sage_bw6_761_g2.nim | 2 +- tests/t_ec_sage_template.nim | 2 +- tests/t_ec_shortw_jac_g1_add_double.nim | 2 +- tests/t_ec_shortw_jac_g1_mul_distri.nim | 2 +- tests/t_ec_shortw_jac_g1_mul_vs_ref.nim | 2 +- .../t_ec_shortw_jac_g2_add_double_bw6_761.nim | 2 +- .../t_ec_shortw_jac_g2_mixed_add_bw6_761.nim | 2 +- .../t_ec_shortw_jac_g2_mul_distri_bw6_761.nim | 2 +- .../t_ec_shortw_jac_g2_mul_sanity_bw6_761.nim | 2 +- .../t_ec_shortw_jac_g2_mul_vs_ref_bw6_761.nim | 2 +- tests/t_ec_shortw_prj_g1_add_double.nim | 2 +- tests/t_ec_shortw_prj_g1_mul_distri.nim | 2 +- tests/t_ec_shortw_prj_g1_mul_vs_ref.nim | 2 +- .../t_ec_shortw_prj_g2_add_double_bw6_761.nim | 2 +- .../t_ec_shortw_prj_g2_mixed_add_bw6_761.nim | 2 +- .../t_ec_shortw_prj_g2_mul_distri_bw6_761.nim | 2 +- .../t_ec_shortw_prj_g2_mul_sanity_bw6_761.nim | 2 +- .../t_ec_shortw_prj_g2_mul_vs_ref_bw6_761.nim | 2 +- tests/t_finite_fields_mulsquare.nim | 10 +- tests/t_fr.nim | 129 ++++++++++++ 52 files changed, 702 insertions(+), 444 deletions(-) create mode 100644 constantine/config/curves_prop_core.nim create mode 100644 constantine/config/curves_prop_derived.nim rename constantine/config/{type_fp.nim => type_ff.nim} (63%) create mode 100644 tests/t_fr.nim diff --git a/benchmarks/bench_fields_template.nim b/benchmarks/bench_fields_template.nim index 312723d..a1b9d28 100644 --- a/benchmarks/bench_fields_template.nim +++ b/benchmarks/bench_fields_template.nim @@ -105,7 +105,7 @@ proc invEuclidBench*(T: typedesc, iters: int) = proc invPowFermatBench*(T: typedesc, iters: int) = let x = rng.random_unsafe(T) - const exponent = T.C.getInvModExponent() + const exponent = T.getInvModExponent() bench("Inversion via exponentiation p-2 (Little Fermat)", T, iters): var r = x r.powUnsafeExponent(exponent) diff --git a/constantine.nimble b/constantine.nimble index a511075..7b81384 100644 --- a/constantine.nimble +++ b/constantine.nimble @@ -148,7 +148,12 @@ const testDesc: seq[tuple[path: string, useGMP: bool]] = @[ ("tests/t_pairing_bls12_381_optate.nim", false), # Hashing vs OpenSSL - ("tests/t_hash_sha256_vs_openssl.nim", true), + # ---------------------------------------------------------- + ("tests/t_hash_sha256_vs_openssl.nim", true), # skip OpenSSL tests on Windows + + # Prime order fields + # ---------------------------------------------------------- + ("tests/t_fr.nim", false), ] # For temporary (hopefully) investigation that can only be reproduced in CI @@ -264,7 +269,7 @@ proc buildAllBenches() = buildBench("bench_pairing_bn254_snarks") buildBench("bench_sha256") echo "All benchmarks compile successfully." - + # Tasks # ---------------------------------------------------------------- diff --git a/constantine/arithmetic/finite_fields.nim b/constantine/arithmetic/finite_fields.nim index 04be32d..4597183 100644 --- a/constantine/arithmetic/finite_fields.nim +++ b/constantine/arithmetic/finite_fields.nim @@ -8,25 +8,27 @@ # ############################################################ # -# Fp: Finite Field arithmetic with prime field modulus P +# FF: Finite Field arithmetic +# Fp: with prime field modulus P +# Fr: with prime curve subgroup order r # # ############################################################ # Constraints: -# - We assume that p is known at compile-time -# - We assume that p is not even: +# - We assume that p and r are known at compile-time +# - We assume that p and r are not even: # - Operations are done in the Montgomery domain # - The Montgomery domain introduce a Montgomery constant that must be coprime # with the field modulus. # - The constant is chosen a power of 2 -# => to be coprime with a power of 2, p must be odd -# - We assume that p is a prime -# - Modular inversion uses the Fermat's little theorem +# => to be coprime with a power of 2, p and r must be odd +# - We assume that p and r are a prime +# - Modular inversion may use the Fermat's little theorem # which requires a prime import ../primitives, - ../config/[common, type_fp, curves], + ../config/[common, type_ff, curves], ./bigints, ./limbs_montgomery when UseASM_X86_64: @@ -37,7 +39,7 @@ when nimvm: else: discard -export Fp +export Fp, Fr, FF # No exceptions allowed {.push raises: [].} @@ -48,34 +50,34 @@ export Fp # # ############################################################ -func fromBig*[C: static Curve](dst: var Fp[C], src: BigInt) {.inline.}= +func fromBig*(dst: var FF, src: BigInt) {.inline.}= ## Convert a BigInt to its Montgomery form when nimvm: - dst.mres.montyResidue_precompute(src, C.Mod, C.getR2modP(), C.getNegInvModWord()) + dst.mres.montyResidue_precompute(src, FF.fieldMod(), FF.getR2modP(), FF.getNegInvModWord()) else: - dst.mres.montyResidue(src, C.Mod, C.getR2modP(), C.getNegInvModWord(), C.canUseNoCarryMontyMul()) + dst.mres.montyResidue(src, FF.fieldMod(), FF.getR2modP(), FF.getNegInvModWord(), FF.canUseNoCarryMontyMul()) -func fromBig*[C: static Curve](T: type Fp[C], src: BigInt): Fp[C] {.noInit, inline.} = +func fromBig*[C: static Curve](T: type FF[C], src: BigInt): FF[C] {.noInit, inline.} = ## Convert a BigInt to its Montgomery form result.fromBig(src) -func toBig*(src: Fp): auto {.noInit, inline.} = +func toBig*(src: FF): auto {.noInit, inline.} = ## Convert a finite-field element to a BigInt in natural representation var r {.noInit.}: typeof(src.mres) - r.redc(src.mres, Fp.C.Mod, Fp.C.getNegInvModWord(), Fp.C.canUseNoCarryMontyMul()) + r.redc(src.mres, FF.fieldMod(), FF.getNegInvModWord(), FF.canUseNoCarryMontyMul()) return r # Copy # ------------------------------------------------------------ -func ccopy*(a: var Fp, b: Fp, ctl: SecretBool) {.inline.} = +func ccopy*(a: var FF, b: FF, ctl: SecretBool) {.inline.} = ## Constant-time conditional copy ## If ctl is true: b is copied into a ## if ctl is false: b is not copied and a is unmodified ## Time and memory accesses are the same whether a copy occurs or not ccopy(a.mres, b.mres, ctl) -func cswap*(a, b: var Fp, ctl: CTBool) {.inline.} = +func cswap*(a, b: var FF, ctl: CTBool) {.inline.} = ## Swap ``a`` and ``b`` if ``ctl`` is true ## ## Constant-time: @@ -98,144 +100,144 @@ func cswap*(a, b: var Fp, ctl: CTBool) {.inline.} = # exist and can be implemented with compile-time specialization. # Note: for `+=`, double, sum -# not(a.mres < Fp.C.Mod) is unnecessary if the prime has the form +# not(a.mres < FF.fieldMod()) is unnecessary if the prime has the form # (2^64)^w - 1 (if using uint64 words). # In practice I'm not aware of such prime being using in elliptic curves. # 2^127 - 1 and 2^521 - 1 are used but 127 and 521 are not multiple of 32/64 -func `==`*(a, b: Fp): SecretBool {.inline.} = +func `==`*(a, b: FF): SecretBool {.inline.} = ## Constant-time equality check a.mres == b.mres -func isZero*(a: Fp): SecretBool {.inline.} = +func isZero*(a: FF): SecretBool {.inline.} = ## Constant-time check if zero a.mres.isZero() -func isOne*(a: Fp): SecretBool {.inline.} = +func isOne*(a: FF): SecretBool {.inline.} = ## Constant-time check if one - a.mres == Fp.C.getMontyOne() + a.mres == FF.getMontyOne() -func isMinusOne*(a: Fp): SecretBool {.inline.} = +func isMinusOne*(a: FF): SecretBool {.inline.} = ## Constant-time check if -1 (mod p) - a.mres == Fp.C.getMontyPrimeMinus1() + a.mres == FF.getMontyPrimeMinus1() -func setZero*(a: var Fp) {.inline.} = +func setZero*(a: var FF) {.inline.} = ## Set ``a`` to zero a.mres.setZero() -func setOne*(a: var Fp) {.inline.} = +func setOne*(a: var FF) {.inline.} = ## Set ``a`` to one # Note: we need 1 in Montgomery residue form # TODO: Nim codegen is not optimal it uses a temporary # Check if the compiler optimizes it away - a.mres = Fp.C.getMontyOne() + a.mres = FF.getMontyOne() -func `+=`*(a: var Fp, b: Fp) {.inline.} = +func `+=`*(a: var FF, b: FF) {.inline.} = ## In-place addition modulo p when UseASM_X86_64 and a.mres.limbs.len <= 6: # TODO: handle spilling - addmod_asm(a.mres.limbs, b.mres.limbs, Fp.C.Mod.limbs) + addmod_asm(a.mres.limbs, b.mres.limbs, FF.fieldMod().limbs) else: var overflowed = add(a.mres, b.mres) - overflowed = overflowed or not(a.mres < Fp.C.Mod) - discard csub(a.mres, Fp.C.Mod, overflowed) + overflowed = overflowed or not(a.mres < FF.fieldMod()) + discard csub(a.mres, FF.fieldMod(), overflowed) -func `-=`*(a: var Fp, b: Fp) {.inline.} = +func `-=`*(a: var FF, b: FF) {.inline.} = ## In-place substraction modulo p when UseASM_X86_64 and a.mres.limbs.len <= 6: # TODO: handle spilling - submod_asm(a.mres.limbs, b.mres.limbs, Fp.C.Mod.limbs) + submod_asm(a.mres.limbs, b.mres.limbs, FF.fieldMod().limbs) else: let underflowed = sub(a.mres, b.mres) - discard cadd(a.mres, Fp.C.Mod, underflowed) + discard cadd(a.mres, FF.fieldMod(), underflowed) -func double*(a: var Fp) {.inline.} = +func double*(a: var FF) {.inline.} = ## Double ``a`` modulo p when UseASM_X86_64 and a.mres.limbs.len <= 6: # TODO: handle spilling - addmod_asm(a.mres.limbs, a.mres.limbs, Fp.C.Mod.limbs) + addmod_asm(a.mres.limbs, a.mres.limbs, FF.fieldMod().limbs) else: var overflowed = double(a.mres) - overflowed = overflowed or not(a.mres < Fp.C.Mod) - discard csub(a.mres, Fp.C.Mod, overflowed) + overflowed = overflowed or not(a.mres < FF.fieldMod()) + discard csub(a.mres, FF.fieldMod(), overflowed) -func sum*(r: var Fp, a, b: Fp) {.inline.} = +func sum*(r: var FF, a, b: FF) {.inline.} = ## Sum ``a`` and ``b`` into ``r`` modulo p ## r is initialized/overwritten when UseASM_X86_64 and a.mres.limbs.len <= 6: # TODO: handle spilling r = a - addmod_asm(r.mres.limbs, b.mres.limbs, Fp.C.Mod.limbs) + addmod_asm(r.mres.limbs, b.mres.limbs, FF.fieldMod().limbs) else: var overflowed = r.mres.sum(a.mres, b.mres) - overflowed = overflowed or not(r.mres < Fp.C.Mod) - discard csub(r.mres, Fp.C.Mod, overflowed) + overflowed = overflowed or not(r.mres < FF.fieldMod()) + discard csub(r.mres, FF.fieldMod(), overflowed) -func sumNoReduce*(r: var Fp, a, b: Fp) {.inline.} = +func sumNoReduce*(r: var FF, a, b: FF) {.inline.} = ## Sum ``a`` and ``b`` into ``r`` without reduction discard r.mres.sum(a.mres, b.mres) -func diff*(r: var Fp, a, b: Fp) {.inline.} = +func diff*(r: var FF, a, b: FF) {.inline.} = ## Substract `b` from `a` and store the result into `r`. ## `r` is initialized/overwritten ## Requires r != b when UseASM_X86_64 and a.mres.limbs.len <= 6: # TODO: handle spilling r = a - submod_asm(r.mres.limbs, b.mres.limbs, Fp.C.Mod.limbs) + submod_asm(r.mres.limbs, b.mres.limbs, FF.fieldMod().limbs) else: var underflowed = r.mres.diff(a.mres, b.mres) - discard cadd(r.mres, Fp.C.Mod, underflowed) + discard cadd(r.mres, FF.fieldMod(), underflowed) -func diffAlias*(r: var Fp, a, b: Fp) {.inline.} = +func diffAlias*(r: var FF, a, b: FF) {.inline.} = ## Substract `b` from `a` and store the result into `r`. ## `r` is initialized/overwritten ## Handles r == b when UseASM_X86_64 and a.mres.limbs.len <= 6: # TODO: handle spilling var t = a - submod_asm(t.mres.limbs, b.mres.limbs, Fp.C.Mod.limbs) + submod_asm(t.mres.limbs, b.mres.limbs, FF.fieldMod().limbs) r = t else: var underflowed = r.mres.diff(a.mres, b.mres) - discard cadd(r.mres, Fp.C.Mod, underflowed) + discard cadd(r.mres, FF.fieldMod(), underflowed) -func diffNoReduce*(r: var Fp, a, b: Fp) {.inline.} = +func diffNoReduce*(r: var FF, a, b: FF) {.inline.} = ## Substract `b` from `a` and store the result into `r` ## without reduction discard r.mres.diff(a.mres, b.mres) -func double*(r: var Fp, a: Fp) {.inline.} = +func double*(r: var FF, a: FF) {.inline.} = ## Double ``a`` into ``r`` ## `r` is initialized/overwritten when UseASM_X86_64 and a.mres.limbs.len <= 6: # TODO: handle spilling r = a - addmod_asm(r.mres.limbs, a.mres.limbs, Fp.C.Mod.limbs) + addmod_asm(r.mres.limbs, a.mres.limbs, FF.fieldMod().limbs) else: var overflowed = r.mres.double(a.mres) - overflowed = overflowed or not(r.mres < Fp.C.Mod) - discard csub(r.mres, Fp.C.Mod, overflowed) + overflowed = overflowed or not(r.mres < FF.fieldMod()) + discard csub(r.mres, FF.fieldMod(), overflowed) -func prod*(r: var Fp, a, b: Fp) {.inline.} = +func prod*(r: var FF, a, b: FF) {.inline.} = ## Store the product of ``a`` by ``b`` modulo p into ``r`` ## ``r`` is initialized / overwritten - r.mres.montyMul(a.mres, b.mres, Fp.C.Mod, Fp.C.getNegInvModWord(), Fp.C.canUseNoCarryMontyMul()) + r.mres.montyMul(a.mres, b.mres, FF.fieldMod(), FF.getNegInvModWord(), FF.canUseNoCarryMontyMul()) -func square*(r: var Fp, a: Fp) {.inline.} = +func square*(r: var FF, a: FF) {.inline.} = ## Squaring modulo p - r.mres.montySquare(a.mres, Fp.C.Mod, Fp.C.getNegInvModWord(), Fp.C.canUseNoCarryMontySquare()) + r.mres.montySquare(a.mres, FF.fieldMod(), FF.getNegInvModWord(), FF.canUseNoCarryMontySquare()) -func neg*(r: var Fp, a: Fp) {.inline.} = +func neg*(r: var FF, a: FF) {.inline.} = ## Negate modulo p when UseASM_X86_64 and defined(gcc): # Clang and every compiler besides GCC # can cleanly optimized this - # especially on Fp2 - negmod_asm(r.mres.limbs, a.mres.limbs, Fp.C.Mod.limbs) + # especially on FF2 + negmod_asm(r.mres.limbs, a.mres.limbs, FF.fieldMod().limbs) else: - discard r.mres.diff(Fp.C.Mod, a.mres) + discard r.mres.diff(FF.fieldMod(), a.mres) -func neg*(a: var Fp) {.inline.} = +func neg*(a: var FF) {.inline.} = ## Negate modulo p a.neg(a) -func div2*(a: var Fp) {.inline.} = +func div2*(a: var FF) {.inline.} = ## Modular division by 2 - a.mres.div2_modular(Fp.C.getPrimePlus1div2()) + a.mres.div2_modular(FF.getPrimePlus1div2()) # ############################################################ # @@ -243,26 +245,26 @@ func div2*(a: var Fp) {.inline.} = # # ############################################################ -func cneg*(r: var Fp, a: Fp, ctl: SecretBool) = +func cneg*(r: var FF, a: FF, ctl: SecretBool) = ## Constant-time in-place conditional negation ## The negation is only performed if ctl is "true" r.neg(a) r.ccopy(a, not ctl) -func cneg*(a: var Fp, ctl: SecretBool) = +func cneg*(a: var FF, ctl: SecretBool) = ## Constant-time in-place conditional negation ## The negation is only performed if ctl is "true" var t = a a.cneg(t, ctl) -func cadd*(a: var Fp, b: Fp, ctl: SecretBool) = +func cadd*(a: var FF, b: FF, ctl: SecretBool) = ## Constant-time in-place conditional addition ## The addition is only performed if ctl is "true" var t = a t += b a.ccopy(t, ctl) -func csub*(a: var Fp, b: Fp, ctl: SecretBool) = +func csub*(a: var FF, b: FF, ctl: SecretBool) = ## Constant-time in-place conditional substraction ## The substraction is only performed if ctl is "true" var t = a @@ -277,33 +279,33 @@ func csub*(a: var Fp, b: Fp, ctl: SecretBool) = # # Internally those procedures will allocate extra scratchspace on the stack -func pow*(a: var Fp, exponent: BigInt) {.inline.} = +func pow*(a: var FF, exponent: BigInt) {.inline.} = ## Exponentiation modulo p ## ``a``: a field element to be exponentiated ## ``exponent``: a big integer const windowSize = 5 # TODO: find best window size for each curves a.mres.montyPow( exponent, - Fp.C.Mod, Fp.C.getMontyOne(), - Fp.C.getNegInvModWord(), windowSize, - Fp.C.canUseNoCarryMontyMul(), - Fp.C.canUseNoCarryMontySquare() + FF.fieldMod(), FF.getMontyOne(), + FF.getNegInvModWord(), windowSize, + FF.canUseNoCarryMontyMul(), + FF.canUseNoCarryMontySquare() ) -func pow*(a: var Fp, exponent: openarray[byte]) {.inline.} = +func pow*(a: var FF, exponent: openarray[byte]) {.inline.} = ## Exponentiation modulo p ## ``a``: a field element to be exponentiated ## ``exponent``: a big integer in canonical big endian representation const windowSize = 5 # TODO: find best window size for each curves a.mres.montyPow( exponent, - Fp.C.Mod, Fp.C.getMontyOne(), - Fp.C.getNegInvModWord(), windowSize, - Fp.C.canUseNoCarryMontyMul(), - Fp.C.canUseNoCarryMontySquare() + FF.fieldMod(), FF.getMontyOne(), + FF.getNegInvModWord(), windowSize, + FF.canUseNoCarryMontyMul(), + FF.canUseNoCarryMontySquare() ) -func powUnsafeExponent*(a: var Fp, exponent: BigInt) {.inline.} = +func powUnsafeExponent*(a: var FF, exponent: BigInt) {.inline.} = ## Exponentiation modulo p ## ``a``: a field element to be exponentiated ## ``exponent``: a big integer @@ -317,13 +319,13 @@ func powUnsafeExponent*(a: var Fp, exponent: BigInt) {.inline.} = const windowSize = 5 # TODO: find best window size for each curves a.mres.montyPowUnsafeExponent( exponent, - Fp.C.Mod, Fp.C.getMontyOne(), - Fp.C.getNegInvModWord(), windowSize, - Fp.C.canUseNoCarryMontyMul(), - Fp.C.canUseNoCarryMontySquare() + FF.fieldMod(), FF.getMontyOne(), + FF.getNegInvModWord(), windowSize, + FF.canUseNoCarryMontyMul(), + FF.canUseNoCarryMontySquare() ) -func powUnsafeExponent*(a: var Fp, exponent: openarray[byte]) {.inline.} = +func powUnsafeExponent*(a: var FF, exponent: openarray[byte]) {.inline.} = ## Exponentiation modulo p ## ``a``: a field element to be exponentiated ## ``exponent``: a big integer a big integer in canonical big endian representation @@ -337,10 +339,10 @@ func powUnsafeExponent*(a: var Fp, exponent: openarray[byte]) {.inline.} = const windowSize = 5 # TODO: find best window size for each curves a.mres.montyPowUnsafeExponent( exponent, - Fp.C.Mod, Fp.C.getMontyOne(), - Fp.C.getNegInvModWord(), windowSize, - Fp.C.canUseNoCarryMontyMul(), - Fp.C.canUseNoCarryMontySquare() + FF.fieldMod(), FF.getMontyOne(), + FF.getNegInvModWord(), windowSize, + FF.canUseNoCarryMontyMul(), + FF.canUseNoCarryMontySquare() ) # ############################################################ @@ -355,36 +357,36 @@ func powUnsafeExponent*(a: var Fp, exponent: openarray[byte]) {.inline.} = # - Those that return a field element # - Those that internally allocate a temporary field element -func `+`*(a, b: Fp): Fp {.noInit, inline.} = +func `+`*(a, b: FF): FF {.noInit, inline.} = ## Addition modulo p result.sum(a, b) -func `-`*(a, b: Fp): Fp {.noInit, inline.} = +func `-`*(a, b: FF): FF {.noInit, inline.} = ## Substraction modulo p result.diff(a, b) -func `*`*(a, b: Fp): Fp {.noInit, inline.} = +func `*`*(a, b: FF): FF {.noInit, inline.} = ## Multiplication modulo p ## ## It is recommended to assign with {.noInit.} - ## as Fp elements are usually large and this + ## as FF elements are usually large and this ## routine will zero init internally the result. result.prod(a, b) -func `*=`*(a: var Fp, b: Fp) {.inline.} = +func `*=`*(a: var FF, b: FF) {.inline.} = ## Multiplication modulo p a.prod(a, b) -func square*(a: var Fp) {.inline.} = +func square*(a: var FF) {.inline.} = ## Squaring modulo p - a.mres.montySquare(a.mres, Fp.C.Mod, Fp.C.getNegInvModWord(), Fp.C.canUseNoCarryMontySquare()) + a.mres.montySquare(a.mres, FF.fieldMod(), FF.getNegInvModWord(), FF.canUseNoCarryMontySquare()) -func square_repeated*(r: var Fp, num: int) {.inline.} = +func square_repeated*(r: var FF, num: int) {.inline.} = ## Repeated squarings for _ in 0 ..< num: r.square() -func `*=`*(a: var Fp, b: static int) {.inline.} = +func `*=`*(a: var FF, b: static int) {.inline.} = ## Multiplication by a small integer known at compile-time # Implementation: # We don't want to go convert the integer to the Montgomery domain (O(n²)) @@ -466,7 +468,7 @@ func `*=`*(a: var Fp, b: static int) {.inline.} = else: {.error: "Multiplication by this small int not implemented".} -func `*`*(b: static int, a: Fp): Fp {.noinit, inline.} = +func `*`*(b: static int, a: FF): FF {.noinit, inline.} = ## Multiplication by a small integer known at compile-time result = a result *= b diff --git a/constantine/arithmetic/finite_fields_double_width.nim b/constantine/arithmetic/finite_fields_double_width.nim index 26d8a1a..0d176b7 100644 --- a/constantine/arithmetic/finite_fields_double_width.nim +++ b/constantine/arithmetic/finite_fields_double_width.nim @@ -7,7 +7,7 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - ../config/[common, curves, type_fp], + ../config/[common, curves, type_ff], ../primitives, ./bigints, ./finite_fields, @@ -38,8 +38,8 @@ func reduce*(r: var Fp, a: FpDbl) {.inline.} = r.mres.limbs, a.limbs2x, Fp.C.Mod.limbs, - Fp.C.getNegInvModWord(), - Fp.C.canUseNoCarryMontyMul() + Fp.getNegInvModWord(), + Fp.canUseNoCarryMontyMul() ) func diffNoInline(r: var FpDbl, a, b: FpDbl): Borrow = diff --git a/constantine/arithmetic/finite_fields_inversion.nim b/constantine/arithmetic/finite_fields_inversion.nim index 27ad282..51ee7b2 100644 --- a/constantine/arithmetic/finite_fields_inversion.nim +++ b/constantine/arithmetic/finite_fields_inversion.nim @@ -7,7 +7,7 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - ../config/[curves, type_fp], + ../config/[curves, type_ff], ./bigints, ../curves/zoo_inversions @@ -23,7 +23,7 @@ func inv_euclid*(r: var Fp, a: Fp) {.inline.} = ## Inversion modulo p via ## Niels Moller constant-time version of ## Stein's GCD derived from extended binary Euclid algorithm - r.mres.steinsGCD(a.mres, Fp.C.getR2modP(), Fp.C.Mod, Fp.C.getPrimePlus1div2()) + r.mres.steinsGCD(a.mres, Fp.getR2modP(), Fp.C.Mod, Fp.getPrimePlus1div2()) func inv*(r: var Fp, a: Fp) {.inline.} = ## Inversion modulo p diff --git a/constantine/arithmetic/finite_fields_square_root.nim b/constantine/arithmetic/finite_fields_square_root.nim index bc2dcfe..c022b3f 100644 --- a/constantine/arithmetic/finite_fields_square_root.nim +++ b/constantine/arithmetic/finite_fields_square_root.nim @@ -8,7 +8,7 @@ import ../primitives, - ../config/[common, type_fp, curves], + ../config/[common, type_ff, curves], ../curves/zoo_square_roots, ./bigints, ./finite_fields @@ -21,7 +21,7 @@ import # Legendre symbol / Euler's Criterion / Kronecker's symbol # ------------------------------------------------------------ -func isSquare*[C](a: Fp[C]): SecretBool {.inline.} = +func isSquare*(a: Fp): SecretBool {.inline.} = ## Returns true if ``a`` is a square (quadratic residue) in 𝔽p ## ## Assumes that the prime modulus ``p`` is public. @@ -30,7 +30,7 @@ func isSquare*[C](a: Fp[C]): SecretBool {.inline.} = # Note that we don't care about leaking the bits of p # as we assume that var xi {.noInit.} = a # TODO: is noInit necessary? see https://github.com/mratsim/constantine/issues/21 - xi.powUnsafeExponent(C.getPrimeMinus1div2_BE()) + xi.powUnsafeExponent(Fp.getPrimeMinus1div2_BE()) result = not(xi.isMinusOne()) # xi can be: # - 1 if a square @@ -46,7 +46,7 @@ func isSquare*[C](a: Fp[C]): SecretBool {.inline.} = # Specialized routine for p ≡ 3 (mod 4) # ------------------------------------------------------------ -func sqrt_p3mod4[C](a: var Fp[C]) {.inline.} = +func sqrt_p3mod4(a: var Fp) {.inline.} = ## Compute the square root of ``a`` ## ## This requires ``a`` to be a square @@ -57,10 +57,10 @@ func sqrt_p3mod4[C](a: var Fp[C]) {.inline.} = ## The square root, if it exist is multivalued, ## i.e. both x² == (-x)² ## This procedure returns a deterministic result - static: doAssert BaseType(C.Mod.limbs[0]) mod 4 == 3 - a.powUnsafeExponent(C.getPrimePlus1div4_BE()) + static: doAssert BaseType(Fp.C.Mod.limbs[0]) mod 4 == 3 + a.powUnsafeExponent(Fp.getPrimePlus1div4_BE()) -func sqrt_invsqrt_p3mod4[C](sqrt, invsqrt: var Fp[C], a: Fp[C]) {.inline.} = +func sqrt_invsqrt_p3mod4(sqrt, invsqrt: var Fp, a: Fp) {.inline.} = ## If ``a`` is a square, compute the square root of ``a`` in sqrt ## and the inverse square root of a in invsqrt ## @@ -74,14 +74,14 @@ func sqrt_invsqrt_p3mod4[C](sqrt, invsqrt: var Fp[C], a: Fp[C]) {.inline.} = # a^((p-1)/2)) * a^-1 ≡ 1/a (mod p) # a^((p-3)/2)) ≡ 1/a (mod p) # a^((p-3)/4)) ≡ 1/√a (mod p) # Requires p ≡ 3 (mod 4) - static: doAssert BaseType(C.Mod.limbs[0]) mod 4 == 3 + static: doAssert BaseType(Fp.C.Mod.limbs[0]) mod 4 == 3 invsqrt = a - invsqrt.powUnsafeExponent(C.getPrimeMinus3div4_BE()) + invsqrt.powUnsafeExponent(Fp.getPrimeMinus3div4_BE()) # √a ≡ a * 1/√a ≡ a^((p+1)/4) (mod p) sqrt.prod(invsqrt, a) -func sqrt_invsqrt_if_square_p3mod4[C](sqrt, invsqrt: var Fp[C], a: Fp[C]): SecretBool {.inline.} = +func sqrt_invsqrt_if_square_p3mod4(sqrt, invsqrt: var Fp, a: Fp): SecretBool {.inline.} = ## If ``a`` is a square, compute the square root of ``a`` in sqrt ## and the inverse square root of a in invsqrt ## @@ -89,11 +89,11 @@ func sqrt_invsqrt_if_square_p3mod4[C](sqrt, invsqrt: var Fp[C], a: Fp[C]): Secre ## ## This assumes that the prime field modulus ``p``: p ≡ 3 (mod 4) sqrt_invsqrt_p3mod4(sqrt, invsqrt, a) - var test {.noInit.}: Fp[C] + var test {.noInit.}: Fp test.square(sqrt) result = test == a -func sqrt_if_square_p3mod4[C](a: var Fp[C]): SecretBool {.inline.} = +func sqrt_if_square_p3mod4(a: var Fp): SecretBool {.inline.} = ## If ``a`` is a square, compute the square root of ``a`` ## if not, ``a`` is unmodified. ## @@ -104,28 +104,28 @@ func sqrt_if_square_p3mod4[C](a: var Fp[C]): SecretBool {.inline.} = ## The square root, if it exist is multivalued, ## i.e. both x² == (-x)² ## This procedure returns a deterministic result - var sqrt {.noInit.}, invsqrt {.noInit.}: Fp[C] + var sqrt {.noInit.}, invsqrt {.noInit.}: Fp result = sqrt_invsqrt_if_square_p3mod4(sqrt, invsqrt, a) a.ccopy(sqrt, result) # Tonelli Shanks for any prime # ------------------------------------------------------------ -func precompute_tonelli_shanks[C]( - a_pre_exp: var Fp[C], - a: Fp[C]) = +func precompute_tonelli_shanks( + a_pre_exp: var Fp, + a: Fp) = a_pre_exp = a - a_pre_exp.powUnsafeExponent(C.tonelliShanks(exponent)) + a_pre_exp.powUnsafeExponent(Fp.C.tonelliShanks(exponent)) -func isSquare_tonelli_shanks[C]( - a, a_pre_exp: Fp[C]): SecretBool = +func isSquare_tonelli_shanks( + a, a_pre_exp: Fp): SecretBool = ## Returns if `a` is a quadratic residue ## This uses common precomputation for ## Tonelli-Shanks based square root and inverse square root ## ## a^((p-1-2^e)/(2*2^e)) - const e = C.tonelliShanks(twoAdicity) - var r {.noInit.}: Fp[C] + const e = Fp.C.tonelliShanks(twoAdicity) + var r {.noInit.}: Fp r.square(a_pre_exp) # a^(2(q-1-2^e)/(2*2^e)) = a^((q-1)/2^e - 1) r *= a # a^((q-1)/2^e) for _ in 0 ..< e-1: @@ -143,9 +143,9 @@ func isSquare_tonelli_shanks[C]( r.isMinusOne() ) -func sqrt_invsqrt_tonelli_shanks[C]( - sqrt, invsqrt: var Fp[C], - a, a_pre_exp: Fp[C]) = +func sqrt_invsqrt_tonelli_shanks( + sqrt, invsqrt: var Fp, + a, a_pre_exp: Fp) = ## Compute the square_root and inverse_square_root ## of `a` via constant-time Tonelli-Shanks ## @@ -153,16 +153,16 @@ func sqrt_invsqrt_tonelli_shanks[C]( ## ThItat is shared with the simultaneous isSquare routine template z: untyped = a_pre_exp template r: untyped = invsqrt - var t {.noInit.}: Fp[C] - const e = C.tonelliShanks(twoAdicity) + var t {.noInit.}: Fp + const e = Fp.C.tonelliShanks(twoAdicity) t.square(z) t *= a r = z var b = t - var root = C.tonelliShanks(root_of_unity) + var root = Fp.C.tonelliShanks(root_of_unity) - var buf {.noInit.}: Fp[C] + var buf {.noInit.}: Fp for i in countdown(e, 2, 1): for j in 1 .. i-2: diff --git a/constantine/config/curves.nim b/constantine/config/curves.nim index 3bd5894..6103a59 100644 --- a/constantine/config/curves.nim +++ b/constantine/config/curves.nim @@ -6,191 +6,5 @@ # * 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/macros, - # Internal - ./type_bigint, ./common, - ./curves_declaration, ./curves_derived, ./curves_parser - -export CurveFamily, Curve, SexticTwist - -# ############################################################ -# -# Field properties -# -# ############################################################ - -{.experimental: "dynamicBindSym".} - -macro Mod*(C: static Curve): untyped = - ## Get the Modulus associated to a curve - result = bindSym($C & "_Modulus") - -template getCurveBitwidth*(C: Curve): int = - ## Returns the number of bits taken by the curve modulus - CurveBitWidth[C] - -template matchingBigInt*(C: static Curve): untyped = - BigInt[CurveBitWidth[C]] - -template family*(C: Curve): CurveFamily = - CurveFamilies[C] - -template matchingLimbs2x*(C: Curve): untyped = - const N2 = wordsRequired(getCurveBitwidth(C)) * 2 # TODO upstream, not precomputing N2 breaks semcheck - array[N2, SecretWord] # TODO upstream, using Limbs[N2] breaks semcheck - -# ############################################################ -# -# Curve properties -# -# ############################################################ - -macro getCurveOrder*(C: static Curve): untyped = - ## Get the curve order `r` - ## i.e. the number of points on the elliptic curve - result = bindSym($C & "_Order") - -macro getCurveOrderBitwidth*(C: static Curve): untyped = - ## Get the curve order `r` - ## i.e. the number of points on the elliptic curve - result = nnkDotExpr.newTree( - getAST(getCurveOrder(C)), - ident"bits" - ) - -macro getEquationForm*(C: static Curve): untyped = - ## Returns the equation form - ## (ShortWeierstrass, Montgomery, Twisted Edwards, Weierstrass, ...) - result = bindSym($C & "_equation_form") - -macro getCoefA*(C: static Curve): untyped = - ## Returns the A coefficient of the curve - ## The return type is polymorphic, it can be an int - ## or a bigInt depending on the curve - result = bindSym($C & "_coef_A") - -macro getCoefB*(C: static Curve): untyped = - ## Returns the B coefficient of the curve - ## The return type is polymorphic, it can be an int - ## or a bigInt depending on the curve - result = bindSym($C & "_coef_B") - -macro get_QNR_Fp*(C: static Curve): untyped = - ## Returns the tower extension quadratic non-residue in 𝔽p - ## i.e. a number that is not a square in 𝔽p - result = bindSym($C & "_nonresidue_quad_fp") - -macro get_CNR_Fp2*(C: static Curve): untyped = - ## Returns the tower extension cubic non-residue 𝔽p² - ## i.e. a number that is not a cube in 𝔽p² - ## - ## The return value is a tuple (a, b) - ## that corresponds to the number a + b𝑗 - ## with 𝑗 choosen for 𝑗² - QNR_Fp == 0 - ## i.e. if -1 is chosen as a quadratic non-residue 𝑗 = √-1 - ## if -2 is chosen as a quadratic non-residue 𝑗 = √-2 - result = bindSym($C & "_nonresidue_cube_fp2") - -macro getEmbeddingDegree*(C: static Curve): untyped = - ## Returns the prime embedding degree, - ## i.e. the smallest k such that r|𝑝^𝑘−1 - ## equivalently 𝑝^𝑘 ≡ 1 (mod r) - ## with r the curve order and p its field modulus - result = bindSym($C & "_embedding_degree") - -macro getSexticTwist*(C: static Curve): untyped = - ## Returns if D-Twist or M-Twist - result = bindSym($C & "_sexticTwist") - -macro get_SNR_Fp2*(C: static Curve): untyped = - ## Returns the sextic non-residue in 𝔽p² - ## choosen to build the twisted curve E'(𝔽p²) - ## i.e. a number µ so that x⁶ - µ is irreducible - result = bindSym($C & "_sexticNonResidue_fp2") - -# ############################################################ -# -# Access precomputed derived constants in ROM -# -# ############################################################ - -genDerivedConstants() - -macro canUseNoCarryMontyMul*(C: static Curve): untyped = - ## Returns true if the Modulus is compatible with a fast - ## Montgomery multiplication that avoids many carries - result = bindSym($C & "_CanUseNoCarryMontyMul") - -macro canUseNoCarryMontySquare*(C: static Curve): untyped = - ## Returns true if the Modulus is compatible with a fast - ## Montgomery squaring that avoids many carries - result = bindSym($C & "_CanUseNoCarryMontySquare") - -macro getR2modP*(C: static Curve): untyped = - ## Get the Montgomery "R^2 mod P" constant associated to a curve field modulus - result = bindSym($C & "_R2modP") - -macro getNegInvModWord*(C: static Curve): untyped = - ## Get the Montgomery "-1/P[0] mod 2^Wordbitwidth" constant associated to a curve field modulus - result = bindSym($C & "_NegInvModWord") - -macro getMontyOne*(C: static Curve): untyped = - ## Get one in Montgomery representation (i.e. R mod P) - result = bindSym($C & "_MontyOne") - -macro getMontyPrimeMinus1*(C: static Curve): untyped = - ## Get (P+1) / 2 for an odd prime - result = bindSym($C & "_MontyPrimeMinus1") - -macro getInvModExponent*(C: static Curve): untyped = - ## Get modular inversion exponent (Modulus-2 in canonical representation) - result = bindSym($C & "_InvModExponent") - -macro getPrimePlus1div2*(C: static Curve): untyped = - ## Get (P+1) / 2 for an odd prime - ## Warning ⚠️: Result in canonical domain (not Montgomery) - result = bindSym($C & "_PrimePlus1div2") - -macro getPrimeMinus1div2_BE*(C: static Curve): untyped = - ## Get (P-1) / 2 in big-endian serialized format - result = bindSym($C & "_PrimeMinus1div2_BE") - -macro getPrimeMinus3div4_BE*(C: static Curve): untyped = - ## Get (P-3) / 2 in big-endian serialized format - result = bindSym($C & "_PrimeMinus3div4_BE") - -macro getPrimePlus1div4_BE*(C: static Curve): untyped = - ## Get (P+1) / 4 for an odd prime in big-endian serialized format - result = bindSym($C & "_PrimePlus1div4_BE") - -# ############################################################ -# -# Debug info printed at compile-time -# -# ############################################################ - -macro debugConsts(): untyped {.used.} = - let curves = bindSym("Curve") - let E = curves.getImpl[2] - - result = newStmtList() - for i in 1 ..< E.len: - let curve = E[i] - let curveName = $curve - let modulus = bindSym(curveName & "_Modulus") - let r2modp = bindSym(curveName & "_R2modP") - let negInvModWord = bindSym(curveName & "_NegInvModWord") - - result.add quote do: - echo "Curve ", `curveName`,':' - echo " Field Modulus: ", `modulus` - echo " Montgomery R² (mod P): ", `r2modp` - echo " Montgomery -1/P[0] (mod 2^", WordBitWidth, "): ", `negInvModWord` - - result.add quote do: - echo "----------------------------------------------------------------------------" - -# debug: # displayed with -d:debugConstantine -# debugConsts() +import curves_prop_core, curves_prop_derived +export curves_prop_core, curves_prop_derived diff --git a/constantine/config/curves_derived.nim b/constantine/config/curves_derived.nim index a843876..87530a7 100644 --- a/constantine/config/curves_derived.nim +++ b/constantine/config/curves_derived.nim @@ -15,7 +15,12 @@ import {.experimental: "dynamicBindSym".} -macro genDerivedConstants*(): untyped = +type + DerivedConstantMode* = enum + kModulus + kOrder + +macro genDerivedConstants*(mode: static DerivedConstantMode): untyped = ## Generate constants derived from the main constants ## ## For example @@ -38,87 +43,92 @@ macro genDerivedConstants*(): untyped = nnkPragma.newTree(ident"used") ) + let ff = if mode == kModulus: "_Fp" else: "_Fr" + + for curveSym in low(Curve) .. high(Curve): let curve = $curveSym + let M = if mode == kModulus: bindSym(curve & "_Modulus") + else: bindSym(curve & "_Order") # const MyCurve_CanUseNoCarryMontyMul = useNoCarryMontyMul(MyCurve_Modulus) result.add newConstStmt( - used(curve & "_CanUseNoCarryMontyMul"), newCall( + used(curve & ff & "_CanUseNoCarryMontyMul"), newCall( bindSym"useNoCarryMontyMul", - bindSym(curve & "_Modulus") + M ) ) # const MyCurve_CanUseNoCarryMontySquare = useNoCarryMontySquare(MyCurve_Modulus) result.add newConstStmt( - used(curve & "_CanUseNoCarryMontySquare"), newCall( + used(curve & ff & "_CanUseNoCarryMontySquare"), newCall( bindSym"useNoCarryMontySquare", - bindSym(curve & "_Modulus") + M ) ) # const MyCurve_R2modP = r2mod(MyCurve_Modulus) result.add newConstStmt( - used(curve & "_R2modP"), newCall( + used(curve & ff & "_R2modP"), newCall( bindSym"r2mod", - bindSym(curve & "_Modulus") + M ) ) # const MyCurve_NegInvModWord = negInvModWord(MyCurve_Modulus) result.add newConstStmt( - used(curve & "_NegInvModWord"), newCall( + used(curve & ff & "_NegInvModWord"), newCall( bindSym"negInvModWord", - bindSym(curve & "_Modulus") + M ) ) # const MyCurve_montyOne = montyOne(MyCurve_Modulus) result.add newConstStmt( - used(curve & "_MontyOne"), newCall( + used(curve & ff & "_MontyOne"), newCall( bindSym"montyOne", - bindSym(curve & "_Modulus") + M ) ) # const MyCurve_MontyPrimeMinus1 = montyPrimeMinus1(MyCurve_Modulus) result.add newConstStmt( - used(curve & "_MontyPrimeMinus1"), newCall( + used(curve & ff & "_MontyPrimeMinus1"), newCall( bindSym"montyPrimeMinus1", - bindSym(curve & "_Modulus") + M ) ) # const MyCurve_InvModExponent = primeMinus2_BE(MyCurve_Modulus) result.add newConstStmt( - used(curve & "_InvModExponent"), newCall( + used(curve & ff & "_InvModExponent"), newCall( bindSym"primeMinus2_BE", - bindSym(curve & "_Modulus") + M ) ) # const MyCurve_PrimePlus1div2 = primePlus1div2(MyCurve_Modulus) result.add newConstStmt( - used(curve & "_PrimePlus1div2"), newCall( + used(curve & ff & "_PrimePlus1div2"), newCall( bindSym"primePlus1div2", - bindSym(curve & "_Modulus") + M ) ) # const MyCurve_PrimeMinus1div2_BE = primeMinus1div2_BE(MyCurve_Modulus) result.add newConstStmt( - used(curve & "_PrimeMinus1div2_BE"), newCall( + used(curve & ff & "_PrimeMinus1div2_BE"), newCall( bindSym"primeMinus1div2_BE", - bindSym(curve & "_Modulus") + M ) ) # const MyCurve_PrimeMinus3div4_BE = primeMinus3div4_BE(MyCurve_Modulus) result.add newConstStmt( - used(curve & "_PrimeMinus3div4_BE"), newCall( + used(curve & ff & "_PrimeMinus3div4_BE"), newCall( bindSym"primeMinus3div4_BE", - bindSym(curve & "_Modulus") + M ) ) # const MyCurve_PrimePlus1div4_BE = primePlus1div4_BE(MyCurve_Modulus) result.add newConstStmt( - used(curve & "_PrimePlus1div4_BE"), newCall( + used(curve & ff & "_PrimePlus1div4_BE"), newCall( bindSym"primePlus1div4_BE", - bindSym(curve & "_Modulus") + M ) ) diff --git a/constantine/config/curves_parser.nim b/constantine/config/curves_parser.nim index 09846fe..bc06aee 100644 --- a/constantine/config/curves_parser.nim +++ b/constantine/config/curves_parser.nim @@ -237,8 +237,12 @@ template getCoef(c: CurveCoef, width: NimNode): untyped {.dirty.}= proc genMainConstants(defs: var seq[CurveParams]): NimNode = ## Generate curves and fields main constants + # MapCurveBitWidth & MapCurveOrderBitWidth + # are workaround for https://github.com/nim-lang/Nim/issues/16774 + var Curves: seq[NimNode] var MapCurveBitWidth = nnkBracket.newTree() + var MapCurveOrderBitWidth = nnkBracket.newTree() var MapCurveFamily = nnkBracket.newTree() var curveModStmts = newStmtList() var curveEllipticStmts = newStmtList() @@ -290,6 +294,22 @@ proc genMainConstants(defs: var seq[CurveParams]): NimNode = curveDef.order ) ) + MapCurveOrderBitWidth.add nnkExprColonExpr.newTree( + curve, curveDef.orderBitwidth + ) + else: # Dummy + curveEllipticStmts.add newConstStmt( + exported($curve & "_Order"), + newCall( + bindSym"fromHex", + nnkBracketExpr.newTree(bindSym"BigInt", newLit 1), + newLit"0x1" + ) + ) + MapCurveOrderBitWidth.add nnkExprColonExpr.newTree( + curve, newLit 1 + ) + if curveDef.coef_A.kind != NoCoef and curveDef.coef_B.kind != NoCoef: curveEllipticStmts.add newConstStmt( exported($curve & "_coef_A"), @@ -347,6 +367,10 @@ proc genMainConstants(defs: var seq[CurveParams]): NimNode = result.add newConstStmt( exported("CurveFamilies"), MapCurveFamily ) + # const CurveOrderBitSize: array[Curve, int] = ... + result.add newConstStmt( + exported("CurveOrderBitWidth"), MapCurveOrderBitWidth + ) result.add curveModStmts result.add curveEllipticStmts diff --git a/constantine/config/curves_prop_core.nim b/constantine/config/curves_prop_core.nim new file mode 100644 index 0000000..e214f9c --- /dev/null +++ b/constantine/config/curves_prop_core.nim @@ -0,0 +1,116 @@ +# 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/macros, + # Internal + ./type_bigint, ./common, + ./curves_declaration, ./curves_parser + +export CurveFamily, Curve, SexticTwist + +# ############################################################ +# +# Field properties +# +# ############################################################ + +{.experimental: "dynamicBindSym".} + +macro Mod*(C: static Curve): untyped = + ## Get the Modulus associated to a curve + result = bindSym($C & "_Modulus") + +template getCurveBitwidth*(C: Curve): int = + ## Returns the number of bits taken by the curve modulus + CurveBitWidth[C] + +template matchingBigInt*(C: static Curve): untyped = + # Workaround: https://github.com/nim-lang/Nim/issues/16774 + BigInt[CurveBitWidth[C]] + +template family*(C: Curve): CurveFamily = + CurveFamilies[C] + +template matchingLimbs2x*(C: Curve): untyped = + const N2 = wordsRequired(getCurveBitwidth(C)) * 2 # TODO upstream, not precomputing N2 breaks semcheck + array[N2, SecretWord] # TODO upstream, using Limbs[N2] breaks semcheck + +# ############################################################ +# +# Curve properties +# +# ############################################################ + +macro getCurveOrder*(C: static Curve): untyped = + ## Get the curve order `r` + ## i.e. the number of points on the elliptic curve + result = bindSym($C & "_Order") + +macro getCurveOrderBitwidth*(C: static Curve): untyped = + ## Get the curve order `r` + ## i.e. the number of points on the elliptic curve + result = nnkDotExpr.newTree( + getAST(getCurveOrder(C)), + ident"bits" + ) + +template matchingOrderBigInt*(C: static Curve): untyped = + # Workaround: https://github.com/nim-lang/Nim/issues/16774 + BigInt[CurveOrderBitWidth[C]] + +macro getEquationForm*(C: static Curve): untyped = + ## Returns the equation form + ## (ShortWeierstrass, Montgomery, Twisted Edwards, Weierstrass, ...) + result = bindSym($C & "_equation_form") + +macro getCoefA*(C: static Curve): untyped = + ## Returns the A coefficient of the curve + ## The return type is polymorphic, it can be an int + ## or a bigInt depending on the curve + result = bindSym($C & "_coef_A") + +macro getCoefB*(C: static Curve): untyped = + ## Returns the B coefficient of the curve + ## The return type is polymorphic, it can be an int + ## or a bigInt depending on the curve + result = bindSym($C & "_coef_B") + +macro get_QNR_Fp*(C: static Curve): untyped = + ## Returns the tower extension quadratic non-residue in 𝔽p + ## i.e. a number that is not a square in 𝔽p + result = bindSym($C & "_nonresidue_quad_fp") + +macro get_CNR_Fp2*(C: static Curve): untyped = + ## Returns the tower extension cubic non-residue 𝔽p² + ## i.e. a number that is not a cube in 𝔽p² + ## + ## The return value is a tuple (a, b) + ## that corresponds to the number a + b𝑗 + ## with 𝑗 choosen for 𝑗² - QNR_Fp == 0 + ## i.e. if -1 is chosen as a quadratic non-residue 𝑗 = √-1 + ## if -2 is chosen as a quadratic non-residue 𝑗 = √-2 + result = bindSym($C & "_nonresidue_cube_fp2") + +macro getEmbeddingDegree*(C: static Curve): untyped = + ## Returns the prime embedding degree, + ## i.e. the smallest k such that r|𝑝^𝑘−1 + ## equivalently 𝑝^𝑘 ≡ 1 (mod r) + ## with r the curve order and p its field modulus + result = bindSym($C & "_embedding_degree") + +macro getSexticTwist*(C: static Curve): untyped = + ## Returns if D-Twist or M-Twist + result = bindSym($C & "_sexticTwist") + +macro get_SNR_Fp2*(C: static Curve): untyped = + ## Returns the sextic non-residue in 𝔽p² + ## choosen to build the twisted curve E'(𝔽p²) + ## i.e. a number µ so that x⁶ - µ is irreducible + result = bindSym($C & "_sexticNonResidue_fp2") diff --git a/constantine/config/curves_prop_derived.nim b/constantine/config/curves_prop_derived.nim new file mode 100644 index 0000000..2439f4e --- /dev/null +++ b/constantine/config/curves_prop_derived.nim @@ -0,0 +1,139 @@ +# 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/macros, + # Internal + ./type_bigint, ./type_ff, ./common, + ./curves_declaration, ./curves_derived + +# ############################################################ +# +# Access precomputed derived constants in ROM +# +# ############################################################ +{.experimental: "dynamicBindSym".} + +genDerivedConstants(kModulus) +genDerivedConstants(kOrder) + +proc bindConstant(ff: NimNode, property: string): NimNode = + # Need to workaround https://github.com/nim-lang/Nim/issues/14021 + # which prevents checking if a type FF[C] = Fp[C] or Fr[C] + # was instantiated with Fp or Fr. + # getTypeInst only returns FF and sameType doesn't work. + # so quote do + when checks. + let T = getTypeInst(ff) + T.expectKind(nnkBracketExpr) + doAssert T[0].eqIdent("typedesc") + + if T[1].kind == nnkBracketExpr: # typedesc[Fp[BLS12_381]] + # doAssert T[1][0].eqIdent"Fp" or T[1][0].eqIdent"Fr", "Found ident: '" & $T[1][0] & "' instead of 'Fp' or 'Fr'" + + T[1][1].expectKind(nnkIntLit) # static enum are ints in the VM + + let curve = $Curve(T[1][1].intVal) + let curve_fp = bindSym(curve & "_Fp_" & property) + let curve_fr = bindSym(curve & "_Fr_" & property) + result = quote do: + when `ff` is Fp: + `curve_fp` + elif `ff` is Fr: + `curve_fr` + else: + {.error: "Unreachable, received type: " & $`ff`.} + + else: + echo T.repr() + echo getTypeInst(T[1]).treerepr + error "getTypeInst didn't return the full instantiation." & + " Dealing with types in macros is hard, complain at https://github.com/nim-lang/RFCs/issues/44" + +template fieldMod*(Field: type FF): auto = + when Field is Fp: + Field.C.Mod + else: + Field.C.getCurveOrder() + +macro canUseNoCarryMontyMul*(ff: type FF): untyped = + ## Returns true if the Modulus is compatible with a fast + ## Montgomery multiplication that avoids many carries + result = bindConstant(ff, "CanUseNoCarryMontyMul") + +macro canUseNoCarryMontySquare*(ff: type FF): untyped = + ## Returns true if the Modulus is compatible with a fast + ## Montgomery squaring that avoids many carries + result = bindConstant(ff, "CanUseNoCarryMontySquare") + +macro getR2modP*(ff: type FF): untyped = + ## Get the Montgomery "R^2 mod P" constant associated to a curve field modulus + result = bindConstant(ff, "R2modP") + +macro getNegInvModWord*(ff: type FF): untyped = + ## Get the Montgomery "-1/P[0] mod 2^Wordbitwidth" constant associated to a curve field modulus + result = bindConstant(ff, "NegInvModWord") + +macro getMontyOne*(ff: type FF): untyped = + ## Get one in Montgomery representation (i.e. R mod P) + result = bindConstant(ff, "MontyOne") + +macro getMontyPrimeMinus1*(ff: type FF): untyped = + ## Get (P+1) / 2 for an odd prime + result = bindConstant(ff, "MontyPrimeMinus1") + +macro getInvModExponent*(ff: type FF): untyped = + ## Get modular inversion exponent (Modulus-2 in canonical representation) + result = bindConstant(ff, "InvModExponent") + +macro getPrimePlus1div2*(ff: type FF): untyped = + ## Get (P+1) / 2 for an odd prime + ## Warning ⚠️: Result in canonical domain (not Montgomery) + result = bindConstant(ff, "PrimePlus1div2") + +macro getPrimeMinus1div2_BE*(ff: type FF): untyped = + ## Get (P-1) / 2 in big-endian serialized format + result = bindConstant(ff, "PrimeMinus1div2_BE") + +macro getPrimeMinus3div4_BE*(ff: type FF): untyped = + ## Get (P-3) / 2 in big-endian serialized format + result = bindConstant(ff, "PrimeMinus3div4_BE") + +macro getPrimePlus1div4_BE*(ff: type FF): untyped = + ## Get (P+1) / 4 for an odd prime in big-endian serialized format + result = bindConstant(ff, "PrimePlus1div4_BE") + +# ############################################################ +# +# Debug info printed at compile-time +# +# ############################################################ + +macro debugConsts(): untyped {.used.} = + let curves = bindSym("Curve") + let E = curves.getImpl[2] + + result = newStmtList() + for i in 1 ..< E.len: + let curve = E[i] + let curveName = $curve + let modulus = bindSym(curveName & "_Fp_Modulus") + let r2modp = bindSym(curveName & "_Fp_R2modP") + let negInvModWord = bindSym(curveName & "_Fp_NegInvModWord") + + result.add quote do: + echo "Curve ", `curveName`,':' + echo " Field Modulus: ", `modulus` + echo " Montgomery R² (mod P): ", `r2modp` + echo " Montgomery -1/P[0] (mod 2^", WordBitWidth, "): ", `negInvModWord` + + result.add quote do: + echo "----------------------------------------------------------------------------" + +# debug: # displayed with -d:debugConstantine +# debugConsts() diff --git a/constantine/config/type_fp.nim b/constantine/config/type_ff.nim similarity index 63% rename from constantine/config/type_fp.nim rename to constantine/config/type_ff.nim index a206021..26f7b87 100644 --- a/constantine/config/type_fp.nim +++ b/constantine/config/type_ff.nim @@ -12,12 +12,21 @@ import type Fp*[C: static Curve] = object - ## All operations on a field are modulo P + ## All operations on a Fp field are modulo P ## P being the prime modulus of the Curve C ## Internally, data is stored in Montgomery n-residue form ## with the magic constant chosen for convenient division (a power of 2 depending on P bitsize) mres*: matchingBigInt(C) + Fr*[C: static Curve] = object + ## All operations on a field are modulo `r` + ## `r` being the prime curve order or subgroup order + ## Internally, data is stored in Montgomery n-residue form + ## with the magic constant chosen for convenient division (a power of 2 depending on P bitsize) + mres*: matchingOrderBigInt(C) + + FF*[C: static Curve] = Fp[C] or Fr[C] + debug: import ./type_bigint @@ -26,3 +35,9 @@ debug: result.add "](" result.add $a.mres result.add ')' + + func `$`*[C: static Curve](a: Fr[C]): string = + result = "Fr[" & $C + result.add "](" + result.add $a.mres + result.add ')' diff --git a/constantine/curves/bls12_377_glv.nim b/constantine/curves/bls12_377_glv.nim index d78c11b..0f55f69 100644 --- a/constantine/curves/bls12_377_glv.nim +++ b/constantine/curves/bls12_377_glv.nim @@ -7,7 +7,7 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - ../config/[curves, type_bigint, type_fp], + ../config/[curves, type_bigint, type_ff], ../io/[io_bigints, io_fields] # BLS12_377 G1 diff --git a/constantine/curves/bls12_377_sqrt.nim b/constantine/curves/bls12_377_sqrt.nim index 8f74af7..f374990 100644 --- a/constantine/curves/bls12_377_sqrt.nim +++ b/constantine/curves/bls12_377_sqrt.nim @@ -7,7 +7,7 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - ../config/[curves, type_bigint, type_fp], + ../config/[curves, type_bigint, type_ff], ../io/[io_bigints, io_fields] const diff --git a/constantine/curves/bls12_381_glv.nim b/constantine/curves/bls12_381_glv.nim index 8c1a0a1..9085d95 100644 --- a/constantine/curves/bls12_381_glv.nim +++ b/constantine/curves/bls12_381_glv.nim @@ -7,7 +7,7 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - ../config/[curves, type_bigint, type_fp], + ../config/[curves, type_bigint, type_ff], ../io/[io_bigints, io_fields] # BLS12_381 G1 diff --git a/constantine/curves/bn254_nogami_glv.nim b/constantine/curves/bn254_nogami_glv.nim index 83ecf4b..302fa63 100644 --- a/constantine/curves/bn254_nogami_glv.nim +++ b/constantine/curves/bn254_nogami_glv.nim @@ -7,7 +7,7 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - ../config/[curves, type_bigint, type_fp], + ../config/[curves, type_bigint, type_ff], ../io/[io_bigints, io_fields] # BN254_Nogami G1 diff --git a/constantine/curves/bn254_snarks_glv.nim b/constantine/curves/bn254_snarks_glv.nim index 32f300b..5955d85 100644 --- a/constantine/curves/bn254_snarks_glv.nim +++ b/constantine/curves/bn254_snarks_glv.nim @@ -7,7 +7,7 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - ../config/[curves, type_bigint, type_fp], + ../config/[curves, type_bigint, type_ff], ../io/[io_bigints, io_fields] # BN254_Snarks G1 diff --git a/constantine/curves/bw6_761_frobenius.nim b/constantine/curves/bw6_761_frobenius.nim index b634e01..19b7cc0 100644 --- a/constantine/curves/bw6_761_frobenius.nim +++ b/constantine/curves/bw6_761_frobenius.nim @@ -7,7 +7,7 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - ../config/[curves, type_fp], + ../config/[curves, type_ff], ../towers, ../io/[io_fields, io_towers] diff --git a/constantine/curves/bw6_761_glv.nim b/constantine/curves/bw6_761_glv.nim index 7daa708..344c2c7 100644 --- a/constantine/curves/bw6_761_glv.nim +++ b/constantine/curves/bw6_761_glv.nim @@ -7,7 +7,7 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - ../config/[curves, type_bigint, type_fp], + ../config/[curves, type_bigint, type_ff], ../io/[io_bigints, io_fields] # BW6_761 G1 diff --git a/constantine/curves/zoo_glv.nim b/constantine/curves/zoo_glv.nim index a69b6eb..daf1857 100644 --- a/constantine/curves/zoo_glv.nim +++ b/constantine/curves/zoo_glv.nim @@ -8,7 +8,7 @@ import std/macros, - ../config/[curves, type_fp], + ../config/[curves, type_ff], ../towers, ./bls12_377_glv, ./bls12_381_glv, diff --git a/constantine/io/io_fields.nim b/constantine/io/io_fields.nim index 81e96c1..3e2cbbd 100644 --- a/constantine/io/io_fields.nim +++ b/constantine/io/io_fields.nim @@ -22,18 +22,18 @@ import # # ############################################################ -func fromUint*(dst: var Fp, +func fromUint*(dst: var FF, src: SomeUnsignedInt) = ## Parse a regular unsigned integer - ## and store it into a Fp - let raw {.noinit.} = (type dst.mres).fromRawUint(cast[array[sizeof(src), byte]](src), cpuEndian) + ## and store it into a Fp or Fr + let raw {.noinit.} = (typeof dst.mres).fromRawUint(cast[array[sizeof(src), byte]](src), cpuEndian) dst.fromBig(raw) -func fromInt*(dst: var Fp, +func fromInt*(dst: var FF, src: SomeInteger) = ## Parse a regular signed integer - ## and store it into a Fp - ## A negative integer will be instantiated as a negated number (mod p) + ## and store it into a Fp or Fr + ## A negative integer will be instantiated as a negated number (mod p) or (mod r) when src is SomeUnsignedInt: dst.fromUint(src) else: @@ -45,7 +45,7 @@ func fromInt*(dst: var Fp, dst.fromBig(raw) func exportRawUint*(dst: var openarray[byte], - src: Fp, + src: FF, dstEndianness: static Endianness) = ## Serialize a finite field element to its canonical big-endian or little-endian ## representation @@ -58,7 +58,7 @@ 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) = +func appendHex*(dst: var string, f: FF, order: static Endianness = bigEndian) = ## Stringify a finite field element to hex. ## Note. Leading zeros are not removed. ## Result is prefixed with 0x @@ -69,7 +69,7 @@ func appendHex*(dst: var string, f: Fp, order: static Endianness = bigEndian) = ## - no leaks dst.appendHex(f.toBig(), order) -func toHex*(f: Fp, order: static Endianness = bigEndian): string = +func toHex*(f: FF, order: static Endianness = bigEndian): string = ## Stringify a finite field element to hex. ## Note. Leading zeros are not removed. ## Result is prefixed with 0x @@ -80,11 +80,11 @@ func toHex*(f: Fp, order: static Endianness = bigEndian): string = ## - no leaks result.appendHex(f, order) -func fromHex*(dst: var Fp, hexString: string) {.raises: [ValueError].}= - ## Convert a hex string to a element of Fp +func fromHex*(dst: var FF, hexString: string) {.raises: [ValueError].}= + ## Convert a hex string to a element of Fp or Fr let raw {.noinit.} = fromHex(dst.mres.typeof, hexString) dst.fromBig(raw) -func fromHex*(T: type Fp, hexString: string): T {.noInit, raises: [ValueError].}= +func fromHex*(T: type FF, hexString: string): T {.noInit, raises: [ValueError].}= ## Convert a hex string to a element of Fp result.fromHex(hexString) diff --git a/constantine/pairing/pairing_bls12.nim b/constantine/pairing/pairing_bls12.nim index d09a293..a5f081c 100644 --- a/constantine/pairing/pairing_bls12.nim +++ b/constantine/pairing/pairing_bls12.nim @@ -7,7 +7,7 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - ../config/[curves, type_fp], + ../config/[curves, type_ff], ../towers, ../elliptic/[ ec_shortweierstrass_affine, diff --git a/constantine/pairing/pairing_bn.nim b/constantine/pairing/pairing_bn.nim index 1c5a288..cb03959 100644 --- a/constantine/pairing/pairing_bn.nim +++ b/constantine/pairing/pairing_bn.nim @@ -7,7 +7,7 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - ../config/[curves, type_fp], + ../config/[curves, type_ff], ../towers, ../elliptic/[ ec_shortweierstrass_affine, diff --git a/constantine/tower_field_extensions/quadratic_extensions.nim b/constantine/tower_field_extensions/quadratic_extensions.nim index b8957b5..8b70efe 100644 --- a/constantine/tower_field_extensions/quadratic_extensions.nim +++ b/constantine/tower_field_extensions/quadratic_extensions.nim @@ -105,7 +105,7 @@ func prod_complex(r: var QuadraticExt, a, b: QuadraticExt) = # Deactivated for now Clang 360 cycles on i9-9980XE @4.1 GHz var a0b0 {.noInit.}, a1b1 {.noInit.}: doubleWidth(typeof(r.c0)) var d {.noInit.}: doubleWidth(typeof(r.c0)) - const msbSet = r.c0.typeof.C.canUseNoCarryMontyMul() + const msbSet = r.c0.typeof.canUseNoCarryMontyMul() a0b0.mulNoReduce(a.c0, b.c0) # 44 cycles - cumul 44 a1b1.mulNoReduce(a.c1, b.c1) # 44 cycles - cumul 88 diff --git a/helpers/prng_unsafe.nim b/helpers/prng_unsafe.nim index 9225444..ebaaeb1 100644 --- a/helpers/prng_unsafe.nim +++ b/helpers/prng_unsafe.nim @@ -9,12 +9,13 @@ import ../constantine/arithmetic/bigints, ../constantine/primitives, - ../constantine/config/[common, curves], + ../constantine/config/[common, curves, type_ff], ../constantine/elliptic/[ ec_shortweierstrass_affine, ec_shortweierstrass_projective, ec_shortweierstrass_jacobian], - ../constantine/io/io_bigints + ../constantine/io/io_bigints, + ../constantine/tower_field_extensions/tower_common # ############################################################ # @@ -137,20 +138,21 @@ func random_unsafe(rng: var RngState, a: var BigInt) = for i in 0 ..< a.limbs.len: a.limbs[i] = SecretWord(rng.next()) -func random_unsafe[T](rng: var RngState, a: var T, C: static Curve) = - ## Recursively initialize a BigInt (part of a field) or Field element +func random_unsafe(rng: var RngState, a: var FF) = + ## Initialize a Field element ## Unsafe: for testing and benchmarking purposes only - when T is BigInt: - var reduced, unreduced{.noInit.}: T - rng.random_unsafe(unreduced) + var reduced, unreduced{.noInit.}: typeof(a.mres) + rng.random_unsafe(unreduced) - # Note: a simple modulo will be biaised but it's simple and "fast" - reduced.reduce(unreduced, C.Mod) - a.montyResidue(reduced, C.Mod, C.getR2modP(), C.getNegInvModWord(), C.canUseNoCarryMontyMul()) + # Note: a simple modulo will be biaised but it's simple and "fast" + reduced.reduce(unreduced, FF.fieldMod()) + a.mres.montyResidue(reduced, FF.fieldMod(), FF.getR2modP(), FF.getNegInvModWord(), FF.canUseNoCarryMontyMul()) - else: - for field in fields(a): - rng.random_unsafe(field, C) +func random_unsafe(rng: var RngState, a: var ExtensionField) = + ## Recursively initialize an extension Field element + ## Unsafe: for testing and benchmarking purposes only + for field in fields(a): + rng.random_unsafe(field) func random_word_highHammingWeight(rng: var RngState): BaseType = let numZeros = rng.random_unsafe(WordBitWidth div 3) # Average Hamming Weight is 1-0.33/2 = 0.83 @@ -165,22 +167,23 @@ func random_highHammingWeight(rng: var RngState, a: var BigInt) = for i in 0 ..< a.limbs.len: a.limbs[i] = SecretWord rng.random_word_highHammingWeight() -func random_highHammingWeight[T](rng: var RngState, a: var T, C: static Curve) = +func random_highHammingWeight(rng: var RngState, a: var FF) = ## Recursively initialize a BigInt (part of a field) or Field element ## Unsafe: for testing and benchmarking purposes only ## The result will have a high Hamming Weight ## to have a higher probability of triggering carries - when T is BigInt: - var reduced, unreduced{.noInit.}: T - rng.random_highHammingWeight(unreduced) + var reduced, unreduced{.noInit.}: typeof(a.mres) + rng.random_highHammingWeight(unreduced) - # Note: a simple modulo will be biaised but it's simple and "fast" - reduced.reduce(unreduced, C.Mod) - a.montyResidue(reduced, C.Mod, C.getR2modP(), C.getNegInvModWord(), C.canUseNoCarryMontyMul()) + # Note: a simple modulo will be biaised but it's simple and "fast" + reduced.reduce(unreduced, FF.fieldMod()) + a.mres.montyResidue(reduced, FF.fieldMod(), FF.getR2modP(), FF.getNegInvModWord(), FF.canUseNoCarryMontyMul()) - else: - for field in fields(a): - rng.random_highHammingWeight(field, C) +func random_highHammingWeight(rng: var RngState, a: var ExtensionField) = + ## Recursively initialize an extension Field element + ## Unsafe: for testing and benchmarking purposes only + for field in fields(a): + rng.random_highHammingWeight(field) func random_long01Seq(rng: var RngState, a: var openArray[byte]) = ## Initialize a bytearray @@ -210,21 +213,22 @@ func random_long01Seq(rng: var RngState, a: var BigInt) = else: a.fromRawUint(buf, littleEndian) -func random_long01Seq[T](rng: var RngState, a: var T, C: static Curve) = +func random_long01Seq(rng: var RngState, a: var FF) = ## Recursively initialize a BigInt (part of a field) or Field element ## It is skewed towards producing strings of 1111... and 0000 ## to trigger edge cases - when T is BigInt: - var reduced, unreduced{.noInit.}: T - rng.random_long01Seq(unreduced) + var reduced, unreduced{.noInit.}: typeof(a.mres) + rng.random_long01Seq(unreduced) - # Note: a simple modulo will be biaised but it's simple and "fast" - reduced.reduce(unreduced, C.Mod) - a.montyResidue(reduced, C.Mod, C.getR2modP(), C.getNegInvModWord(), C.canUseNoCarryMontyMul()) + # Note: a simple modulo will be biaised but it's simple and "fast" + reduced.reduce(unreduced, FF.fieldMod()) + a.mres.montyResidue(reduced, FF.fieldMod(), FF.getR2modP(), FF.getNegInvModWord(), FF.canUseNoCarryMontyMul()) - else: - for field in fields(a): - rng.random_highHammingWeight(field, C) +func random_long01Seq(rng: var RngState, a: var ExtensionField) = + ## Recursively initialize an extension Field element + ## Unsafe: for testing and benchmarking purposes only + for field in fields(a): + rng.random_long01Seq(field) # Elliptic curves # ------------------------------------------------------------ @@ -238,20 +242,20 @@ func random_unsafe(rng: var RngState, a: var (ECP_ShortW_Proj or ECP_ShortW_Aff while not bool(success): # Euler's criterion: there are (p-1)/2 squares in a field with modulus `p` # so we have a probability of ~0.5 to get a good point - rng.random_unsafe(fieldElem, a.F.C) + rng.random_unsafe(fieldElem) success = trySetFromCoordX(a, fieldElem) func random_unsafe_with_randZ(rng: var RngState, a: var (ECP_ShortW_Proj or ECP_ShortW_Jac)) = ## Initialize a random curve point with Z coordinate being random ## Unsafe: for testing and benchmarking purposes only var Z{.noInit.}: a.F - rng.random_unsafe(Z, a.F.C) # If Z is zero, X will be zero and that will be an infinity point + rng.random_unsafe(Z) # If Z is zero, X will be zero and that will be an infinity point var fieldElem {.noInit.}: a.F var success = CtFalse while not bool(success): - rng.random_unsafe(fieldElem, a.F.C) + rng.random_unsafe(fieldElem) success = trySetFromCoordsXandZ(a, fieldElem, Z) func random_highHammingWeight(rng: var RngState, a: var (ECP_ShortW_Proj or ECP_ShortW_Aff or ECP_ShortW_Jac)) = @@ -264,7 +268,7 @@ func random_highHammingWeight(rng: var RngState, a: var (ECP_ShortW_Proj or ECP_ while not bool(success): # Euler's criterion: there are (p-1)/2 squares in a field with modulus `p` # so we have a probability of ~0.5 to get a good point - rng.random_highHammingWeight(fieldElem, a.F.C) + rng.random_highHammingWeight(fieldElem) success = trySetFromCoordX(a, fieldElem) func random_highHammingWeight_with_randZ(rng: var RngState, a: var (ECP_ShortW_Proj or ECP_ShortW_Jac)) = @@ -272,13 +276,13 @@ func random_highHammingWeight_with_randZ(rng: var RngState, a: var (ECP_ShortW_P ## This will be generated with a biaised RNG with high Hamming Weight ## to trigger carry bugs var Z{.noInit.}: a.F - rng.random_highHammingWeight(Z, a.F.C) # If Z is zero, X will be zero and that will be an infinity point + rng.random_highHammingWeight(Z) # If Z is zero, X will be zero and that will be an infinity point var fieldElem {.noInit.}: a.F var success = CtFalse while not bool(success): - rng.random_highHammingWeight(fieldElem, a.F.C) + rng.random_highHammingWeight(fieldElem) success = trySetFromCoordsXandZ(a, fieldElem, Z) func random_long01Seq(rng: var RngState, a: var (ECP_ShortW_Proj or ECP_ShortW_Aff or ECP_ShortW_Jac)) = @@ -292,7 +296,7 @@ func random_long01Seq(rng: var RngState, a: var (ECP_ShortW_Proj or ECP_ShortW_A while not bool(success): # Euler's criterion: there are (p-1)/2 squares in a field with modulus `p` # so we have a probability of ~0.5 to get a good point - rng.random_long01Seq(fieldElem, a.F.C) + rng.random_long01Seq(fieldElem) success = trySetFromCoordX(a, fieldElem) func random_long01Seq_with_randZ(rng: var RngState, a: var (ECP_ShortW_Proj or ECP_ShortW_Jac)) = @@ -301,13 +305,13 @@ func random_long01Seq_with_randZ(rng: var RngState, a: var (ECP_ShortW_Proj or E ## that produces long bitstrings of 0 and 1 ## to trigger edge cases var Z{.noInit.}: a.F - rng.random_long01Seq(Z, a.F.C) # If Z is zero, X will be zero and that will be an infinity point + rng.random_long01Seq(Z) # If Z is zero, X will be zero and that will be an infinity point var fieldElem {.noInit.}: a.F var success = CtFalse while not bool(success): - rng.random_long01Seq(fieldElem, a.F.C) + rng.random_long01Seq(fieldElem) success = trySetFromCoordsXandZ(a, fieldElem, Z) # Generic over any Constantine type @@ -323,7 +327,7 @@ func random_unsafe*(rng: var RngState, T: typedesc): T = elif T is BigInt: rng.random_unsafe(result) else: # Fields - rng.random_unsafe(result, T.C) + rng.random_unsafe(result) func random_unsafe_with_randZ*(rng: var RngState, T: typedesc[ECP_ShortW_Proj or ECP_ShortW_Jac]): T = ## Create a random curve element with a random Z coordinate @@ -340,7 +344,7 @@ func random_highHammingWeight*(rng: var RngState, T: typedesc): T = elif T is BigInt: rng.random_highHammingWeight(result) else: # Fields - rng.random_highHammingWeight(result, T.C) + rng.random_highHammingWeight(result) func random_highHammingWeight_with_randZ*(rng: var RngState, T: typedesc[ECP_ShortW_Proj or ECP_ShortW_Jac]): T = ## Create a random curve element with a random Z coordinate @@ -357,7 +361,7 @@ func random_long01Seq*(rng: var RngState, T: typedesc): T = elif T is BigInt: rng.random_long01Seq(result) else: # Fields - rng.random_long01Seq(result, T.C) + rng.random_long01Seq(result) func random_long01Seq_with_randZ*(rng: var RngState, T: typedesc[ECP_ShortW_Proj or ECP_ShortW_Jac]): T = ## Create a random curve element with a random Z coordinate diff --git a/sage/derive_endomorphisms.sage b/sage/derive_endomorphisms.sage index 32f90de..0bea450 100644 --- a/sage/derive_endomorphisms.sage +++ b/sage/derive_endomorphisms.sage @@ -266,7 +266,7 @@ if __name__ == "__main__": f.write('\n\n') f.write(inspect.cleandoc(f""" import - ../config/[curves, type_bigint, type_fp], + ../config/[curves, type_bigint, type_ff], ../io/[io_bigints, io_fields] # {curve} G1 diff --git a/sage/derive_frobenius.sage b/sage/derive_frobenius.sage index b5a5630..9ad614f 100644 --- a/sage/derive_frobenius.sage +++ b/sage/derive_frobenius.sage @@ -285,7 +285,7 @@ if __name__ == "__main__": else: f.write(inspect.cleandoc(""" import - ../config/[curves, type_fp], + ../config/[curves, type_ff], ../towers, ../io/[io_fields, io_towers] """)) diff --git a/tests/t_ec_sage_bls12_377.nim b/tests/t_ec_sage_bls12_377.nim index 37ff5cd..4b29493 100644 --- a/tests/t_ec_sage_bls12_377.nim +++ b/tests/t_ec_sage_bls12_377.nim @@ -8,7 +8,7 @@ import # Internals - ../constantine/config/[type_fp, curves], + ../constantine/config/[type_ff, curves], ../constantine/towers, ../constantine/elliptic/ec_shortweierstrass_jacobian, ../constantine/elliptic/ec_shortweierstrass_projective, diff --git a/tests/t_ec_sage_bls12_381.nim b/tests/t_ec_sage_bls12_381.nim index ce8b2c5..78b7fab 100644 --- a/tests/t_ec_sage_bls12_381.nim +++ b/tests/t_ec_sage_bls12_381.nim @@ -8,7 +8,7 @@ import # Internals - ../constantine/config/[type_fp, curves], + ../constantine/config/[type_ff, curves], ../constantine/towers, ../constantine/elliptic/ec_shortweierstrass_jacobian, ../constantine/elliptic/ec_shortweierstrass_projective, diff --git a/tests/t_ec_sage_bn254_nogami.nim b/tests/t_ec_sage_bn254_nogami.nim index 8023e85..88a3e30 100644 --- a/tests/t_ec_sage_bn254_nogami.nim +++ b/tests/t_ec_sage_bn254_nogami.nim @@ -8,7 +8,7 @@ import # Internals - ../constantine/config/[type_fp, curves], + ../constantine/config/[type_ff, curves], ../constantine/towers, ../constantine/elliptic/ec_shortweierstrass_jacobian, ../constantine/elliptic/ec_shortweierstrass_projective, diff --git a/tests/t_ec_sage_bn254_snarks.nim b/tests/t_ec_sage_bn254_snarks.nim index 5c5be27..ce3fc89 100644 --- a/tests/t_ec_sage_bn254_snarks.nim +++ b/tests/t_ec_sage_bn254_snarks.nim @@ -8,7 +8,7 @@ import # Internals - ../constantine/config/[type_fp, curves], + ../constantine/config/[type_ff, curves], ../constantine/towers, ../constantine/elliptic/ec_shortweierstrass_jacobian, ../constantine/elliptic/ec_shortweierstrass_projective, diff --git a/tests/t_ec_sage_bw6_761_g1.nim b/tests/t_ec_sage_bw6_761_g1.nim index b69e109..afc1550 100644 --- a/tests/t_ec_sage_bw6_761_g1.nim +++ b/tests/t_ec_sage_bw6_761_g1.nim @@ -8,7 +8,7 @@ import # Internals - ../constantine/config/[type_fp, curves], + ../constantine/config/[type_ff, curves], ../constantine/elliptic/ec_shortweierstrass_jacobian, ../constantine/elliptic/ec_shortweierstrass_projective, # Test utilities diff --git a/tests/t_ec_sage_bw6_761_g2.nim b/tests/t_ec_sage_bw6_761_g2.nim index c3c2269..4cf2962 100644 --- a/tests/t_ec_sage_bw6_761_g2.nim +++ b/tests/t_ec_sage_bw6_761_g2.nim @@ -8,7 +8,7 @@ import # Internals - ../constantine/config/[type_fp, curves], + ../constantine/config/[type_ff, curves], ../constantine/elliptic/ec_shortweierstrass_jacobian, ../constantine/elliptic/ec_shortweierstrass_projective, # Test utilities diff --git a/tests/t_ec_sage_template.nim b/tests/t_ec_sage_template.nim index 7132b9e..9611b6b 100644 --- a/tests/t_ec_sage_template.nim +++ b/tests/t_ec_sage_template.nim @@ -12,7 +12,7 @@ import # 3rd party serialization, json_serialization, # Internals - ../constantine/config/[common, curves, type_bigint, type_fp], + ../constantine/config/[common, curves, type_bigint, type_ff], ../constantine/towers, ../constantine/io/[io_bigints, io_ec], ../constantine/elliptic/[ diff --git a/tests/t_ec_shortw_jac_g1_add_double.nim b/tests/t_ec_shortw_jac_g1_add_double.nim index 979fa99..469f63f 100644 --- a/tests/t_ec_shortw_jac_g1_add_double.nim +++ b/tests/t_ec_shortw_jac_g1_add_double.nim @@ -8,7 +8,7 @@ import # Internals - ../constantine/config/[type_fp, curves], + ../constantine/config/[type_ff, curves], ../constantine/elliptic/ec_shortweierstrass_jacobian, # Test utilities ./t_ec_template diff --git a/tests/t_ec_shortw_jac_g1_mul_distri.nim b/tests/t_ec_shortw_jac_g1_mul_distri.nim index 5311b6a..b0c1144 100644 --- a/tests/t_ec_shortw_jac_g1_mul_distri.nim +++ b/tests/t_ec_shortw_jac_g1_mul_distri.nim @@ -8,7 +8,7 @@ import # Internals - ../constantine/config/[type_fp, curves], + ../constantine/config/[type_ff, curves], ../constantine/elliptic/ec_shortweierstrass_jacobian, # Test utilities ./t_ec_template diff --git a/tests/t_ec_shortw_jac_g1_mul_vs_ref.nim b/tests/t_ec_shortw_jac_g1_mul_vs_ref.nim index e0cf62d..d06ae60 100644 --- a/tests/t_ec_shortw_jac_g1_mul_vs_ref.nim +++ b/tests/t_ec_shortw_jac_g1_mul_vs_ref.nim @@ -8,7 +8,7 @@ import # Internals - ../constantine/config/[type_fp, curves], + ../constantine/config/[type_ff, curves], ../constantine/elliptic/ec_shortweierstrass_jacobian, # Test utilities ./t_ec_template diff --git a/tests/t_ec_shortw_jac_g2_add_double_bw6_761.nim b/tests/t_ec_shortw_jac_g2_add_double_bw6_761.nim index 3697ea3..e1eb379 100644 --- a/tests/t_ec_shortw_jac_g2_add_double_bw6_761.nim +++ b/tests/t_ec_shortw_jac_g2_add_double_bw6_761.nim @@ -8,7 +8,7 @@ import # Internals - ../constantine/config/[type_fp, curves], + ../constantine/config/[type_ff, curves], ../constantine/elliptic/ec_shortweierstrass_jacobian, # Test utilities ./t_ec_template diff --git a/tests/t_ec_shortw_jac_g2_mixed_add_bw6_761.nim b/tests/t_ec_shortw_jac_g2_mixed_add_bw6_761.nim index ac9c09c..83050e8 100644 --- a/tests/t_ec_shortw_jac_g2_mixed_add_bw6_761.nim +++ b/tests/t_ec_shortw_jac_g2_mixed_add_bw6_761.nim @@ -8,7 +8,7 @@ import # Internals - ../constantine/config/[type_fp, curves], + ../constantine/config/[type_ff, curves], ../constantine/elliptic/ec_shortweierstrass_jacobian, # Test utilities ./t_ec_template diff --git a/tests/t_ec_shortw_jac_g2_mul_distri_bw6_761.nim b/tests/t_ec_shortw_jac_g2_mul_distri_bw6_761.nim index 0f4f606..dd53fb0 100644 --- a/tests/t_ec_shortw_jac_g2_mul_distri_bw6_761.nim +++ b/tests/t_ec_shortw_jac_g2_mul_distri_bw6_761.nim @@ -8,7 +8,7 @@ import # Internals - ../constantine/config/[type_fp, curves], + ../constantine/config/[type_ff, curves], ../constantine/elliptic/ec_shortweierstrass_jacobian, # Test utilities ./t_ec_template diff --git a/tests/t_ec_shortw_jac_g2_mul_sanity_bw6_761.nim b/tests/t_ec_shortw_jac_g2_mul_sanity_bw6_761.nim index 6e2b287..6fb2e3a 100644 --- a/tests/t_ec_shortw_jac_g2_mul_sanity_bw6_761.nim +++ b/tests/t_ec_shortw_jac_g2_mul_sanity_bw6_761.nim @@ -8,7 +8,7 @@ import # Internals - ../constantine/config/[type_fp, curves], + ../constantine/config/[type_ff, curves], ../constantine/elliptic/ec_shortweierstrass_jacobian, # Test utilities ./t_ec_template diff --git a/tests/t_ec_shortw_jac_g2_mul_vs_ref_bw6_761.nim b/tests/t_ec_shortw_jac_g2_mul_vs_ref_bw6_761.nim index baad56a..029f8ea 100644 --- a/tests/t_ec_shortw_jac_g2_mul_vs_ref_bw6_761.nim +++ b/tests/t_ec_shortw_jac_g2_mul_vs_ref_bw6_761.nim @@ -8,7 +8,7 @@ import # Internals - ../constantine/config/[type_fp, curves], + ../constantine/config/[type_ff, curves], ../constantine/elliptic/ec_shortweierstrass_jacobian, # Test utilities ./t_ec_template diff --git a/tests/t_ec_shortw_prj_g1_add_double.nim b/tests/t_ec_shortw_prj_g1_add_double.nim index 7632a79..67ccb89 100644 --- a/tests/t_ec_shortw_prj_g1_add_double.nim +++ b/tests/t_ec_shortw_prj_g1_add_double.nim @@ -8,7 +8,7 @@ import # Internals - ../constantine/config/[type_fp, curves], + ../constantine/config/[type_ff, curves], ../constantine/elliptic/ec_shortweierstrass_projective, # Test utilities ./t_ec_template diff --git a/tests/t_ec_shortw_prj_g1_mul_distri.nim b/tests/t_ec_shortw_prj_g1_mul_distri.nim index f43bdb7..82b01d1 100644 --- a/tests/t_ec_shortw_prj_g1_mul_distri.nim +++ b/tests/t_ec_shortw_prj_g1_mul_distri.nim @@ -8,7 +8,7 @@ import # Internals - ../constantine/config/[type_fp, curves], + ../constantine/config/[type_ff, curves], ../constantine/elliptic/ec_shortweierstrass_projective, # Test utilities ./t_ec_template diff --git a/tests/t_ec_shortw_prj_g1_mul_vs_ref.nim b/tests/t_ec_shortw_prj_g1_mul_vs_ref.nim index 1c03a38..3e50618 100644 --- a/tests/t_ec_shortw_prj_g1_mul_vs_ref.nim +++ b/tests/t_ec_shortw_prj_g1_mul_vs_ref.nim @@ -8,7 +8,7 @@ import # Internals - ../constantine/config/[type_fp, curves], + ../constantine/config/[type_ff, curves], ../constantine/elliptic/ec_shortweierstrass_projective, # Test utilities ./t_ec_template diff --git a/tests/t_ec_shortw_prj_g2_add_double_bw6_761.nim b/tests/t_ec_shortw_prj_g2_add_double_bw6_761.nim index 9597f1f..1745a6b 100644 --- a/tests/t_ec_shortw_prj_g2_add_double_bw6_761.nim +++ b/tests/t_ec_shortw_prj_g2_add_double_bw6_761.nim @@ -8,7 +8,7 @@ import # Internals - ../constantine/config/[type_fp, curves], + ../constantine/config/[type_ff, curves], ../constantine/elliptic/ec_shortweierstrass_projective, # Test utilities ./t_ec_template diff --git a/tests/t_ec_shortw_prj_g2_mixed_add_bw6_761.nim b/tests/t_ec_shortw_prj_g2_mixed_add_bw6_761.nim index e6d32c1..bcd58be 100644 --- a/tests/t_ec_shortw_prj_g2_mixed_add_bw6_761.nim +++ b/tests/t_ec_shortw_prj_g2_mixed_add_bw6_761.nim @@ -8,7 +8,7 @@ import # Internals - ../constantine/config/[type_fp, curves], + ../constantine/config/[type_ff, curves], ../constantine/elliptic/ec_shortweierstrass_projective, # Test utilities ./t_ec_template diff --git a/tests/t_ec_shortw_prj_g2_mul_distri_bw6_761.nim b/tests/t_ec_shortw_prj_g2_mul_distri_bw6_761.nim index bbb210c..0ac2da9 100644 --- a/tests/t_ec_shortw_prj_g2_mul_distri_bw6_761.nim +++ b/tests/t_ec_shortw_prj_g2_mul_distri_bw6_761.nim @@ -8,7 +8,7 @@ import # Internals - ../constantine/config/[type_fp, curves], + ../constantine/config/[type_ff, curves], ../constantine/elliptic/ec_shortweierstrass_projective, # Test utilities ./t_ec_template diff --git a/tests/t_ec_shortw_prj_g2_mul_sanity_bw6_761.nim b/tests/t_ec_shortw_prj_g2_mul_sanity_bw6_761.nim index d9d13a4..0362a10 100644 --- a/tests/t_ec_shortw_prj_g2_mul_sanity_bw6_761.nim +++ b/tests/t_ec_shortw_prj_g2_mul_sanity_bw6_761.nim @@ -8,7 +8,7 @@ import # Internals - ../constantine/config/[type_fp, curves], + ../constantine/config/[type_ff, curves], ../constantine/elliptic/ec_shortweierstrass_projective, # Test utilities ./t_ec_template diff --git a/tests/t_ec_shortw_prj_g2_mul_vs_ref_bw6_761.nim b/tests/t_ec_shortw_prj_g2_mul_vs_ref_bw6_761.nim index 43b952a..bcb0fb6 100644 --- a/tests/t_ec_shortw_prj_g2_mul_vs_ref_bw6_761.nim +++ b/tests/t_ec_shortw_prj_g2_mul_vs_ref_bw6_761.nim @@ -8,7 +8,7 @@ import # Internals - ../constantine/config/[type_fp, curves], + ../constantine/config/[type_ff, curves], ../constantine/elliptic/ec_shortweierstrass_projective, # Test utilities ./t_ec_template diff --git a/tests/t_finite_fields_mulsquare.nim b/tests/t_finite_fields_mulsquare.nim index d5ea038..49cd86b 100644 --- a/tests/t_finite_fields_mulsquare.nim +++ b/tests/t_finite_fields_mulsquare.nim @@ -27,7 +27,7 @@ echo "test_finite_fields_mulsquare xoshiro512** seed: ", seed static: doAssert defined(testingCurves), "This modules requires the -d:testingCurves compile option" proc sanity(C: static Curve) = - test "Squaring 0,1,2 with "& $Curve(C) & " [FastSquaring = " & $C.canUseNoCarryMontySquare & "]": + test "Squaring 0,1,2 with "& $Curve(C) & " [FastSquaring = " & $Fp[C].canUseNoCarryMontySquare & "]": block: # 0² mod var n: Fp[C] @@ -89,7 +89,7 @@ mainSanity() proc mainSelectCases() = suite "Modular Squaring: selected tricky cases" & " [" & $WordBitwidth & "-bit mode]": - test "P-256 [FastSquaring = " & $P256.canUseNoCarryMontySquare & "]": + test "P-256 [FastSquaring = " & $Fp[P256].canUseNoCarryMontySquare & "]": block: # Triggered an issue in the (t[N+1], t[N]) = t[N] + (A1, A0) # between the squaring and reduction step, with t[N+1] and A1 being carry bits. @@ -136,7 +136,7 @@ proc random_long01Seq(C: static Curve) = doAssert bool(r_mul == r_sqr) suite "Random Modular Squaring is consistent with Modular Multiplication" & " [" & $WordBitwidth & "-bit mode]": - test "Random squaring mod P-224 [FastSquaring = " & $P224.canUseNoCarryMontySquare & "]": + test "Random squaring mod P-224 [FastSquaring = " & $Fp[P224].canUseNoCarryMontySquare & "]": for _ in 0 ..< Iters: randomCurve(P224) for _ in 0 ..< Iters: @@ -144,7 +144,7 @@ suite "Random Modular Squaring is consistent with Modular Multiplication" & " [" for _ in 0 ..< Iters: random_long01Seq(P224) - test "Random squaring mod P-256 [FastSquaring = " & $P256.canUseNoCarryMontySquare & "]": + test "Random squaring mod P-256 [FastSquaring = " & $Fp[P256].canUseNoCarryMontySquare & "]": for _ in 0 ..< Iters: randomCurve(P256) for _ in 0 ..< Iters: @@ -152,7 +152,7 @@ suite "Random Modular Squaring is consistent with Modular Multiplication" & " [" for _ in 0 ..< Iters: random_long01Seq(P256) - test "Random squaring mod BLS12_381 [FastSquaring = " & $BLS12_381.canUseNoCarryMontySquare & "]": + test "Random squaring mod BLS12_381 [FastSquaring = " & $Fp[BLS12_381].canUseNoCarryMontySquare & "]": for _ in 0 ..< Iters: randomCurve(BLS12_381) for _ in 0 ..< Iters: diff --git a/tests/t_fr.nim b/tests/t_fr.nim new file mode 100644 index 0000000..d6eb349 --- /dev/null +++ b/tests/t_fr.nim @@ -0,0 +1,129 @@ +# 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], + # Internal + ../constantine/arithmetic, + ../constantine/io/[io_bigints, io_fields], + ../constantine/config/[curves, common, type_bigint], + # Test utilities + ../helpers/prng_unsafe + +const Iters = 24 + +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_fr xoshiro512** seed: ", seed + +proc sanity(C: static Curve) = + test "Fr: Squaring 0,1,2 with "& $Fr[C] & " [FastSquaring = " & $Fr[C].canUseNoCarryMontySquare & "]": + block: # 0² mod + var n: Fr[C] + + n.fromUint(0'u32) + let expected = n + + # Out-of-place + var r: Fr[C] + r.square(n) + # In-place + n.square() + + check: + bool(r == expected) + bool(n == expected) + + block: # 1² mod + var n: Fr[C] + + n.fromUint(1'u32) + let expected = n + + # Out-of-place + var r: Fr[C] + r.square(n) + # In-place + n.square() + + check: + bool(r == expected) + bool(n == expected) + + block: # 2² mod + var n, expected: Fr[C] + + n.fromUint(2'u32) + expected.fromUint(4'u32) + + # Out-of-place + var r: Fr[C] + r.square(n) + # In-place + n.square() + + check: + bool(r == expected) + bool(n == expected) + +proc mainSanity() = + suite "Fr: Modular squaring is consistent with multiplication on special elements" & " [" & $WordBitwidth & "-bit mode]": + sanity BN254_Snarks + sanity BLS12_381 + +mainSanity() + +proc randomCurve(C: static Curve) = + let a = rng.random_unsafe(Fr[C]) + + var r_mul, r_sqr: Fr[C] + + r_mul.prod(a, a) + r_sqr.square(a) + + doAssert bool(r_mul == r_sqr) + +proc randomHighHammingWeight(C: static Curve) = + let a = rng.random_highHammingWeight(Fr[C]) + + var r_mul, r_sqr: Fr[C] + + r_mul.prod(a, a) + r_sqr.square(a) + + doAssert bool(r_mul == r_sqr) + +proc random_long01Seq(C: static Curve) = + let a = rng.random_long01Seq(Fr[C]) + + var r_mul, r_sqr: Fr[C] + + r_mul.prod(a, a) + r_sqr.square(a) + + doAssert bool(r_mul == r_sqr) + +suite "Fr: Random Modular Squaring is consistent with Modular Multiplication" & " [" & $WordBitwidth & "-bit mode]": + test "Random squaring mod r_BN254_Snarks [FastSquaring = " & $Fr[BN254_Snarks].canUseNoCarryMontySquare & "]": + for _ in 0 ..< Iters: + randomCurve(BN254_Snarks) + for _ in 0 ..< Iters: + randomHighHammingWeight(BN254_Snarks) + for _ in 0 ..< Iters: + random_long01Seq(BN254_Snarks) + + test "Random squaring mod r_BLS12_381 [FastSquaring = " & $Fr[BLS12_381].canUseNoCarryMontySquare & "]": + for _ in 0 ..< Iters: + randomCurve(BLS12_381) + for _ in 0 ..< Iters: + randomHighHammingWeight(BLS12_381) + for _ in 0 ..< Iters: + random_long01Seq(BLS12_381)