96 lines
2.9 KiB
Python
96 lines
2.9 KiB
Python
import random, os, signal
|
|
from Crypto.Util.number import long_to_bytes as l2b, bytes_to_long as b2l
|
|
from secret import FLAG
|
|
|
|
ROUNDS = 3
|
|
USED_STATES = []
|
|
_ROL_ = lambda x, i : ((x << i) | (x >> (N-i))) & (2**N - 1)
|
|
N = 128
|
|
|
|
def handler(signum, frame):
|
|
print("\n\nToo slow, don't try to do sneaky things.")
|
|
exit()
|
|
|
|
def validate_state(state):
|
|
if not all(0 < s < 2**N-1 for s in user_state[-2:]) or not all(0 <= s < N for s in user_state[:4]):
|
|
print('Please, make sure your input satisfies the upper and lower bounds.')
|
|
return False
|
|
|
|
if sorted(state[:4]) in USED_STATES:
|
|
print('You cannot reuse the same state')
|
|
return False
|
|
|
|
if sum(user_state[:4]) < 2:
|
|
print('We have to deal with some edge cases...')
|
|
return False
|
|
|
|
return True
|
|
|
|
class HashRoll:
|
|
def __init__(self):
|
|
self.reset_state()
|
|
|
|
def hash_step(self, i):
|
|
r1, r2 = self.state[2*i], self.state[2*i+1]
|
|
return _ROL_(self.state[-2], r1) ^ _ROL_(self.state[-1], r2)
|
|
|
|
def update_state(self, state=None):
|
|
if not state:
|
|
self.state = [0] * 6
|
|
self.state[:4] = [random.randint(0, N) for _ in range(4)]
|
|
self.state[-2:] = [random.randint(0, 2**N) for _ in range(2)]
|
|
else:
|
|
self.state = state
|
|
|
|
def reset_state(self):
|
|
self.update_state()
|
|
|
|
def digest(self, buffer):
|
|
buffer = int.from_bytes(buffer, byteorder='big')
|
|
m1 = buffer >> N
|
|
m2 = buffer & (2**N - 1)
|
|
self.h = b''
|
|
for i in range(2):
|
|
self.h += int.to_bytes(self.hash_step(i) ^ (m1 if not i else m2), length=N//8, byteorder='big')
|
|
return self.h
|
|
|
|
|
|
print('Can you test my hash function for second preimage resistance? You get to select the state and I get to choose the message ... Good luck!')
|
|
|
|
hashfunc = HashRoll()
|
|
|
|
for _ in range(ROUNDS):
|
|
print(f'ROUND {_+1}/{ROUNDS}!')
|
|
|
|
server_msg = os.urandom(32)
|
|
hashfunc.reset_state()
|
|
server_hash = hashfunc.digest(server_msg)
|
|
print(f'You know H({server_msg.hex()}) = {server_hash.hex()}')
|
|
|
|
signal.signal(signal.SIGALRM, handler)
|
|
signal.alarm(2)
|
|
|
|
user_state = input('Send your hash function state (format: a,b,c,d,e,f) :: ').split(',')
|
|
|
|
try:
|
|
user_state = list(map(int, user_state))
|
|
|
|
if not validate_state(user_state):
|
|
print("The state is not valid! Try again.")
|
|
exit()
|
|
|
|
hashfunc.update_state(user_state)
|
|
|
|
if hashfunc.digest(server_msg) == server_hash:
|
|
print(f'Moving on to the next round!')
|
|
USED_STATES.append(sorted(user_state[:4]))
|
|
else:
|
|
print('Not today.')
|
|
exit()
|
|
except:
|
|
print("The hash function's state must be all integers.")
|
|
exit()
|
|
finally:
|
|
signal.alarm(0)
|
|
|
|
print(f'Uhm... how did you do that? I thought I had cryptanalyzed it enough ... {FLAG}') |