From 2d5b173a39eca9ecf86506b1a256f79792c40d55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mamy=20Andr=C3=A9-Ratsimbazafy?= Date: Sun, 22 Mar 2020 12:28:53 +0100 Subject: [PATCH] =?UTF-8?q?Less=20magics,=20les=20macros,=20faster=20compi?= =?UTF-8?q?le-times=20(or=20not,=20Fp6=20starts=20to=20get=20really=20slow?= =?UTF-8?q?,=20like=205s)=20+=20some=20cleanups=20in=20curve=20families=20?= =?UTF-8?q?+=20test=20=F0=9D=94=BDp6=20on=2032-bit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- constantine.nimble | 8 +- constantine/arithmetic/finite_fields.nim | 55 +++++----- .../arithmetic/finite_fields_inversion.nim | 2 +- constantine/config/curves.nim | 103 +++--------------- constantine/config/curves_parser.nim | 57 +--------- helpers/prng.nim | 4 +- sage/non_residues.sage | 56 +++++++--- tests/test_finite_fields_vs_gmp.nim | 2 +- tests/test_fp2.nim | 6 +- 9 files changed, 97 insertions(+), 196 deletions(-) diff --git a/constantine.nimble b/constantine.nimble index 9278846..981052b 100644 --- a/constantine.nimble +++ b/constantine.nimble @@ -72,8 +72,8 @@ task test, "Run all tests": test "-d:Constantine32", "tests/test_finite_fields_vs_gmp.nim" # Towers of extension fields - test "", "tests/test_fp2.nim" - test "", "tests/test_fp6.nim" + test "-d:Constantine32", "tests/test_fp2.nim" + test "-d:Constantine32", "tests/test_fp6.nim" task test_no_gmp, "Run tests that don't require GMP": # -d:testingCurves is configured in a *.nim.cfg for convenience @@ -112,8 +112,8 @@ task test_no_gmp, "Run tests that don't require GMP": test "-d:Constantine32", "tests/test_finite_fields_powinv.nim" # Towers of extension fields - test "", "tests/test_fp2.nim" - test "", "tests/test_fp6.nim" + test "-d:Constantine32", "tests/test_fp2.nim" + test "-d:Constantine32", "tests/test_fp6.nim" proc runBench(benchName: string, compiler = "") = if not dirExists "build": diff --git a/constantine/arithmetic/finite_fields.nim b/constantine/arithmetic/finite_fields.nim index 8463f7d..f11d5da 100644 --- a/constantine/arithmetic/finite_fields.nim +++ b/constantine/arithmetic/finite_fields.nim @@ -29,14 +29,13 @@ import ../config/[common, curves], ./bigints, ./limbs_montgomery -# type -# `Fp`*[C: static Curve] = object -# ## All operations on a 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) -export Fp # defined in ../config/curves to avoid recursive module dependencies +type + Fp*[C: static Curve] = object + ## All operations on a 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) debug: func `$`*[C: static Curve](a: Fp[C]): string = @@ -57,16 +56,16 @@ debug: 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.mres, C.getR2modP(), C.getNegInvModWord(), C.canUseNoCarryMontyMul()) + result.mres.montyResidue(src, C.Mod, C.getR2modP(), C.getNegInvModWord(), C.canUseNoCarryMontyMul()) func fromBig*[C: static Curve](dst: var Fp[C], src: BigInt) {.noInit.} = ## Convert a BigInt to its Montgomery form - dst.mres.montyResidue(src, C.Mod.mres, C.getR2modP(), C.getNegInvModWord(), C.canUseNoCarryMontyMul()) + dst.mres.montyResidue(src, C.Mod, C.getR2modP(), C.getNegInvModWord(), C.canUseNoCarryMontyMul()) func toBig*(src: Fp): auto {.noInit.} = ## Convert a finite-field element to a BigInt in natural representation var r {.noInit.}: typeof(src.mres) - r.redc(src.mres, Fp.C.Mod.mres, Fp.C.getNegInvModWord(), Fp.C.canUseNoCarryMontyMul()) + r.redc(src.mres, Fp.C.Mod, Fp.C.getNegInvModWord(), Fp.C.canUseNoCarryMontyMul()) return r # ############################################################ @@ -84,7 +83,7 @@ func toBig*(src: Fp): auto {.noInit.} = # exist and can be implemented with compile-time specialization. # Note: for `+=`, double, sum -# not(a.mres < Fp.C.Mod.mres) is unnecessary if the prime has the form +# not(a.mres < Fp.C.Mod) 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 @@ -107,52 +106,52 @@ func setOne*(a: var Fp) = func `+=`*(a: var Fp, b: Fp) = ## In-place addition modulo p var overflowed = add(a.mres, b.mres) - overflowed = overflowed or not(a.mres < Fp.C.Mod.mres) - discard csub(a.mres, Fp.C.Mod.mres, overflowed) + overflowed = overflowed or not(a.mres < Fp.C.Mod) + discard csub(a.mres, Fp.C.Mod, overflowed) func `-=`*(a: var Fp, b: Fp) = ## In-place substraction modulo p let underflowed = sub(a.mres, b.mres) - discard cadd(a.mres, Fp.C.Mod.mres, underflowed) + discard cadd(a.mres, Fp.C.Mod, underflowed) func double*(a: var Fp) = ## Double ``a`` modulo p var overflowed = double(a.mres) - overflowed = overflowed or not(a.mres < Fp.C.Mod.mres) - discard csub(a.mres, Fp.C.Mod.mres, overflowed) + overflowed = overflowed or not(a.mres < Fp.C.Mod) + discard csub(a.mres, Fp.C.Mod, overflowed) func sum*(r: var Fp, a, b: Fp) = ## Sum ``a`` and ``b`` into ``r`` module p ## r is initialized/overwritten var overflowed = r.mres.sum(a.mres, b.mres) - overflowed = overflowed or not(r.mres < Fp.C.Mod.mres) - discard csub(r.mres, Fp.C.Mod.mres, overflowed) + overflowed = overflowed or not(r.mres < Fp.C.Mod) + discard csub(r.mres, Fp.C.Mod, overflowed) func diff*(r: var Fp, a, b: Fp) = ## Substract `b` from `a` and store the result into `r`. ## `r` is initialized/overwritten var underflowed = r.mres.diff(a.mres, b.mres) - discard cadd(r.mres, Fp.C.Mod.mres, underflowed) + discard cadd(r.mres, Fp.C.Mod, underflowed) func double*(r: var Fp, a: Fp) = ## Double ``a`` into ``r`` ## `r` is initialized/overwritten var overflowed = r.mres.double(a.mres) - overflowed = overflowed or not(r.mres < Fp.C.Mod.mres) - discard csub(r.mres, Fp.C.Mod.mres, overflowed) + overflowed = overflowed or not(r.mres < Fp.C.Mod) + discard csub(r.mres, Fp.C.Mod, overflowed) func prod*(r: var Fp, a, b: Fp) = ## 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.mres, Fp.C.getNegInvModWord(), Fp.C.canUseNoCarryMontyMul()) + r.mres.montyMul(a.mres, b.mres, Fp.C.Mod, Fp.C.getNegInvModWord(), Fp.C.canUseNoCarryMontyMul()) func square*(r: var Fp, a: Fp) = ## Squaring modulo p - r.mres.montySquare(a.mres, Fp.C.Mod.mres, Fp.C.getNegInvModWord(), Fp.C.canUseNoCarryMontySquare()) + r.mres.montySquare(a.mres, Fp.C.Mod, Fp.C.getNegInvModWord(), Fp.C.canUseNoCarryMontySquare()) func neg*(r: var Fp, a: Fp) = ## Negate modulo p - discard r.mres.diff(Fp.C.Mod.mres, a.mres) + discard r.mres.diff(Fp.C.Mod, a.mres) # ############################################################ # @@ -169,7 +168,7 @@ func pow*(a: var Fp, exponent: BigInt) = const windowSize = 5 # TODO: find best window size for each curves a.mres.montyPow( exponent, - Fp.C.Mod.mres, Fp.C.getMontyOne(), + Fp.C.Mod, Fp.C.getMontyOne(), Fp.C.getNegInvModWord(), windowSize, Fp.C.canUseNoCarryMontyMul() ) @@ -188,7 +187,7 @@ func powUnsafeExponent*(a: var Fp, exponent: BigInt) = const windowSize = 5 # TODO: find best window size for each curves a.mres.montyPowUnsafeExponent( exponent, - Fp.C.Mod.mres, Fp.C.getMontyOne(), + Fp.C.Mod, Fp.C.getMontyOne(), Fp.C.getNegInvModWord(), windowSize, Fp.C.canUseNoCarryMontyMul() ) @@ -228,4 +227,4 @@ func `*=`*(a: var Fp, b: Fp) = func square*(a: var Fp) = ## Squaring modulo p - a.mres.montySquare(a.mres, Fp.C.Mod.mres, Fp.C.getNegInvModWord(), Fp.C.canUseNoCarryMontySquare()) + a.mres.montySquare(a.mres, Fp.C.Mod, Fp.C.getNegInvModWord(), Fp.C.canUseNoCarryMontySquare()) diff --git a/constantine/arithmetic/finite_fields_inversion.nim b/constantine/arithmetic/finite_fields_inversion.nim index feafb5e..4f657a6 100644 --- a/constantine/arithmetic/finite_fields_inversion.nim +++ b/constantine/arithmetic/finite_fields_inversion.nim @@ -131,4 +131,4 @@ func inv*(r: var Fp, a: Fp) = # For now we don't activate the addition chain. # Performance is equal to GCD and it does not pass test on 𝔽p2 # We need faster squaring/multiplications - r.mres.steinsGCD(a.mres, Fp.C.getR2modP(), Fp.C.Mod.mres, Fp.C.getPrimePlus1div2()) + r.mres.steinsGCD(a.mres, Fp.C.getR2modP(), Fp.C.Mod, Fp.C.getPrimePlus1div2()) diff --git a/constantine/config/curves.nim b/constantine/config/curves.nim index 3422d33..ea607fa 100644 --- a/constantine/config/curves.nim +++ b/constantine/config/curves.nim @@ -131,62 +131,7 @@ declareCurves: # ############################################################ # -# Curve Families -# -# ############################################################ -type CurveFamily = enum - None - BN # Barreto-Naehrig - BLS # Barreto-Lynn-Scott - -func family*(curve: Curve): CurveFamily = - case curve - of BN254: - BN - of BLS12_381: - BLS - else: - None - -# ############################################################ -# -# Curve Specific Parameters -# -# ############################################################ -# -# In the form CurveXXX_ParameterName where CurveXXX is the curve name + number of bits -# of the field modulus - -# BN Curves -# ------------------------------------------------------------ -# See https://tools.ietf.org/id/draft-yonezawa-pairing-friendly-curves-00.html -# -# The prime p and order r are primes and of the form -# p = 36u^4 + 36u^3 + 24u^2 + 6u + 1 -# r = 36u^4 + 36u^3 + 18u^2 + 6u + 1 -# -# https://eprint.iacr.org/2010/429.pdf -# https://eprint.iacr.org/2013/879.pdf -# Usage: Zero-Knowledge Proofs / zkSNARKs in ZCash and Ethereum 1 -# https://eips.ethereum.org/EIPS/eip-196 - -# BLS Curves -# ------------------------------------------------------------ -# See https://tools.ietf.org/id/draft-yonezawa-pairing-friendly-curves-00.html -# -# BLS12 curves -# The prime p and order r are primes and of the form -# p = (u - 1)^2 (u^4 - u^2 + 1)/3 + u -# r = u^4 - u^2 + 1 -# -# BLS48 curves -# The prime p and order r are primes and of the form -# p = (u - 1)^2 (u^16 - u^8 + 1)/3 + u -# r = u^16 - u^8 + 1 - -# ############################################################ -# -# Curve Modulus Accessor +# Curve characteristics # # ############################################################ @@ -196,6 +141,13 @@ macro Mod*(C: static Curve): untyped = ## Get the Modulus associated to a curve result = bindSym($C & "_Modulus") +func getCurveBitSize*(C: static Curve): static int = + ## Returns the number of bits taken by the curve modulus + result = static(CurveBitSize[C]) + +template matchingBigInt*(C: static Curve): untyped = + BigInt[CurveBitSize[C]] + # ############################################################ # # Autogeneration of precomputed Montgomery constants in ROM @@ -220,10 +172,7 @@ macro genMontyMagics(T: typed): untyped = result.add newConstStmt( ident($curve & "_CanUseNoCarryMontyMul"), newCall( bindSym"useNoCarryMontyMul", - nnkDotExpr.newTree( - bindSym($curve & "_Modulus"), - ident"mres" - ) + bindSym($curve & "_Modulus") ) ) @@ -231,10 +180,7 @@ macro genMontyMagics(T: typed): untyped = result.add newConstStmt( ident($curve & "_CanUseNoCarryMontySquare"), newCall( bindSym"useNoCarryMontySquare", - nnkDotExpr.newTree( - bindSym($curve & "_Modulus"), - ident"mres" - ) + bindSym($curve & "_Modulus") ) ) @@ -242,10 +188,7 @@ macro genMontyMagics(T: typed): untyped = result.add newConstStmt( ident($curve & "_R2modP"), newCall( bindSym"r2mod", - nnkDotExpr.newTree( - bindSym($curve & "_Modulus"), - ident"mres" - ) + bindSym($curve & "_Modulus") ) ) @@ -253,40 +196,28 @@ macro genMontyMagics(T: typed): untyped = result.add newConstStmt( ident($curve & "_NegInvModWord"), newCall( bindSym"negInvModWord", - nnkDotExpr.newTree( - bindSym($curve & "_Modulus"), - ident"mres" - ) + bindSym($curve & "_Modulus") ) ) # const MyCurve_montyOne = montyOne(MyCurve_Modulus) result.add newConstStmt( ident($curve & "_MontyOne"), newCall( bindSym"montyOne", - nnkDotExpr.newTree( - bindSym($curve & "_Modulus"), - ident"mres" - ) + bindSym($curve & "_Modulus") ) ) # const MyCurve_InvModExponent = primeMinus2_BE(MyCurve_Modulus) result.add newConstStmt( ident($curve & "_InvModExponent"), newCall( bindSym"primeMinus2_BE", - nnkDotExpr.newTree( - bindSym($curve & "_Modulus"), - ident"mres" - ) + bindSym($curve & "_Modulus") ) ) # const MyCurve_PrimePlus1div2 = primePlus1div2(MyCurve_Modulus) result.add newConstStmt( ident($curve & "_PrimePlus1div2"), newCall( bindSym"primePlus1div2", - nnkDotExpr.newTree( - bindSym($curve & "_Modulus"), - ident"mres" - ) + bindSym($curve & "_Modulus") ) ) @@ -294,10 +225,6 @@ macro genMontyMagics(T: typed): untyped = genMontyMagics(Curve) -func getCurveBitSize*(C: static Curve): static int = - ## Returns the number of bits taken by the curve modulus - result = static(CurveBitSize[C]) - macro canUseNoCarryMontyMul*(C: static Curve): untyped = ## Returns true if the Modulus is compatible with a fast ## Montgomery multiplication that avoids many carries diff --git a/constantine/config/curves_parser.nim b/constantine/config/curves_parser.nim index 9f494da..ba2564a 100644 --- a/constantine/config/curves_parser.nim +++ b/constantine/config/curves_parser.nim @@ -59,8 +59,6 @@ macro declareCurves*(curves: untyped): untyped = var CurveBitSize = nnKBracket.newTree() var curveModStmts = newStmtList() - let Fp = ident"Fp" - for curveDesc in curves: curveDesc.expectKind(nnkCommand) doAssert curveDesc[0].eqIdent"curve" @@ -94,20 +92,14 @@ macro declareCurves*(curves: untyped): untyped = curve, bitSize ) - # const BN254_Modulus = Fp[BN254](value: fromHex(BigInt[254], "0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47")) + # const BN254_Modulus = fromHex(BigInt[254], "0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47") let modulusID = ident($curve & "_Modulus") curveModStmts.add newConstStmt( modulusID, - nnkObjConstr.newTree( - nnkBracketExpr.newTree(Fp, curve), - nnkExprColonExpr.newTree( - ident"mres", - newCall( - bindSym"fromHex", - nnkBracketExpr.newTree(bindSym"BigInt", bitSize), - modulus - ) - ) + newCall( + bindSym"fromHex", + nnkBracketExpr.newTree(bindSym"BigInt", bitSize), + modulus ) ) @@ -130,45 +122,6 @@ macro declareCurves*(curves: untyped): untyped = cbs, CurveBitSize ) - # Need template indirection in the type section to avoid Nim sigmatch bug - # template matchingBigInt(C: static Curve): untyped = - # BigInt[CurveBitSize[C]] - let C = ident"C" - let matchingBigInt = genSym(nskTemplate, "matchingBigInt") - result.add newProc( - name = matchingBigInt, - params = [ident"untyped", newIdentDefs(C, nnkStaticTy.newTree(Curve))], - body = nnkBracketExpr.newTree(bindSym"BigInt", nnkBracketExpr.newTree(cbs, C)), - procType = nnkTemplateDef - ) - - # type - # `Fp`*[C: static Curve] = object - # ## All operations on a 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) - result.add nnkTypeSection.newTree( - nnkTypeDef.newTree( - nnkPostfix.newTree(ident"*", Fp), - nnkGenericParams.newTree(newIdentDefs( - C, nnkStaticTy.newTree(Curve), newEmptyNode() - )), - # TODO: where should I put the nnkCommentStmt? - nnkObjectTy.newTree( - newEmptyNode(), - newEmptyNode(), - nnkRecList.newTree( - newIdentDefs( - nnkPostfix.newTree(ident"*", ident"mres"), - newCall(matchingBigInt, C) - ) - ) - ) - ) - ) - result.add curveModStmts # echo result.toStrLit() diff --git a/helpers/prng.nim b/helpers/prng.nim index 50ca9ae..d5bea9a 100644 --- a/helpers/prng.nim +++ b/helpers/prng.nim @@ -90,8 +90,8 @@ func random[T](rng: var RngState, a: var T, C: static Curve) {.noInit.}= unreduced.limbs[i] = Word(rng.next()) # Note: a simple modulo will be biaised but it's simple and "fast" - reduced.reduce(unreduced, C.Mod.mres) - a.montyResidue(reduced, C.Mod.mres, C.getR2modP(), C.getNegInvModWord(), C.canUseNoCarryMontyMul()) + reduced.reduce(unreduced, C.Mod) + a.montyResidue(reduced, C.Mod, C.getR2modP(), C.getNegInvModWord(), C.canUseNoCarryMontyMul()) else: for field in fields(a): diff --git a/sage/non_residues.sage b/sage/non_residues.sage index 7f76199..6d25d95 100644 --- a/sage/non_residues.sage +++ b/sage/non_residues.sage @@ -6,14 +6,35 @@ # * 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. +# This script checks polynomial irreducibility +# +# Constructing Tower Extensions for the implementation of Pairing-Based Cryptography +# Naomi Benger and Michael Scott, 2009 +# https://eprint.iacr.org/2009/556 + + + + + + + + + + + + + + + + + + # ############################################################ # -# Quadratic and Cubic Non-Residue +# Failed experiments of actually instantiating +# the tower of extension fields # # ############################################################ -# -# This script checks the compatibility of a field modulus -# with given tower extensions # ############################################################ # 1st try @@ -50,19 +71,20 @@ # K. = NumberField([x^3 - I - 1, x^2 + 1, x - 1]) # ############################################################ -# Let's at least verify Fp6 -print('Verifying non-residues') +# 4th try, just trying to verify Fp6 +# print('Verifying non-residues') -modulus = Integer('0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47') +# modulus = Integer('0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47') -Fp.

