From 7e97cd4ac5ae8b69c8274de1a32406e8f8d91c2b Mon Sep 17 00:00:00 2001 From: Mamy Ratsimbazafy Date: Sun, 24 Jan 2021 12:35:27 +0100 Subject: [PATCH] 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 --- constantine.nimble | 12 ++ .../assembly/limbs_asm_modular_x86.nim | 32 ++- .../limbs_asm_montred_x86_adx_bmi2.nim | 3 + constantine/arithmetic/bigints.nim | 4 + constantine/arithmetic/finite_fields.nim | 10 +- constantine/arithmetic/limbs.nim | 7 + constantine/io/io_towers.nim | 2 +- .../primitives/macro_assembler_x86.nim | 91 ++++++++- tests/t_finite_fields.nim | 16 ++ tests/t_fp12_anti_regression.nim | 187 ++++++++++++++++++ tests/t_fp2_sqrt.nim | 33 +++- 11 files changed, 378 insertions(+), 19 deletions(-) create mode 100644 tests/t_fp12_anti_regression.nim diff --git a/constantine.nimble b/constantine.nimble index f664d7f..7a257a8 100644 --- a/constantine.nimble +++ b/constantine.nimble @@ -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) diff --git a/constantine/arithmetic/assembly/limbs_asm_modular_x86.nim b/constantine/arithmetic/assembly/limbs_asm_modular_x86.nim index 149b9db..e4b3197 100644 --- a/constantine/arithmetic/assembly/limbs_asm_modular_x86.nim +++ b/constantine/arithmetic/assembly/limbs_asm_modular_x86.nim @@ -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) diff --git a/constantine/arithmetic/assembly/limbs_asm_montred_x86_adx_bmi2.nim b/constantine/arithmetic/assembly/limbs_asm_montred_x86_adx_bmi2.nim index f8b467a..9108d4d 100644 --- a/constantine/arithmetic/assembly/limbs_asm_montred_x86_adx_bmi2.nim +++ b/constantine/arithmetic/assembly/limbs_asm_montred_x86_adx_bmi2.nim @@ -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 # ------------------------------------------------------------ diff --git a/constantine/arithmetic/bigints.nim b/constantine/arithmetic/bigints.nim index 53ec303..b6f565c 100644 --- a/constantine/arithmetic/bigints.nim +++ b/constantine/arithmetic/bigints.nim @@ -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 # ------------------------------------------------------------ diff --git a/constantine/arithmetic/finite_fields.nim b/constantine/arithmetic/finite_fields.nim index 2933064..fdd5961 100644 --- a/constantine/arithmetic/finite_fields.nim +++ b/constantine/arithmetic/finite_fields.nim @@ -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 diff --git a/constantine/arithmetic/limbs.nim b/constantine/arithmetic/limbs.nim index 633f2cd..a7e1027 100644 --- a/constantine/arithmetic/limbs.nim +++ b/constantine/arithmetic/limbs.nim @@ -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 # ------------------------------------------------------------ diff --git a/constantine/io/io_towers.nim b/constantine/io/io_towers.nim index fcf2034..dcc375c 100644 --- a/constantine/io/io_towers.nim +++ b/constantine/io/io_towers.nim @@ -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: diff --git a/constantine/primitives/macro_assembler_x86.nim b/constantine/primitives/macro_assembler_x86.nim index 70a79cc..ae17ba9 100644 --- a/constantine/primitives/macro_assembler_x86.nim +++ b/constantine/primitives/macro_assembler_x86.nim @@ -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 diff --git a/tests/t_finite_fields.nim b/tests/t_finite_fields.nim index a06d247..37b5362 100644 --- a/tests/t_finite_fields.nim +++ b/tests/t_finite_fields.nim @@ -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() diff --git a/tests/t_fp12_anti_regression.nim b/tests/t_fp12_anti_regression.nim new file mode 100644 index 0000000..6783a06 --- /dev/null +++ b/tests/t_fp12_anti_regression.nim @@ -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..