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.

171 lines
4.3 KiB

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