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.

184 lines
4.3 KiB

5 years ago
  1. package poseidon
  2. import (
  3. "bytes"
  4. "errors"
  5. "math/big"
  6. "strconv"
  7. "github.com/iden3/go-iden3-crypto/field"
  8. "golang.org/x/crypto/blake2b"
  9. )
  10. const SEED = "poseidon"
  11. const NROUNDSF = 8
  12. const NROUNDSP = 57
  13. const T = 6
  14. var constants = generateConstantsData()
  15. type constantsData struct {
  16. fqR field.Fq
  17. c []*big.Int
  18. m [][]*big.Int
  19. }
  20. // checkBigIntInField checks if given big.Int fits in a Field R element
  21. func checkBigIntInField(a *big.Int, q *big.Int) bool {
  22. if a.Cmp(q) != -1 {
  23. return false
  24. }
  25. return true
  26. }
  27. // checkBigIntArrayInField checks if given big.Int fits in a Field R element
  28. func checkBigIntArrayInField(arr []*big.Int, q *big.Int) bool {
  29. for _, a := range arr {
  30. if !checkBigIntInField(a, q) {
  31. return false
  32. }
  33. }
  34. return true
  35. }
  36. func generateConstantsData() constantsData {
  37. var constants constantsData
  38. r, ok := new(big.Int).SetString("21888242871839275222246405745257275088548364400416034343698204186575808495617", 10)
  39. if !ok {
  40. }
  41. fqR := field.NewFq(r)
  42. constants.fqR = fqR
  43. constants.c = getPseudoRandom(fqR, SEED+"_constants", big.NewInt(int64(NROUNDSF+NROUNDSP)))
  44. constants.m = getMDS(fqR)
  45. return constants
  46. }
  47. func getPseudoRandom(fqR field.Fq, seed string, n *big.Int) []*big.Int {
  48. var res []*big.Int
  49. hash := blake2b.Sum256([]byte(seed))
  50. for big.NewInt(int64(len(res))).Cmp(n) == -1 { // res < n
  51. newN := fqR.Affine(leByteArrayToBigInt(fqR, hash[:]))
  52. res = append(res, newN)
  53. hash = blake2b.Sum256(hash[:])
  54. }
  55. return res
  56. }
  57. func leByteArrayToBigInt(fqR field.Fq, b []byte) *big.Int {
  58. res := fqR.Zero()
  59. for i := 0; i < len(b); i++ {
  60. n := big.NewInt(int64(b[i]))
  61. res = new(big.Int).Add(res, new(big.Int).Lsh(n, uint(i*8)))
  62. }
  63. return res
  64. }
  65. func nonceToString(n int) string {
  66. r := strconv.Itoa(n)
  67. for len(r) < 4 {
  68. r = "0" + r
  69. }
  70. return r
  71. }
  72. // https://eprint.iacr.org/2019/458.pdf pag.8
  73. func getMDS(fqR field.Fq) [][]*big.Int {
  74. nonce := 0
  75. cauchyMatrix := getPseudoRandom(fqR, SEED+"_matrix_"+nonceToString(nonce), big.NewInt(T*2))
  76. for !checkAllDifferent(cauchyMatrix) {
  77. nonce += 1
  78. cauchyMatrix = getPseudoRandom(fqR, SEED+"_matrix_"+nonceToString(nonce), big.NewInt(T*2))
  79. }
  80. var m [][]*big.Int
  81. for i := 0; i < T; i++ {
  82. var mi []*big.Int
  83. for j := 0; j < T; j++ {
  84. mi = append(mi, fqR.Inverse(fqR.Sub(cauchyMatrix[i], cauchyMatrix[T+j])))
  85. }
  86. m = append(m, mi)
  87. }
  88. return m
  89. }
  90. func checkAllDifferent(v []*big.Int) bool {
  91. for i := 0; i < len(v); i++ {
  92. if bytes.Equal(v[i].Bytes(), big.NewInt(int64(0)).Bytes()) {
  93. return false
  94. }
  95. for j := i + 1; j < len(v); j++ {
  96. if bytes.Equal(v[i].Bytes(), v[j].Bytes()) {
  97. return false
  98. }
  99. }
  100. }
  101. return true
  102. }
  103. // ark computes Add-Round Key, from the paper https://eprint.iacr.org/2019/458.pdf
  104. func ark(state []*big.Int, c *big.Int) []*big.Int {
  105. for i := 0; i < len(state); i++ {
  106. state[i] = constants.fqR.Add(state[i], c)
  107. }
  108. return state
  109. }
  110. // cubic performs x^3 mod p
  111. func cubic(a *big.Int) *big.Int {
  112. return constants.fqR.Mul(a, constants.fqR.Square(constants.fqR.Square(a)))
  113. }
  114. // sbox https://eprint.iacr.org/2019/458.pdf pag.6
  115. func sbox(state []*big.Int, i int) []*big.Int {
  116. if (i < NROUNDSF/2) || (i >= NROUNDSF/2+NROUNDSP) {
  117. for j := 0; j < T; j++ {
  118. state[j] = cubic(state[j])
  119. }
  120. } else {
  121. state[0] = cubic(state[0])
  122. }
  123. return state
  124. }
  125. // mix returns [[matrix]] * [vector]
  126. func mix(state []*big.Int, m [][]*big.Int) []*big.Int {
  127. var newState []*big.Int
  128. for i := 0; i < len(state); i++ {
  129. newState = append(newState, constants.fqR.Zero())
  130. for j := 0; j < len(state); j++ {
  131. newState[i] = constants.fqR.Add(newState[i], constants.fqR.Mul(m[i][j], state[j]))
  132. }
  133. }
  134. for i := 0; i < len(state); i++ {
  135. state[i] = newState[i]
  136. }
  137. return state
  138. }
  139. // Hash computes the Poseidon hash for the given inputs
  140. func Hash(inp []*big.Int) (*big.Int, error) {
  141. var state []*big.Int
  142. if len(inp) < 0 || len(inp) > T {
  143. return nil, errors.New("wrong inputs length")
  144. }
  145. if !checkBigIntArrayInField(inp, constants.fqR.Q) {
  146. return nil, errors.New("inputs values not inside Finite Field")
  147. }
  148. for i := 0; i < len(inp); i++ {
  149. state = append(state, inp[i])
  150. }
  151. for i := len(inp); i < T; i++ {
  152. state = append(state, constants.fqR.Zero())
  153. }
  154. // ARK --> SBox --> M, https://eprint.iacr.org/2019/458.pdf pag.5
  155. for i := 0; i < NROUNDSF+NROUNDSP; i++ {
  156. state = ark(state, constants.c[i])
  157. state = sbox(state, i)
  158. state = mix(state, constants.m)
  159. }
  160. return state[0], nil
  161. }