11. Oracle de détection CBC/ECB

11. Oracle de détection CBC/ECB

An ECB/CBC detection oracle

Now that you have ECB and CBC working:
Write a function to generate a random AES key; that's just 16 random bytes.
Write a function that encrypts data under an unknown key --- that is, a function that generates a random key and encrypts under it.
The function should look like:
encryption_oracle(your-input)
=> [MEANINGLESS JIBBER JABBER]
Under the hood, have the function append 5-10 bytes (count chosen randomly) before the plaintext and 5-10 bytes after the plaintext.
Now, have the function choose to encrypt under ECB 1/2 the time, and under CBC the other half (just use random IVs each time for CBC). Use rand(2) to decide which to use.
Detect the block cipher mode the function is using each time. You should end up with a piece of code that, pointed at a block box that might be encrypting ECB or CBC, tells you which one is happening.

Cet exercice est une mise en situation d'un cas de cryptanalyse où l'on n'a pas connaissance de la méthode de chiffrement utilisé, mais on a accès à une fonction qui, pour une valeur d'entrée, va nous donner sa valeur de sortie chiffrée.

Le but ici est d'arriver à retrouver sans la connaitre la méthode de chiffrement utilisée en utilisant à notre avantage les propriétés spécifiques de ECB et CBC.

Chiffrement aléatoire entre ECB et CBC

La fonction que l'on nous demande de coder doit utiliser :

Voici notre fonction:

from cryptopals import ecb_encrypt, cbc_encrypt

import os
import random

def random_encryption(text):
    method = random.randint(0, 1)
    random_key = os.urandom(16)
    prepend = os.urandom(random.randint(5, 10))
    append = os.urandom(random.randint(5, 10))
    text = prepend+text+append
    if(method):
        random_iv = os.urandom(16)
        return cbc_encrypt(text, random_key, random_iv)
    else:
        return ecb_encrypt(text, random_key)

Remarque: Par soucis de clarté les fonctions présentes dans les exercices précédents (ecb_encrypt, cbc_encrypt) sont stockés dans un fichier cryptopals.py. Une fois importées on peut les utiliser dans notre code.

J'utilise volontairement deux fonctions de randomisation :

Si on exécute ce code on obtient bien une suite d'octets intelligibles :

b"\xfa\x0e\xf5ZH\xbb\xa4\xf4i'F\x19\xa9\xd5u\xc7\x15w\xe0bo\x9aU\x8c\xdcw\xb8\xd7s\n\x1c\xfc\xcfm\x14(\xc5~z\x88\x9f}\xac\x92\x06\xd8\xb8\xa6"

Si l'on soumet plusieurs fois la même chaine de caractères, on obtient à chaque appel une chaine de sortie différente.

Oracle de détection de chiffrement

Il suffit littéralement d'une ligne pour déterminer si la fonction de chiffrement utilise un mode ECB ou CBC :

cipher_text = random_encryption(b"\x00"*59)
if(cipher_text[32:48] == cipher_text[48:64]):
    print("ECB detected")
else:
    print("CBC detected")

On utilise le fait que AES en mode ECB va toujours chiffrer de la même manière 2 blocs de texte identique. Sur la première ligne on génère un texte composée du même caractère répété 59 fois (64 - 5 = 59, car le texte ajouté par la fonction de chiffrement avant notre texte à chiffrer fera au minimum 5 caractères de long).

Une fois le résultat du chiffrement obtenu, on sait qu'il sera toujours constitué d'exactement 4 blocs de 16 caractères:

Ainsi si l'on compare le second bloc avec le troisième, 2 possibilités se présentent :

Remarque: On aurait pu directement utiliser la fonction de détection d'ECB de l'exercice 6 ; si la ligne est suffisament longue et est redondante alors elle est chiffré avec ECB.