// Copyright 2020 ConsenSys AG
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// Code generated by goff (v0.2.0) DO NOT EDIT
|
|
|
|
// Package ff contains field arithmetic operations
|
|
package ff
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"math/big"
|
|
"math/bits"
|
|
mrand "math/rand"
|
|
"testing"
|
|
)
|
|
|
|
func TestELEMENTCorrectnessAgainstBigInt(t *testing.T) {
|
|
modulus, _ := new(big.Int).SetString("21888242871839275222246405745257275088548364400416034343698204186575808495617", 10)
|
|
cmpEandB := func(e *Element, b *big.Int, name string) {
|
|
var _e big.Int
|
|
if e.FromMont().ToBigInt(&_e).Cmp(b) != 0 {
|
|
t.Fatal(name, "failed")
|
|
}
|
|
}
|
|
var modulusMinusOne, one big.Int
|
|
one.SetUint64(1)
|
|
|
|
modulusMinusOne.Sub(modulus, &one)
|
|
|
|
var n int
|
|
if testing.Short() {
|
|
n = 10
|
|
} else {
|
|
n = 500
|
|
}
|
|
|
|
for i := 0; i < n; i++ {
|
|
|
|
// sample 2 random big int
|
|
b1, _ := rand.Int(rand.Reader, modulus)
|
|
b2, _ := rand.Int(rand.Reader, modulus)
|
|
rExp := mrand.Uint64()
|
|
|
|
// adding edge cases
|
|
// TODO need more edge cases
|
|
switch i {
|
|
case 0:
|
|
rExp = 0
|
|
b1.SetUint64(0)
|
|
case 1:
|
|
b2.SetUint64(0)
|
|
case 2:
|
|
b1.SetUint64(0)
|
|
b2.SetUint64(0)
|
|
case 3:
|
|
rExp = 0
|
|
case 4:
|
|
rExp = 1
|
|
case 5:
|
|
rExp = ^uint64(0) // max uint
|
|
case 6:
|
|
rExp = 2
|
|
b1.Set(&modulusMinusOne)
|
|
case 7:
|
|
b2.Set(&modulusMinusOne)
|
|
case 8:
|
|
b1.Set(&modulusMinusOne)
|
|
b2.Set(&modulusMinusOne)
|
|
}
|
|
|
|
rbExp := new(big.Int).SetUint64(rExp)
|
|
|
|
var bMul, bAdd, bSub, bDiv, bNeg, bLsh, bInv, bExp, bExp2, bSquare big.Int
|
|
|
|
// e1 = mont(b1), e2 = mont(b2)
|
|
var e1, e2, eMul, eAdd, eSub, eDiv, eNeg, eLsh, eInv, eExp, eSquare, eMulAssign, eSubAssign, eAddAssign Element
|
|
e1.SetBigInt(b1)
|
|
e2.SetBigInt(b2)
|
|
|
|
// (e1*e2).FromMont() === b1*b2 mod q ... etc
|
|
eSquare.Square(&e1)
|
|
eMul.Mul(&e1, &e2)
|
|
eMulAssign.Set(&e1)
|
|
eMulAssign.MulAssign(&e2)
|
|
eAdd.Add(&e1, &e2)
|
|
eAddAssign.Set(&e1)
|
|
eAddAssign.AddAssign(&e2)
|
|
eSub.Sub(&e1, &e2)
|
|
eSubAssign.Set(&e1)
|
|
eSubAssign.SubAssign(&e2)
|
|
eDiv.Div(&e1, &e2)
|
|
eNeg.Neg(&e1)
|
|
eInv.Inverse(&e1)
|
|
eExp.Exp(e1, rExp)
|
|
eLsh.Double(&e1)
|
|
|
|
// same operations with big int
|
|
bAdd.Add(b1, b2).Mod(&bAdd, modulus)
|
|
bMul.Mul(b1, b2).Mod(&bMul, modulus)
|
|
bSquare.Mul(b1, b1).Mod(&bSquare, modulus)
|
|
bSub.Sub(b1, b2).Mod(&bSub, modulus)
|
|
bDiv.ModInverse(b2, modulus)
|
|
bDiv.Mul(&bDiv, b1).
|
|
Mod(&bDiv, modulus)
|
|
bNeg.Neg(b1).Mod(&bNeg, modulus)
|
|
|
|
bInv.ModInverse(b1, modulus)
|
|
bExp.Exp(b1, rbExp, modulus)
|
|
bLsh.Lsh(b1, 1).Mod(&bLsh, modulus)
|
|
|
|
cmpEandB(&eSquare, &bSquare, "Square")
|
|
cmpEandB(&eMul, &bMul, "Mul")
|
|
cmpEandB(&eMulAssign, &bMul, "MulAssign")
|
|
cmpEandB(&eAdd, &bAdd, "Add")
|
|
cmpEandB(&eAddAssign, &bAdd, "AddAssign")
|
|
cmpEandB(&eSub, &bSub, "Sub")
|
|
cmpEandB(&eSubAssign, &bSub, "SubAssign")
|
|
cmpEandB(&eDiv, &bDiv, "Div")
|
|
cmpEandB(&eNeg, &bNeg, "Neg")
|
|
cmpEandB(&eInv, &bInv, "Inv")
|
|
cmpEandB(&eExp, &bExp, "Exp")
|
|
|
|
cmpEandB(&eLsh, &bLsh, "Lsh")
|
|
|
|
// legendre symbol
|
|
if e1.Legendre() != big.Jacobi(b1, modulus) {
|
|
t.Fatal("legendre symbol computation failed")
|
|
}
|
|
if e2.Legendre() != big.Jacobi(b2, modulus) {
|
|
t.Fatal("legendre symbol computation failed")
|
|
}
|
|
|
|
// these are slow, killing circle ci
|
|
if n <= 5 {
|
|
// sqrt
|
|
var eSqrt, eExp2 Element
|
|
var bSqrt big.Int
|
|
bSqrt.ModSqrt(b1, modulus)
|
|
eSqrt.Sqrt(&e1)
|
|
cmpEandB(&eSqrt, &bSqrt, "Sqrt")
|
|
|
|
bits := b2.Bits()
|
|
exponent := make([]uint64, len(bits))
|
|
for k := 0; k < len(bits); k++ {
|
|
exponent[k] = uint64(bits[k])
|
|
}
|
|
eExp2.Exp(e1, exponent...)
|
|
bExp2.Exp(b1, b2, modulus)
|
|
cmpEandB(&eExp2, &bExp2, "Exp multi words")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestELEMENTIsRandom(t *testing.T) {
|
|
for i := 0; i < 50; i++ {
|
|
var x, y Element
|
|
x.SetRandom()
|
|
y.SetRandom()
|
|
if x.Equal(&y) {
|
|
t.Fatal("2 random numbers are unlikely to be equal")
|
|
}
|
|
}
|
|
}
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// benchmarks
|
|
// most benchmarks are rudimentary and should sample a large number of random inputs
|
|
// or be run multiple times to ensure it didn't measure the fastest path of the function
|
|
|
|
var benchResElement Element
|
|
|
|
func BenchmarkInverseELEMENT(b *testing.B) {
|
|
var x Element
|
|
x.SetRandom()
|
|
benchResElement.SetRandom()
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
benchResElement.Inverse(&x)
|
|
}
|
|
|
|
}
|
|
func BenchmarkExpELEMENT(b *testing.B) {
|
|
var x Element
|
|
x.SetRandom()
|
|
benchResElement.SetRandom()
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
benchResElement.Exp(x, mrand.Uint64())
|
|
}
|
|
}
|
|
|
|
func BenchmarkDoubleELEMENT(b *testing.B) {
|
|
benchResElement.SetRandom()
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
benchResElement.Double(&benchResElement)
|
|
}
|
|
}
|
|
|
|
func BenchmarkAddELEMENT(b *testing.B) {
|
|
var x Element
|
|
x.SetRandom()
|
|
benchResElement.SetRandom()
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
benchResElement.Add(&x, &benchResElement)
|
|
}
|
|
}
|
|
|
|
func BenchmarkSubELEMENT(b *testing.B) {
|
|
var x Element
|
|
x.SetRandom()
|
|
benchResElement.SetRandom()
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
benchResElement.Sub(&x, &benchResElement)
|
|
}
|
|
}
|
|
|
|
func BenchmarkNegELEMENT(b *testing.B) {
|
|
benchResElement.SetRandom()
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
benchResElement.Neg(&benchResElement)
|
|
}
|
|
}
|
|
|
|
func BenchmarkDivELEMENT(b *testing.B) {
|
|
var x Element
|
|
x.SetRandom()
|
|
benchResElement.SetRandom()
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
benchResElement.Div(&x, &benchResElement)
|
|
}
|
|
}
|
|
|
|
func BenchmarkFromMontELEMENT(b *testing.B) {
|
|
benchResElement.SetRandom()
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
benchResElement.FromMont()
|
|
}
|
|
}
|
|
|
|
func BenchmarkToMontELEMENT(b *testing.B) {
|
|
benchResElement.SetRandom()
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
benchResElement.ToMont()
|
|
}
|
|
}
|
|
func BenchmarkSquareELEMENT(b *testing.B) {
|
|
benchResElement.SetRandom()
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
benchResElement.Square(&benchResElement)
|
|
}
|
|
}
|
|
|
|
func BenchmarkSqrtELEMENT(b *testing.B) {
|
|
var a Element
|
|
a.SetRandom()
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
benchResElement.Sqrt(&a)
|
|
}
|
|
}
|
|
|
|
func BenchmarkMulAssignELEMENT(b *testing.B) {
|
|
x := Element{
|
|
1997599621687373223,
|
|
6052339484930628067,
|
|
10108755138030829701,
|
|
150537098327114917,
|
|
}
|
|
benchResElement.SetOne()
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
benchResElement.MulAssign(&x)
|
|
}
|
|
}
|
|
|
|
func BenchmarkMulAssignASMELEMENT(b *testing.B) {
|
|
x := Element{
|
|
1997599621687373223,
|
|
6052339484930628067,
|
|
10108755138030829701,
|
|
150537098327114917,
|
|
}
|
|
benchResElement.SetOne()
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
MulAssignElement(&benchResElement, &x)
|
|
}
|
|
}
|
|
|
|
func TestELEMENTAsm(t *testing.T) {
|
|
// ensure ASM implementations matches the ones using math/bits
|
|
modulus, _ := new(big.Int).SetString("21888242871839275222246405745257275088548364400416034343698204186575808495617", 10)
|
|
for i := 0; i < 500; i++ {
|
|
// sample 2 random big int
|
|
b1, _ := rand.Int(rand.Reader, modulus)
|
|
b2, _ := rand.Int(rand.Reader, modulus)
|
|
|
|
// e1 = mont(b1), e2 = mont(b2)
|
|
var e1, e2, eTestMul, eMulAssign, eSquare, eTestSquare Element
|
|
e1.SetBigInt(b1)
|
|
e2.SetBigInt(b2)
|
|
|
|
eTestMul = e1
|
|
eTestMul.testMulAssign(&e2)
|
|
eMulAssign = e1
|
|
eMulAssign.MulAssign(&e2)
|
|
|
|
if !eTestMul.Equal(&eMulAssign) {
|
|
t.Fatal("inconsisntencies between MulAssign and testMulAssign --> check if MulAssign is calling ASM implementaiton on amd64")
|
|
}
|
|
|
|
// square
|
|
eSquare.Square(&e1)
|
|
eTestSquare.testSquare(&e1)
|
|
|
|
if !eTestSquare.Equal(&eSquare) {
|
|
t.Fatal("inconsisntencies between Square and testSquare --> check if Square is calling ASM implementaiton on amd64")
|
|
}
|
|
}
|
|
}
|
|
|
|
// this is here for consistency purposes, to ensure MulAssign on AMD64 using asm implementation gives consistent results
|
|
func (z *Element) testMulAssign(x *Element) *Element {
|
|
|
|
var t [4]uint64
|
|
var c [3]uint64
|
|
{
|
|
// round 0
|
|
v := z[0]
|
|
c[1], c[0] = bits.Mul64(v, x[0])
|
|
m := c[0] * 14042775128853446655
|
|
c[2] = madd0(m, 4891460686036598785, c[0])
|
|
c[1], c[0] = madd1(v, x[1], c[1])
|
|
c[2], t[0] = madd2(m, 2896914383306846353, c[2], c[0])
|
|
c[1], c[0] = madd1(v, x[2], c[1])
|
|
c[2], t[1] = madd2(m, 13281191951274694749, c[2], c[0])
|
|
c[1], c[0] = madd1(v, x[3], c[1])
|
|
t[3], t[2] = madd3(m, 3486998266802970665, c[0], c[2], c[1])
|
|
}
|
|
{
|
|
// round 1
|
|
v := z[1]
|
|
c[1], c[0] = madd1(v, x[0], t[0])
|
|
m := c[0] * 14042775128853446655
|
|
c[2] = madd0(m, 4891460686036598785, c[0])
|
|
c[1], c[0] = madd2(v, x[1], c[1], t[1])
|
|
c[2], t[0] = madd2(m, 2896914383306846353, c[2], c[0])
|
|
c[1], c[0] = madd2(v, x[2], c[1], t[2])
|
|
c[2], t[1] = madd2(m, 13281191951274694749, c[2], c[0])
|
|
c[1], c[0] = madd2(v, x[3], c[1], t[3])
|
|
t[3], t[2] = madd3(m, 3486998266802970665, c[0], c[2], c[1])
|
|
}
|
|
{
|
|
// round 2
|
|
v := z[2]
|
|
c[1], c[0] = madd1(v, x[0], t[0])
|
|
m := c[0] * 14042775128853446655
|
|
c[2] = madd0(m, 4891460686036598785, c[0])
|
|
c[1], c[0] = madd2(v, x[1], c[1], t[1])
|
|
c[2], t[0] = madd2(m, 2896914383306846353, c[2], c[0])
|
|
c[1], c[0] = madd2(v, x[2], c[1], t[2])
|
|
c[2], t[1] = madd2(m, 13281191951274694749, c[2], c[0])
|
|
c[1], c[0] = madd2(v, x[3], c[1], t[3])
|
|
t[3], t[2] = madd3(m, 3486998266802970665, c[0], c[2], c[1])
|
|
}
|
|
{
|
|
// round 3
|
|
v := z[3]
|
|
c[1], c[0] = madd1(v, x[0], t[0])
|
|
m := c[0] * 14042775128853446655
|
|
c[2] = madd0(m, 4891460686036598785, c[0])
|
|
c[1], c[0] = madd2(v, x[1], c[1], t[1])
|
|
c[2], z[0] = madd2(m, 2896914383306846353, c[2], c[0])
|
|
c[1], c[0] = madd2(v, x[2], c[1], t[2])
|
|
c[2], z[1] = madd2(m, 13281191951274694749, c[2], c[0])
|
|
c[1], c[0] = madd2(v, x[3], c[1], t[3])
|
|
z[3], z[2] = madd3(m, 3486998266802970665, c[0], c[2], c[1])
|
|
}
|
|
|
|
// if z > q --> z -= q
|
|
// note: this is NOT constant time
|
|
if !(z[3] < 3486998266802970665 || (z[3] == 3486998266802970665 && (z[2] < 13281191951274694749 || (z[2] == 13281191951274694749 && (z[1] < 2896914383306846353 || (z[1] == 2896914383306846353 && (z[0] < 4891460686036598785))))))) {
|
|
var b uint64
|
|
z[0], b = bits.Sub64(z[0], 4891460686036598785, 0)
|
|
z[1], b = bits.Sub64(z[1], 2896914383306846353, b)
|
|
z[2], b = bits.Sub64(z[2], 13281191951274694749, b)
|
|
z[3], _ = bits.Sub64(z[3], 3486998266802970665, b)
|
|
}
|
|
return z
|
|
}
|
|
|
|
// this is here for consistency purposes, to ensure Square on AMD64 using asm implementation gives consistent results
|
|
func (z *Element) testSquare(x *Element) *Element {
|
|
|
|
var p [4]uint64
|
|
|
|
var u, v uint64
|
|
{
|
|
// round 0
|
|
u, p[0] = bits.Mul64(x[0], x[0])
|
|
m := p[0] * 14042775128853446655
|
|
C := madd0(m, 4891460686036598785, p[0])
|
|
var t uint64
|
|
t, u, v = madd1sb(x[0], x[1], u)
|
|
C, p[0] = madd2(m, 2896914383306846353, v, C)
|
|
t, u, v = madd1s(x[0], x[2], t, u)
|
|
C, p[1] = madd2(m, 13281191951274694749, v, C)
|
|
_, u, v = madd1s(x[0], x[3], t, u)
|
|
p[3], p[2] = madd3(m, 3486998266802970665, v, C, u)
|
|
}
|
|
{
|
|
// round 1
|
|
m := p[0] * 14042775128853446655
|
|
C := madd0(m, 4891460686036598785, p[0])
|
|
u, v = madd1(x[1], x[1], p[1])
|
|
C, p[0] = madd2(m, 2896914383306846353, v, C)
|
|
var t uint64
|
|
t, u, v = madd2sb(x[1], x[2], p[2], u)
|
|
C, p[1] = madd2(m, 13281191951274694749, v, C)
|
|
_, u, v = madd2s(x[1], x[3], p[3], t, u)
|
|
p[3], p[2] = madd3(m, 3486998266802970665, v, C, u)
|
|
}
|
|
{
|
|
// round 2
|
|
m := p[0] * 14042775128853446655
|
|
C := madd0(m, 4891460686036598785, p[0])
|
|
C, p[0] = madd2(m, 2896914383306846353, p[1], C)
|
|
u, v = madd1(x[2], x[2], p[2])
|
|
C, p[1] = madd2(m, 13281191951274694749, v, C)
|
|
_, u, v = madd2sb(x[2], x[3], p[3], u)
|
|
p[3], p[2] = madd3(m, 3486998266802970665, v, C, u)
|
|
}
|
|
{
|
|
// round 3
|
|
m := p[0] * 14042775128853446655
|
|
C := madd0(m, 4891460686036598785, p[0])
|
|
C, z[0] = madd2(m, 2896914383306846353, p[1], C)
|
|
C, z[1] = madd2(m, 13281191951274694749, p[2], C)
|
|
u, v = madd1(x[3], x[3], p[3])
|
|
z[3], z[2] = madd3(m, 3486998266802970665, v, C, u)
|
|
}
|
|
|
|
// if z > q --> z -= q
|
|
// note: this is NOT constant time
|
|
if !(z[3] < 3486998266802970665 || (z[3] == 3486998266802970665 && (z[2] < 13281191951274694749 || (z[2] == 13281191951274694749 && (z[1] < 2896914383306846353 || (z[1] == 2896914383306846353 && (z[0] < 4891460686036598785))))))) {
|
|
var b uint64
|
|
z[0], b = bits.Sub64(z[0], 4891460686036598785, 0)
|
|
z[1], b = bits.Sub64(z[1], 2896914383306846353, b)
|
|
z[2], b = bits.Sub64(z[2], 13281191951274694749, b)
|
|
z[3], _ = bits.Sub64(z[3], 3486998266802970665, b)
|
|
}
|
|
return z
|
|
|
|
}
|