From 05a2c6a34b43ba42dc1e1fe7a6b9d85d994adb14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mamy=20Andr=C3=A9-Ratsimbazafy?= Date: Sat, 15 Feb 2020 20:43:38 +0100 Subject: [PATCH] Properly precompute the montomery cosntants at compile-time and store them in ROM --- constantine/config/curves.nim | 48 ++++++++++++++++++++++++++++ constantine/config/curves_parser.nim | 19 ++++++----- constantine/io/io_fields.nim | 6 ++-- constantine/math/bigints_checked.nim | 2 +- constantine/math/finite_fields.nim | 10 +++--- constantine/math/precomputed.nim | 28 ++++++++++------ 6 files changed, 82 insertions(+), 31 deletions(-) diff --git a/constantine/config/curves.nim b/constantine/config/curves.nim index 214f9ca..439eb85 100644 --- a/constantine/config/curves.nim +++ b/constantine/config/curves.nim @@ -7,6 +7,8 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import + # Standard library + macros, # Internal ./curves_parser, ./common, ../math/[precomputed, bigints_checked] @@ -52,3 +54,49 @@ else: curve Fake101: bitsize: 7 modulus: "0x65" # 101 in hex + +# ############################################################ +# +# Autogeneration of precomputed constants in ROM +# +# ############################################################ + +const MontyNegInvModWord* = block: + ## Store the Montgomery Magic Constant "Negative Inverse mod 2^63" in ROM + var buf: array[Curve, BaseType] + for curve in Curve: + buf[curve] = curve.Mod.mres.negInvModWord + buf + +{.experimental: "dynamicBindSym".} + +macro genR2modP(T: typed): untyped = + ## Store the Montgomery Magic Constant "R^2 mod N" in ROM + ## For each curve under the private symbol "MyCurve_R2modP" + T.getImpl.expectKind(nnkTypeDef) + T.getImpl[2].expectKind(nnkEnumTy) + + result = newStmtList() + + let E = T.getImpl[2] + for i in 1 ..< E.len: + let curve = E[i] + result.add newConstStmt( + ident($curve & "_R2modP"), newCall( + bindSym"r2mod", + # The curve parser creates modulus + # under symbol "MyCurve_Modulus" + nnkDotExpr.newTree( + bindSym($curve & "_Modulus"), + ident"mres" + ) + ) + ) + + # echo result.toStrLit + +genR2modP(Curve) + +macro getR2modP*(C: static Curve): untyped = + ## Get the Montgomery Magic Constant Associated to a curve + result = bindSym($C & "_R2modP") diff --git a/constantine/config/curves_parser.nim b/constantine/config/curves_parser.nim index 7853a82..a5ec6f7 100644 --- a/constantine/config/curves_parser.nim +++ b/constantine/config/curves_parser.nim @@ -58,7 +58,7 @@ macro declareCurves*(curves: untyped): untyped = var Curves: seq[NimNode] var CurveBitSize = nnKBracket.newTree() var curveModStmts = newStmtList() - var curveModWhenStmt = nnkWhenStmt.newTree() + var curveModWhenStmt = nnkIfStmt.newTree() let Fq = ident"Fq" @@ -112,7 +112,7 @@ macro declareCurves*(curves: untyped): untyped = ident"curve", curve ), - modulusID + newAssignment(ident"result", modulusID) ) # end for --------------------------------------------------- @@ -172,13 +172,11 @@ macro declareCurves*(curves: untyped): untyped = ) ) - # Add 'else: {.error: "Unreachable".}' to the when statements + # Add 'else: error"Unreachable" to the when statements curveModWhenStmt.add nnkElse.newTree( - nnkPragma.newTree( - nnkExprColonExpr.newTree( - ident"error", - newLit"Unreachable: the curve does not exist." - ) + newCall( + bindSym"error", + newLit"Unreachable: the curve does not exist." ) ) @@ -191,11 +189,12 @@ macro declareCurves*(curves: untyped): untyped = ident"auto", newIdentDefs( name = ident"curve", - kind = nnkStaticTy.newTree(ident"Curve") + kind = ident"Curve" ) ], body = curveModWhenStmt, - procType = nnkFuncDef + procType = nnkFuncDef, + pragmas = nnkPragma.newTree(ident"compileTime") ) # echo result.toStrLit() diff --git a/constantine/io/io_fields.nim b/constantine/io/io_fields.nim index cf22c6b..9abd7c9 100644 --- a/constantine/io/io_fields.nim +++ b/constantine/io/io_fields.nim @@ -9,9 +9,7 @@ import ./io_bigints, ../config/curves, - ../math/[bigints_checked, finite_fields], - # TODO: should be a const/proc in curves.nim - ../math/precomputed + ../math/[bigints_checked, finite_fields] # ############################################################ # @@ -24,7 +22,7 @@ func fromUint*(dst: var Fq, ## Parse a regular unsigned integer ## and store it into a BigInt of size `bits` let raw = (type dst.mres).fromRawUint(cast[array[sizeof(src), byte]](src), cpuEndian) - dst.mres.unsafeMontyResidue(raw, Fq.C.Mod.mres, r2mod(Fq.C.Mod.mres), neginvModWord(Fq.C.Mod.mres)) + dst.mres.unsafeMontyResidue(raw, Fq.C.Mod.mres, Fq.C.getR2modP(), MontyNegInvModWord[Fq.C]) func serializeRawUint*(dst: var openarray[byte], src: Fq, diff --git a/constantine/math/bigints_checked.nim b/constantine/math/bigints_checked.nim index 44383a0..9481d35 100644 --- a/constantine/math/bigints_checked.nim +++ b/constantine/math/bigints_checked.nim @@ -53,7 +53,7 @@ type ## ## This internal representation can be changed ## without notice and should not be used by external applications or libraries. - bitLength: uint32 + bitLength*: uint32 limbs*: array[bits.wordsRequired, Word] template view*(a: BigInt): BigIntViewConst = diff --git a/constantine/math/finite_fields.nim b/constantine/math/finite_fields.nim index 627af72..6f276e3 100644 --- a/constantine/math/finite_fields.nim +++ b/constantine/math/finite_fields.nim @@ -23,9 +23,7 @@ import ../primitives/constant_time, ../config/[common, curves], - ./bigints_checked, - # TODO: should be a const/proc in curves.nim - ./precomputed + ./bigints_checked # type # `Fq`*[C: static Curve] = object @@ -53,12 +51,12 @@ debug: func fromBig*(T: type Fq, src: BigInt): T = ## Convert a BigInt to its Montgomery form - result.mres.unsafeMontyResidue(src, Fq.C.Mod.mres, r2mod(Fq.C.Mod.mres), neginvModWord(Fq.C.Mod.mres)) + result.mres.unsafeMontyResidue(src, Fq.C.Mod.mres, Fq.C.getR2modP(), MontyNegInvModWord[Fq.C]) func toBig*(src: Fq): auto = ## Convert a finite-field element to a BigInt in natral representation result = src.mres - result.unsafeRedC(Fq.C.Mod.mres, Fq.C.Mod.mres.negInvModWord()) + result.unsafeRedC(Fq.C.Mod.mres, MontyNegInvModWord[Fq.C]) # ############################################################ # @@ -116,4 +114,4 @@ func `*`*(a, b: Fq): Fq {.noInit.} = ## as Fq elements are usually large and this ## routine will zero init internally the result. result.mres.setInternalBitLength() - result.mres.montyMul(a.mres, b.mres, Fq.C.Mod.mres, Fq.C.Mod.mres.negInvModWord()) + result.mres.montyMul(a.mres, b.mres, Fq.C.Mod.mres, MontyNegInvModWord[Fq.C]) diff --git a/constantine/math/precomputed.nim b/constantine/math/precomputed.nim index 7c98f3d..32b527f 100644 --- a/constantine/math/precomputed.nim +++ b/constantine/math/precomputed.nim @@ -24,22 +24,30 @@ import # They are generic over the bitsize: enabling them at runtime # would create a copy for each bitsize used (monomorphization) # leading to code-bloat. +# Thos are NOT compile-time, using CTBool seems to confuse the VM -func double(a: var BigInt): CTBool[Word] = +# We don't use distinct types here, they confuse the VM +# Similarly, isMsbSet causes trouble with distinct type in the VM + +func isMsbSet(x: BaseType): bool = + const msb_pos = BaseType.sizeof * 8 - 1 + bool(x shr msb_pos) + +func double(a: var BigInt): bool = ## In-place multiprecision double ## a -> 2a for i in 0 ..< a.limbs.len: - BaseType(a.limbs[i]) *= 2 - a.limbs[i] += Word(result) - result = a.limbs[i].isMsbSet() - a.limbs[i] = a.limbs[i] and MaxWord + var z = BaseType(a.limbs[i]) * 2 + BaseType(result) + result = z.isMsbSet() + a.limbs[i] = Word(z) and MaxWord -func sub(a: var BigInt, b: BigInt, ctl: CTBool[Word]): CTBool[Word] = +func sub(a: var BigInt, b: BigInt, ctl: bool): bool = ## In-place optional substraction for i in 0 ..< a.limbs.len: - let new_a = a.limbs[i] - b.limbs[i] - Word(result) + let new_a = BaseType(a.limbs[i]) - BaseType(b.limbs[i]) - BaseType(result) result = new_a.isMsbSet() - a.limbs[i] = ctl.mux(new_a and MaxWord, a.limbs[i]) + a.limbs[i] = if ctl: new_a.Word and MaxWord + else: a.limbs[i] func doubleMod(a: var BigInt, M: BigInt) = ## In-place modular double @@ -49,8 +57,8 @@ func doubleMod(a: var BigInt, M: BigInt) = ## only for compile-time precomputation ## of non-secret data. var ctl = double(a) - ctl = ctl or not a.sub(M, CtFalse) - discard a.sub(M, ctl) + ctl = ctl or not sub(a, M, false) + discard sub(a, M, ctl) # ############################################################ #