Files
darkfi/script/research/poseidon/poseidon.py

130 lines
3.5 KiB
Python

#!/usr/bin/env python3
import numpy
from finite_fields.finitefield import IntegersModP
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 = IntegersModP(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)))