Compare commits

...

5 Commits

7 changed files with 262 additions and 155 deletions

View File

@@ -2,9 +2,10 @@ package babyjub
import ( import (
"fmt" "fmt"
"math/big"
"github.com/iden3/go-iden3-crypto/constants" "github.com/iden3/go-iden3-crypto/constants"
"github.com/iden3/go-iden3-crypto/utils" "github.com/iden3/go-iden3-crypto/utils"
"math/big"
) )
// A is one of the babyjub constants. // A is one of the babyjub constants.
@@ -35,9 +36,9 @@ func init() {
B8 = NewPoint() B8 = NewPoint()
B8.X = utils.NewIntFromString( B8.X = utils.NewIntFromString(
"17777552123799933955779906779655732241715742912184938656739573121738514868268") "5299619240641551281634865583518297030282874472190772894086521144482721001553")
B8.Y = utils.NewIntFromString( B8.Y = utils.NewIntFromString(
"2626589144620713026669568689430873010625803728049924121243784502389097019475") "16950150798460657717958625567821834550301663161624707787222815936182638968203")
} }
// Point represents a point of the babyjub curve. // Point represents a point of the babyjub curve.
@@ -74,7 +75,7 @@ func (res *Point) Add(a *Point, b *Point) *Point {
x2.Mod(x2, constants.Q) x2.Mod(x2, constants.Q)
x2.ModInverse(x2, constants.Q) // x2 = (1 + D * a.x * b.x * a.y * b.y)^-1 x2.ModInverse(x2, constants.Q) // x2 = (1 + D * a.x * b.x * a.y * b.y)^-1
// y = (a.y * b.y + A * a.x * a.x) * (1 - D * a.x * b.x * a.y * b.y)^-1 mod q // y = (a.y * b.y - A * a.x * b.x) * (1 - D * a.x * b.x * a.y * b.y)^-1 mod q
y1a := new(big.Int).Mul(a.Y, b.Y) y1a := new(big.Int).Mul(a.Y, b.Y)
y1b := new(big.Int).Set(A) y1b := new(big.Int).Set(A)
y1b.Mul(y1b, a.X) y1b.Mul(y1b, a.X)
@@ -170,10 +171,7 @@ func PackPoint(ay *big.Int, sign bool) [32]byte {
// Compress the point into a 32 byte array that contains the y coordinate in // Compress the point into a 32 byte array that contains the y coordinate in
// little endian and the sign of the x coordinate. // little endian and the sign of the x coordinate.
func (p *Point) Compress() [32]byte { func (p *Point) Compress() [32]byte {
sign := false sign := PointCoordSign(p.X)
if PointCoordSign(p.X) {
sign = true
}
return PackPoint(p.Y, sign) return PackPoint(p.Y, sign)
} }
@@ -205,7 +203,10 @@ func (p *Point) Decompress(leBuf [32]byte) (*Point, error) {
xb.ModInverse(xb, constants.Q) xb.ModInverse(xb, constants.Q)
p.X.Mul(xa, xb) // xa / xb p.X.Mul(xa, xb) // xa / xb
p.X.Mod(p.X, constants.Q) p.X.Mod(p.X, constants.Q)
p.X.ModSqrt(p.X, constants.Q) noSqrt := p.X.ModSqrt(p.X, constants.Q)
if noSqrt == nil {
return nil, fmt.Errorf("x is not a square mod q")
}
if (sign && !PointCoordSign(p.X)) || (!sign && PointCoordSign(p.X)) { if (sign && !PointCoordSign(p.X)) || (!sign && PointCoordSign(p.X)) {
p.X.Mul(p.X, constants.MinusOne) p.X.Mul(p.X, constants.MinusOne)
} }

View File

@@ -4,6 +4,7 @@ import (
"crypto/rand" "crypto/rand"
"github.com/iden3/go-iden3-crypto/mimc7" "github.com/iden3/go-iden3-crypto/mimc7"
"github.com/iden3/go-iden3-crypto/poseidon"
"github.com/iden3/go-iden3-crypto/utils" "github.com/iden3/go-iden3-crypto/utils"
"math/big" "math/big"
@@ -179,11 +180,11 @@ func (k *PrivateKey) SignMimc7(msg *big.Int) *Signature {
r.Mod(r, SubOrder) r.Mod(r, SubOrder)
R8 := NewPoint().Mul(r, B8) // R8 = r * 8 * B R8 := NewPoint().Mul(r, B8) // R8 = r * 8 * B
A := k.Public().Point() A := k.Public().Point()
hmInput, err := mimc7.BigIntsToRElems([]*big.Int{R8.X, R8.Y, A.X, A.Y, msg}) hmInput := []*big.Int{R8.X, R8.Y, A.X, A.Y, msg}
hm, err := mimc7.Hash(hmInput, nil) // hm = H1(8*R.x, 8*R.y, A.x, A.y, msg)
if err != nil { if err != nil {
panic(err) panic(err)
} }
hm := mimc7.Hash(hmInput, nil) // hm = H1(8*R.x, 8*R.y, A.x, A.y, msg)
S := new(big.Int).Lsh(k.Scalar().BigInt(), 3) S := new(big.Int).Lsh(k.Scalar().BigInt(), 3)
S = S.Mul(hm, S) S = S.Mul(hm, S)
S.Add(r, S) S.Add(r, S)
@@ -195,11 +196,53 @@ func (k *PrivateKey) SignMimc7(msg *big.Int) *Signature {
// VerifyMimc7 verifies the signature of a message encoded as a big.Int in Zq // VerifyMimc7 verifies the signature of a message encoded as a big.Int in Zq
// using blake-512 hash for buffer hashing and mimc7 for big.Int hashing. // using blake-512 hash for buffer hashing and mimc7 for big.Int hashing.
func (p *PublicKey) VerifyMimc7(msg *big.Int, sig *Signature) bool { func (p *PublicKey) VerifyMimc7(msg *big.Int, sig *Signature) bool {
hmInput, err := mimc7.BigIntsToRElems([]*big.Int{sig.R8.X, sig.R8.Y, p.X, p.Y, msg}) hmInput := []*big.Int{sig.R8.X, sig.R8.Y, p.X, p.Y, msg}
hm, err := mimc7.Hash(hmInput, nil) // hm = H1(8*R.x, 8*R.y, A.x, A.y, msg)
if err != nil {
panic(err)
}
left := NewPoint().Mul(sig.S, B8) // left = s * 8 * B
r1 := big.NewInt(8)
r1.Mul(r1, hm)
right := NewPoint().Mul(r1, p.Point())
right.Add(sig.R8, right) // right = 8 * R + 8 * hm * A
return (left.X.Cmp(right.X) == 0) && (left.Y.Cmp(right.Y) == 0)
}
// SignPoseidon signs a message encoded as a big.Int in Zq using blake-512 hash
// for buffer hashing and Poseidon for big.Int hashing.
func (k *PrivateKey) SignPoseidon(msg *big.Int) *Signature {
h1 := Blake512(k[:])
msgBuf := utils.BigIntLEBytes(msg)
msgBuf32 := [32]byte{}
copy(msgBuf32[:], msgBuf[:])
rBuf := Blake512(append(h1[32:], msgBuf32[:]...))
r := utils.SetBigIntFromLEBytes(new(big.Int), rBuf) // r = H(H_{32..63}(k), msg)
r.Mod(r, SubOrder)
R8 := NewPoint().Mul(r, B8) // R8 = r * 8 * B
A := k.Public().Point()
hmInput := []*big.Int{R8.X, R8.Y, A.X, A.Y, msg}
hm, err := poseidon.Hash(hmInput) // hm = H1(8*R.x, 8*R.y, A.x, A.y, msg)
if err != nil {
panic(err)
}
S := new(big.Int).Lsh(k.Scalar().BigInt(), 3)
S = S.Mul(hm, S)
S.Add(r, S)
S.Mod(S, SubOrder) // S = r + hm * 8 * s
return &Signature{R8: R8, S: S}
}
// VerifyPoseidon verifies the signature of a message encoded as a big.Int in Zq
// using blake-512 hash for buffer hashing and Poseidon for big.Int hashing.
func (p *PublicKey) VerifyPoseidon(msg *big.Int, sig *Signature) bool {
hmInput := []*big.Int{sig.R8.X, sig.R8.Y, p.X, p.Y, msg}
hm, err := poseidon.Hash(hmInput) // hm = H1(8*R.x, 8*R.y, A.x, A.y, msg)
if err != nil { if err != nil {
panic(err) panic(err)
} }
hm := mimc7.Hash(hmInput, nil) // hm = H1(8*R.x, 8*R.y, A.x, A.y, msg)
left := NewPoint().Mul(sig.S, B8) // left = s * 8 * B left := NewPoint().Mul(sig.S, B8) // left = s * 8 * B
r1 := big.NewInt(8) r1 := big.NewInt(8)

View File

@@ -4,13 +4,12 @@ import (
"crypto/rand" "crypto/rand"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"math/big"
"testing"
"github.com/iden3/go-iden3-crypto/constants" "github.com/iden3/go-iden3-crypto/constants"
"github.com/iden3/go-iden3-crypto/utils" "github.com/iden3/go-iden3-crypto/utils"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"math/big"
"testing"
) )
func genInputs() (*PrivateKey, *big.Int) { func genInputs() (*PrivateKey, *big.Int) {
@@ -26,7 +25,7 @@ func genInputs() (*PrivateKey, *big.Int) {
return &k, msg return &k, msg
} }
func TestSignVerify1(t *testing.T) { func TestSignVerifyMimc7(t *testing.T) {
var k PrivateKey var k PrivateKey
hex.Decode(k[:], []byte("0001020304050607080900010203040506070809000102030405060708090001")) hex.Decode(k[:], []byte("0001020304050607080900010203040506070809000102030405060708090001"))
msgBuf, err := hex.DecodeString("00010203040506070809") msgBuf, err := hex.DecodeString("00010203040506070809")
@@ -37,21 +36,21 @@ func TestSignVerify1(t *testing.T) {
pk := k.Public() pk := k.Public()
assert.Equal(t, assert.Equal(t,
"2610057752638682202795145288373380503107623443963127956230801721756904484787", "13277427435165878497778222415993513565335242147425444199013288855685581939618",
pk.X.String()) pk.X.String())
assert.Equal(t, assert.Equal(t,
"16617171478497210597712478520507818259149717466230047843969353176573634386897", "13622229784656158136036771217484571176836296686641868549125388198837476602820",
pk.Y.String()) pk.Y.String())
sig := k.SignMimc7(msg) sig := k.SignMimc7(msg)
assert.Equal(t, assert.Equal(t,
"4974729414807584049518234760796200867685098748448054182902488636762478901554", "11384336176656855268977457483345535180380036354188103142384839473266348197733",
sig.R8.X.String()) sig.R8.X.String())
assert.Equal(t, assert.Equal(t,
"18714049394522540751536514815950425694461287643205706667341348804546050128733", "15383486972088797283337779941324724402501462225528836549661220478783371668959",
sig.R8.Y.String()) sig.R8.Y.String())
assert.Equal(t, assert.Equal(t,
"2171284143457722024136077617757713039502332290425057126942676527240038689549", "2523202440825208709475937830811065542425109372212752003460238913256192595070",
sig.S.String()) sig.S.String())
ok := pk.VerifyMimc7(msg, sig) ok := pk.VerifyMimc7(msg, sig)
@@ -62,14 +61,58 @@ func TestSignVerify1(t *testing.T) {
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
assert.Equal(t, ""+ assert.Equal(t, ""+
"5dfb6f843c023fe3e52548ccf22e55c81b426f7af81b4f51f7152f2fcfc65f29"+ "dfedb4315d3f2eb4de2d3c510d7a987dcab67089c8ace06308827bf5bcbe02a2"+
"0dab19c5a0a75973cd75a54780de0c3a41ede6f57396fe99b5307fff3ce7cc04", "7ed40dab29bf993c928e789d007387998901a24913d44fddb64b1f21fc149405",
hex.EncodeToString(sigBuf[:])) hex.EncodeToString(sigBuf[:]))
ok = pk.VerifyMimc7(msg, sig2) ok = pk.VerifyMimc7(msg, sig2)
assert.Equal(t, true, ok) assert.Equal(t, true, ok)
} }
func TestSignVerifyPoseidon(t *testing.T) {
var k PrivateKey
hex.Decode(k[:], []byte("0001020304050607080900010203040506070809000102030405060708090001"))
msgBuf, err := hex.DecodeString("00010203040506070809")
if err != nil {
panic(err)
}
msg := utils.SetBigIntFromLEBytes(new(big.Int), msgBuf)
pk := k.Public()
assert.Equal(t,
"13277427435165878497778222415993513565335242147425444199013288855685581939618",
pk.X.String())
assert.Equal(t,
"13622229784656158136036771217484571176836296686641868549125388198837476602820",
pk.Y.String())
sig := k.SignPoseidon(msg)
assert.Equal(t,
"11384336176656855268977457483345535180380036354188103142384839473266348197733",
sig.R8.X.String())
assert.Equal(t,
"15383486972088797283337779941324724402501462225528836549661220478783371668959",
sig.R8.Y.String())
assert.Equal(t,
"248298168863866362217836334079793350221620631973732197668910946177382043688",
sig.S.String())
ok := pk.VerifyPoseidon(msg, sig)
assert.Equal(t, true, ok)
sigBuf := sig.Compress()
sig2, err := new(Signature).Decompress(sigBuf)
assert.Equal(t, nil, err)
assert.Equal(t, ""+
"dfedb4315d3f2eb4de2d3c510d7a987dcab67089c8ace06308827bf5bcbe02a2"+
"28506bce274aa1b3f7e7c2fd7e4fe09bff8f9aa37a42def7994e98f322888c00",
hex.EncodeToString(sigBuf[:]))
ok = pk.VerifyPoseidon(msg, sig2)
assert.Equal(t, true, ok)
}
func TestCompressDecompress(t *testing.T) { func TestCompressDecompress(t *testing.T) {
var k PrivateKey var k PrivateKey
hex.Decode(k[:], []byte("0001020304050607080900010203040506070809000102030405060708090001")) hex.Decode(k[:], []byte("0001020304050607080900010203040506070809000102030405060708090001"))

View File

@@ -2,19 +2,16 @@ package mimc7
import ( import (
"errors" "errors"
"fmt"
"math/big" "math/big"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
_constants "github.com/iden3/go-iden3-crypto/constants" _constants "github.com/iden3/go-iden3-crypto/constants"
"github.com/iden3/go-iden3-crypto/field" "github.com/iden3/go-iden3-crypto/field"
"github.com/iden3/go-iden3-crypto/utils"
) )
const SEED = "mimc" const SEED = "mimc"
// RElem is a big.Int of maximum 253 bits
type RElem *big.Int
var constants = generateConstantsData() var constants = generateConstantsData()
type constantsData struct { type constantsData struct {
@@ -26,9 +23,6 @@ type constantsData struct {
cts []*big.Int cts []*big.Int
} }
func getIV(seed string) {
}
func generateConstantsData() constantsData { func generateConstantsData() constantsData {
var constants constantsData var constants constantsData
@@ -43,45 +37,12 @@ func generateConstantsData() constantsData {
constants.iv = new(big.Int).Mod(c, constants.maxFieldVal) constants.iv = new(big.Int).Mod(c, constants.maxFieldVal)
constants.nRounds = 91 constants.nRounds = 91
cts, err := getConstants(constants.fqR, SEED, constants.nRounds) cts := getConstants(constants.fqR, SEED, constants.nRounds)
if err != nil {
panic(err)
}
constants.cts = cts constants.cts = cts
return constants return constants
} }
// BigIntToRElem checks if given big.Int fits in a Field R element, and returns the RElem type func getConstants(fqR field.Fq, seed string, nRounds int) []*big.Int {
func BigIntToRElem(a *big.Int) (RElem, error) {
if a.Cmp(constants.maxFieldVal) != -1 {
return RElem(a), errors.New("Given big.Int don't fits in the Finite Field over R")
}
return RElem(a), nil
}
//BigIntsToRElems converts from array of *big.Int to array of RElem
func BigIntsToRElems(arr []*big.Int) ([]RElem, error) {
o := make([]RElem, len(arr))
for i, a := range arr {
e, err := BigIntToRElem(a)
if err != nil {
return o, fmt.Errorf("element in position %v don't fits in Finite Field over R", i)
}
o[i] = e
}
return o, nil
}
// RElemsToBigInts converts from array of RElem to array of *big.Int
func RElemsToBigInts(arr []RElem) []*big.Int {
o := make([]*big.Int, len(arr))
for i, a := range arr {
o[i] = a
}
return o
}
func getConstants(fqR field.Fq, seed string, nRounds int) ([]*big.Int, error) {
cts := make([]*big.Int, nRounds) cts := make([]*big.Int, nRounds)
cts[0] = big.NewInt(int64(0)) cts[0] = big.NewInt(int64(0))
c := new(big.Int).SetBytes(crypto.Keccak256([]byte(SEED))) c := new(big.Int).SetBytes(crypto.Keccak256([]byte(SEED)))
@@ -91,15 +52,12 @@ func getConstants(fqR field.Fq, seed string, nRounds int) ([]*big.Int, error) {
n := fqR.Affine(c) n := fqR.Affine(c)
cts[i] = n cts[i] = n
} }
return cts, nil return cts
} }
// MIMC7HashGeneric performs the MIMC7 hash over a RElem, in a generic way, where it can be specified the Finite Field over R, and the number of rounds // MIMC7HashGeneric performs the MIMC7 hash over a *big.Int, in a generic way, where it can be specified the Finite Field over R, and the number of rounds
func MIMC7HashGeneric(fqR field.Fq, xIn, k *big.Int, nRounds int) (*big.Int, error) { func MIMC7HashGeneric(fqR field.Fq, xIn, k *big.Int, nRounds int) *big.Int {
cts, err := getConstants(fqR, SEED, nRounds) cts := getConstants(fqR, SEED, nRounds)
if err != nil {
return &big.Int{}, err
}
var r *big.Int var r *big.Int
for i := 0; i < nRounds; i++ { for i := 0; i < nRounds; i++ {
var t *big.Int var t *big.Int
@@ -112,24 +70,26 @@ func MIMC7HashGeneric(fqR field.Fq, xIn, k *big.Int, nRounds int) (*big.Int, err
t4 := fqR.Square(t2) t4 := fqR.Square(t2)
r = fqR.Mul(fqR.Mul(t4, t2), t) r = fqR.Mul(fqR.Mul(t4, t2), t)
} }
return fqR.Affine(fqR.Add(r, k)), nil return fqR.Affine(fqR.Add(r, k))
} }
// HashGeneric performs the MIMC7 hash over a RElem array, in a generic way, where it can be specified the Finite Field over R, and the number of rounds // HashGeneric performs the MIMC7 hash over a *big.Int array, in a generic way, where it can be specified the Finite Field over R, and the number of rounds
func HashGeneric(iv *big.Int, arrEl []RElem, fqR field.Fq, nRounds int) (RElem, error) { func HashGeneric(iv *big.Int, arr []*big.Int, fqR field.Fq, nRounds int) (*big.Int, error) {
arr := RElemsToBigInts(arrEl) if !utils.CheckBigIntArrayInField(arr, constants.fqR.Q) {
return nil, errors.New("inputs values not inside Finite Field")
}
r := iv r := iv
var err error var err error
for i := 0; i < len(arr); i++ { for i := 0; i < len(arr); i++ {
r, err = MIMC7HashGeneric(fqR, r, arr[i], nRounds) r = MIMC7HashGeneric(fqR, r, arr[i], nRounds)
if err != nil { if err != nil {
return r, err return r, err
} }
} }
return RElem(r), nil return r, nil
} }
// MIMC7Hash performs the MIMC7 hash over a RElem, using the Finite Field over R and the number of rounds setted in the `constants` variable // MIMC7Hash performs the MIMC7 hash over a *big.Int, using the Finite Field over R and the number of rounds setted in the `constants` variable
func MIMC7Hash(xIn, k *big.Int) *big.Int { func MIMC7Hash(xIn, k *big.Int) *big.Int {
var r *big.Int var r *big.Int
for i := 0; i < constants.nRounds; i++ { for i := 0; i < constants.nRounds; i++ {
@@ -146,16 +106,17 @@ func MIMC7Hash(xIn, k *big.Int) *big.Int {
return constants.fqR.Affine(constants.fqR.Add(r, k)) return constants.fqR.Affine(constants.fqR.Add(r, k))
} }
// Hash performs the MIMC7 hash over a RElem array // Hash performs the MIMC7 hash over a *big.Int array
func Hash(arrEl []RElem, key *big.Int) RElem { func Hash(arr []*big.Int, key *big.Int) (*big.Int, error) {
arr := RElemsToBigInts(arrEl) if !utils.CheckBigIntArrayInField(arr, constants.fqR.Q) {
return nil, errors.New("inputs values not inside Finite Field")
}
var r *big.Int var r *big.Int
if key == nil { if key == nil {
r = constants.fqR.Zero() r = constants.fqR.Zero()
} else { } else {
r = key r = key
} }
// r := constants.iv
for i := 0; i < len(arr); i++ { for i := 0; i < len(arr); i++ {
r = constants.fqR.Add( r = constants.fqR.Add(
constants.fqR.Add( constants.fqR.Add(
@@ -164,5 +125,23 @@ func Hash(arrEl []RElem, key *big.Int) RElem {
), ),
MIMC7Hash(arr[i], r)) MIMC7Hash(arr[i], r))
} }
return RElem(r) return r, nil
}
// HashBytes hashes a msg byte slice by blocks of 31 bytes encoded as
// little-endian
func HashBytes(b []byte) (*big.Int, error) {
n := 31
bElems := make([]*big.Int, 0, len(b)/n+1)
for i := 0; i < len(b)/n; i++ {
v := new(big.Int)
utils.SetBigIntFromLEBytes(v, b[n*i:n*(i+1)])
bElems = append(bElems, v)
}
if len(b)%n != 0 {
v := new(big.Int)
utils.SetBigIntFromLEBytes(v, b[(len(b)/n)*n:])
bElems = append(bElems, v)
}
return Hash(bElems, nil)
} }

View File

@@ -5,42 +5,16 @@ import (
"math/big" "math/big"
"testing" "testing"
"github.com/ethereum/go-ethereum/crypto"
"github.com/iden3/go-iden3-crypto/field" "github.com/iden3/go-iden3-crypto/field"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestUtils(t *testing.T) { func TestKeccak256(t *testing.T) {
b1 := big.NewInt(int64(1)) res := crypto.Keccak256([]byte(SEED))
b2 := big.NewInt(int64(2)) assert.Equal(t, "b6e489e6b37224a50bebfddbe7d89fa8fdcaa84304a70bd13f79b5d9f7951e9e", hex.EncodeToString(res))
b3 := big.NewInt(int64(3)) c := new(big.Int).SetBytes(crypto.Keccak256([]byte(SEED)))
arrBigInt := []*big.Int{b1, b2, b3} assert.Equal(t, "82724731331859054037315113496710413141112897654334566532528783843265082629790", c.String())
// *big.Int array to RElem array
rElems, err := BigIntsToRElems(arrBigInt)
assert.Nil(t, err)
// RElem array to *big.Int array
bElems := RElemsToBigInts(rElems)
assert.Equal(t, arrBigInt, bElems)
r, ok := new(big.Int).SetString("21888242871839275222246405745257275088548364400416034343698204186575808495617", 10)
assert.True(t, ok)
// greater or equal than R will give error when passing bigInts to RElems, to fit in the R Finite Field
overR, ok := new(big.Int).SetString("21888242871839275222246405745257275088548364400416034343698204186575808495618", 10)
assert.True(t, ok)
_, err = BigIntsToRElems([]*big.Int{b1, overR, b2})
assert.True(t, err != nil)
_, err = BigIntsToRElems([]*big.Int{b1, r, b2})
assert.True(t, err != nil)
// smaller than R will not give error when passing bigInts to RElems, to fit in the R Finite Field
underR, ok := new(big.Int).SetString("21888242871839275222246405745257275088548364400416034343698204186575808495616", 10)
assert.True(t, ok)
_, err = BigIntsToRElems([]*big.Int{b1, underR, b2})
assert.Nil(t, err)
} }
func TestMIMC7Generic(t *testing.T) { func TestMIMC7Generic(t *testing.T) {
@@ -53,14 +27,11 @@ func TestMIMC7Generic(t *testing.T) {
fqR := field.NewFq(r) fqR := field.NewFq(r)
bigArray := []*big.Int{b1, b2, b3} bigArray := []*big.Int{b1, b2, b3}
elementsArray, err := BigIntsToRElems(bigArray)
assert.Nil(t, err)
// Generic Hash // Generic Hash
mhg, err := MIMC7HashGeneric(fqR, b1, b2, 91) mhg := MIMC7HashGeneric(fqR, b1, b2, 91)
assert.Nil(t, err)
assert.Equal(t, "10594780656576967754230020536574539122676596303354946869887184401991294982664", mhg.String()) assert.Equal(t, "10594780656576967754230020536574539122676596303354946869887184401991294982664", mhg.String())
hg, err := HashGeneric(fqR.Zero(), elementsArray, fqR, 91) hg, err := HashGeneric(fqR.Zero(), bigArray, fqR, 91)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, "6464402164086696096195815557694604139393321133243036833927490113253119343397", (*big.Int)(hg).String()) assert.Equal(t, "6464402164086696096195815557694604139393321133243036833927490113253119343397", (*big.Int)(hg).String())
} }
@@ -73,51 +44,44 @@ func TestMIMC7(t *testing.T) {
// h1, hash of 1 elements // h1, hash of 1 elements
bigArray1 := []*big.Int{b12} bigArray1 := []*big.Int{b12}
elementsArray1, err := BigIntsToRElems(bigArray1)
assert.Nil(t, err)
h1 := Hash(elementsArray1, nil) h1, err := Hash(bigArray1, nil)
assert.Nil(t, err) assert.Nil(t, err)
// same hash value than the iden3js and circomlib tests: // same hash value than the iden3js and circomlib tests:
assert.Equal(t, "0x"+hex.EncodeToString((*big.Int)(h1).Bytes()), "0x237c92644dbddb86d8a259e0e923aaab65a93f1ec5758b8799988894ac0958fd") assert.Equal(t, "0x"+hex.EncodeToString((*big.Int)(h1).Bytes()), "0x237c92644dbddb86d8a259e0e923aaab65a93f1ec5758b8799988894ac0958fd")
// h2a, hash of 2 elements // h2a, hash of 2 elements
bigArray2a := []*big.Int{b78, b41} bigArray2a := []*big.Int{b78, b41}
elementsArray2a, err := BigIntsToRElems(bigArray2a)
assert.Nil(t, err)
mh2a := MIMC7Hash(b12, b45) h2a, err := Hash(bigArray2a, nil)
assert.Nil(t, err)
assert.Equal(t, "0x"+hex.EncodeToString((*big.Int)(mh2a).Bytes()), "0x2ba7ebad3c6b6f5a20bdecba2333c63173ca1a5f2f49d958081d9fa7179c44e4")
h2a := Hash(elementsArray2a, nil)
assert.Nil(t, err) assert.Nil(t, err)
// same hash value than the iden3js and circomlib tests: // same hash value than the iden3js and circomlib tests:
assert.Equal(t, "0x"+hex.EncodeToString((*big.Int)(h2a).Bytes()), "0x067f3202335ea256ae6e6aadcd2d5f7f4b06a00b2d1e0de903980d5ab552dc70") assert.Equal(t, "0x"+hex.EncodeToString((*big.Int)(h2a).Bytes()), "0x067f3202335ea256ae6e6aadcd2d5f7f4b06a00b2d1e0de903980d5ab552dc70")
// h2b, hash of 2 elements // h2b, hash of 2 elements
bigArray2b := []*big.Int{b12, b45} bigArray2b := []*big.Int{b12, b45}
elementsArray2b, err := BigIntsToRElems(bigArray2b)
assert.Nil(t, err)
mh2b := MIMC7Hash(b12, b45) mh2b := MIMC7Hash(b12, b45)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, "0x"+hex.EncodeToString((*big.Int)(mh2b).Bytes()), "0x2ba7ebad3c6b6f5a20bdecba2333c63173ca1a5f2f49d958081d9fa7179c44e4") assert.Equal(t, "0x"+hex.EncodeToString((*big.Int)(mh2b).Bytes()), "0x2ba7ebad3c6b6f5a20bdecba2333c63173ca1a5f2f49d958081d9fa7179c44e4")
h2b := Hash(elementsArray2b, nil) h2b, err := Hash(bigArray2b, nil)
assert.Nil(t, err) assert.Nil(t, err)
// same hash value than the iden3js and circomlib tests: // same hash value than the iden3js and circomlib tests:
assert.Equal(t, "0x"+hex.EncodeToString((*big.Int)(h2b).Bytes()), "0x15ff7fe9793346a17c3150804bcb36d161c8662b110c50f55ccb7113948d8879") assert.Equal(t, "0x"+hex.EncodeToString((*big.Int)(h2b).Bytes()), "0x15ff7fe9793346a17c3150804bcb36d161c8662b110c50f55ccb7113948d8879")
// h4, hash of 4 elements // h4, hash of 4 elements
bigArray4 := []*big.Int{b12, b45, b78, b41} bigArray4 := []*big.Int{b12, b45, b78, b41}
elementsArray4, err := BigIntsToRElems(bigArray4)
assert.Nil(t, err)
h4 := Hash(elementsArray4, nil) h4, err := Hash(bigArray4, nil)
assert.Nil(t, err) assert.Nil(t, err)
// same hash value than the iden3js and circomlib tests: // same hash value than the iden3js and circomlib tests:
assert.Equal(t, "0x"+hex.EncodeToString((*big.Int)(h4).Bytes()), "0x284bc1f34f335933a23a433b6ff3ee179d682cd5e5e2fcdd2d964afa85104beb") assert.Equal(t, "0x"+hex.EncodeToString((*big.Int)(h4).Bytes()), "0x284bc1f34f335933a23a433b6ff3ee179d682cd5e5e2fcdd2d964afa85104beb")
msg := []byte("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")
hmsg, err := HashBytes(msg)
assert.Nil(t, err)
assert.Equal(t, "16855787120419064316734350414336285711017110414939748784029922801367685456065", hmsg.String())
} }
func BenchmarkMIMC7(b *testing.B) { func BenchmarkMIMC7(b *testing.B) {
@@ -126,12 +90,10 @@ func BenchmarkMIMC7(b *testing.B) {
b78 := big.NewInt(int64(78)) b78 := big.NewInt(int64(78))
b41 := big.NewInt(int64(41)) b41 := big.NewInt(int64(41))
bigArray4 := []*big.Int{b12, b45, b78, b41} bigArray4 := []*big.Int{b12, b45, b78, b41}
elementsArray4, err := BigIntsToRElems(bigArray4)
assert.Nil(b, err)
var h4 RElem var h4 *big.Int
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
h4 = Hash(elementsArray4, nil) h4, _ = Hash(bigArray4, nil)
} }
println(h4) println(h4)
} }

View File

@@ -107,12 +107,13 @@ func ark(state []*big.Int, c *big.Int) []*big.Int {
return state return state
} }
// cubic performs x^3 mod p // cubic performs x^5 mod p
// https://eprint.iacr.org/2019/458.pdf page 8
func cubic(a *big.Int) *big.Int { func cubic(a *big.Int) *big.Int {
return constants.fqR.Mul(a, constants.fqR.Square(constants.fqR.Square(a))) return constants.fqR.Mul(a, constants.fqR.Square(constants.fqR.Square(a)))
} }
// sbox https://eprint.iacr.org/2019/458.pdf pag.6 // sbox https://eprint.iacr.org/2019/458.pdf page 6
func sbox(state []*big.Int, i int) []*big.Int { func sbox(state []*big.Int, i int) []*big.Int {
if (i < NROUNDSF/2) || (i >= NROUNDSF/2+NROUNDSP) { if (i < NROUNDSF/2) || (i >= NROUNDSF/2+NROUNDSP) {
for j := 0; j < T; j++ { for j := 0; j < T; j++ {
@@ -133,25 +134,18 @@ func mix(state []*big.Int, m [][]*big.Int) []*big.Int {
newState[i] = constants.fqR.Add(newState[i], constants.fqR.Mul(m[i][j], state[j])) newState[i] = constants.fqR.Add(newState[i], constants.fqR.Mul(m[i][j], state[j]))
} }
} }
for i := 0; i < len(state); i++ { return newState
state[i] = newState[i]
}
return state
} }
// Hash computes the Poseidon hash for the given inputs // PoseidonHash computes the Poseidon hash for the given inputs
func Hash(inp []*big.Int) (*big.Int, error) { func PoseidonHash(inp []*big.Int) (*big.Int, error) {
var state []*big.Int
if len(inp) == 0 || len(inp) > T { if len(inp) == 0 || len(inp) > T {
return nil, errors.New("wrong inputs length") return nil, errors.New("wrong inputs length")
} }
if !utils.CheckBigIntArrayInField(inp, constants.fqR.Q) { if !utils.CheckBigIntArrayInField(inp, constants.fqR.Q) {
return nil, errors.New("inputs values not inside Finite Field") return nil, errors.New("inputs values not inside Finite Field")
} }
state := inp
for i := 0; i < len(inp); i++ {
state = append(state, inp[i])
}
for i := len(inp); i < T; i++ { for i := len(inp); i < T; i++ {
state = append(state, constants.fqR.Zero()) state = append(state, constants.fqR.Zero())
} }
@@ -164,3 +158,50 @@ func Hash(inp []*big.Int) (*big.Int, error) {
} }
return state[0], nil return state[0], nil
} }
// Hash performs the Poseidon hash over a *big.Int array
// in chunks of 5 elements
func Hash(arr []*big.Int) (*big.Int, error) {
if !utils.CheckBigIntArrayInField(arr, constants.fqR.Q) {
return nil, errors.New("inputs values not inside Finite Field")
}
r := constants.fqR.Zero()
for i := 0; i < len(arr); i = i + 5 {
var fiveElems []*big.Int
for j := 0; j < 5; j++ {
if i+j < len(arr) {
fiveElems = append(fiveElems, arr[i+j])
} else {
fiveElems = append(fiveElems, big.NewInt(int64(0)))
}
}
ph, err := PoseidonHash(fiveElems)
if err != nil {
return nil, err
}
r = constants.fqR.Add(
r,
ph)
}
return r, nil
}
// HashBytes hashes a msg byte slice by blocks of 31 bytes encoded as
// little-endian
func HashBytes(b []byte) (*big.Int, error) {
n := 31
bElems := make([]*big.Int, 0, len(b)/n+1)
for i := 0; i < len(b)/n; i++ {
v := new(big.Int)
utils.SetBigIntFromLEBytes(v, b[n*i:n*(i+1)])
bElems = append(bElems, v)
}
if len(b)%n != 0 {
v := new(big.Int)
utils.SetBigIntFromLEBytes(v, b[(len(b)/n)*n:])
bElems = append(bElems, v)
}
return Hash(bElems)
}

View File

@@ -5,6 +5,7 @@ import (
"math/big" "math/big"
"testing" "testing"
"github.com/iden3/go-iden3-crypto/utils"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"golang.org/x/crypto/blake2b" "golang.org/x/crypto/blake2b"
) )
@@ -26,4 +27,41 @@ func TestPoseidon(t *testing.T) {
h, err = Hash([]*big.Int{b3, b4}) h, err = Hash([]*big.Int{b3, b4})
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, "17185195740979599334254027721507328033796809509313949281114643312710535000993", h.String()) assert.Equal(t, "17185195740979599334254027721507328033796809509313949281114643312710535000993", h.String())
msg := []byte("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")
n := 31
msgElems := make([]*big.Int, 0, len(msg)/n+1)
for i := 0; i < len(msg)/n; i++ {
v := new(big.Int)
utils.SetBigIntFromLEBytes(v, msg[n*i:n*(i+1)])
msgElems = append(msgElems, v)
}
if len(msg)%n != 0 {
v := new(big.Int)
utils.SetBigIntFromLEBytes(v, msg[(len(msg)/n)*n:])
msgElems = append(msgElems, v)
}
hmsg, err := Hash(msgElems)
assert.Nil(t, err)
assert.Equal(t, "11821124228916291136371255062457365369197326845706357273715164664419275913793", hmsg.String())
msg2 := []byte("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet.")
msg2Elems := make([]*big.Int, 0, len(msg2)/n+1)
for i := 0; i < len(msg2)/n; i++ {
v := new(big.Int)
utils.SetBigIntFromLEBytes(v, msg2[n*i:n*(i+1)])
msg2Elems = append(msg2Elems, v)
}
if len(msg2)%n != 0 {
v := new(big.Int)
utils.SetBigIntFromLEBytes(v, msg2[(len(msg2)/n)*n:])
msg2Elems = append(msg2Elems, v)
}
hmsg2, err := Hash(msg2Elems)
assert.Nil(t, err)
assert.Equal(t, "10747013384255785702102976082726575658403084163954725275481577373644732938016", hmsg2.String())
hmsg2, err = HashBytes(msg2)
assert.Nil(t, err)
assert.Equal(t, "10747013384255785702102976082726575658403084163954725275481577373644732938016", hmsg2.String())
} }