github.com/consensys/gnark-crypto@v0.14.0/ecc/bw6-761/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-761/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  }