import socket
import re

attack_message = b'flagflagflagflag'

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('localhost', 1337))

# https://nitratine.net/blog/post/xor-python-byte-strings/
def byte_string_xor(s1, s2):
    return bytes([a ^ b for a, b in zip(s1, s2)])

known_message_and_sig = s.recv(1024).decode('utf-8')

extracted_info = re.match(r'Hey there, have a message (?P<msg>.*) and its signature (?P<sig>.*)!', known_message_and_sig)

known_message = extracted_info.group('msg').encode('utf-8')
known_sig = bytes.fromhex(extracted_info.group('sig'))

# first half is the actual output from CBC, second half is the random IV used
known_signature = known_sig[:16]
known_iv = known_sig[16:]

# we produce a (sig, iv) such that sig remains the same for a different message
# because CBC computes AES(MESSAGE XOR IV), all we need is
# ATTACK_MESSAGE XOR ATTACK_IV == KNOWN_MESSAGE XOR KNOWN_IV
# so we just do ATTACK_IV == KNOWN_MESSAGE XOR KNOWN_IV XOR ATTACK_MESSAGE
attack_iv = byte_string_xor(byte_string_xor(known_message, known_iv), attack_message)

attack_sig = (known_signature + attack_iv).hex()

print(f'Produced attack signature\n{attack_sig}\nfrom given signature\n{known_sig.hex()}')

s.send(attack_message + b'\n')
s.recv(4096)  # remove any query text
s.send(attack_sig.encode('utf-8') + b'\n')

print(s.recv(4096))
