import requests
from binascii import hexlify, unhexlify


url = "http://padding-oracle.et.r.appspot.com/q?p=5&info="


def is_padding_ok(ciphertext: bytes):
    # print(ciphertext)
    response = requests.get(f"{url}{hexlify(ciphertext).decode()}")
    return response.status_code in (200, 404)


def combine(blocks: bytearray):
    """
    Binary blocks -> hex_string
    """
    return hexlify(b"".join(blks)).decode()


def split_blocks(data: str):
    """
    hex_string -> binary blocks
    """
    # Split it into blocks
    blks = []
    data = unhexlify(original.encode())
    for i in range(0, len(data) - 1, 16):
        b = data[i : i + 16]
        blks.append(b)
    return blks


original = "75896b6dc9282fc16bbf7584413cd3db7de1b37b984cbec7ac0eff3e4b1f4bef55455bd405a38fb03b88178f05cd1099b4ffb2896379257ea08188f53603acfcf785c5b26744849ead4181a42569ffaf"

BLOCK_SIZE = 16

# Sanity check if the msg enc and dec correctly
blks = split_blocks(original)
o = combine(blks)
print(f"Sanity Check:{o == original}")


def attack_message(msg: str):
    """Hex string -> String"""

    cipherfake = [0] * 16
    plaintext = [0] * 16
    current = 0
    message = ""

    # I divide the list of bytes in blocks, and I put them in another list
    # Converts hex string to blocks of 16 byte chars
    blocks = split_blocks(msg)

    for z in range(
        len(blocks) - 1
    ):  # for each message, I calculate the number of block
        for itera in range(
            1, 17
        ):  # the length of each block is 16. I start by one because than I use its in a counter
            for v in range(256):
                cipherfake[-itera] = v
                if is_padding_ok(
                    bytes(cipherfake) + blocks[z + 1]
                ):  # the idea is that I put in 'is_padding_ok' the cipherfake(array of all 0) plus the last block
                    # if the function return true I found the value
                    current = itera
                    plaintext[-itera] = v ^ itera ^ blocks[z][-itera]

            for w in range(1, current + 1):
                cipherfake[-w] = (
                    plaintext[-w] ^ itera + 1 ^ blocks[z][-w]
                )  # for decode the second byte I must set the previous bytes with 'itera+1'

        for i in range(16):
            if plaintext[i] >= 32:
                char = chr(int(plaintext[i]))
                message += char
        print(message)

    # print("Crack: " + message + "\n")
    return str.encode(message)


# Attack the msg
print(attack_message(original))