introduce unconditional add/sub and fused initialization+operation sum/diff

This commit is contained in:
Mamy André-Ratsimbazafy 2020-02-25 03:11:32 +01:00
parent ddce056bb4
commit 2aec16d8d8
No known key found for this signature in database
GPG Key ID: 7B88AD1FE79492E1
4 changed files with 141 additions and 48 deletions

View File

@ -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.}

View File

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

View File

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

View File

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