constantine/tests/t_finite_fields.nim
Mamy Ratsimbazafy 7e97cd4ac5
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
2021-01-24 12:35:27 +01:00

333 lines
8.3 KiB
Nim

# 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 std/unittest,
../constantine/arithmetic,
../constantine/io/io_fields,
../constantine/config/curves
static: doAssert defined(testingCurves), "This modules requires the -d:testingCurves compile option"
echo "\n------------------------------------------------------\n"
proc main() =
suite "Basic arithmetic over finite fields":
test "Addition mod 101":
block:
var x, y, z: Fp[Fake101]
x.fromUint(80'u32)
y.fromUint(10'u32)
z.fromUint(90'u32)
let u = x + y
x += y
var x_bytes: array[8, byte]
x_bytes.exportRawUint(x, cpuEndian)
check:
# Check equality in the Montgomery domain
bool(z == x)
bool(z == u)
# Check equality when converting back to natural domain
90'u64 == cast[uint64](x_bytes)
block:
var x, y, z: Fp[Fake101]
x.fromUint(80'u32)
y.fromUint(21'u32)
z.fromUint(0'u32)
let u = x + y
x += y
var x_bytes: array[8, byte]
x_bytes.exportRawUint(x, cpuEndian)
check:
# Check equality in the Montgomery domain
bool(z == x)
bool(z == u)
# Check equality when converting back to natural domain
0'u64 == cast[uint64](x_bytes)
block:
var x, y, z: Fp[Fake101]
x.fromUint(80'u32)
y.fromUint(22'u32)
z.fromUint(1'u32)
let u = x + y
x += y
var x_bytes: array[8, byte]
x_bytes.exportRawUint(x, cpuEndian)
check:
# Check equality in the Montgomery domain
bool(z == x)
bool(z == u)
# Check equality when converting back to natural domain
1'u64 == cast[uint64](x_bytes)
test "Substraction mod 101":
block:
var x, y, z: Fp[Fake101]
x.fromUint(80'u32)
y.fromUint(10'u32)
z.fromUint(70'u32)
let u = x - y
x -= y
var x_bytes: array[8, byte]
x_bytes.exportRawUint(x, cpuEndian)
check:
# Check equality in the Montgomery domain
bool(z == x)
bool(z == u)
# Check equality when converting back to natural domain
70'u64 == cast[uint64](x_bytes)
block:
var x, y, z: Fp[Fake101]
x.fromUint(80'u32)
y.fromUint(80'u32)
z.fromUint(0'u32)
let u = x - y
x -= y
var x_bytes: array[8, byte]
x_bytes.exportRawUint(x, cpuEndian)
check:
# Check equality in the Montgomery domain
bool(z == x)
bool(z == u)
# Check equality when converting back to natural domain
0'u64 == cast[uint64](x_bytes)
block:
var x, y, z: Fp[Fake101]
x.fromUint(80'u32)
y.fromUint(81'u32)
z.fromUint(100'u32)
let u = x - y
x -= y
var x_bytes: array[8, byte]
x_bytes.exportRawUint(x, cpuEndian)
check:
# Check equality in the Montgomery domain
bool(z == x)
bool(z == u)
# Check equality when converting back to natural domain
100'u64 == cast[uint64](x_bytes)
test "Multiplication mod 101":
block:
var x, y, z: Fp[Fake101]
x.fromUint(10'u32)
y.fromUint(10'u32)
z.fromUint(100'u32)
let r = x * y
var r_bytes: array[8, byte]
r_bytes.exportRawUint(r, cpuEndian)
check:
# Check equality in the Montgomery domain
bool(z == r)
# Check equality when converting back to natural domain
100'u64 == cast[uint64](r_bytes)
block:
var x, y, z: Fp[Fake101]
x.fromUint(10'u32)
y.fromUint(11'u32)
z.fromUint(9'u32)
let r = x * y
var r_bytes: array[8, byte]
r_bytes.exportRawUint(r, cpuEndian)
check:
# Check equality in the Montgomery domain
bool(z == r)
# Check equality when converting back to natural domain
9'u64 == cast[uint64](r_bytes)
test "Addition mod 2^61 - 1":
block:
var x, y, z: Fp[Mersenne61]
x.fromUint(80'u64)
y.fromUint(10'u64)
z.fromUint(90'u64)
x += y
var x_bytes: array[8, byte]
x_bytes.exportRawUint(x, cpuEndian)
let new_x = cast[uint64](x_bytes)
check:
# Check equality in the Montgomery domain
bool(z == x)
# Check equality when converting back to natural domain
new_x == 90'u64
block:
var x, y, z: Fp[Mersenne61]
x.fromUint(1'u64 shl 61 - 2)
y.fromUint(1'u32)
z.fromUint(0'u32)
x += y
var x_bytes: array[8, byte]
x_bytes.exportRawUint(x, cpuEndian)
let new_x = cast[uint64](x_bytes)
check:
# Check equality in the Montgomery domain
bool(z == x)
# Check equality when converting back to natural domain
new_x == 0'u64
block:
var x, y, z: Fp[Mersenne61]
x.fromUint(1'u64 shl 61 - 2)
y.fromUint(2'u64)
z.fromUint(1'u64)
x += y
var x_bytes: array[8, byte]
x_bytes.exportRawUint(x, cpuEndian)
let new_x = cast[uint64](x_bytes)
check:
# Check equality in the Montgomery domain
bool(z == x)
# Check equality when converting back to natural domain
new_x == 1'u64
test "Substraction mod 2^61 - 1":
block:
var x, y, z: Fp[Mersenne61]
x.fromUint(80'u64)
y.fromUint(10'u64)
z.fromUint(70'u64)
x -= y
var x_bytes: array[8, byte]
x_bytes.exportRawUint(x, cpuEndian)
let new_x = cast[uint64](x_bytes)
check:
# Check equality in the Montgomery domain
bool(z == x)
# Check equality when converting back to natural domain
new_x == 70'u64
block:
var x, y, z: Fp[Mersenne61]
x.fromUint(0'u64)
y.fromUint(1'u64)
z.fromUint(1'u64 shl 61 - 2)
x -= y
var x_bytes: array[8, byte]
x_bytes.exportRawUint(x, cpuEndian)
let new_x = cast[uint64](x_bytes)
check:
# Check equality in the Montgomery domain
bool(z == x)
# Check equality when converting back to natural domain
new_x == 1'u64 shl 61 - 2
test "Multiplication mod 2^61 - 1":
block:
var x, y, z: Fp[Mersenne61]
x.fromUint(10'u32)
y.fromUint(10'u32)
z.fromUint(100'u32)
let r = x * y
var r_bytes: array[8, byte]
r_bytes.exportRawUint(r, cpuEndian)
let new_r = cast[uint64](r_bytes)
check:
# Check equality in the Montgomery domain
bool(z == r)
# Check equality when converting back to natural domain
cast[uint64](r_bytes) == 100'u64
block:
var x, y, z: Fp[Mersenne61]
x.fromUint(1'u32 shl 31)
y.fromUint(1'u32 shl 31)
z.fromUint(2'u32)
let r = x * y
var r_bytes: array[8, byte]
r_bytes.exportRawUint(r, cpuEndian)
let new_r = cast[uint64](r_bytes)
check:
# Check equality in the Montgomery domain
bool(z == r)
# Check equality when converting back to natural domain
new_r == 2'u64
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()