arnaucube
arnaucube/go-iden3-cryptoPublic Self-hosted repository
go-iden3-crypto/mimc7/mimc7.go
mimc7/mimc7.goRaw
  1package mimc7
  2
  3import (
  4	"errors"
  5	"fmt"
  6	"math/big"
  7
  8	"github.com/ethereum/go-ethereum/crypto"
  9	_constants "github.com/iden3/go-iden3-crypto/constants"
 10	"github.com/iden3/go-iden3-crypto/field"
 11)
 12
 13const SEED = "mimc"
 14
 15// RElem is a big.Int of maximum 253 bits
 16type RElem *big.Int
 17
 18var constants = generateConstantsData()
 19
 20type constantsData struct {
 21	maxFieldVal *big.Int
 22	seedHash    *big.Int
 23	iv          *big.Int
 24	fqR         field.Fq
 25	nRounds     int
 26	cts         []*big.Int
 27}
 28
 29func getIV(seed string) {
 30}
 31
 32func generateConstantsData() constantsData {
 33	var constants constantsData
 34
 35	fqR := field.NewFq(_constants.Q)
 36	constants.fqR = fqR
 37
 38	// maxFieldVal is the R value of the Finite Field
 39	constants.maxFieldVal = constants.fqR.Q
 40
 41	constants.seedHash = new(big.Int).SetBytes(crypto.Keccak256([]byte(SEED)))
 42	c := new(big.Int).SetBytes(crypto.Keccak256([]byte(SEED + "_iv")))
 43	constants.iv = new(big.Int).Mod(c, constants.maxFieldVal)
 44
 45	constants.nRounds = 91
 46	cts, err := getConstants(constants.fqR, SEED, constants.nRounds)
 47	if err != nil {
 48		panic(err)
 49	}
 50	constants.cts = cts
 51	return constants
 52}
 53
 54// BigIntToRElem checks if given big.Int fits in a Field R element, and returns the RElem type
 55func BigIntToRElem(a *big.Int) (RElem, error) {
 56	if a.Cmp(constants.maxFieldVal) != -1 {
 57		return RElem(a), errors.New("Given big.Int don't fits in the Finite Field over R")
 58	}
 59	return RElem(a), nil
 60}
 61
 62//BigIntsToRElems converts from array of *big.Int to array of RElem
 63func BigIntsToRElems(arr []*big.Int) ([]RElem, error) {
 64	o := make([]RElem, len(arr))
 65	for i, a := range arr {
 66		e, err := BigIntToRElem(a)
 67		if err != nil {
 68			return o, fmt.Errorf("element in position %v don't fits in Finite Field over R", i)
 69		}
 70		o[i] = e
 71	}
 72	return o, nil
 73}
 74
 75// RElemsToBigInts converts from array of RElem to array of *big.Int
 76func RElemsToBigInts(arr []RElem) []*big.Int {
 77	o := make([]*big.Int, len(arr))
 78	for i, a := range arr {
 79		o[i] = a
 80	}
 81	return o
 82}
 83
 84func getConstants(fqR field.Fq, seed string, nRounds int) ([]*big.Int, error) {
 85	cts := make([]*big.Int, nRounds)
 86	cts[0] = big.NewInt(int64(0))
 87	c := new(big.Int).SetBytes(crypto.Keccak256([]byte(SEED)))
 88	for i := 1; i < nRounds; i++ {
 89		c = new(big.Int).SetBytes(crypto.Keccak256(c.Bytes()))
 90
 91		n := fqR.Affine(c)
 92		cts[i] = n
 93	}
 94	return cts, nil
 95}
 96
 97// 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
 98func MIMC7HashGeneric(fqR field.Fq, xIn, k *big.Int, nRounds int) (*big.Int, error) {
 99	cts, err := getConstants(fqR, SEED, nRounds)
100	if err != nil {
101		return &big.Int{}, err
102	}
103	var r *big.Int
104	for i := 0; i < nRounds; i++ {
105		var t *big.Int
106		if i == 0 {
107			t = fqR.Add(xIn, k)
108		} else {
109			t = fqR.Add(fqR.Add(r, k), cts[i])
110		}
111		t2 := fqR.Square(t)
112		t4 := fqR.Square(t2)
113		r = fqR.Mul(fqR.Mul(t4, t2), t)
114	}
115	return fqR.Affine(fqR.Add(r, k)), nil
116}
117
118// 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
119func HashGeneric(iv *big.Int, arrEl []RElem, fqR field.Fq, nRounds int) (RElem, error) {
120	arr := RElemsToBigInts(arrEl)
121	r := iv
122	var err error
123	for i := 0; i < len(arr); i++ {
124		r, err = MIMC7HashGeneric(fqR, r, arr[i], nRounds)
125		if err != nil {
126			return r, err
127		}
128	}
129	return RElem(r), nil
130}
131
132// MIMC7Hash performs the MIMC7 hash over a RElem, using the Finite Field over R and the number of rounds setted in the `constants` variable
133func MIMC7Hash(xIn, k *big.Int) *big.Int {
134	var r *big.Int
135	for i := 0; i < constants.nRounds; i++ {
136		var t *big.Int
137		if i == 0 {
138			t = constants.fqR.Add(xIn, k)
139		} else {
140			t = constants.fqR.Add(constants.fqR.Add(r, k), constants.cts[i])
141		}
142		t2 := constants.fqR.Square(t)
143		t4 := constants.fqR.Square(t2)
144		r = constants.fqR.Mul(constants.fqR.Mul(t4, t2), t)
145	}
146	return constants.fqR.Affine(constants.fqR.Add(r, k))
147}
148
149// Hash performs the MIMC7 hash over a RElem array
150func Hash(arrEl []RElem, key *big.Int) RElem {
151	arr := RElemsToBigInts(arrEl)
152	var r *big.Int
153	if key == nil {
154		r = constants.fqR.Zero()
155	} else {
156		r = key
157	}
158	// r := constants.iv
159	for i := 0; i < len(arr); i++ {
160		r = constants.fqR.Add(
161			constants.fqR.Add(
162				r,
163				arr[i],
164			),
165			MIMC7Hash(arr[i], r))
166	}
167	return RElem(r)
168}