Kolejny raz będziemy opierali się na właściwości trybu ECB – powtarzające się bloki w danych wejściowych powoduje powstanie powtarzających się bloków w danych wyjściowych. Nasze zadanie jest podzielone na kilka etapów.
W pierwszym kroku musimy stworzyć funkcję która generuje 16-bajtowy ciąg bajtów którego będziemy używali jako klucza szyfrującego. Ja stworzyłem funkcję która przyjmuje jako parametr długość losowych danych które mają zostać wygenerowane – to nam się przyda w dalszej części zadania.
def generateRandomData(data_size):
result = []
for i in range(0, data_size):
result.append(randint(0, 255))
return bytearray(result)
W kolejnym kroku mamy stworzyć funkcję, która będzie szyfrowała podane jako parametr dane losowym kluczem.
def encryptionOracle(input_data):
if isinstance(input_data, str):
input_data = bytearray(input_data, 'ascii')
input_data = generateRandomDataSize(5, 10) + input_data + generateRandomDataSize(5, 10)
iv_data = generateRandomData(16)
key_data = generateRandomData(16)
ciph = CustomAES()
ciph_mode = CustomAES.Mode.CBC
if randint(0, 1) == 0:
ciph_mode = CustomAES.Mode.ECB
else:
ciph_mode = CustomAES.Mode.CBC
return ciph.encode(input_data, key_data, ciph_mode, iv_data), ciph_mode
Funkcja ma robić kilka rzeczy:
1. Dane mają być szyfrowane w trybie ECB lub CBC – to jakim tryb będzie użyty jest losowe (50% ECB i 50% CBC).
2. Do podanych przez użytkownika danych na początku i na końcu dodawane są dodatkowe losowej długości (5-10 bajtów) bloki losowych bajtów.
3. Jeżeli dane będą szyfrowane w trybie CBC ma być użyty losowy IV.
Dodatkowo na potrzeby testów moja funkcja zwraca informację o tym jaki tryb był użyty w danym wywołaniu w celu ułatwienia weryfikacji poprawności algorytmu wykrywającego użyty tryb.
def s2challenge11():
print("Challenge 11 start")
for i in range(0, 10):
ciphered_data, cipher_type = oracles.encryptionOracle("a"*48)
print(i+1, ". Cipher type: ", cipher_type == CustomAES.Mode.ECB, " Guessed Type", canBeAesECB(ciphered_data))
Jak widać to zadanie nie jest zbyt skomplikowane. Do wcześniej stworzonej funkcji jako parametr przekazuję ciąg 48 znaków „a”. Dlaczego 48 znaków? Ponieważ wiemy, że AES tworzy bloki o długości 16 bajtów, ale nie wiemy jakiej długości dane będą dodane na początku ciągu który kontrolujemy, to żeby mieć pewność, że wśród zwróconych bajtów będą co najmniej dwa bloki o takich samych wartościach w przypadku kiedy użyty będzie tryb ECB. Blok mógłby być krótszy bo wiemy, że dane na początku mają długość między 5 a 10 bajtów: w przypadku kiedy wylosowana długość to 5 bajtów będziemy potrzebowali 13 na dopełnienie pierwszego bloku a następnie dwa bloki po 16 bajtów, więc minimalna długość jaka możemy przekazać to 45. Używając długości 48 mamy pewność, że algorytm zadziała niezależnie od danych na które nie mamy wpływu.