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}