You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

168 lines
4.2 KiB

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