Highlight that bools and words are "Secret" in the codebase

This commit is contained in:
Mamy André-Ratsimbazafy 2020-04-14 22:55:52 +02:00 committed by Mamy Ratsimbazafy
parent 75557d88d8
commit 8a9cb9287c
15 changed files with 168 additions and 166 deletions

View File

@ -72,12 +72,12 @@ type
##
## This internal representation can be changed
## without notice and should not be used by external applications or libraries.
limbs*: array[bits.wordsRequired, Word]
limbs*: array[bits.wordsRequired, SecretWord]
# For unknown reason, `bits` doesn't semcheck if
# `limbs: Limbs[bits.wordsRequired]`
# with
# `Limbs[N: static int] = distinct array[N, Word]`
# `Limbs[N: static int] = distinct array[N, SecretWord]`
# so we don't set Limbs as a distinct type
debug:
@ -108,7 +108,7 @@ func setOne*(a: var BigInt) =
# Copy
# ------------------------------------------------------------
func ccopy*(a: var BigInt, b: BigInt, ctl: CTBool[Word]) =
func ccopy*(a: var BigInt, b: BigInt, ctl: SecretBool) =
## Constant-time conditional copy
## If ctl is true: b is copied into a
## if ctl is false: b is not copied and a is untouched
@ -126,92 +126,92 @@ func cswap*(a, b: var BigInt, ctl: CTBool) =
# Comparison
# ------------------------------------------------------------
func `==`*(a, b: BigInt): CTBool[Word] =
func `==`*(a, b: BigInt): SecretBool =
## Returns true if 2 big ints are equal
## Comparison is constant-time
a.limbs == b.limbs
func `<`*(a, b: BigInt): CTBool[Word] =
func `<`*(a, b: BigInt): SecretBool =
## Returns true if a < b
a.limbs < b.limbs
func `<=`*(a, b: BigInt): CTBool[Word] =
func `<=`*(a, b: BigInt): SecretBool =
## Returns true if a <= b
a.limbs <= b.limbs
func isZero*(a: BigInt): CTBool[Word] =
func isZero*(a: BigInt): SecretBool =
## Returns true if a big int is equal to zero
a.limbs.isZero
func isOne*(a: BigInt): CTBool[Word] =
func isOne*(a: BigInt): SecretBool =
## Returns true if a big int is equal to one
a.limbs.isOne
func isOdd*(a: BigInt): CTBool[Word] =
func isOdd*(a: BigInt): SecretBool =
## Returns true if a is odd
a.limbs.isOdd
# Arithmetic
# ------------------------------------------------------------
func cadd*(a: var BigInt, b: BigInt, ctl: CTBool[Word]): CTBool[Word] =
func cadd*(a: var BigInt, b: BigInt, ctl: SecretBool): SecretBool =
## Constant-time in-place conditional addition
## The addition is only performed if ctl is "true"
## The result carry is always computed.
(CTBool[Word]) cadd(a.limbs, b.limbs, ctl)
(SecretBool) cadd(a.limbs, b.limbs, ctl)
func csub*(a: var BigInt, b: BigInt, ctl: CTBool[Word]): CTBool[Word] =
func csub*(a: var BigInt, b: BigInt, ctl: SecretBool): SecretBool =
## Constant-time in-place conditional addition
## The addition is only performed if ctl is "true"
## The result carry is always computed.
(CTBool[Word]) csub(a.limbs, b.limbs, ctl)
(SecretBool) csub(a.limbs, b.limbs, ctl)
func cdouble*(a: var BigInt, ctl: CTBool[Word]): CTBool[Word] =
func cdouble*(a: var BigInt, ctl: SecretBool): SecretBool =
## Constant-time in-place conditional doubling
## The doubling is only performed if ctl is "true"
## The result carry is always computed.
(CTBool[Word]) cadd(a.limbs, a.limbs, ctl)
(SecretBool) cadd(a.limbs, a.limbs, ctl)
func add*(a: var BigInt, b: BigInt): CTBool[Word] =
func add*(a: var BigInt, b: BigInt): SecretBool =
## Constant-time in-place addition
## Returns the carry
(CTBool[Word]) add(a.limbs, b.limbs)
(SecretBool) add(a.limbs, b.limbs)
func add*(a: var BigInt, b: Word): CTBool[Word] =
func add*(a: var BigInt, b: SecretWord): SecretBool =
## Constant-time in-place addition
## Returns the carry
(CTBool[Word]) add(a.limbs, b)
(SecretBool) add(a.limbs, b)
func sub*(a: var BigInt, b: BigInt): CTBool[Word] =
func sub*(a: var BigInt, b: BigInt): SecretBool =
## Constant-time in-place substraction
## Returns the borrow
(CTBool[Word]) sub(a.limbs, b.limbs)
(SecretBool) sub(a.limbs, b.limbs)
func double*(a: var BigInt): CTBool[Word] =
func double*(a: var BigInt): SecretBool =
## Constant-time in-place doubling
## Returns the carry
(CTBool[Word]) add(a.limbs, a.limbs)
(SecretBool) add(a.limbs, a.limbs)
func sum*(r: var BigInt, a, b: BigInt): CTBool[Word] =
func sum*(r: var BigInt, a, b: BigInt): SecretBool =
## Sum `a` and `b` into `r`.
## `r` is initialized/overwritten
##
## Returns the carry
(CTBool[Word]) sum(r.limbs, a.limbs, b.limbs)
(SecretBool) sum(r.limbs, a.limbs, b.limbs)
func diff*(r: var BigInt, a, b: BigInt): CTBool[Word] =
func diff*(r: var BigInt, a, b: BigInt): SecretBool =
## Substract `b` from `a` and store the result into `r`.
## `r` is initialized/overwritten
##
## Returns the borrow
(CTBool[Word]) diff(r.limbs, a.limbs, b.limbs)
(SecretBool) diff(r.limbs, a.limbs, b.limbs)
func double*(r: var BigInt, a: BigInt): CTBool[Word] =
func double*(r: var BigInt, a: BigInt): SecretBool =
## Double `a` into `r`.
## `r` is initialized/overwritten
##
## Returns the carry
(CTBool[Word]) sum(r.limbs, a.limbs, a.limbs)
(SecretBool) sum(r.limbs, a.limbs, a.limbs)
func div2*(a: var BigInt) =
## In-place divide ``a`` by 2

