poseidon.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. #!/usr/bin/env python3
  2. import numpy
  3. from finite_fields.finitefield import IntegersModP
  4. from constants import round_constants, MDS_matrix
  5. # Width
  6. T = 3
  7. # Full rounds
  8. R_F = 8
  9. # Partial rounds
  10. R_P = 56
  11. # Sponge rate
  12. RATE = 2
  13. # pallas
  14. p = 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001
  15. Fp = IntegersModP(p)
  16. MDS_MATRIX = numpy.array([[Fp(0)] * T] * T)
  17. ROUND_CONSTANTS = []
  18. for i in range(0, T):
  19. for j in range(0, T):
  20. MDS_MATRIX[i][j] = Fp(MDS_matrix[i][j])
  21. for i in range(0, R_F + R_P):
  22. for j in range(0, T):
  23. ROUND_CONSTANTS.append(Fp(round_constants[i][j]))
  24. def perm(inp):
  25. half_full_rounds = int(R_F / 2)
  26. state_words = numpy.array(inp)
  27. rcf = ROUND_CONSTANTS.copy()
  28. # First full rounds
  29. for _ in range(0, half_full_rounds):
  30. # Round constants, nonlinear layer, matrix multiplication
  31. for i in range(0, T):
  32. state_words[i] = state_words[i] + rcf[0]
  33. rcf.pop(0)
  34. for i in range(0, T):
  35. state_words[i] = (state_words[i])**5 # sbox
  36. state_words = numpy.array(numpy.dot(MDS_MATRIX, state_words))
  37. # Middle partial rounds
  38. for _ in range(0, R_P):
  39. # Round constants, nonlinear layer, matrix multiplication
  40. for i in range(0, T):
  41. state_words[i] = state_words[i] + rcf[0]
  42. rcf.pop(0)
  43. state_words[0] = (state_words[0])**5 # sbox
  44. state_words = numpy.array(numpy.dot(MDS_MATRIX, state_words))
  45. # Last full rounds
  46. for _ in range(0, half_full_rounds):
  47. # Round constants, nonlinear layer, matrix multiplication
  48. for i in range(0, T):
  49. state_words[i] = state_words[i] + rcf[0]
  50. rcf.pop(0)
  51. for i in range(0, T):
  52. state_words[i] = (state_words[i])**5 # sbox
  53. state_words = numpy.array(numpy.dot(MDS_MATRIX, state_words))
  54. return state_words
  55. def poseidon_hash(messages):
  56. L = len(messages)
  57. k = int((L + RATE - 1) / RATE)
  58. padding = [Fp(0)] * (k * RATE - L)
  59. messages.extend(padding)
  60. # Sponge
  61. mode = [None] * RATE
  62. output = [None] * RATE
  63. state = [Fp(0)] * T
  64. # Capacity value is L ⋅ 2^64 + (o-1) where o is the output length
  65. initial_capacity_element = Fp(L << 64)
  66. state[RATE] = initial_capacity_element
  67. # This outermost loop absorbs the messages in the sponge.
  68. for n, value in enumerate(messages):
  69. loop = False # Use this to mark we should reiterate
  70. for i in range(0, len(mode)):
  71. if mode[i] is None:
  72. mode[i] = value
  73. loop = True
  74. break
  75. if loop:
  76. continue
  77. # zip short-circuits when one iterator completes, so this will
  78. # only mutate the rate portion of the state.
  79. for i, _ in enumerate(zip(state, mode)):
  80. state[i] += mode[i]
  81. # Permutation of the current state
  82. state = perm(state)
  83. for i, _ in enumerate(zip(output, state)):
  84. output[i] = state[i]
  85. # Reinit sponge with the current message as the first value.
  86. mode = [None] * RATE
  87. mode[0] = value
  88. for i, _ in enumerate(zip(state, mode)):
  89. state[i] += mode[i]
  90. # Permutation of the final state
  91. state = perm(state)
  92. for i, _ in enumerate(zip(output, state)):
  93. output[i] = state[i]
  94. # Sponge now has the output, so the first element is our hash.
  95. mode = output
  96. return output[0]
  97. if __name__ == "__main__":
  98. words = []
  99. for word in range(0, 10):
  100. words.append(Fp(word))
  101. h = poseidon_hash(words.copy())
  102. print(hex(int(h)))