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.

208 lines
4.9 KiB

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