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.

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