github.com/consensys/gnark-crypto@v0.14.0/ecc/bw6-756/fr/mimc/mimc.go (about) 1 // Copyright 2020 Consensys Software Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Code generated by consensys/gnark-crypto DO NOT EDIT 16 17 package mimc 18 19 import ( 20 "errors" 21 "hash" 22 23 "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" 24 "golang.org/x/crypto/sha3" 25 "math/big" 26 "sync" 27 ) 28 29 const ( 30 mimcNbRounds = 163 31 seed = "seed" // seed to derive the constants 32 BlockSize = fr.Bytes // BlockSize size that mimc consumes 33 ) 34 35 // Params constants for the mimc hash function 36 var ( 37 mimcConstants [mimcNbRounds]fr.Element 38 once sync.Once 39 ) 40 41 // digest represents the partial evaluation of the checksum 42 // along with the params of the mimc function 43 type digest struct { 44 h fr.Element 45 data []fr.Element // data to hash 46 byteOrder fr.ByteOrder 47 } 48 49 // GetConstants exposed to be used in gnark 50 func GetConstants() []big.Int { 51 once.Do(initConstants) // init constants 52 res := make([]big.Int, mimcNbRounds) 53 for i := 0; i < mimcNbRounds; i++ { 54 mimcConstants[i].BigInt(&res[i]) 55 } 56 return res 57 } 58 59 // NewMiMC returns a MiMCImpl object, pure-go reference implementation 60 func NewMiMC(opts ...Option) hash.Hash { 61 d := new(digest) 62 d.Reset() 63 cfg := mimcOptions(opts...) 64 d.byteOrder = cfg.byteOrder 65 return d 66 } 67 68 // Reset resets the Hash to its initial state. 69 func (d *digest) Reset() { 70 d.data = d.data[:0] 71 d.h = fr.Element{0, 0, 0, 0} 72 } 73 74 // Sum appends the current hash to b and returns the resulting slice. 75 // It does not change the underlying hash state. 76 func (d *digest) Sum(b []byte) []byte { 77 buffer := d.checksum() 78 d.data = nil // flush the data already hashed 79 hash := buffer.Bytes() 80 b = append(b, hash[:]...) 81 return b 82 } 83 84 // BlockSize returns the hash's underlying block size. 85 // The Write method must be able to accept any amount 86 // of data, but it may operate more efficiently if all writes 87 // are a multiple of the block size. 88 func (d *digest) Size() int { 89 return BlockSize 90 } 91 92 // BlockSize returns the number of bytes Sum will return. 93 func (d *digest) BlockSize() int { 94 return BlockSize 95 } 96 97 // Write (via the embedded io.Writer interface) adds more data to the running hash. 98 // 99 // Each []byte block of size BlockSize represents a big endian fr.Element. 100 // 101 // If len(p) is not a multiple of BlockSize and any of the []byte in p represent an integer 102 // larger than fr.Modulus, this function returns an error. 103 // 104 // To hash arbitrary data ([]byte not representing canonical field elements) use fr.Hash first 105 func (d *digest) Write(p []byte) (int, error) { 106 // we usually expect multiple of block size. But sometimes we hash short 107 // values (FS transcript). Instead of forcing to hash to field, we left-pad the 108 // input here. 109 if len(p) > 0 && len(p) < BlockSize { 110 pp := make([]byte, BlockSize) 111 copy(pp[len(pp)-len(p):], p) 112 p = pp 113 } 114 115 var start int 116 for start = 0; start < len(p); start += BlockSize { 117 if elem, err := d.byteOrder.Element((*[BlockSize]byte)(p[start : start+BlockSize])); err == nil { 118 d.data = append(d.data, elem) 119 } else { 120 return 0, err 121 } 122 } 123 124 if start != len(p) { 125 return 0, errors.New("invalid input length: must represent a list of field elements, expects a []byte of len m*BlockSize") 126 } 127 return len(p), nil 128 } 129 130 // Hash hash using Miyaguchi-Preneel: 131 // https://en.wikipedia.org/wiki/One-way_compression_function 132 // The XOR operation is replaced by field addition, data is in Montgomery form 133 func (d *digest) checksum() fr.Element { 134 // Write guarantees len(data) % BlockSize == 0 135 136 // TODO @ThomasPiellard shouldn't Sum() returns an error if there is no data? 137 // TODO: @Tabaie, @Thomas Piellard Now sure what to make of this 138 /*if len(d.data) == 0 { 139 d.data = make([]byte, BlockSize) 140 }*/ 141 142 for i := range d.data { 143 r := d.encrypt(d.data[i]) 144 d.h.Add(&r, &d.h).Add(&d.h, &d.data[i]) 145 } 146 147 return d.h 148 } 149 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)^5 159 tmp.Add(&m, &d.h).Add(&tmp, &mimcConstants[i]) 160 m.Square(&tmp). 161 Square(&m). 162 Mul(&m, &tmp) 163 } 164 m.Add(&m, &d.h) 165 return m 166 } 167 168 // Sum computes the mimc hash of msg from seed 169 func Sum(msg []byte) ([]byte, error) { 170 var d digest 171 if _, err := d.Write(msg); err != nil { 172 return nil, err 173 } 174 h := d.checksum() 175 bytes := h.Bytes() 176 return bytes[:], nil 177 } 178 179 func initConstants() { 180 bseed := ([]byte)(seed) 181 182 hash := sha3.NewLegacyKeccak256() 183 _, _ = hash.Write(bseed) 184 rnd := hash.Sum(nil) // pre hash before use 185 hash.Reset() 186 _, _ = hash.Write(rnd) 187 188 for i := 0; i < mimcNbRounds; i++ { 189 rnd = hash.Sum(nil) 190 mimcConstants[i].SetBytes(rnd) 191 hash.Reset() 192 _, _ = hash.Write(rnd) 193 } 194 } 195 196 // WriteString writes a string that doesn't necessarily consist of field elements 197 func (d *digest) WriteString(rawBytes []byte) error { 198 if elems, err := fr.Hash(rawBytes, []byte("string:"), 1); err != nil { 199 return err 200 } else { 201 d.data = append(d.data, elems[0]) 202 } 203 return nil 204 }