diff --git a/constantine/arithmetic/bigints_checked.nim b/constantine/arithmetic/bigints_checked.nim index 2880789..1f5339c 100644 --- a/constantine/arithmetic/bigints_checked.nim +++ b/constantine/arithmetic/bigints_checked.nim @@ -113,24 +113,77 @@ func setOne*(a: var BigInt) = when a.limbs.len > 1: zeroMem(a.limbs[1].unsafeAddr, (a.limbs.len-1) * sizeof(Word)) -func cadd*[bits](a: var BigInt[bits], b: BigInt[bits], ctl: CTBool[Word]): CTBool[Word] = +func cadd*(a: var BigInt, b: BigInt, ctl: CTBool[Word]): CTBool[Word] = ## Constant-time in-place conditional addition ## The addition is only performed if ctl is "true" ## The result carry is always computed. cadd(a.view, b.view, ctl) -func csub*[bits](a: var BigInt[bits], b: BigInt[bits], ctl: CTBool[Word]): CTBool[Word] = +func csub*(a: var BigInt, b: BigInt, ctl: CTBool[Word]): CTBool[Word] = ## Constant-time in-place conditional addition ## The addition is only performed if ctl is "true" ## The result carry is always computed. csub(a.view, b.view, ctl) -func cdouble*[bits](a: var BigInt[bits], ctl: CTBool[Word]): CTBool[Word] = +func cdouble*(a: var BigInt, ctl: CTBool[Word]): CTBool[Word] = ## Constant-time in-place conditional doubling ## The doubling is only performed if ctl is "true" ## The result carry is always computed. cadd(a.view, a.view, ctl) +# ############################################################ +# +# BigInt Primitives Optimized for speed +# +# ############################################################ +# +# TODO: fallback to cadd / csub with a "size" compile-option + +func add*(a: var BigInt, b: BigInt): CTBool[Word] = + ## Constant-time in-place addition + ## Returns the carry + add(a.view, b.view) + +func sub*(a: var BigInt, b: BigInt): CTBool[Word] = + ## Constant-time in-place substraction + ## Returns the borrow + sub(a.view, b.view) + +func double*(a: var BigInt): CTBool[Word] = + ## Constant-time in-place doubling + ## Returns the carry + add(a.view, a.view) + +func sum*(r: var BigInt, a, b: BigInt): CTBool[Word] = + ## Sum `a` and `b` into `r`. + ## `r` is initialized/overwritten + ## + ## Returns the carry + sum(r.view, a.view, b.view) + +func diff*(r: var BigInt, a, b: BigInt): CTBool[Word] = + ## Substract `b` from `a` and store the result into `r`. + ## `r` is initialized/overwritten + ## + ## Returns the borrow + diff(r.view, a.view, b.view) + +# ############################################################ +# +# Comparisons +# +# ############################################################ + +# Use "csub", which unfortunately requires the first operand to be mutable. +# for example for a <= b, we now that if a-b borrows then b > a and so a<=b is false +# This can be tested with "not csub(a, b, CtFalse)" + +# ############################################################ +# +# Modular BigInt +# +# ############################################################ + func reduce*[aBits, mBits](r: var BigInt[mBits], a: BigInt[aBits], M: BigInt[mBits]) = ## Reduce `a` modulo `M` and store the result in `r` ## @@ -143,7 +196,7 @@ func reduce*[aBits, mBits](r: var BigInt[mBits], a: BigInt[aBits], M: BigInt[mBi # pass a pointer+length to a fixed session of the BSS. reduce(r.view, a.view, M.view) -func montyResidue*[mBits](mres: var BigInt[mBits], a, N, r2modN: BigInt[mBits], negInvModWord: static BaseType) = +func montyResidue*(mres: var BigInt, a, N, r2modN: BigInt, negInvModWord: static BaseType) = ## Convert a BigInt from its natural representation ## to the Montgomery n-residue form ## @@ -168,14 +221,20 @@ func redc*[mBits](r: var BigInt[mBits], a, N: BigInt[mBits], negInvModWord: stat one redc(r.view, a.view, one.view, N.view, Word(negInvModWord)) -func montyMul*[mBits](r: var BigInt[mBits], a, b, M: BigInt[mBits], negInvModWord: static BaseType) = +# ############################################################ +# +# Montgomery Arithmetic +# +# ############################################################ + +func montyMul*(r: var BigInt, a, b, M: BigInt, negInvModWord: static BaseType) = ## Compute r <- a*b (mod M) in the Montgomery domain ## ## This resets r to zero before processing. Use {.noInit.} ## to avoid duplicating with Nim zero-init policy montyMul(r.view, a.view, b.view, M.view, Word(negInvModWord)) -func montySquare*[mBits](r: var BigInt[mBits], a, M: BigInt[mBits], negInvModWord: static BaseType) = +func montySquare*(r: var BigInt, a, M: BigInt, negInvModWord: static BaseType) = ## Compute r <- a^2 (mod M) in the Montgomery domain ## ## This resets r to zero before processing. Use {.noInit.} diff --git a/constantine/arithmetic/bigints_raw.nim b/constantine/arithmetic/bigints_raw.nim index ff9a23f..159081d 100644 --- a/constantine/arithmetic/bigints_raw.nim +++ b/constantine/arithmetic/bigints_raw.nim @@ -299,6 +299,71 @@ func shiftRight*(a: BigIntViewMut, k: int) = a[i] = (a[i] shr k) or mask(a[i+1] shl (WordBitSize - k)) a[len-1] = a[len-1] shr k +# ############################################################ +# +# BigInt Primitives Optimized for speed +# +# ############################################################ +# +# This section implements primitives that improve the speed +# of common use-cases at the expense of a slight increase in code-size. +# Where code size is a concern, the high-level API should use +# copy and/or the conditional operations. + +func add*(a: BigIntViewMut, b: BigIntViewAny): CTBool[Word] = + ## Constant-time in-place addition + ## Returns the carry + ## + ## a and b MAY be the same buffer + ## a and b MUST have the same announced bitlength (i.e. `bits` static parameters) + checkMatchingBitlengths(a, b) + + for i in 0 ..< a.numLimbs(): + a[i] = a[i] + b[i] + Word(result) + result = a[i].isMsbSet() + a[i] = a[i].mask() + +func sub*(a: BigIntViewMut, b: BigIntViewAny): CTBool[Word] = + ## Constant-time in-place substraction + ## Returns the borrow + ## + ## a and b MAY be the same buffer + ## a and b MUST have the same announced bitlength (i.e. `bits` static parameters) + checkMatchingBitlengths(a, b) + + for i in 0 ..< a.numLimbs(): + a[i] = a[i] - b[i] - Word(result) + result = a[i].isMsbSet() + a[i] = a[i].mask() + +func sum*(r: BigIntViewMut, a, b: BigIntViewAny): CTBool[Word] = + ## Sum `a` and `b` into `r`. + ## `r` is initialized/overwritten + ## + ## Returns the carry + checkMatchingBitlengths(a, b) + + r.setBitLength(bitSizeof(M)) + + for i in 0 ..< a.numLimbs(): + r[i] = a[i] + b[i] + Word(result) + result = a[i].isMsbSet() + r[i] = r[i].mask() + +func diff*(r: BigIntViewMut, a, b: BigIntViewAny): CTBool[Word] = + ## Substract `b` from `a` and store the result into `r`. + ## `r` is initialized/overwritten + ## + ## Returns the borrow + checkMatchingBitlengths(a, b) + + r.setBitLength(bitSizeof(M)) + + for i in 0 ..< a.numLimbs(): + r[i] = a[i] - b[i] - Word(result) + result = a[i].isMsbSet() + r[i] = r[i].mask() + # ############################################################ # # Modular BigInt @@ -550,7 +615,7 @@ func montyResidue*( montyMul(r, a, r2ModN, N, negInvModWord) -func montySquare( +func montySquare*( r: BigIntViewMut, a: BigIntViewAny, M: BigIntViewConst, negInvModWord: Word) {.inline.} = ## Compute r <- a^2 (mod M) in the Montgomery domain @@ -598,13 +663,6 @@ func montySquare( # of the exponent, leaking this to cache attacks # - in contrast BearSSL touches the whole table to # hide the actual selection -# -# Directly using the Hamming weight would probably -# significantly improve pairing-friendly curves as -# they are chosen for their low Hamming-Weight (see BLS12-381 x factor) -# --> Expose an exponent-leaky powMod? -# If so, create distinct type for leaked bits and BigInt -# so that sensitive data use is compiler-checked func getWindowLen(bufLen: int): uint = ## Compute the maximum window size that fits in the scratchspace buffer diff --git a/constantine/arithmetic/finite_fields.nim b/constantine/arithmetic/finite_fields.nim index 585fde0..233ebf9 100644 --- a/constantine/arithmetic/finite_fields.nim +++ b/constantine/arithmetic/finite_fields.nim @@ -75,30 +75,6 @@ func toBig*(src: Fp): auto {.noInit.} = r.redc(src.mres, Fp.C.Mod.mres, Fp.C.getNegInvModWord()) return r -# ############################################################ -# -# Aliases -# -# ############################################################ - -template cadd(a: var Fp, b: Fp, ctl: CTBool[Word]): CTBool[Word] = - ## Constant-time in-place conditional addition - ## The addition is only performed if ctl is "true" - ## The result carry is always computed. - ## - ## a and b MAY be the same buffer - ## a and b MUST have the same announced bitlength (i.e. `bits` static parameters) - cadd(a.mres, b.mres, ctl) - -template csub(a: var Fp, b: Fp, ctl: CTBool[Word]): CTBool[Word] = - ## Constant-time in-place conditional substraction - ## The substraction is only performed if ctl is "true" - ## The result carry is always computed. - ## - ## a and b MAY be the same buffer - ## a and b MUST have the same announced bitlength (i.e. `bits` static parameters) - csub(a.mres, b.mres, ctl) - # ############################################################ # # Field arithmetic primitives @@ -124,20 +100,20 @@ func setOne*(a: var Fp) = func `+=`*(a: var Fp, b: Fp) = ## Addition modulo p - var ctl = cadd(a, b, CtTrue) - ctl = ctl or not csub(a, Fp.C.Mod, CtFalse) - discard csub(a, Fp.C.Mod, ctl) + var overflowed = add(a.mres, b.mres) + overflowed = overflowed or not csub(a.mres, Fp.C.Mod.mres, CtFalse) # a >= P + discard csub(a.mres, Fp.C.Mod.mres, overflowed) func `-=`*(a: var Fp, b: Fp) = ## Substraction modulo p - let ctl = csub(a, b, CtTrue) - discard cadd(a, Fp.C.Mod, ctl) + let underflowed = sub(a.mres, b.mres) + discard cadd(a.mres, Fp.C.Mod.mres, underflowed) func double*(a: var Fp) = ## Double ``a`` modulo p - var ctl = cdouble(a, CtTrue) - ctl = ctl or not csub(a, Fp.C.Mod, CtFalse) - discard csub(a, Fp.C.Mod, ctl) + var overflowed = double(a.mres) + overflowed = overflowed or not csub(a.mres, Fp.C.Mod.mres, CtFalse) # a >= P + discard csub(a.mres, Fp.C.Mod.mres, overflowed) func `*`*(a, b: Fp): Fp {.noInit.} = ## Multiplication modulo p diff --git a/constantine/arithmetic/precomputed.nim b/constantine/arithmetic/precomputed.nim index c635f06..47f2537 100644 --- a/constantine/arithmetic/precomputed.nim +++ b/constantine/arithmetic/precomputed.nim @@ -34,7 +34,7 @@ func isMsbSet(x: BaseType): bool = const msb_pos = BaseType.sizeof * 8 - 1 bool(x shr msb_pos) -func double(a: var BigInt): bool = +func dbl(a: var BigInt): bool = ## In-place multiprecision double ## a -> 2a for i in 0 ..< a.limbs.len: @@ -61,7 +61,7 @@ func doubleMod(a: var BigInt, M: BigInt) = ## It is NOT constant-time and is intended ## only for compile-time precomputation ## of non-secret data. - var ctl = double(a) + var ctl = dbl(a) ctl = ctl or not sub(a, M, false) discard sub(a, M, ctl)