[WIP] Skewed RNGs that trigger corner cases (#59)
* Add a RNG skewed to high hamming weights * Add libsecp256k1 skewed RNG that found a CVE in OpenSSL * Add initial skewed RNGs tests to finite fields * Add Fp towers skewed tests * Add ellptic curve skewed tests
This commit is contained in:
parent
a2a2495351
commit
e491f3b91d
|
@ -8,8 +8,10 @@
|
||||||
|
|
||||||
import
|
import
|
||||||
../constantine/arithmetic/bigints,
|
../constantine/arithmetic/bigints,
|
||||||
|
../constantine/primitives,
|
||||||
../constantine/config/[common, curves],
|
../constantine/config/[common, curves],
|
||||||
../constantine/elliptic/[ec_weierstrass_affine, ec_weierstrass_projective]
|
../constantine/elliptic/[ec_weierstrass_affine, ec_weierstrass_projective],
|
||||||
|
../constantine/io/io_bigints
|
||||||
|
|
||||||
# ############################################################
|
# ############################################################
|
||||||
#
|
#
|
||||||
|
@ -79,17 +81,65 @@ func next(rng: var RngState): uint64 =
|
||||||
|
|
||||||
rng.s[7] = rotl(rng.s[7], 21);
|
rng.s[7] = rotl(rng.s[7], 21);
|
||||||
|
|
||||||
|
# Integer ranges
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
|
||||||
|
func random_unsafe*(rng: var RngState, maxExclusive: uint32): uint32 =
|
||||||
|
## Generate a random integer in 0 ..< maxExclusive
|
||||||
|
## Uses an unbiaised generation method
|
||||||
|
## See Lemire's algorithm modified by Melissa O'Neill
|
||||||
|
## https://www.pcg-random.org/posts/bounded-rands.html
|
||||||
|
let max = maxExclusive
|
||||||
|
var x = uint32 rng.next()
|
||||||
|
var m = x.uint64 * max.uint64
|
||||||
|
var l = uint32 m
|
||||||
|
if l < max:
|
||||||
|
var t = not(max) + 1 # -max
|
||||||
|
if t >= max:
|
||||||
|
t -= max
|
||||||
|
if t >= max:
|
||||||
|
t = t mod max
|
||||||
|
while l < t:
|
||||||
|
x = uint32 rng.next()
|
||||||
|
m = x.uint64 * max.uint64
|
||||||
|
l = uint32 m
|
||||||
|
return uint32(m shr 32)
|
||||||
|
|
||||||
|
func random_unsafe*[T: SomeInteger](rng: var RngState, inclRange: Slice[T]): T =
|
||||||
|
## Return a random integer in the given range.
|
||||||
|
## The range bounds must fit in an int32.
|
||||||
|
let maxExclusive = inclRange.b + 1 - inclRange.a
|
||||||
|
result = T(rng.random_unsafe(uint32 maxExclusive))
|
||||||
|
result += inclRange.a
|
||||||
|
|
||||||
|
# Containers
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
|
||||||
|
func sample_unsafe*[T](rng: var RngState, src: openarray[T]): T =
|
||||||
|
## Return a random sample from an array
|
||||||
|
result = src[rng.random_unsafe(uint32 src.len)]
|
||||||
|
|
||||||
# BigInts and Fields
|
# BigInts and Fields
|
||||||
# ------------------------------------------------------------
|
# ------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Statistics note:
|
||||||
|
# - A skewed distribution is not symmetric, it has a longer tail in one direction.
|
||||||
|
# for example a RNG that is not centered over 0.5 distribution of 0 and 1 but
|
||||||
|
# might produces more 1 than 0 or vice-versa.
|
||||||
|
# - A bias is a result that is consistently off from the true value i.e.
|
||||||
|
# a deviation of an estimate from the quantity under observation
|
||||||
|
|
||||||
|
func random_unsafe(rng: var RngState, a: var BigInt) =
|
||||||
|
## Initialize a standalone BigInt
|
||||||
|
for i in 0 ..< a.limbs.len:
|
||||||
|
a.limbs[i] = SecretWord(rng.next())
|
||||||
|
|
||||||
func random_unsafe[T](rng: var RngState, a: var T, C: static Curve) =
|
func random_unsafe[T](rng: var RngState, a: var T, C: static Curve) =
|
||||||
## Recursively initialize a BigInt (part of a field) or Field element
|
## Recursively initialize a BigInt (part of a field) or Field element
|
||||||
## Unsafe: for testing and benchmarking purposes only
|
## Unsafe: for testing and benchmarking purposes only
|
||||||
when T is BigInt:
|
when T is BigInt:
|
||||||
var reduced, unreduced{.noInit.}: T
|
var reduced, unreduced{.noInit.}: T
|
||||||
|
rng.random_unsafe(unreduced)
|
||||||
for i in 0 ..< unreduced.limbs.len:
|
|
||||||
unreduced.limbs[i] = SecretWord(rng.next())
|
|
||||||
|
|
||||||
# Note: a simple modulo will be biaised but it's simple and "fast"
|
# Note: a simple modulo will be biaised but it's simple and "fast"
|
||||||
reduced.reduce(unreduced, C.Mod)
|
reduced.reduce(unreduced, C.Mod)
|
||||||
|
@ -99,10 +149,79 @@ func random_unsafe[T](rng: var RngState, a: var T, C: static Curve) =
|
||||||
for field in fields(a):
|
for field in fields(a):
|
||||||
rng.random_unsafe(field, C)
|
rng.random_unsafe(field, C)
|
||||||
|
|
||||||
func random_unsafe(rng: var RngState, a: var BigInt) =
|
func random_word_highHammingWeight(rng: var RngState): BaseType =
|
||||||
|
let numZeros = rng.random_unsafe(WordBitWidth div 3) # Average Hamming Weight is 1-0.33/2 = 0.83
|
||||||
|
result = high(BaseType)
|
||||||
|
for _ in 0 ..< numZeros:
|
||||||
|
result = result.clearBit rng.random_unsafe(WordBitWidth)
|
||||||
|
|
||||||
|
func random_highHammingWeight(rng: var RngState, a: var BigInt) =
|
||||||
## Initialize a standalone BigInt
|
## Initialize a standalone BigInt
|
||||||
|
## with high Hamming weight
|
||||||
|
## to have a higher probability of triggering carries
|
||||||
for i in 0 ..< a.limbs.len:
|
for i in 0 ..< a.limbs.len:
|
||||||
a.limbs[i] = SecretWord(rng.next())
|
a.limbs[i] = SecretWord rng.random_word_highHammingWeight()
|
||||||
|
|
||||||
|
func random_highHammingWeight[T](rng: var RngState, a: var T, C: static Curve) =
|
||||||
|
## Recursively initialize a BigInt (part of a field) or Field element
|
||||||
|
## Unsafe: for testing and benchmarking purposes only
|
||||||
|
## The result will have a high Hamming Weight
|
||||||
|
## to have a higher probability of triggering carries
|
||||||
|
when T is BigInt:
|
||||||
|
var reduced, unreduced{.noInit.}: T
|
||||||
|
rng.random_highHammingWeight(unreduced)
|
||||||
|
|
||||||
|
# Note: a simple modulo will be biaised but it's simple and "fast"
|
||||||
|
reduced.reduce(unreduced, C.Mod)
|
||||||
|
a.montyResidue(reduced, C.Mod, C.getR2modP(), C.getNegInvModWord(), C.canUseNoCarryMontyMul())
|
||||||
|
|
||||||
|
else:
|
||||||
|
for field in fields(a):
|
||||||
|
rng.random_highHammingWeight(field, C)
|
||||||
|
|
||||||
|
func random_long01Seq(rng: var RngState, a: var openArray[byte]) =
|
||||||
|
## Initialize a bytearray
|
||||||
|
## It is skewed towards producing strings of 1111... and 0000
|
||||||
|
## to trigger edge cases
|
||||||
|
# See libsecp256k1: https://github.com/bitcoin-core/secp256k1/blob/dbd41db1/src/testrand_impl.h#L90-L104
|
||||||
|
let Bits = a.len * 8
|
||||||
|
var bit = 0
|
||||||
|
zeroMem(a[0].addr, a.len)
|
||||||
|
while bit < Bits :
|
||||||
|
var now = 1 + (rng.random_unsafe(1 shl 6) * rng.random_unsafe(1 shl 5) + 16) div 31
|
||||||
|
let val = rng.sample_unsafe([0, 1])
|
||||||
|
while now > 0 and bit < Bits:
|
||||||
|
a[bit shr 3] = a[bit shr 3] or byte(val shl (bit and 7))
|
||||||
|
dec now
|
||||||
|
inc bit
|
||||||
|
|
||||||
|
func random_long01Seq(rng: var RngState, a: var BigInt) =
|
||||||
|
## Initialize a bigint
|
||||||
|
## It is skewed towards producing strings of 1111... and 0000
|
||||||
|
## to trigger edge cases
|
||||||
|
var buf: array[(a.bits + 7) div 8, byte]
|
||||||
|
rng.random_long01Seq(buf)
|
||||||
|
let order = rng.sample_unsafe([bigEndian, littleEndian])
|
||||||
|
if order == bigEndian:
|
||||||
|
a.fromRawUint(buf, bigEndian)
|
||||||
|
else:
|
||||||
|
a.fromRawUint(buf, littleEndian)
|
||||||
|
|
||||||
|
func random_long01Seq[T](rng: var RngState, a: var T, C: static Curve) =
|
||||||
|
## Recursively initialize a BigInt (part of a field) or Field element
|
||||||
|
## It is skewed towards producing strings of 1111... and 0000
|
||||||
|
## to trigger edge cases
|
||||||
|
when T is BigInt:
|
||||||
|
var reduced, unreduced{.noInit.}: T
|
||||||
|
rng.random_long01Seq(unreduced)
|
||||||
|
|
||||||
|
# Note: a simple modulo will be biaised but it's simple and "fast"
|
||||||
|
reduced.reduce(unreduced, C.Mod)
|
||||||
|
a.montyResidue(reduced, C.Mod, C.getR2modP(), C.getNegInvModWord(), C.canUseNoCarryMontyMul())
|
||||||
|
|
||||||
|
else:
|
||||||
|
for field in fields(a):
|
||||||
|
rng.random_highHammingWeight(field, C)
|
||||||
|
|
||||||
# Elliptic curves
|
# Elliptic curves
|
||||||
# ------------------------------------------------------------
|
# ------------------------------------------------------------
|
||||||
|
@ -132,44 +251,65 @@ func random_unsafe_with_randZ[F](rng: var RngState, a: var ECP_SWei_Proj[F]) =
|
||||||
rng.random_unsafe(fieldElem, F.C)
|
rng.random_unsafe(fieldElem, F.C)
|
||||||
success = trySetFromCoordsXandZ(a, fieldElem, Z)
|
success = trySetFromCoordsXandZ(a, fieldElem, Z)
|
||||||
|
|
||||||
# Integer ranges
|
func random_highHammingWeight[F](rng: var RngState, a: var ECP_SWei_Proj[F]) =
|
||||||
|
## Initialize a random curve point with Z coordinate == 1
|
||||||
|
## This will be generated with a biaised RNG with high Hamming Weight
|
||||||
|
## to trigger carry bugs
|
||||||
|
var fieldElem {.noInit.}: F
|
||||||
|
var success = CtFalse
|
||||||
|
|
||||||
|
while not bool(success):
|
||||||
|
# Euler's criterion: there are (p-1)/2 squares in a field with modulus `p`
|
||||||
|
# so we have a probability of ~0.5 to get a good point
|
||||||
|
rng.random_highHammingWeight(fieldElem, F.C)
|
||||||
|
success = trySetFromCoordX(a, fieldElem)
|
||||||
|
|
||||||
|
func random_highHammingWeight_with_randZ[F](rng: var RngState, a: var ECP_SWei_Proj[F]) =
|
||||||
|
## Initialize a random curve point with Z coordinate == 1
|
||||||
|
## This will be generated with a biaised RNG with high Hamming Weight
|
||||||
|
## to trigger carry bugs
|
||||||
|
var Z{.noInit.}: F
|
||||||
|
rng.random_highHammingWeight(Z, F.C) # If Z is zero, X will be zero and that will be an infinity point
|
||||||
|
|
||||||
|
var fieldElem {.noInit.}: F
|
||||||
|
var success = CtFalse
|
||||||
|
|
||||||
|
while not bool(success):
|
||||||
|
rng.random_highHammingWeight(fieldElem, F.C)
|
||||||
|
success = trySetFromCoordsXandZ(a, fieldElem, Z)
|
||||||
|
|
||||||
|
func random_long01Seq[F](rng: var RngState, a: var ECP_SWei_Proj[F]) =
|
||||||
|
## Initialize a random curve point with Z coordinate == 1
|
||||||
|
## This will be generated with a biaised RNG
|
||||||
|
## that produces long bitstrings of 0 and 1
|
||||||
|
## to trigger edge cases
|
||||||
|
var fieldElem {.noInit.}: F
|
||||||
|
var success = CtFalse
|
||||||
|
|
||||||
|
while not bool(success):
|
||||||
|
# Euler's criterion: there are (p-1)/2 squares in a field with modulus `p`
|
||||||
|
# so we have a probability of ~0.5 to get a good point
|
||||||
|
rng.random_long01Seq(fieldElem, F.C)
|
||||||
|
success = trySetFromCoordX(a, fieldElem)
|
||||||
|
|
||||||
|
func random_long01Seq_with_randZ[F](rng: var RngState, a: var ECP_SWei_Proj[F]) =
|
||||||
|
## Initialize a random curve point with Z coordinate == 1
|
||||||
|
## This will be generated with a biaised RNG
|
||||||
|
## that produces long bitstrings of 0 and 1
|
||||||
|
## to trigger edge cases
|
||||||
|
var Z{.noInit.}: F
|
||||||
|
rng.random_long01Seq(Z, F.C) # If Z is zero, X will be zero and that will be an infinity point
|
||||||
|
|
||||||
|
var fieldElem {.noInit.}: F
|
||||||
|
var success = CtFalse
|
||||||
|
|
||||||
|
while not bool(success):
|
||||||
|
rng.random_long01Seq(fieldElem, F.C)
|
||||||
|
success = trySetFromCoordsXandZ(a, fieldElem, Z)
|
||||||
|
|
||||||
|
# Generic over any Constantine type
|
||||||
# ------------------------------------------------------------
|
# ------------------------------------------------------------
|
||||||
|
|
||||||
func random_unsafe*(rng: var RngState, maxExclusive: uint32): uint32 =
|
|
||||||
## Generate a random integer in 0 ..< maxExclusive
|
|
||||||
## Uses an unbiaised generation method
|
|
||||||
## See Lemire's algorithm modified by Melissa O'Neill
|
|
||||||
## https://www.pcg-random.org/posts/bounded-rands.html
|
|
||||||
let max = maxExclusive
|
|
||||||
var x = uint32 rng.next()
|
|
||||||
var m = x.uint64 * max.uint64
|
|
||||||
var l = uint32 m
|
|
||||||
if l < max:
|
|
||||||
var t = not(max) + 1 # -max
|
|
||||||
if t >= max:
|
|
||||||
t -= max
|
|
||||||
if t >= max:
|
|
||||||
t = t mod max
|
|
||||||
while l < t:
|
|
||||||
x = uint32 rng.next()
|
|
||||||
m = x.uint64 * max.uint64
|
|
||||||
l = uint32 m
|
|
||||||
return uint32(m shr 32)
|
|
||||||
|
|
||||||
# Generic over any supported type
|
|
||||||
# ------------------------------------------------------------
|
|
||||||
|
|
||||||
func sample_unsafe*[T](rng: var RngState, src: openarray[T]): T =
|
|
||||||
## Return a random sample from an array
|
|
||||||
result = src[rng.random_unsafe(uint32 src.len)]
|
|
||||||
|
|
||||||
func random_unsafe*[T: SomeInteger](rng: var RngState, inclRange: Slice[T]): T =
|
|
||||||
## Return a random integer in the given range.
|
|
||||||
## The range bounds must fit in an int32.
|
|
||||||
let maxExclusive = inclRange.b + 1 - inclRange.a
|
|
||||||
result = T(rng.random_unsafe(uint32 maxExclusive))
|
|
||||||
result += inclRange.a
|
|
||||||
|
|
||||||
func random_unsafe*(rng: var RngState, T: typedesc): T =
|
func random_unsafe*(rng: var RngState, T: typedesc): T =
|
||||||
## Create a random Field or Extension Field or Curve Element
|
## Create a random Field or Extension Field or Curve Element
|
||||||
## Unsafe: for testing and benchmarking purposes only
|
## Unsafe: for testing and benchmarking purposes only
|
||||||
|
@ -187,11 +327,45 @@ func random_unsafe_with_randZ*(rng: var RngState, T: typedesc[ECP_SWei_Proj]): T
|
||||||
## Unsafe: for testing and benchmarking purposes only
|
## Unsafe: for testing and benchmarking purposes only
|
||||||
rng.random_unsafe_with_randZ(result)
|
rng.random_unsafe_with_randZ(result)
|
||||||
|
|
||||||
|
func random_highHammingWeight*(rng: var RngState, T: typedesc): T =
|
||||||
|
## Create a random Field or Extension Field or Curve Element
|
||||||
|
## Skewed towards high Hamming Weight
|
||||||
|
when T is ECP_SWei_Proj:
|
||||||
|
rng.random_highHammingWeight(result)
|
||||||
|
elif T is SomeNumber:
|
||||||
|
cast[T](rng.next()) # TODO: Rely on casting integer actually converting in C (i.e. uint64->uint32 is valid)
|
||||||
|
elif T is BigInt:
|
||||||
|
rng.random_highHammingWeight(result)
|
||||||
|
else: # Fields
|
||||||
|
rng.random_highHammingWeight(result, T.C)
|
||||||
|
|
||||||
|
func random_highHammingWeight_with_randZ*(rng: var RngState, T: typedesc[ECP_SWei_Proj]): T =
|
||||||
|
## Create a random curve element with a random Z coordinate
|
||||||
|
## Skewed towards high Hamming Weight
|
||||||
|
rng.random_highHammingWeight_with_randZ(result)
|
||||||
|
|
||||||
|
func random_long01Seq*(rng: var RngState, T: typedesc): T =
|
||||||
|
## Create a random Field or Extension Field or Curve Element
|
||||||
|
## Skewed towards long bitstrings of 0 or 1
|
||||||
|
when T is ECP_SWei_Proj:
|
||||||
|
rng.random_long01Seq(result)
|
||||||
|
elif T is SomeNumber:
|
||||||
|
cast[T](rng.next()) # TODO: Rely on casting integer actually converting in C (i.e. uint64->uint32 is valid)
|
||||||
|
elif T is BigInt:
|
||||||
|
rng.random_long01Seq(result)
|
||||||
|
else: # Fields
|
||||||
|
rng.random_long01Seq(result, T.C)
|
||||||
|
|
||||||
|
func random_long01Seq_with_randZ*(rng: var RngState, T: typedesc[ECP_SWei_Proj]): T =
|
||||||
|
## Create a random curve element with a random Z coordinate
|
||||||
|
## Skewed towards long bitstrings of 0 or 1
|
||||||
|
rng.random_long01Seq_with_randZ(result)
|
||||||
|
|
||||||
# Sanity checks
|
# Sanity checks
|
||||||
# ------------------------------------------------------------
|
# ------------------------------------------------------------
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
import std/[tables, times]
|
import std/[tables, times, strutils]
|
||||||
|
|
||||||
var rng: RngState
|
var rng: RngState
|
||||||
let timeSeed = uint32(getTime().toUnix() and (1'i64 shl 32 - 1)) # unixTime mod 2^32
|
let timeSeed = uint32(getTime().toUnix() and (1'i64 shl 32 - 1)) # unixTime mod 2^32
|
||||||
|
@ -211,3 +385,22 @@ when isMainModule:
|
||||||
test(0..2)
|
test(0..2)
|
||||||
test(1..52)
|
test(1..52)
|
||||||
test(-10..10)
|
test(-10..10)
|
||||||
|
|
||||||
|
echo "\n-----------------------------\n"
|
||||||
|
echo "High Hamming Weight check"
|
||||||
|
for _ in 0 ..< 10:
|
||||||
|
let word = rng.random_word_highHammingWeight()
|
||||||
|
echo "0b", cast[BiggestInt](word).toBin(WordBitWidth), " - 0x", word.toHex()
|
||||||
|
|
||||||
|
echo "\n-----------------------------\n"
|
||||||
|
echo "Long strings of 0 or 1 check"
|
||||||
|
for _ in 0 ..< 10:
|
||||||
|
var a: BigInt[127]
|
||||||
|
rng.random_long01seq(a)
|
||||||
|
stdout.write "0b"
|
||||||
|
for word in a.limbs:
|
||||||
|
stdout.write cast[BiggestInt](word).toBin(WordBitWidth)
|
||||||
|
stdout.write " - 0x"
|
||||||
|
for word in a.limbs:
|
||||||
|
stdout.write word.BaseType.toHex()
|
||||||
|
stdout.write '\n'
|
||||||
|
|
|
@ -25,6 +25,28 @@ import
|
||||||
../helpers/prng_unsafe,
|
../helpers/prng_unsafe,
|
||||||
./support/ec_reference_scalar_mult
|
./support/ec_reference_scalar_mult
|
||||||
|
|
||||||
|
type
|
||||||
|
RandomGen = enum
|
||||||
|
Uniform
|
||||||
|
HighHammingWeight
|
||||||
|
Long01Sequence
|
||||||
|
|
||||||
|
func random_point(rng: var RngState, F: typedesc, randZ: static bool, gen: static RandomGen): F {.inline, noInit.} =
|
||||||
|
when not randZ:
|
||||||
|
when gen == Uniform:
|
||||||
|
result = rng.random_unsafe(F)
|
||||||
|
elif gen == HighHammingWeight:
|
||||||
|
result = rng.random_highHammingWeight(F)
|
||||||
|
else:
|
||||||
|
result = rng.random_long01Seq(F)
|
||||||
|
else:
|
||||||
|
when gen == Uniform:
|
||||||
|
result = rng.random_unsafe_with_randZ(F)
|
||||||
|
elif gen == HighHammingWeight:
|
||||||
|
result = rng.random_highHammingWeight_with_randZ(F)
|
||||||
|
else:
|
||||||
|
result = rng.random_long01Seq_with_randZ(F)
|
||||||
|
|
||||||
proc run_EC_addition_tests*(
|
proc run_EC_addition_tests*(
|
||||||
ec: typedesc,
|
ec: typedesc,
|
||||||
Iters: static int,
|
Iters: static int,
|
||||||
|
@ -47,17 +69,14 @@ proc run_EC_addition_tests*(
|
||||||
|
|
||||||
suite testSuiteDesc & " - " & $ec & " - [" & $WordBitwidth & "-bit mode]":
|
suite testSuiteDesc & " - " & $ec & " - [" & $WordBitwidth & "-bit mode]":
|
||||||
test "The infinity point is the neutral element w.r.t. to EC " & G1_or_G2 & " addition":
|
test "The infinity point is the neutral element w.r.t. to EC " & G1_or_G2 & " addition":
|
||||||
proc test(EC: typedesc, randZ: static bool) =
|
proc test(EC: typedesc, randZ: static bool, gen: static RandomGen) =
|
||||||
var inf {.noInit.}: EC
|
var inf {.noInit.}: EC
|
||||||
inf.setInf()
|
inf.setInf()
|
||||||
check: bool inf.isInf()
|
check: bool inf.isInf()
|
||||||
|
|
||||||
for _ in 0 ..< Iters:
|
for _ in 0 ..< Iters:
|
||||||
var r{.noInit.}: EC
|
var r{.noInit.}: EC
|
||||||
when randZ:
|
let P = rng.random_point(EC, randZ, gen)
|
||||||
let P = rng.random_unsafe_with_randZ(EC)
|
|
||||||
else:
|
|
||||||
let P = rng.random_unsafe(EC)
|
|
||||||
|
|
||||||
r.sum(P, inf)
|
r.sum(P, inf)
|
||||||
check: bool(r == P)
|
check: bool(r == P)
|
||||||
|
@ -65,17 +84,18 @@ proc run_EC_addition_tests*(
|
||||||
r.sum(inf, P)
|
r.sum(inf, P)
|
||||||
check: bool(r == P)
|
check: bool(r == P)
|
||||||
|
|
||||||
test(ec, randZ = false)
|
test(ec, randZ = false, gen = Uniform)
|
||||||
test(ec, randZ = true)
|
test(ec, randZ = true, gen = Uniform)
|
||||||
|
test(ec, randZ = false, gen = HighHammingWeight)
|
||||||
|
test(ec, randZ = true, gen = HighHammingWeight)
|
||||||
|
test(ec, randZ = false, gen = Long01Sequence)
|
||||||
|
test(ec, randZ = true, gen = Long01Sequence)
|
||||||
|
|
||||||
test "Adding opposites gives an infinity point":
|
test "Adding opposites gives an infinity point":
|
||||||
proc test(EC: typedesc, randZ: static bool) =
|
proc test(EC: typedesc, randZ: static bool, gen: static RandomGen) =
|
||||||
for _ in 0 ..< Iters:
|
for _ in 0 ..< Iters:
|
||||||
var r{.noInit.}: EC
|
var r{.noInit.}: EC
|
||||||
when randZ:
|
let P = rng.random_point(EC, randZ, gen)
|
||||||
let P = rng.random_unsafe_with_randZ(EC)
|
|
||||||
else:
|
|
||||||
let P = rng.random_unsafe(EC)
|
|
||||||
var Q = P
|
var Q = P
|
||||||
Q.neg()
|
Q.neg()
|
||||||
|
|
||||||
|
@ -85,38 +105,37 @@ proc run_EC_addition_tests*(
|
||||||
r.sum(Q, P)
|
r.sum(Q, P)
|
||||||
check: bool r.isInf()
|
check: bool r.isInf()
|
||||||
|
|
||||||
test(ec, randZ = false)
|
test(ec, randZ = false, gen = Uniform)
|
||||||
test(ec, randZ = true)
|
test(ec, randZ = true, gen = Uniform)
|
||||||
|
test(ec, randZ = false, gen = HighHammingWeight)
|
||||||
|
test(ec, randZ = true, gen = HighHammingWeight)
|
||||||
|
test(ec, randZ = false, gen = Long01Sequence)
|
||||||
|
test(ec, randZ = true, gen = Long01Sequence)
|
||||||
|
|
||||||
test "EC " & G1_or_G2 & " add is commutative":
|
test "EC " & G1_or_G2 & " add is commutative":
|
||||||
proc test(EC: typedesc, randZ: static bool) =
|
proc test(EC: typedesc, randZ: static bool, gen: static RandomGen) =
|
||||||
for _ in 0 ..< Iters:
|
for _ in 0 ..< Iters:
|
||||||
var r0{.noInit.}, r1{.noInit.}: EC
|
var r0{.noInit.}, r1{.noInit.}: EC
|
||||||
when randZ:
|
let P = rng.random_point(EC, randZ, gen)
|
||||||
let P = rng.random_unsafe_with_randZ(EC)
|
let Q = rng.random_point(EC, randZ, gen)
|
||||||
let Q = rng.random_unsafe_with_randZ(EC)
|
|
||||||
else:
|
|
||||||
let P = rng.random_unsafe(EC)
|
|
||||||
let Q = rng.random_unsafe(EC)
|
|
||||||
|
|
||||||
r0.sum(P, Q)
|
r0.sum(P, Q)
|
||||||
r1.sum(Q, P)
|
r1.sum(Q, P)
|
||||||
check: bool(r0 == r1)
|
check: bool(r0 == r1)
|
||||||
|
|
||||||
test(ec, randZ = false)
|
test(ec, randZ = false, gen = Uniform)
|
||||||
test(ec, randZ = true)
|
test(ec, randZ = true, gen = Uniform)
|
||||||
|
test(ec, randZ = false, gen = HighHammingWeight)
|
||||||
|
test(ec, randZ = true, gen = HighHammingWeight)
|
||||||
|
test(ec, randZ = false, gen = Long01Sequence)
|
||||||
|
test(ec, randZ = true, gen = Long01Sequence)
|
||||||
|
|
||||||
test "EC " & G1_or_G2 & " add is associative":
|
test "EC " & G1_or_G2 & " add is associative":
|
||||||
proc test(EC: typedesc, randZ: static bool) =
|
proc test(EC: typedesc, randZ: static bool, gen: static RandomGen) =
|
||||||
for _ in 0 ..< Iters:
|
for _ in 0 ..< Iters:
|
||||||
when randZ:
|
let a = rng.random_point(EC, randZ, gen)
|
||||||
let a = rng.random_unsafe_with_randZ(EC)
|
let b = rng.random_point(EC, randZ, gen)
|
||||||
let b = rng.random_unsafe_with_randZ(EC)
|
let c = rng.random_point(EC, randZ, gen)
|
||||||
let c = rng.random_unsafe_with_randZ(EC)
|
|
||||||
else:
|
|
||||||
let a = rng.random_unsafe(EC)
|
|
||||||
let b = rng.random_unsafe(EC)
|
|
||||||
let c = rng.random_unsafe(EC)
|
|
||||||
|
|
||||||
var tmp1{.noInit.}, tmp2{.noInit.}: EC
|
var tmp1{.noInit.}, tmp2{.noInit.}: EC
|
||||||
|
|
||||||
|
@ -153,16 +172,17 @@ proc run_EC_addition_tests*(
|
||||||
bool(r0 == r3)
|
bool(r0 == r3)
|
||||||
bool(r0 == r4)
|
bool(r0 == r4)
|
||||||
|
|
||||||
test(ec, randZ = false)
|
test(ec, randZ = false, gen = Uniform)
|
||||||
test(ec, randZ = true)
|
test(ec, randZ = true, gen = Uniform)
|
||||||
|
test(ec, randZ = false, gen = HighHammingWeight)
|
||||||
|
test(ec, randZ = true, gen = HighHammingWeight)
|
||||||
|
test(ec, randZ = false, gen = Long01Sequence)
|
||||||
|
test(ec, randZ = true, gen = Long01Sequence)
|
||||||
|
|
||||||
test "EC " & G1_or_G2 & " double and EC " & G1_or_G2 & " add are consistent":
|
test "EC " & G1_or_G2 & " double and EC " & G1_or_G2 & " add are consistent":
|
||||||
proc test(EC: typedesc, randZ: static bool) =
|
proc test(EC: typedesc, randZ: static bool, gen: static RandomGen) =
|
||||||
for _ in 0 ..< Iters:
|
for _ in 0 ..< Iters:
|
||||||
when randZ:
|
let a = rng.random_point(EC, randZ, gen)
|
||||||
let a = rng.random_unsafe_with_randZ(EC)
|
|
||||||
else:
|
|
||||||
let a = rng.random_unsafe(EC)
|
|
||||||
|
|
||||||
var r0{.noInit.}, r1{.noInit.}: EC
|
var r0{.noInit.}, r1{.noInit.}: EC
|
||||||
|
|
||||||
|
@ -171,8 +191,12 @@ proc run_EC_addition_tests*(
|
||||||
|
|
||||||
check: bool(r0 == r1)
|
check: bool(r0 == r1)
|
||||||
|
|
||||||
test(ec, randZ = false)
|
test(ec, randZ = false, gen = Uniform)
|
||||||
test(ec, randZ = true)
|
test(ec, randZ = true, gen = Uniform)
|
||||||
|
test(ec, randZ = false, gen = HighHammingWeight)
|
||||||
|
test(ec, randZ = true, gen = HighHammingWeight)
|
||||||
|
test(ec, randZ = false, gen = Long01Sequence)
|
||||||
|
test(ec, randZ = true, gen = Long01Sequence)
|
||||||
|
|
||||||
proc run_EC_mul_sanity_tests*(
|
proc run_EC_mul_sanity_tests*(
|
||||||
ec: typedesc,
|
ec: typedesc,
|
||||||
|
@ -196,12 +220,9 @@ proc run_EC_mul_sanity_tests*(
|
||||||
|
|
||||||
suite testSuiteDesc & " - " & $ec & " - [" & $WordBitwidth & "-bit mode]":
|
suite testSuiteDesc & " - " & $ec & " - [" & $WordBitwidth & "-bit mode]":
|
||||||
test "EC " & G1_or_G2 & " mul [0]P == Inf":
|
test "EC " & G1_or_G2 & " mul [0]P == Inf":
|
||||||
proc test(EC: typedesc, bits: static int, randZ: static bool) =
|
proc test(EC: typedesc, bits: static int, randZ: static bool, gen: static RandomGen) =
|
||||||
for _ in 0 ..< ItersMul:
|
for _ in 0 ..< ItersMul:
|
||||||
when randZ:
|
let a = rng.random_point(EC, randZ, gen)
|
||||||
let a = rng.random_unsafe_with_randZ(EC)
|
|
||||||
else:
|
|
||||||
let a = rng.random_unsafe(EC)
|
|
||||||
|
|
||||||
# zeroInit
|
# zeroInit
|
||||||
var exponentCanonical: array[(bits+7) div 8, byte]
|
var exponentCanonical: array[(bits+7) div 8, byte]
|
||||||
|
@ -218,16 +239,17 @@ proc run_EC_mul_sanity_tests*(
|
||||||
bool(impl.isInf())
|
bool(impl.isInf())
|
||||||
bool(reference.isInf())
|
bool(reference.isInf())
|
||||||
|
|
||||||
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = false)
|
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = false, gen = Uniform)
|
||||||
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = true)
|
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = true, gen = Uniform)
|
||||||
|
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = false, gen = HighHammingWeight)
|
||||||
|
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = true, gen = HighHammingWeight)
|
||||||
|
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = false, gen = Long01Sequence)
|
||||||
|
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = true, gen = Long01Sequence)
|
||||||
|
|
||||||
test "EC " & G1_or_G2 & " mul [1]P == P":
|
test "EC " & G1_or_G2 & " mul [1]P == P":
|
||||||
proc test(EC: typedesc, bits: static int, randZ: static bool) =
|
proc test(EC: typedesc, bits: static int, randZ: static bool, gen: static RandomGen) =
|
||||||
for _ in 0 ..< ItersMul:
|
for _ in 0 ..< ItersMul:
|
||||||
when randZ:
|
let a = rng.random_point(EC, randZ, gen)
|
||||||
let a = rng.random_unsafe_with_randZ(EC)
|
|
||||||
else:
|
|
||||||
let a = rng.random_unsafe(EC)
|
|
||||||
|
|
||||||
var exponent{.noInit.}: BigInt[bits]
|
var exponent{.noInit.}: BigInt[bits]
|
||||||
exponent.setOne()
|
exponent.setOne()
|
||||||
|
@ -246,16 +268,17 @@ proc run_EC_mul_sanity_tests*(
|
||||||
bool(impl == a)
|
bool(impl == a)
|
||||||
bool(reference == a)
|
bool(reference == a)
|
||||||
|
|
||||||
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = false)
|
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = false, gen = Uniform)
|
||||||
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = true)
|
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = true, gen = Uniform)
|
||||||
|
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = false, gen = HighHammingWeight)
|
||||||
|
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = true, gen = HighHammingWeight)
|
||||||
|
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = false, gen = Long01Sequence)
|
||||||
|
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = true, gen = Long01Sequence)
|
||||||
|
|
||||||
test "EC " & G1_or_G2 & " mul [2]P == P.double()":
|
test "EC " & G1_or_G2 & " mul [2]P == P.double()":
|
||||||
proc test(EC: typedesc, bits: static int, randZ: static bool) =
|
proc test(EC: typedesc, bits: static int, randZ: static bool, gen: static RandomGen) =
|
||||||
for _ in 0 ..< ItersMul:
|
for _ in 0 ..< ItersMul:
|
||||||
when randZ:
|
let a = rng.random_point(EC, randZ, gen)
|
||||||
let a = rng.random_unsafe_with_randZ(EC)
|
|
||||||
else:
|
|
||||||
let a = rng.random_unsafe(EC)
|
|
||||||
|
|
||||||
var doubleA{.noInit.}: EC
|
var doubleA{.noInit.}: EC
|
||||||
doubleA.double(a)
|
doubleA.double(a)
|
||||||
|
@ -276,8 +299,12 @@ proc run_EC_mul_sanity_tests*(
|
||||||
bool(impl == doubleA)
|
bool(impl == doubleA)
|
||||||
bool(reference == doubleA)
|
bool(reference == doubleA)
|
||||||
|
|
||||||
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = false)
|
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = false, gen = Uniform)
|
||||||
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = true)
|
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = true, gen = Uniform)
|
||||||
|
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = false, gen = HighHammingWeight)
|
||||||
|
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = true, gen = HighHammingWeight)
|
||||||
|
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = false, gen = Long01Sequence)
|
||||||
|
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = true, gen = Long01Sequence)
|
||||||
|
|
||||||
proc run_EC_mul_distributive_tests*(
|
proc run_EC_mul_distributive_tests*(
|
||||||
ec: typedesc,
|
ec: typedesc,
|
||||||
|
@ -302,14 +329,10 @@ proc run_EC_mul_distributive_tests*(
|
||||||
suite testSuiteDesc & " - " & $ec & " - [" & $WordBitwidth & "-bit mode]":
|
suite testSuiteDesc & " - " & $ec & " - [" & $WordBitwidth & "-bit mode]":
|
||||||
|
|
||||||
test "EC " & G1_or_G2 & " mul is distributive over EC add":
|
test "EC " & G1_or_G2 & " mul is distributive over EC add":
|
||||||
proc test(EC: typedesc, bits: static int, randZ: static bool) =
|
proc test(EC: typedesc, bits: static int, randZ: static bool, gen: static RandomGen) =
|
||||||
for _ in 0 ..< ItersMul:
|
for _ in 0 ..< ItersMul:
|
||||||
when randZ:
|
let a = rng.random_point(EC, randZ, gen)
|
||||||
let a = rng.random_unsafe_with_randZ(EC)
|
let b = rng.random_point(EC, randZ, gen)
|
||||||
let b = rng.random_unsafe_with_randZ(EC)
|
|
||||||
else:
|
|
||||||
let a = rng.random_unsafe(EC)
|
|
||||||
let b = rng.random_unsafe_with_randZ(EC)
|
|
||||||
|
|
||||||
let exponent = rng.random_unsafe(BigInt[bits])
|
let exponent = rng.random_unsafe(BigInt[bits])
|
||||||
var exponentCanonical{.noInit.}: array[(bits+7) div 8, byte]
|
var exponentCanonical{.noInit.}: array[(bits+7) div 8, byte]
|
||||||
|
@ -349,8 +372,12 @@ proc run_EC_mul_distributive_tests*(
|
||||||
bool(fReference == kakbRef)
|
bool(fReference == kakbRef)
|
||||||
bool(fImpl == fReference)
|
bool(fImpl == fReference)
|
||||||
|
|
||||||
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = false)
|
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = false, gen = Uniform)
|
||||||
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = true)
|
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = true, gen = Uniform)
|
||||||
|
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = false, gen = HighHammingWeight)
|
||||||
|
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = true, gen = HighHammingWeight)
|
||||||
|
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = false, gen = Long01Sequence)
|
||||||
|
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = true, gen = Long01Sequence)
|
||||||
|
|
||||||
proc run_EC_mul_vs_ref_impl*(
|
proc run_EC_mul_vs_ref_impl*(
|
||||||
ec: typedesc,
|
ec: typedesc,
|
||||||
|
@ -374,12 +401,9 @@ proc run_EC_mul_vs_ref_impl*(
|
||||||
|
|
||||||
suite testSuiteDesc & " - " & $ec & " - [" & $WordBitwidth & "-bit mode]":
|
suite testSuiteDesc & " - " & $ec & " - [" & $WordBitwidth & "-bit mode]":
|
||||||
test "EC " & G1_or_G2 & " mul constant-time is equivalent to a simple double-and-add algorithm":
|
test "EC " & G1_or_G2 & " mul constant-time is equivalent to a simple double-and-add algorithm":
|
||||||
proc test(EC: typedesc, bits: static int, randZ: static bool) =
|
proc test(EC: typedesc, bits: static int, randZ: static bool, gen: static RandomGen) =
|
||||||
for _ in 0 ..< ItersMul:
|
for _ in 0 ..< ItersMul:
|
||||||
when randZ:
|
let a = rng.random_point(EC, randZ, gen)
|
||||||
let a = rng.random_unsafe_with_randZ(EC)
|
|
||||||
else:
|
|
||||||
let a = rng.random_unsafe(EC)
|
|
||||||
|
|
||||||
let exponent = rng.random_unsafe(BigInt[bits])
|
let exponent = rng.random_unsafe(BigInt[bits])
|
||||||
var exponentCanonical{.noInit.}: array[(bits+7) div 8, byte]
|
var exponentCanonical{.noInit.}: array[(bits+7) div 8, byte]
|
||||||
|
@ -395,5 +419,9 @@ proc run_EC_mul_vs_ref_impl*(
|
||||||
|
|
||||||
check: bool(impl == reference)
|
check: bool(impl == reference)
|
||||||
|
|
||||||
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = false)
|
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = false, gen = Uniform)
|
||||||
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = true)
|
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = true, gen = Uniform)
|
||||||
|
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = false, gen = HighHammingWeight)
|
||||||
|
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = true, gen = HighHammingWeight)
|
||||||
|
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = false, gen = Long01Sequence)
|
||||||
|
test(ec, bits = ec.F.C.getCurveOrderBitwidth(), randZ = true, gen = Long01Sequence)
|
||||||
|
|
|
@ -115,15 +115,47 @@ proc randomCurve(C: static Curve) =
|
||||||
|
|
||||||
doAssert bool(r_mul == r_sqr)
|
doAssert bool(r_mul == r_sqr)
|
||||||
|
|
||||||
|
proc randomHighHammingWeight(C: static Curve) =
|
||||||
|
let a = rng.random_highHammingWeight(Fp[C])
|
||||||
|
|
||||||
|
var r_mul, r_sqr: Fp[C]
|
||||||
|
|
||||||
|
r_mul.prod(a, a)
|
||||||
|
r_sqr.square(a)
|
||||||
|
|
||||||
|
doAssert bool(r_mul == r_sqr)
|
||||||
|
|
||||||
|
proc random_long01Seq(C: static Curve) =
|
||||||
|
let a = rng.random_long01Seq(Fp[C])
|
||||||
|
|
||||||
|
var r_mul, r_sqr: Fp[C]
|
||||||
|
|
||||||
|
r_mul.prod(a, a)
|
||||||
|
r_sqr.square(a)
|
||||||
|
|
||||||
|
doAssert bool(r_mul == r_sqr)
|
||||||
|
|
||||||
suite "Random Modular Squaring is consistent with Modular Multiplication" & " [" & $WordBitwidth & "-bit mode]":
|
suite "Random Modular Squaring is consistent with Modular Multiplication" & " [" & $WordBitwidth & "-bit mode]":
|
||||||
test "Random squaring mod P-224 [FastSquaring = " & $P224.canUseNoCarryMontySquare & "]":
|
test "Random squaring mod P-224 [FastSquaring = " & $P224.canUseNoCarryMontySquare & "]":
|
||||||
for _ in 0 ..< Iters:
|
for _ in 0 ..< Iters:
|
||||||
randomCurve(P224)
|
randomCurve(P224)
|
||||||
|
for _ in 0 ..< Iters:
|
||||||
|
randomHighHammingWeight(P224)
|
||||||
|
for _ in 0 ..< Iters:
|
||||||
|
random_long01Seq(P224)
|
||||||
|
|
||||||
test "Random squaring mod P-256 [FastSquaring = " & $P256.canUseNoCarryMontySquare & "]":
|
test "Random squaring mod P-256 [FastSquaring = " & $P256.canUseNoCarryMontySquare & "]":
|
||||||
for _ in 0 ..< Iters:
|
for _ in 0 ..< Iters:
|
||||||
randomCurve(P256)
|
randomCurve(P256)
|
||||||
|
for _ in 0 ..< Iters:
|
||||||
|
randomHighHammingWeight(P256)
|
||||||
|
for _ in 0 ..< Iters:
|
||||||
|
random_long01Seq(P256)
|
||||||
|
|
||||||
test "Random squaring mod BLS12_381 [FastSquaring = " & $BLS12_381.canUseNoCarryMontySquare & "]":
|
test "Random squaring mod BLS12_381 [FastSquaring = " & $BLS12_381.canUseNoCarryMontySquare & "]":
|
||||||
for _ in 0 ..< Iters:
|
for _ in 0 ..< Iters:
|
||||||
randomCurve(BLS12_381)
|
randomCurve(BLS12_381)
|
||||||
|
for _ in 0 ..< Iters:
|
||||||
|
randomHighHammingWeight(BLS12_381)
|
||||||
|
for _ in 0 ..< Iters:
|
||||||
|
random_long01Seq(BLS12_381)
|
||||||
|
|
|
@ -170,6 +170,26 @@ proc main() =
|
||||||
a2.double()
|
a2.double()
|
||||||
check: bool(a == a2)
|
check: bool(a == a2)
|
||||||
|
|
||||||
|
for _ in 0 ..< Iters:
|
||||||
|
let a = rng.randomHighHammingWeight(Fp[curve])
|
||||||
|
var a2 = a
|
||||||
|
a2.double()
|
||||||
|
a2.div2()
|
||||||
|
check: bool(a == a2)
|
||||||
|
a2.div2()
|
||||||
|
a2.double()
|
||||||
|
check: bool(a == a2)
|
||||||
|
|
||||||
|
for _ in 0 ..< Iters:
|
||||||
|
let a = rng.random_long01Seq(Fp[curve])
|
||||||
|
var a2 = a
|
||||||
|
a2.double()
|
||||||
|
a2.div2()
|
||||||
|
check: bool(a == a2)
|
||||||
|
a2.div2()
|
||||||
|
a2.double()
|
||||||
|
check: bool(a == a2)
|
||||||
|
|
||||||
testRandomDiv2 P224
|
testRandomDiv2 P224
|
||||||
testRandomDiv2 BN254_Nogami
|
testRandomDiv2 BN254_Nogami
|
||||||
testRandomDiv2 BN254_Snarks
|
testRandomDiv2 BN254_Snarks
|
||||||
|
@ -245,6 +265,22 @@ proc main() =
|
||||||
r.prod(aInv, a)
|
r.prod(aInv, a)
|
||||||
check: bool r.isOne()
|
check: bool r.isOne()
|
||||||
|
|
||||||
|
for _ in 0 ..< Iters:
|
||||||
|
let a = rng.randomHighHammingWeight(Fp[curve])
|
||||||
|
aInv.inv(a)
|
||||||
|
r.prod(a, aInv)
|
||||||
|
check: bool r.isOne()
|
||||||
|
r.prod(aInv, a)
|
||||||
|
check: bool r.isOne()
|
||||||
|
|
||||||
|
for _ in 0 ..< Iters:
|
||||||
|
let a = rng.random_long01Seq(Fp[curve])
|
||||||
|
aInv.inv(a)
|
||||||
|
r.prod(a, aInv)
|
||||||
|
check: bool r.isOne()
|
||||||
|
r.prod(aInv, a)
|
||||||
|
check: bool r.isOne()
|
||||||
|
|
||||||
testRandomInv P224
|
testRandomInv P224
|
||||||
testRandomInv BN254_Nogami
|
testRandomInv BN254_Nogami
|
||||||
testRandomInv BN254_Snarks
|
testRandomInv BN254_Snarks
|
||||||
|
|
|
@ -83,27 +83,38 @@ proc exhaustiveCheck_p3mod4(C: static Curve, modulus: static int) =
|
||||||
bool (a == a2) # a shouldn't be modified
|
bool (a == a2) # a shouldn't be modified
|
||||||
|
|
||||||
proc randomSqrtCheck_p3mod4(C: static Curve) =
|
proc randomSqrtCheck_p3mod4(C: static Curve) =
|
||||||
|
template testImpl(a: untyped): untyped {.dirty.} =
|
||||||
|
var na{.noInit.}: Fp[C]
|
||||||
|
na.neg(a)
|
||||||
|
|
||||||
|
var a2 = a
|
||||||
|
var na2 = na
|
||||||
|
a2.square()
|
||||||
|
na2.square()
|
||||||
|
check:
|
||||||
|
bool a2 == na2
|
||||||
|
bool a2.isSquare()
|
||||||
|
|
||||||
|
var r, s = a2
|
||||||
|
r.sqrt()
|
||||||
|
let ok = s.sqrt_if_square()
|
||||||
|
check:
|
||||||
|
bool ok
|
||||||
|
bool(r == s)
|
||||||
|
bool(r == a or r == na)
|
||||||
|
|
||||||
test "Random square root check for p ≡ 3 (mod 4) on " & $Curve(C):
|
test "Random square root check for p ≡ 3 (mod 4) on " & $Curve(C):
|
||||||
for _ in 0 ..< Iters:
|
for _ in 0 ..< Iters:
|
||||||
let a = rng.random_unsafe(Fp[C])
|
let a = rng.random_unsafe(Fp[C])
|
||||||
var na{.noInit.}: Fp[C]
|
testImpl(a)
|
||||||
na.neg(a)
|
|
||||||
|
|
||||||
var a2 = a
|
for _ in 0 ..< Iters:
|
||||||
var na2 = na
|
let a = rng.randomHighHammingWeight(Fp[C])
|
||||||
a2.square()
|
testImpl(a)
|
||||||
na2.square()
|
|
||||||
check:
|
|
||||||
bool a2 == na2
|
|
||||||
bool a2.isSquare()
|
|
||||||
|
|
||||||
var r, s = a2
|
for _ in 0 ..< Iters:
|
||||||
r.sqrt()
|
let a = rng.random_long01Seq(Fp[C])
|
||||||
let ok = s.sqrt_if_square()
|
testImpl(a)
|
||||||
check:
|
|
||||||
bool ok
|
|
||||||
bool(r == s)
|
|
||||||
bool(r == a or r == na)
|
|
||||||
|
|
||||||
proc main() =
|
proc main() =
|
||||||
suite "Modular square root" & " [" & $WordBitwidth & "-bit mode]":
|
suite "Modular square root" & " [" & $WordBitwidth & "-bit mode]":
|
||||||
|
|
|
@ -35,6 +35,20 @@ template ExtField(degree: static int, curve: static Curve): untyped =
|
||||||
else:
|
else:
|
||||||
{.error: "Unconfigured extension degree".}
|
{.error: "Unconfigured extension degree".}
|
||||||
|
|
||||||
|
type
|
||||||
|
RandomGen = enum
|
||||||
|
Uniform
|
||||||
|
HighHammingWeight
|
||||||
|
Long01Sequence
|
||||||
|
|
||||||
|
func random_elem(rng: var RngState, F: typedesc, gen: static RandomGen): F {.inline, noInit.} =
|
||||||
|
when gen == Uniform:
|
||||||
|
result = rng.random_unsafe(F)
|
||||||
|
elif gen == HighHammingWeight:
|
||||||
|
result = rng.random_highHammingWeight(F)
|
||||||
|
else:
|
||||||
|
result = rng.random_long01Seq(F)
|
||||||
|
|
||||||
proc runTowerTests*[N](
|
proc runTowerTests*[N](
|
||||||
ExtDegree: static int,
|
ExtDegree: static int,
|
||||||
Iters: static int,
|
Iters: static int,
|
||||||
|
@ -63,19 +77,19 @@ proc runTowerTests*[N](
|
||||||
test(ExtField(ExtDegree, curve))
|
test(ExtField(ExtDegree, curve))
|
||||||
|
|
||||||
test "Addition, substraction negation are consistent":
|
test "Addition, substraction negation are consistent":
|
||||||
proc test(Field: typedesc, Iters: static int) =
|
proc test(Field: typedesc, Iters: static int, gen: static RandomGen) =
|
||||||
# Try to exercise all code paths for in-place/out-of-place add/sum/sub/diff/double/neg
|
# Try to exercise all code paths for in-place/out-of-place add/sum/sub/diff/double/neg
|
||||||
# (1 - (-a) - b + (-a) - 2a) + (2a + 2b + (-b)) == 1
|
# (1 - (-a) - b + (-a) - 2a) + (2a + 2b + (-b)) == 1
|
||||||
var accum {.noInit.}, One {.noInit.}, a{.noInit.}, na{.noInit.}, b{.noInit.}, nb{.noInit.}, a2 {.noInit.}, b2 {.noInit.}: Field
|
var accum {.noInit.}, One {.noInit.}, a{.noInit.}, na{.noInit.}, b{.noInit.}, nb{.noInit.}, a2 {.noInit.}, b2 {.noInit.}: Field
|
||||||
|
|
||||||
for _ in 0 ..< Iters:
|
for _ in 0 ..< Iters:
|
||||||
One.setOne()
|
One.setOne()
|
||||||
a = rng.random_unsafe(Field)
|
a = rng.random_elem(Field, gen)
|
||||||
a2 = a
|
a2 = a
|
||||||
a2.double()
|
a2.double()
|
||||||
na.neg(a)
|
na.neg(a)
|
||||||
|
|
||||||
b = rng.random_unsafe(Field)
|
b = rng.random_elem(Field, gen)
|
||||||
b2.double(b)
|
b2.double(b)
|
||||||
nb.neg(b)
|
nb.neg(b)
|
||||||
|
|
||||||
|
@ -92,12 +106,14 @@ proc runTowerTests*[N](
|
||||||
check: bool accum.isOne()
|
check: bool accum.isOne()
|
||||||
|
|
||||||
staticFor(curve, TestCurves):
|
staticFor(curve, TestCurves):
|
||||||
test(ExtField(ExtDegree, curve), Iters)
|
test(ExtField(ExtDegree, curve), Iters, gen = Uniform)
|
||||||
|
test(ExtField(ExtDegree, curve), Iters, gen = HighHammingWeight)
|
||||||
|
test(ExtField(ExtDegree, curve), Iters, gen = Long01Sequence)
|
||||||
|
|
||||||
test "Division by 2":
|
test "Division by 2":
|
||||||
proc test(Field: typedesc, Iters: static int) =
|
proc test(Field: typedesc, Iters: static int, gen: static RandomGen) =
|
||||||
for _ in 0 ..< Iters:
|
for _ in 0 ..< Iters:
|
||||||
let a = rng.random_unsafe(Field)
|
let a = rng.random_elem(Field, gen)
|
||||||
var a2 = a
|
var a2 = a
|
||||||
a2.double()
|
a2.double()
|
||||||
a2.div2()
|
a2.div2()
|
||||||
|
@ -107,7 +123,9 @@ proc runTowerTests*[N](
|
||||||
check: bool(a == a2)
|
check: bool(a == a2)
|
||||||
|
|
||||||
staticFor(curve, TestCurves):
|
staticFor(curve, TestCurves):
|
||||||
test(ExtField(ExtDegree, curve), Iters)
|
test(ExtField(ExtDegree, curve), Iters, gen = Uniform)
|
||||||
|
test(ExtField(ExtDegree, curve), Iters, gen = HighHammingWeight)
|
||||||
|
test(ExtField(ExtDegree, curve), Iters, gen = Long01Sequence)
|
||||||
|
|
||||||
test "Squaring 1 returns 1":
|
test "Squaring 1 returns 1":
|
||||||
proc test(Field: typedesc) =
|
proc test(Field: typedesc) =
|
||||||
|
@ -247,9 +265,9 @@ proc runTowerTests*[N](
|
||||||
check: bool(r == x)
|
check: bool(r == x)
|
||||||
|
|
||||||
test "Multiplication and Squaring are consistent":
|
test "Multiplication and Squaring are consistent":
|
||||||
proc test(Field: typedesc, Iters: static int) =
|
proc test(Field: typedesc, Iters: static int, gen: static RandomGen) =
|
||||||
for _ in 0 ..< Iters:
|
for _ in 0 ..< Iters:
|
||||||
let a = rng.random_unsafe(Field)
|
let a = rng.random_elem(Field, gen)
|
||||||
var rMul{.noInit.}, rSqr{.noInit.}: Field
|
var rMul{.noInit.}, rSqr{.noInit.}: Field
|
||||||
|
|
||||||
rMul.prod(a, a)
|
rMul.prod(a, a)
|
||||||
|
@ -258,12 +276,14 @@ proc runTowerTests*[N](
|
||||||
check: bool(rMul == rSqr)
|
check: bool(rMul == rSqr)
|
||||||
|
|
||||||
staticFor(curve, TestCurves):
|
staticFor(curve, TestCurves):
|
||||||
test(ExtField(ExtDegree, curve), Iters)
|
test(ExtField(ExtDegree, curve), Iters, gen = Uniform)
|
||||||
|
test(ExtField(ExtDegree, curve), Iters, gen = HighHammingWeight)
|
||||||
|
test(ExtField(ExtDegree, curve), Iters, gen = Long01Sequence)
|
||||||
|
|
||||||
test "Squaring the opposite gives the same result":
|
test "Squaring the opposite gives the same result":
|
||||||
proc test(Field: typedesc, Iters: static int) =
|
proc test(Field: typedesc, Iters: static int, gen: static RandomGen) =
|
||||||
for _ in 0 ..< Iters:
|
for _ in 0 ..< Iters:
|
||||||
let a = rng.random_unsafe(Field)
|
let a = rng.random_elem(Field, gen)
|
||||||
var na{.noInit.}: Field
|
var na{.noInit.}: Field
|
||||||
na.neg(a)
|
na.neg(a)
|
||||||
|
|
||||||
|
@ -275,14 +295,16 @@ proc runTowerTests*[N](
|
||||||
check: bool(rSqr == rNegSqr)
|
check: bool(rSqr == rNegSqr)
|
||||||
|
|
||||||
staticFor(curve, TestCurves):
|
staticFor(curve, TestCurves):
|
||||||
test(ExtField(ExtDegree, curve), Iters)
|
test(ExtField(ExtDegree, curve), Iters, gen = Uniform)
|
||||||
|
test(ExtField(ExtDegree, curve), Iters, gen = HighHammingWeight)
|
||||||
|
test(ExtField(ExtDegree, curve), Iters, gen = Long01Sequence)
|
||||||
|
|
||||||
test "Multiplication and Addition/Substraction are consistent":
|
test "Multiplication and Addition/Substraction are consistent":
|
||||||
proc test(Field: typedesc, Iters: static int) =
|
proc test(Field: typedesc, Iters: static int, gen: static RandomGen) =
|
||||||
for _ in 0 ..< Iters:
|
for _ in 0 ..< Iters:
|
||||||
let factor = rng.random_unsafe(-30..30)
|
let factor = rng.random_unsafe(-30..30)
|
||||||
|
|
||||||
let a = rng.random_unsafe(Field)
|
let a = rng.random_elem(Field, gen)
|
||||||
|
|
||||||
if factor == 0: continue
|
if factor == 0: continue
|
||||||
|
|
||||||
|
@ -309,14 +331,16 @@ proc runTowerTests*[N](
|
||||||
check: bool(r == sum)
|
check: bool(r == sum)
|
||||||
|
|
||||||
staticFor(curve, TestCurves):
|
staticFor(curve, TestCurves):
|
||||||
test(ExtField(ExtDegree, curve), Iters)
|
test(ExtField(ExtDegree, curve), Iters, gen = Uniform)
|
||||||
|
test(ExtField(ExtDegree, curve), Iters, gen = HighHammingWeight)
|
||||||
|
test(ExtField(ExtDegree, curve), Iters, gen = Long01Sequence)
|
||||||
|
|
||||||
test "Addition is associative and commutative":
|
test "Addition is associative and commutative":
|
||||||
proc test(Field: typedesc, Iters: static int) =
|
proc test(Field: typedesc, Iters: static int, gen: static RandomGen) =
|
||||||
for _ in 0 ..< Iters:
|
for _ in 0 ..< Iters:
|
||||||
let a = rng.random_unsafe(Field)
|
let a = rng.random_elem(Field, gen)
|
||||||
let b = rng.random_unsafe(Field)
|
let b = rng.random_elem(Field, gen)
|
||||||
let c = rng.random_unsafe(Field)
|
let c = rng.random_elem(Field, gen)
|
||||||
|
|
||||||
var tmp1{.noInit.}, tmp2{.noInit.}: Field
|
var tmp1{.noInit.}, tmp2{.noInit.}: Field
|
||||||
|
|
||||||
|
@ -354,14 +378,16 @@ proc runTowerTests*[N](
|
||||||
bool(r0 == r4)
|
bool(r0 == r4)
|
||||||
|
|
||||||
staticFor(curve, TestCurves):
|
staticFor(curve, TestCurves):
|
||||||
test(ExtField(ExtDegree, curve), Iters)
|
test(ExtField(ExtDegree, curve), Iters, gen = Uniform)
|
||||||
|
test(ExtField(ExtDegree, curve), Iters, gen = HighHammingWeight)
|
||||||
|
test(ExtField(ExtDegree, curve), Iters, gen = Long01Sequence)
|
||||||
|
|
||||||
test "Multiplication is associative and commutative":
|
test "Multiplication is associative and commutative":
|
||||||
proc test(Field: typedesc, Iters: static int) =
|
proc test(Field: typedesc, Iters: static int, gen: static RandomGen) =
|
||||||
for _ in 0 ..< Iters:
|
for _ in 0 ..< Iters:
|
||||||
let a = rng.random_unsafe(Field)
|
let a = rng.random_elem(Field, gen)
|
||||||
let b = rng.random_unsafe(Field)
|
let b = rng.random_elem(Field, gen)
|
||||||
let c = rng.random_unsafe(Field)
|
let c = rng.random_elem(Field, gen)
|
||||||
|
|
||||||
var tmp1{.noInit.}, tmp2{.noInit.}: Field
|
var tmp1{.noInit.}, tmp2{.noInit.}: Field
|
||||||
|
|
||||||
|
@ -399,14 +425,16 @@ proc runTowerTests*[N](
|
||||||
bool(r0 == r4)
|
bool(r0 == r4)
|
||||||
|
|
||||||
staticFor(curve, TestCurves):
|
staticFor(curve, TestCurves):
|
||||||
test(ExtField(ExtDegree, curve), Iters)
|
test(ExtField(ExtDegree, curve), Iters, gen = Uniform)
|
||||||
|
test(ExtField(ExtDegree, curve), Iters, gen = HighHammingWeight)
|
||||||
|
test(ExtField(ExtDegree, curve), Iters, gen = Long01Sequence)
|
||||||
|
|
||||||
test "Extension field multiplicative inverse":
|
test "Extension field multiplicative inverse":
|
||||||
proc test(Field: typedesc, Iters: static int) =
|
proc test(Field: typedesc, Iters: static int, gen: static RandomGen) =
|
||||||
var aInv, r{.noInit.}: Field
|
var aInv, r{.noInit.}: Field
|
||||||
|
|
||||||
for _ in 0 ..< Iters:
|
for _ in 0 ..< Iters:
|
||||||
let a = rng.random_unsafe(Field)
|
let a = rng.random_elem(Field, gen)
|
||||||
aInv.inv(a)
|
aInv.inv(a)
|
||||||
r.prod(a, aInv)
|
r.prod(a, aInv)
|
||||||
check: bool(r.isOne())
|
check: bool(r.isOne())
|
||||||
|
@ -414,7 +442,9 @@ proc runTowerTests*[N](
|
||||||
check: bool(r.isOne())
|
check: bool(r.isOne())
|
||||||
|
|
||||||
staticFor(curve, TestCurves):
|
staticFor(curve, TestCurves):
|
||||||
test(ExtField(ExtDegree, curve), Iters)
|
test(ExtField(ExtDegree, curve), Iters, gen = Uniform)
|
||||||
|
test(ExtField(ExtDegree, curve), Iters, gen = HighHammingWeight)
|
||||||
|
test(ExtField(ExtDegree, curve), Iters, gen = Long01Sequence)
|
||||||
|
|
||||||
test "0 does not have a multiplicative inverse and should return 0 for projective/jacobian => affine coordinates conversion":
|
test "0 does not have a multiplicative inverse and should return 0 for projective/jacobian => affine coordinates conversion":
|
||||||
proc test(Field: typedesc) =
|
proc test(Field: typedesc) =
|
||||||
|
|
Loading…
Reference in New Issue