Add bigint isZero, and equality and initial tests
This commit is contained in:
parent
3034c07525
commit
2c750cbc5b
|
@ -51,6 +51,17 @@ func wordsRequired(bits: int): int {.compileTime.}=
|
|||
|
||||
type
|
||||
BigInt*[bits: static int] = object
|
||||
## Fixed-precision big integer
|
||||
##
|
||||
## "limbs" is an internal field that holds the internal representation
|
||||
## of the big integer. This internal representation can be changed
|
||||
## without notice and should not be used by external applications or libraries.
|
||||
# Constantine BigInt have a word-size chosen to minimize bigint memory usage
|
||||
# while allowing carry-less operations in a machine-efficient type like uint32
|
||||
# uint64 or uint128 if available.
|
||||
# In practice the word size is 63-bit.
|
||||
#
|
||||
# "Limb-endianess" is little-endian (least significant limb at BigInt.limbs[0])
|
||||
limbs*: array[bits.wordsRequired, Word]
|
||||
|
||||
const MaxWord* = (not Ct[uint64](0)) shr 1
|
||||
|
@ -71,38 +82,40 @@ template `[]=`*(a: var Bigint, idx: int, w: Word) =
|
|||
#
|
||||
# ############################################################
|
||||
|
||||
# The primitives all accept a control input that indicates
|
||||
# TODO: {.inline.} analysis
|
||||
|
||||
func isZero*(a: BigInt): CTBool[Word] {.raises: [].} =
|
||||
## Returns if a big int is equal to zero
|
||||
var accum: Word
|
||||
for i in static(0 ..< a.limbs.len):
|
||||
accum = accum or a.limbs[i]
|
||||
result = accum.isZero()
|
||||
|
||||
func `==`*(a, b: BigInt): CTBool[Word] {.raises: [].}=
|
||||
## Returns true if 2 big ints are equal
|
||||
var accum: Word
|
||||
for i in static(0 ..< a.limbs.len):
|
||||
accum = accum or (a.limbs[i] xor b.limbs[i])
|
||||
result = accum.isZero
|
||||
|
||||
# The arithmetic primitives all accept a control input that indicates
|
||||
# if it is a placebo operation. It stills performs the
|
||||
# same memory accesses to be side-channel attack resistant
|
||||
# same memory accesses to be side-channel attack resistant.
|
||||
|
||||
# For efficiency we can define templates and will create functions
|
||||
# specialised for runtime and compile-time inputs.
|
||||
#
|
||||
# We don't specialise for the control word, any optimizing compiler
|
||||
# will keep it in registers.
|
||||
|
||||
template addImpl[bits](result: CTBool[Word], a: var BigInt[bits], b: BigInt[bits], ctl: CTBool[Word]) =
|
||||
## Constant-time big integer in-place addition
|
||||
## Returns if addition carried
|
||||
func add*[bits](a: var BigInt[bits], b: BigInt[bits], ctl: CTBool[Word]): CTBool[Word] {.raises: [].}=
|
||||
## Constant-time big integer in-place optional addition
|
||||
## The addition is only performed if ctl is "true"
|
||||
## The result carry is always computed.
|
||||
for i in static(0 ..< a.limbs.len):
|
||||
let new_a = a.limbs[i] + b.limbs[i] + Word(result)
|
||||
result = new_a.isMsbSet()
|
||||
a[i] = ctl.mux(new_a and MaxWord, a)
|
||||
a[i] = ctl.mux(new_a and MaxWord, a[i])
|
||||
|
||||
func add*[bits](a: var BigInt[bits], b: BigInt[bits], ctl: CTBool[Word]): CTBool[Word] =
|
||||
## Constant-time big integer in-place addition
|
||||
## Returns the "carry flag"
|
||||
result.addImpl(a, b, ctl)
|
||||
|
||||
template subImpl[bits](result: CTBool[Word], a: var BigInt[bits], b: BigInt[bits], ctl: CTBool[Word]) =
|
||||
## Constant-time big integer in-place substraction
|
||||
## Returns the "borrow flag"
|
||||
func sub*[bits](a: var BigInt[bits], b: BigInt[bits], ctl: CTBool[Word]): CTBool[Word] {.raises: [].}=
|
||||
## Constant-time big integer in-place optional substraction
|
||||
## The substraction is only performed if ctl is "true"
|
||||
## The result carry is always computed.
|
||||
for i in static(0 ..< a.limbs.len):
|
||||
let new_a = a.limbs[i] - b.limbs[i] - Word(result)
|
||||
result = new_a.isMsbSet()
|
||||
a[i] = ctl.mux(new_a and MaxWord, a)
|
||||
|
||||
func sub*[bits](a: var BigInt[bits], b: BigInt[bits], ctl: CTBool[Word]): CTBool[Word] =
|
||||
## Constant-time big integer in-place addition
|
||||
## Returns the "carry flag"
|
||||
result.subImpl(a, b, ctl)
|
||||
a[i] = ctl.mux(new_a and MaxWord, a[i])
|
||||
|
|
|
@ -8,4 +8,5 @@
|
|||
|
||||
import
|
||||
test_word_types,
|
||||
test_io
|
||||
test_io,
|
||||
test_bigints
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
# 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 unittest, random,
|
||||
../constantine/[io, bigints, word_types]
|
||||
|
||||
suite "isZero":
|
||||
test "isZero for zero":
|
||||
var x: BigInt[128]
|
||||
check: x.isZero().bool
|
||||
test "isZero for non-zero":
|
||||
block:
|
||||
var x = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
|
||||
check: not x.isZero().bool
|
||||
block:
|
||||
var x = fromHex(BigInt[128], "0x00000000_00000001_00000000_00000000")
|
||||
check: not x.isZero().bool
|
||||
block:
|
||||
var x = fromHex(BigInt[128], "0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF")
|
||||
check: not x.isZero().bool
|
||||
|
||||
suite "Arithmetic operations - Addition":
|
||||
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.add(b, ctrue(Word))
|
||||
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.add(b, ctrue(Word))
|
||||
|
||||
let c = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
|
||||
check:
|
||||
bool(a == c)
|
||||
block:
|
||||
var a = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
|
||||
let b = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000000")
|
||||
let carry = a.add(b, ctrue(Word))
|
||||
|
||||
let c = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
|
||||
check:
|
||||
bool(a == c)
|
||||
|
||||
test "Adding 1 zero - fake addition":
|
||||
block:
|
||||
var a = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000000")
|
||||
let b = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
|
||||
let carry = a.add(b, cfalse(Word))
|
||||
|
||||
let c = a
|
||||
check:
|
||||
bool(a == c)
|
||||
block:
|
||||
var a = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
|
||||
let b = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000000")
|
||||
let carry = a.add(b, cfalse(Word))
|
||||
|
||||
let c = a
|
||||
check:
|
||||
bool(a == c)
|
||||
|
||||
test "Adding non-zeros - real addition":
|
||||
block:
|
||||
var a = fromHex(BigInt[128], "0x00000000_00000001_00000000_00000000")
|
||||
let b = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
|
||||
let carry = a.add(b, ctrue(Word))
|
||||
|
||||
let c = fromHex(BigInt[128], "0x00000000_00000001_00000000_00000001")
|
||||
check:
|
||||
bool(a == c)
|
||||
block:
|
||||
var a = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
|
||||
let b = fromHex(BigInt[128], "0x00000000_00000001_00000000_00000000")
|
||||
let carry = a.add(b, ctrue(Word))
|
||||
|
||||
let c = fromHex(BigInt[128], "0x00000000_00000001_00000000_00000001")
|
||||
check:
|
||||
bool(a == c)
|
||||
|
||||
test "Adding non-zeros - fake addition":
|
||||
block:
|
||||
var a = fromHex(BigInt[128], "0x00000000_00000001_00000000_00000000")
|
||||
let b = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
|
||||
let carry = a.add(b, cfalse(Word))
|
||||
|
||||
let c = a
|
||||
check:
|
||||
bool(a == c)
|
||||
block:
|
||||
var a = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
|
||||
let b = fromHex(BigInt[128], "0x00000000_00000001_00000000_00000000")
|
||||
let carry = a.add(b, cfalse(Word))
|
||||
|
||||
let c = a
|
||||
check:
|
||||
bool(a == c)
|
Loading…
Reference in New Issue