| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128 |
- #!/usr/bin/env python3
- import numpy
- from constants import round_constants, MDS_matrix
- # Width
- T = 3
- # Full rounds
- R_F = 8
- # Partial rounds
- R_P = 56
- # Sponge rate
- RATE = 2
- # pallas
- p = 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001
- Fp = GF(p)
- MDS_MATRIX = numpy.array([[Fp(0)] * T] * T)
- ROUND_CONSTANTS = []
- for i in range(0, T):
- for j in range(0, T):
- MDS_MATRIX[i][j] = Fp(MDS_matrix[i][j])
- for i in range(0, R_F + R_P):
- for j in range(0, T):
- ROUND_CONSTANTS.append(Fp(round_constants[i][j]))
- def perm(inp):
- half_full_rounds = int(R_F / 2)
- state_words = numpy.array(inp)
- rcf = ROUND_CONSTANTS.copy()
- # First full rounds
- for _ in range(0, half_full_rounds):
- # Round constants, nonlinear layer, matrix multiplication
- for i in range(0, T):
- state_words[i] = state_words[i] + rcf[0]
- rcf.pop(0)
- for i in range(0, T):
- state_words[i] = (state_words[i])**5 # sbox
- state_words = numpy.array(numpy.dot(MDS_MATRIX, state_words))
- # Middle partial rounds
- for _ in range(0, R_P):
- # Round constants, nonlinear layer, matrix multiplication
- for i in range(0, T):
- state_words[i] = state_words[i] + rcf[0]
- rcf.pop(0)
- state_words[0] = (state_words[0])**5 # sbox
- state_words = numpy.array(numpy.dot(MDS_MATRIX, state_words))
- # Last full rounds
- for _ in range(0, half_full_rounds):
- # Round constants, nonlinear layer, matrix multiplication
- for i in range(0, T):
- state_words[i] = state_words[i] + rcf[0]
- rcf.pop(0)
- for i in range(0, T):
- state_words[i] = (state_words[i])**5 # sbox
- state_words = numpy.array(numpy.dot(MDS_MATRIX, state_words))
- return state_words
- def poseidon_hash(messages):
- L = len(messages)
- k = int((L + RATE - 1) / RATE)
- padding = [Fp(0)] * (k * RATE - L)
- messages.extend(padding)
- # Sponge
- mode = [None] * RATE
- output = [None] * RATE
- state = [Fp(0)] * T
- # Capacity value is L ⋅ 2^64 + (o-1) where o is the output length
- initial_capacity_element = Fp(L << 64)
- state[RATE] = initial_capacity_element
- # This outermost loop absorbs the messages in the sponge.
- for n, value in enumerate(messages):
- loop = False # Use this to mark we should reiterate
- for i in range(0, len(mode)):
- if mode[i] is None:
- mode[i] = value
- loop = True
- break
- if loop:
- continue
- # zip short-circuits when one iterator completes, so this will
- # only mutate the rate portion of the state.
- for i, _ in enumerate(zip(state, mode)):
- state[i] += mode[i]
- # Permutation of the current state
- state = perm(state)
- for i, _ in enumerate(zip(output, state)):
- output[i] = state[i]
- # Reinit sponge with the current message as the first value.
- mode = [None] * RATE
- mode[0] = value
- for i, _ in enumerate(zip(state, mode)):
- state[i] += mode[i]
- # Permutation of the final state
- state = perm(state)
- for i, _ in enumerate(zip(output, state)):
- output[i] = state[i]
- # Sponge now has the output, so the first element is our hash.
- mode = output
- return output[0]
- if __name__ == "__main__":
- words = []
- for word in range(0, 10):
- words.append(Fp(word))
- h = poseidon_hash(words.copy())
- print(hex(int(h)))
|