github.com/consensys/gnark-crypto@v0.14.0/internal/generator/crypto/hash/mimc/template/mimc.go.tmpl (about) 1 import ( 2 "errors" 3 "hash" 4 5 "math/big" 6 "github.com/consensys/gnark-crypto/ecc/{{ .Name }}/fr" 7 "golang.org/x/crypto/sha3" 8 "sync" 9 ) 10 11 const ( 12 {{ if eq .Name "bn254" }} 13 mimcNbRounds = 110 14 {{- else if eq .Name "bls12-381"}} 15 mimcNbRounds = 111 16 {{- else if eq .Name "bls12-377"}} 17 mimcNbRounds = 62 18 {{- else if or (eq .Name "bls12-378") (eq .Name "bls24-315")}} 19 mimcNbRounds = 109 20 {{- else if eq .Name "bls24-317"}} 21 mimcNbRounds = 91 22 {{- else if eq .Name "bw6-633"}} 23 mimcNbRounds = 136 24 {{- else if or (eq .Name "bw6-761") (eq .Name "bw6-756")}} 25 mimcNbRounds = 163 26 {{- end}} 27 seed = "seed" // seed to derive the constants 28 BlockSize = fr.Bytes // BlockSize size that mimc consumes 29 ) 30 31 // Params constants for the mimc hash function 32 var ( 33 mimcConstants [mimcNbRounds]fr.Element 34 once sync.Once 35 ) 36 37 38 39 // digest represents the partial evaluation of the checksum 40 // along with the params of the mimc function 41 type digest struct { 42 h fr.Element 43 data []fr.Element // data to hash 44 byteOrder fr.ByteOrder 45 } 46 47 // GetConstants exposed to be used in gnark 48 func GetConstants() []big.Int { 49 once.Do(initConstants) // init constants 50 res := make([]big.Int, mimcNbRounds) 51 for i := 0; i < mimcNbRounds; i++ { 52 mimcConstants[i].BigInt(&res[i]) 53 } 54 return res 55 } 56 57 // NewMiMC returns a MiMCImpl object, pure-go reference implementation 58 func NewMiMC(opts ...Option) hash.Hash { 59 d := new(digest) 60 d.Reset() 61 cfg := mimcOptions(opts...) 62 d.byteOrder = cfg.byteOrder 63 return d 64 } 65 66 // Reset resets the Hash to its initial state. 67 func (d *digest) Reset() { 68 d.data = d.data[:0] 69 d.h = fr.Element{0, 0, 0, 0} 70 } 71 72 // Sum appends the current hash to b and returns the resulting slice. 73 // It does not change the underlying hash state. 74 func (d *digest) Sum(b []byte) []byte { 75 buffer := d.checksum() 76 d.data = nil // flush the data already hashed 77 hash := buffer.Bytes() 78 b = append(b, hash[:]...) 79 return b 80 } 81 82 // BlockSize returns the hash's underlying block size. 83 // The Write method must be able to accept any amount 84 // of data, but it may operate more efficiently if all writes 85 // are a multiple of the block size. 86 func (d *digest) Size() int { 87 return BlockSize 88 } 89 90 // BlockSize returns the number of bytes Sum will return. 91 func (d *digest) BlockSize() int { 92 return BlockSize 93 } 94 95 // Write (via the embedded io.Writer interface) adds more data to the running hash. 96 // 97 // Each []byte block of size BlockSize represents a big endian fr.Element. 98 // 99 // If len(p) is not a multiple of BlockSize and any of the []byte in p represent an integer 100 // larger than fr.Modulus, this function returns an error. 101 // 102 // To hash arbitrary data ([]byte not representing canonical field elements) use fr.Hash first 103 func (d *digest) Write(p []byte) (int, error) { 104 // we usually expect multiple of block size. But sometimes we hash short 105 // values (FS transcript). Instead of forcing to hash to field, we left-pad the 106 // input here. 107 if len(p) > 0 && len(p) < BlockSize { 108 pp := make([]byte,BlockSize) 109 copy(pp[len(pp)-len(p):], p) 110 p = pp 111 } 112 113 var start int 114 for start = 0; start < len(p); start += BlockSize { 115 if elem, err := d.byteOrder.Element((*[BlockSize]byte)(p[start:start+BlockSize])); err == nil { 116 d.data = append(d.data, elem) 117 } else { 118 return 0, err 119 } 120 } 121 122 if start != len(p) { 123 return 0, errors.New("invalid input length: must represent a list of field elements, expects a []byte of len m*BlockSize") 124 } 125 return len(p), nil 126 } 127 128 // Hash hash using Miyaguchi-Preneel: 129 // https://en.wikipedia.org/wiki/One-way_compression_function 130 // The XOR operation is replaced by field addition, data is in Montgomery form 131 func (d *digest) checksum() fr.Element { 132 // Write guarantees len(data) % BlockSize == 0 133 134 // TODO @ThomasPiellard shouldn't Sum() returns an error if there is no data? 135 // TODO: @Tabaie, @Thomas Piellard Now sure what to make of this 136 /*if len(d.data) == 0 { 137 d.data = make([]byte, BlockSize) 138 }*/ 139 140 for i := range d.data { 141 r := d.encrypt(d.data[i]) 142 d.h.Add(&r, &d.h).Add(&d.h, &d.data[i]) 143 } 144 145 return d.h 146 } 147 148 149 {{ if eq .Name "bls12-377" }} 150 // plain execution of a mimc run 151 // m: message 152 // k: encryption key 153 func (d *digest) encrypt(m fr.Element) fr.Element { 154 once.Do(initConstants) // init constants 155 156 var tmp fr.Element 157 for i:=0; i < mimcNbRounds; i++ { 158 // m = (m+k+c)^**17 159 tmp.Add(&m, &d.h).Add(&tmp, &mimcConstants[i]) 160 m.Square(&tmp). 161 Square(&m). 162 Square(&m). 163 Square(&m). 164 Mul(&m, &tmp) 165 } 166 m.Add(&m, &d.h) 167 return m 168 } 169 {{ else if eq .Name "bls24-317" }} 170 // plain execution of a mimc run 171 // m: message 172 // k: encryption key 173 func (d *digest) encrypt(m fr.Element) fr.Element { 174 once.Do(initConstants) // init constants 175 176 var tmp1, tmp2 fr.Element 177 for i := 0; i < mimcNbRounds; i++ { 178 // m = (m+k+c)^7 179 tmp1.Add(&m, &d.h).Add(&tmp1, &mimcConstants[i]) 180 tmp2.Square(&tmp1) 181 m.Square(&tmp2). 182 Mul(&m, &tmp2). 183 Mul(&m, &tmp1) 184 } 185 186 m.Add(&m, &d.h) 187 return m 188 } 189 {{ else }} 190 // plain execution of a mimc run 191 // m: message 192 // k: encryption key 193 func (d *digest) encrypt(m fr.Element) fr.Element { 194 once.Do(initConstants) // init constants 195 196 var tmp fr.Element 197 for i := 0; i < mimcNbRounds; i++ { 198 // m = (m+k+c)^5 199 tmp.Add(&m, &d.h).Add(&tmp, &mimcConstants[i]) 200 m.Square(&tmp). 201 Square(&m). 202 Mul(&m, &tmp) 203 } 204 m.Add(&m, &d.h) 205 return m 206 } 207 {{end}} 208 209 // Sum computes the mimc hash of msg from seed 210 func Sum(msg []byte) ([]byte, error) { 211 var d digest 212 if _, err := d.Write(msg); err != nil { 213 return nil, err 214 } 215 h := d.checksum() 216 bytes := h.Bytes() 217 return bytes[:], nil 218 } 219 220 221 func initConstants() { 222 bseed := ([]byte)(seed) 223 224 hash := sha3.NewLegacyKeccak256() 225 _, _ = hash.Write(bseed) 226 rnd := hash.Sum(nil) // pre hash before use 227 hash.Reset() 228 _, _ = hash.Write(rnd) 229 230 for i := 0; i < mimcNbRounds; i++ { 231 rnd = hash.Sum(nil) 232 mimcConstants[i].SetBytes(rnd) 233 hash.Reset() 234 _, _ = hash.Write(rnd) 235 } 236 } 237 238 // WriteString writes a string that doesn't necessarily consist of field elements 239 func (d *digest) WriteString(rawBytes []byte) error { 240 if elems, err := fr.Hash(rawBytes, []byte("string:"), 1); err != nil { 241 return err 242 } else { 243 d.data = append(d.data, elems[0]) 244 } 245 return nil 246 }