Некоторые режимы блочных шифров, такие как OFB, CTR или CFB, превращают блочный шифр в потоковый. Идея потоковых шифров заключается в том, чтобы генерировать псевдослучайный ключевой поток (keystream), который затем XOR‘ится с открытым текстом. Одно из преимуществ потоковых шифров — они могут работать с данными произвольной длины, без необходимости добавления паддинга.
OFB — довольно редкий режим шифрования, который сегодня не имеет существенных преимуществ по сравнению с CTR. В этом задании рассматривается необычное свойство режима OFB.
Исходный код
from Crypto.Cipher import AES
KEY = ?
FLAG = ?
@chal.route('/symmetry/encrypt/<plaintext>/<iv>/')
def encrypt(plaintext, iv):
plaintext = bytes.fromhex(plaintext)
iv = bytes.fromhex(iv)
if len(iv) != 16:
return {"error": "IV length must be 16"}
cipher = AES.new(KEY, AES.MODE_OFB, iv)
encrypted = cipher.encrypt(plaintext)
ciphertext = encrypted.hex()
return {"ciphertext": ciphertext}
@chal.route('/symmetry/encrypt_flag/')
def encrypt_flag():
iv = os.urandom(16)
cipher = AES.new(KEY, AES.MODE_OFB, iv)
encrypted = cipher.encrypt(FLAG.encode())
ciphertext = iv.hex() + encrypted.hex()
return {"ciphertext": ciphertext}
Что такое OFB
Это режим использования блочного шифра в качестве синхронного потокового шифра. Похож на CFB за исключением того, что n битов предыдущего выходного блока сдвигаются в крайние правые позиции очереди. В OFB шифрование и дешифровка идентичны — это и есть «симметрия» из названия задачи.

Проще говоря, мы получаем поток данных, с которыми нам нужно XOR‘ить открытый текст.
Анализ
В шифротекст добавляется iv:
ciphertext = iv.hex() + encrypted.hex()
Сам по себе это нормальная практика — без iv получатель не расшифрует. Проблема в другом: функция encrypt() принимает произвольный iv и открытый текст с тем же ключом. Это позволяет нам передать полученный шифротекст обратно в encrypt() с тем же iv — и получить открытый текст, потому что в OFB повторный XOR с тем же keystream отменяет шифрование: A ^ K ^ K = A (подробнее о свойствах XOR — в лабе XOR Properties).
Реализация
Реализую обёртки для API, которые буду использовать для получения шифротекста и шифрования:
import requests
URL = 'http://aes.cryptohack.org/symmetry'
def encrypt_flag():
url = f'{URL}/encrypt_flag/'
response = requests.get(url)
if response.status_code == 200:
return response.json()['ciphertext']
return ''
def encrypt(plaintext: bytes, iv: bytes) -> str:
url = f'{URL}/encrypt/{plaintext.hex()}/{iv.hex()}/'
response = requests.get(url)
if response.status_code == 200:
return response.json()['ciphertext']
return ''
Теперь нужно извлечь iv и зашифровать текст ещё раз:
import requests
URL = 'http://aes.cryptohack.org/symmetry'
def encrypt_flag():
url = f'{URL}/encrypt_flag/'
response = requests.get(url)
if response.status_code == 200:
return response.json()['ciphertext']
return ''
def encrypt(plaintext: bytes, iv: bytes) -> str:
url = f'{URL}/encrypt/{plaintext.hex()}/{iv.hex()}/'
response = requests.get(url)
if response.status_code == 200:
return response.json()['ciphertext']
return ''
def main():
raw = bytes.fromhex(encrypt_flag())
iv, ciphertext = raw[:16], raw[16:]
flag = bytes.fromhex(encrypt(ciphertext, iv)).decode()
print(flag)
if __name__ == '__main__':
main()
Запущу:
cu63:Symmetry/ $ python solver.py
crypto{0fb_15_5ymm37r1c4l_!!!11!}