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