mirror of
https://github.com/status-im/constantine.git
synced 2025-02-21 16:38:24 +00:00
Constant time (#185)
* Implement fully constant-time division closes #2 closes #9 * constant-time hex parsing * prevent cache timing attacks in toHex() conversion (which is only for test/debug purposes anyway)
This commit is contained in:
parent
ffacf61e8a
commit
26954f905a
@ -157,7 +157,7 @@ func validate_sig*(signature: Signature): CttBLSStatus =
|
||||
func serialize_secret_key*(dst: var array[32, byte], secret_key: SecretKey): CttBLSStatus =
|
||||
## Serialize a secret key
|
||||
## Returns cttBLS_Success if successful
|
||||
dst.exportRawUint(secret_key.raw, bigEndian)
|
||||
dst.marshal(secret_key.raw, bigEndian)
|
||||
return cttBLS_Success
|
||||
|
||||
func serialize_public_key_compressed*(dst: var array[48, byte], public_key: PublicKey): CttBLSStatus =
|
||||
@ -170,7 +170,7 @@ func serialize_public_key_compressed*(dst: var array[48, byte], public_key: Publ
|
||||
dst[0] = byte 0b11000000 # Compressed + Infinity
|
||||
return cttBLS_Success
|
||||
|
||||
dst.exportRawUint(public_key.raw.x, bigEndian)
|
||||
dst.marshal(public_key.raw.x, bigEndian)
|
||||
# The curve equation has 2 solutions for y² = x³ + 4 with y unknown and x known
|
||||
# The lexicographically largest will have bit 381 set to 1
|
||||
# (and bit 383 for the compressed representation)
|
||||
@ -191,8 +191,8 @@ func serialize_signature_compressed*(dst: var array[96, byte], signature: Signat
|
||||
dst[0] = byte 0b11000000 # Compressed + Infinity
|
||||
return cttBLS_Success
|
||||
|
||||
dst.toOpenArray(0, 48-1).exportRawUint(signature.raw.x.c1, bigEndian)
|
||||
dst.toOpenArray(48, 96-1).exportRawUint(signature.raw.x.c0, bigEndian)
|
||||
dst.toOpenArray(0, 48-1).marshal(signature.raw.x.c1, bigEndian)
|
||||
dst.toOpenArray(48, 96-1).marshal(signature.raw.x.c0, bigEndian)
|
||||
|
||||
let isLexicographicallyLargest =
|
||||
if signature.raw.y.c1.isZero().bool():
|
||||
@ -208,7 +208,7 @@ func deserialize_secret_key*(dst: var SecretKey, src: array[32, byte]): CttBLSSt
|
||||
##
|
||||
## This is protected against side-channel unless your key is invalid.
|
||||
## In that case it will like whether it's all zeros or larger than the curve order.
|
||||
dst.raw.fromRawUint(src, bigEndian)
|
||||
dst.raw.unmarshal(src, bigEndian)
|
||||
let status = validate_seckey(dst)
|
||||
if status != cttBLS_Success:
|
||||
dst.raw.setZero()
|
||||
@ -240,7 +240,7 @@ func deserialize_public_key_compressed_unchecked*(dst: var PublicKey, src: array
|
||||
|
||||
# General case
|
||||
var t{.noInit.}: matchingBigInt(BLS12_381)
|
||||
t.fromRawUint(src, bigEndian)
|
||||
t.unmarshal(src, bigEndian)
|
||||
t.limbs[^1] = t.limbs[^1] and (MaxWord shr 3) # The first 3 bytes contain metadata to mask out
|
||||
|
||||
if bool(t >= BLS12_381.Mod()):
|
||||
@ -294,7 +294,7 @@ func deserialize_signature_compressed_unchecked*(dst: var Signature, src: array[
|
||||
|
||||
# General case
|
||||
var t{.noInit.}: matchingBigInt(BLS12_381)
|
||||
t.fromRawUint(src.toOpenArray(0, 48-1), bigEndian)
|
||||
t.unmarshal(src.toOpenArray(0, 48-1), bigEndian)
|
||||
t.limbs[^1] = t.limbs[^1] and (MaxWord shr 3) # The first 3 bytes contain metadata to mask out
|
||||
|
||||
if bool(t >= BLS12_381.Mod()):
|
||||
@ -303,7 +303,7 @@ func deserialize_signature_compressed_unchecked*(dst: var Signature, src: array[
|
||||
var x{.noInit.}: Fp2[BLS12_381]
|
||||
x.c1.fromBig(t)
|
||||
|
||||
t.fromRawUint(src.toOpenArray(48, 96-1), bigEndian)
|
||||
t.unmarshal(src.toOpenArray(48, 96-1), bigEndian)
|
||||
if bool(t >= BLS12_381.Mod()):
|
||||
return cttBLS_CoordinateGreaterOrEqualThanModulus
|
||||
|
||||
|
@ -43,7 +43,7 @@ func parseRawUint(
|
||||
## Return false if the integer is larger than the field modulus.
|
||||
## Returns true on success.
|
||||
var big {.noInit.}: BigInt[254]
|
||||
big.fromRawUint(src, bigEndian)
|
||||
big.unmarshal(src, bigEndian)
|
||||
|
||||
if not bool(big < Mod(BN254_Snarks)):
|
||||
return cttEVM_IntLargerThanModulus
|
||||
@ -136,10 +136,10 @@ func eth_evm_ecadd*(
|
||||
var aff{.noInit.}: ECP_ShortW_Aff[Fp[BN254_Snarks], G1]
|
||||
aff.affine(R)
|
||||
|
||||
r.toOpenArray(0, 31).exportRawUint(
|
||||
r.toOpenArray(0, 31).marshal(
|
||||
aff.x, bigEndian
|
||||
)
|
||||
r.toOpenArray(32, 63).exportRawUint(
|
||||
r.toOpenArray(32, 63).marshal(
|
||||
aff.y, bigEndian
|
||||
)
|
||||
|
||||
@ -185,7 +185,7 @@ func eth_evm_ecmul*(
|
||||
|
||||
var smod{.noInit.}: Fr[BN254_Snarks]
|
||||
var s{.noInit.}: BigInt[256]
|
||||
s.fromRawUint(padded.toOpenArray(64,95), bigEndian)
|
||||
s.unmarshal(padded.toOpenArray(64,95), bigEndian)
|
||||
|
||||
when true:
|
||||
# The spec allows s to be bigger than the curve order r and the field modulus p.
|
||||
@ -210,10 +210,10 @@ func eth_evm_ecmul*(
|
||||
var aff{.noInit.}: ECP_ShortW_Aff[Fp[BN254_Snarks], G1]
|
||||
aff.affine(P)
|
||||
|
||||
r.toOpenArray(0, 31).exportRawUint(
|
||||
r.toOpenArray(0, 31).marshal(
|
||||
aff.x, bigEndian
|
||||
)
|
||||
r.toOpenArray(32, 63).exportRawUint(
|
||||
r.toOpenArray(32, 63).marshal(
|
||||
aff.y, bigEndian
|
||||
)
|
||||
|
||||
|
@ -223,7 +223,7 @@ func hashToField*[Field; B1, B2, B3: byte|char, count: static int](
|
||||
template tv: untyped = uniform_bytes.toOpenArray(elm_offset, elm_offset + L-1)
|
||||
|
||||
var big2x {.noInit.}: BigInt[2 * getCurveBitwidth(Field.C)]
|
||||
big2x.fromRawUint(tv, bigEndian)
|
||||
big2x.unmarshal(tv, bigEndian)
|
||||
|
||||
# Reduces modulo p and output in Montgomery domain
|
||||
when m == 1:
|
||||
|
@ -114,7 +114,7 @@ func powMontUnsafeExponent*[mBits: static int](
|
||||
var scratchSpace {.noInit.}: array[scratchLen, Limbs[mBits.wordsRequired]]
|
||||
powMontUnsafeExponent(a.limbs, exponent, M.limbs, one.limbs, negInvModWord, scratchSpace, spareBits)
|
||||
|
||||
from ../io/io_bigints import exportRawUint
|
||||
from ../io/io_bigints import marshal
|
||||
# Workaround recursive dependencies
|
||||
|
||||
func powMont*[mBits, eBits: static int](
|
||||
@ -132,7 +132,7 @@ func powMont*[mBits, eBits: static int](
|
||||
## This is constant-time: the window optimization does
|
||||
## not reveal the exponent bits or hamming weight
|
||||
var expBE {.noInit.}: array[(ebits + 7) div 8, byte]
|
||||
expBE.exportRawUint(exponent, bigEndian)
|
||||
expBE.marshal(exponent, bigEndian)
|
||||
|
||||
powMont(a, expBE, M, one, negInvModWord, windowSize, spareBits)
|
||||
|
||||
@ -155,7 +155,7 @@ func powMontUnsafeExponent*[mBits, eBits: static int](
|
||||
## This uses fixed window optimization
|
||||
## A window size in the range [1, 5] must be chosen
|
||||
var expBE {.noInit.}: array[(ebits + 7) div 8, byte]
|
||||
expBE.exportRawUint(exponent, bigEndian)
|
||||
expBE.marshal(exponent, bigEndian)
|
||||
|
||||
powMontUnsafeExponent(a, expBE, M, one, negInvModWord, windowSize, spareBits)
|
||||
|
||||
|
@ -367,7 +367,7 @@ func div10*(a: var Limbs): SecretWord =
|
||||
# Normalize
|
||||
hi = (hi shl clz) or (lo shr (WordBitWidth - clz))
|
||||
lo = lo shl clz
|
||||
unsafeDiv2n1n(a[i], result, hi, lo, norm10)
|
||||
div2n1n(a[i], result, hi, lo, norm10)
|
||||
# Undo normalization
|
||||
result = result shr clz
|
||||
|
||||
|
@ -165,7 +165,7 @@ func shlAddMod_estimate(a: LimbsViewMut, aLen: int,
|
||||
# Get a quotient q, at most we will be 2 iterations off
|
||||
# from the true quotient
|
||||
var q, r: SecretWord
|
||||
unsafeDiv2n1n(q, r, a0, a1, m0) # Estimate quotient
|
||||
div2n1n(q, r, a0, a1, m0) # Estimate quotient
|
||||
q = mux( # If n_hi == divisor
|
||||
a0 == m0, MaxWord, # Quotient == MaxWord (0b1111...1111)
|
||||
mux(
|
||||
@ -226,7 +226,7 @@ func shlAddMod(a: LimbsViewMut, aLen: int,
|
||||
let m0 = M[0] shl (WordBitWidth-R)
|
||||
|
||||
var q, r: SecretWord
|
||||
unsafeDiv2n1n(q, r, hi, lo, m0) # (hi, lo) mod M
|
||||
div2n1n(q, r, hi, lo, m0) # (hi, lo) mod M
|
||||
|
||||
a[0] = r shr (WordBitWidth-R)
|
||||
|
||||
|
@ -661,7 +661,7 @@ func powMont*(
|
||||
## - On input ``a`` is the base, on ``output`` a = a^exponent (mod M)
|
||||
## ``a`` is in the Montgomery domain
|
||||
## - ``exponent`` is the exponent in big-endian canonical format (octet-string)
|
||||
## Use ``exportRawUint`` for conversion
|
||||
## Use ``marshal`` for conversion
|
||||
## - ``M`` is the modulus
|
||||
## - ``one`` is 1 (mod M) in montgomery representation
|
||||
## - ``m0ninv`` is the montgomery magic constant "-1/M[0] mod 2^WordBitWidth"
|
||||
|
@ -69,7 +69,7 @@ declareCurves:
|
||||
# -----------------------------------------------------------------------------
|
||||
curve P224: # NIST P-224
|
||||
bitwidth: 224
|
||||
modulus: "0xffffffff_ffffffff_ffffffff_ffffffff_00000000_00000000_00000001"
|
||||
modulus: "0xffffffffffffffffffffffffffffffff000000000000000000000001"
|
||||
curve BN254_Nogami: # Integer Variable χ–Based Ate Pairing, 2008, Nogami et al
|
||||
bitwidth: 254
|
||||
modulus: "0x2523648240000001ba344d80000000086121000000000013a700000000000013"
|
||||
@ -173,7 +173,7 @@ declareCurves:
|
||||
modulus: "0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff"
|
||||
curve Secp256k1: # Bitcoin curve
|
||||
bitwidth: 256
|
||||
modulus: "0xFFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2F"
|
||||
modulus: "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"
|
||||
curve BLS12_377:
|
||||
# Zexe curve
|
||||
# (p41) https://eprint.iacr.org/2018/962.pdf
|
||||
|
@ -380,7 +380,7 @@ func primeMinus2_BE*[bits: static int](
|
||||
var tmp = P
|
||||
discard tmp.sub(2)
|
||||
|
||||
result.exportRawUint(tmp, bigEndian)
|
||||
result.marshal(tmp, bigEndian)
|
||||
|
||||
func primePlus1div2*(P: BigInt): BigInt =
|
||||
## Compute (P+1)/2, assumes P is odd
|
||||
@ -415,7 +415,7 @@ func primeMinus1div2_BE*[bits: static int](
|
||||
discard tmp.sub(1)
|
||||
tmp.shiftRight(1)
|
||||
|
||||
result.exportRawUint(tmp, bigEndian)
|
||||
result.marshal(tmp, bigEndian)
|
||||
|
||||
func primeMinus3div4_BE*[bits: static int](
|
||||
P: BigInt[bits]
|
||||
@ -434,7 +434,7 @@ func primeMinus3div4_BE*[bits: static int](
|
||||
discard tmp.sub(3)
|
||||
tmp.shiftRight(2)
|
||||
|
||||
result.exportRawUint(tmp, bigEndian)
|
||||
result.marshal(tmp, bigEndian)
|
||||
|
||||
func primeMinus5div8_BE*[bits: static int](
|
||||
P: BigInt[bits]
|
||||
@ -453,7 +453,7 @@ func primeMinus5div8_BE*[bits: static int](
|
||||
discard tmp.sub(5)
|
||||
tmp.shiftRight(3)
|
||||
|
||||
result.exportRawUint(tmp, bigEndian)
|
||||
result.marshal(tmp, bigEndian)
|
||||
|
||||
func primePlus1Div4_BE*[bits: static int](
|
||||
P: BigInt[bits]
|
||||
@ -475,14 +475,14 @@ func primePlus1Div4_BE*[bits: static int](
|
||||
# then divide by 2
|
||||
tmp.shiftRight(1)
|
||||
|
||||
result.exportRawUint(tmp, bigEndian)
|
||||
result.marshal(tmp, bigEndian)
|
||||
|
||||
func toCanonicalIntRepr*[bits: static int](
|
||||
a: BigInt[bits]
|
||||
): array[(bits+7) div 8, byte] {.noInit.} =
|
||||
## Export a bigint to its canonical BigEndian representation
|
||||
## (octet-string)
|
||||
result.exportRawUint(a, bigEndian)
|
||||
result.marshal(a, bigEndian)
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
|
@ -29,20 +29,24 @@ type
|
||||
|
||||
|
||||
debug:
|
||||
import std/strutils
|
||||
func toHex*(a: SecretWord): string =
|
||||
const hexChars = "0123456789abcdef"
|
||||
const L = 2*sizeof(SecretWord)
|
||||
result = newString(2 + L)
|
||||
result[0] = '0'
|
||||
result[1] = 'x'
|
||||
var a = a
|
||||
for j in countdown(L-1, 0):
|
||||
result[j] = hexChars.secretLookup(a and SecretWord 0xF)
|
||||
a = a shr 4
|
||||
|
||||
func toString*(a: Limbs): string =
|
||||
result = "["
|
||||
result.add " 0x" & toHex(BaseType(a[0]))
|
||||
result.add " " & toHex(a[0])
|
||||
for i in 1 ..< a.len:
|
||||
result.add ", 0x" & toHex(BaseType(a[i]))
|
||||
result.add ", " & toHex(a[i])
|
||||
result.add "])"
|
||||
|
||||
func toHex*(a: Limbs): string =
|
||||
result = "0x"
|
||||
for i in countdown(a.len-1, 0):
|
||||
result.add toHex(BaseType(a[i]))
|
||||
|
||||
func `$`*(a: BigInt): string =
|
||||
result = "BigInt["
|
||||
result.add $BigInt.bits
|
||||
|
@ -224,7 +224,7 @@ func scalarMulGeneric*[EC](P: var EC, scalar: BigInt, window: static int = 5) =
|
||||
var
|
||||
scratchSpace: array[1 shl window, EC]
|
||||
scalarCanonicalBE: array[(scalar.bits+7) div 8, byte] # canonical big endian representation
|
||||
scalarCanonicalBE.exportRawUint(scalar, bigEndian) # Export is constant-time
|
||||
scalarCanonicalBE.marshal(scalar, bigEndian) # Export is constant-time
|
||||
P.scalarMulGeneric(scalarCanonicalBE, scratchSpace)
|
||||
|
||||
func scalarMul*[EC](
|
||||
|
@ -184,5 +184,5 @@ func powUnsafeExponent*[F; bits: static int](
|
||||
## - power analysis
|
||||
## - timing analysis
|
||||
var expBE {.noInit.}: array[(bits + 7) div 8, byte]
|
||||
expBE.exportRawUint(exponent, bigEndian)
|
||||
expBE.marshal(exponent, bigEndian)
|
||||
a.powUnsafeExponent(expBE, window)
|
||||
|
@ -17,7 +17,7 @@ Constant-time APIs only leak the number of bits, of bytes or words of the
|
||||
big integer.
|
||||
|
||||
The bytes API are constant-time and do not allocate:
|
||||
- BigInt or octet-string: fromRawUint, fromUint
|
||||
- BigInt or octet-string: unmarshal, fromUint
|
||||
- Machine sized integers: fromUint
|
||||
|
||||
If you decide to use the internal hex or decimal API, you SHOULD ensure that the data is well-formatted:
|
||||
@ -33,13 +33,13 @@ The internal API is may be constant-time (temporarily) and may allocate.
|
||||
The hexadecimal API allocates:
|
||||
- `toHex` is constant-time
|
||||
- `appendHex` is constant-time
|
||||
- `fromHex` is **NOT constant-time** (yet), it is intended for debugging or
|
||||
- `fromHex` is constant-time, it is intended for debugging or
|
||||
(compile-time) configuration. It does not allocate.
|
||||
In particular it scans spaces and underscores and checks if the string
|
||||
starts with '0x'.
|
||||
|
||||
The decimal API allocates:
|
||||
- `toDecimal` is **NOT constant-time** (yet) and allocates
|
||||
- `toDecimal` is constant-time and allocates
|
||||
- `fromDecimal` is constant-time and does not allocate.
|
||||
|
||||
## Avoiding secret mistakes
|
||||
|
@ -36,7 +36,7 @@ export BigInt, wordsRequired
|
||||
# prototyping, research and debugging purposes,
|
||||
# and can use exceptions.
|
||||
|
||||
func fromRawUintLE(
|
||||
func unmarshalLE(
|
||||
dst: var BigInt,
|
||||
src: openarray[byte]) =
|
||||
## Parse an unsigned integer from its canonical
|
||||
@ -74,7 +74,7 @@ func fromRawUintLE(
|
||||
for i in dst_idx + 1 ..< dst.limbs.len:
|
||||
dst.limbs[i] = Zero
|
||||
|
||||
func fromRawUintBE(
|
||||
func unmarshalBE(
|
||||
dst: var BigInt,
|
||||
src: openarray[byte]) =
|
||||
## Parse an unsigned integer from its canonical
|
||||
@ -114,7 +114,7 @@ func fromRawUintBE(
|
||||
for i in dst_idx + 1 ..< dst.limbs.len:
|
||||
dst.limbs[i] = Zero
|
||||
|
||||
func fromRawUint*(
|
||||
func unmarshal*(
|
||||
dst: var BigInt,
|
||||
src: openarray[byte],
|
||||
srcEndianness: static Endianness) =
|
||||
@ -129,11 +129,11 @@ func fromRawUint*(
|
||||
## from a canonical integer representation
|
||||
|
||||
when srcEndianness == littleEndian:
|
||||
dst.fromRawUintLE(src)
|
||||
dst.unmarshalLE(src)
|
||||
else:
|
||||
dst.fromRawUintBE(src)
|
||||
dst.unmarshalBE(src)
|
||||
|
||||
func fromRawUint*(
|
||||
func unmarshal*(
|
||||
T: type BigInt,
|
||||
src: openarray[byte],
|
||||
srcEndianness: static Endianness): T {.inline.}=
|
||||
@ -146,21 +146,21 @@ func fromRawUint*(
|
||||
##
|
||||
## Can work at compile-time to embed curve moduli
|
||||
## from a canonical integer representation
|
||||
result.fromRawUint(src, srcEndianness)
|
||||
result.unmarshal(src, srcEndianness)
|
||||
|
||||
func fromUint*(
|
||||
T: type BigInt,
|
||||
src: SomeUnsignedInt): T {.inline.}=
|
||||
## Parse a regular unsigned integer
|
||||
## and store it into a BigInt of size `bits`
|
||||
result.fromRawUint(cast[array[sizeof(src), byte]](src), cpuEndian)
|
||||
result.unmarshal(cast[array[sizeof(src), byte]](src), cpuEndian)
|
||||
|
||||
func fromUint*(
|
||||
dst: var BigInt,
|
||||
src: SomeUnsignedInt) {.inline.}=
|
||||
## Parse a regular unsigned integer
|
||||
## and store it into a BigInt of size `bits`
|
||||
dst.fromRawUint(cast[array[sizeof(src), byte]](src), cpuEndian)
|
||||
dst.unmarshal(cast[array[sizeof(src), byte]](src), cpuEndian)
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
@ -180,7 +180,7 @@ template blobFrom(dst: var openArray[byte], src: SomeUnsignedInt, startIdx: int,
|
||||
for i in 0 ..< sizeof(src):
|
||||
dst[startIdx+sizeof(src)-1-i] = toByte(src shr (i * 8))
|
||||
|
||||
func exportRawUintLE(
|
||||
func marshalLE(
|
||||
dst: var openarray[byte],
|
||||
src: BigInt) =
|
||||
## Serialize a bigint into its canonical little-endian representation
|
||||
@ -229,7 +229,7 @@ func exportRawUintLE(
|
||||
dst[dst_idx+i] = toByte(lo shr ((tail-i)*8))
|
||||
return
|
||||
|
||||
func exportRawUintBE(
|
||||
func marshalBE(
|
||||
dst: var openarray[byte],
|
||||
src: BigInt) =
|
||||
## Serialize a bigint into its canonical big-endian representation
|
||||
@ -281,7 +281,7 @@ func exportRawUintBE(
|
||||
dst[tail-1-i] = toByte(lo shr ((tail-i)*8))
|
||||
return
|
||||
|
||||
func exportRawUint*(
|
||||
func marshal*(
|
||||
dst: var openarray[byte],
|
||||
src: BigInt,
|
||||
dstEndianness: static Endianness) =
|
||||
@ -301,9 +301,9 @@ func exportRawUint*(
|
||||
zeroMem(dst, dst.len)
|
||||
|
||||
when dstEndianness == littleEndian:
|
||||
exportRawUintLE(dst, src)
|
||||
marshalLE(dst, src)
|
||||
else:
|
||||
exportRawUintBE(dst, src)
|
||||
marshalBE(dst, src)
|
||||
|
||||
{.pop.} # {.push raises: [].}
|
||||
|
||||
@ -312,54 +312,23 @@ func exportRawUint*(
|
||||
# Conversion helpers
|
||||
#
|
||||
# ############################################################
|
||||
# TODO: constant-time
|
||||
|
||||
func readHexChar(c: char): uint8 {.inline.}=
|
||||
func readHexChar(c: char): SecretWord {.inline.}=
|
||||
## Converts an hex char to an int
|
||||
## CT: leaks position of invalid input if any.
|
||||
## and leaks if 0..9 or a..f or A..F
|
||||
case c
|
||||
of '0'..'9': result = uint8 ord(c) - ord('0')
|
||||
of 'a'..'f': result = uint8 ord(c) - ord('a') + 10
|
||||
of 'A'..'F': result = uint8 ord(c) - ord('A') + 10
|
||||
else:
|
||||
raise newException(ValueError, $c & "is not a hexadecimal character")
|
||||
template sw(a: char or int): SecretWord = SecretWord(a)
|
||||
const k = WordBitWidth - 1
|
||||
|
||||
func skipPrefixes(current_idx: var int, str: string, radix: static range[2..16]) {.inline.} =
|
||||
## Returns the index of the first meaningful char in `hexStr` by skipping
|
||||
## "0x" prefix
|
||||
## CT:
|
||||
## - leaks if input length < 2
|
||||
## - leaks if input start with 0x, 0o or 0b prefix
|
||||
let c = sw(c)
|
||||
|
||||
if str.len < 2:
|
||||
return
|
||||
let lowercaseMask = not -(((c - sw'a') or (sw('f') - c)) shr k)
|
||||
let uppercaseMask = not -(((c - sw'A') or (sw('F') - c)) shr k)
|
||||
|
||||
var val = c - sw'0'
|
||||
val = val xor ((val xor (c - sw('a') + sw(10))) and lowercaseMask)
|
||||
val = val xor ((val xor (c - sw('a') + sw(10))) and uppercaseMask)
|
||||
val = val and sw(0xF) # Prevent overflow of invalid inputs
|
||||
|
||||
assert current_idx == 0, "skipPrefixes only works for prefixes (position 0 and 1 of the string)"
|
||||
if str[0] == '0':
|
||||
case str[1]
|
||||
of {'x', 'X'}:
|
||||
assert radix == 16, "Parsing mismatch, 0x prefix is only valid for a hexadecimal number (base 16)"
|
||||
current_idx = 2
|
||||
of {'o', 'O'}:
|
||||
assert radix == 8, "Parsing mismatch, 0o prefix is only valid for an octal number (base 8)"
|
||||
current_idx = 2
|
||||
of {'b', 'B'}:
|
||||
assert radix == 2, "Parsing mismatch, 0b prefix is only valid for a binary number (base 2)"
|
||||
current_idx = 2
|
||||
else: discard
|
||||
|
||||
func countNonBlanks(hexStr: string, startPos: int): int =
|
||||
## Count the number of non-blank characters
|
||||
## ' ' (space) and '_' (underscore) are considered blank
|
||||
##
|
||||
## CT:
|
||||
## - Leaks white-spaces and non-white spaces position
|
||||
const blanks = {' ', '_'}
|
||||
|
||||
for c in hexStr:
|
||||
if c in blanks:
|
||||
result += 1
|
||||
return val
|
||||
|
||||
func hexToPaddedByteArray*(hexStr: string, output: var openArray[byte], order: static[Endianness]) =
|
||||
## Read a hex string and store it in a byte array `output`.
|
||||
@ -367,31 +336,42 @@ func hexToPaddedByteArray*(hexStr: string, output: var openArray[byte], order: s
|
||||
##
|
||||
## The source string must be hex big-endian.
|
||||
## The destination array can be big or little endian
|
||||
##
|
||||
## Only characters accepted are 0x or 0X prefix
|
||||
## and 0-9,a-f,A-F in particular spaces and _ are not valid.
|
||||
##
|
||||
## Procedure is constant-time except for the presence (or absence) of the 0x prefix.
|
||||
##
|
||||
## This procedure is intended for configuration, prototyping, research and debugging purposes.
|
||||
## You MUST NOT use it for production.
|
||||
|
||||
template sw(a: bool or int): SecretWord = SecretWord(a)
|
||||
|
||||
var
|
||||
skip = 0
|
||||
skip = Zero
|
||||
dstIdx: int
|
||||
shift = 4
|
||||
skipPrefixes(skip, hexStr, 16)
|
||||
|
||||
const blanks = {' ', '_'}
|
||||
let nonBlanksCount = countNonBlanks(hexStr, skip)
|
||||
|
||||
if hexStr.len >= 2:
|
||||
skip = sw(2)*(
|
||||
sw(hexStr[0] == '0') and
|
||||
(sw(hexStr[1] == 'x') or sw(hexStr[1] == 'X'))
|
||||
)
|
||||
|
||||
let maxStrSize = output.len * 2
|
||||
let size = hexStr.len - skip - nonBlanksCount
|
||||
let size = hexStr.len - skip.int
|
||||
|
||||
doAssert size <= maxStrSize, "size: " & $size & " (without blanks or prefix), maxSize: " & $maxStrSize
|
||||
doAssert size <= maxStrSize, "size: " & $size & ", maxSize: " & $maxStrSize
|
||||
|
||||
if size < maxStrSize:
|
||||
# include extra byte if odd length
|
||||
dstIdx = output.len - (size + 1) div 2
|
||||
dstIdx = output.len - (size + 1) shr 1
|
||||
# start with shl of 4 if length is even
|
||||
shift = 4 - size mod 2 * 4
|
||||
shift = 4 - (size and 1) * 4
|
||||
|
||||
for srcIdx in skip ..< hexStr.len:
|
||||
if hexStr[srcIdx] in blanks:
|
||||
continue
|
||||
|
||||
let nibble = hexStr[srcIdx].readHexChar shl shift
|
||||
for srcIdx in skip.int ..< hexStr.len:
|
||||
let c = hexStr[srcIdx]
|
||||
let nibble = byte(c.readHexChar() shl shift)
|
||||
when order == bigEndian:
|
||||
output[dstIdx] = output[dstIdx] or nibble
|
||||
else:
|
||||
@ -409,11 +389,13 @@ func nativeEndianToHex(bytes: openarray[byte], order: static[Endianness]): strin
|
||||
result[1] = 'x'
|
||||
for i in 0 ..< bytes.len:
|
||||
when order == system.cpuEndian:
|
||||
result[2 + 2*i] = hexChars[int bytes[i] shr 4 and 0xF]
|
||||
result[2 + 2*i+1] = hexChars[int bytes[i] and 0xF]
|
||||
let bi = bytes[i]
|
||||
result[2 + 2*i] = hexChars.secretLookup(SecretWord bi shr 4 and 0xF)
|
||||
result[2 + 2*i+1] = hexChars.secretLookup(SecretWord bi and 0xF)
|
||||
else:
|
||||
result[2 + 2*i] = hexChars[int bytes[bytes.high - i] shr 4 and 0xF]
|
||||
result[2 + 2*i+1] = hexChars[int bytes[bytes.high - i] and 0xF]
|
||||
let bmi = bytes[bytes.high - i]
|
||||
result[2 + 2*i] = hexChars.secretLookup(SecretWord bmi shr 4 and 0xF)
|
||||
result[2 + 2*i+1] = hexChars.secretLookup(SecretWord bmi and 0xF)
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
@ -429,8 +411,10 @@ func fromHex*(a: var BigInt, s: string) =
|
||||
##
|
||||
## Hex string is assumed big-endian
|
||||
##
|
||||
## This API is intended for configuration and debugging purposes
|
||||
## Do not pass secret or private data to it.
|
||||
## Procedure is constant-time except for the presence (or absence) of the 0x prefix.
|
||||
##
|
||||
## This procedure is intended for configuration, prototyping, research and debugging purposes.
|
||||
## You MUST NOT use it for production.
|
||||
##
|
||||
## Can work at compile-time to declare curve moduli from their hex strings
|
||||
|
||||
@ -440,7 +424,7 @@ func fromHex*(a: var BigInt, s: string) =
|
||||
hexToPaddedByteArray(s, bytes, bigEndian)
|
||||
|
||||
# 2. Convert canonical uint to Big Int
|
||||
a.fromRawUint(bytes, bigEndian)
|
||||
a.unmarshal(bytes, bigEndian)
|
||||
|
||||
func fromHex*(T: type BigInt, s: string): T {.noInit.} =
|
||||
## Convert a hex string to BigInt that can hold
|
||||
@ -450,8 +434,10 @@ func fromHex*(T: type BigInt, s: string): T {.noInit.} =
|
||||
##
|
||||
## Hex string is assumed big-endian
|
||||
##
|
||||
## This API is intended for configuration and debugging purposes
|
||||
## Do not pass secret or private data to it.
|
||||
## Procedure is constant-time except for the presence (or absence) of the 0x prefix.
|
||||
##
|
||||
## This procedure is intended for configuration, prototyping, research and debugging purposes.
|
||||
## You MUST NOT use it for production.
|
||||
##
|
||||
## Can work at compile-time to declare curve moduli from their hex strings
|
||||
result.fromHex(s)
|
||||
@ -468,11 +454,13 @@ func appendHex*(dst: var string, big: BigInt, order: static Endianness = bigEndi
|
||||
##
|
||||
## This is useful to reduce the number of allocations when serializing
|
||||
## Fp towers
|
||||
##
|
||||
## This function may allocate.
|
||||
|
||||
# 1. Convert Big Int to canonical uint
|
||||
const canonLen = (big.bits + 8 - 1) div 8
|
||||
var bytes: array[canonLen, byte]
|
||||
exportRawUint(bytes, big, cpuEndian)
|
||||
marshal(bytes, big, cpuEndian)
|
||||
|
||||
# 2 Convert canonical uint to hex
|
||||
dst.add bytes.nativeEndianToHex(order)
|
||||
@ -646,8 +634,6 @@ const log10_2_Denom = 42039
|
||||
#
|
||||
# 1 and 2 is solved by precomputing the length and make the number of add be fixed.
|
||||
# 3 is easily solved by doing "digitToPrint + ord('0')" instead
|
||||
#
|
||||
# For 4 for now we use non-constant-time division (TODO)
|
||||
|
||||
func decimalLength(bits: static int): int =
|
||||
doAssert bits < (high(uint) div log10_2_Num),
|
||||
@ -659,10 +645,11 @@ func decimalLength(bits: static int): int =
|
||||
func toDecimal*(a: BigInt): string =
|
||||
## Convert to a decimal string.
|
||||
##
|
||||
## It is intended for configuration, prototyping, research and debugging purposes.
|
||||
## This procedure is intended for configuration, prototyping, research and debugging purposes.
|
||||
## You MUST NOT use it for production.
|
||||
##
|
||||
## This function is NOT constant-time at the moment.
|
||||
## This function is constant-time.
|
||||
## This function does heap-allocation.
|
||||
const len = decimalLength(BigInt.bits)
|
||||
result = newString(len)
|
||||
|
||||
|
@ -27,7 +27,7 @@ func fromUint*(dst: var FF,
|
||||
src: SomeUnsignedInt) =
|
||||
## Parse a regular unsigned integer
|
||||
## and store it into a Fp or Fr
|
||||
let raw {.noinit.} = (typeof dst.mres).fromRawUint(cast[array[sizeof(src), byte]](src), cpuEndian)
|
||||
let raw {.noinit.} = (typeof dst.mres).unmarshal(cast[array[sizeof(src), byte]](src), cpuEndian)
|
||||
dst.fromBig(raw)
|
||||
|
||||
func fromInt*(dst: var FF,
|
||||
@ -42,11 +42,11 @@ func fromInt*(dst: var FF,
|
||||
let isNeg = SecretBool((src shr msb_pos) and 1)
|
||||
|
||||
let src = isNeg.mux(SecretWord -src, SecretWord src)
|
||||
let raw {.noinit.} = (type dst.mres).fromRawUint(cast[array[sizeof(src), byte]](src), cpuEndian)
|
||||
let raw {.noinit.} = (type dst.mres).unmarshal(cast[array[sizeof(src), byte]](src), cpuEndian)
|
||||
dst.fromBig(raw)
|
||||
dst.cneg(isNeg)
|
||||
|
||||
func exportRawUint*(dst: var openarray[byte],
|
||||
func marshal*(dst: var openarray[byte],
|
||||
src: FF,
|
||||
dstEndianness: static Endianness) =
|
||||
## Serialize a finite field element to its canonical big-endian or little-endian
|
||||
@ -58,7 +58,7 @@ func exportRawUint*(dst: var openarray[byte],
|
||||
## If the buffer is bigger, output will be zero-padded left for big-endian
|
||||
## or zero-padded right for little-endian.
|
||||
## I.e least significant bit is aligned to buffer boundary
|
||||
exportRawUint(dst, src.toBig(), dstEndianness)
|
||||
marshal(dst, src.toBig(), dstEndianness)
|
||||
|
||||
func appendHex*(dst: var string, f: FF, order: static Endianness = bigEndian) =
|
||||
## Stringify a finite field element to hex.
|
||||
|
@ -297,7 +297,7 @@ iterator unpack(scalarByte: byte): bool =
|
||||
|
||||
func cyclotomic_exp*[FT](r: var FT, a: FT, exponent: BigInt, invert: bool) {.meter.} =
|
||||
var eBytes: array[(exponent.bits+7) div 8, byte]
|
||||
eBytes.exportRawUint(exponent, bigEndian)
|
||||
eBytes.marshal(exponent, bigEndian)
|
||||
|
||||
r.setOne()
|
||||
for b in eBytes:
|
||||
|
@ -23,23 +23,6 @@ import
|
||||
#
|
||||
# ############################################################
|
||||
|
||||
func unsafeDiv2n1n*(q, r: var Ct[uint32], n_hi, n_lo, d: Ct[uint32]) {.inline.}=
|
||||
## Division uint64 by uint32
|
||||
## Warning ⚠️ :
|
||||
## - if n_hi == d, quotient does not fit in an uint32
|
||||
## - 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.
|
||||
# TODO !!! - Replace by constant-time, portable, non-assembly version
|
||||
# -> use uint128? Compiler might add unwanted branches
|
||||
{.warning: "unsafeDiv2n1n is not constant-time at the moment on most hardware".}
|
||||
let dividend = (uint64(n_hi) shl 32) or uint64(n_lo)
|
||||
let divisor = uint64(d)
|
||||
q = (Ct[uint32])(dividend div divisor)
|
||||
r = (Ct[uint32])(dividend mod divisor)
|
||||
|
||||
func mul*(hi, lo: var Ct[uint32], a, b: Ct[uint32]) {.inline.} =
|
||||
## Extended precision multiplication
|
||||
## (hi, lo) <- a*b
|
||||
@ -96,15 +79,14 @@ func smul*(hi, lo: var Ct[uint32], a, b: Ct[uint32]) {.inline.} =
|
||||
|
||||
when sizeof(int) == 8:
|
||||
when defined(vcc):
|
||||
from ./extended_precision_x86_64_msvc import unsafeDiv2n1n, mul, muladd1, muladd2, smul
|
||||
from ./extended_precision_x86_64_msvc import mul, muladd1, muladd2, smul
|
||||
elif GCCCompatible:
|
||||
# TODO: constant-time div2n1n
|
||||
when X86:
|
||||
from ./extended_precision_x86_64_gcc import unsafeDiv2n1n
|
||||
from ./extended_precision_64bit_uint128 import mul, muladd1, muladd2, smul
|
||||
else:
|
||||
from ./extended_precision_64bit_uint128 import unsafeDiv2n1n, mul, muladd1, muladd2, smul
|
||||
export unsafeDiv2n1n, mul, muladd1, muladd2, smul
|
||||
from ./extended_precision_64bit_uint128 import mul, muladd1, muladd2, smul
|
||||
export mul, muladd1, muladd2, smul
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
|
@ -18,24 +18,6 @@ static:
|
||||
doAssert GCC_Compatible
|
||||
doAssert sizeof(int) == 8
|
||||
|
||||
func unsafeDiv2n1n*(q, r: var Ct[uint64], n_hi, n_lo, d: Ct[uint64]) {.inline.}=
|
||||
## 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
|
||||
{.warning: "unsafeDiv2n1n is not constant-time at the moment on most hardware".}
|
||||
|
||||
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 defined(cpp):
|
||||
{.emit:[q, " = (NU64)(", dblPrec," / ", d, ");"].}
|
||||
{.emit:[r, " = (NU64)(", dblPrec," % ", d, ");"].}
|
||||
else:
|
||||
{.emit:["*",q, " = (NU64)(", dblPrec," / ", d, ");"].}
|
||||
{.emit:["*",r, " = (NU64)(", dblPrec," % ", d, ");"].}
|
||||
|
||||
func mul*(hi, lo: var Ct[uint64], a, b: Ct[uint64]) {.inline.} =
|
||||
## Extended precision multiplication
|
||||
## (hi, lo) <- a*b
|
||||
|
@ -1,60 +0,0 @@
|
||||
# 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 ../constant_time/ct_types
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
# Extended precision primitives for X86-64 on GCC & Clang
|
||||
#
|
||||
# ############################################################
|
||||
|
||||
static:
|
||||
doAssert(defined(gcc) or defined(clang) or defined(llvm_gcc))
|
||||
doAssert sizeof(int) == 8
|
||||
doAssert X86
|
||||
|
||||
func unsafeDiv2n1n*(q, r: var Ct[uint64], n_hi, n_lo, d: Ct[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
|
||||
{.warning: "unsafeDiv2n1n is not constant-time at the moment on most hardware".}
|
||||
|
||||
# TODO !!! - Replace by constant-time, portable, non-assembly version
|
||||
# -> use uint128? Compiler might add unwanted branches
|
||||
|
||||
# DIV r/m64
|
||||
# Divide RDX:RAX (n_hi:n_lo) by r/m64
|
||||
#
|
||||
# Inputs
|
||||
# - numerator high word in RDX,
|
||||
# - numerator low word in RAX,
|
||||
# - divisor as r/m parameter (register or memory at the compiler discretion)
|
||||
# Result
|
||||
# - Quotient in RAX
|
||||
# - Remainder in RDX
|
||||
|
||||
# 1. name the register/memory "divisor"
|
||||
# 2. don't forget to dereference the var hidden pointer
|
||||
# 3. -
|
||||
# 4. no clobbered registers beside explicitly used RAX and RDX
|
||||
when defined(cpp):
|
||||
asm """
|
||||
divq %[divisor]
|
||||
: "=a" (`q`), "=d" (`r`)
|
||||
: "d" (`n_hi`), "a" (`n_lo`), [divisor] "rm" (`d`)
|
||||
:
|
||||
"""
|
||||
else:
|
||||
asm """
|
||||
divq %[divisor]
|
||||
: "=a" (`*q`), "=d" (`*r`)
|
||||
: "d" (`n_hi`), "a" (`n_lo`), [divisor] "rm" (`d`)
|
||||
:
|
||||
"""
|
@ -21,29 +21,11 @@ static:
|
||||
doAssert sizeof(int) == 8
|
||||
doAssert X86
|
||||
|
||||
func udiv128(highDividend, lowDividend, divisor: Ct[uint64], remainder: var Ct[uint64]): Ct[uint64] {.importc:"_udiv128", header: "<intrin.h>", nodecl.}
|
||||
## 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 umul128(a, b: Ct[uint64], hi: var Ct[uint64]): Ct[uint64] {.importc:"_umul128", header:"<intrin.h>", nodecl.}
|
||||
## Unsigned extended precision multiplication
|
||||
## (hi, lo) <-- a * b
|
||||
## Return value is the low word
|
||||
|
||||
func unsafeDiv2n1n*(q, r: var Ct[uint64], n_hi, n_lo, d: Ct[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
|
||||
{.warning: "unsafeDiv2n1n is not constant-time at the moment on most hardware".}
|
||||
|
||||
# TODO !!! - Replace by constant-time, portable, non-assembly version
|
||||
# -> use uint128? Compiler might add unwanted branches
|
||||
q = udiv128(n_hi, n_lo, d, r)
|
||||
|
||||
func mul*(hi, lo: var Ct[uint64], a, b: Ct[uint64]) {.inline.} =
|
||||
## Extended precision multiplication
|
||||
## (hi, lo) <- a*b
|
||||
|
78
constantine/platforms/constant_time/ct_division.nim
Normal file
78
constantine/platforms/constant_time/ct_division.nim
Normal file
@ -0,0 +1,78 @@
|
||||
# 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 ./ct_types, ./ct_routines, ./multiplexers
|
||||
|
||||
func div2n1n*[T: Ct](q, r: var T, n_hi, n_lo, d: T) =
|
||||
## Division uint128 by uint64 or uint64 by uint32
|
||||
## Warning ⚠️ :
|
||||
## - if n_hi == d, quotient does not fit in an uint32
|
||||
## - 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.
|
||||
# See
|
||||
# https://www.bearssl.org/bigint.html
|
||||
# br_divrem
|
||||
const bits = sizeof(T)*8
|
||||
|
||||
q = T(0)
|
||||
var hi = mux(n_hi == d, T(0), n_hi)
|
||||
var lo = n_lo
|
||||
|
||||
for k in countdown(bits-1, 1):
|
||||
let j = bits-k
|
||||
let w = (hi shl j) or (lo shr k)
|
||||
let ctl = (w >= d) or CTBool[T](hi shr k)
|
||||
let hi2 = (w-d) shr j
|
||||
let lo2 = lo - (d shl k)
|
||||
hi = ctl.mux(hi2, hi)
|
||||
lo = ctl.mux(lo2, lo)
|
||||
q = q or (T(ctl) shl k)
|
||||
|
||||
let carry = (lo >= d) or hi.isNonZero()
|
||||
q = q or T(carry)
|
||||
r = carry.mux(lo - d, lo)
|
||||
|
||||
|
||||
# Performance:
|
||||
# If division ever becomes a bottleneck, we can avoid binary-shift
|
||||
# by first computing a single-word reciprocal (mod 2⁶⁴)
|
||||
# Then using multiplication for each quotient/remainder
|
||||
# See algorithm 2 (reciprocal) and 4 in https://gmplib.org/~tege/division-paper.pdf
|
||||
#
|
||||
# Algorithm 2
|
||||
# can be made constant-time via Newton-Raphson iterations
|
||||
# or use binary shift division with
|
||||
#
|
||||
# v ← [2⁶⁴-1-d, 2⁶⁴-1]/d
|
||||
#
|
||||
# or (for assembly)
|
||||
#
|
||||
# v ← not(d << 64)/d
|
||||
#
|
||||
# (d normalized)
|
||||
#
|
||||
# Algorithm 4
|
||||
# can be made constant-time via conditional moves.
|
||||
#
|
||||
# (q, r) ← DIV2BY1(〈u1, u0〉, d, v)
|
||||
# In: β/2 ≤ d < β, u1 < d, v = ⌊(β2 − 1)/d⌋ − β
|
||||
#
|
||||
# 1 〈q1, q0〉 ← vu1 // umul
|
||||
# 2 〈q1, q0〉 ← 〈q1, q0〉 + 〈u1, u0〉
|
||||
# 3 q1 ← (q1 + 1) mod β
|
||||
# 4 r ← (u0 − q1d) mod β // umullo
|
||||
# 5 if r > q0 // Unpredictable condition
|
||||
# 6 q1 ← (q1 − 1) mod β
|
||||
# 7 r ← (r + d) mod β
|
||||
# 8 if r ≥ d // Unlikely condition
|
||||
# 9 q1 ← q1 + 1
|
||||
# 10 r ← r − d
|
||||
# 11 return q1, r
|
@ -186,6 +186,22 @@ template cneg*[T: Ct](x: T, ctl: CTBool[T]): T =
|
||||
template trmFixSystemNotEq*{x != y}[T: Ct](x, y: T): CTBool[T] =
|
||||
noteq(x, y)
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
# Table lookups
|
||||
#
|
||||
# ############################################################
|
||||
|
||||
func secretLookup*[T; S: Ct](table: openArray[T], index: S): T =
|
||||
## Return table[index]
|
||||
## This is constant-time, whatever the `index`, its value is not leaked
|
||||
## This is also protected against cache-timing attack by always scanning the whole table
|
||||
var val: S
|
||||
for i in 0 ..< table.len:
|
||||
let selector = S(i) == index
|
||||
selector.ccopy(val, S table[i])
|
||||
return T(val)
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
# Optimized hardened zero comparison
|
||||
|
@ -137,25 +137,34 @@ func mux*[T](ctl: CTBool[T], x, y: T): T {.inline.}=
|
||||
## Returns x if ctl is true
|
||||
## else returns y
|
||||
## So equivalent to ctl? x: y
|
||||
when X86 and GCC_Compatible:
|
||||
mux_x86(ctl, x, y)
|
||||
else:
|
||||
when nimvm:
|
||||
mux_fallback(ctl, x, y)
|
||||
else:
|
||||
when X86 and GCC_Compatible:
|
||||
mux_x86(ctl, x, y)
|
||||
else:
|
||||
mux_fallback(ctl, x, y)
|
||||
|
||||
func mux*[T: CTBool](ctl: CTBool, x, y: T): T {.inline.}=
|
||||
## Multiplexer / selector
|
||||
## Returns x if ctl is true
|
||||
## else returns y
|
||||
## So equivalent to ctl? x: y
|
||||
when X86 and GCC_Compatible:
|
||||
mux_x86(ctl, x, y)
|
||||
else:
|
||||
when nimvm:
|
||||
mux_fallback(ctl, x, y)
|
||||
else:
|
||||
when X86 and GCC_Compatible:
|
||||
mux_x86(ctl, x, y)
|
||||
else:
|
||||
mux_fallback(ctl, x, y)
|
||||
|
||||
func ccopy*[T](ctl: CTBool[T], x: var T, y: T) {.inline.}=
|
||||
## Conditional copy
|
||||
## Copy ``y`` into ``x`` if ``ctl`` is true
|
||||
when X86 and GCC_Compatible:
|
||||
ccopy_x86(ctl, x, y)
|
||||
else:
|
||||
when nimvm:
|
||||
ccopy_fallback(ctl, x, y)
|
||||
else:
|
||||
when X86 and GCC_Compatible:
|
||||
ccopy_x86(ctl, x, y)
|
||||
else:
|
||||
ccopy_fallback(ctl, x, y)
|
||||
|
@ -10,7 +10,8 @@ import
|
||||
constant_time/[
|
||||
ct_types,
|
||||
ct_routines,
|
||||
multiplexers
|
||||
multiplexers,
|
||||
ct_division
|
||||
],
|
||||
compilers/[
|
||||
addcarry_subborrow,
|
||||
@ -25,6 +26,7 @@ export
|
||||
multiplexers,
|
||||
addcarry_subborrow,
|
||||
extended_precision,
|
||||
ct_division,
|
||||
bithacks,
|
||||
staticFor
|
||||
|
||||
|
@ -211,9 +211,9 @@ func random_long01Seq(rng: var RngState, a: var BigInt) =
|
||||
rng.random_long01Seq(buf)
|
||||
let order = rng.sample_unsafe([bigEndian, littleEndian])
|
||||
if order == bigEndian:
|
||||
a.fromRawUint(buf, bigEndian)
|
||||
a.unmarshal(buf, bigEndian)
|
||||
else:
|
||||
a.fromRawUint(buf, littleEndian)
|
||||
a.unmarshal(buf, littleEndian)
|
||||
|
||||
func random_long01Seq(rng: var RngState, a: var FF) =
|
||||
## Recursively initialize a BigInt (part of a field) or Field element
|
||||
|
@ -37,7 +37,7 @@ func unsafe_ECmul_double_add*[EC](
|
||||
##
|
||||
## This is highly VULNERABLE to timing attacks and power analysis attacks
|
||||
var scalarCanonical: array[(scalar.bits+7) div 8, byte]
|
||||
scalarCanonical.exportRawUint(scalar, bigEndian)
|
||||
scalarCanonical.marshal(scalar, bigEndian)
|
||||
|
||||
var t0{.noInit.}, t1{.noInit.}: typeof(P)
|
||||
t0.setInf()
|
||||
|
@ -25,13 +25,13 @@ proc mainArith() =
|
||||
check: x.isZero().bool
|
||||
test "isZero for non-zero":
|
||||
block:
|
||||
let x = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
|
||||
let x = fromHex(BigInt[128], "0x00000000000000000000000000000001")
|
||||
check: not x.isZero().bool
|
||||
block:
|
||||
let x = fromHex(BigInt[128], "0x00000000_00000001_00000000_00000000")
|
||||
let x = fromHex(BigInt[128], "0x00000000000000010000000000000000")
|
||||
check: not x.isZero().bool
|
||||
block:
|
||||
let x = fromHex(BigInt[128], "0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF")
|
||||
let x = fromHex(BigInt[128], "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
|
||||
check: not x.isZero().bool
|
||||
|
||||
test "isZero for zero (compile-time)":
|
||||
@ -39,53 +39,53 @@ proc mainArith() =
|
||||
check: static(x.isZero().bool)
|
||||
test "isZero for non-zero (compile-time)":
|
||||
block:
|
||||
const x = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
|
||||
const x = fromHex(BigInt[128], "0x00000000000000000000000000000001")
|
||||
check: static(not x.isZero().bool)
|
||||
block:
|
||||
const x = fromHex(BigInt[128], "0x00000000_00000001_00000000_00000000")
|
||||
const x = fromHex(BigInt[128], "0x00000000000000010000000000000000")
|
||||
check: static(not x.isZero().bool)
|
||||
block:
|
||||
const x = fromHex(BigInt[128], "0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF")
|
||||
const x = fromHex(BigInt[128], "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
|
||||
check: static(not x.isZero().bool)
|
||||
|
||||
|
||||
suite "Arithmetic operations - Addition" & " [" & $WordBitwidth & "-bit mode]":
|
||||
test "Adding 2 zeros":
|
||||
var a = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000000")
|
||||
let b = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000000")
|
||||
var a = fromHex(BigInt[128], "0x00000000000000000000000000000000")
|
||||
let b = fromHex(BigInt[128], "0x00000000000000000000000000000000")
|
||||
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")
|
||||
var a = fromHex(BigInt[128], "0x00000000000000000000000000000000")
|
||||
let b = fromHex(BigInt[128], "0x00000000000000000000000000000001")
|
||||
let carry = a.cadd(b, CtTrue)
|
||||
|
||||
let c = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
|
||||
let c = fromHex(BigInt[128], "0x00000000000000000000000000000001")
|
||||
check:
|
||||
bool(a == c)
|
||||
block:
|
||||
var a = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
|
||||
let b = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000000")
|
||||
var a = fromHex(BigInt[128], "0x00000000000000000000000000000001")
|
||||
let b = fromHex(BigInt[128], "0x00000000000000000000000000000000")
|
||||
let carry = a.cadd(b, CtTrue)
|
||||
|
||||
let c = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
|
||||
let c = fromHex(BigInt[128], "0x00000000000000000000000000000001")
|
||||
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")
|
||||
var a = fromHex(BigInt[128], "0x00000000000000000000000000000000")
|
||||
let b = fromHex(BigInt[128], "0x00000000000000000000000000000001")
|
||||
let carry = a.cadd(b, CtFalse)
|
||||
|
||||
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")
|
||||
var a = fromHex(BigInt[128], "0x00000000000000000000000000000001")
|
||||
let b = fromHex(BigInt[128], "0x00000000000000000000000000000000")
|
||||
let carry = a.cadd(b, CtFalse)
|
||||
|
||||
let c = a
|
||||
@ -94,34 +94,34 @@ proc mainArith() =
|
||||
|
||||
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")
|
||||
var a = fromHex(BigInt[128], "0x00000000000000010000000000000000")
|
||||
let b = fromHex(BigInt[128], "0x00000000000000000000000000000001")
|
||||
let carry = a.cadd(b, CtTrue)
|
||||
|
||||
let c = fromHex(BigInt[128], "0x00000000_00000001_00000000_00000001")
|
||||
let c = fromHex(BigInt[128], "0x00000000000000010000000000000001")
|
||||
check:
|
||||
bool(a == c)
|
||||
block:
|
||||
var a = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
|
||||
let b = fromHex(BigInt[128], "0x00000000_00000001_00000000_00000000")
|
||||
var a = fromHex(BigInt[128], "0x00000000000000000000000000000001")
|
||||
let b = fromHex(BigInt[128], "0x00000000000000010000000000000000")
|
||||
let carry = a.cadd(b, CtTrue)
|
||||
|
||||
let c = fromHex(BigInt[128], "0x00000000_00000001_00000000_00000001")
|
||||
let c = fromHex(BigInt[128], "0x00000000000000010000000000000001")
|
||||
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")
|
||||
var a = fromHex(BigInt[128], "0x00000000000000010000000000000000")
|
||||
let b = fromHex(BigInt[128], "0x00000000000000000000000000000001")
|
||||
let carry = a.cadd(b, CtFalse)
|
||||
|
||||
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")
|
||||
var a = fromHex(BigInt[128], "0x00000000000000000000000000000001")
|
||||
let b = fromHex(BigInt[128], "0x00000000000000010000000000000000")
|
||||
let carry = a.cadd(b, CtFalse)
|
||||
|
||||
let c = a
|
||||
@ -130,21 +130,21 @@ proc mainArith() =
|
||||
|
||||
test "Addition limbs carry":
|
||||
block:
|
||||
var a = fromHex(BigInt[128], "0x00000000_FFFFFFFF_FFFFFFFF_FFFFFFFE")
|
||||
let b = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
|
||||
var a = fromHex(BigInt[128], "0x00000000FFFFFFFFFFFFFFFFFFFFFFFE")
|
||||
let b = fromHex(BigInt[128], "0x00000000000000000000000000000001")
|
||||
let carry = a.cadd(b, CtTrue)
|
||||
|
||||
let c = fromHex(BigInt[128], "0x00000000_FFFFFFFF_FFFFFFFF_FFFFFFFF")
|
||||
let c = fromHex(BigInt[128], "0x00000000FFFFFFFFFFFFFFFFFFFFFFFF")
|
||||
check:
|
||||
bool(a == c)
|
||||
not bool(carry)
|
||||
|
||||
block:
|
||||
var a = fromHex(BigInt[128], "0x00000000_FFFFFFFF_FFFFFFFF_FFFFFFFF")
|
||||
let b = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
|
||||
var a = fromHex(BigInt[128], "0x00000000FFFFFFFFFFFFFFFFFFFFFFFF")
|
||||
let b = fromHex(BigInt[128], "0x00000000000000000000000000000001")
|
||||
let carry = a.cadd(b, CtTrue)
|
||||
|
||||
let c = fromHex(BigInt[128], "0x00000001_00000000_00000000_00000000")
|
||||
let c = fromHex(BigInt[128], "0x00000001000000000000000000000000")
|
||||
check:
|
||||
bool(a == c)
|
||||
not bool(carry)
|
||||
@ -164,8 +164,8 @@ proc mainMul() =
|
||||
test "Same size operand into double size result":
|
||||
block:
|
||||
var r = canary(BigInt[256])
|
||||
let a = BigInt[128].fromHex"0x12345678_FF11FFAA_00321321_CAFECAFE"
|
||||
let b = BigInt[128].fromHex"0xDEADBEEF_DEADBEEF_DEADBEEF_DEADBEEF"
|
||||
let a = BigInt[128].fromHex"0x12345678FF11FFAA00321321CAFECAFE"
|
||||
let b = BigInt[128].fromHex"0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF"
|
||||
|
||||
let expected = BigInt[256].fromHex"fd5bdef43d64113f371ab5d8843beca889c07fd549b84d8a5001a8f102e0722"
|
||||
|
||||
@ -178,7 +178,7 @@ proc mainMul() =
|
||||
block:
|
||||
var r = canary(BigInt[200])
|
||||
let a = BigInt[29].fromHex"0x12345678"
|
||||
let b = BigInt[128].fromHex"0xDEADBEEF_DEADBEEF_DEADBEEF_DEADBEEF"
|
||||
let b = BigInt[128].fromHex"0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF"
|
||||
|
||||
let expected = BigInt[200].fromHex"fd5bdee65f787f665f787f665f787f65621ca08"
|
||||
|
||||
@ -189,9 +189,9 @@ proc mainMul() =
|
||||
|
||||
test "Destination is properly zero-padded if multiplicands are too short":
|
||||
block:
|
||||
var r = BigInt[200].fromHex"0xDEADBEEF_DEADBEEF_DEADBEEF_DEADBEEF_DEADBEEF_DEADBEEF_DE"
|
||||
var r = BigInt[200].fromHex"0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDE"
|
||||
let a = BigInt[29].fromHex"0x12345678"
|
||||
let b = BigInt[128].fromHex"0xDEADBEEF_DEADBEEF_DEADBEEF_DEADBEEF"
|
||||
let b = BigInt[128].fromHex"0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF"
|
||||
|
||||
let expected = BigInt[200].fromHex"fd5bdee65f787f665f787f665f787f65621ca08"
|
||||
|
||||
@ -205,108 +205,108 @@ proc mainMulHigh() =
|
||||
test "Same size operand into double size result - discard first word":
|
||||
block:
|
||||
var r = canary(BigInt[256])
|
||||
let a = BigInt[128].fromHex"0x12345678_FF11FFAA_00321321_CAFECAFE"
|
||||
let b = BigInt[128].fromHex"0xDEADBEEF_DEADBEEF_DEADBEEF_DEADBEEF"
|
||||
let a = BigInt[128].fromHex"0x12345678FF11FFAA00321321CAFECAFE"
|
||||
let b = BigInt[128].fromHex"0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF"
|
||||
|
||||
when WordBitWidth == 32:
|
||||
let expected = BigInt[256].fromHex"fd5bdef43d64113f371ab5d8843beca889c07fd549b84d8a5001a8f"
|
||||
else:
|
||||
let expected = BigInt[256].fromHex"fd5bdef43d64113f371ab5d8843beca889c07fd549b84d8"
|
||||
|
||||
r.prod_high_words(a, b, 1)
|
||||
r.prodhighwords(a, b, 1)
|
||||
check: bool(r == expected)
|
||||
r.prod_high_words(b, a, 1)
|
||||
r.prodhighwords(b, a, 1)
|
||||
check: bool(r == expected)
|
||||
|
||||
test "Same size operand into double size result - discard first 3 words":
|
||||
block:
|
||||
var r = canary(BigInt[256])
|
||||
let a = BigInt[128].fromHex"0x12345678_FF11FFAA_00321321_CAFECAFE"
|
||||
let b = BigInt[128].fromHex"0xDEADBEEF_DEADBEEF_DEADBEEF_DEADBEEF"
|
||||
let a = BigInt[128].fromHex"0x12345678FF11FFAA00321321CAFECAFE"
|
||||
let b = BigInt[128].fromHex"0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF"
|
||||
|
||||
when WordBitWidth == 32:
|
||||
let expected = BigInt[256].fromHex"fd5bdef43d64113f371ab5d8843beca889c07fd"
|
||||
else:
|
||||
let expected = BigInt[256].fromHex"fd5bdef43d64113"
|
||||
|
||||
r.prod_high_words(a, b, 3)
|
||||
r.prodhighwords(a, b, 3)
|
||||
check: bool(r == expected)
|
||||
r.prod_high_words(b, a, 3)
|
||||
r.prodhighwords(b, a, 3)
|
||||
check: bool(r == expected)
|
||||
|
||||
test "All lower words trigger a carry":
|
||||
block:
|
||||
var r = canary(BigInt[256])
|
||||
let a = BigInt[256].fromHex"0xFFFFF000_FFFFF111_FFFFFFFA_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF"
|
||||
let b = BigInt[256].fromHex"0xFFFFFFFF_FFFFF222_FFFFFFFB_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF"
|
||||
let a = BigInt[256].fromHex"0xFFFFF000FFFFF111FFFFFFFAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
|
||||
let b = BigInt[256].fromHex"0xFFFFFFFFFFFFF222FFFFFFFBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
|
||||
|
||||
# Full product:
|
||||
# fffff000_ffffe335_00ddc21a_00cf3972_00008109_00000013_ffffffff_fffffffe
|
||||
# 00000fff_00001ccb_00000009_00000000_00000000_00000000_00000000_00000001
|
||||
let expected = BigInt[256].fromHex"0xfffff000_ffffe335_00ddc21a_00cf3972_00008109_00000013_ffffffff_fffffffe"
|
||||
# fffff000ffffe33500ddc21a00cf39720000810900000013fffffffffffffffe
|
||||
# 00000fff00001ccb000000090000000000000000000000000000000000000001
|
||||
let expected = BigInt[256].fromHex"0xfffff000ffffe33500ddc21a00cf39720000810900000013fffffffffffffffe"
|
||||
when WordBitWidth == 32:
|
||||
const startWord = 8
|
||||
else:
|
||||
const startWord = 4
|
||||
|
||||
r.prod_high_words(a, b, startWord)
|
||||
r.prodhighwords(a, b, startWord)
|
||||
check: bool(r == expected)
|
||||
r.prod_high_words(b, a, startWord)
|
||||
r.prodhighwords(b, a, startWord)
|
||||
check: bool(r == expected)
|
||||
|
||||
test "Different size into large result":
|
||||
block:
|
||||
var r = canary(BigInt[200])
|
||||
let a = BigInt[29].fromHex"0x12345678"
|
||||
let b = BigInt[128].fromHex"0xDEADBEEF_DEADBEEF_DEADBEEF_DEADBEEF"
|
||||
let b = BigInt[128].fromHex"0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF"
|
||||
|
||||
when WordBitWidth == 32:
|
||||
let expected = BigInt[200].fromHex"fd5bdee65f787f665f787f6"
|
||||
else:
|
||||
let expected = BigInt[200].fromHex"fd5bdee"
|
||||
|
||||
r.prod_high_words(a, b, 2)
|
||||
r.prodhighwords(a, b, 2)
|
||||
check: bool(r == expected)
|
||||
r.prod_high_words(b, a, 2)
|
||||
r.prodhighwords(b, a, 2)
|
||||
check: bool(r == expected)
|
||||
|
||||
test "Destination is properly zero-padded if multiplicands are too short":
|
||||
block:
|
||||
var r = BigInt[200].fromHex"0xDEADBEEF_DEADBEEF_DEADBEEF_DEADBEEF_DEADBEEF_DEADBEEF_DE"
|
||||
var r = BigInt[200].fromHex"0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDE"
|
||||
let a = BigInt[29].fromHex"0x12345678"
|
||||
let b = BigInt[128].fromHex"0xDEADBEEF_DEADBEEF_DEADBEEF_DEADBEEF"
|
||||
let b = BigInt[128].fromHex"0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF"
|
||||
|
||||
when WordBitWidth == 32:
|
||||
let expected = BigInt[200].fromHex"fd5bdee65f787f665f787f6"
|
||||
else:
|
||||
let expected = BigInt[200].fromHex"fd5bdee"
|
||||
|
||||
r.prod_high_words(a, b, 2)
|
||||
r.prodhighwords(a, b, 2)
|
||||
check: bool(r == expected)
|
||||
r.prod_high_words(b, a, 2)
|
||||
r.prodhighwords(b, a, 2)
|
||||
check: bool(r == expected)
|
||||
|
||||
proc mainSquare() =
|
||||
suite "Multi-precision multiplication" & " [" & $WordBitwidth & "-bit mode]":
|
||||
test "Squaring is consistent with multiplication (rBits = 2*aBits)":
|
||||
block:
|
||||
let a = BigInt[200].fromHex"0xDEADBEEF_DEADBEEF_DEADBEEF_DEADBEEF_DEADBEEF_DEADBEEF_DE"
|
||||
let a = BigInt[200].fromHex"0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDE"
|
||||
|
||||
var r_mul, r_sqr: BigInt[400]
|
||||
var rmul, rsqr: BigInt[400]
|
||||
|
||||
r_mul.prod(a, a)
|
||||
r_sqr.square(a)
|
||||
check: bool(r_mul == r_sqr)
|
||||
rmul.prod(a, a)
|
||||
rsqr.square(a)
|
||||
check: bool(rmul == rsqr)
|
||||
|
||||
test "Squaring is consistent with multiplication (truncated)":
|
||||
block:
|
||||
let a = BigInt[200].fromHex"0xDEADBEEF_DEADBEEF_DEADBEEF_DEADBEEF_DEADBEEF_DEADBEEF_DE"
|
||||
let a = BigInt[200].fromHex"0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDE"
|
||||
|
||||
var r_mul, r_sqr: BigInt[256]
|
||||
var rmul, rsqr: BigInt[256]
|
||||
|
||||
r_mul.prod(a, a)
|
||||
r_sqr.square(a)
|
||||
check: bool(r_mul == r_sqr)
|
||||
rmul.prod(a, a)
|
||||
rsqr.square(a)
|
||||
check: bool(rmul == rsqr)
|
||||
|
||||
proc mainModular() =
|
||||
suite "Modular operations - small modulus" & " [" & $WordBitwidth & "-bit mode]":
|
||||
@ -347,7 +347,7 @@ proc mainModular() =
|
||||
"\n expected (ll repr): " & $expected
|
||||
|
||||
test "2^64 mod 3":
|
||||
let a = BigInt[65].fromHex("0x1_00000000_00000000")
|
||||
let a = BigInt[65].fromHex("0x10000000000000000")
|
||||
let m = BigInt[8].fromUint(3'u8)
|
||||
|
||||
var r = canary(BigInt[8])
|
||||
@ -404,8 +404,8 @@ proc mainNeg() =
|
||||
suite "Conditional negation" & " [" & $WordBitwidth & "-bit mode]":
|
||||
test "Conditional negation":
|
||||
block:
|
||||
var a = fromHex(BigInt[128], "0x12345678_FF11FFAA_00321321_CAFECAFE")
|
||||
var b = fromHex(BigInt[128], "0xDEADBEEF_DEADBEEF_DEADBEEF_DEADBEEF")
|
||||
var a = fromHex(BigInt[128], "0x12345678FF11FFAA00321321CAFECAFE")
|
||||
var b = fromHex(BigInt[128], "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF")
|
||||
|
||||
let a2 = a
|
||||
let b2 = b
|
||||
@ -421,8 +421,8 @@ proc mainNeg() =
|
||||
bool(b.isZero)
|
||||
|
||||
block:
|
||||
var a = fromHex(BigInt[128], "0x12345678_FF11FFAA_00321321_CAFECAFE")
|
||||
var b = fromHex(BigInt[128], "0xDEADBEEF_DEADBEEF_DEADBEEF_DEADBEEF")
|
||||
var a = fromHex(BigInt[128], "0x12345678FF11FFAA00321321CAFECAFE")
|
||||
var b = fromHex(BigInt[128], "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF")
|
||||
|
||||
let a2 = a
|
||||
let b2 = b
|
||||
@ -436,8 +436,8 @@ proc mainNeg() =
|
||||
|
||||
test "Conditional negation with carries":
|
||||
block:
|
||||
var a = fromHex(BigInt[128], "0x12345678_FF11FFAA_00321321_FFFFFFFF")
|
||||
var b = fromHex(BigInt[128], "0xFFFFFFFF_FFFFFFFF_00000000_00000000")
|
||||
var a = fromHex(BigInt[128], "0x12345678FF11FFAA00321321FFFFFFFF")
|
||||
var b = fromHex(BigInt[128], "0xFFFFFFFFFFFFFFFF0000000000000000")
|
||||
|
||||
let a2 = a
|
||||
let b2 = b
|
||||
@ -453,8 +453,8 @@ proc mainNeg() =
|
||||
bool(b.isZero)
|
||||
|
||||
block:
|
||||
var a = fromHex(BigInt[128], "0x12345678_00000000_00321321_FFFFFFFF")
|
||||
var b = fromHex(BigInt[128], "0xFFFFFFFF_FFFFFFFF_00000000_00000000")
|
||||
var a = fromHex(BigInt[128], "0x123456780000000000321321FFFFFFFF")
|
||||
var b = fromHex(BigInt[128], "0xFFFFFFFFFFFFFFFF0000000000000000")
|
||||
|
||||
let a2 = a
|
||||
let b2 = b
|
||||
@ -468,8 +468,8 @@ proc mainNeg() =
|
||||
|
||||
test "Conditional all-zero bit or all-one bit":
|
||||
block:
|
||||
var a = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000000")
|
||||
var b = fromHex(BigInt[128], "0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF")
|
||||
var a = fromHex(BigInt[128], "0x00000000000000000000000000000000")
|
||||
var b = fromHex(BigInt[128], "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
|
||||
|
||||
let a2 = a
|
||||
let b2 = b
|
||||
@ -485,8 +485,8 @@ proc mainNeg() =
|
||||
bool(b.isZero)
|
||||
|
||||
block:
|
||||
var a = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000000")
|
||||
var b = fromHex(BigInt[128], "0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF")
|
||||
var a = fromHex(BigInt[128], "0x00000000000000000000000000000000")
|
||||
var b = fromHex(BigInt[128], "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
|
||||
|
||||
let a2 = a
|
||||
let b2 = b
|
||||
@ -502,8 +502,8 @@ proc mainCopySwap() =
|
||||
suite "Copy and Swap" & " [" & $WordBitwidth & "-bit mode]":
|
||||
test "Conditional copy":
|
||||
block:
|
||||
var a = fromHex(BigInt[128], "0x12345678_FF11FFAA_00321321_CAFECAFE")
|
||||
let b = fromHex(BigInt[128], "0xDEADBEEF_DEADBEEF_DEADBEEF_DEADBEEF")
|
||||
var a = fromHex(BigInt[128], "0x12345678FF11FFAA00321321CAFECAFE")
|
||||
let b = fromHex(BigInt[128], "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF")
|
||||
|
||||
var expected = a
|
||||
a.ccopy(b, CtFalse)
|
||||
@ -511,8 +511,8 @@ proc mainCopySwap() =
|
||||
check: bool(expected == a)
|
||||
|
||||
block:
|
||||
var a = fromHex(BigInt[128], "0x00000000_FFFFFFFF_FFFFFFFF_FFFFFFFF")
|
||||
let b = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
|
||||
var a = fromHex(BigInt[128], "0x00000000FFFFFFFFFFFFFFFFFFFFFFFF")
|
||||
let b = fromHex(BigInt[128], "0x00000000000000000000000000000001")
|
||||
|
||||
var expected = b
|
||||
a.ccopy(b, CtTrue)
|
||||
@ -521,8 +521,8 @@ proc mainCopySwap() =
|
||||
|
||||
test "Conditional swap":
|
||||
block:
|
||||
var a = fromHex(BigInt[128], "0x12345678_FF11FFAA_00321321_CAFECAFE")
|
||||
var b = fromHex(BigInt[128], "0xDEADBEEF_DEADBEEF_DEADBEEF_DEADBEEF")
|
||||
var a = fromHex(BigInt[128], "0x12345678FF11FFAA00321321CAFECAFE")
|
||||
var b = fromHex(BigInt[128], "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF")
|
||||
|
||||
let eA = a
|
||||
let eB = b
|
||||
@ -533,8 +533,8 @@ proc mainCopySwap() =
|
||||
bool(eB == b)
|
||||
|
||||
block:
|
||||
var a = fromHex(BigInt[128], "0x00000000_FFFFFFFF_FFFFFFFF_FFFFFFFF")
|
||||
var b = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
|
||||
var a = fromHex(BigInt[128], "0x00000000FFFFFFFFFFFFFFFFFFFFFFFF")
|
||||
var b = fromHex(BigInt[128], "0x00000000000000000000000000000001")
|
||||
|
||||
let eA = b
|
||||
let eB = a
|
||||
@ -548,7 +548,7 @@ proc mainModularInverse() =
|
||||
suite "Modular Inverse (with odd modulus)" & " [" & $WordBitwidth & "-bit mode]":
|
||||
# Note: We don't define multi-precision multiplication
|
||||
# because who needs it when you have Montgomery?
|
||||
# ¯\_(ツ)_/¯
|
||||
# ¯\(ツ)/¯
|
||||
test "42^-1 (mod 2017) = 1969":
|
||||
block: # small int
|
||||
let a = BigInt[16].fromUint(42'u16)
|
||||
|
@ -127,8 +127,8 @@ proc main() =
|
||||
doAssert mLen == mW, "Expected " & $mLen & " bytes but wrote " & $mW & " for " & toHex(mBuf) & " (big-endian)"
|
||||
|
||||
# Build the bigint
|
||||
let aTest = BigInt[aBits].fromRawUint(aBuf.toOpenArray(0, aW-1), bigEndian)
|
||||
let mTest = BigInt[mBits].fromRawUint(mBuf.toOpenArray(0, mW-1), bigEndian)
|
||||
let aTest = BigInt[aBits].unmarshal(aBuf.toOpenArray(0, aW-1), bigEndian)
|
||||
let mTest = BigInt[mBits].unmarshal(mBuf.toOpenArray(0, mW-1), bigEndian)
|
||||
|
||||
#########################################################
|
||||
# Modulus
|
||||
@ -144,7 +144,7 @@ proc main() =
|
||||
discard mpz_export(rGMP[0].addr, rW.addr, GMP_MostSignificantWordFirst, 1, GMP_WordNativeEndian, 0, r)
|
||||
|
||||
var rConstantine: array[mLen, byte]
|
||||
exportRawUint(rConstantine, rTest, bigEndian)
|
||||
marshal(rConstantine, rTest, bigEndian)
|
||||
|
||||
# echo "rGMP: ", rGMP.toHex()
|
||||
# echo "rConstantine: ", rConstantine.toHex()
|
||||
|
@ -102,8 +102,8 @@ proc main() =
|
||||
doAssert bLen >= bW, "Expected at most " & $bLen & " bytes but wrote " & $bW & " for " & toHex(bBuf) & " (big-endian)"
|
||||
|
||||
# Build the bigint
|
||||
let aTest = BigInt[aBits].fromRawUint(aBuf.toOpenArray(0, aW-1), bigEndian)
|
||||
let bTest = BigInt[bBits].fromRawUint(bBuf.toOpenArray(0, bW-1), bigEndian)
|
||||
let aTest = BigInt[aBits].unmarshal(aBuf.toOpenArray(0, aW-1), bigEndian)
|
||||
let bTest = BigInt[bBits].unmarshal(bBuf.toOpenArray(0, bW-1), bigEndian)
|
||||
|
||||
#########################################################
|
||||
# Multiplication + drop low words
|
||||
@ -132,7 +132,7 @@ proc main() =
|
||||
discard mpz_export(rGMP[0].addr, rW.addr, GMP_MostSignificantWordFirst, 1, GMP_WordNativeEndian, 0, r)
|
||||
|
||||
var rConstantine: array[rLen, byte]
|
||||
exportRawUint(rConstantine, rTest, bigEndian)
|
||||
marshal(rConstantine, rTest, bigEndian)
|
||||
|
||||
# Note: in bigEndian, GMP aligns left while constantine aligns right
|
||||
doAssert rGMP.toOpenArray(0, rW-1) == rConstantine.toOpenArray(rLen-rW, rLen-1), block:
|
||||
|
@ -95,8 +95,8 @@ proc main() =
|
||||
doAssert bLen >= bW, "Expected at most " & $bLen & " bytes but wrote " & $bW & " for " & toHex(bBuf) & " (big-endian)"
|
||||
|
||||
# Build the bigint
|
||||
let aTest = BigInt[aBits].fromRawUint(aBuf.toOpenArray(0, aW-1), bigEndian)
|
||||
let bTest = BigInt[bBits].fromRawUint(bBuf.toOpenArray(0, bW-1), bigEndian)
|
||||
let aTest = BigInt[aBits].unmarshal(aBuf.toOpenArray(0, aW-1), bigEndian)
|
||||
let bTest = BigInt[bBits].unmarshal(bBuf.toOpenArray(0, bW-1), bigEndian)
|
||||
|
||||
#########################################################
|
||||
# Multiplication
|
||||
@ -122,7 +122,7 @@ proc main() =
|
||||
discard mpz_export(rGMP[0].addr, rW.addr, GMP_MostSignificantWordFirst, 1, GMP_WordNativeEndian, 0, r)
|
||||
|
||||
var rConstantine: array[rLen, byte]
|
||||
exportRawUint(rConstantine, rTest, bigEndian)
|
||||
marshal(rConstantine, rTest, bigEndian)
|
||||
|
||||
# Note: in bigEndian, GMP aligns left while constantine aligns right
|
||||
doAssert rGMP.toOpenArray(0, rW-1) == rConstantine.toOpenArray(rLen-rW, rLen-1), block:
|
||||
|
@ -29,7 +29,7 @@ proc main() =
|
||||
x += y
|
||||
|
||||
var x_bytes: array[8, byte]
|
||||
x_bytes.exportRawUint(x, cpuEndian)
|
||||
x_bytes.marshal(x, cpuEndian)
|
||||
|
||||
check:
|
||||
# Check equality in the Montgomery domain
|
||||
@ -47,7 +47,7 @@ proc main() =
|
||||
x += y
|
||||
|
||||
var x_bytes: array[8, byte]
|
||||
x_bytes.exportRawUint(x, cpuEndian)
|
||||
x_bytes.marshal(x, cpuEndian)
|
||||
|
||||
check:
|
||||
# Check equality in the Montgomery domain
|
||||
@ -65,7 +65,7 @@ proc main() =
|
||||
x += y
|
||||
|
||||
var x_bytes: array[8, byte]
|
||||
x_bytes.exportRawUint(x, cpuEndian)
|
||||
x_bytes.marshal(x, cpuEndian)
|
||||
|
||||
check:
|
||||
# Check equality in the Montgomery domain
|
||||
@ -84,7 +84,7 @@ proc main() =
|
||||
x -= y
|
||||
|
||||
var x_bytes: array[8, byte]
|
||||
x_bytes.exportRawUint(x, cpuEndian)
|
||||
x_bytes.marshal(x, cpuEndian)
|
||||
|
||||
check:
|
||||
# Check equality in the Montgomery domain
|
||||
@ -102,7 +102,7 @@ proc main() =
|
||||
x -= y
|
||||
|
||||
var x_bytes: array[8, byte]
|
||||
x_bytes.exportRawUint(x, cpuEndian)
|
||||
x_bytes.marshal(x, cpuEndian)
|
||||
|
||||
check:
|
||||
# Check equality in the Montgomery domain
|
||||
@ -120,7 +120,7 @@ proc main() =
|
||||
x -= y
|
||||
|
||||
var x_bytes: array[8, byte]
|
||||
x_bytes.exportRawUint(x, cpuEndian)
|
||||
x_bytes.marshal(x, cpuEndian)
|
||||
|
||||
check:
|
||||
# Check equality in the Montgomery domain
|
||||
@ -139,7 +139,7 @@ proc main() =
|
||||
r.prod(x, y)
|
||||
|
||||
var r_bytes: array[8, byte]
|
||||
r_bytes.exportRawUint(r, cpuEndian)
|
||||
r_bytes.marshal(r, cpuEndian)
|
||||
|
||||
check:
|
||||
# Check equality in the Montgomery domain
|
||||
@ -157,7 +157,7 @@ proc main() =
|
||||
r.prod(x, y)
|
||||
|
||||
var r_bytes: array[8, byte]
|
||||
r_bytes.exportRawUint(r, cpuEndian)
|
||||
r_bytes.marshal(r, cpuEndian)
|
||||
|
||||
check:
|
||||
# Check equality in the Montgomery domain
|
||||
@ -176,7 +176,7 @@ proc main() =
|
||||
x += y
|
||||
|
||||
var x_bytes: array[8, byte]
|
||||
x_bytes.exportRawUint(x, cpuEndian)
|
||||
x_bytes.marshal(x, cpuEndian)
|
||||
let new_x = cast[uint64](x_bytes)
|
||||
|
||||
check:
|
||||
@ -195,7 +195,7 @@ proc main() =
|
||||
x += y
|
||||
|
||||
var x_bytes: array[8, byte]
|
||||
x_bytes.exportRawUint(x, cpuEndian)
|
||||
x_bytes.marshal(x, cpuEndian)
|
||||
let new_x = cast[uint64](x_bytes)
|
||||
|
||||
check:
|
||||
@ -214,7 +214,7 @@ proc main() =
|
||||
x += y
|
||||
|
||||
var x_bytes: array[8, byte]
|
||||
x_bytes.exportRawUint(x, cpuEndian)
|
||||
x_bytes.marshal(x, cpuEndian)
|
||||
let new_x = cast[uint64](x_bytes)
|
||||
|
||||
check:
|
||||
@ -234,7 +234,7 @@ proc main() =
|
||||
x -= y
|
||||
|
||||
var x_bytes: array[8, byte]
|
||||
x_bytes.exportRawUint(x, cpuEndian)
|
||||
x_bytes.marshal(x, cpuEndian)
|
||||
let new_x = cast[uint64](x_bytes)
|
||||
|
||||
check:
|
||||
@ -253,7 +253,7 @@ proc main() =
|
||||
x -= y
|
||||
|
||||
var x_bytes: array[8, byte]
|
||||
x_bytes.exportRawUint(x, cpuEndian)
|
||||
x_bytes.marshal(x, cpuEndian)
|
||||
let new_x = cast[uint64](x_bytes)
|
||||
|
||||
check:
|
||||
@ -273,7 +273,7 @@ proc main() =
|
||||
r.prod(x, y)
|
||||
|
||||
var r_bytes: array[8, byte]
|
||||
r_bytes.exportRawUint(r, cpuEndian)
|
||||
r_bytes.marshal(r, cpuEndian)
|
||||
let new_r = cast[uint64](r_bytes)
|
||||
|
||||
check:
|
||||
@ -292,7 +292,7 @@ proc main() =
|
||||
r.prod(x, y)
|
||||
|
||||
var r_bytes: array[8, byte]
|
||||
r_bytes.exportRawUint(r, cpuEndian)
|
||||
r_bytes.marshal(r, cpuEndian)
|
||||
let new_r = cast[uint64](r_bytes)
|
||||
|
||||
check:
|
||||
@ -322,7 +322,7 @@ proc largeField() =
|
||||
block:
|
||||
var a: Fp[Secp256k1]
|
||||
a.mres = Fp[Secp256k1].getMontyPrimeMinus1()
|
||||
let expected = BigInt[256].fromHex"0xFFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2E"
|
||||
let expected = BigInt[256].fromHex"0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2E"
|
||||
|
||||
var r: BigInt[256]
|
||||
r.fromField(a)
|
||||
@ -333,8 +333,8 @@ proc largeField() =
|
||||
var d: FpDbl[Secp256k1]
|
||||
|
||||
# Set Montgomery repr to the largest field element in Montgomery Residue form
|
||||
a.mres = BigInt[256].fromHex"0xFFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2E"
|
||||
d.limbs2x = (BigInt[512].fromHex"0xFFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2E").limbs
|
||||
a.mres = BigInt[256].fromHex"0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2E"
|
||||
d.limbs2x = (BigInt[512].fromHex"0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2E").limbs
|
||||
|
||||
var r, expected: BigInt[256]
|
||||
|
||||
|
@ -43,7 +43,7 @@ proc main() =
|
||||
r.prod(n, n)
|
||||
|
||||
var r_bytes: array[8, byte]
|
||||
r_bytes.exportRawUint(r, cpuEndian)
|
||||
r_bytes.marshal(r, cpuEndian)
|
||||
let rU64 = cast[uint64](r_bytes)
|
||||
|
||||
check:
|
||||
@ -61,7 +61,7 @@ proc main() =
|
||||
n.pow(exponent)
|
||||
|
||||
var n_bytes: array[8, byte]
|
||||
n_bytes.exportRawUint(n, cpuEndian)
|
||||
n_bytes.marshal(n, cpuEndian)
|
||||
let r = cast[uint64](n_bytes)
|
||||
|
||||
check:
|
||||
@ -79,7 +79,7 @@ proc main() =
|
||||
n.pow(exponent)
|
||||
|
||||
var n_bytes: array[8, byte]
|
||||
n_bytes.exportRawUint(n, cpuEndian)
|
||||
n_bytes.marshal(n, cpuEndian)
|
||||
let r = cast[uint64](n_bytes)
|
||||
|
||||
check:
|
||||
@ -97,7 +97,7 @@ proc main() =
|
||||
n.pow(exponent)
|
||||
|
||||
var n_bytes: array[8, byte]
|
||||
n_bytes.exportRawUint(n, cpuEndian)
|
||||
n_bytes.marshal(n, cpuEndian)
|
||||
let r = cast[uint64](n_bytes)
|
||||
|
||||
check:
|
||||
@ -115,7 +115,7 @@ proc main() =
|
||||
n.pow(exponent)
|
||||
|
||||
var n_bytes: array[8, byte]
|
||||
n_bytes.exportRawUint(n, cpuEndian)
|
||||
n_bytes.marshal(n, cpuEndian)
|
||||
let r = cast[uint64](n_bytes)
|
||||
|
||||
check:
|
||||
|
@ -41,7 +41,7 @@ proc exhaustiveCheck(C: static Curve, modulus: static int) =
|
||||
a.square()
|
||||
|
||||
var r_bytes: array[8, byte]
|
||||
r_bytes.exportRawUint(a, cpuEndian)
|
||||
r_bytes.marshal(a, cpuEndian)
|
||||
let r = uint16(cast[uint64](r_bytes))
|
||||
|
||||
squares_to_roots.mgetOrPut(r, default(set[uint16])).incl(i)
|
||||
@ -69,7 +69,7 @@ proc exhaustiveCheck(C: static Curve, modulus: static int) =
|
||||
check: bool(a == a2)
|
||||
|
||||
var r_bytes: array[8, byte]
|
||||
r_bytes.exportRawUint(a, cpuEndian)
|
||||
r_bytes.marshal(a, cpuEndian)
|
||||
let r = uint16(cast[uint64](r_bytes))
|
||||
|
||||
# r is one of the 2 square roots of `i`
|
||||
|
@ -73,8 +73,8 @@ proc binary_prologue[C: static Curve, N: static int](
|
||||
doAssert len >= bW, "Expected at most " & $len & " bytes but wrote " & $bW & " for " & toHex(bBuf) & " (big-endian)"
|
||||
|
||||
# Build the bigint
|
||||
aTest = Fp[C].fromBig BigInt[bits].fromRawUint(aBuf.toOpenArray(0, aW-1), bigEndian)
|
||||
bTest = Fp[C].fromBig BigInt[bits].fromRawUint(bBuf.toOpenArray(0, bW-1), bigEndian)
|
||||
aTest = Fp[C].fromBig BigInt[bits].unmarshal(aBuf.toOpenArray(0, aW-1), bigEndian)
|
||||
bTest = Fp[C].fromBig BigInt[bits].unmarshal(bBuf.toOpenArray(0, bW-1), bigEndian)
|
||||
|
||||
proc binary_epilogue[C: static Curve, N: static int](
|
||||
r, a, b: mpz_t,
|
||||
@ -90,7 +90,7 @@ proc binary_epilogue[C: static Curve, N: static int](
|
||||
discard mpz_export(rGMP[0].addr, rW.addr, GMP_MostSignificantWordFirst, 1, GMP_WordNativeEndian, 0, r)
|
||||
|
||||
var rConstantine: array[N, byte]
|
||||
exportRawUint(rConstantine, rTest, bigEndian)
|
||||
marshal(rConstantine, rTest, bigEndian)
|
||||
|
||||
# Note: in bigEndian, GMP aligns left while constantine aligns right
|
||||
doAssert rGMP.toOpenArray(0, rW-1) == rConstantine.toOpenArray(N-rW, N-1), block:
|
||||
|
@ -27,7 +27,7 @@ proc main() =
|
||||
block: # Sanity check
|
||||
let x = 0'u64
|
||||
let x_bytes = cast[array[8, byte]](x)
|
||||
let big = BigInt[64].fromRawUint(x_bytes, cpuEndian)
|
||||
let big = BigInt[64].unmarshal(x_bytes, cpuEndian)
|
||||
|
||||
check:
|
||||
T(big.limbs[0]) == 0
|
||||
@ -37,29 +37,29 @@ proc main() =
|
||||
# "Little-endian" - 2^63
|
||||
let x = 1'u64 shl 63
|
||||
let x_bytes = cast[array[8, byte]](x)
|
||||
let big = BigInt[64].fromRawUint(x_bytes, littleEndian) # It's fine even on big-endian platform. We only want the byte-pattern
|
||||
let big = BigInt[64].unmarshal(x_bytes, littleEndian) # It's fine even on big-endian platform. We only want the byte-pattern
|
||||
|
||||
var r_bytes: array[8, byte]
|
||||
exportRawUint(r_bytes, big, littleEndian)
|
||||
marshal(r_bytes, big, littleEndian)
|
||||
check: x_bytes == r_bytes
|
||||
|
||||
block: # "Little-endian" - single random
|
||||
let x = rng.random_unsafe(uint64)
|
||||
let x_bytes = cast[array[8, byte]](x)
|
||||
let big = BigInt[64].fromRawUint(x_bytes, littleEndian) # It's fine even on big-endian platform. We only want the byte-pattern
|
||||
let big = BigInt[64].unmarshal(x_bytes, littleEndian) # It's fine even on big-endian platform. We only want the byte-pattern
|
||||
|
||||
var r_bytes: array[8, byte]
|
||||
exportRawUint(r_bytes, big, littleEndian)
|
||||
marshal(r_bytes, big, littleEndian)
|
||||
check: x_bytes == r_bytes
|
||||
|
||||
block: # "Little-endian" - 10 random cases
|
||||
for _ in 0 ..< 10:
|
||||
let x = rng.random_unsafe(uint64)
|
||||
let x_bytes = cast[array[8, byte]](x)
|
||||
let big = BigInt[64].fromRawUint(x_bytes, littleEndian) # It's fine even on big-endian platform. We only want the byte-pattern
|
||||
let big = BigInt[64].unmarshal(x_bytes, littleEndian) # It's fine even on big-endian platform. We only want the byte-pattern
|
||||
|
||||
var r_bytes: array[8, byte]
|
||||
exportRawUint(r_bytes, big, littleEndian)
|
||||
marshal(r_bytes, big, littleEndian)
|
||||
check: x_bytes == r_bytes
|
||||
|
||||
test "Round trip on elliptic curve constants":
|
||||
|
@ -32,7 +32,7 @@ proc main() =
|
||||
f.fromUint(x)
|
||||
|
||||
var r_bytes: array[sizeof(BaseType), byte]
|
||||
exportRawUint(r_bytes, f, littleEndian)
|
||||
marshal(r_bytes, f, littleEndian)
|
||||
check: x_bytes == r_bytes
|
||||
|
||||
block:
|
||||
@ -43,7 +43,7 @@ proc main() =
|
||||
f.fromUint(x)
|
||||
|
||||
var r_bytes: array[sizeof(BaseType), byte]
|
||||
exportRawUint(r_bytes, f, littleEndian)
|
||||
marshal(r_bytes, f, littleEndian)
|
||||
check: x_bytes == r_bytes
|
||||
|
||||
# Mersenne 61 ---------------------------------
|
||||
@ -55,7 +55,7 @@ proc main() =
|
||||
f.fromUint(x)
|
||||
|
||||
var r_bytes: array[8, byte]
|
||||
exportRawUint(r_bytes, f, littleEndian)
|
||||
marshal(r_bytes, f, littleEndian)
|
||||
check: x_bytes == r_bytes
|
||||
|
||||
block:
|
||||
@ -66,7 +66,7 @@ proc main() =
|
||||
f.fromUint(x)
|
||||
|
||||
var r_bytes: array[8, byte]
|
||||
exportRawUint(r_bytes, f, littleEndian)
|
||||
marshal(r_bytes, f, littleEndian)
|
||||
check: x_bytes == r_bytes
|
||||
|
||||
block:
|
||||
@ -77,7 +77,7 @@ proc main() =
|
||||
f.fromUint(x)
|
||||
|
||||
var r_bytes: array[8, byte]
|
||||
exportRawUint(r_bytes, f, littleEndian)
|
||||
marshal(r_bytes, f, littleEndian)
|
||||
check: x_bytes == r_bytes
|
||||
|
||||
block:
|
||||
@ -88,7 +88,7 @@ proc main() =
|
||||
f.fromUint(x)
|
||||
|
||||
var r_bytes: array[8, byte]
|
||||
exportRawUint(r_bytes, f, littleEndian)
|
||||
marshal(r_bytes, f, littleEndian)
|
||||
check: x_bytes == r_bytes
|
||||
|
||||
# Mersenne 127 ---------------------------------
|
||||
@ -100,7 +100,7 @@ proc main() =
|
||||
f.fromUint(x)
|
||||
|
||||
var r_bytes: array[16, byte]
|
||||
exportRawUint(r_bytes, f, littleEndian)
|
||||
marshal(r_bytes, f, littleEndian)
|
||||
check: x_bytes == r_bytes[0 ..< 8]
|
||||
|
||||
block: # "Little-endian" - single random
|
||||
@ -110,7 +110,7 @@ proc main() =
|
||||
f.fromUint(x)
|
||||
|
||||
var r_bytes: array[16, byte]
|
||||
exportRawUint(r_bytes, f, littleEndian)
|
||||
marshal(r_bytes, f, littleEndian)
|
||||
check: x_bytes == r_bytes[0 ..< 8]
|
||||
|
||||
block: # "Little-endian" - 10 random cases
|
||||
@ -121,7 +121,7 @@ proc main() =
|
||||
f.fromUint(x)
|
||||
|
||||
var r_bytes: array[16, byte]
|
||||
exportRawUint(r_bytes, f, littleEndian)
|
||||
marshal(r_bytes, f, littleEndian)
|
||||
check: x_bytes == r_bytes[0 ..< 8]
|
||||
|
||||
test "Round trip on large constant":
|
||||
|
Loading…
x
Reference in New Issue
Block a user