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
|
type
|
||||||
BigInt*[bits: static int] = object
|
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]
|
limbs*: array[bits.wordsRequired, Word]
|
||||||
|
|
||||||
const MaxWord* = (not Ct[uint64](0)) shr 1
|
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
|
# 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
|
func add*[bits](a: var BigInt[bits], b: BigInt[bits], ctl: CTBool[Word]): CTBool[Word] {.raises: [].}=
|
||||||
# specialised for runtime and compile-time inputs.
|
## Constant-time big integer in-place optional addition
|
||||||
#
|
## The addition is only performed if ctl is "true"
|
||||||
# We don't specialise for the control word, any optimizing compiler
|
## The result carry is always computed.
|
||||||
# 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
|
|
||||||
for i in static(0 ..< a.limbs.len):
|
for i in static(0 ..< a.limbs.len):
|
||||||
let new_a = a.limbs[i] + b.limbs[i] + Word(result)
|
let new_a = a.limbs[i] + b.limbs[i] + Word(result)
|
||||||
result = new_a.isMsbSet()
|
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] =
|
func sub*[bits](a: var BigInt[bits], b: BigInt[bits], ctl: CTBool[Word]): CTBool[Word] {.raises: [].}=
|
||||||
## Constant-time big integer in-place addition
|
## Constant-time big integer in-place optional substraction
|
||||||
## Returns the "carry flag"
|
## The substraction is only performed if ctl is "true"
|
||||||
result.addImpl(a, b, ctl)
|
## The result carry is always computed.
|
||||||
|
|
||||||
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"
|
|
||||||
for i in static(0 ..< a.limbs.len):
|
for i in static(0 ..< a.limbs.len):
|
||||||
let new_a = a.limbs[i] - b.limbs[i] - Word(result)
|
let new_a = a.limbs[i] - b.limbs[i] - Word(result)
|
||||||
result = new_a.isMsbSet()
|
result = new_a.isMsbSet()
|
||||||
a[i] = ctl.mux(new_a and MaxWord, a)
|
a[i] = ctl.mux(new_a and MaxWord, a[i])
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
|
@ -8,4 +8,5 @@
|
||||||
|
|
||||||
import
|
import
|
||||||
test_word_types,
|
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