poseidon.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. #!/usr/bin/env python3
  2. import numpy
  3. from finite_fields.finitefield import IntegersModP
  4. from constants import MDS_matrix, round_constants
  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 debug(n, s, m):
  56. if enable_debug:
  57. print(f"State {n} absorb:")
  58. pprint([hex(int(i)) for i in s])
  59. print(f"Mode {n} absorb:")
  60. pprint([hex(int(i)) if i is not None else None for i in m])
  61. def poseidon_hash(messages):
  62. L = len(messages)
  63. k = int((L + RATE - 1) / RATE)
  64. padding = [Fp(0)] * (k * RATE - L)
  65. messages.extend(padding)
  66. # Sponge
  67. mode = [None] * RATE
  68. output = [None] * RATE
  69. state = [Fp(0)] * T
  70. # Capacity value is L ⋅ 2^64 + (o-1) where o is the output length
  71. initial_capacity_element = Fp(L << 64)
  72. state[RATE] = initial_capacity_element
  73. # This outermost loop absorbs the messages in the sponge.
  74. for n, value in enumerate(messages):
  75. debug(f"before {n+1}", state, mode)
  76. loop = False # Use this to mark we should reiterate
  77. for i in range(0, len(mode)):
  78. if mode[i] is None:
  79. mode[i] = value
  80. loop = True
  81. break
  82. if loop:
  83. debug(f"after {n+1}", state, mode)
  84. continue
  85. # zip short-circuits when one iterator completes, so this will
  86. # only mutate the rate portion of the state.
  87. for i, _ in enumerate(zip(state, mode)):
  88. state[i] += mode[i]
  89. # Permutation of the current state
  90. state = perm(state)
  91. for i, _ in enumerate(zip(output, state)):
  92. output[i] = state[i]
  93. # Reinit sponge with the current message as the first value.
  94. mode = [None] * RATE
  95. mode[0] = value
  96. debug(f"after {n+1}", state, mode)
  97. debug("before final", state, mode)
  98. for i, _ in enumerate(zip(state, mode)):
  99. state[i] += mode[i]
  100. # Permutation of the final state
  101. state = perm(state)
  102. for i, _ in enumerate(zip(output, state)):
  103. output[i] = state[i]
  104. # Sponge now has the output, so the first element is our hash.
  105. mode = output
  106. debug("after final", state, mode)
  107. return output[0]
  108. if __name__ == "__main__":
  109. enable_debug = False
  110. if enable_debug:
  111. from pprint import pprint
  112. #input_words = []
  113. #for i in range(0, T):
  114. # input_words.append(Fp(i))
  115. #output_words = perm(input_words)
  116. #print([hex(int(i)) for i in output_words])
  117. words = []
  118. for i in range(0, 10):
  119. words.append(Fp(i))
  120. h = poseidon_hash(words.copy())
  121. print(hex(int(h)))