Properly precompute the montomery cosntants at compile-time and store them in ROM

This commit is contained in:
Mamy André-Ratsimbazafy 2020-02-15 20:43:38 +01:00
parent 4970572393
commit 05a2c6a34b
No known key found for this signature in database
GPG Key ID: 7B88AD1FE79492E1
6 changed files with 82 additions and 31 deletions

View File

@ -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")

View File

@ -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()

View File

@ -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,

View File

@ -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 =

View File

@ -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])

View File

@ -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)
# ############################################################
#