= NumberField(x - 1) -r1 = Fp(-1).residue_symbol(Fp.ideal(modulus),2) -print('Fp² = Fp[sqrt(-1)]: ' + str(r1)) +# Fp.

= NumberField(x - 1) +# r1 = Fp(-1).residue_symbol(Fp.ideal(modulus),2) +# print('Fp² = Fp[sqrt(-1)]: ' + str(r1)) -Fp2. = Fp.extension(x^2 + 1) -xi = Fp2(1+im) -r2 = xi.residue_symbol(Fp2.ideal(modulus),3) -# ValueError: The residue symbol to that power is not defined for the number field -# ^ AFAIK that means that Fp2 doesn't contain the 3rd root of unity -# so we are clear -print('Fp6 = Fp²[cubicRoot(1+I)]: ' + str(r2)) +# Fp2. = Fp.extension(x^2 + 1) + +# xi = Fp2(1+im) +# r2 = xi.residue_symbol(Fp2.ideal(modulus),3) +# # ValueError: The residue symbol to that power is not defined for the number field +# # ^ AFAIK that means that Fp2 doesn't contain the 3rd root of unity +# # so we are clear +# print('Fp6 = Fp²[cubicRoot(1+I)]: ' + str(r2)) diff --git a/tests/test_finite_fields_vs_gmp.nim b/tests/test_finite_fields_vs_gmp.nim index 9027022..f12f09a 100644 --- a/tests/test_finite_fields_vs_gmp.nim +++ b/tests/test_finite_fields_vs_gmp.nim @@ -61,7 +61,7 @@ proc binary_prologue[C: static Curve, N: static int]( mpz_urandomb(a, gmpRng, uint bits) mpz_urandomb(b, gmpRng, uint bits) # Set modulus to curve modulus - let err = mpz_set_str(p, Curve(C).Mod.mres.toHex(), 0) + let err = mpz_set_str(p, Curve(C).Mod.toHex(), 0) doAssert err == 0, "Error on prime for curve " & $Curve(C) ######################################################### diff --git a/tests/test_fp2.nim b/tests/test_fp2.nim index 02029bf..068a0d1 100644 --- a/tests/test_fp2.nim +++ b/tests/test_fp2.nim @@ -40,12 +40,12 @@ suite "𝔽p2 = 𝔽p[𝑖] (irreducible polynomial x²+1)": O.setOne() O let oneBig = block: - var O{.noInit.}: typeof(C.Mod.mres) + var O{.noInit.}: typeof(C.Mod) O.setOne() O - var r: typeof(C.Mod.mres) - r.redc(oneFp2.c0.mres, C.Mod.mres, C.getNegInvModWord(), canUseNoCarryMontyMul = false) + var r: typeof(C.Mod) + r.redc(oneFp2.c0.mres, C.Mod, C.getNegInvModWord(), canUseNoCarryMontyMul = false) check: bool(r == oneBig)