<filename>python/src/cryptopals/set2/challenge12.py
from Crypto.Cipher import AES
import secrets
from base64 import b64decode
from cryptopals.basics import padding

key = secrets.token_bytes(16)

unknown_string = """
<KEY>
"""


def encryption_oracle(your_string):
    padded = padding(your_string + b64decode(unknown_string))
    aes = AES.new(key, AES.MODE_ECB)
    return aes.encrypt(padded)


def discover_next_hop(old_bs):
    i = 1
    while True:
        new_bs = len(encryption_oracle(b"\x00" * i))
        if old_bs < new_bs:
            return new_bs
        i += 1


def discover_block_size():
    """
    >>> discover_block_size()
    16
    """
    first_bs = len(encryption_oracle(b""))
    hop1 = discover_next_hop(first_bs)
    hop2 = discover_next_hop(hop1 + 1)
    return hop2 - hop1


def discover_mode():
    """
    >>> discover_mode()
    'ECB'
    """
    secret = encryption_oracle(b"\x00" * 48)
    return "ECB" if secret[16:32] == secret[32:48] else "CBC"


def lookup(i, known_secret: bytes, known_content: bytes):
    for b in range(256):
        secret = encryption_oracle(b"\x00" * i + known_content + bytes([b]))
        if secret[:160] == known_secret:
            return b


def encryption_detector(printing=False):
    """
    >>> encryption_detector().startswith('Rollin')
    True
    """
    known = bytearray()
    for i in range(159, -1, -1):
        secret = encryption_oracle(b"\x00" * i)
        b = lookup(i, secret[:160], known)
        if not b:
            return known.decode()
        known.append(b)
        if printing:
            print(chr(b), end="")


if __name__ == "__main__":
    encryption_detector(True)

<filename>python/src/cryptopals/set2/challenge12.py
from Crypto.Cipher import AES
import secrets
from base64 import b64decode
from cryptopals.basics import padding

key = secrets.token_bytes(16)

unknown_string = """
<KEY>
"""


def encryption_oracle(your_string):
    padded = padding(your_string + b64decode(unknown_string))
    aes = AES.new(key, AES.MODE_ECB)
    return aes.encrypt(padded)


def discover_next_hop(old_bs):
    i = 1
    while True:
        new_bs = len(encryption_oracle(b"\x00" * i))
        if old_bs < new_bs:
            return new_bs
        i += 1


def discover_block_size():
    """
    >>> discover_block_size()
    16
    """
    first_bs = len(encryption_oracle(b""))
    hop1 = discover_next_hop(first_bs)
    hop2 = discover_next_hop(hop1 + 1)
    return hop2 - hop1


def discover_mode():
    """
    >>> discover_mode()
    'ECB'
    """
    secret = encryption_oracle(b"\x00" * 48)
    return "ECB" if secret[16:32] == secret[32:48] else "CBC"


def lookup(i, known_secret: bytes, known_content: bytes):
    for b in range(256):
        secret = encryption_oracle(b"\x00" * i + known_content + bytes([b]))
        if secret[:160] == known_secret:
            return b


def encryption_detector(printing=False):
    """
    >>> encryption_detector().startswith('Rollin')
    True
    """
    known = bytearray()
    for i in range(159, -1, -1):
        secret = encryption_oracle(b"\x00" * i)
        b = lookup(i, secret[:160], known)
        if not b:
            return known.decode()
        known.append(b)
        if printing:
            print(chr(b), end="")


if __name__ == "__main__":
    encryption_detector(True)

<KEY> >>> discover_block_size()     16 >>> discover_mode()     'ECB' >>> encryption_detector().startswith('Rollin')     True