EVM modexp: solve DOS vectors (#286)

* stash prep for Barret Reduction

* benches lost in rebase

* fix vartime reduction

* some improvement and fixes on reduce_vartime

* Fuse reductions when converting to Montgomery + use window=1 in powMont for small exponents. ~2.7x to 3.3x accel

* modexp: Introduce a no-reduction path for small base+exponent compared to modulus. Fix DOS

* optim for padded exponents

* remove commented out code [skip ci]

* Missing noInline for allocStackArray
This commit is contained in:
Mamy Ratsimbazafy 2023-10-19 01:20:52 +02:00 committed by GitHub
parent 34baa74bc0
commit 4ccd8aaab8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 1116 additions and 154 deletions

View File

@ -14,6 +14,7 @@ import
# Internal
../constantine/math/io/io_bigints,
../constantine/math/arithmetic,
../constantine/math_arbitrary_precision/arithmetic/limbs_divmod_vartime,
../constantine/platforms/abstractions,
../constantine/serialization/codecs,
# Test utilities
@ -124,6 +125,13 @@ proc main() =
let stopCTTMod = getMonoTime()
echo "Constantine - ", aBits, " mod ", bBits, " -> ", bBits, " mod: ", float(inNanoseconds((stopCTTmod-startCTTmod)))/float(NumIters), " ns"
let startCTTvartimeMod = getMonoTime()
var q {.noInit.}: BigInt[bBits]
for _ in 0 ..< NumIters:
discard divRem_vartime(q.limbs, rTestMod.limbs, aTest.limbs, bTest.limbs)
let stopCTTvartimeMod = getMonoTime()
echo "Constantine - ", aBits, " mod ", bBits, " (vartime) -> ", bBits, " mod: ", float(inNanoseconds((stopCTTvartimeMod-startCTTvartimeMod)))/float(NumIters), " ns"
echo "----"
# Modular reduction - double-size
@ -139,7 +147,12 @@ proc main() =
let stopCTTMod2 = getMonoTime()
echo "Constantine - ", rBits, " mod ", bBits, " -> ", bBits, " mod: ", float(inNanoseconds((stopCTTmod2-startCTTmod2)))/float(NumIters), " ns"
# Constantine
let startCTTvartimeMod2 = getMonoTime()
var q2 {.noInit.}: BigInt[bBits]
for _ in 0 ..< NumIters:
discard divRem_vartime(q2.limbs, rTestMod.limbs, rTest.limbs, bTest.limbs)
let stopCTTvartimeMod2 = getMonoTime()
echo "Constantine - ", rBits, " mod ", bBits, " (vartime) -> ", bBits, " mod: ", float(inNanoseconds((stopCTTvartimeMod2-startCTTvartimeMod2)))/float(NumIters), " ns"
echo ""

View File

@ -56,9 +56,8 @@ func prod_comba[rLen, aLen, bLen: static int](r: var Limbs[rLen], a: Limbs[aLen]
u = t
t = Zero
if aLen+bLen < rLen:
for i in aLen+bLen ..< rLen:
r[i] = Zero
for i in aLen+bLen ..< rLen:
r[i] = Zero
func prod*[rLen, aLen, bLen: static int](r{.noalias.}: var Limbs[rLen], a: Limbs[aLen], b: Limbs[bLen]) {.inline.} =
## Multi-precision multiplication
@ -156,9 +155,8 @@ func square_Comba[rLen, aLen](
u = t
t = Zero
if aLen*2 < rLen:
for i in aLen*2 ..< rLen:
r[i] = Zero
for i in aLen*2 ..< rLen:
r[i] = Zero
func square_operandScan[rLen, aLen](
r: var Limbs[rLen],

View File

@ -15,7 +15,7 @@ import
./limbs_mod2k,
./limbs_multiprec,
./limbs_extmul,
./limbs_divmod
./limbs_divmod_vartime
# No exceptions allowed
{.push raises: [], checks: off.}
@ -41,7 +41,34 @@ import
# Also need to take into account constant-time for RSA
# i.e. countLeadingZeros can only be done on public moduli.
func powOddMod_vartime*(
iterator unpackBE(scalarByte: byte): bool =
for i in countdown(7, 0):
yield bool((scalarByte shr i) and 1)
func pow_vartime(
r: var openArray[SecretWord],
a: openArray[SecretWord],
exponent: openArray[byte]) {.tags:[VarTime, Alloca], meter.} =
## r <- a^exponent
r.setOne()
var isOne = true
for e in exponent:
for bit in unpackBE(e):
if not isOne:
r.square_vartime(r)
if bit:
if isOne:
for i in 0 ..< a.len:
r[i] = a[i]
for i in a.len ..< r.len:
r[i] = Zero
isOne = false
else:
r.prod_vartime(r, a)
func powOddMod_vartime(
r: var openArray[SecretWord],
a: openArray[SecretWord],
exponent: openArray[byte],
@ -55,33 +82,20 @@ func powOddMod_vartime*(
debug:
doAssert bool(M.isOdd())
let aBits = a.getBits_LE_vartime()
let mBits = M.getBits_LE_vartime()
let eBits = exponent.getBits_BE_vartime()
if eBits == 1:
r.view().reduce(a.view(), aBits, M.view(), mBits)
discard r.reduce_vartime(a, M)
return
let L = wordsRequired(mBits)
let m0ninv = M[0].negInvModWord()
var rMont = allocStackArray(SecretWord, L)
block:
var r2Buf = allocStackArray(SecretWord, L)
template r2: untyped = r2Buf.toOpenArray(0, L-1)
r2.r2_vartime(M.toOpenArray(0, L-1))
var aMont_buf = allocStackArray(SecretWord, L)
template aMont: untyped = aMont_buf.toOpenArray(0, L-1)
# Conversion to Montgomery can auto-reduced by up to M*R
# if we use redc2xMont (a/R) and montgomery multiplication by R³
# For now, we call explicit reduction as it can handle all sizes.
# TODO: explicit reduction uses constant-time division which is **very** expensive
if a.len != M.len:
let t = allocStackArray(SecretWord, L)
t.LimbsViewMut.reduce(a.view(), aBits, M.view(), mBits)
rMont.LimbsViewMut.getMont(LimbsViewConst t, M.view(), LimbsViewConst r2.view(), m0ninv, mBits)
else:
rMont.LimbsViewMut.getMont(a.view(), M.view(), LimbsViewConst r2.view(), m0ninv, mBits)
aMont.getMont_vartime(a, M)
block:
var oneMontBuf = allocStackArray(SecretWord, L)
@ -91,12 +105,11 @@ func powOddMod_vartime*(
let scratchLen = L * ((1 shl window) + 1)
var scratchSpace = allocStackArray(SecretWord, scratchLen)
rMont.LimbsViewMut.powMont_vartime(
aMont_buf.LimbsViewMut.powMont_vartime(
exponent, M.view(), LimbsViewConst oneMontBuf,
m0ninv, LimbsViewMut scratchSpace, scratchLen, mBits)
r.view().fromMont(LimbsViewConst rMont, M.view(), m0ninv, mBits)
r.view().fromMont(LimbsViewConst aMont_buf, M.view(), m0ninv, mBits)
func powMod_vartime*(
r: var openArray[SecretWord],
@ -128,6 +141,14 @@ func powMod_vartime*(
r[i] = Zero
return
# No modular reduction needed
# -------------------------------------------------------------------
if eBits < WordBitWidth and
aBits.uint shr (WordBitWidth - eBits) == 0 and # handle overflow of uint128 [0, aBits] << eBits
aBits.uint shl eBits < mBits.uint:
r.pow_vartime(a, exponent)
return
# Odd modulus
# -------------------------------------------------------------------
if M.isOdd().bool:
@ -197,5 +218,5 @@ func powMod_vartime*(
var qyBuf = allocStackArray(SecretWord, M.len)
template qy: untyped = qyBuf.toOpenArray(0, M.len-1)
qy.prod(q, y)
qy.prod_vartime(q, y)
discard r.addMP(qy, a1)

View File

@ -12,7 +12,7 @@ import
./limbs_fixedprec
# No exceptions allowed
{.push raises: [].}
{.push raises: [], checks: off.}
# ############################################################
#
@ -63,7 +63,7 @@ func shlAddMod_estimate(a: LimbsViewMut, aLen: int,
a1 = (a[^1] shl (WordBitWidth-R)) or (a[^2] shr R)
m0 = (M[^1] shl (WordBitWidth-R)) or (M[^2] shr R)
# m0 has its high bit set. (a0, a1)/p0 fits in a limb.
# m0 has its high bit set. (a0, a1)/m0 fits in a limb.
# Get a quotient q, at most we will be 2 iterations off
# from the true quotient
var q, r: SecretWord
@ -78,29 +78,29 @@ func shlAddMod_estimate(a: LimbsViewMut, aLen: int,
# Now substract a*2^64 - q*p
var carry = Zero
var over_p = CtTrue # Track if quotient greater than the modulus
var overM = CtTrue # Track if quotient greater than the modulus
for i in 0 ..< MLen:
var qp_lo: SecretWord
var qm_lo: SecretWord
block: # q*p
# q * p + carry (doubleword) carry from previous limb
muladd1(carry, qp_lo, q, M[i], carry)
block: # q*m
# q * m + carry (doubleword) carry from previous limb
muladd1(carry, qm_lo, q, M[i], carry)
block: # a*2^64 - q*p
var borrow: Borrow
subB(borrow, a[i], a[i], qp_lo, Borrow(0))
subB(borrow, a[i], a[i], qm_lo, Borrow(0))
carry += SecretWord(borrow) # Adjust if borrow
over_p = mux(a[i] == M[i], over_p, a[i] > M[i])
overM = mux(a[i] == M[i], overM, a[i] > M[i])
# Fix quotient, the true quotient is either q-1, q or q+1
#
# if carry < q or carry == q and over_p we must do "a -= p"
# if carry > hi (negative result) we must do "a += p"
# if carry < q or carry == q and over_p we must do "a -= m"
# if carry > hi (negative result) we must do "a += m"
result.neg = carry > hi
result.tooBig = not(result.neg) and (over_p or (carry < hi))
result.tooBig = not(result.neg) and (overM or (carry < hi))
func shlAddMod(a: LimbsViewMut, aLen: int,
c: SecretWord, M: LimbsViewConst, mBits: int) =

View File

@ -0,0 +1,228 @@
# Constantine
# Copyright (c) 2018-2019 Status Research & Development GmbH
# Copyright (c) 2020-Present Mamy André-Ratsimbazafy
# Licensed and distributed under either of
# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
../../platforms/abstractions,
../../platforms/intrinsics/extended_precision_vartime,
./limbs_views
# No exceptions allowed
{.push raises: [], checks: off.}
# ############################################################
#
# Division and Modular Reduction
# (variable-time)
#
# ############################################################
func shlAddMod_multiprec_vartime(
a: var openArray[SecretWord], c: SecretWord,
M: openArray[SecretWord], mBits: int): SecretWord {.meter.} =
## Fused modular left-shift + add
## Computes: a <- a shl 2ʷ + c (mod M)
## Returns: (a shl 2ʷ + c) / M
##
## with w the base word width, usually 32 on 32-bit platforms and 64 on 64-bit platforms
##
## The modulus `M` most-significant bit at `mBits` MUST be set.
##
## Specialized for M being a multi-precision integer.
# Assuming 64-bit words
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: SecretWord
if R == 0: # If the number of mBits is a multiple of 64
a0 = a[^1] #
copyWords(a.view(), 1, # we can just shift words
a.view(), 0, a.len-1) #
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.
let clz = WordBitWidth-R
a0 = (a[^1] shl clz) or (a[^2] shr R)
copyWords(a.view(), 1,
a.view(), 0, a.len-1)
a[0] = c
a1 = (a[^1] shl clz) or (a[^2] shr R)
m0 = (M[^1] shl clz) or (M[^2] shr R)
# m0 has its high bit set. (a0, a1)/m0 fits in a limb.
# Get a quotient q, at most we will be 2 iterations off
# from the true quotient
var q: SecretWord # Estimate quotient
if bool(a0 == m0): # if a_hi == divisor
q = MaxWord # quotient = MaxWord (0b1111...1111)
elif bool(a0.isZero()) and
bool(a1 < m0): # elif q == 0, true quotient = 0
q = Zero
return q
else:
var r: SecretWord
div2n1n_vartime(q, r, a0, a1, m0) # else instead of being of by 0, 1 or 2
q -= One # we return q-1 to be off by -1, 0 or 1
# Now substract a*2^64 - q*m
var carry = Zero
var overM = true # Track if quotient greater than the modulus
for i in 0 ..< M.len:
var qm_lo: SecretWord
block: # q*m
# q * m + carry (doubleword) carry from previous limb
muladd1(carry, qm_lo, q, M[i], carry)
block: # a*2^64 - q*m
var borrow: Borrow
subB(borrow, a[i], a[i], qm_lo, Borrow(0))
carry += SecretWord(borrow) # Adjust if borrow
if bool(a[i] != M[i]):
overM = bool(a[i] > M[i])
# Fix quotient, the true quotient is either q-1, q or q+1
#
# if carry < q or carry == q and overM we must do "a -= M"
# if carry > hi (negative result) we must do "a += M"
if bool(carry > hi):
var c = Carry(0)
for i in 0 ..< a.len:
addC(c, a[i], a[i], M[i], c)
q -= One
elif overM or bool(carry < hi):
var b = Borrow(0)
for i in 0 ..< a.len:
subB(b, a[i], a[i], M[i], b)
q += One
return q
func shortDiv_vartime*(remainder: var SecretWord, n_hi, n_lo, d: SecretWord, normFactor: int): SecretWord =
# We normalize d with clz so that the MSB is set
# And normalize (n_hi * 2^64 + n_lo) by normFactor as well to maintain the result
# This ensures that (n_hi, n_hi)/d fits in a limb.
if normFactor == 0:
div2n1n_vartime(result, remainder, n_hi, n_lo, d)
else:
let clz = WordBitWidth-normFactor
let hi = (n_hi shl clz) or (n_lo shr normFactor)
let lo = n_lo shl clz
let d = d shl clz
div2n1n_vartime(result, remainder, hi, lo, d)
remainder = remainder shr clz
func shlAddMod_vartime(a: var openArray[SecretWord], c: SecretWord,
M: openArray[SecretWord], mBits: int): SecretWord {.meter.} =
## Fused modular left-shift + add
## Computes: a <- a shl 2ʷ + c (mod M)
## Returns: (a shl 2ʷ + c) / M
##
## with w the base word width, usually 32 on 32-bit platforms and 64 on 64-bit platforms
##
## The modulus `M` most-significant bit at `mBits` MUST be set.
if mBits <= WordBitWidth:
# If M fits in a single limb
# We normalize M with clz so that the MSB is set
# And normalize (a * 2^64 + c) by R as well to maintain the result
# This ensures that (a0, a1)/m0 fits in a limb.
let R = mBits and (WordBitWidth - 1)
# (hi, lo) = a * 2^64 + c
return shortDiv_vartime(remainder = a[0], n_hi = a[0], n_lo = c, d = M[0], normFactor = R)
else:
return shlAddMod_multiprec_vartime(a, c, M, mBits)
func divRem_vartime*(
q, r: var openArray[SecretWord],
a, b: openArray[SecretWord]): bool {.meter.} =
# q = a div b
# r = a mod b
#
# Requirements:
# b != 0
# q.len > a.len - b.len
# r.len >= b.len
let aBits = getBits_LE_vartime(a)
let bBits = getBits_LE_vartime(b)
let aLen = wordsRequired(aBits)
let bLen = wordsRequired(bBits)
let rLen = bLen
let aOffset = a.len - b.len
# Note: don't confuse a.len and aLen (actually used words)
if unlikely(bool(r.len < bLen)):
# remainder buffer cannot store up to modulus size
return false
if unlikely(bool(q.len < aOffset+1)):
# quotient buffer cannot store up to quotient size
return false
if unlikely(bBits == 0):
# Divide by zero
return false
if aBits < bBits:
# if a uses less bits than b,
# a < b, so q = 0 and r = a
copyWords(r.view(), 0, a.view(), 0, aLen)
for i in aLen ..< r.len:
r[i] = Zero
for i in 0 ..< q.len:
q[i] = Zero
else:
# The length of a is at least the divisor
# We can copy bLen-1 words
# and modular shift-left-add the rest
copyWords(r.view(), 0, a.view(), aOffset+1, b.len-1)
r[rLen-1] = Zero
# Now shift-left the copied words while adding the new word mod b
for i in countdown(aOffset, 0):
q[i] = shlAddMod_vartime(
r.toOpenArray(0, rLen-1),
a[i],
b.toOpenArray(0, bLen-1),
bBits)
# Clean up extra words
for i in aOffset+1 ..< q.len:
q[i] = Zero
for i in rLen ..< r.len:
r[i] = Zero
return true
func reduce_vartime*(r: var openArray[SecretWord],
a, b: openArray[SecretWord]): bool {.noInline, meter.} =
let aOffset = max(a.len - b.len, 0)
var qBuf = allocStackArray(SecretWord, aOffset+1)
template q: untyped = qBuf.toOpenArray(0, aOffset)
result = divRem_vartime(q, r, a, b)
# ############################################################
#
# Barrett Reduction
#
# ############################################################
# - https://en.wikipedia.org/wiki/Barrett_reduction
# - Handbook of Applied Cryptography
# Alfred J. Menezes, Paul C. van Oorschot and Scott A. Vanstone
# https://cacr.uwaterloo.ca/hac/about/chap14.pdf
# - Modern Computer Arithmetic
# Richard P. Brent and Paul Zimmermann
# https://members.loria.fr/PZimmermann/mca/mca-cup-0.5.9.pdf

View File

@ -8,7 +8,8 @@
import
# Internal
../../platforms/[abstractions, allocs]
../../platforms/[abstractions, allocs],
./limbs_views
func prod_comba(r: var openArray[SecretWord], a, b: openArray[SecretWord]) {.noInline, tags: [Alloca].} =
## Extended precision multiplication
@ -41,4 +42,63 @@ func prod_comba(r: var openArray[SecretWord], a, b: openArray[SecretWord]) {.noI
func prod*(r: var openArray[SecretWord], a, b: openArray[SecretWord]) {.inline, meter.}=
## Extended precision multiplication
r.prod_comba(a, b)
r.prod_comba(a, b)
func prod_vartime*(r: var openArray[SecretWord], a, b: openArray[SecretWord]) {.inline, meter.}=
## Extended precision multiplication (vartime)
let aBits = a.getBits_LE_vartime()
let bBits = b.getBits_LE_vartime()
let aWords = wordsRequired(aBits)
let bWords = wordsRequired(bBits)
r.prod_comba(
a.toOpenArray(0, aWords-1),
b.toOpenArray(0, bWords-1))
func square_comba(r: var openArray[SecretWord], a: openArray[SecretWord]) {.noInline, tags: [Alloca].} =
## Extended precision squaring
# We use Product Scanning / Comba multiplication
var t, u, v = Zero
let stopEx = min(a.len * 2, r.len)
let tmp = allocStackArray(SecretWord, stopEx)
for i in 0 ..< stopEx:
# Invariant for product scanning:
# if we multiply accumulate by a[k1] * b[k2]
# we have k1+k2 == i
let ib = min(a.len-1, i)
let ia = i - ib
for j in 0 ..< min(a.len - ia, ib+1):
let k1 = ia+j
let k2 = ib-j
if k1 < k2:
mulDoubleAcc(t, u, v, a[k1], a[k2])
elif k1 == k2:
mulAcc(t, u, v, a[k1], a[k2])
else:
discard
tmp[i] = v
if i < stopEx-1:
v = u
u = t
t = Zero
for i in 0 ..< stopEx:
r[i] = tmp[i]
for i in stopEx ..< r.len:
r[i] = Zero
func square*(r: var openArray[SecretWord], a: openArray[SecretWord]) {.inline, meter.}=
## Extended precision squaring
r.square_comba(a)
func square_vartime*(r: var openArray[SecretWord], a: openArray[SecretWord]) {.inline, meter.}=
## Extended precision squaring (vartime)
let aBits = a.getBits_LE_vartime()
let aWords = wordsRequired(aBits)
r.square_comba(a.toOpenArray(0, aWords-1))

View File

@ -65,7 +65,12 @@ func submod2k_vartime*(r{.noAlias.}: var openArray[SecretWord], a, b: openArray[
func mulmod2k_vartime*(r: var openArray[SecretWord], a, b: openArray[SecretWord], k: uint) {.inline, meter.} =
## r <- a*b (mod 2ᵏ)
r.prod(a, b)
r.prod_vartime(a, b)
r.mod2k_vartime(k)
func sqrmod2k_vartime*(r: var openArray[SecretWord], a: openArray[SecretWord], k: uint) {.inline, meter.} =
## r <- a² (mod 2ᵏ)
r.square_vartime(a)
r.mod2k_vartime(k)
iterator unpackLE(scalarByte: byte): bool =
@ -153,11 +158,11 @@ func powMod2k_vartime*(
sBuf[i] = Zero
# TODO: sliding/fixed window exponentiation
for i in countdown(exponent.len-1, 0):
for i in countdown(exponent.len-1, 0): # Little-endian exponentiation
for bit in unpackLE(exponent[i]):
if bit:
r.mulmod2k_vartime(r, s, k)
s.mulmod2k_vartime(s, s, k)
s.sqrmod2k_vartime(s, k)
bitsLeft -= 1
if bitsLeft == 0:
return

View File

@ -10,9 +10,9 @@ import
# Internal
../../platforms/[abstractions, allocs, bithacks],
./limbs_views,
./limbs_mod,
./limbs_multiprec,
./limbs_fixedprec,
./limbs_divmod
./limbs_divmod_vartime
# No exceptions allowed
{.push raises: [], checks: off.}
@ -26,77 +26,13 @@ import
# Montgomery magic constants
# ------------------------------------------
func r_powmod_vartime(r: var openArray[SecretWord], M: openArray[SecretWord], n: static int) =
## Returns the Montgomery domain magic constant for the input modulus:
##
## R ≡ R (mod M) with R = (2^WordBitWidth)^numWords
## or
## R² ≡ R² (mod M) with R = (2^WordBitWidth)^numWords
##
## Assuming a field modulus of size 256-bit with 63-bit words, we require 5 words
## R² ≡ ((2^63)^5)^2 (mod M) = 2^630 (mod M)
# Algorithm
# Bos and Montgomery, Montgomery Arithmetic from a Software Perspective
# https://eprint.iacr.org/2017/1057.pdf
#
# For R = r^n = 2^wn and 2^(wn 1) ≤ N < 2^wn
# r^n = 2^63 in on 64-bit and w the number of words
#
# 1. C0 = 2^(wn - 1), the power of two immediately less than N
# 2. for i in 1 ... wn+1
# Ci = C(i-1) + C(i-1) (mod M)
#
# Thus: C(wn+1) ≡ 2^(wn+1) C0 ≡ 2^(wn + 1) 2^(wn - 1) ≡ 2^(2wn) ≡ (2^wn)^2 ≡ R² (mod M)
debug:
doAssert bool(M[0] and One)
doAssert BaseType(M[M.len-1]) != 0
doAssert r.len == M.len
let
w = M.len
msb = int log2_vartime(BaseType M[M.len-1])
start = (w-1)*WordBitWidth + msb
stop = n*WordBitWidth*w
for i in 0 ..< r.len-1:
r[i] = Zero
r[r.len-1] = SecretWord(BaseType(1) shl msb) # C0 = 2^(wn-1), the power of 2 immediatly less than the modulus
for i in start ..< stop:
r.doublemod_vartime(r, M)
func oneMont_vartime*(r: var openArray[SecretWord], M: openArray[SecretWord]) {.meter.} =
func oneMont_vartime*(r: var openArray[SecretWord], M: openArray[SecretWord]) {.noInline, meter.} =
## Returns 1 in Montgomery domain:
# r.r_powmod_vartime(M, 1)
let mBits = getBits_LE_vartime(M)
let t = allocStackArray(SecretWord, M.len + 1)
zeroMem(t, M.len*sizeof(SecretWord))
t[M.len] = One
r.view().reduce(LimbsViewMut t, M.len*WordBitWidth+1, M.view(), mBits)
func r2_vartime*(r: var openArray[SecretWord], M: openArray[SecretWord]) {.meter.} =
## Returns the Montgomery domain magic constant for the input modulus:
##
## R² ≡ R² (mod M) with R = (2^WordBitWidth)^numWords
##
## Assuming a field modulus of size 256-bit with 63-bit words, we require 5 words
## R² ≡ ((2^63)^5)^2 (mod M) = 2^630 (mod M)
# r.r_powmod_vartime(M, 2)
let mBits = getBits_LE_vartime(M)
let t = allocStackArray(SecretWord, 2*M.len + 1)
zeroMem(t, 2*M.len*sizeof(SecretWord))
t[2*M.len] = One
r.view().reduce(LimbsViewMut t, 2*M.len*WordBitWidth+1, M.view(), mBits)
discard r.reduce_vartime(t.toOpenArray(0, M.len), M)
# Montgomery multiplication
@ -206,6 +142,21 @@ func getMont*(r: LimbsViewMut, a: LimbsViewAny, M, r2modM: LimbsViewConst,
# Reference: https://eprint.iacr.org/2017/1057.pdf
mulMont_FIPS(r, a, r2ModM, M, m0ninv, mBits)
func getMont_vartime*(r: var openArray[SecretWord], a, M: openArray[SecretWord]) {.noInline, meter.} =
## Transform a bigint ``a`` from it's natural representation (mod N)
## to a the Montgomery n-residue representation
##
## Shift + reduction based
let aBits = a.getBits_LE_vartime()
let mBits = M.getBits_LE_vartime()
let L = wordsRequired(mBits)
let aR_len = wordsRequired(aBits) + L
var aR_buf = allocStackArray(SecretWord, aR_len)
template aR: untyped = aR_Buf.toOpenArray(0, aR_len - 1)
aR.shiftLeft_vartime(a, WordBitWidth * L)
discard r.reduce_vartime(aR, M)
# Montgomery Modular Exponentiation
# ------------------------------------------
@ -248,6 +199,30 @@ func getWindowLen(bufLen, wordLen: int): uint =
while ((1 shl result) + 1)*wordLen > bufLen:
dec result
func precomputeWindow(
a: LimbsViewMut,
M, one: LimbsViewConst,
m0ninv: SecretWord,
scratchspace: LimbsViewMut,
wordLen: int,
mBits: int,
windowLen: uint) =
# Precompute window content, special case for window = 1
# (i.e scratchspace has only space for 2 temporaries)
# The content scratchspace[2+k] is set at aᵏ
# with scratchspace[0] untouched
if windowLen == 1:
scratchspace.copyWords(1*wordLen, a, 0, wordLen)
else:
scratchspace.copyWords(2*wordLen, a, 0, wordLen)
for k in 2 ..< 1 shl windowLen:
let sk1 = cast[LimbsViewMut](scratchspace[(k+1)*wordLen].addr)
let sk = cast[LimbsViewConst](scratchspace[k*wordLen].addr)
sk1.mulMont_FIPS(sk, a, M, m0ninv, mBits)
# Set a to one
a.copyWords(0, one, 0, wordLen)
func powMontPrologue(
a: LimbsViewMut, M, one: LimbsViewConst,
m0ninv: SecretWord,
@ -262,17 +237,11 @@ func powMontPrologue(
# with scratchspace[0] untouched
let wordLen = wordsRequired(mBits)
result = scratchLen.getWindowLen(wordLen)
if result == 1:
scratchspace.copyWords(1*wordLen, a, 0, wordLen)
else:
scratchspace.copyWords(2*wordLen, a, 0, wordLen)
for k in 2 ..< 1 shl result:
let sk1 = cast[LimbsViewMut](scratchspace[(k+1)*wordLen].addr)
let sk = cast[LimbsViewConst](scratchspace[k*wordLen].addr)
sk1.mulMont_FIPS(sk, a, M, m0ninv, mBits)
# Set a to one
a.copyWords(0, one, 0, wordLen)
precomputeWindow(
a, M, one,
m0ninv, scratchSpace,
wordLen, mBits,
result)
func powMontSquarings(
a: LimbsViewMut,
@ -392,6 +361,38 @@ func powMont*(
s0.mulMont_FIPS(a, s1, M, m0ninv, mBits)
a.ccopyWords(0, s0, 0, SecretWord(bits).isNonZero(), N)
# Montgomery Modular Exponentiation (vartime)
# -------------------------------------------
func getWindowLen_vartime(bufLen, expLen, wordLen: int): uint =
## Compute the maximum window size that fits in the scratchspace buffer
if expLen == 1:
return 1
else:
return getWindowLen(bufLen, wordLen)
func powMontPrologue_vartime(
a: LimbsViewMut,
expLen: int,
M, one: LimbsViewConst,
m0ninv: SecretWord,
scratchspace: LimbsViewMut,
scratchLen: int,
mBits: int): uint {.tags:[Alloca], meter.} =
## Setup the scratchspace
## Returns the fixed-window size for exponentiation with window optimization.
# Precompute window content, special case for window = 1
# (i.e scratchspace has only space for 2 temporaries)
# The content scratchspace[2+k] is set at aᵏ
# with scratchspace[0] untouched
let wordLen = wordsRequired(mBits)
result = scratchLen.getWindowLen_vartime(expLen, wordLen)
precomputeWindow(
a, M, one,
m0ninv, scratchSpace,
wordLen, mBits,
result)
func powMont_vartime*(
a: LimbsViewMut,
exponent: openArray[byte],
@ -412,7 +413,10 @@ func powMont_vartime*(
# TODO: scratchspace[1] is unused when window > 1
let N = wordsRequired(mBits)
let window = powMontPrologue(a, M, one, m0ninv, scratchspace, scratchLen, mBits)
let eBits = exponent.getBits_BE_vartime()
let eBytes = bytesRequired(eBits)
let window = powMontPrologue_vartime(a, eBytes, M, one, m0ninv, scratchspace, scratchLen, mBits)
var
acc, acc_len: uint
@ -421,9 +425,11 @@ func powMont_vartime*(
let s0 = cast[LimbsViewMut](scratchspace[0].addr)
let s1 = cast[LimbsViewConst](scratchspace[1*N].addr)
while acc_len > 0 or e < exponent.len:
while acc_len > 0 or e < eBytes:
let (_, bits) = powMontSquarings(
a, exponent, M, m0ninv, mBits,
a,
exponent.toOpenArray(exponent.len - eBytes, exponent.len-1), # BigEndian slicing
M, m0ninv, mBits,
s0, window,
acc, acc_len, e)

View File

@ -54,6 +54,9 @@ func shrLarge(r {.noalias.}: var openArray[SecretWord], a: openArray[SecretWord]
func shrWords(r {.noalias.}: var openArray[SecretWord], a: openArray[SecretWord], w: SomeInteger) =
## Shift right by w word
if w >= a.len:
r.setZero()
return
for i in 0 ..< a.len-w:
r[i] = a[i+w]
@ -84,6 +87,68 @@ func shiftRight_vartime*(r {.noalias.}: var openArray[SecretWord], a: openArray[
else:
r.shrLarge(a, w, shift)
func shlSmall(r: var openArray[SecretWord], a: openArray[SecretWord], k: SomeInteger) =
## Compute the `shift left` operation of x and k
##
## k MUST be less than the base word size (2^32 or 2^64)
r[0] = a[0] shl k
for i in 1 ..< a.len:
r[i] = (a[i] shl k) or (a[i-1] shr (WordBitWidth - k))
for i in a.len ..< r.len:
r[i] = Zero
func shlLarge(r: var openArray[SecretWord], a: openArray[SecretWord], w, shift: SomeInteger) =
## Shift left by `w` words + `shift` bits
## Assumes `r` is 0 initialized
if w >= a.len:
return
r[w] = a[0] shl shift
for i in 1+w ..< r.len:
r[i] = (a[i-w] shl shift) or (a[i-w-1] shr (WordBitWidth - shift))
for i in a.len-w ..< r.len:
r[i] = Zero
func shlWords(r: var openArray[SecretWord], a: openArray[SecretWord], w: SomeInteger) =
## Shift left by w word
if w >= r.len:
r.setZero()
return
for i in 0 ..< w:
r[i] = Zero
for i in w ..< a.len+w:
r[i] = a[i-w]
for i in a.len+w ..< r.len:
r[i] = Zero
func shiftLeft_vartime*(r: var openArray[SecretWord], a: openArray[SecretWord], k: SomeInteger) {.meter.} =
## Shift `a` left by k bits and store in `r`
if k == 0:
let min = min(a.len, r.len)
for i in 0 ..< min:
r[i] = a[i]
for i in min ..< r.len:
r[i] = Zero
return
if k < WordBitWidth:
r.shlSmall(a, k)
return
# w = k div WordBitWidth, shift = k mod WordBitWidth
let w = k shr static(log2_vartime(uint32(WordBitWidth)))
let shift = k and (WordBitWidth - 1)
if shift == 0:
r.shlWords(a, w)
else:
r.shlLarge(a, w, shift)
# Arithmetic
# --------------------------------------------------------

View File

@ -67,6 +67,16 @@ const
One* = SecretWord(1)
MaxWord* = SecretWord(high(BaseType))
func bytesRequired*(bits: int): int {.inline.} =
## Compute the number of limbs required
## from the **announced** bit length
# bits.ceilDiv_vartime(WordBitWidth)
# with guarantee to avoid division (especially at compile-time)
const bitsInByte = 8
const divShiftor = log2_vartime(uint32 bitsInByte)
result = (bits + bitsInByte - 1) shr divShiftor
func wordsRequired*(bits: int): int {.inline.} =
## Compute the number of limbs required
## from the **announced** bit length

View File

@ -0,0 +1,122 @@
# Constantine
# Copyright (c) 2018-2019 Status Research & Development GmbH
# Copyright (c) 2020-Present Mamy André-Ratsimbazafy
# Licensed and distributed under either of
# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except according to those terms.
# ############################################################
#
# Variable-time extended-precision
# compiler intrinsics
#
# ############################################################
import ../abstractions
func div2n1n_nim_vartime[T: SomeUnsignedInt](q, r: var T, n_hi, n_lo, d: T) {.tags:[VarTime].}=
## Division uint128 by uint64
## Warning ⚠️ :
## - if n_hi == d, quotient does not fit in an uint64 and will throw SIGFPE
## - if n_hi > d result is undefined
# doAssert leadingZeros(d) == 0, "Divisor was not normalized"
const
size = sizeof(q) * 8
halfSize = size div 2
halfMask = (1.T shl halfSize) - 1.T
func halfQR(n_hi, n_lo, d, d_hi, d_lo: T): tuple[q, r: T] {.nimcall.} =
var (q, r) = (n_hi div d_hi, n_hi mod d_hi)
let m = q * d_lo
r = (r shl halfSize) or n_lo
# Fix the reminder, we're at most 2 iterations off
if r < m:
dec q
r += d
if r >= d and r < m:
dec q
r += d
r -= m
(q, r)
let
d_hi = d shr halfSize
d_lo = d and halfMask
n_lohi = n_lo shr halfSize
n_lolo = n_lo and halfMask
# First half of the quotient
let (q1, r1) = halfQR(n_hi, n_lohi, d, d_hi, d_lo)
# Second half
let (q2, r2) = halfQR(r1, n_lolo, d, d_hi, d_lo)
q = (q1 shl halfSize) or q2
r = r2
when sizeof(int) == 8 and defined(vcc):
func udiv128_vartime(highDividend, lowDividend, divisor: uint64, remainder: var uint64): uint64 {.importc:"_udiv128", header: "<intrin.h>", nodecl, tags:[VarTime].}
## Division 128 by 64, Microsoft only, 64-bit only,
## returns quotient as return value remainder as var parameter
## Warning ⚠️ :
## - if n_hi == d, quotient does not fit in an uint64 and will throw SIGFPE
## - if n_hi > d result is undefined
func div2n1n_128_vartime(q, r: var uint64, n_hi, n_lo, d: uint64) {.inline.}=
## Division uint128 by uint64
## Warning ⚠️ :
## - if n_hi == d, quotient does not fit in an uint64 and will throw SIGFPE
## - if n_hi > d result is undefined
q = udiv128_vartime(n_hi, n_lo, d, r)
elif sizeof(int) == 8 and GCC_Compatible:
type
uint128{.importc: "unsigned __int128".} = object
const
newerNim = (NimMajor, NimMinor) > (1, 6)
noExplicitPtrDeref = defined(cpp) or newerNim
func div2n1n_128_vartime(q, r: var uint64, n_hi, n_lo, d: uint64) {.inline, tags:[VarTime].}=
## Division uint128 by uint64
## Warning ⚠️ :
## - if n_hi == d, quotient does not fit in an uint64 and will throw SIGFPE on some platforms
## - if n_hi > d result is undefined
var dblPrec {.noinit.}: uint128
{.emit:[dblPrec, " = (unsigned __int128)", n_hi," << 64 | (unsigned __int128)",n_lo,";"].}
# Don't forget to dereference the var param in C mode
when noExplicitPtrDeref:
{.emit:[q, " = (NU64)(", dblPrec," / ", d, ");"].}
{.emit:[r, " = (NU64)(", dblPrec," % ", d, ");"].}
else:
{.emit:["*",q, " = (NU64)(", dblPrec," / ", d, ");"].}
{.emit:["*",r, " = (NU64)(", dblPrec," % ", d, ");"].}
func div2n1n_vartime*(q, r: var SecretWord, n_hi, n_lo, d: SecretWord) {.inline.} =
## Division uint128 by uint64
## Warning ⚠️ :
## - if n_hi == d, quotient does not fit in an uint64 and will throw SIGFPE
## - if n_hi > d result is undefined
##
## To avoid issues, n_hi, n_lo, d should be normalized.
## i.e. shifted (== multiplied by the same power of 2)
## so that the most significant bit in d is set.
when sizeof(int) == 4:
let dividend = (uint64(n_hi) shl 32) or uint64(n_lo)
let divisor = uint64(d)
q = uint32(dividend div divisor)
r = uint32(dividend mod divisor)
when nimvm:
div2n1n_nim_vartime(BaseType q, BaseType r, BaseType n_hi, BaseType n_lo, BaseType d)
else:
when declared(div2n1n_128_vartime):
div2n1n_128_vartime(BaseType q, BaseType r, BaseType n_hi, BaseType n_lo, BaseType d)
else:
div2n1n_nim_vartime(BaseType q, BaseType r, BaseType n_hi, BaseType n_lo, BaseType d)

View File

@ -3,17 +3,20 @@
import
../../constantine/math/[
arithmetic,
io/io_bigints]
io/io_bigints],
../../constantine/math_arbitrary_precision/arithmetic/limbs_divmod_vartime
let a = BigInt[64].fromUint(0xa0e5cb56a1c08396'u64)
let M = BigInt[64].fromUint(0xae57180eceb0206f'u64)
var r: BigInt[64]
var r, r2: BigInt[64]
r.reduce(a, M)
doAssert r2.limbs.reduce_vartime(a.limbs, M.limbs)
let rU64 = 0xa0e5cb56a1c08396'u64 mod 0xae57180eceb0206f'u64
echo r.toHex()
# echo r.toHex()
doAssert rU64 == a.limbs[0].uint64
doAssert bool(a == r)
doAssert bool(a == r)
echo "SUCCESS: t_bigints_mod.nim"

View File

@ -15,6 +15,7 @@ import
../../constantine/math/[arithmetic, io/io_bigints],
../../constantine/platforms/primitives,
../../constantine/serialization/codecs,
../../constantine/math_arbitrary_precision/arithmetic/limbs_divmod_vartime,
# Test utilities
../../helpers/prng_unsafe
@ -126,8 +127,9 @@ proc main() =
# Modulus
mpz_mod(r, a, m)
var rTest: BigInt[mBits]
var rTest, rTest_vartime: BigInt[mBits]
rTest.reduce(aTest, mTest)
doAssert rTest_vartime.limbs.reduce_vartime(aTest.limbs, mTest.limbs)
#########################################################
# Check
@ -139,8 +141,9 @@ proc main() =
var rGMP: array[mLen, byte]
discard mpz_export(rGMP[0].addr, rW.addr, GMP_MostSignificantWordFirst, 1, GMP_WordNativeEndian, 0, r)
var rConstantine: array[mLen, byte]
var rConstantine, rCttVartime: array[mLen, byte]
marshal(rConstantine, rTest, bigEndian)
marshal(rCttVartime, rTest_vartime, bigEndian)
# echo "rGMP: ", rGMP.toHex()
# echo "rConstantine: ", rConstantine.toHex()
@ -158,4 +161,20 @@ proc main() =
" Constantine: " & rConstantine.toHex() & "\n" &
"(Note that GMP aligns bytes left while constantine aligns bytes right)"
doAssert rGMP.toOpenArray(0, rW-1) == rCttVartime.toOpenArray(mLen-rW, mLen-1), block:
# Reexport as bigEndian for debugging
discard mpz_export(aBuf[0].addr, aW.addr, GMP_MostSignificantWordFirst, 1, GMP_WordNativeEndian, 0, a)
discard mpz_export(mBuf[0].addr, mW.addr, GMP_MostSignificantWordFirst, 1, GMP_WordNativeEndian, 0, m)
"\nModulus with operands\n" &
" a (" & align($aBits, 4) & "-bit): " & aBuf.toHex & "\n" &
" m (" & align($mBits, 4) & "-bit): " & mBuf.toHex & "\n" &
"failed:" & "\n" &
" GMP: " & rGMP.toHex() & "\n" &
" Constantine: " & rCttVartime.toHex() & "\n" &
"(Note that GMP aligns bytes left while constantine aligns bytes right)"
mpz_clear(a)
mpz_clear(m)
mpz_clear(r)
main()

View File

@ -7,8 +7,9 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
../../constantine/math_arbitrary_precision/arithmetic/[bigints_views, limbs_views, limbs_divmod],
../../constantine/math_arbitrary_precision/arithmetic/[bigints_views, limbs_views],
../../constantine/platforms/abstractions,
../../constantine/serialization/codecs,
../../helpers/prng_unsafe,
std/[times, strformat],
@ -74,23 +75,11 @@ proc test(rng: var RngState) =
ee.mpz_import(eLen, GMP_MostSignificantWordFirst, sizeof(byte), GMP_WordNativeEndian, 0, e[0].addr)
mm.mpz_import(mLen, GMP_LeastSignificantWordFirst, sizeof(SecretWord), GMP_WordNativeEndian, 0, M[0].addr)
debug:
echo "----------------------------------------------"
echo " a (Ctt): ", a.toString()
echo " a (GMP): ", aa.toHex()
echo " e (Ctt): ", e.toHex()
echo " e (GMP): ", ee.toHex()
echo " M (Ctt): ", M.toString()
echo " M (GMP): ", mm.toHex()
rr.mpz_powm(aa, ee, mm)
var rWritten: csize
discard rGMP[0].addr.mpz_export(rWritten.addr, GMP_LeastSignificantWordFirst, sizeof(SecretWord), GMP_WordNativeEndian, 0, rr)
debug:
echo " r (GMP): ", rr.toHex()
mpz_clear(aa)
mpz_clear(ee)
mpz_clear(mm)
@ -103,15 +92,18 @@ proc test(rng: var RngState) =
rCtt.powMod_vartime(a, e, M, window = 4)
debug:
echo " r (GMP): " & rGMP.toString()
echo " r (Ctt): " & rCtt.toString()
doAssert (seq[BaseType])(rGMP) == (seq[BaseType])(rCtt), block:
"Failure with sizes:\n" &
"\nModular exponentiation failure:\n" &
&" a.len (word): {a.len:>3}, a.bits: {aBits:>4}\n" &
&" e.len (byte): {e.len:>3}, e.bits: {eBits:>4}\n" &
&" M.len (word): {M.len:>3}, M.bits: {mBits:>4}\n"
&" M.len (word): {M.len:>3}, M.bits: {mBits:>4}\n" &
" ------------------------------------------------\n" &
&" a: {aa.toHex()}\n" &
&" e: {ee.toHex()}\n" &
&" M: {mm.toHex()}\n" &
" ------------------------------------------------\n" &
&" r (GMP): {rGMP.toString()}\n" &
&" r (Ctt): {rCtt.toString()}\n"
for _ in 0 ..< Iters:

View File

@ -0,0 +1 @@
-d:CTT_DEBUG

View File

@ -206,4 +206,423 @@ suite "EVM ModExp precompile (EIP-198)":
0xa0]
var r = newSeq[byte](1)
let status = r.eth_evm_modexp(input)
doAssert status == cttEVM_Success
test "Audit #6 - DOS Vector 1":
let input = [
# Length of base (32)
uint8 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
# Length of exponent (32)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
# Length of modulus (32)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
# Base (96064778440517843452771003943013638877275214272712651271554889917016327417616)
0xd4, 0x62, 0xbc, 0xde, 0x8f, 0x57, 0xb0, 0x4a, 0x3f, 0xe1, 0x16, 0xc8, 0x12, 0x8c, 0x44, 0x34,
0xcf, 0x10, 0x25, 0x2e, 0x48, 0xa3, 0xcc, 0x0d, 0x28, 0xdf, 0x2b, 0xac, 0x4a, 0x8d, 0x6f, 0x10,
# Exponent (96064778440517843452771003943013638877275214272712651271554889917016327417616)
0xd4, 0x62, 0xbc, 0xde, 0x8f, 0x57, 0xb0, 0x4a, 0x3f, 0xe1, 0x16, 0xc8, 0x12, 0x8c, 0x44, 0x34,
0xcf, 0x10, 0x25, 0x2e, 0x48, 0xa3, 0xcc, 0x0d, 0x28, 0xdf, 0x2b, 0xac, 0x4a, 0x8d, 0x6f, 0x10,
# Modulus (57896044618658097711785492504343953926634992332820282019728792003956564819968)
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]
var r = newSeq[byte](32)
let status = r.eth_evm_modexp(input)
doAssert status == cttEVM_Success
doAssert r == @[byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
test "Audit #6 - DOS Vector 2":
let input = [
# Length of base (1)
uint8 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
# Length of exponent (1)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
# Length of modulus (121)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79,
# Base
0x33,
# Exponent
0x01,
# Modulus
0x04, 0xea, 0xbb, 0x12, 0x55, 0x88, 0xd7, 0x3c, 0xad, 0x22, 0xea, 0x2b, 0x4a, 0x77, 0x6e, 0x9d,
0x4d, 0xfc, 0x13, 0xa8, 0x1b, 0xf9, 0x0c, 0x0d, 0x37, 0xe8, 0x4e, 0x8b, 0xeb, 0xb2, 0xa5, 0x48,
0x8b, 0x2c, 0x87, 0x6d, 0x13, 0x51, 0x75, 0xeb, 0x97, 0xc6, 0x13, 0xd9, 0x06, 0xce, 0x8b, 0x53,
0xd0, 0x02, 0x68, 0xb8, 0xd6, 0x12, 0xab, 0x8b, 0x15, 0x0c, 0xef, 0x0a, 0xd0, 0x3b, 0x73, 0xd2,
0xdb, 0x9d, 0x2a, 0xa5, 0x23, 0x70, 0xdc, 0x26, 0x55, 0x80, 0xca, 0xf2, 0xc0, 0x18, 0xe3, 0xe3,
0x1b, 0xad, 0xd5, 0x22, 0xdd, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x1c, 0x05, 0x71, 0x52, 0x7c, 0x3a, 0xb0, 0x77,
]
var r = newSeq[byte](121)
let status = r.eth_evm_modexp(input)
doAssert status == cttEVM_Success
doAssert r == @[byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51]
test "Audit #6 - DOS Vector 2.a - shortcuttable with even modulus":
let input = [
# Length of base (1)
uint8 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
# Length of exponent (1)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
# Length of modulus (121)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79,
# Base
0x33,
# Exponent
0x01,
# Modulus
0x04, 0xea, 0xbb, 0x12, 0x55, 0x88, 0xd7, 0x3c, 0xad, 0x22, 0xea, 0x2b, 0x4a, 0x77, 0x6e, 0x9d,
0x4d, 0xfc, 0x13, 0xa8, 0x1b, 0xf9, 0x0c, 0x0d, 0x37, 0xe8, 0x4e, 0x8b, 0xeb, 0xb2, 0xa5, 0x48,
0x8b, 0x2c, 0x87, 0x6d, 0x13, 0x51, 0x75, 0xeb, 0x97, 0xc6, 0x13, 0xd9, 0x06, 0xce, 0x8b, 0x53,
0xd0, 0x02, 0x68, 0xb8, 0xd6, 0x12, 0xab, 0x8b, 0x15, 0x0c, 0xef, 0x0a, 0xd0, 0x3b, 0x73, 0xd2,
0xdb, 0x9d, 0x2a, 0xa5, 0x23, 0x70, 0xdc, 0x26, 0x55, 0x80, 0xca, 0xf2, 0xc0, 0x18, 0xe3, 0xe3,
0x1b, 0xad, 0xd5, 0x22, 0xdd, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x1c, 0x05, 0x71, 0x52, 0x7c, 0x3a, 0xb0, 0x76,
]
var r = newSeq[byte](121)
let status = r.eth_evm_modexp(input)
doAssert status == cttEVM_Success
doAssert r == @[byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51]
test "Audit #6 - DOS Vector 2.b - odd modulus with no shortcut":
let input = [
# Length of base (1)
uint8 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
# Length of exponent (1)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
# Length of modulus (121)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79,
# Base
0x33,
# Exponent
0x10,
# Modulus
0x04, 0xea, 0xbb, 0x12, 0x55, 0x88, 0xd7, 0x3c, 0xad, 0x22, 0xea, 0x2b, 0x4a, 0x77, 0x6e, 0x9d,
0x4d, 0xfc, 0x13, 0xa8, 0x1b, 0xf9, 0x0c, 0x0d, 0x37, 0xe8, 0x4e, 0x8b, 0xeb, 0xb2, 0xa5, 0x48,
0x8b, 0x2c, 0x87, 0x6d, 0x13, 0x51, 0x75, 0xeb, 0x97, 0xc6, 0x13, 0xd9, 0x06, 0xce, 0x8b, 0x53,
0xd0, 0x02, 0x68, 0xb8, 0xd6, 0x12, 0xab, 0x8b, 0x15, 0x0c, 0xef, 0x0a, 0xd0, 0x3b, 0x73, 0xd2,
0xdb, 0x9d, 0x2a, 0xa5, 0x23, 0x70, 0xdc, 0x26, 0x55, 0x80, 0xca, 0xf2, 0xc0, 0x18, 0xe3, 0xe3,
0x1b, 0xad, 0xd5, 0x22, 0xdd, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x1c, 0x05, 0x71, 0x52, 0x7c, 0x3a, 0xb0, 0x77,
]
var r = newSeq[byte](121)
let status = r.eth_evm_modexp(input)
doAssert status == cttEVM_Success
doAssert r == @[byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 196, 178, 252, 11, 73, 111, 4, 209, 77, 144, 65]
test "Audit #6 - DOS Vector 2.c - odd modulus with no shortcut":
let input = [
# Length of base (1)
uint8 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
# Length of exponent (1)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
# Length of modulus (121)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79,
# Base
0x33,
# Exponent
0x07,
# Modulus
0x04, 0xea, 0xbb, 0x12, 0x55, 0x88, 0xd7, 0x3c, 0xad, 0x22, 0xea, 0x2b, 0x4a, 0x77, 0x6e, 0x9d,
0x4d, 0xfc, 0x13, 0xa8, 0x1b, 0xf9, 0x0c, 0x0d, 0x37, 0xe8, 0x4e, 0x8b, 0xeb, 0xb2, 0xa5, 0x48,
0x8b, 0x2c, 0x87, 0x6d, 0x13, 0x51, 0x75, 0xeb, 0x97, 0xc6, 0x13, 0xd9, 0x06, 0xce, 0x8b, 0x53,
0xd0, 0x02, 0x68, 0xb8, 0xd6, 0x12, 0xab, 0x8b, 0x15, 0x0c, 0xef, 0x0a, 0xd0, 0x3b, 0x73, 0xd2,
0xdb, 0x9d, 0x2a, 0xa5, 0x23, 0x70, 0xdc, 0x26, 0x55, 0x80, 0xca, 0xf2, 0xc0, 0x18, 0xe3, 0xe3,
0x1b, 0xad, 0xd5, 0x22, 0xdd, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x1c, 0x05, 0x71, 0x52, 0x7c, 0x3a, 0xb0, 0x77,
]
var r = newSeq[byte](121)
let status = r.eth_evm_modexp(input)
doAssert status == cttEVM_Success
doAssert r == @[byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 241, 216, 60, 91]
test "Audit #6 - DOS Vector 2.d - power-of-2 modulus with no shortcut":
let input = [
# Length of base (1)
uint8 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
# Length of exponent (1)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
# Length of modulus (121)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79,
# Base
0x33,
# Exponent
0x07,
# Modulus
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]
var r = newSeq[byte](121)
let status = r.eth_evm_modexp(input)
doAssert status == cttEVM_Success
doAssert r == @[byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 241, 216, 60, 91]
test "Audit #5 - Modified padded exponent":
let input = [
# Length of base (1)
uint8 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
# Length of exponent (1)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
# Length of modulus (1)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
# Base
0x06,
# Exponent
0x00, 0x00, 0x02,
# Modulus
0x04
]
var r = newSeq[byte](1)
let status = r.eth_evm_modexp(input)
doAssert status == cttEVM_Success
doAssert r[0] == 0, ". Result was " & $r[0]
test "Audit #5-2 - Modified padded exponent":
let input = [
# Length of base (1)
uint8 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
# Length of exponent (5)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A,
# Length of modulus (1)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
# Base
0x3a,
# Exponent
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
# Modulus
0x08
]
var r = newSeq[byte](1)
let status = r.eth_evm_modexp(input)
doAssert status == cttEVM_Success
doAssert r[0] == 0, ". Result was " & $r[0]
test "Audit #5-3 - Modified padded exponent":
let input = [
# Length of base (1)
uint8 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
# Length of exponent (2)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
# Length of modulus (9)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09,
# Base
0x02,
# Exponent
0x00, 0x02, 0x65,
# Modulus
0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x98
]
var r = newSeq[byte](9)
let status = r.eth_evm_modexp(input)
doAssert status == cttEVM_Success
doAssert r == @[byte 0, 0, 1, 45, 106, 227, 225, 162, 136], ". Result was " & $r
test "Audit #5-4 - Modified padded exponent":
var input = [
# Length of base
uint8 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c,
# Length of exponent
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09,
# Length of modulus
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b,
# Base
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
# Exponent
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0,
# Modulus
0x17, 0xc6, 0xab, 0xaa, 0x3f, 0x00, 0xe5, 0xc0, 0x5b, 0x75, 0x74, 0xcb,
0xcf, 0x2a, 0x44, 0xd4, 0x3a, 0xca, 0x4a, 0xc0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]
var r = newSeq[byte](0x2b)
let status = eth_evm_modexp(r, input)
doAssert status == cttEVM_Success
doAssert r == @[byte 10, 141, 74, 46, 2, 18, 2, 37, 247, 220, 246, 65, 109, 246, 7, 144, 85, 202, 194, 191, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], ". Result was " & $r
test "Audit #8 - Modified padded exponent":
let input = [
# Length of base (24)
uint8 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
# Length of exponent (36)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25,
# Length of modulus (56)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38,
# Base
0x07, 0x19, 0x2b, 0x95, 0xff, 0xc8, 0xda, 0x78, 0x63, 0x10, 0x11, 0xed, 0x6b, 0x24, 0xcd, 0xd5,
0x73, 0xf9, 0x77, 0xa1, 0x1e, 0x79, 0x48, 0x11,
# Exponent
0x00,
0x03, 0x67, 0x68, 0x54, 0xfe, 0x24, 0x14, 0x1c, 0xb9, 0x8f, 0xe6, 0xd4, 0xb2, 0x0d, 0x02, 0xb4,
0x51, 0x6f, 0xf7, 0x02, 0x35, 0x0e, 0xdd, 0xb0, 0x82, 0x67, 0x79, 0xc8, 0x13, 0xf0, 0xdf, 0x45,
0xbe, 0x81, 0x12, 0xf4,
# Modulus
0x1a, 0xbf, 0x81, 0x1f, 0x86, 0xe1, 0x02, 0x78, 0x66, 0xe4, 0x23, 0x65, 0x49, 0x0f, 0x8d, 0x6e,
0xc2, 0x23, 0x94, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]
var r = newSeq[byte](56)
let status = r.eth_evm_modexp(input)
doAssert status == cttEVM_Success
test "Audit #18 - Modified padded exponent":
let input = [
# Base length
uint8 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
# Exponent length
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0xa8, 0xff,
# Modulus length
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0xc1, 0x00, 0x00, 0x00, 0x51, 0x00, 0x9b, 0x9b,
0x00, 0x00,
0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
0x9b, 0x9b, 0x9b, 0x9b, 0x00, 0x50, 0x50, 0x50,
0x50, 0x50, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
0x9b, 0x9b, 0xbc, 0x9b, 0xa0, 0x9b, 0x9b, 0x9b,
0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
0x9b, 0x9b, 0x9b, 0x9b, 0x00, 0x50, 0x50, 0x50,
0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50,
0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50,
0x50, 0x50, 0x50, 0x50, 0x50, 0x00, 0x00, 0x00,
0xa0]
var r = newSeq[byte](1)
let status = r.eth_evm_modexp(input)
doAssert status == cttEVM_Success