mirror of
https://github.com/status-im/constantine.git
synced 2025-02-22 08:58:05 +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 =
|
func serialize_secret_key*(dst: var array[32, byte], secret_key: SecretKey): CttBLSStatus =
|
||||||
## Serialize a secret key
|
## Serialize a secret key
|
||||||
## Returns cttBLS_Success if successful
|
## Returns cttBLS_Success if successful
|
||||||
dst.exportRawUint(secret_key.raw, bigEndian)
|
dst.marshal(secret_key.raw, bigEndian)
|
||||||
return cttBLS_Success
|
return cttBLS_Success
|
||||||
|
|
||||||
func serialize_public_key_compressed*(dst: var array[48, byte], public_key: PublicKey): CttBLSStatus =
|
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
|
dst[0] = byte 0b11000000 # Compressed + Infinity
|
||||||
return cttBLS_Success
|
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 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
|
# The lexicographically largest will have bit 381 set to 1
|
||||||
# (and bit 383 for the compressed representation)
|
# (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
|
dst[0] = byte 0b11000000 # Compressed + Infinity
|
||||||
return cttBLS_Success
|
return cttBLS_Success
|
||||||
|
|
||||||
dst.toOpenArray(0, 48-1).exportRawUint(signature.raw.x.c1, bigEndian)
|
dst.toOpenArray(0, 48-1).marshal(signature.raw.x.c1, bigEndian)
|
||||||
dst.toOpenArray(48, 96-1).exportRawUint(signature.raw.x.c0, bigEndian)
|
dst.toOpenArray(48, 96-1).marshal(signature.raw.x.c0, bigEndian)
|
||||||
|
|
||||||
let isLexicographicallyLargest =
|
let isLexicographicallyLargest =
|
||||||
if signature.raw.y.c1.isZero().bool():
|
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.
|
## 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.
|
## 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)
|
let status = validate_seckey(dst)
|
||||||
if status != cttBLS_Success:
|
if status != cttBLS_Success:
|
||||||
dst.raw.setZero()
|
dst.raw.setZero()
|
||||||
@ -240,7 +240,7 @@ func deserialize_public_key_compressed_unchecked*(dst: var PublicKey, src: array
|
|||||||
|
|
||||||
# General case
|
# General case
|
||||||
var t{.noInit.}: matchingBigInt(BLS12_381)
|
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
|
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()):
|
if bool(t >= BLS12_381.Mod()):
|
||||||
@ -294,7 +294,7 @@ func deserialize_signature_compressed_unchecked*(dst: var Signature, src: array[
|
|||||||
|
|
||||||
# General case
|
# General case
|
||||||
var t{.noInit.}: matchingBigInt(BLS12_381)
|
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
|
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()):
|
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]
|
var x{.noInit.}: Fp2[BLS12_381]
|
||||||
x.c1.fromBig(t)
|
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()):
|
if bool(t >= BLS12_381.Mod()):
|
||||||
return cttBLS_CoordinateGreaterOrEqualThanModulus
|
return cttBLS_CoordinateGreaterOrEqualThanModulus
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ func parseRawUint(
|
|||||||
## Return false if the integer is larger than the field modulus.
|
## Return false if the integer is larger than the field modulus.
|
||||||
## Returns true on success.
|
## Returns true on success.
|
||||||
var big {.noInit.}: BigInt[254]
|
var big {.noInit.}: BigInt[254]
|
||||||
big.fromRawUint(src, bigEndian)
|
big.unmarshal(src, bigEndian)
|
||||||
|
|
||||||
if not bool(big < Mod(BN254_Snarks)):
|
if not bool(big < Mod(BN254_Snarks)):
|
||||||
return cttEVM_IntLargerThanModulus
|
return cttEVM_IntLargerThanModulus
|
||||||
@ -136,10 +136,10 @@ func eth_evm_ecadd*(
|
|||||||
var aff{.noInit.}: ECP_ShortW_Aff[Fp[BN254_Snarks], G1]
|
var aff{.noInit.}: ECP_ShortW_Aff[Fp[BN254_Snarks], G1]
|
||||||
aff.affine(R)
|
aff.affine(R)
|
||||||
|
|
||||||
r.toOpenArray(0, 31).exportRawUint(
|
r.toOpenArray(0, 31).marshal(
|
||||||
aff.x, bigEndian
|
aff.x, bigEndian
|
||||||
)
|
)
|
||||||
r.toOpenArray(32, 63).exportRawUint(
|
r.toOpenArray(32, 63).marshal(
|
||||||
aff.y, bigEndian
|
aff.y, bigEndian
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -185,7 +185,7 @@ func eth_evm_ecmul*(
|
|||||||
|
|
||||||
var smod{.noInit.}: Fr[BN254_Snarks]
|
var smod{.noInit.}: Fr[BN254_Snarks]
|
||||||
var s{.noInit.}: BigInt[256]
|
var s{.noInit.}: BigInt[256]
|
||||||
s.fromRawUint(padded.toOpenArray(64,95), bigEndian)
|
s.unmarshal(padded.toOpenArray(64,95), bigEndian)
|
||||||
|
|
||||||
when true:
|
when true:
|
||||||
# The spec allows s to be bigger than the curve order r and the field modulus p.
|
# 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]
|
var aff{.noInit.}: ECP_ShortW_Aff[Fp[BN254_Snarks], G1]
|
||||||
aff.affine(P)
|
aff.affine(P)
|
||||||
|
|
||||||
r.toOpenArray(0, 31).exportRawUint(
|
r.toOpenArray(0, 31).marshal(
|
||||||
aff.x, bigEndian
|
aff.x, bigEndian
|
||||||
)
|
)
|
||||||
r.toOpenArray(32, 63).exportRawUint(
|
r.toOpenArray(32, 63).marshal(
|
||||||
aff.y, bigEndian
|
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)
|
template tv: untyped = uniform_bytes.toOpenArray(elm_offset, elm_offset + L-1)
|
||||||
|
|
||||||
var big2x {.noInit.}: BigInt[2 * getCurveBitwidth(Field.C)]
|
var big2x {.noInit.}: BigInt[2 * getCurveBitwidth(Field.C)]
|
||||||
big2x.fromRawUint(tv, bigEndian)
|
big2x.unmarshal(tv, bigEndian)
|
||||||
|
|
||||||
# Reduces modulo p and output in Montgomery domain
|
# Reduces modulo p and output in Montgomery domain
|
||||||
when m == 1:
|
when m == 1:
|
||||||
|
@ -114,7 +114,7 @@ func powMontUnsafeExponent*[mBits: static int](
|
|||||||
var scratchSpace {.noInit.}: array[scratchLen, Limbs[mBits.wordsRequired]]
|
var scratchSpace {.noInit.}: array[scratchLen, Limbs[mBits.wordsRequired]]
|
||||||
powMontUnsafeExponent(a.limbs, exponent, M.limbs, one.limbs, negInvModWord, scratchSpace, spareBits)
|
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
|
# Workaround recursive dependencies
|
||||||
|
|
||||||
func powMont*[mBits, eBits: static int](
|
func powMont*[mBits, eBits: static int](
|
||||||
@ -132,7 +132,7 @@ func powMont*[mBits, eBits: static int](
|
|||||||
## This is constant-time: the window optimization does
|
## This is constant-time: the window optimization does
|
||||||
## not reveal the exponent bits or hamming weight
|
## not reveal the exponent bits or hamming weight
|
||||||
var expBE {.noInit.}: array[(ebits + 7) div 8, byte]
|
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)
|
powMont(a, expBE, M, one, negInvModWord, windowSize, spareBits)
|
||||||
|
|
||||||
@ -155,7 +155,7 @@ func powMontUnsafeExponent*[mBits, eBits: static int](
|
|||||||
## This uses fixed window optimization
|
## This uses fixed window optimization
|
||||||
## A window size in the range [1, 5] must be chosen
|
## A window size in the range [1, 5] must be chosen
|
||||||
var expBE {.noInit.}: array[(ebits + 7) div 8, byte]
|
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)
|
powMontUnsafeExponent(a, expBE, M, one, negInvModWord, windowSize, spareBits)
|
||||||
|
|
||||||
|
@ -367,7 +367,7 @@ func div10*(a: var Limbs): SecretWord =
|
|||||||
# Normalize
|
# Normalize
|
||||||
hi = (hi shl clz) or (lo shr (WordBitWidth - clz))
|
hi = (hi shl clz) or (lo shr (WordBitWidth - clz))
|
||||||
lo = lo shl clz
|
lo = lo shl clz
|
||||||
unsafeDiv2n1n(a[i], result, hi, lo, norm10)
|
div2n1n(a[i], result, hi, lo, norm10)
|
||||||
# Undo normalization
|
# Undo normalization
|
||||||
result = result shr clz
|
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
|
# Get a quotient q, at most we will be 2 iterations off
|
||||||
# from the true quotient
|
# from the true quotient
|
||||||
var q, r: SecretWord
|
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
|
q = mux( # If n_hi == divisor
|
||||||
a0 == m0, MaxWord, # Quotient == MaxWord (0b1111...1111)
|
a0 == m0, MaxWord, # Quotient == MaxWord (0b1111...1111)
|
||||||
mux(
|
mux(
|
||||||
@ -226,7 +226,7 @@ func shlAddMod(a: LimbsViewMut, aLen: int,
|
|||||||
let m0 = M[0] shl (WordBitWidth-R)
|
let m0 = M[0] shl (WordBitWidth-R)
|
||||||
|
|
||||||
var q, r: SecretWord
|
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)
|
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)
|
## - On input ``a`` is the base, on ``output`` a = a^exponent (mod M)
|
||||||
## ``a`` is in the Montgomery domain
|
## ``a`` is in the Montgomery domain
|
||||||
## - ``exponent`` is the exponent in big-endian canonical format (octet-string)
|
## - ``exponent`` is the exponent in big-endian canonical format (octet-string)
|
||||||
## Use ``exportRawUint`` for conversion
|
## Use ``marshal`` for conversion
|
||||||
## - ``M`` is the modulus
|
## - ``M`` is the modulus
|
||||||
## - ``one`` is 1 (mod M) in montgomery representation
|
## - ``one`` is 1 (mod M) in montgomery representation
|
||||||
## - ``m0ninv`` is the montgomery magic constant "-1/M[0] mod 2^WordBitWidth"
|
## - ``m0ninv`` is the montgomery magic constant "-1/M[0] mod 2^WordBitWidth"
|
||||||
|
@ -69,7 +69,7 @@ declareCurves:
|
|||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
curve P224: # NIST P-224
|
curve P224: # NIST P-224
|
||||||
bitwidth: 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
|
curve BN254_Nogami: # Integer Variable χ–Based Ate Pairing, 2008, Nogami et al
|
||||||
bitwidth: 254
|
bitwidth: 254
|
||||||
modulus: "0x2523648240000001ba344d80000000086121000000000013a700000000000013"
|
modulus: "0x2523648240000001ba344d80000000086121000000000013a700000000000013"
|
||||||
@ -173,7 +173,7 @@ declareCurves:
|
|||||||
modulus: "0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff"
|
modulus: "0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff"
|
||||||
curve Secp256k1: # Bitcoin curve
|
curve Secp256k1: # Bitcoin curve
|
||||||
bitwidth: 256
|
bitwidth: 256
|
||||||
modulus: "0xFFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2F"
|
modulus: "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"
|
||||||
curve BLS12_377:
|
curve BLS12_377:
|
||||||
# Zexe curve
|
# Zexe curve
|
||||||
# (p41) https://eprint.iacr.org/2018/962.pdf
|
# (p41) https://eprint.iacr.org/2018/962.pdf
|
||||||
|
@ -380,7 +380,7 @@ func primeMinus2_BE*[bits: static int](
|
|||||||
var tmp = P
|
var tmp = P
|
||||||
discard tmp.sub(2)
|
discard tmp.sub(2)
|
||||||
|
|
||||||
result.exportRawUint(tmp, bigEndian)
|
result.marshal(tmp, bigEndian)
|
||||||
|
|
||||||
func primePlus1div2*(P: BigInt): BigInt =
|
func primePlus1div2*(P: BigInt): BigInt =
|
||||||
## Compute (P+1)/2, assumes P is odd
|
## Compute (P+1)/2, assumes P is odd
|
||||||
@ -415,7 +415,7 @@ func primeMinus1div2_BE*[bits: static int](
|
|||||||
discard tmp.sub(1)
|
discard tmp.sub(1)
|
||||||
tmp.shiftRight(1)
|
tmp.shiftRight(1)
|
||||||
|
|
||||||
result.exportRawUint(tmp, bigEndian)
|
result.marshal(tmp, bigEndian)
|
||||||
|
|
||||||
func primeMinus3div4_BE*[bits: static int](
|
func primeMinus3div4_BE*[bits: static int](
|
||||||
P: BigInt[bits]
|
P: BigInt[bits]
|
||||||
@ -434,7 +434,7 @@ func primeMinus3div4_BE*[bits: static int](
|
|||||||
discard tmp.sub(3)
|
discard tmp.sub(3)
|
||||||
tmp.shiftRight(2)
|
tmp.shiftRight(2)
|
||||||
|
|
||||||
result.exportRawUint(tmp, bigEndian)
|
result.marshal(tmp, bigEndian)
|
||||||
|
|
||||||
func primeMinus5div8_BE*[bits: static int](
|
func primeMinus5div8_BE*[bits: static int](
|
||||||
P: BigInt[bits]
|
P: BigInt[bits]
|
||||||
@ -453,7 +453,7 @@ func primeMinus5div8_BE*[bits: static int](
|
|||||||
discard tmp.sub(5)
|
discard tmp.sub(5)
|
||||||
tmp.shiftRight(3)
|
tmp.shiftRight(3)
|
||||||
|
|
||||||
result.exportRawUint(tmp, bigEndian)
|
result.marshal(tmp, bigEndian)
|
||||||
|
|
||||||
func primePlus1Div4_BE*[bits: static int](
|
func primePlus1Div4_BE*[bits: static int](
|
||||||
P: BigInt[bits]
|
P: BigInt[bits]
|
||||||
@ -475,14 +475,14 @@ func primePlus1Div4_BE*[bits: static int](
|
|||||||
# then divide by 2
|
# then divide by 2
|
||||||
tmp.shiftRight(1)
|
tmp.shiftRight(1)
|
||||||
|
|
||||||
result.exportRawUint(tmp, bigEndian)
|
result.marshal(tmp, bigEndian)
|
||||||
|
|
||||||
func toCanonicalIntRepr*[bits: static int](
|
func toCanonicalIntRepr*[bits: static int](
|
||||||
a: BigInt[bits]
|
a: BigInt[bits]
|
||||||
): array[(bits+7) div 8, byte] {.noInit.} =
|
): array[(bits+7) div 8, byte] {.noInit.} =
|
||||||
## Export a bigint to its canonical BigEndian representation
|
## Export a bigint to its canonical BigEndian representation
|
||||||
## (octet-string)
|
## (octet-string)
|
||||||
result.exportRawUint(a, bigEndian)
|
result.marshal(a, bigEndian)
|
||||||
|
|
||||||
# ############################################################
|
# ############################################################
|
||||||
#
|
#
|
||||||
|
@ -29,20 +29,24 @@ type
|
|||||||
|
|
||||||
|
|
||||||
debug:
|
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 =
|
func toString*(a: Limbs): string =
|
||||||
result = "["
|
result = "["
|
||||||
result.add " 0x" & toHex(BaseType(a[0]))
|
result.add " " & toHex(a[0])
|
||||||
for i in 1 ..< a.len:
|
for i in 1 ..< a.len:
|
||||||
result.add ", 0x" & toHex(BaseType(a[i]))
|
result.add ", " & toHex(a[i])
|
||||||
result.add "])"
|
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 =
|
func `$`*(a: BigInt): string =
|
||||||
result = "BigInt["
|
result = "BigInt["
|
||||||
result.add $BigInt.bits
|
result.add $BigInt.bits
|
||||||
|
@ -224,7 +224,7 @@ func scalarMulGeneric*[EC](P: var EC, scalar: BigInt, window: static int = 5) =
|
|||||||
var
|
var
|
||||||
scratchSpace: array[1 shl window, EC]
|
scratchSpace: array[1 shl window, EC]
|
||||||
scalarCanonicalBE: array[(scalar.bits+7) div 8, byte] # canonical big endian representation
|
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)
|
P.scalarMulGeneric(scalarCanonicalBE, scratchSpace)
|
||||||
|
|
||||||
func scalarMul*[EC](
|
func scalarMul*[EC](
|
||||||
|
@ -184,5 +184,5 @@ func powUnsafeExponent*[F; bits: static int](
|
|||||||
## - power analysis
|
## - power analysis
|
||||||
## - timing analysis
|
## - timing analysis
|
||||||
var expBE {.noInit.}: array[(bits + 7) div 8, byte]
|
var expBE {.noInit.}: array[(bits + 7) div 8, byte]
|
||||||
expBE.exportRawUint(exponent, bigEndian)
|
expBE.marshal(exponent, bigEndian)
|
||||||
a.powUnsafeExponent(expBE, window)
|
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.
|
big integer.
|
||||||
|
|
||||||
The bytes API are constant-time and do not allocate:
|
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
|
- Machine sized integers: fromUint
|
||||||
|
|
||||||
If you decide to use the internal hex or decimal API, you SHOULD ensure that the data is well-formatted:
|
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:
|
The hexadecimal API allocates:
|
||||||
- `toHex` is constant-time
|
- `toHex` is constant-time
|
||||||
- `appendHex` 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.
|
(compile-time) configuration. It does not allocate.
|
||||||
In particular it scans spaces and underscores and checks if the string
|
In particular it scans spaces and underscores and checks if the string
|
||||||
starts with '0x'.
|
starts with '0x'.
|
||||||
|
|
||||||
The decimal API allocates:
|
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.
|
- `fromDecimal` is constant-time and does not allocate.
|
||||||
|
|
||||||
## Avoiding secret mistakes
|
## Avoiding secret mistakes
|
||||||
|
@ -36,7 +36,7 @@ export BigInt, wordsRequired
|
|||||||
# prototyping, research and debugging purposes,
|
# prototyping, research and debugging purposes,
|
||||||
# and can use exceptions.
|
# and can use exceptions.
|
||||||
|
|
||||||
func fromRawUintLE(
|
func unmarshalLE(
|
||||||
dst: var BigInt,
|
dst: var BigInt,
|
||||||
src: openarray[byte]) =
|
src: openarray[byte]) =
|
||||||
## Parse an unsigned integer from its canonical
|
## Parse an unsigned integer from its canonical
|
||||||
@ -74,7 +74,7 @@ func fromRawUintLE(
|
|||||||
for i in dst_idx + 1 ..< dst.limbs.len:
|
for i in dst_idx + 1 ..< dst.limbs.len:
|
||||||
dst.limbs[i] = Zero
|
dst.limbs[i] = Zero
|
||||||
|
|
||||||
func fromRawUintBE(
|
func unmarshalBE(
|
||||||
dst: var BigInt,
|
dst: var BigInt,
|
||||||
src: openarray[byte]) =
|
src: openarray[byte]) =
|
||||||
## Parse an unsigned integer from its canonical
|
## Parse an unsigned integer from its canonical
|
||||||
@ -114,7 +114,7 @@ func fromRawUintBE(
|
|||||||
for i in dst_idx + 1 ..< dst.limbs.len:
|
for i in dst_idx + 1 ..< dst.limbs.len:
|
||||||
dst.limbs[i] = Zero
|
dst.limbs[i] = Zero
|
||||||
|
|
||||||
func fromRawUint*(
|
func unmarshal*(
|
||||||
dst: var BigInt,
|
dst: var BigInt,
|
||||||
src: openarray[byte],
|
src: openarray[byte],
|
||||||
srcEndianness: static Endianness) =
|
srcEndianness: static Endianness) =
|
||||||
@ -129,11 +129,11 @@ func fromRawUint*(
|
|||||||
## from a canonical integer representation
|
## from a canonical integer representation
|
||||||
|
|
||||||
when srcEndianness == littleEndian:
|
when srcEndianness == littleEndian:
|
||||||
dst.fromRawUintLE(src)
|
dst.unmarshalLE(src)
|
||||||
else:
|
else:
|
||||||
dst.fromRawUintBE(src)
|
dst.unmarshalBE(src)
|
||||||
|
|
||||||
func fromRawUint*(
|
func unmarshal*(
|
||||||
T: type BigInt,
|
T: type BigInt,
|
||||||
src: openarray[byte],
|
src: openarray[byte],
|
||||||
srcEndianness: static Endianness): T {.inline.}=
|
srcEndianness: static Endianness): T {.inline.}=
|
||||||
@ -146,21 +146,21 @@ func fromRawUint*(
|
|||||||
##
|
##
|
||||||
## Can work at compile-time to embed curve moduli
|
## Can work at compile-time to embed curve moduli
|
||||||
## from a canonical integer representation
|
## from a canonical integer representation
|
||||||
result.fromRawUint(src, srcEndianness)
|
result.unmarshal(src, srcEndianness)
|
||||||
|
|
||||||
func fromUint*(
|
func fromUint*(
|
||||||
T: type BigInt,
|
T: type BigInt,
|
||||||
src: SomeUnsignedInt): T {.inline.}=
|
src: SomeUnsignedInt): T {.inline.}=
|
||||||
## Parse a regular unsigned integer
|
## Parse a regular unsigned integer
|
||||||
## and store it into a BigInt of size `bits`
|
## 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*(
|
func fromUint*(
|
||||||
dst: var BigInt,
|
dst: var BigInt,
|
||||||
src: SomeUnsignedInt) {.inline.}=
|
src: SomeUnsignedInt) {.inline.}=
|
||||||
## Parse a regular unsigned integer
|
## Parse a regular unsigned integer
|
||||||
## and store it into a BigInt of size `bits`
|
## 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):
|
for i in 0 ..< sizeof(src):
|
||||||
dst[startIdx+sizeof(src)-1-i] = toByte(src shr (i * 8))
|
dst[startIdx+sizeof(src)-1-i] = toByte(src shr (i * 8))
|
||||||
|
|
||||||
func exportRawUintLE(
|
func marshalLE(
|
||||||
dst: var openarray[byte],
|
dst: var openarray[byte],
|
||||||
src: BigInt) =
|
src: BigInt) =
|
||||||
## Serialize a bigint into its canonical little-endian representation
|
## 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))
|
dst[dst_idx+i] = toByte(lo shr ((tail-i)*8))
|
||||||
return
|
return
|
||||||
|
|
||||||
func exportRawUintBE(
|
func marshalBE(
|
||||||
dst: var openarray[byte],
|
dst: var openarray[byte],
|
||||||
src: BigInt) =
|
src: BigInt) =
|
||||||
## Serialize a bigint into its canonical big-endian representation
|
## 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))
|
dst[tail-1-i] = toByte(lo shr ((tail-i)*8))
|
||||||
return
|
return
|
||||||
|
|
||||||
func exportRawUint*(
|
func marshal*(
|
||||||
dst: var openarray[byte],
|
dst: var openarray[byte],
|
||||||
src: BigInt,
|
src: BigInt,
|
||||||
dstEndianness: static Endianness) =
|
dstEndianness: static Endianness) =
|
||||||
@ -301,9 +301,9 @@ func exportRawUint*(
|
|||||||
zeroMem(dst, dst.len)
|
zeroMem(dst, dst.len)
|
||||||
|
|
||||||
when dstEndianness == littleEndian:
|
when dstEndianness == littleEndian:
|
||||||
exportRawUintLE(dst, src)
|
marshalLE(dst, src)
|
||||||
else:
|
else:
|
||||||
exportRawUintBE(dst, src)
|
marshalBE(dst, src)
|
||||||
|
|
||||||
{.pop.} # {.push raises: [].}
|
{.pop.} # {.push raises: [].}
|
||||||
|
|
||||||
@ -312,54 +312,23 @@ func exportRawUint*(
|
|||||||
# Conversion helpers
|
# Conversion helpers
|
||||||
#
|
#
|
||||||
# ############################################################
|
# ############################################################
|
||||||
# TODO: constant-time
|
|
||||||
|
|
||||||
func readHexChar(c: char): uint8 {.inline.}=
|
func readHexChar(c: char): SecretWord {.inline.}=
|
||||||
## Converts an hex char to an int
|
## Converts an hex char to an int
|
||||||
## CT: leaks position of invalid input if any.
|
template sw(a: char or int): SecretWord = SecretWord(a)
|
||||||
## and leaks if 0..9 or a..f or A..F
|
const k = WordBitWidth - 1
|
||||||
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")
|
|
||||||
|
|
||||||
func skipPrefixes(current_idx: var int, str: string, radix: static range[2..16]) {.inline.} =
|
let c = sw(c)
|
||||||
## 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
|
|
||||||
|
|
||||||
if str.len < 2:
|
let lowercaseMask = not -(((c - sw'a') or (sw('f') - c)) shr k)
|
||||||
return
|
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)"
|
return val
|
||||||
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
|
|
||||||
|
|
||||||
func hexToPaddedByteArray*(hexStr: string, output: var openArray[byte], order: static[Endianness]) =
|
func hexToPaddedByteArray*(hexStr: string, output: var openArray[byte], order: static[Endianness]) =
|
||||||
## Read a hex string and store it in a byte array `output`.
|
## 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 source string must be hex big-endian.
|
||||||
## The destination array can be big or little 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
|
var
|
||||||
skip = 0
|
skip = Zero
|
||||||
dstIdx: int
|
dstIdx: int
|
||||||
shift = 4
|
shift = 4
|
||||||
skipPrefixes(skip, hexStr, 16)
|
|
||||||
|
if hexStr.len >= 2:
|
||||||
const blanks = {' ', '_'}
|
skip = sw(2)*(
|
||||||
let nonBlanksCount = countNonBlanks(hexStr, skip)
|
sw(hexStr[0] == '0') and
|
||||||
|
(sw(hexStr[1] == 'x') or sw(hexStr[1] == 'X'))
|
||||||
|
)
|
||||||
|
|
||||||
let maxStrSize = output.len * 2
|
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:
|
if size < maxStrSize:
|
||||||
# include extra byte if odd length
|
# 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
|
# 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:
|
for srcIdx in skip.int ..< hexStr.len:
|
||||||
if hexStr[srcIdx] in blanks:
|
let c = hexStr[srcIdx]
|
||||||
continue
|
let nibble = byte(c.readHexChar() shl shift)
|
||||||
|
|
||||||
let nibble = hexStr[srcIdx].readHexChar shl shift
|
|
||||||
when order == bigEndian:
|
when order == bigEndian:
|
||||||
output[dstIdx] = output[dstIdx] or nibble
|
output[dstIdx] = output[dstIdx] or nibble
|
||||||
else:
|
else:
|
||||||
@ -409,11 +389,13 @@ func nativeEndianToHex(bytes: openarray[byte], order: static[Endianness]): strin
|
|||||||
result[1] = 'x'
|
result[1] = 'x'
|
||||||
for i in 0 ..< bytes.len:
|
for i in 0 ..< bytes.len:
|
||||||
when order == system.cpuEndian:
|
when order == system.cpuEndian:
|
||||||
result[2 + 2*i] = hexChars[int bytes[i] shr 4 and 0xF]
|
let bi = bytes[i]
|
||||||
result[2 + 2*i+1] = hexChars[int bytes[i] and 0xF]
|
result[2 + 2*i] = hexChars.secretLookup(SecretWord bi shr 4 and 0xF)
|
||||||
|
result[2 + 2*i+1] = hexChars.secretLookup(SecretWord bi and 0xF)
|
||||||
else:
|
else:
|
||||||
result[2 + 2*i] = hexChars[int bytes[bytes.high - i] shr 4 and 0xF]
|
let bmi = bytes[bytes.high - i]
|
||||||
result[2 + 2*i+1] = hexChars[int bytes[bytes.high - i] and 0xF]
|
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
|
## Hex string is assumed big-endian
|
||||||
##
|
##
|
||||||
## This API is intended for configuration and debugging purposes
|
## Procedure is constant-time except for the presence (or absence) of the 0x prefix.
|
||||||
## Do not pass secret or private data to it.
|
##
|
||||||
|
## 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
|
## 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)
|
hexToPaddedByteArray(s, bytes, bigEndian)
|
||||||
|
|
||||||
# 2. Convert canonical uint to Big Int
|
# 2. Convert canonical uint to Big Int
|
||||||
a.fromRawUint(bytes, bigEndian)
|
a.unmarshal(bytes, bigEndian)
|
||||||
|
|
||||||
func fromHex*(T: type BigInt, s: string): T {.noInit.} =
|
func fromHex*(T: type BigInt, s: string): T {.noInit.} =
|
||||||
## Convert a hex string to BigInt that can hold
|
## 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
|
## Hex string is assumed big-endian
|
||||||
##
|
##
|
||||||
## This API is intended for configuration and debugging purposes
|
## Procedure is constant-time except for the presence (or absence) of the 0x prefix.
|
||||||
## Do not pass secret or private data to it.
|
##
|
||||||
|
## 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
|
## Can work at compile-time to declare curve moduli from their hex strings
|
||||||
result.fromHex(s)
|
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
|
## This is useful to reduce the number of allocations when serializing
|
||||||
## Fp towers
|
## Fp towers
|
||||||
|
##
|
||||||
|
## This function may allocate.
|
||||||
|
|
||||||
# 1. Convert Big Int to canonical uint
|
# 1. Convert Big Int to canonical uint
|
||||||
const canonLen = (big.bits + 8 - 1) div 8
|
const canonLen = (big.bits + 8 - 1) div 8
|
||||||
var bytes: array[canonLen, byte]
|
var bytes: array[canonLen, byte]
|
||||||
exportRawUint(bytes, big, cpuEndian)
|
marshal(bytes, big, cpuEndian)
|
||||||
|
|
||||||
# 2 Convert canonical uint to hex
|
# 2 Convert canonical uint to hex
|
||||||
dst.add bytes.nativeEndianToHex(order)
|
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.
|
# 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
|
# 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 =
|
func decimalLength(bits: static int): int =
|
||||||
doAssert bits < (high(uint) div log10_2_Num),
|
doAssert bits < (high(uint) div log10_2_Num),
|
||||||
@ -659,10 +645,11 @@ func decimalLength(bits: static int): int =
|
|||||||
func toDecimal*(a: BigInt): string =
|
func toDecimal*(a: BigInt): string =
|
||||||
## Convert to a decimal 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.
|
## 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)
|
const len = decimalLength(BigInt.bits)
|
||||||
result = newString(len)
|
result = newString(len)
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ func fromUint*(dst: var FF,
|
|||||||
src: SomeUnsignedInt) =
|
src: SomeUnsignedInt) =
|
||||||
## Parse a regular unsigned integer
|
## Parse a regular unsigned integer
|
||||||
## and store it into a Fp or Fr
|
## 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)
|
dst.fromBig(raw)
|
||||||
|
|
||||||
func fromInt*(dst: var FF,
|
func fromInt*(dst: var FF,
|
||||||
@ -42,11 +42,11 @@ func fromInt*(dst: var FF,
|
|||||||
let isNeg = SecretBool((src shr msb_pos) and 1)
|
let isNeg = SecretBool((src shr msb_pos) and 1)
|
||||||
|
|
||||||
let src = isNeg.mux(SecretWord -src, SecretWord src)
|
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.fromBig(raw)
|
||||||
dst.cneg(isNeg)
|
dst.cneg(isNeg)
|
||||||
|
|
||||||
func exportRawUint*(dst: var openarray[byte],
|
func marshal*(dst: var openarray[byte],
|
||||||
src: FF,
|
src: FF,
|
||||||
dstEndianness: static Endianness) =
|
dstEndianness: static Endianness) =
|
||||||
## Serialize a finite field element to its canonical big-endian or little-endian
|
## 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
|
## If the buffer is bigger, output will be zero-padded left for big-endian
|
||||||
## or zero-padded right for little-endian.
|
## or zero-padded right for little-endian.
|
||||||
## I.e least significant bit is aligned to buffer boundary
|
## 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) =
|
func appendHex*(dst: var string, f: FF, order: static Endianness = bigEndian) =
|
||||||
## Stringify a finite field element to hex.
|
## 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.} =
|
func cyclotomic_exp*[FT](r: var FT, a: FT, exponent: BigInt, invert: bool) {.meter.} =
|
||||||
var eBytes: array[(exponent.bits+7) div 8, byte]
|
var eBytes: array[(exponent.bits+7) div 8, byte]
|
||||||
eBytes.exportRawUint(exponent, bigEndian)
|
eBytes.marshal(exponent, bigEndian)
|
||||||
|
|
||||||
r.setOne()
|
r.setOne()
|
||||||
for b in eBytes:
|
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.} =
|
func mul*(hi, lo: var Ct[uint32], a, b: Ct[uint32]) {.inline.} =
|
||||||
## Extended precision multiplication
|
## Extended precision multiplication
|
||||||
## (hi, lo) <- a*b
|
## (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 sizeof(int) == 8:
|
||||||
when defined(vcc):
|
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:
|
elif GCCCompatible:
|
||||||
# TODO: constant-time div2n1n
|
# TODO: constant-time div2n1n
|
||||||
when X86:
|
when X86:
|
||||||
from ./extended_precision_x86_64_gcc import unsafeDiv2n1n
|
|
||||||
from ./extended_precision_64bit_uint128 import mul, muladd1, muladd2, smul
|
from ./extended_precision_64bit_uint128 import mul, muladd1, muladd2, smul
|
||||||
else:
|
else:
|
||||||
from ./extended_precision_64bit_uint128 import unsafeDiv2n1n, mul, muladd1, muladd2, smul
|
from ./extended_precision_64bit_uint128 import mul, muladd1, muladd2, smul
|
||||||
export unsafeDiv2n1n, mul, muladd1, muladd2, smul
|
export mul, muladd1, muladd2, smul
|
||||||
|
|
||||||
# ############################################################
|
# ############################################################
|
||||||
#
|
#
|
||||||
|
@ -18,24 +18,6 @@ static:
|
|||||||
doAssert GCC_Compatible
|
doAssert GCC_Compatible
|
||||||
doAssert sizeof(int) == 8
|
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.} =
|
func mul*(hi, lo: var Ct[uint64], a, b: Ct[uint64]) {.inline.} =
|
||||||
## Extended precision multiplication
|
## Extended precision multiplication
|
||||||
## (hi, lo) <- a*b
|
## (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 sizeof(int) == 8
|
||||||
doAssert X86
|
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.}
|
func umul128(a, b: Ct[uint64], hi: var Ct[uint64]): Ct[uint64] {.importc:"_umul128", header:"<intrin.h>", nodecl.}
|
||||||
## Unsigned extended precision multiplication
|
## Unsigned extended precision multiplication
|
||||||
## (hi, lo) <-- a * b
|
## (hi, lo) <-- a * b
|
||||||
## Return value is the low word
|
## 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.} =
|
func mul*(hi, lo: var Ct[uint64], a, b: Ct[uint64]) {.inline.} =
|
||||||
## Extended precision multiplication
|
## Extended precision multiplication
|
||||||
## (hi, lo) <- a*b
|
## (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] =
|
template trmFixSystemNotEq*{x != y}[T: Ct](x, y: T): CTBool[T] =
|
||||||
noteq(x, y)
|
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
|
# 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
|
## Returns x if ctl is true
|
||||||
## else returns y
|
## else returns y
|
||||||
## So equivalent to ctl? x: y
|
## So equivalent to ctl? x: y
|
||||||
when X86 and GCC_Compatible:
|
when nimvm:
|
||||||
mux_x86(ctl, x, y)
|
|
||||||
else:
|
|
||||||
mux_fallback(ctl, x, y)
|
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.}=
|
func mux*[T: CTBool](ctl: CTBool, x, y: T): T {.inline.}=
|
||||||
## Multiplexer / selector
|
## Multiplexer / selector
|
||||||
## Returns x if ctl is true
|
## Returns x if ctl is true
|
||||||
## else returns y
|
## else returns y
|
||||||
## So equivalent to ctl? x: y
|
## So equivalent to ctl? x: y
|
||||||
when X86 and GCC_Compatible:
|
when nimvm:
|
||||||
mux_x86(ctl, x, y)
|
|
||||||
else:
|
|
||||||
mux_fallback(ctl, x, y)
|
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.}=
|
func ccopy*[T](ctl: CTBool[T], x: var T, y: T) {.inline.}=
|
||||||
## Conditional copy
|
## Conditional copy
|
||||||
## Copy ``y`` into ``x`` if ``ctl`` is true
|
## Copy ``y`` into ``x`` if ``ctl`` is true
|
||||||
when X86 and GCC_Compatible:
|
when nimvm:
|
||||||
ccopy_x86(ctl, x, y)
|
|
||||||
else:
|
|
||||||
ccopy_fallback(ctl, x, y)
|
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/[
|
constant_time/[
|
||||||
ct_types,
|
ct_types,
|
||||||
ct_routines,
|
ct_routines,
|
||||||
multiplexers
|
multiplexers,
|
||||||
|
ct_division
|
||||||
],
|
],
|
||||||
compilers/[
|
compilers/[
|
||||||
addcarry_subborrow,
|
addcarry_subborrow,
|
||||||
@ -25,6 +26,7 @@ export
|
|||||||
multiplexers,
|
multiplexers,
|
||||||
addcarry_subborrow,
|
addcarry_subborrow,
|
||||||
extended_precision,
|
extended_precision,
|
||||||
|
ct_division,
|
||||||
bithacks,
|
bithacks,
|
||||||
staticFor
|
staticFor
|
||||||
|
|
||||||
|
@ -211,9 +211,9 @@ func random_long01Seq(rng: var RngState, a: var BigInt) =
|
|||||||
rng.random_long01Seq(buf)
|
rng.random_long01Seq(buf)
|
||||||
let order = rng.sample_unsafe([bigEndian, littleEndian])
|
let order = rng.sample_unsafe([bigEndian, littleEndian])
|
||||||
if order == bigEndian:
|
if order == bigEndian:
|
||||||
a.fromRawUint(buf, bigEndian)
|
a.unmarshal(buf, bigEndian)
|
||||||
else:
|
else:
|
||||||
a.fromRawUint(buf, littleEndian)
|
a.unmarshal(buf, littleEndian)
|
||||||
|
|
||||||
func random_long01Seq(rng: var RngState, a: var FF) =
|
func random_long01Seq(rng: var RngState, a: var FF) =
|
||||||
## Recursively initialize a BigInt (part of a field) or Field element
|
## 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
|
## This is highly VULNERABLE to timing attacks and power analysis attacks
|
||||||
var scalarCanonical: array[(scalar.bits+7) div 8, byte]
|
var scalarCanonical: array[(scalar.bits+7) div 8, byte]
|
||||||
scalarCanonical.exportRawUint(scalar, bigEndian)
|
scalarCanonical.marshal(scalar, bigEndian)
|
||||||
|
|
||||||
var t0{.noInit.}, t1{.noInit.}: typeof(P)
|
var t0{.noInit.}, t1{.noInit.}: typeof(P)
|
||||||
t0.setInf()
|
t0.setInf()
|
||||||
|
@ -25,13 +25,13 @@ proc mainArith() =
|
|||||||
check: x.isZero().bool
|
check: x.isZero().bool
|
||||||
test "isZero for non-zero":
|
test "isZero for non-zero":
|
||||||
block:
|
block:
|
||||||
let x = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
|
let x = fromHex(BigInt[128], "0x00000000000000000000000000000001")
|
||||||
check: not x.isZero().bool
|
check: not x.isZero().bool
|
||||||
block:
|
block:
|
||||||
let x = fromHex(BigInt[128], "0x00000000_00000001_00000000_00000000")
|
let x = fromHex(BigInt[128], "0x00000000000000010000000000000000")
|
||||||
check: not x.isZero().bool
|
check: not x.isZero().bool
|
||||||
block:
|
block:
|
||||||
let x = fromHex(BigInt[128], "0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF")
|
let x = fromHex(BigInt[128], "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
|
||||||
check: not x.isZero().bool
|
check: not x.isZero().bool
|
||||||
|
|
||||||
test "isZero for zero (compile-time)":
|
test "isZero for zero (compile-time)":
|
||||||
@ -39,53 +39,53 @@ proc mainArith() =
|
|||||||
check: static(x.isZero().bool)
|
check: static(x.isZero().bool)
|
||||||
test "isZero for non-zero (compile-time)":
|
test "isZero for non-zero (compile-time)":
|
||||||
block:
|
block:
|
||||||
const x = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
|
const x = fromHex(BigInt[128], "0x00000000000000000000000000000001")
|
||||||
check: static(not x.isZero().bool)
|
check: static(not x.isZero().bool)
|
||||||
block:
|
block:
|
||||||
const x = fromHex(BigInt[128], "0x00000000_00000001_00000000_00000000")
|
const x = fromHex(BigInt[128], "0x00000000000000010000000000000000")
|
||||||
check: static(not x.isZero().bool)
|
check: static(not x.isZero().bool)
|
||||||
block:
|
block:
|
||||||
const x = fromHex(BigInt[128], "0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF")
|
const x = fromHex(BigInt[128], "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
|
||||||
check: static(not x.isZero().bool)
|
check: static(not x.isZero().bool)
|
||||||
|
|
||||||
|
|
||||||
suite "Arithmetic operations - Addition" & " [" & $WordBitwidth & "-bit mode]":
|
suite "Arithmetic operations - Addition" & " [" & $WordBitwidth & "-bit mode]":
|
||||||
test "Adding 2 zeros":
|
test "Adding 2 zeros":
|
||||||
var a = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000000")
|
var a = fromHex(BigInt[128], "0x00000000000000000000000000000000")
|
||||||
let b = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000000")
|
let b = fromHex(BigInt[128], "0x00000000000000000000000000000000")
|
||||||
let carry = a.cadd(b, CtTrue)
|
let carry = a.cadd(b, CtTrue)
|
||||||
check: a.isZero().bool
|
check: a.isZero().bool
|
||||||
|
|
||||||
test "Adding 1 zero - real addition":
|
test "Adding 1 zero - real addition":
|
||||||
block:
|
block:
|
||||||
var a = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000000")
|
var a = fromHex(BigInt[128], "0x00000000000000000000000000000000")
|
||||||
let b = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
|
let b = fromHex(BigInt[128], "0x00000000000000000000000000000001")
|
||||||
let carry = a.cadd(b, CtTrue)
|
let carry = a.cadd(b, CtTrue)
|
||||||
|
|
||||||
let c = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
|
let c = fromHex(BigInt[128], "0x00000000000000000000000000000001")
|
||||||
check:
|
check:
|
||||||
bool(a == c)
|
bool(a == c)
|
||||||
block:
|
block:
|
||||||
var a = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
|
var a = fromHex(BigInt[128], "0x00000000000000000000000000000001")
|
||||||
let b = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000000")
|
let b = fromHex(BigInt[128], "0x00000000000000000000000000000000")
|
||||||
let carry = a.cadd(b, CtTrue)
|
let carry = a.cadd(b, CtTrue)
|
||||||
|
|
||||||
let c = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
|
let c = fromHex(BigInt[128], "0x00000000000000000000000000000001")
|
||||||
check:
|
check:
|
||||||
bool(a == c)
|
bool(a == c)
|
||||||
|
|
||||||
test "Adding 1 zero - fake addition":
|
test "Adding 1 zero - fake addition":
|
||||||
block:
|
block:
|
||||||
var a = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000000")
|
var a = fromHex(BigInt[128], "0x00000000000000000000000000000000")
|
||||||
let b = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
|
let b = fromHex(BigInt[128], "0x00000000000000000000000000000001")
|
||||||
let carry = a.cadd(b, CtFalse)
|
let carry = a.cadd(b, CtFalse)
|
||||||
|
|
||||||
let c = a
|
let c = a
|
||||||
check:
|
check:
|
||||||
bool(a == c)
|
bool(a == c)
|
||||||
block:
|
block:
|
||||||
var a = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
|
var a = fromHex(BigInt[128], "0x00000000000000000000000000000001")
|
||||||
let b = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000000")
|
let b = fromHex(BigInt[128], "0x00000000000000000000000000000000")
|
||||||
let carry = a.cadd(b, CtFalse)
|
let carry = a.cadd(b, CtFalse)
|
||||||
|
|
||||||
let c = a
|
let c = a
|
||||||
@ -94,34 +94,34 @@ proc mainArith() =
|
|||||||
|
|
||||||
test "Adding non-zeros - real addition":
|
test "Adding non-zeros - real addition":
|
||||||
block:
|
block:
|
||||||
var a = fromHex(BigInt[128], "0x00000000_00000001_00000000_00000000")
|
var a = fromHex(BigInt[128], "0x00000000000000010000000000000000")
|
||||||
let b = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
|
let b = fromHex(BigInt[128], "0x00000000000000000000000000000001")
|
||||||
let carry = a.cadd(b, CtTrue)
|
let carry = a.cadd(b, CtTrue)
|
||||||
|
|
||||||
let c = fromHex(BigInt[128], "0x00000000_00000001_00000000_00000001")
|
let c = fromHex(BigInt[128], "0x00000000000000010000000000000001")
|
||||||
check:
|
check:
|
||||||
bool(a == c)
|
bool(a == c)
|
||||||
block:
|
block:
|
||||||
var a = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
|
var a = fromHex(BigInt[128], "0x00000000000000000000000000000001")
|
||||||
let b = fromHex(BigInt[128], "0x00000000_00000001_00000000_00000000")
|
let b = fromHex(BigInt[128], "0x00000000000000010000000000000000")
|
||||||
let carry = a.cadd(b, CtTrue)
|
let carry = a.cadd(b, CtTrue)
|
||||||
|
|
||||||
let c = fromHex(BigInt[128], "0x00000000_00000001_00000000_00000001")
|
let c = fromHex(BigInt[128], "0x00000000000000010000000000000001")
|
||||||
check:
|
check:
|
||||||
bool(a == c)
|
bool(a == c)
|
||||||
|
|
||||||
test "Adding non-zeros - fake addition":
|
test "Adding non-zeros - fake addition":
|
||||||
block:
|
block:
|
||||||
var a = fromHex(BigInt[128], "0x00000000_00000001_00000000_00000000")
|
var a = fromHex(BigInt[128], "0x00000000000000010000000000000000")
|
||||||
let b = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
|
let b = fromHex(BigInt[128], "0x00000000000000000000000000000001")
|
||||||
let carry = a.cadd(b, CtFalse)
|
let carry = a.cadd(b, CtFalse)
|
||||||
|
|
||||||
let c = a
|
let c = a
|
||||||
check:
|
check:
|
||||||
bool(a == c)
|
bool(a == c)
|
||||||
block:
|
block:
|
||||||
var a = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
|
var a = fromHex(BigInt[128], "0x00000000000000000000000000000001")
|
||||||
let b = fromHex(BigInt[128], "0x00000000_00000001_00000000_00000000")
|
let b = fromHex(BigInt[128], "0x00000000000000010000000000000000")
|
||||||
let carry = a.cadd(b, CtFalse)
|
let carry = a.cadd(b, CtFalse)
|
||||||
|
|
||||||
let c = a
|
let c = a
|
||||||
@ -130,21 +130,21 @@ proc mainArith() =
|
|||||||
|
|
||||||
test "Addition limbs carry":
|
test "Addition limbs carry":
|
||||||
block:
|
block:
|
||||||
var a = fromHex(BigInt[128], "0x00000000_FFFFFFFF_FFFFFFFF_FFFFFFFE")
|
var a = fromHex(BigInt[128], "0x00000000FFFFFFFFFFFFFFFFFFFFFFFE")
|
||||||
let b = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
|
let b = fromHex(BigInt[128], "0x00000000000000000000000000000001")
|
||||||
let carry = a.cadd(b, CtTrue)
|
let carry = a.cadd(b, CtTrue)
|
||||||
|
|
||||||
let c = fromHex(BigInt[128], "0x00000000_FFFFFFFF_FFFFFFFF_FFFFFFFF")
|
let c = fromHex(BigInt[128], "0x00000000FFFFFFFFFFFFFFFFFFFFFFFF")
|
||||||
check:
|
check:
|
||||||
bool(a == c)
|
bool(a == c)
|
||||||
not bool(carry)
|
not bool(carry)
|
||||||
|
|
||||||
block:
|
block:
|
||||||
var a = fromHex(BigInt[128], "0x00000000_FFFFFFFF_FFFFFFFF_FFFFFFFF")
|
var a = fromHex(BigInt[128], "0x00000000FFFFFFFFFFFFFFFFFFFFFFFF")
|
||||||
let b = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
|
let b = fromHex(BigInt[128], "0x00000000000000000000000000000001")
|
||||||
let carry = a.cadd(b, CtTrue)
|
let carry = a.cadd(b, CtTrue)
|
||||||
|
|
||||||
let c = fromHex(BigInt[128], "0x00000001_00000000_00000000_00000000")
|
let c = fromHex(BigInt[128], "0x00000001000000000000000000000000")
|
||||||
check:
|
check:
|
||||||
bool(a == c)
|
bool(a == c)
|
||||||
not bool(carry)
|
not bool(carry)
|
||||||
@ -164,8 +164,8 @@ proc mainMul() =
|
|||||||
test "Same size operand into double size result":
|
test "Same size operand into double size result":
|
||||||
block:
|
block:
|
||||||
var r = canary(BigInt[256])
|
var r = canary(BigInt[256])
|
||||||
let a = BigInt[128].fromHex"0x12345678_FF11FFAA_00321321_CAFECAFE"
|
let a = BigInt[128].fromHex"0x12345678FF11FFAA00321321CAFECAFE"
|
||||||
let b = BigInt[128].fromHex"0xDEADBEEF_DEADBEEF_DEADBEEF_DEADBEEF"
|
let b = BigInt[128].fromHex"0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF"
|
||||||
|
|
||||||
let expected = BigInt[256].fromHex"fd5bdef43d64113f371ab5d8843beca889c07fd549b84d8a5001a8f102e0722"
|
let expected = BigInt[256].fromHex"fd5bdef43d64113f371ab5d8843beca889c07fd549b84d8a5001a8f102e0722"
|
||||||
|
|
||||||
@ -178,7 +178,7 @@ proc mainMul() =
|
|||||||
block:
|
block:
|
||||||
var r = canary(BigInt[200])
|
var r = canary(BigInt[200])
|
||||||
let a = BigInt[29].fromHex"0x12345678"
|
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"
|
let expected = BigInt[200].fromHex"fd5bdee65f787f665f787f665f787f65621ca08"
|
||||||
|
|
||||||
@ -189,9 +189,9 @@ proc mainMul() =
|
|||||||
|
|
||||||
test "Destination is properly zero-padded if multiplicands are too short":
|
test "Destination is properly zero-padded if multiplicands are too short":
|
||||||
block:
|
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 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"
|
let expected = BigInt[200].fromHex"fd5bdee65f787f665f787f665f787f65621ca08"
|
||||||
|
|
||||||
@ -205,108 +205,108 @@ proc mainMulHigh() =
|
|||||||
test "Same size operand into double size result - discard first word":
|
test "Same size operand into double size result - discard first word":
|
||||||
block:
|
block:
|
||||||
var r = canary(BigInt[256])
|
var r = canary(BigInt[256])
|
||||||
let a = BigInt[128].fromHex"0x12345678_FF11FFAA_00321321_CAFECAFE"
|
let a = BigInt[128].fromHex"0x12345678FF11FFAA00321321CAFECAFE"
|
||||||
let b = BigInt[128].fromHex"0xDEADBEEF_DEADBEEF_DEADBEEF_DEADBEEF"
|
let b = BigInt[128].fromHex"0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF"
|
||||||
|
|
||||||
when WordBitWidth == 32:
|
when WordBitWidth == 32:
|
||||||
let expected = BigInt[256].fromHex"fd5bdef43d64113f371ab5d8843beca889c07fd549b84d8a5001a8f"
|
let expected = BigInt[256].fromHex"fd5bdef43d64113f371ab5d8843beca889c07fd549b84d8a5001a8f"
|
||||||
else:
|
else:
|
||||||
let expected = BigInt[256].fromHex"fd5bdef43d64113f371ab5d8843beca889c07fd549b84d8"
|
let expected = BigInt[256].fromHex"fd5bdef43d64113f371ab5d8843beca889c07fd549b84d8"
|
||||||
|
|
||||||
r.prod_high_words(a, b, 1)
|
r.prodhighwords(a, b, 1)
|
||||||
check: bool(r == expected)
|
check: bool(r == expected)
|
||||||
r.prod_high_words(b, a, 1)
|
r.prodhighwords(b, a, 1)
|
||||||
check: bool(r == expected)
|
check: bool(r == expected)
|
||||||
|
|
||||||
test "Same size operand into double size result - discard first 3 words":
|
test "Same size operand into double size result - discard first 3 words":
|
||||||
block:
|
block:
|
||||||
var r = canary(BigInt[256])
|
var r = canary(BigInt[256])
|
||||||
let a = BigInt[128].fromHex"0x12345678_FF11FFAA_00321321_CAFECAFE"
|
let a = BigInt[128].fromHex"0x12345678FF11FFAA00321321CAFECAFE"
|
||||||
let b = BigInt[128].fromHex"0xDEADBEEF_DEADBEEF_DEADBEEF_DEADBEEF"
|
let b = BigInt[128].fromHex"0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF"
|
||||||
|
|
||||||
when WordBitWidth == 32:
|
when WordBitWidth == 32:
|
||||||
let expected = BigInt[256].fromHex"fd5bdef43d64113f371ab5d8843beca889c07fd"
|
let expected = BigInt[256].fromHex"fd5bdef43d64113f371ab5d8843beca889c07fd"
|
||||||
else:
|
else:
|
||||||
let expected = BigInt[256].fromHex"fd5bdef43d64113"
|
let expected = BigInt[256].fromHex"fd5bdef43d64113"
|
||||||
|
|
||||||
r.prod_high_words(a, b, 3)
|
r.prodhighwords(a, b, 3)
|
||||||
check: bool(r == expected)
|
check: bool(r == expected)
|
||||||
r.prod_high_words(b, a, 3)
|
r.prodhighwords(b, a, 3)
|
||||||
check: bool(r == expected)
|
check: bool(r == expected)
|
||||||
|
|
||||||
test "All lower words trigger a carry":
|
test "All lower words trigger a carry":
|
||||||
block:
|
block:
|
||||||
var r = canary(BigInt[256])
|
var r = canary(BigInt[256])
|
||||||
let a = BigInt[256].fromHex"0xFFFFF000_FFFFF111_FFFFFFFA_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF"
|
let a = BigInt[256].fromHex"0xFFFFF000FFFFF111FFFFFFFAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
|
||||||
let b = BigInt[256].fromHex"0xFFFFFFFF_FFFFF222_FFFFFFFB_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF"
|
let b = BigInt[256].fromHex"0xFFFFFFFFFFFFF222FFFFFFFBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
|
||||||
|
|
||||||
# Full product:
|
# Full product:
|
||||||
# fffff000_ffffe335_00ddc21a_00cf3972_00008109_00000013_ffffffff_fffffffe
|
# fffff000ffffe33500ddc21a00cf39720000810900000013fffffffffffffffe
|
||||||
# 00000fff_00001ccb_00000009_00000000_00000000_00000000_00000000_00000001
|
# 00000fff00001ccb000000090000000000000000000000000000000000000001
|
||||||
let expected = BigInt[256].fromHex"0xfffff000_ffffe335_00ddc21a_00cf3972_00008109_00000013_ffffffff_fffffffe"
|
let expected = BigInt[256].fromHex"0xfffff000ffffe33500ddc21a00cf39720000810900000013fffffffffffffffe"
|
||||||
when WordBitWidth == 32:
|
when WordBitWidth == 32:
|
||||||
const startWord = 8
|
const startWord = 8
|
||||||
else:
|
else:
|
||||||
const startWord = 4
|
const startWord = 4
|
||||||
|
|
||||||
r.prod_high_words(a, b, startWord)
|
r.prodhighwords(a, b, startWord)
|
||||||
check: bool(r == expected)
|
check: bool(r == expected)
|
||||||
r.prod_high_words(b, a, startWord)
|
r.prodhighwords(b, a, startWord)
|
||||||
check: bool(r == expected)
|
check: bool(r == expected)
|
||||||
|
|
||||||
test "Different size into large result":
|
test "Different size into large result":
|
||||||
block:
|
block:
|
||||||
var r = canary(BigInt[200])
|
var r = canary(BigInt[200])
|
||||||
let a = BigInt[29].fromHex"0x12345678"
|
let a = BigInt[29].fromHex"0x12345678"
|
||||||
let b = BigInt[128].fromHex"0xDEADBEEF_DEADBEEF_DEADBEEF_DEADBEEF"
|
let b = BigInt[128].fromHex"0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF"
|
||||||
|
|
||||||
when WordBitWidth == 32:
|
when WordBitWidth == 32:
|
||||||
let expected = BigInt[200].fromHex"fd5bdee65f787f665f787f6"
|
let expected = BigInt[200].fromHex"fd5bdee65f787f665f787f6"
|
||||||
else:
|
else:
|
||||||
let expected = BigInt[200].fromHex"fd5bdee"
|
let expected = BigInt[200].fromHex"fd5bdee"
|
||||||
|
|
||||||
r.prod_high_words(a, b, 2)
|
r.prodhighwords(a, b, 2)
|
||||||
check: bool(r == expected)
|
check: bool(r == expected)
|
||||||
r.prod_high_words(b, a, 2)
|
r.prodhighwords(b, a, 2)
|
||||||
check: bool(r == expected)
|
check: bool(r == expected)
|
||||||
|
|
||||||
test "Destination is properly zero-padded if multiplicands are too short":
|
test "Destination is properly zero-padded if multiplicands are too short":
|
||||||
block:
|
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 a = BigInt[29].fromHex"0x12345678"
|
||||||
let b = BigInt[128].fromHex"0xDEADBEEF_DEADBEEF_DEADBEEF_DEADBEEF"
|
let b = BigInt[128].fromHex"0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF"
|
||||||
|
|
||||||
when WordBitWidth == 32:
|
when WordBitWidth == 32:
|
||||||
let expected = BigInt[200].fromHex"fd5bdee65f787f665f787f6"
|
let expected = BigInt[200].fromHex"fd5bdee65f787f665f787f6"
|
||||||
else:
|
else:
|
||||||
let expected = BigInt[200].fromHex"fd5bdee"
|
let expected = BigInt[200].fromHex"fd5bdee"
|
||||||
|
|
||||||
r.prod_high_words(a, b, 2)
|
r.prodhighwords(a, b, 2)
|
||||||
check: bool(r == expected)
|
check: bool(r == expected)
|
||||||
r.prod_high_words(b, a, 2)
|
r.prodhighwords(b, a, 2)
|
||||||
check: bool(r == expected)
|
check: bool(r == expected)
|
||||||
|
|
||||||
proc mainSquare() =
|
proc mainSquare() =
|
||||||
suite "Multi-precision multiplication" & " [" & $WordBitwidth & "-bit mode]":
|
suite "Multi-precision multiplication" & " [" & $WordBitwidth & "-bit mode]":
|
||||||
test "Squaring is consistent with multiplication (rBits = 2*aBits)":
|
test "Squaring is consistent with multiplication (rBits = 2*aBits)":
|
||||||
block:
|
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)
|
rmul.prod(a, a)
|
||||||
r_sqr.square(a)
|
rsqr.square(a)
|
||||||
check: bool(r_mul == r_sqr)
|
check: bool(rmul == rsqr)
|
||||||
|
|
||||||
test "Squaring is consistent with multiplication (truncated)":
|
test "Squaring is consistent with multiplication (truncated)":
|
||||||
block:
|
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)
|
rmul.prod(a, a)
|
||||||
r_sqr.square(a)
|
rsqr.square(a)
|
||||||
check: bool(r_mul == r_sqr)
|
check: bool(rmul == rsqr)
|
||||||
|
|
||||||
proc mainModular() =
|
proc mainModular() =
|
||||||
suite "Modular operations - small modulus" & " [" & $WordBitwidth & "-bit mode]":
|
suite "Modular operations - small modulus" & " [" & $WordBitwidth & "-bit mode]":
|
||||||
@ -347,7 +347,7 @@ proc mainModular() =
|
|||||||
"\n expected (ll repr): " & $expected
|
"\n expected (ll repr): " & $expected
|
||||||
|
|
||||||
test "2^64 mod 3":
|
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)
|
let m = BigInt[8].fromUint(3'u8)
|
||||||
|
|
||||||
var r = canary(BigInt[8])
|
var r = canary(BigInt[8])
|
||||||
@ -404,8 +404,8 @@ proc mainNeg() =
|
|||||||
suite "Conditional negation" & " [" & $WordBitwidth & "-bit mode]":
|
suite "Conditional negation" & " [" & $WordBitwidth & "-bit mode]":
|
||||||
test "Conditional negation":
|
test "Conditional negation":
|
||||||
block:
|
block:
|
||||||
var a = fromHex(BigInt[128], "0x12345678_FF11FFAA_00321321_CAFECAFE")
|
var a = fromHex(BigInt[128], "0x12345678FF11FFAA00321321CAFECAFE")
|
||||||
var b = fromHex(BigInt[128], "0xDEADBEEF_DEADBEEF_DEADBEEF_DEADBEEF")
|
var b = fromHex(BigInt[128], "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF")
|
||||||
|
|
||||||
let a2 = a
|
let a2 = a
|
||||||
let b2 = b
|
let b2 = b
|
||||||
@ -421,8 +421,8 @@ proc mainNeg() =
|
|||||||
bool(b.isZero)
|
bool(b.isZero)
|
||||||
|
|
||||||
block:
|
block:
|
||||||
var a = fromHex(BigInt[128], "0x12345678_FF11FFAA_00321321_CAFECAFE")
|
var a = fromHex(BigInt[128], "0x12345678FF11FFAA00321321CAFECAFE")
|
||||||
var b = fromHex(BigInt[128], "0xDEADBEEF_DEADBEEF_DEADBEEF_DEADBEEF")
|
var b = fromHex(BigInt[128], "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF")
|
||||||
|
|
||||||
let a2 = a
|
let a2 = a
|
||||||
let b2 = b
|
let b2 = b
|
||||||
@ -436,8 +436,8 @@ proc mainNeg() =
|
|||||||
|
|
||||||
test "Conditional negation with carries":
|
test "Conditional negation with carries":
|
||||||
block:
|
block:
|
||||||
var a = fromHex(BigInt[128], "0x12345678_FF11FFAA_00321321_FFFFFFFF")
|
var a = fromHex(BigInt[128], "0x12345678FF11FFAA00321321FFFFFFFF")
|
||||||
var b = fromHex(BigInt[128], "0xFFFFFFFF_FFFFFFFF_00000000_00000000")
|
var b = fromHex(BigInt[128], "0xFFFFFFFFFFFFFFFF0000000000000000")
|
||||||
|
|
||||||
let a2 = a
|
let a2 = a
|
||||||
let b2 = b
|
let b2 = b
|
||||||
@ -453,8 +453,8 @@ proc mainNeg() =
|
|||||||
bool(b.isZero)
|
bool(b.isZero)
|
||||||
|
|
||||||
block:
|
block:
|
||||||
var a = fromHex(BigInt[128], "0x12345678_00000000_00321321_FFFFFFFF")
|
var a = fromHex(BigInt[128], "0x123456780000000000321321FFFFFFFF")
|
||||||
var b = fromHex(BigInt[128], "0xFFFFFFFF_FFFFFFFF_00000000_00000000")
|
var b = fromHex(BigInt[128], "0xFFFFFFFFFFFFFFFF0000000000000000")
|
||||||
|
|
||||||
let a2 = a
|
let a2 = a
|
||||||
let b2 = b
|
let b2 = b
|
||||||
@ -468,8 +468,8 @@ proc mainNeg() =
|
|||||||
|
|
||||||
test "Conditional all-zero bit or all-one bit":
|
test "Conditional all-zero bit or all-one bit":
|
||||||
block:
|
block:
|
||||||
var a = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000000")
|
var a = fromHex(BigInt[128], "0x00000000000000000000000000000000")
|
||||||
var b = fromHex(BigInt[128], "0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF")
|
var b = fromHex(BigInt[128], "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
|
||||||
|
|
||||||
let a2 = a
|
let a2 = a
|
||||||
let b2 = b
|
let b2 = b
|
||||||
@ -485,8 +485,8 @@ proc mainNeg() =
|
|||||||
bool(b.isZero)
|
bool(b.isZero)
|
||||||
|
|
||||||
block:
|
block:
|
||||||
var a = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000000")
|
var a = fromHex(BigInt[128], "0x00000000000000000000000000000000")
|
||||||
var b = fromHex(BigInt[128], "0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF")
|
var b = fromHex(BigInt[128], "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
|
||||||
|
|
||||||
let a2 = a
|
let a2 = a
|
||||||
let b2 = b
|
let b2 = b
|
||||||
@ -502,8 +502,8 @@ proc mainCopySwap() =
|
|||||||
suite "Copy and Swap" & " [" & $WordBitwidth & "-bit mode]":
|
suite "Copy and Swap" & " [" & $WordBitwidth & "-bit mode]":
|
||||||
test "Conditional copy":
|
test "Conditional copy":
|
||||||
block:
|
block:
|
||||||
var a = fromHex(BigInt[128], "0x12345678_FF11FFAA_00321321_CAFECAFE")
|
var a = fromHex(BigInt[128], "0x12345678FF11FFAA00321321CAFECAFE")
|
||||||
let b = fromHex(BigInt[128], "0xDEADBEEF_DEADBEEF_DEADBEEF_DEADBEEF")
|
let b = fromHex(BigInt[128], "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF")
|
||||||
|
|
||||||
var expected = a
|
var expected = a
|
||||||
a.ccopy(b, CtFalse)
|
a.ccopy(b, CtFalse)
|
||||||
@ -511,8 +511,8 @@ proc mainCopySwap() =
|
|||||||
check: bool(expected == a)
|
check: bool(expected == a)
|
||||||
|
|
||||||
block:
|
block:
|
||||||
var a = fromHex(BigInt[128], "0x00000000_FFFFFFFF_FFFFFFFF_FFFFFFFF")
|
var a = fromHex(BigInt[128], "0x00000000FFFFFFFFFFFFFFFFFFFFFFFF")
|
||||||
let b = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
|
let b = fromHex(BigInt[128], "0x00000000000000000000000000000001")
|
||||||
|
|
||||||
var expected = b
|
var expected = b
|
||||||
a.ccopy(b, CtTrue)
|
a.ccopy(b, CtTrue)
|
||||||
@ -521,8 +521,8 @@ proc mainCopySwap() =
|
|||||||
|
|
||||||
test "Conditional swap":
|
test "Conditional swap":
|
||||||
block:
|
block:
|
||||||
var a = fromHex(BigInt[128], "0x12345678_FF11FFAA_00321321_CAFECAFE")
|
var a = fromHex(BigInt[128], "0x12345678FF11FFAA00321321CAFECAFE")
|
||||||
var b = fromHex(BigInt[128], "0xDEADBEEF_DEADBEEF_DEADBEEF_DEADBEEF")
|
var b = fromHex(BigInt[128], "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF")
|
||||||
|
|
||||||
let eA = a
|
let eA = a
|
||||||
let eB = b
|
let eB = b
|
||||||
@ -533,8 +533,8 @@ proc mainCopySwap() =
|
|||||||
bool(eB == b)
|
bool(eB == b)
|
||||||
|
|
||||||
block:
|
block:
|
||||||
var a = fromHex(BigInt[128], "0x00000000_FFFFFFFF_FFFFFFFF_FFFFFFFF")
|
var a = fromHex(BigInt[128], "0x00000000FFFFFFFFFFFFFFFFFFFFFFFF")
|
||||||
var b = fromHex(BigInt[128], "0x00000000_00000000_00000000_00000001")
|
var b = fromHex(BigInt[128], "0x00000000000000000000000000000001")
|
||||||
|
|
||||||
let eA = b
|
let eA = b
|
||||||
let eB = a
|
let eB = a
|
||||||
@ -548,7 +548,7 @@ proc mainModularInverse() =
|
|||||||
suite "Modular Inverse (with odd modulus)" & " [" & $WordBitwidth & "-bit mode]":
|
suite "Modular Inverse (with odd modulus)" & " [" & $WordBitwidth & "-bit mode]":
|
||||||
# Note: We don't define multi-precision multiplication
|
# Note: We don't define multi-precision multiplication
|
||||||
# because who needs it when you have Montgomery?
|
# because who needs it when you have Montgomery?
|
||||||
# ¯\_(ツ)_/¯
|
# ¯\(ツ)/¯
|
||||||
test "42^-1 (mod 2017) = 1969":
|
test "42^-1 (mod 2017) = 1969":
|
||||||
block: # small int
|
block: # small int
|
||||||
let a = BigInt[16].fromUint(42'u16)
|
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)"
|
doAssert mLen == mW, "Expected " & $mLen & " bytes but wrote " & $mW & " for " & toHex(mBuf) & " (big-endian)"
|
||||||
|
|
||||||
# Build the bigint
|
# Build the bigint
|
||||||
let aTest = BigInt[aBits].fromRawUint(aBuf.toOpenArray(0, aW-1), bigEndian)
|
let aTest = BigInt[aBits].unmarshal(aBuf.toOpenArray(0, aW-1), bigEndian)
|
||||||
let mTest = BigInt[mBits].fromRawUint(mBuf.toOpenArray(0, mW-1), bigEndian)
|
let mTest = BigInt[mBits].unmarshal(mBuf.toOpenArray(0, mW-1), bigEndian)
|
||||||
|
|
||||||
#########################################################
|
#########################################################
|
||||||
# Modulus
|
# Modulus
|
||||||
@ -144,7 +144,7 @@ proc main() =
|
|||||||
discard mpz_export(rGMP[0].addr, rW.addr, GMP_MostSignificantWordFirst, 1, GMP_WordNativeEndian, 0, r)
|
discard mpz_export(rGMP[0].addr, rW.addr, GMP_MostSignificantWordFirst, 1, GMP_WordNativeEndian, 0, r)
|
||||||
|
|
||||||
var rConstantine: array[mLen, byte]
|
var rConstantine: array[mLen, byte]
|
||||||
exportRawUint(rConstantine, rTest, bigEndian)
|
marshal(rConstantine, rTest, bigEndian)
|
||||||
|
|
||||||
# echo "rGMP: ", rGMP.toHex()
|
# echo "rGMP: ", rGMP.toHex()
|
||||||
# echo "rConstantine: ", rConstantine.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)"
|
doAssert bLen >= bW, "Expected at most " & $bLen & " bytes but wrote " & $bW & " for " & toHex(bBuf) & " (big-endian)"
|
||||||
|
|
||||||
# Build the bigint
|
# Build the bigint
|
||||||
let aTest = BigInt[aBits].fromRawUint(aBuf.toOpenArray(0, aW-1), bigEndian)
|
let aTest = BigInt[aBits].unmarshal(aBuf.toOpenArray(0, aW-1), bigEndian)
|
||||||
let bTest = BigInt[bBits].fromRawUint(bBuf.toOpenArray(0, bW-1), bigEndian)
|
let bTest = BigInt[bBits].unmarshal(bBuf.toOpenArray(0, bW-1), bigEndian)
|
||||||
|
|
||||||
#########################################################
|
#########################################################
|
||||||
# Multiplication + drop low words
|
# 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)
|
discard mpz_export(rGMP[0].addr, rW.addr, GMP_MostSignificantWordFirst, 1, GMP_WordNativeEndian, 0, r)
|
||||||
|
|
||||||
var rConstantine: array[rLen, byte]
|
var rConstantine: array[rLen, byte]
|
||||||
exportRawUint(rConstantine, rTest, bigEndian)
|
marshal(rConstantine, rTest, bigEndian)
|
||||||
|
|
||||||
# Note: in bigEndian, GMP aligns left while constantine aligns right
|
# Note: in bigEndian, GMP aligns left while constantine aligns right
|
||||||
doAssert rGMP.toOpenArray(0, rW-1) == rConstantine.toOpenArray(rLen-rW, rLen-1), block:
|
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)"
|
doAssert bLen >= bW, "Expected at most " & $bLen & " bytes but wrote " & $bW & " for " & toHex(bBuf) & " (big-endian)"
|
||||||
|
|
||||||
# Build the bigint
|
# Build the bigint
|
||||||
let aTest = BigInt[aBits].fromRawUint(aBuf.toOpenArray(0, aW-1), bigEndian)
|
let aTest = BigInt[aBits].unmarshal(aBuf.toOpenArray(0, aW-1), bigEndian)
|
||||||
let bTest = BigInt[bBits].fromRawUint(bBuf.toOpenArray(0, bW-1), bigEndian)
|
let bTest = BigInt[bBits].unmarshal(bBuf.toOpenArray(0, bW-1), bigEndian)
|
||||||
|
|
||||||
#########################################################
|
#########################################################
|
||||||
# Multiplication
|
# Multiplication
|
||||||
@ -122,7 +122,7 @@ proc main() =
|
|||||||
discard mpz_export(rGMP[0].addr, rW.addr, GMP_MostSignificantWordFirst, 1, GMP_WordNativeEndian, 0, r)
|
discard mpz_export(rGMP[0].addr, rW.addr, GMP_MostSignificantWordFirst, 1, GMP_WordNativeEndian, 0, r)
|
||||||
|
|
||||||
var rConstantine: array[rLen, byte]
|
var rConstantine: array[rLen, byte]
|
||||||
exportRawUint(rConstantine, rTest, bigEndian)
|
marshal(rConstantine, rTest, bigEndian)
|
||||||
|
|
||||||
# Note: in bigEndian, GMP aligns left while constantine aligns right
|
# Note: in bigEndian, GMP aligns left while constantine aligns right
|
||||||
doAssert rGMP.toOpenArray(0, rW-1) == rConstantine.toOpenArray(rLen-rW, rLen-1), block:
|
doAssert rGMP.toOpenArray(0, rW-1) == rConstantine.toOpenArray(rLen-rW, rLen-1), block:
|
||||||
|
@ -29,7 +29,7 @@ proc main() =
|
|||||||
x += y
|
x += y
|
||||||
|
|
||||||
var x_bytes: array[8, byte]
|
var x_bytes: array[8, byte]
|
||||||
x_bytes.exportRawUint(x, cpuEndian)
|
x_bytes.marshal(x, cpuEndian)
|
||||||
|
|
||||||
check:
|
check:
|
||||||
# Check equality in the Montgomery domain
|
# Check equality in the Montgomery domain
|
||||||
@ -47,7 +47,7 @@ proc main() =
|
|||||||
x += y
|
x += y
|
||||||
|
|
||||||
var x_bytes: array[8, byte]
|
var x_bytes: array[8, byte]
|
||||||
x_bytes.exportRawUint(x, cpuEndian)
|
x_bytes.marshal(x, cpuEndian)
|
||||||
|
|
||||||
check:
|
check:
|
||||||
# Check equality in the Montgomery domain
|
# Check equality in the Montgomery domain
|
||||||
@ -65,7 +65,7 @@ proc main() =
|
|||||||
x += y
|
x += y
|
||||||
|
|
||||||
var x_bytes: array[8, byte]
|
var x_bytes: array[8, byte]
|
||||||
x_bytes.exportRawUint(x, cpuEndian)
|
x_bytes.marshal(x, cpuEndian)
|
||||||
|
|
||||||
check:
|
check:
|
||||||
# Check equality in the Montgomery domain
|
# Check equality in the Montgomery domain
|
||||||
@ -84,7 +84,7 @@ proc main() =
|
|||||||
x -= y
|
x -= y
|
||||||
|
|
||||||
var x_bytes: array[8, byte]
|
var x_bytes: array[8, byte]
|
||||||
x_bytes.exportRawUint(x, cpuEndian)
|
x_bytes.marshal(x, cpuEndian)
|
||||||
|
|
||||||
check:
|
check:
|
||||||
# Check equality in the Montgomery domain
|
# Check equality in the Montgomery domain
|
||||||
@ -102,7 +102,7 @@ proc main() =
|
|||||||
x -= y
|
x -= y
|
||||||
|
|
||||||
var x_bytes: array[8, byte]
|
var x_bytes: array[8, byte]
|
||||||
x_bytes.exportRawUint(x, cpuEndian)
|
x_bytes.marshal(x, cpuEndian)
|
||||||
|
|
||||||
check:
|
check:
|
||||||
# Check equality in the Montgomery domain
|
# Check equality in the Montgomery domain
|
||||||
@ -120,7 +120,7 @@ proc main() =
|
|||||||
x -= y
|
x -= y
|
||||||
|
|
||||||
var x_bytes: array[8, byte]
|
var x_bytes: array[8, byte]
|
||||||
x_bytes.exportRawUint(x, cpuEndian)
|
x_bytes.marshal(x, cpuEndian)
|
||||||
|
|
||||||
check:
|
check:
|
||||||
# Check equality in the Montgomery domain
|
# Check equality in the Montgomery domain
|
||||||
@ -139,7 +139,7 @@ proc main() =
|
|||||||
r.prod(x, y)
|
r.prod(x, y)
|
||||||
|
|
||||||
var r_bytes: array[8, byte]
|
var r_bytes: array[8, byte]
|
||||||
r_bytes.exportRawUint(r, cpuEndian)
|
r_bytes.marshal(r, cpuEndian)
|
||||||
|
|
||||||
check:
|
check:
|
||||||
# Check equality in the Montgomery domain
|
# Check equality in the Montgomery domain
|
||||||
@ -157,7 +157,7 @@ proc main() =
|
|||||||
r.prod(x, y)
|
r.prod(x, y)
|
||||||
|
|
||||||
var r_bytes: array[8, byte]
|
var r_bytes: array[8, byte]
|
||||||
r_bytes.exportRawUint(r, cpuEndian)
|
r_bytes.marshal(r, cpuEndian)
|
||||||
|
|
||||||
check:
|
check:
|
||||||
# Check equality in the Montgomery domain
|
# Check equality in the Montgomery domain
|
||||||
@ -176,7 +176,7 @@ proc main() =
|
|||||||
x += y
|
x += y
|
||||||
|
|
||||||
var x_bytes: array[8, byte]
|
var x_bytes: array[8, byte]
|
||||||
x_bytes.exportRawUint(x, cpuEndian)
|
x_bytes.marshal(x, cpuEndian)
|
||||||
let new_x = cast[uint64](x_bytes)
|
let new_x = cast[uint64](x_bytes)
|
||||||
|
|
||||||
check:
|
check:
|
||||||
@ -195,7 +195,7 @@ proc main() =
|
|||||||
x += y
|
x += y
|
||||||
|
|
||||||
var x_bytes: array[8, byte]
|
var x_bytes: array[8, byte]
|
||||||
x_bytes.exportRawUint(x, cpuEndian)
|
x_bytes.marshal(x, cpuEndian)
|
||||||
let new_x = cast[uint64](x_bytes)
|
let new_x = cast[uint64](x_bytes)
|
||||||
|
|
||||||
check:
|
check:
|
||||||
@ -214,7 +214,7 @@ proc main() =
|
|||||||
x += y
|
x += y
|
||||||
|
|
||||||
var x_bytes: array[8, byte]
|
var x_bytes: array[8, byte]
|
||||||
x_bytes.exportRawUint(x, cpuEndian)
|
x_bytes.marshal(x, cpuEndian)
|
||||||
let new_x = cast[uint64](x_bytes)
|
let new_x = cast[uint64](x_bytes)
|
||||||
|
|
||||||
check:
|
check:
|
||||||
@ -234,7 +234,7 @@ proc main() =
|
|||||||
x -= y
|
x -= y
|
||||||
|
|
||||||
var x_bytes: array[8, byte]
|
var x_bytes: array[8, byte]
|
||||||
x_bytes.exportRawUint(x, cpuEndian)
|
x_bytes.marshal(x, cpuEndian)
|
||||||
let new_x = cast[uint64](x_bytes)
|
let new_x = cast[uint64](x_bytes)
|
||||||
|
|
||||||
check:
|
check:
|
||||||
@ -253,7 +253,7 @@ proc main() =
|
|||||||
x -= y
|
x -= y
|
||||||
|
|
||||||
var x_bytes: array[8, byte]
|
var x_bytes: array[8, byte]
|
||||||
x_bytes.exportRawUint(x, cpuEndian)
|
x_bytes.marshal(x, cpuEndian)
|
||||||
let new_x = cast[uint64](x_bytes)
|
let new_x = cast[uint64](x_bytes)
|
||||||
|
|
||||||
check:
|
check:
|
||||||
@ -273,7 +273,7 @@ proc main() =
|
|||||||
r.prod(x, y)
|
r.prod(x, y)
|
||||||
|
|
||||||
var r_bytes: array[8, byte]
|
var r_bytes: array[8, byte]
|
||||||
r_bytes.exportRawUint(r, cpuEndian)
|
r_bytes.marshal(r, cpuEndian)
|
||||||
let new_r = cast[uint64](r_bytes)
|
let new_r = cast[uint64](r_bytes)
|
||||||
|
|
||||||
check:
|
check:
|
||||||
@ -292,7 +292,7 @@ proc main() =
|
|||||||
r.prod(x, y)
|
r.prod(x, y)
|
||||||
|
|
||||||
var r_bytes: array[8, byte]
|
var r_bytes: array[8, byte]
|
||||||
r_bytes.exportRawUint(r, cpuEndian)
|
r_bytes.marshal(r, cpuEndian)
|
||||||
let new_r = cast[uint64](r_bytes)
|
let new_r = cast[uint64](r_bytes)
|
||||||
|
|
||||||
check:
|
check:
|
||||||
@ -322,7 +322,7 @@ proc largeField() =
|
|||||||
block:
|
block:
|
||||||
var a: Fp[Secp256k1]
|
var a: Fp[Secp256k1]
|
||||||
a.mres = Fp[Secp256k1].getMontyPrimeMinus1()
|
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]
|
var r: BigInt[256]
|
||||||
r.fromField(a)
|
r.fromField(a)
|
||||||
@ -333,8 +333,8 @@ proc largeField() =
|
|||||||
var d: FpDbl[Secp256k1]
|
var d: FpDbl[Secp256k1]
|
||||||
|
|
||||||
# Set Montgomery repr to the largest field element in Montgomery Residue form
|
# 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"
|
a.mres = BigInt[256].fromHex"0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2E"
|
||||||
d.limbs2x = (BigInt[512].fromHex"0xFFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2E").limbs
|
d.limbs2x = (BigInt[512].fromHex"0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2E").limbs
|
||||||
|
|
||||||
var r, expected: BigInt[256]
|
var r, expected: BigInt[256]
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ proc main() =
|
|||||||
r.prod(n, n)
|
r.prod(n, n)
|
||||||
|
|
||||||
var r_bytes: array[8, byte]
|
var r_bytes: array[8, byte]
|
||||||
r_bytes.exportRawUint(r, cpuEndian)
|
r_bytes.marshal(r, cpuEndian)
|
||||||
let rU64 = cast[uint64](r_bytes)
|
let rU64 = cast[uint64](r_bytes)
|
||||||
|
|
||||||
check:
|
check:
|
||||||
@ -61,7 +61,7 @@ proc main() =
|
|||||||
n.pow(exponent)
|
n.pow(exponent)
|
||||||
|
|
||||||
var n_bytes: array[8, byte]
|
var n_bytes: array[8, byte]
|
||||||
n_bytes.exportRawUint(n, cpuEndian)
|
n_bytes.marshal(n, cpuEndian)
|
||||||
let r = cast[uint64](n_bytes)
|
let r = cast[uint64](n_bytes)
|
||||||
|
|
||||||
check:
|
check:
|
||||||
@ -79,7 +79,7 @@ proc main() =
|
|||||||
n.pow(exponent)
|
n.pow(exponent)
|
||||||
|
|
||||||
var n_bytes: array[8, byte]
|
var n_bytes: array[8, byte]
|
||||||
n_bytes.exportRawUint(n, cpuEndian)
|
n_bytes.marshal(n, cpuEndian)
|
||||||
let r = cast[uint64](n_bytes)
|
let r = cast[uint64](n_bytes)
|
||||||
|
|
||||||
check:
|
check:
|
||||||
@ -97,7 +97,7 @@ proc main() =
|
|||||||
n.pow(exponent)
|
n.pow(exponent)
|
||||||
|
|
||||||
var n_bytes: array[8, byte]
|
var n_bytes: array[8, byte]
|
||||||
n_bytes.exportRawUint(n, cpuEndian)
|
n_bytes.marshal(n, cpuEndian)
|
||||||
let r = cast[uint64](n_bytes)
|
let r = cast[uint64](n_bytes)
|
||||||
|
|
||||||
check:
|
check:
|
||||||
@ -115,7 +115,7 @@ proc main() =
|
|||||||
n.pow(exponent)
|
n.pow(exponent)
|
||||||
|
|
||||||
var n_bytes: array[8, byte]
|
var n_bytes: array[8, byte]
|
||||||
n_bytes.exportRawUint(n, cpuEndian)
|
n_bytes.marshal(n, cpuEndian)
|
||||||
let r = cast[uint64](n_bytes)
|
let r = cast[uint64](n_bytes)
|
||||||
|
|
||||||
check:
|
check:
|
||||||
|
@ -41,7 +41,7 @@ proc exhaustiveCheck(C: static Curve, modulus: static int) =
|
|||||||
a.square()
|
a.square()
|
||||||
|
|
||||||
var r_bytes: array[8, byte]
|
var r_bytes: array[8, byte]
|
||||||
r_bytes.exportRawUint(a, cpuEndian)
|
r_bytes.marshal(a, cpuEndian)
|
||||||
let r = uint16(cast[uint64](r_bytes))
|
let r = uint16(cast[uint64](r_bytes))
|
||||||
|
|
||||||
squares_to_roots.mgetOrPut(r, default(set[uint16])).incl(i)
|
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)
|
check: bool(a == a2)
|
||||||
|
|
||||||
var r_bytes: array[8, byte]
|
var r_bytes: array[8, byte]
|
||||||
r_bytes.exportRawUint(a, cpuEndian)
|
r_bytes.marshal(a, cpuEndian)
|
||||||
let r = uint16(cast[uint64](r_bytes))
|
let r = uint16(cast[uint64](r_bytes))
|
||||||
|
|
||||||
# r is one of the 2 square roots of `i`
|
# 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)"
|
doAssert len >= bW, "Expected at most " & $len & " bytes but wrote " & $bW & " for " & toHex(bBuf) & " (big-endian)"
|
||||||
|
|
||||||
# Build the bigint
|
# Build the bigint
|
||||||
aTest = Fp[C].fromBig BigInt[bits].fromRawUint(aBuf.toOpenArray(0, aW-1), bigEndian)
|
aTest = Fp[C].fromBig BigInt[bits].unmarshal(aBuf.toOpenArray(0, aW-1), bigEndian)
|
||||||
bTest = Fp[C].fromBig BigInt[bits].fromRawUint(bBuf.toOpenArray(0, bW-1), bigEndian)
|
bTest = Fp[C].fromBig BigInt[bits].unmarshal(bBuf.toOpenArray(0, bW-1), bigEndian)
|
||||||
|
|
||||||
proc binary_epilogue[C: static Curve, N: static int](
|
proc binary_epilogue[C: static Curve, N: static int](
|
||||||
r, a, b: mpz_t,
|
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)
|
discard mpz_export(rGMP[0].addr, rW.addr, GMP_MostSignificantWordFirst, 1, GMP_WordNativeEndian, 0, r)
|
||||||
|
|
||||||
var rConstantine: array[N, byte]
|
var rConstantine: array[N, byte]
|
||||||
exportRawUint(rConstantine, rTest, bigEndian)
|
marshal(rConstantine, rTest, bigEndian)
|
||||||
|
|
||||||
# Note: in bigEndian, GMP aligns left while constantine aligns right
|
# Note: in bigEndian, GMP aligns left while constantine aligns right
|
||||||
doAssert rGMP.toOpenArray(0, rW-1) == rConstantine.toOpenArray(N-rW, N-1), block:
|
doAssert rGMP.toOpenArray(0, rW-1) == rConstantine.toOpenArray(N-rW, N-1), block:
|
||||||
|
@ -27,7 +27,7 @@ proc main() =
|
|||||||
block: # Sanity check
|
block: # Sanity check
|
||||||
let x = 0'u64
|
let x = 0'u64
|
||||||
let x_bytes = cast[array[8, byte]](x)
|
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:
|
check:
|
||||||
T(big.limbs[0]) == 0
|
T(big.limbs[0]) == 0
|
||||||
@ -37,29 +37,29 @@ proc main() =
|
|||||||
# "Little-endian" - 2^63
|
# "Little-endian" - 2^63
|
||||||
let x = 1'u64 shl 63
|
let x = 1'u64 shl 63
|
||||||
let x_bytes = cast[array[8, byte]](x)
|
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]
|
var r_bytes: array[8, byte]
|
||||||
exportRawUint(r_bytes, big, littleEndian)
|
marshal(r_bytes, big, littleEndian)
|
||||||
check: x_bytes == r_bytes
|
check: x_bytes == r_bytes
|
||||||
|
|
||||||
block: # "Little-endian" - single random
|
block: # "Little-endian" - single random
|
||||||
let x = rng.random_unsafe(uint64)
|
let x = rng.random_unsafe(uint64)
|
||||||
let x_bytes = cast[array[8, byte]](x)
|
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]
|
var r_bytes: array[8, byte]
|
||||||
exportRawUint(r_bytes, big, littleEndian)
|
marshal(r_bytes, big, littleEndian)
|
||||||
check: x_bytes == r_bytes
|
check: x_bytes == r_bytes
|
||||||
|
|
||||||
block: # "Little-endian" - 10 random cases
|
block: # "Little-endian" - 10 random cases
|
||||||
for _ in 0 ..< 10:
|
for _ in 0 ..< 10:
|
||||||
let x = rng.random_unsafe(uint64)
|
let x = rng.random_unsafe(uint64)
|
||||||
let x_bytes = cast[array[8, byte]](x)
|
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]
|
var r_bytes: array[8, byte]
|
||||||
exportRawUint(r_bytes, big, littleEndian)
|
marshal(r_bytes, big, littleEndian)
|
||||||
check: x_bytes == r_bytes
|
check: x_bytes == r_bytes
|
||||||
|
|
||||||
test "Round trip on elliptic curve constants":
|
test "Round trip on elliptic curve constants":
|
||||||
|
@ -32,7 +32,7 @@ proc main() =
|
|||||||
f.fromUint(x)
|
f.fromUint(x)
|
||||||
|
|
||||||
var r_bytes: array[sizeof(BaseType), byte]
|
var r_bytes: array[sizeof(BaseType), byte]
|
||||||
exportRawUint(r_bytes, f, littleEndian)
|
marshal(r_bytes, f, littleEndian)
|
||||||
check: x_bytes == r_bytes
|
check: x_bytes == r_bytes
|
||||||
|
|
||||||
block:
|
block:
|
||||||
@ -43,7 +43,7 @@ proc main() =
|
|||||||
f.fromUint(x)
|
f.fromUint(x)
|
||||||
|
|
||||||
var r_bytes: array[sizeof(BaseType), byte]
|
var r_bytes: array[sizeof(BaseType), byte]
|
||||||
exportRawUint(r_bytes, f, littleEndian)
|
marshal(r_bytes, f, littleEndian)
|
||||||
check: x_bytes == r_bytes
|
check: x_bytes == r_bytes
|
||||||
|
|
||||||
# Mersenne 61 ---------------------------------
|
# Mersenne 61 ---------------------------------
|
||||||
@ -55,7 +55,7 @@ proc main() =
|
|||||||
f.fromUint(x)
|
f.fromUint(x)
|
||||||
|
|
||||||
var r_bytes: array[8, byte]
|
var r_bytes: array[8, byte]
|
||||||
exportRawUint(r_bytes, f, littleEndian)
|
marshal(r_bytes, f, littleEndian)
|
||||||
check: x_bytes == r_bytes
|
check: x_bytes == r_bytes
|
||||||
|
|
||||||
block:
|
block:
|
||||||
@ -66,7 +66,7 @@ proc main() =
|
|||||||
f.fromUint(x)
|
f.fromUint(x)
|
||||||
|
|
||||||
var r_bytes: array[8, byte]
|
var r_bytes: array[8, byte]
|
||||||
exportRawUint(r_bytes, f, littleEndian)
|
marshal(r_bytes, f, littleEndian)
|
||||||
check: x_bytes == r_bytes
|
check: x_bytes == r_bytes
|
||||||
|
|
||||||
block:
|
block:
|
||||||
@ -77,7 +77,7 @@ proc main() =
|
|||||||
f.fromUint(x)
|
f.fromUint(x)
|
||||||
|
|
||||||
var r_bytes: array[8, byte]
|
var r_bytes: array[8, byte]
|
||||||
exportRawUint(r_bytes, f, littleEndian)
|
marshal(r_bytes, f, littleEndian)
|
||||||
check: x_bytes == r_bytes
|
check: x_bytes == r_bytes
|
||||||
|
|
||||||
block:
|
block:
|
||||||
@ -88,7 +88,7 @@ proc main() =
|
|||||||
f.fromUint(x)
|
f.fromUint(x)
|
||||||
|
|
||||||
var r_bytes: array[8, byte]
|
var r_bytes: array[8, byte]
|
||||||
exportRawUint(r_bytes, f, littleEndian)
|
marshal(r_bytes, f, littleEndian)
|
||||||
check: x_bytes == r_bytes
|
check: x_bytes == r_bytes
|
||||||
|
|
||||||
# Mersenne 127 ---------------------------------
|
# Mersenne 127 ---------------------------------
|
||||||
@ -100,7 +100,7 @@ proc main() =
|
|||||||
f.fromUint(x)
|
f.fromUint(x)
|
||||||
|
|
||||||
var r_bytes: array[16, byte]
|
var r_bytes: array[16, byte]
|
||||||
exportRawUint(r_bytes, f, littleEndian)
|
marshal(r_bytes, f, littleEndian)
|
||||||
check: x_bytes == r_bytes[0 ..< 8]
|
check: x_bytes == r_bytes[0 ..< 8]
|
||||||
|
|
||||||
block: # "Little-endian" - single random
|
block: # "Little-endian" - single random
|
||||||
@ -110,7 +110,7 @@ proc main() =
|
|||||||
f.fromUint(x)
|
f.fromUint(x)
|
||||||
|
|
||||||
var r_bytes: array[16, byte]
|
var r_bytes: array[16, byte]
|
||||||
exportRawUint(r_bytes, f, littleEndian)
|
marshal(r_bytes, f, littleEndian)
|
||||||
check: x_bytes == r_bytes[0 ..< 8]
|
check: x_bytes == r_bytes[0 ..< 8]
|
||||||
|
|
||||||
block: # "Little-endian" - 10 random cases
|
block: # "Little-endian" - 10 random cases
|
||||||
@ -121,7 +121,7 @@ proc main() =
|
|||||||
f.fromUint(x)
|
f.fromUint(x)
|
||||||
|
|
||||||
var r_bytes: array[16, byte]
|
var r_bytes: array[16, byte]
|
||||||
exportRawUint(r_bytes, f, littleEndian)
|
marshal(r_bytes, f, littleEndian)
|
||||||
check: x_bytes == r_bytes[0 ..< 8]
|
check: x_bytes == r_bytes[0 ..< 8]
|
||||||
|
|
||||||
test "Round trip on large constant":
|
test "Round trip on large constant":
|
||||||
|
Loading…
x
Reference in New Issue
Block a user