Add bigint isZero, and equality and initial tests

This commit is contained in:
Mamy André-Ratsimbazafy 2020-02-08 18:50:01 +01:00
parent 3034c07525
commit 2c750cbc5b
No known key found for this signature in database
GPG Key ID: 7B88AD1FE79492E1
3 changed files with 145 additions and 27 deletions

View File

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

View File

@ -8,4 +8,5 @@
import
test_word_types,
test_io
test_io,
test_bigints

104
tests/test_bigints.nim Normal file
View File

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