mirror of
https://github.com/logos-storage/constantine.git
synced 2026-01-02 13:13:07 +00:00
Fuzz fix - non-unique modular representation after Assembly negate (#137)
* Fix #114 - Negating 0 left the prime modulus, which is working most of the time for everything except for comparison. (also somehow triggers and workaround weird compiler bug where exceptions tracking is activated in macros and all the curve enums were stringified as their ordinal value) * https://github.com/mratsim/constantine/issues/136 was also fixed, add to anti-regression * add comment in test * Fix the pure Nim fallback as well
This commit is contained in:
parent
5b1d280486
commit
7e97cd4ac5
@ -58,6 +58,7 @@ const testDesc: seq[tuple[path: string, useGMP: bool]] = @[
|
||||
("tests/t_fp12_bls12_377.nim", false),
|
||||
("tests/t_fp12_bls12_381.nim", false),
|
||||
("tests/t_fp12_exponentiation.nim", false),
|
||||
("tests/t_fp12_anti_regression.nim", false),
|
||||
|
||||
# ("tests/t_fp4_frobenius.nim", false),
|
||||
# ("tests/t_fp6_frobenius.nim", false),
|
||||
@ -285,6 +286,17 @@ task test, "Run all tests":
|
||||
if not defined(windows) or not (existsEnv"UCPU" or getEnv"UCPU" == "i686"):
|
||||
buildAllBenches()
|
||||
|
||||
task test_no_assembler, "Run all tests":
|
||||
# -d:testingCurves is configured in a *.nim.cfg for convenience
|
||||
runTests(requireGMP = true, testASM = false)
|
||||
|
||||
# if sizeof(int) == 8: # 32-bit tests on 64-bit arch
|
||||
# runTests(requireGMP = true, test32bit = true)
|
||||
|
||||
# Ensure benchmarks stay relevant. Ignore Windows 32-bit at the moment
|
||||
if not defined(windows) or not (existsEnv"UCPU" or getEnv"UCPU" == "i686"):
|
||||
buildAllBenches()
|
||||
|
||||
task test_no_gmp, "Run tests that don't require GMP":
|
||||
# -d:testingCurves is configured in a *.nim.cfg for convenience
|
||||
runTests(requireGMP = false)
|
||||
|
||||
@ -128,7 +128,7 @@ macro submod_gen[N: static int](a: var Limbs[N], b, M: Limbs[N]): untyped =
|
||||
# Interleaved copy the modulus to hide SBB latencies
|
||||
ctx.mov arrTadd[i], arrM[i]
|
||||
|
||||
# Mask: undeflowed contains 0xFFFF or 0x0000
|
||||
# Mask: underflowed contains 0xFFFF or 0x0000
|
||||
let underflowed = arrB.reuseRegister()
|
||||
ctx.sbb underflowed, underflowed
|
||||
|
||||
@ -166,21 +166,37 @@ macro negmod_gen[N: static int](r: var Limbs[N], a, M: Limbs[N]): untyped =
|
||||
var ctx = init(Assembler_x86, BaseType)
|
||||
let
|
||||
arrA = init(OperandArray, nimSymbol = a, N, PointerInReg, Input)
|
||||
arrR = init(OperandArray, nimSymbol = r, N, ElemsInReg, InputOutput)
|
||||
arrR = init(OperandArray, nimSymbol = r, N, PointerInReg, InputOutput)
|
||||
arrT = init(OperandArray, nimSymbol = ident"t", N, ElemsInReg, Output_EarlyClobber)
|
||||
# We could force M as immediate by specializing per moduli
|
||||
arrM = init(OperandArray, nimSymbol = M, N, PointerInReg, Input)
|
||||
# We reuse the reg used for M for overflow detection
|
||||
arrM = init(OperandArray, nimSymbol = M, N, PointerInReg, InputOutput)
|
||||
|
||||
# Addition
|
||||
# Substraction M - a
|
||||
for i in 0 ..< N:
|
||||
ctx.mov arrR[i], arrM[i]
|
||||
ctx.mov arrT[i], arrM[i]
|
||||
if i == 0:
|
||||
ctx.sub arrR[0], arrA[0]
|
||||
ctx.sub arrT[0], arrA[0]
|
||||
else:
|
||||
ctx.sbb arrR[i], arrA[i]
|
||||
ctx.sbb arrT[i], arrA[i]
|
||||
|
||||
# Deal with a == 0
|
||||
let isZero = arrM.reuseRegister()
|
||||
ctx.mov isZero, arrA[0]
|
||||
for i in 1 ..< N:
|
||||
ctx.`or` isZero, arrA[i]
|
||||
|
||||
# Zero result if a == 0
|
||||
for i in 0 ..< N:
|
||||
ctx.cmovz arrT[i], isZero
|
||||
ctx.mov arrR[i], arrT[i]
|
||||
|
||||
let t = arrT.nimSymbol
|
||||
result.add quote do:
|
||||
var `t`{.noinit.}: typeof(`a`)
|
||||
result.add ctx.generate
|
||||
|
||||
func negmod_asm*(r: var Limbs, a, M: Limbs) {.inline.} =
|
||||
func negmod_asm*(r: var Limbs, a, M: Limbs) =
|
||||
## Constant-time modular negation
|
||||
negmod_gen(r, a, M)
|
||||
|
||||
|
||||
@ -29,6 +29,9 @@ static: doAssert UseASM_X86_64
|
||||
# Necessary for the compiler to find enough registers (enabled at -O1)
|
||||
{.localPassC:"-fomit-frame-pointer".}
|
||||
|
||||
# No exceptions allowed
|
||||
{.push raises: [].}
|
||||
|
||||
# Montgomery reduction
|
||||
# ------------------------------------------------------------
|
||||
|
||||
|
||||
@ -74,6 +74,10 @@ func setOne*(a: var BigInt) =
|
||||
## Set a BigInt to 1
|
||||
a.limbs.setOne()
|
||||
|
||||
func czero*(a: var BigInt, ctl: SecretBool) =
|
||||
## Set ``a`` to 0 if ``ctl`` is true
|
||||
a.limbs.czero(ctl)
|
||||
|
||||
# Copy
|
||||
# ------------------------------------------------------------
|
||||
|
||||
|
||||
@ -229,7 +229,15 @@ func neg*(r: var FF, a: FF) {.inline.} =
|
||||
# especially on FF2
|
||||
negmod_asm(r.mres.limbs, a.mres.limbs, FF.fieldMod().limbs)
|
||||
else:
|
||||
discard r.mres.diff(FF.fieldMod(), a.mres)
|
||||
# If a = 0 we need r = 0 and not r = M
|
||||
# as comparison operator assume unicity
|
||||
# of the modular representation.
|
||||
# Also make sure to handle aliasing where r.addr = a.addr
|
||||
var t {.noInit.}: FF
|
||||
let isZero = a.isZero()
|
||||
discard t.mres.diff(FF.fieldMod(), a.mres)
|
||||
t.mres.czero(isZero)
|
||||
r = t
|
||||
|
||||
func neg*(a: var FF) {.inline.} =
|
||||
## Negate modulo p
|
||||
|
||||
@ -71,6 +71,13 @@ func setOne*(a: var Limbs) =
|
||||
when a.len > 1:
|
||||
zeroMem(a[1].addr, (a.len - 1) * sizeof(SecretWord))
|
||||
|
||||
func czero*(a: var Limbs, ctl: SecretBool) =
|
||||
## Set ``a`` to 0 if ``ctl`` is true
|
||||
# Only used for FF neg in pure Nim fallback
|
||||
# so no need for assembly
|
||||
for i in 0 ..< a.len:
|
||||
ctl.ccopy(a[i], SecretWord 0)
|
||||
|
||||
# Copy
|
||||
# ------------------------------------------------------------
|
||||
|
||||
|
||||
@ -92,7 +92,7 @@ func fromHex*(dst: var Fp12,
|
||||
c4, c5, c6, c7: string,
|
||||
c8, c9, c10, c11: string) {.raises: [ValueError].}=
|
||||
## Convert 12 coordinates to an element of 𝔽p12
|
||||
when Fp12.c0 is Fp6:
|
||||
when dst.c0 is Fp6:
|
||||
dst.c0.fromHex(c0, c1, c2, c3, c4, c5)
|
||||
dst.c1.fromHex(c6, c7, c8, c9, c10, c11)
|
||||
else:
|
||||
|
||||
@ -10,6 +10,9 @@ import std/[macros, strutils, sets, hashes, algorithm]
|
||||
|
||||
# A compile-time inline assembler
|
||||
|
||||
# No exceptions allowed
|
||||
{.push raises: [].}
|
||||
|
||||
type
|
||||
RM* = enum
|
||||
## Register or Memory operand
|
||||
@ -43,7 +46,6 @@ type
|
||||
## GCC extended assembly modifier
|
||||
Input = ""
|
||||
Input_Commutative = "%"
|
||||
Input_EarlyClobber = "&"
|
||||
Output_Overwrite = "="
|
||||
Output_EarlyClobber = "=&"
|
||||
InputOutput = "+"
|
||||
@ -94,7 +96,10 @@ const OutputReg = {Output_EarlyClobber, InputOutput, InputOutput_EnsureClobber,
|
||||
|
||||
func hash(od: OperandDesc): Hash =
|
||||
{.noSideEffect.}:
|
||||
hash($od.nimSymbol)
|
||||
try: # Why does this raise a generic exception?
|
||||
hash($od.nimSymbol)
|
||||
except:
|
||||
raise newException(Defect, "Broke Nim")
|
||||
|
||||
# TODO: remove the need of OperandArray
|
||||
|
||||
@ -138,7 +143,10 @@ func init*(T: type OperandArray, nimSymbol: NimNode, len: int, rm: RM, constrain
|
||||
let nimSymbol = if isHiddenDeref: nimSymbol[0]
|
||||
else: nimSymbol
|
||||
{.noSideEffect.}:
|
||||
let symStr = $nimSymbol
|
||||
let symStr = try: # Why does this raise a generic exception?
|
||||
$nimSymbol
|
||||
except:
|
||||
raise newException(Defect, "Broke Nim!")
|
||||
|
||||
result.nimSymbol = nimSymbol
|
||||
|
||||
@ -197,7 +205,10 @@ func setToCarryFlag*(a: var Assembler_x86, carry: NimNode) =
|
||||
let nimSymbol = if isHiddenDeref: carry[0]
|
||||
else: carry
|
||||
{.noSideEffect.}:
|
||||
let symStr = $nimSymbol
|
||||
let symStr = try: # Why does this raise a generic exception?
|
||||
$nimSymbol
|
||||
except:
|
||||
raise newException(Defect, "Broke Nim!")
|
||||
|
||||
let desc = OperandDesc(
|
||||
asmId: "",
|
||||
@ -403,13 +414,29 @@ func codeFragment(a: var Assembler_x86, instr: string, reg0: OperandReuse, reg1:
|
||||
# ⚠️ Warning:
|
||||
# The caller should deal with destination/source operand
|
||||
# so that it fits GNU Assembly
|
||||
let off1 = a.getStrOffset(reg1)
|
||||
|
||||
if a.wordBitWidth == 64:
|
||||
a.code &= instr & "q %" & $reg0.asmId & ", %" & $reg1.desc.asmId & '\n'
|
||||
a.code &= instr & "q %" & $reg0.asmId & ", " & off1 & '\n'
|
||||
else:
|
||||
a.code &= instr & "l %" & $reg0.asmId & ", %" & $reg1.desc.asmId & '\n'
|
||||
a.code &= instr & "l %" & $reg0.asmId & ", " & off1 & '\n'
|
||||
|
||||
a.operands.incl reg1.desc
|
||||
|
||||
func codeFragment(a: var Assembler_x86, instr: string, reg0: Operand, reg1: OperandReuse) =
|
||||
# Generate a code fragment
|
||||
# ⚠️ Warning:
|
||||
# The caller should deal with destination/source operand
|
||||
# so that it fits GNU Assembly
|
||||
let off0 = a.getStrOffset(reg0)
|
||||
|
||||
if a.wordBitWidth == 64:
|
||||
a.code &= instr & "q " & off0 & ", %" & $reg1.asmId & '\n'
|
||||
else:
|
||||
a.code &= instr & "l " & off0 & ", %" & $reg1.asmId & '\n'
|
||||
|
||||
a.operands.incl reg0.desc
|
||||
|
||||
func reuseRegister*(reg: OperandArray): OperandReuse =
|
||||
# TODO: disable the reg input
|
||||
doAssert reg.buf[0].desc.constraint == InputOutput
|
||||
@ -526,10 +553,28 @@ func test*(a: var Assembler_x86, x, y: Operand) =
|
||||
a.codeFragment("test", x, y)
|
||||
a.areFlagsClobbered = true
|
||||
|
||||
func `xor`*(a: var Assembler_x86, x, y: Operand) =
|
||||
func test*(a: var Assembler_x86, x, y: OperandReuse) =
|
||||
## Compute the bitwise AND of x and y and
|
||||
## set the Sign, Zero and Parity flags
|
||||
a.codeFragment("test", x, y)
|
||||
a.areFlagsClobbered = true
|
||||
|
||||
func `or`*(a: var Assembler_x86, dst, src: Operand) =
|
||||
## Compute the bitwise or of x and y and
|
||||
## reset all flags
|
||||
a.codeFragment("or", src, dst)
|
||||
a.areFlagsClobbered = true
|
||||
|
||||
func `or`*(a: var Assembler_x86, dst: OperandReuse, src: Operand) =
|
||||
## Compute the bitwise or of x and y and
|
||||
## reset all flags
|
||||
a.codeFragment("or", src, dst)
|
||||
a.areFlagsClobbered = true
|
||||
|
||||
func `xor`*(a: var Assembler_x86, dst, src: Operand) =
|
||||
## Compute the bitwise xor of x and y and
|
||||
## reset all flags
|
||||
a.codeFragment("xor", x, y)
|
||||
a.codeFragment("xor", src, dst)
|
||||
a.areFlagsClobbered = true
|
||||
|
||||
func mov*(a: var Assembler_x86, dst, src: Operand) =
|
||||
@ -539,6 +584,20 @@ func mov*(a: var Assembler_x86, dst, src: Operand) =
|
||||
a.codeFragment("mov", src, dst)
|
||||
# No clobber
|
||||
|
||||
func mov*(a: var Assembler_x86, dst: Operand, src: OperandReuse) =
|
||||
## Does: dst <- src
|
||||
doAssert dst.desc.constraint in OutputReg, $dst.repr
|
||||
|
||||
a.codeFragment("mov", src, dst)
|
||||
# No clobber
|
||||
|
||||
func mov*(a: var Assembler_x86, dst: OperandReuse, src: Operand) =
|
||||
## Does: dst <- src
|
||||
# doAssert dst.desc.constraint in OutputReg, $dst.repr
|
||||
|
||||
a.codeFragment("mov", src, dst)
|
||||
# No clobber
|
||||
|
||||
func mov*(a: var Assembler_x86, dst: Operand, imm: int) =
|
||||
## Does: dst <- imm
|
||||
doAssert dst.desc.constraint in OutputReg, $dst.repr
|
||||
@ -570,6 +629,14 @@ func cmovz*(a: var Assembler_x86, dst, src: Operand) =
|
||||
a.codeFragment("cmovz", src, dst)
|
||||
# No clobber
|
||||
|
||||
func cmovz*(a: var Assembler_x86, dst: Operand, src: OperandReuse) =
|
||||
## Does: dst <- src if the zero flag is not set
|
||||
doAssert dst.desc.rm in {Reg, ElemsInReg}, "The destination operand must be a register: " & $dst.repr
|
||||
doAssert dst.desc.constraint in OutputReg, $dst.repr
|
||||
|
||||
a.codeFragment("cmovz", src, dst)
|
||||
# No clobber
|
||||
|
||||
func cmovnz*(a: var Assembler_x86, dst, src: Operand) =
|
||||
## Does: dst <- src if the zero flag is not set
|
||||
doAssert dst.desc.rm in {Reg, ElemsInReg}, "The destination operand must be a register: " & $dst.repr
|
||||
@ -578,6 +645,14 @@ func cmovnz*(a: var Assembler_x86, dst, src: Operand) =
|
||||
a.codeFragment("cmovnz", src, dst)
|
||||
# No clobber
|
||||
|
||||
func cmovnz*(a: var Assembler_x86, dst: Operand, src: OperandReuse) =
|
||||
## Does: dst <- src if the zero flag is not set
|
||||
doAssert dst.desc.rm in {Reg, ElemsInReg}, "The destination operand must be a register: " & $dst.repr
|
||||
doAssert dst.desc.constraint in OutputReg, $dst.repr
|
||||
|
||||
a.codeFragment("cmovnz", src, dst)
|
||||
# No clobber
|
||||
|
||||
func cmovs*(a: var Assembler_x86, dst, src: Operand) =
|
||||
## Does: dst <- src if the sign flag
|
||||
doAssert dst.desc.rm in {Reg, ElemsInReg}, "The destination operand must be a register: " & $dst.repr
|
||||
|
||||
@ -314,3 +314,19 @@ proc main() =
|
||||
|
||||
|
||||
main()
|
||||
|
||||
proc largeField() =
|
||||
suite "Large field":
|
||||
test "Negate 0 returns 0 (unique Montgomery repr)":
|
||||
# https://github.com/mratsim/constantine/issues/136
|
||||
# and https://github.com/mratsim/constantine/issues/114
|
||||
# The assembly implementation of neg didn't check
|
||||
# after M-a if a was zero and so while in mod M
|
||||
# M ≡ 0 (mod M), the `==` doesn't support unreduced representation.
|
||||
var a: Fp[BN254_Snarks]
|
||||
var r {.noInit.}: Fp[BN254_Snarks]
|
||||
r.neg(a)
|
||||
|
||||
check: bool r.isZero()
|
||||
|
||||
largeField()
|
||||
|
||||
187
tests/t_fp12_anti_regression.nim
Normal file
187
tests/t_fp12_anti_regression.nim
Normal file
@ -0,0 +1,187 @@
|
||||
# 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
|
||||
# stdlib
|
||||
std/unittest,
|
||||
# Internals
|
||||
../constantine/config/[common, type_ff],
|
||||
../constantine/towers,
|
||||
../constantine/config/curves,
|
||||
../constantine/io/io_towers,
|
||||
../constantine/towers
|
||||
|
||||
# ###############################################################
|
||||
#
|
||||
# Edge cases highlighted by property-based testing or fuzzing
|
||||
#
|
||||
# ###############################################################
|
||||
|
||||
# Fuzzing failure #114: Fp12 BN254 Mul and add/sub are consistent
|
||||
# Highlighted by the Long01Seq skewed RNG
|
||||
# with random seeds
|
||||
# - 1611183150
|
||||
# - 1611267611
|
||||
# - 1611393788
|
||||
# - 1611420927
|
||||
# - 1611402369
|
||||
|
||||
proc test114(factor: int, a: Fp12[BN254_Snarks]): bool =
|
||||
var sum{.noInit.}, one{.noInit.}, f{.noInit.}: Fp12[BN254_Snarks]
|
||||
one.setOne()
|
||||
|
||||
if factor < 0:
|
||||
sum.neg(a)
|
||||
f.neg(one)
|
||||
for i in 1 ..< -factor:
|
||||
sum -= a
|
||||
f -= one
|
||||
else:
|
||||
sum = a
|
||||
f = one
|
||||
for i in 1 ..< factor:
|
||||
sum += a
|
||||
f += one
|
||||
|
||||
var r{.noInit.}: Fp12[BN254_Snarks]
|
||||
|
||||
r.prod(a, f)
|
||||
|
||||
result = bool(r == sum)
|
||||
|
||||
if not result:
|
||||
echo "Failure for"
|
||||
echo "==================="
|
||||
echo "r: ", r.toHex()
|
||||
echo "-------------------"
|
||||
echo "sum: ", sum.toHex()
|
||||
echo "-------------------"
|
||||
debug:
|
||||
echo "r (raw montgomery): ", $r
|
||||
echo "-------------------"
|
||||
echo "sum (raw montgomery):", $sum
|
||||
echo "-------------------"
|
||||
echo "\n\n"
|
||||
|
||||
# Requires a Fp -> Fp2 -> Fp4 -> Fp12 towering
|
||||
var t114_cases: seq[tuple[factor: int, a: Fp12[BN254_Snarks]]]
|
||||
|
||||
t114_cases.add (
|
||||
# seed 1611183150
|
||||
-13,
|
||||
Fp12[BN254_Snarks].fromHex(
|
||||
"0x0000000000ffffffffffffffff3f00c00100000000fcffff0700000000000000",
|
||||
"0x0000000000ffffffffffff7f000000e0ffff03000000fcff07e0ffffff9fffff",
|
||||
"0x0080ffffffffff1f00f00080ffffffffffffffffffffffffffffffffffffffff",
|
||||
"0x0c0a77c19a07df2f666ea36f7899461c0a78ec28b5d70b3dd35d430dc58f0d9d",
|
||||
"0x000007fc00000000000000000000003ffffffffffff1ffffff8000000001ffff",
|
||||
"0x000000c0ffffffdfffffffff0100feffff03c0ffffffffffffffff3f00000000",
|
||||
"0x000000000000000000000080ffffffffff3f0000f0dfff0f80ffffffffff0700",
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"0x0e0a77c199c7df2f666ee36f7879422c0a78ed28f5c70b3dd2dd448dc58eed9d",
|
||||
"0x0e0a77a19a07df2f866ea36f7839462c0a78eb28f5d70b3dd3dd438dc58f0d9c",
|
||||
"0x000000000000000000000000003fc0000003f80000000000000007ffffffffff",
|
||||
"0x0000001fff0000000000000000038000003ffffffffffff800000000000ff000"
|
||||
)
|
||||
)
|
||||
|
||||
var x = Fp12[BN254_Snarks].fromHex(
|
||||
"0x30644e72d431a029b85045b68b4e4e9d8a816a915b98ca99e1208c16d87cfd47",
|
||||
"0x30644e72d431a029b8504c4381814cf0978e43916864f199d5b38c16dd5cfd54",
|
||||
"0x29d74e72e131ab96ac203f298181585d97816a916871ca8d3c208c16d87cfd54",
|
||||
"0x250924f6b2602b3eada2ca30e63cd209d5e1ac3465db981134c5c8a859b04423",
|
||||
"0x3063e6a6e131a029b85045b68181551d97816a916927ca8d42a08c16d862fd54",
|
||||
"0x306444a5e131a1c9b85045c37474655da4509d916871ca8d3c2095e3d87cfd47",
|
||||
"0x30644e72e131a029b8503f298181585da14e6a852d11d6c3af208c16d889a247",
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"0x0b0924f6b5a02b3ead9f8a30e7dd0539d5e19f3126ab98113b45b52859b1e423",
|
||||
"0x0b092696b2602b3d0da2ca30eb1cd139d5e1b93125db98112e45c22859b04430",
|
||||
"0x30644e72e131a029b85045b67e44985d974dd2916871ca8d3c202416d87cfd54",
|
||||
"0x30644cd2ee31a029b85045b68153d85d94416a916871caf53c208c16d7adcd47"
|
||||
)
|
||||
|
||||
t114_cases.add (
|
||||
# seed 1611267611
|
||||
-7,
|
||||
Fp12[BN254_Snarks].fromHex(
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"0x0e0477c19a07de6e666ea46eb77947290a786a28f5c70b3dd35d4486c58f0cdc",
|
||||
"0x00fffffffffffffffffffffff80000000003ffffffffffffc0000000007fffff",
|
||||
"0x00ffff00000080ffffffffffffffffffffff1f00000000000000000000c03f00",
|
||||
"0x00000000c0ff00c0ff07000000000000000000000000000000feffffffffffff",
|
||||
"0x000000000007ffffffffff000000e003f83fffffe0000000001ffff803ffc000",
|
||||
"0x0000003fffffffffffffffffffffffffffff801fffffffc01f00000007ffffff",
|
||||
"0x00000000003fffffffe00000000000ffffffe08003fff800007fffffffffffff",
|
||||
"0x0e0a57c19a47dfaf666ea36f787945ac8a78eb28f5c70b3dd2dd438dc58f0d9d",
|
||||
"0x0000000000feffffffffff1f0000000000000000000080ffff03f8ffffffffff",
|
||||
"0x000000f87f0000c0ffffffffffffffffffffffffffffffffffffff07fcffffff",
|
||||
"0x01fffffffe0000000001fcffffffffffffffffc003ffffff8001ffffffffffff"
|
||||
)
|
||||
)
|
||||
|
||||
t114_cases.add (
|
||||
# seed 1611393788
|
||||
-15,
|
||||
Fp12[BN254_Snarks].fromHex(
|
||||
"0x0e0a77c192085f2f666e63777879462c0a78eb08f5c70b3dd35d438dd58f0d9c",
|
||||
"0x0fffe03ffe0000000000000000001fffffff0000000fffffe0000fffffffffff",
|
||||
"0x000000000003ffffffffffff00000000000000000000000000000ffffffeffff",
|
||||
"0x00f0ffffffff3f0000f0ffffffffff0700000000000000000000600000001f00",
|
||||
"0x0f9bb18c1ece5fd647afba4d7e7ea7a0687ebd6a978e3572c3df73e9278306b8",
|
||||
"0x00e0ff3f00f0ffffffffff010000000080ffffffffffffffffffff000000ffff",
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"0x0dca77c19a07e02f6666a56f7878462c0a792b28f5c6cb3dd35d438dcd8f0d80",
|
||||
"0x0e0a76c19a07df2f6e6ea36f7879462c0a78eb28f5c70b3dd359438dc59f0d7d",
|
||||
"0x0e0a77c11a07df2f666ea36f8075462c0a78eb28f5c70b3dd35d538dc58f0dac",
|
||||
"0x0e0a77819a083f2f766e9b6f7879462c0a78eb28f5c70b3dd35d438dc592119c",
|
||||
"0x000000000ffffffffffffe000000003ffffc0000000000000000000000000000"
|
||||
)
|
||||
)
|
||||
|
||||
t114_cases.add (
|
||||
# seed 1611420927
|
||||
-25,
|
||||
Fp12[BN254_Snarks].fromHex(
|
||||
"0x0000000000ffffc00000000000000fffffffffffffffffffff00007fffe003ff",
|
||||
"0x00000000ffff1fc0ffffff1ff8ffffffffffff00fc010000feffffffff0300f0",
|
||||
"0x000000000000001800000000e00300feffffffffffff1f00f0ffffffffffffff",
|
||||
"0x0e0a75c1da07df2f666ea36f7879461c0a78ec28f5c70b35d35d438dc590ed9d",
|
||||
"0x0e09f6c19a085f30666f846f7780c72c097feb29f5c70b3cd65dc48d44900cbc",
|
||||
"0x0000000001ffffe7e0000000000000003fffffffffffff000000000001fff800",
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"0x0e09b6c28b07df2f666ea36f7879462c0a780ae9f5c70b3dd35c638cc48f0da3",
|
||||
"0x0e0a77c19a07df2f666da46f7879462c0a78eb2976c60a7cd35d438eb68f0c9d",
|
||||
"0x0007f00007fffff00000000000000003ffffffff8000000fffffc001ffffffff",
|
||||
"0x0e0a77c19a07df2f666ea36f7879462c0a68ec28f5c70b3dd35c438dcd4f0e1c",
|
||||
"0x1ffffffffffffffffffffffffffffff000000000000000000fc000ffffffffff"
|
||||
)
|
||||
)
|
||||
|
||||
t114_cases.add (
|
||||
# seed 1611402369
|
||||
-10,
|
||||
Fp12[BN254_Snarks].fromHex(
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"0x00000000000020000007fffff800000000000001ffffffffffffffffffffe000",
|
||||
"0x0000000000000000000000f8fffffffffffffff7ffffffffffff1f0000000200",
|
||||
"0x0000030000000003fffc00000000003ffffffe000000000000ffc00000000000",
|
||||
"0x0e0a76e09a07df2f666ea3705881432c0a78e828f5c70b3d125e348d058f0cbc",
|
||||
"0x0000000f01fffc7fffffffffffffffffffffffe000000000000fffffc0000000",
|
||||
"0x0e0a77c0b907e02c666ea36f77f8462c0a78eb28f5c70b3e545d438dc58f0d9c",
|
||||
"0x0e0a77a19a07df2f668ea36f78793a2c0a78eb2875c74b3dd355438dc59f0d9c",
|
||||
"0x0e0a75c19a07df31662ea36f7879462c0a78eb28f5c70c1dd361438dc58f0d9c",
|
||||
"0x00000000000000000000000000feffff00001c000007e0ffffffffff07000000",
|
||||
"0x00001ffffffff000007fffffffff0000007f000000000000ffffffffffffffff",
|
||||
"0x0e0996c19a08d02e756ea36f7879462c0a78eb28f5c70b3dd43dc28dc58f0d9d"
|
||||
)
|
||||
)
|
||||
|
||||
suite "Fuzzing failure #114: Fp12 BN254 Mul and add/sub are consistent":
|
||||
test $t114_cases.len & " failure cases are now successful":
|
||||
for i in 0..<t114_cases.len:
|
||||
check: test114(t114_cases[i].factor, t114_cases[i].a)
|
||||
@ -34,7 +34,7 @@ type
|
||||
Long01Sequence
|
||||
|
||||
var rng: RngState
|
||||
let seed = uint32(getTime().toUnix() and (1'i64 shl 32 - 1)) # unixTime mod 2^32
|
||||
let seed = 1611432811 # uint32(getTime().toUnix() and (1'i64 shl 32 - 1)) # unixTime mod 2^32
|
||||
rng.seed(seed)
|
||||
echo "\n------------------------------------------------------\n"
|
||||
echo "test_fp2_sqrt xoshiro512** seed: ", seed
|
||||
@ -98,4 +98,35 @@ proc main() =
|
||||
bool not a.isSquare()
|
||||
bool not a.sqrt_if_square()
|
||||
|
||||
suite "Modular square root - Assembly bugs highlighted by property-based testing " & " [" & $WordBitwidth & "-bit mode]":
|
||||
test "Don't set Neg(Zero) fields to modulus (non-unique Montgomery repr) - #136":
|
||||
# https://github.com/mratsim/constantine/issues/136
|
||||
# and https://github.com/mratsim/constantine/issues/114
|
||||
# The assembly implementation of neg didn't check
|
||||
# after M-a if a was zero and so while in mod M
|
||||
# M ≡ 0 (mod M), the `==` doesn't support unreduced representation.
|
||||
# Seed: 1611432811
|
||||
let a = Fp2[BN254_Snarks].fromHex(
|
||||
"0x0e097bc0990edfae676ba36f7879462c09b7eb28f6450b6dd3de438dc58f0d9c",
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
)
|
||||
var na{.noInit.}: Fp2[BN254_Snarks]
|
||||
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(s == a or s == na)
|
||||
main()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user