View File

@ -71,7 +71,7 @@ func toBig*(src: Fp): auto {.noInit.} =
# Copy
# ------------------------------------------------------------
func ccopy*(a: var Fp, b: Fp, ctl: CTBool[Word]) =
func ccopy*(a: var Fp, b: Fp, ctl: SecretBool) =
## Constant-time conditional copy
## If ctl is true: b is copied into a
## if ctl is false: b is not copied and a is untouched
@ -106,15 +106,15 @@ func cswap*(a, b: var Fp, ctl: CTBool) =
# 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
func `==`*(a, b: Fp): CTBool[Word] =
func `==`*(a, b: Fp): SecretBool =
## Constant-time equality check
a.mres == b.mres
func isZero*(a: Fp): CTBool[Word] =
func isZero*(a: Fp): SecretBool =
## Constant-time check if zero
a.mres.isZero()
func isOne*(a: Fp): CTBool[Word] =
func isOne*(a: Fp): SecretBool =
## Constant-time check if one
a.mres == Fp.C.getMontyOne()
@ -246,7 +246,7 @@ func powUnsafeExponent*(a: var Fp, exponent: openarray[byte]) =
#
# ############################################################
func isSquare*[C](a: Fp[C]): CTBool[Word] =
func isSquare*[C](a: Fp[C]): SecretBool =
## Returns true if ``a`` is a square (quadratic residue) in 𝔽p
##
## Assumes that the prime modulus ``p`` is public.
@ -274,7 +274,7 @@ func sqrt_p3mod4*[C](a: var Fp[C]) =
static: doAssert C.Mod.limbs[0].BaseType mod 4 == 3
a.powUnsafeExponent(C.getPrimePlus1div4_BE())
func sqrt_if_square_p3mod4*[C](a: var Fp[C]): CTBool[Word] =
func sqrt_if_square_p3mod4*[C](a: var Fp[C]): SecretBool =
## If ``a`` is a square, compute the square root of ``a``
## if not, ``a`` is unmodified.
##

View File

@ -36,7 +36,7 @@ import
# The limb-endianess is little-endian, less significant limb is at index 0.
# The word-endianness is native-endian.
type Limbs*[N: static int] = array[N, Word]
type Limbs*[N: static int] = array[N, SecretWord]
## Limbs-type
## Should be distinct type to avoid builtins to use non-constant time
## implementation, for example for comparison.
@ -69,14 +69,14 @@ debug:
#
# Commented out since we don't use a distinct type
# template `[]`[N](v: Limbs[N], idx: int): Word =
# (array[N, Word])(v)[idx]
# template `[]`[N](v: Limbs[N], idx: int): SecretWord =
# (array[N, SecretWord])(v)[idx]
#
# template `[]`[N](v: var Limbs[N], idx: int): var Word =
# (array[N, Word])(v)[idx]
# template `[]`[N](v: var Limbs[N], idx: int): var SecretWord =
# (array[N, SecretWord])(v)[idx]
#
# template `[]=`[N](v: Limbs[N], idx: int, val: Word) =
# (array[N, Word])(v)[idx] = val
# template `[]=`[N](v: Limbs[N], idx: int, val: SecretWord) =
# (array[N, SecretWord])(v)[idx] = val
# ############################################################
#
@ -106,14 +106,14 @@ func setZero*(a: var Limbs) =
func setOne*(a: var Limbs) =
## Set ``a`` to 1
a[0] = Word(1)
a[0] = SecretWord(1)
when a.len > 1:
zeroMem(a[1].addr, (a.len - 1) * sizeof(Word))
zeroMem(a[1].addr, (a.len - 1) * sizeof(SecretWord))
# Copy
# ------------------------------------------------------------
func ccopy*(a: var Limbs, b: Limbs, ctl: CTBool[Word]) =
func ccopy*(a: var Limbs, b: Limbs, ctl: SecretBool) =
## Constant-time conditional copy
## If ctl is true: b is copied into a
## if ctl is false: b is not copied and a is untouched
@ -131,7 +131,7 @@ func cswap*(a, b: var Limbs, ctl: CTBool) =
## Whether ``ctl`` is true or not, the same
## memory accesses are done (unless the compiler tries to be clever)
var mask = -(Word ctl)
var mask = -(SecretWord ctl)
for i in 0 ..< a.len:
let t = mask and (a[i] xor b[i])
a[i] = a[i] xor t
@ -140,7 +140,7 @@ func cswap*(a, b: var Limbs, ctl: CTBool) =
# Comparison
# ------------------------------------------------------------
func `==`*(a, b: Limbs): CTBool[Word] =
func `==`*(a, b: Limbs): SecretBool =
## Returns true if 2 limbs are equal
## Comparison is constant-time
var accum = Zero
@ -148,37 +148,37 @@ func `==`*(a, b: Limbs): CTBool[Word] =
accum = accum or (a[i] xor b[i])
result = accum.isZero()
func `<`*(a, b: Limbs): CTBool[Word] =
func `<`*(a, b: Limbs): SecretBool =
## Returns true if a < b
## Comparison is constant-time
var diff: Word
var diff: SecretWord
var borrow: Borrow
for i in 0 ..< a.len:
subB(borrow, diff, a[i], b[i], borrow)
result = (CTBool[Word])(borrow)
result = (SecretBool)(borrow)
func `<=`*(a, b: Limbs): CTBool[Word] =
func `<=`*(a, b: Limbs): SecretBool =
## Returns true if a <= b
## Comparison is constant-time
not(b < a)
func isZero*(a: Limbs): CTBool[Word] =
func isZero*(a: Limbs): SecretBool =
## Returns true if ``a`` is equal to zero
var accum = Zero
for i in 0 ..< a.len:
accum = accum or a[i]
result = accum.isZero()
func isOne*(a: Limbs): CTBool[Word] =
func isOne*(a: Limbs): SecretBool =
## Returns true if ``a`` is equal to one
result = a[0] == Word(1)
result = a[0] == SecretWord(1)
for i in 1 ..< a.len:
result = result and a[i].isZero()
func isOdd*(a: Limbs): CTBool[Word] =
func isOdd*(a: Limbs): SecretBool =
## Returns true if a is odd
CTBool[Word](a[0] and Word(1))
SecretBool(a[0] and SecretWord(1))
# Arithmetic
# ------------------------------------------------------------
@ -190,7 +190,7 @@ func add*(a: var Limbs, b: Limbs): Carry =
for i in 0 ..< a.len:
addC(result, a[i], a[i], b[i], result)
func add*(a: var Limbs, w: Word): Carry =
func add*(a: var Limbs, w: SecretWord): Carry =
## Limbs addition, add a number that fits in a word
## Returns the carry
result = Carry(0)
@ -198,7 +198,7 @@ func add*(a: var Limbs, w: Word): Carry =
for i in 1 ..< a.len:
addC(result, a[i], a[i], Zero, result)
func cadd*(a: var Limbs, b: Limbs, ctl: CTBool[Word]): Carry =
func cadd*(a: var Limbs, b: Limbs, ctl: SecretBool): Carry =
## Limbs conditional addition
## Returns the carry
##
@ -208,7 +208,7 @@ func cadd*(a: var Limbs, b: Limbs, ctl: CTBool[Word]): Carry =
##
## Time and memory accesses are the same whether a copy occurs or not
result = Carry(0)
var sum: Word
var sum: SecretWord
for i in 0 ..< a.len:
addC(result, sum, a[i], b[i], result)
ctl.ccopy(a[i], sum)
@ -229,7 +229,7 @@ func sub*(a: var Limbs, b: Limbs): Borrow =
for i in 0 ..< a.len:
subB(result, a[i], a[i], b[i], result)
func csub*(a: var Limbs, b: Limbs, ctl: CTBool[Word]): Borrow =
func csub*(a: var Limbs, b: Limbs, ctl: SecretBool): Borrow =
## Limbs conditional substraction
## Returns the borrow
##
@ -239,7 +239,7 @@ func csub*(a: var Limbs, b: Limbs, ctl: CTBool[Word]): Borrow =
##
## Time and memory accesses are the same whether a copy occurs or not
result = Borrow(0)
var diff: Word
var diff: SecretWord
for i in 0 ..< a.len:
subB(result, diff, a[i], b[i], result)
ctl.ccopy(a[i], diff)
@ -266,11 +266,11 @@ func cneg*(a: var Limbs, ctl: CTBool) =
# So we need to xor all words and then add 1
# The "+1" might carry
# So we fuse the 2 steps
let mask = -Word(ctl) # Obtain a 0xFF... or 0x00... mask
var carry = Word(ctl)
let mask = -SecretWord(ctl) # Obtain a 0xFF... or 0x00... mask
var carry = SecretWord(ctl)
for i in 0 ..< a.len:
let t = (a[i] xor mask) + carry # XOR with mask and add 0x01 or 0x00 respectively
carry = Word(t < carry) # Carry on
carry = SecretWord(t < carry) # Carry on
a[i] = t
# Bit manipulation

