Les fichiers 'sets/4_rounds_ciphertexts' et 'sets/5_rounds_ciphertexts' contiennent des paires de textes clairs/chiffrés (des lambda-sets) résultants respectivement d'un chiffrement AES-128 à 4 et 5 tours.
L'objectif est de retrouver les deux clés (128 bits) utilisées.
- Linux (Windows pose trop de problèmes)
- gcc installé sur la machine et accessible depuis le bash pour compiler
- CUDA installé et nvcc accessible depuis le bash
- Une carte graphique NVIDIA pour lancer le code CUDA
makemake cleanbuild/main_4rbuild/main_5rbuild/main_cudatest/testsDans AES, un « état » (state) est la représentation intermédiaire du bloc de données (128 bits) pendant le chiffrement/déchiffrement.
C'est une matrice 4 x 4 d'octets, notée
Pour
Prenons
c'est-à-dire lorsque la cellule est traversée par tous les octets à travers les 256 états.
Soit
où
On dit qu’un ensemble de 256 états
Soit
L'idée est la suivante : lorsque l'on donne en entrée d'AES un
Plus précisément, si l'on note les cellules active en gris, les cellules inatives en blanc et les cellules équilibrées avec une flèche, voici comment se propage le
Ceci va alors nous servir de distingueur pour rejeter des hypothèses de clé.
On envoie, en entrée d'AES, 256 textes clairs (256 états) dont une cellule est active et les autres inactives. Une fois les chiffrés obtenus, on veut remonter le chiffré à l'état en entrée du tour 4.
Pour ce faire il est facile d'inverser ShiftRows et SubBytes (il n'y a pas de MixColumns au dernier tour), mais il faut connaître la clé du tour 4 pour inverser
Il suffit donc d'appliquer
En faisant cela on trouve en moyenne deux candidats par octet de clé : la bonne valeur et un faux. En effet, XORer les 256 valeurs pour un octet de clé qui n'est pas le bon revient à du bruit/random donc le résultat est un nombre aléatoire entre 0 et 255, d'où le fait que l'on a une chance sur 256 que cela fasse 0 et que donc la cellule soit équilibrée. On a donc une chance sur 256 par hypothèse d'octet d'avoir un faux, mais comme on fait 256 hypothèses par octet, on a en moyenne un faux positif par octet en plus de la bonne valeur.
Pour savoir lequel est le bon on peut soit refaire l'attaque avec un autre
Avec plusieurs
Notons que l'on obtient alors la clé du tour 4. Il faut encore utiliser invKeySchedule pour remonter à la clé maître.
Pour s'assurer que l'on a retrouvé la bonne clé on peut alors chiffrer des messages avec la clé obtenue et comparer les chiffrés avec ceux des
L'idée reste la même : en partant du chiffré (sortie du cinquième tour), on veut remonter à l'état en entrée du tour 4 et vérifier si c'est équilibré.
Cependant il y a une complication. Les primitives SubBytes et ShiftRows (il n'y a pas de MixColumns au dernier tour) effectuent des permutations entre les octets mais ne les mélangent pas entre eux. Cela nous permet, lors de l'attaque sur AES 4 tours, de faire des hypothèses sur un seul octet de clé afin d'inverser ARK : la cellule ciblée est permutée par
Pour l'attaque sur 5 tours, la cellule ciblée lors de l'entrée du tour 4 est permutée par
Ainsi, pour chaque cellule on doit faire 4 hypothèses d'octets afin d'annuler
Donc pour résumer : pour chaque cellule, on déduit les positions des 4 octets à XORer par
En réalité cette version naïve est très coûteuse (comme expliqué plus bas dans la section du la complexité). On peut cependant l'accélérer grandement. L'idée est d'appliquer InvMixColumns avant
Voilà pourquoi cela fonctionne : MixColumns est une application linéaire, notons par
Pour chaque cellule (il y en a 16 en tout), on fait 256 hypothèses d'octet de clé. Ensuite on applique
Ceci est tout à fait abordable de nos jours avec un ordinateur personnel.
Pour chaque colonne on fait 256 hypothèses pour chaque octet dont on a besoin dans la clé
inversions des tours 4 et 5.
Notons que sans l'astuce d'appliquer MC avant ARK on serait à
Cependant il est possible de paralléliser le calcul et de l'envoyer sur carte graphique. Cela peut faire gagner un temps énorme.
Calculé sur CPU i7 9700K.
Notons que pour obtenir ces résultats, les calculs ont été faits sur RTX 2060, ce qui a permis de diviser le temps par au moins 20 (voire 40) par rapport au CPU (i7 9700K).
Cette attaque est massivement parallélisable et la RTX 2060 n'est pas suffisamment puissante (et n'a pas assez de threads) pour réellement paralléliser sur plus que deux octets par colonne. Cependant, avec une meilleure carte graphique (voire plusieurs en même temps), on peut encore énormément diviser le temps de calcul.
L'attaque a été faite de manière réaliste : les clés utilisées pour obtenir les
Je dois le début de ma compréhension de l'attaque sur 4 tours et les images explicatives de ce README à Kévin Duverger. Merci Kévin ! Le travail de nvietsang (voir son github sur cette même attaque) m'a aussi beaucoup aidé pour implémenter l'attaque sur 5 tours sur CPU.