View File

@ -92,7 +92,7 @@ func steinsGCD*(v: var Limbs, a: Limbs, F, M: Limbs, bits: int, mp1div2: Limbs)
let isOddA = a.isOdd()
# if isOddA: a -= b
let aLessThanB = isOddA and (CTBool[Word]) a.csub(b, isOddA)
let aLessThanB = isOddA and (SecretBool) a.csub(b, isOddA)
# if a < b and the sub was processed
# in that case, b <- a = a - b + b
discard b.cadd(a, aLessThanB)
@ -104,7 +104,7 @@ func steinsGCD*(v: var Limbs, a: Limbs, F, M: Limbs, bits: int, mp1div2: Limbs)
# Swap u and v is a < b
u.cswap(v, aLessThanB)
# if isOddA: u -= v (mod M)
let neg = isOddA and (CTBool[Word]) u.csub(v, isOddA)
let neg = isOddA and (SecretBool) u.csub(v, isOddA)
let corrected = u.cadd(M, neg)
let isOddU = u.isOdd()
@ -141,7 +141,7 @@ func steinsGCD*(v: var Limbs, a: Limbs, F, M: Limbs, bits: int, mp1div2: Limbs)
# ------------------------------------------------------------
type
LimbsView = ptr UncheckedArray[Word]
LimbsView = ptr UncheckedArray[SecretWord]
## Type-erased fixed-precision limbs
##
## This type mirrors the Limb type and is used
@ -172,19 +172,19 @@ template view(a: var Limbs): LimbsViewMut =
## Returns a borrowed type-erased mutable view to a mutable bigint
LimbsViewMut(cast[LimbsView](a.addr))
template `[]`*(v: LimbsViewConst, limbIdx: int): Word =
template `[]`*(v: LimbsViewConst, limbIdx: int): SecretWord =
LimbsView(v)[limbIdx]
template `[]`*(v: LimbsViewMut, limbIdx: int): var Word =
template `[]`*(v: LimbsViewMut, limbIdx: int): var SecretWord =
LimbsView(v)[limbIdx]
template `[]=`*(v: LimbsViewMut, limbIdx: int, val: Word) =
template `[]=`*(v: LimbsViewMut, limbIdx: int, val: SecretWord) =
LimbsView(v)[limbIdx] = val
# Type-erased add-sub
# ------------------------------------------------------------
func cadd(a: LimbsViewMut, b: LimbsViewAny, ctl: CTBool[Word], len: int): Carry =
func cadd(a: LimbsViewMut, b: LimbsViewAny, ctl: SecretBool, len: int): Carry =
## Type-erased conditional addition
## Returns the carry
##
@ -194,12 +194,12 @@ func cadd(a: LimbsViewMut, b: LimbsViewAny, ctl: CTBool[Word], len: int): Carry
##
## Time and memory accesses are the same whether a copy occurs or not
result = Carry(0)
var sum: Word
var sum: SecretWord
for i in 0 ..< len:
addC(result, sum, a[i], b[i], result)
ctl.ccopy(a[i], sum)
func csub(a: LimbsViewMut, b: LimbsViewAny, ctl: CTBool[Word], len: int): Borrow =
func csub(a: LimbsViewMut, b: LimbsViewAny, ctl: SecretBool, len: int): Borrow =
## Type-erased conditional addition
## Returns the borrow
##
@ -209,7 +209,7 @@ func csub(a: LimbsViewMut, b: LimbsViewAny, ctl: CTBool[Word], len: int): Borrow
##
## Time and memory accesses are the same whether a copy occurs or not
result = Borrow(0)
var diff: Word
var diff: SecretWord
for i in 0 ..< len:
subB(result, diff, a[i], b[i], result)
ctl.ccopy(a[i], diff)
@ -222,8 +222,8 @@ func numWordsFromBits(bits: int): int {.inline.} =
result = (bits + WordBitWidth - 1) shr divShiftor
func shlAddMod_estimate(a: LimbsViewMut, aLen: int,
c: Word, M: LimbsViewConst, mBits: int
): tuple[neg, tooBig: CTBool[Word]] =
c: SecretWord, M: LimbsViewConst, mBits: int
): tuple[neg, tooBig: SecretBool] =
## Estimate a <- a shl 2^w + c (mod M)
##
## with w the base word width, usually 32 on 32-bit platforms and 64 on 64-bit platforms
@ -237,7 +237,7 @@ func shlAddMod_estimate(a: LimbsViewMut, aLen: int,
let MLen = numWordsFromBits(mBits)
# Captures aLen and MLen
template `[]`(v: untyped, limbIdxFromEnd: BackwardsIndex): Word {.dirty.}=
template `[]`(v: untyped, limbIdxFromEnd: BackwardsIndex): SecretWord {.dirty.}=
v[`v Len` - limbIdxFromEnd.int]
# ----------------------------------------------------------------------
@ -245,16 +245,16 @@ func shlAddMod_estimate(a: LimbsViewMut, aLen: int,
let hi = a[^1] # Save the high word to detect carries
let R = mBits and (WordBitWidth - 1) # R = mBits mod 64
var a0, a1, m0: Word
var a0, a1, m0: SecretWord
if R == 0: # If the number of mBits is a multiple of 64
a0 = a[^1] #
moveMem(a[1].addr, a[0].addr, (aLen-1) * Word.sizeof) # we can just shift words
moveMem(a[1].addr, a[0].addr, (aLen-1) * SecretWord.sizeof) # we can just shift words
a[0] = c # and replace the first one by c
a1 = a[^1]
m0 = M[^1]
else: # Else: need to deal with partial word shifts at the edge.
a0 = (a[^1] shl (WordBitWidth-R)) or (a[^2] shr R)
moveMem(a[1].addr, a[0].addr, (aLen-1) * Word.sizeof)
moveMem(a[1].addr, a[0].addr, (aLen-1) * SecretWord.sizeof)
a[0] = c
a1 = (a[^1] shl (WordBitWidth-R)) or (a[^2] shr R)
m0 = (M[^1] shl (WordBitWidth-R)) or (M[^2] shr R)
@ -262,7 +262,7 @@ func shlAddMod_estimate(a: LimbsViewMut, aLen: int,
# m0 has its high bit set. (a0, a1)/p0 fits in a limb.
# Get a quotient q, at most we will be 2 iterations off
# from the true quotient
var q, r: Word
var q, r: SecretWord
unsafeDiv2n1n(q, r, a0, a1, m0) # Estimate quotient
q = mux( # If n_hi == divisor
a0 == m0, MaxWord, # Quotient == MaxWord (0b1111...1111)
@ -277,7 +277,7 @@ func shlAddMod_estimate(a: LimbsViewMut, aLen: int,
var over_p = CtTrue # Track if quotient greater than the modulus
for i in 0 ..< MLen:
var qp_lo: Word
var qp_lo: SecretWord
block: # q*p
# q * p + carry (doubleword) carry from previous limb
@ -286,7 +286,7 @@ func shlAddMod_estimate(a: LimbsViewMut, aLen: int,
block: # a*2^64 - q*p
var borrow: Borrow
subB(borrow, a[i], a[i], qp_lo, Borrow(0))
carry += Word(borrow) # Adjust if borrow
carry += SecretWord(borrow) # Adjust if borrow
over_p = mux(
a[i] == M[i], over_p,
@ -302,7 +302,7 @@ func shlAddMod_estimate(a: LimbsViewMut, aLen: int,
result.tooBig = not(result.neg) and (over_p or (carry < hi))
func shlAddMod(a: LimbsViewMut, aLen: int,
c: Word, M: LimbsViewConst, mBits: int) =
c: SecretWord, M: LimbsViewConst, mBits: int) =
## Fused modular left-shift + add
## Shift input `a` by a word and add `c` modulo `M`
##
@ -323,7 +323,7 @@ func shlAddMod(a: LimbsViewMut, aLen: int,
let lo = c shl (WordBitWidth-R)
let m0 = M[0] shl (WordBitWidth-R)
var q, r: Word
var q, r: SecretWord
unsafeDiv2n1n(q, r, hi, lo, m0) # (hi, lo) mod M
a[0] = r shr (WordBitWidth-R)
@ -346,7 +346,7 @@ func reduce(r: LimbsViewMut,
# if a uses less bits than the modulus,
# it is guaranteed < modulus.
# This relies on the precondition that the modulus uses all declared bits
copyMem(r[0].addr, a[0].unsafeAddr, aLen * sizeof(Word))
copyMem(r[0].addr, a[0].unsafeAddr, aLen * sizeof(SecretWord))
for i in aLen ..< mLen:
r[i] = Zero
else:
@ -354,7 +354,7 @@ func reduce(r: LimbsViewMut,
# we can copy modulus.limbs-1 words
# and modular shift-left-add the rest
let aOffset = aLen - mLen
copyMem(r[0].addr, a[aOffset+1].unsafeAddr, (mLen-1) * sizeof(Word))
copyMem(r[0].addr, a[aOffset+1].unsafeAddr, (mLen-1) * sizeof(SecretWord))
r[rLen - 1] = Zero
# Now shift-left the copied words while adding the new word modulo M
for i in countdown(aOffset, 0):

View File

@ -90,7 +90,7 @@ func montyMul_CIOS_nocarry(r: var Limbs, a, b, M: Limbs, m0ninv: BaseType) =
## Montgomery Multiplication using Coarse Grained Operand Scanning (CIOS)
## and no-carry optimization.
## This requires the most significant word of the Modulus
## M[^1] < high(Word) shr 1 (i.e. less than 0b01111...1111)
## M[^1] < high(SecretWord) shr 1 (i.e. less than 0b01111...1111)
## https://hackmd.io/@zkteam/modular_multiplication
# We want all the computation to be kept in registers
@ -101,10 +101,10 @@ func montyMul_CIOS_nocarry(r: var Limbs, a, b, M: Limbs, m0ninv: BaseType) =
# (A, t[0]) <- a[0] * b[i] + t[0]
# m <- (t[0] * m0ninv) mod 2^w
# (C, _) <- m * M[0] + t[0]
var A: Word
var A: SecretWord
muladd1(A, t[0], a[0], b[i], t[0])
let m = t[0] * Word(m0ninv)
var C, lo: Word
let m = t[0] * SecretWord(m0ninv)
var C, lo: SecretWord
muladd1(C, lo, m, M[0], t[0])
staticFor j, 1, N:
@ -133,7 +133,7 @@ func montyMul_CIOS(r: var Limbs, a, b, M: Limbs, m0ninv: BaseType) =
var t: typeof(M) # zero-init
const N = t.len
# Extra words to handle up to 2 carries t[N] and t[N+1]
var tN: Word
var tN: SecretWord
var tNp1: Carry
staticFor i, 0, N:
@ -148,7 +148,7 @@ func montyMul_CIOS(r: var Limbs, a, b, M: Limbs, m0ninv: BaseType) =
# m <- (t[0] * m0ninv) mod 2^w
# (C, _) <- m * M[0] + t[0]
var C, lo = Zero
let m = t[0] * Word(m0ninv)
let m = t[0] * SecretWord(m0ninv)
muladd1(C, lo, m, M[0], t[0])
staticFor j, 1, N:
# (C, t[j-1]) <- m*M[j] + t[j] + C
@ -158,7 +158,7 @@ func montyMul_CIOS(r: var Limbs, a, b, M: Limbs, m0ninv: BaseType) =
# (_, t[N]) <- t[N+1] + C
var carry: Carry
addC(carry, t[N-1], tN, C, Carry(0))
addC(carry, tN, Word(tNp1), Zero, carry)
addC(carry, tN, SecretWord(tNp1), Zero, carry)
# t[N+1] can only be non-zero in the intermediate computation
# since it is immediately reduce to t[N] at the end of each "i" iteration
@ -170,7 +170,7 @@ func montySquare_CIOS_nocarry(r: var Limbs, a, M: Limbs, m0ninv: BaseType) =
## Montgomery Multiplication using Coarse Grained Operand Scanning (CIOS)
## and no-carry optimization.
## This requires the most significant word of the Modulus
## M[^1] < high(Word) shr 2 (i.e. less than 0b00111...1111)
## M[^1] < high(SecretWord) shr 2 (i.e. less than 0b00111...1111)
## https://hackmd.io/@zkteam/modular_multiplication
# We want all the computation to be kept in registers
@ -181,7 +181,7 @@ func montySquare_CIOS_nocarry(r: var Limbs, a, M: Limbs, m0ninv: BaseType) =
# Squaring
var
A1: Carry
A0: Word
A0: SecretWord
# (A0, t[i]) <- a[i] * a[i] + t[i]
muladd1(A0, t[i], a[i], a[i], t[i])
staticFor j, i+1, N:
@ -192,8 +192,8 @@ func montySquare_CIOS_nocarry(r: var Limbs, a, M: Limbs, m0ninv: BaseType) =
# Reduction
# m <- (t[0] * m0ninv) mod 2^w
# (C, _) <- m * M[0] + t[0]
let m = t[0] * Word(m0ninv)
var C, lo: Word
let m = t[0] * SecretWord(m0ninv)
var C, lo: SecretWord
muladd1(C, lo, m, M[0], t[0])
staticFor j, 1, N:
# (C, t[j-1]) <- m*M[j] + t[j] + C
@ -220,14 +220,14 @@ func montySquare_CIOS(r: var Limbs, a, M: Limbs, m0ninv: BaseType) =
var t: typeof(M) # zero-init
const N = t.len
# Extra words to handle up to 2 carries t[N] and t[N+1]
var tNp1: Word
var tN: Word
var tNp1: SecretWord
var tN: SecretWord
staticFor i, 0, N:
# Squaring
var
A1: Carry
A0: Word
A0: SecretWord
# (A0, t[i]) <- a[i] * a[i] + t[i]
muladd1(A0, t[i], a[i], a[i], t[i])
staticFor j, i+1, N:
@ -237,13 +237,13 @@ func montySquare_CIOS(r: var Limbs, a, M: Limbs, m0ninv: BaseType) =
var carryS: Carry
addC(carryS, tN, tN, A0, Carry(0))
addC(carryS, tNp1, Word(A1), Zero, carryS)
addC(carryS, tNp1, SecretWord(A1), Zero, carryS)
# Reduction
# m <- (t[0] * m0ninv) mod 2^w
# (C, _) <- m * M[0] + t[0]
var C, lo: Word
let m = t[0] * Word(m0ninv)
var C, lo: SecretWord
let m = t[0] * SecretWord(m0ninv)
muladd1(C, lo, m, M[0], t[0])
staticFor j, 1, N:
# (C, t[j-1]) <- m*M[j] + t[j] + C
@ -253,7 +253,7 @@ func montySquare_CIOS(r: var Limbs, a, M: Limbs, m0ninv: BaseType) =
# (_, t[N]) <- t[N+1] + C
var carryR: Carry
addC(carryR, t[N-1], tN, C, Carry(0))
addC(carryR, tN, Word(tNp1), Zero, carryR)
addC(carryR, tN, SecretWord(tNp1), Zero, carryR)
discard t.csub(M, tN.isNonZero() or not(t < M)) # TODO: (t >= M) is unnecessary for prime in the form (2^64)^w
r = t
@ -265,7 +265,7 @@ func montyMul*(
r: var Limbs, a, b, M: Limbs,
m0ninv: static BaseType, canUseNoCarryMontyMul: static bool) =
## Compute r <- a*b (mod M) in the Montgomery domain
## `m0ninv` = -1/M (mod Word). Our words are 2^32 or 2^64
## `m0ninv` = -1/M (mod SecretWord). Our words are 2^32 or 2^64
##
## This resets r to zero before processing. Use {.noInit.}
## to avoid duplicating with Nim zero-init policy
@ -298,7 +298,7 @@ func montyMul*(
func montySquare*(r: var Limbs, a, M: Limbs,
m0ninv: static BaseType, canUseNoCarryMontySquare: static bool) =
## Compute r <- a^2 (mod M) in the Montgomery domain
## `m0ninv` = -1/M (mod Word). Our words are 2^31 or 2^63
## `m0ninv` = -1/M (mod SecretWord). Our words are 2^31 or 2^63
when canUseNoCarryMontySquare:
montySquare_CIOS_nocarry(r, a, M, m0ninv)
@ -512,13 +512,13 @@ func montyPow*(
# in particular we need the same memory accesses, we can't
# just index the openarray with the bits to avoid cache attacks.
for i in 1 ..< 1 shl k:
let ctl = Word(i) == Word(bits)
let ctl = SecretWord(i) == SecretWord(bits)
scratchspace[1].ccopy(scratchspace[1+i], ctl)
# Multiply with the looked-up value
# we keep the product only if the exponent bits are not all zero
scratchspace[0].montyMul(a, scratchspace[1], M, m0ninv, canUseNoCarryMontyMul)
a.ccopy(scratchspace[0], Word(bits).isNonZero())
a.ccopy(scratchspace[0], SecretWord(bits).isNonZero())
func montyPowUnsafeExponent*(
a: var Limbs,

View File

@ -72,11 +72,11 @@ func add(a: var BigInt, w: BaseType): bool =
## Returns the carry
var carry, sum: BaseType
addC(carry, sum, BaseType(a.limbs[0]), w, carry)
a.limbs[0] = Word(sum)
a.limbs[0] = SecretWord(sum)
for i in 1 ..< a.limbs.len:
let ai = BaseType(a.limbs[i])
addC(carry, sum, ai, 0, carry)
a.limbs[i] = Word(sum)
a.limbs[i] = SecretWord(sum)
result = bool(carry)
@ -87,7 +87,7 @@ func dbl(a: var BigInt): bool =
for i in 0 ..< a.limbs.len:
let ai = BaseType(a.limbs[i])
addC(carry, sum, ai, ai, carry)
a.limbs[i] = Word(sum)
a.limbs[i] = SecretWord(sum)
result = bool(carry)
@ -96,11 +96,11 @@ func sub(a: var BigInt, w: BaseType): bool =
## Returns the carry
var borrow, diff: BaseType
subB(borrow, diff, BaseType(a.limbs[0]), w, borrow)
a.limbs[0] = Word(diff)
a.limbs[0] = SecretWord(diff)
for i in 1 ..< a.limbs.len:
let ai = BaseType(a.limbs[i])
subB(borrow, diff, ai, 0, borrow)
a.limbs[i] = Word(diff)
a.limbs[i] = SecretWord(diff)
result = bool(borrow)
@ -116,7 +116,7 @@ func cadd(a: var BigInt, b: BigInt, ctl: bool): bool =
let bi = BaseType(b.limbs[i])
addC(carry, sum, ai, bi, carry)
if ctl:
a.limbs[i] = Word(sum)
a.limbs[i] = SecretWord(sum)
result = bool(carry)
@ -132,7 +132,7 @@ func csub(a: var BigInt, b: BigInt, ctl: bool): bool =
let bi = BaseType(b.limbs[i])
subB(borrow, diff, ai, bi, borrow)
if ctl:
a.limbs[i] = Word(diff)
a.limbs[i] = SecretWord(diff)
result = bool(borrow)
@ -181,7 +181,7 @@ func useNoCarryMontySquare*(M: BigInt): bool =
func negInvModWord*(M: BigInt): BaseType =
## Returns the Montgomery domain magic constant for the input modulus:
##
## µ ≡ -1/M[0] (mod Word)
## µ ≡ -1/M[0] (mod SecretWord)
##
## M[0] is the least significant limb of M
## M must be odd and greater than 2.
@ -265,7 +265,7 @@ func r_powmod(n: static int, M: BigInt): BigInt =
start = (w-1)*WordBitWidth + msb
stop = n*WordBitWidth*w
result.limbs[^1] = Word(BaseType(1) shl msb) # C0 = 2^(wn-1), the power of 2 immediatly less than the modulus
result.limbs[^1] = SecretWord(BaseType(1) shl msb) # C0 = 2^(wn-1), the power of 2 immediatly less than the modulus
for _ in start ..< stop:
result.doubleMod(M)

View File

@ -24,20 +24,22 @@ else:
## Physical BigInt for conversion in "normal integers"
type
Word* = Ct[BaseType]
SecretWord* = Ct[BaseType]
## Logical BigInt word
## A logical BigInt word is of size physical MachineWord-1
SecretBool* = CTBool[SecretWord]
const
WordBitWidth* = sizeof(Word) * 8
WordBitWidth* = sizeof(SecretWord) * 8
## Logical word size
CtTrue* = ctrue(Word)
CtFalse* = cfalse(Word)
CtTrue* = ctrue(SecretWord)
CtFalse* = cfalse(SecretWord)
Zero* = Word(0)
One* = Word(1)
MaxWord* = Word(high(BaseType))
Zero* = SecretWord(0)
One* = SecretWord(1)
MaxWord* = SecretWord(high(BaseType))
# ############################################################
#

View File

@ -42,7 +42,7 @@ func curve_eq_rhs*[F](y2: var F, x: F) =
t *= F.C.getCoefA()
y2 += t
func isOnCurve*[F](x, y: F): CTBool[Word] =
func isOnCurve*[F](x, y: F): SecretBool =
## Returns true if the (x, y) coordinates
## represents a point of the elliptic curve

View File

@ -32,7 +32,7 @@ type ECP_SWei_Proj*[F] = object
## Note that projective coordinates are not unique
x, y, z: F
func `==`*[F](P, Q: ECP_SWei_Proj[F]): CTBool[Word] =
func `==`*[F](P, Q: ECP_SWei_Proj[F]): SecretBool =
## Constant-time equality check
# Reminder: the representation is not unique
@ -46,7 +46,7 @@ func `==`*[F](P, Q: ECP_SWei_Proj[F]): CTBool[Word] =
b.prod(Q.y, P.z)
result = result and a == b
func isInf*(P: ECP_SWei_Proj): CTBool[Word] =
func isInf*(P: ECP_SWei_Proj): SecretBool =
## Returns true if P is an infinity point
## and false otherwise
##
@ -62,7 +62,7 @@ func setInf*(P: var ECP_SWei_Proj) =
P.y.setOne()
P.z.setZero()
func trySetFromCoordsXandZ*[F](P: var ECP_SWei_Proj[F], x, z: F): CTBool[Word] =
func trySetFromCoordsXandZ*[F](P: var ECP_SWei_Proj[F], x, z: F): SecretBool =
## Try to create a point the elliptic curve
## Y²Z = X³ + aXZ² + bZ³ (projective coordinates)
## y² = x³ + a x + b (affine coordinate)
@ -79,7 +79,7 @@ func trySetFromCoordsXandZ*[F](P: var ECP_SWei_Proj[F], x, z: F): CTBool[Word] =
P.y *= z
P.z = z
func trySetFromCoordX*[F](P: var ECP_SWei_Proj[F], x: F): CTBool[Word] =
func trySetFromCoordX*[F](P: var ECP_SWei_Proj[F], x: F): SecretBool =
## Try to create a point the elliptic curve
## y² = x³ + a x + b (affine coordinate)
##

View File

@ -46,7 +46,7 @@ func fromRawUintLE(
acc_len = 0
for src_idx in 0 ..< src.len:
let src_byte = Word(src[src_idx])
let src_byte = SecretWord(src[src_idx])
# buffer reads
acc = acc or (src_byte shl acc_len)
@ -83,7 +83,7 @@ func fromRawUintBE(
acc_len = 0
for src_idx in countdown(src.len-1, 0):
let src_byte = Word(src[src_idx])
let src_byte = SecretWord(src[src_idx])
# buffer reads
acc = acc or (src_byte shl acc_len)
@ -194,7 +194,7 @@ func exportRawUintLE(
acc = w
acc_len = WordBitWidth
else:
when WordBitWidth == sizeof(Word) * 8:
when WordBitWidth == sizeof(SecretWord) * 8:
let lo = acc
acc = w
else: # If using 63-bit (or less) out of uint64
@ -202,11 +202,11 @@ func exportRawUintLE(
dec acc_len
acc = w shr (WordBitWidth - acc_len)
if tail >= sizeof(Word):
if tail >= sizeof(SecretWord):
# Unrolled copy
dst.blobFrom(src = lo, dst_idx, littleEndian)
dst_idx += sizeof(Word)
tail -= sizeof(Word)
dst_idx += sizeof(SecretWord)
tail -= sizeof(SecretWord)
else:
# Process the tail and exit
when cpuEndian == littleEndian:
@ -247,7 +247,7 @@ func exportRawUintBE(
acc = w
acc_len = WordBitWidth
else:
when WordBitWidth == sizeof(Word) * 8:
when WordBitWidth == sizeof(SecretWord) * 8:
let lo = acc
acc = w
else: # If using 63-bit (or less) out of uint64
@ -255,9 +255,9 @@ func exportRawUintBE(
dec acc_len
acc = w shr (WordBitWidth - acc_len)
if tail >= sizeof(Word):
if tail >= sizeof(SecretWord):
# Unrolled copy
tail -= sizeof(Word)
tail -= sizeof(SecretWord)
dst.blobFrom(src = lo, tail, bigEndian)
else:
# Process the tail and exit

View File

@ -16,7 +16,7 @@ import ./constant_time_types
# No exceptions allowed
{.push raises: [].}
# Word primitives are inlined
# SecretWord primitives are inlined
{.push inline.}
# ############################################################

View File

@ -16,9 +16,9 @@ import ./constant_time_types
# For efficiency, those are implemented in inline assembly if possible
# API:
# - mux(CTBool, Word, Word)
# - mux(CTBool, SecretWord, SecretWord)
# - mux(CTBool, CTBool, CTBool)
# - ccopy(CTBool, var Word, Word)
# - ccopy(CTBool, var SecretWord, SecretWord)
#
# Those prevents the compiler from introducing branches and leaking secret data:
# - https://www.cl.cam.ac.uk/~rja14/Papers/whatyouc.pdf

View File

@ -59,19 +59,19 @@ func setOne*(a: var ExtensionField) =
# Comparison
# -------------------------------------------------------------------
func `==`*(a, b: ExtensionField): CTBool[Word] =
func `==`*(a, b: ExtensionField): SecretBool =
## Constant-time equality check
result = CtTrue
for fA, fB in fields(a, b):
result = result and (fA == fB)
func isZero*(a: ExtensionField): CTBool[Word] =
func isZero*(a: ExtensionField): SecretBool =
## Constant-time check if zero
result = CtTrue
for fA in fields(a):
result = result and fA.isZero()
func isOne*(a: ExtensionField): CTBool[Word] =
func isOne*(a: ExtensionField): SecretBool =
## Constant-time check if one
result = CtTrue
for fieldName, fA in fieldPairs(a):

View File

@ -89,7 +89,7 @@ func random_unsafe[T](rng: var RngState, a: var T, C: static Curve) {.noInit.}=
var reduced, unreduced{.noInit.}: T
for i in 0 ..< unreduced.limbs.len:
unreduced.limbs[i] = Word(rng.next())
unreduced.limbs[i] = SecretWord(rng.next())
# Note: a simple modulo will be biaised but it's simple and "fast"
reduced.reduce(unreduced, C.Mod)

View File

@ -32,14 +32,14 @@ proc mainArith() =
test "Adding 2 zeros":
var a = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000000")
let b = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000000")
let carry = a.cadd(b, ctrue(Word))
let carry = a.cadd(b, CtTrue)
check: a.isZero().bool
test "Adding 1 zero - real addition":
block:
var a = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000000")
let b = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
let carry = a.cadd(b, ctrue(Word))
let carry = a.cadd(b, CtTrue)
let c = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
check:
@ -47,7 +47,7 @@ proc mainArith() =
block:
var a = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
let b = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000000")
let carry = a.cadd(b, ctrue(Word))
let carry = a.cadd(b, CtTrue)
let c = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
check:
@ -57,7 +57,7 @@ proc mainArith() =
block:
var a = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000000")
let b = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
let carry = a.cadd(b, cfalse(Word))
let carry = a.cadd(b, CtFalse)
let c = a
check:
@ -65,7 +65,7 @@ proc mainArith() =
block:
var a = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
let b = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000000")
let carry = a.cadd(b, cfalse(Word))
let carry = a.cadd(b, CtFalse)
let c = a
check:
@ -75,7 +75,7 @@ proc mainArith() =
block:
var a = fromHex(BigInt[128], "0x00000000_00000001_00000000_00000000")
let b = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
let carry = a.cadd(b, ctrue(Word))
let carry = a.cadd(b, CtTrue)
let c = fromHex(BigInt[128], "0x00000000_00000001_00000000_00000001")
check:
@ -83,7 +83,7 @@ proc mainArith() =
block:
var a = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
let b = fromHex(BigInt[128], "0x00000000_00000001_00000000_00000000")
let carry = a.cadd(b, ctrue(Word))
let carry = a.cadd(b, CtTrue)
let c = fromHex(BigInt[128], "0x00000000_00000001_00000000_00000001")
check:
@ -93,7 +93,7 @@ proc mainArith() =
block:
var a = fromHex(BigInt[128], "0x00000000_00000001_00000000_00000000")
let b = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
let carry = a.cadd(b, cfalse(Word))
let carry = a.cadd(b, CtFalse)
let c = a
check:
@ -101,7 +101,7 @@ proc mainArith() =
block:
var a = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
let b = fromHex(BigInt[128], "0x00000000_00000001_00000000_00000000")
let carry = a.cadd(b, cfalse(Word))
let carry = a.cadd(b, CtFalse)
let c = a
check:
@ -111,7 +111,7 @@ proc mainArith() =
block:
var a = fromHex(BigInt[128], "0x00000000_FFFFFFFF_FFFFFFFF_FFFFFFFE")
let b = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
let carry = a.cadd(b, ctrue(Word))
let carry = a.cadd(b, CtTrue)
let c = fromHex(BigInt[128], "0x00000000_FFFFFFFF_FFFFFFFF_FFFFFFFF")
check:
@ -121,21 +121,21 @@ proc mainArith() =
block:
var a = fromHex(BigInt[128], "0x00000000_FFFFFFFF_FFFFFFFF_FFFFFFFF")
let b = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
let carry = a.cadd(b, ctrue(Word))
let carry = a.cadd(b, CtTrue)
let c = fromHex(BigInt[128], "0x00000001_00000000_00000000_00000000")
check:
bool(a == c)
not bool(carry)
suite "BigInt + Word":
suite "BigInt + SecretWord":
test "Addition limbs carry":
block: # P256 / 2
var a = BigInt[256].fromhex"0x7fffffff800000008000000000000000000000007fffffffffffffffffffffff"
let expected = BigInt[256].fromHex"7fffffff80000000800000000000000000000000800000000000000000000000"
discard a.add(Word 1)
discard a.add(SecretWord 1)
check: bool(a == expected)
suite "Modular operations - small modulus":
@ -370,7 +370,7 @@ proc mainModularInverse() =
let M = BigInt[16].fromUint(2017'u16)
var mp1div2 = M
discard mp1div2.add(Word 1)
discard mp1div2.add(SecretWord 1)
mp1div2.shiftRight(1)
let expected = BigInt[16].fromUint(1969'u16)
@ -385,7 +385,7 @@ proc mainModularInverse() =
let M = BigInt[381].fromUint(2017'u16)
var mp1div2 = M
discard mp1div2.add(Word 1)
discard mp1div2.add(SecretWord 1)
mp1div2.shiftRight(1)
let expected = BigInt[381].fromUint(1969'u16)
@ -401,7 +401,7 @@ proc mainModularInverse() =
let M = BigInt[16].fromUint(383'u16)
var mp1div2 = M
discard mp1div2.add(Word 1)
discard mp1div2.add(SecretWord 1)
mp1div2.shiftRight(1)
let expected = BigInt[16].fromUint(106'u16)
@ -416,7 +416,7 @@ proc mainModularInverse() =
let M = BigInt[381].fromUint(383'u16)
var mp1div2 = M
discard mp1div2.add(Word 1)
discard mp1div2.add(SecretWord 1)
mp1div2.shiftRight(1)
let expected = BigInt[381].fromUint(106'u16)
@ -431,7 +431,7 @@ proc mainModularInverse() =
let M = BigInt[381].fromHex("0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab")
var mp1div2 = M
discard mp1div2.add(Word 1)
discard mp1div2.add(SecretWord 1)
mp1div2.shiftRight(1)
let expected = BigInt[381].fromHex("0x0636759a0f3034fa47174b2c0334902f11e9915b7bd89c6a2b3082b109abbc9837da17201f6d8286fe6203caa1b9d4c8")
@ -448,7 +448,7 @@ proc mainModularInverse() =
var mp1div2 = M
mp1div2.shiftRight(1)
discard mp1div2.add(Word 1)
discard mp1div2.add(SecretWord 1)
let expected = BigInt[16].fromUint(0'u16)
var r {.noInit.}: BigInt[16]
@ -463,7 +463,7 @@ proc mainModularInverse() =
var mp1div2 = M
mp1div2.shiftRight(1)
discard mp1div2.add(Word 1)
discard mp1div2.add(SecretWord 1)
let expected = BigInt[381].fromUint(0'u16)
var r {.noInit.}: BigInt[381]