RSA - Attaque de Bleichenbacher
L’attaque de Bleichenbacher, applicable sur de nombreux protocoles reposant sur l’algorithme de cryptographie asymétrique RSA, permet de décrypter un message chiffré par RSA, sous réserve qu’un oracle nous indique si le padding d’un cryptogramme donné est correct ou non.
Soit une paire de clés RSA ,
représentant respectivement le module de la clé, l’exposant publique, et l’exposant privé.
Considérons de plus un message , et
le cryptogramme correspondant.
Notons l’ensemble des mots de longueur inférieure à
de l’alphabet
contenant tous les caractères usuels,
l’ensemble des cryptogrammes pouvant être générés à l’aide de la clé privée
,
la fonction de chiffrement RSA, et
la fonction de déchiffrement RSA.
L’attaquant dispose du message chiffré , et a la possibilité de soumettre un message à un oracle qui va déchiffrer ce ciphertext, et nous informer de la validité du padding. Notons
la fonction permettant d’effectuer une telle requête à l’oracle.
Rappelons également une propriété du chiffrement RSA qui nous sera très utile au cours de cette attaque, en conservant les notations précédemment introduites :
En effet, on a, :
L’algorithme
L’algorithme sur lequel repose cette attaque se décompose en quatre parties :
1. Blinding
Il s’agit, dans un premier temps, de faire en sorte que l’oracle ne puisse pas prendre connaissance du cryptogramme que l’on essaye de déchiffrer. Pour ce faire, nous allons générer aléatoirement des entiers positifs jusqu’à ce que
nous retourne vrai, c’est-à-dire jusqu’à ce que
soit conforme. Nous appellerons
le cryptogramme obtenu à l’issue de cette première étape.
Notons la longueur en bytes de n, et
2. Searching for PKCS conforming messages
Bleichenbacher distingue alors trois cas :
- Soit nous effectuons l’itération initiale de l’algorithme, et alors il s’agit de rechercher le premier
tel que l’on ait
, puis de poser
, sachant que les
seront des listes d’intervalles dont l’un encadrera le message en clair recherché ;
- Soit nous effectuons une itération non-initiale, et la longueur courante de
est supérieure à 2, et alors nous recherchons le premier
supérieur à la valeur du
obtenu lors de l’itération précédente, tel que l’on ait
;
- Soit nous effectuons une itération non-initiale, et la longueur courante de
est égale à 1. Nous avons donc un unique intervalle
, si bien que l’on peut utiliser une recherche binaire optimisée afin de trouver la prochaine valeur de
telle que l’ont ait
. Il s’agit tester les
compris entre
et
, avec
.
3. Narrowing the set of solutions
Une fois que nous avons trouvé et que nous avons une liste d’intervalles
, nous allons créer une nouvelle liste
contenant des intervalles plus précis, et moins nombreux dans le cas où l’on a plusieurs intervalles.
Pour ce faire, il nous faut considérer chaque intervalle contenue dans
, et, pour
,
- Trouver le minimum
entre
et
;
- Trouver le maximum
entre
et
;
- Ajouter l’intervalle
à la nouvelle liste
que l’on est en train de former uniquement si
.
Ainsi, au fur et à mesure des itérations, nous nous débarrasserons des intervalles superflues, afin d’obtenir, à terme, un unique intervalle qui pourra être affiné très rapidement grâce à la recherche binaire, jusqu’à ce que les deux bornes de celui-ci soient égales.
4. Computing the solution
Une fois la nouvelle liste obtenue, on distingue deux cas :
- Soit
est composé d’un unique encadrement
d’amplitude nulle, et alors on obtient directement le message en calculant
, le modulo inverse de
pouvant être facilement calculé à l’aide de l’algorithme d’Euclide étendu ;
- Soit on retourne à l’étape 2.
Pour plus de précisions, nous vous invitons à lire l’article original de Bleichenbacher.
L’implémentation
L’implémentation de cet algorithme repose sur une optimisation bien connue : l’exponentiation modulaire. En effet, dans la pratique, l’exposant peut valoir 65537, et les
sont souvent composés de plus d’une quinzaine de chiffres, ce qui peut rentre le calcul de
particulièrement long si l’on n’applique le modulo qu’à la fin de calcul, comme nous avons pu le voir dans certaines implémentations en Python (e ** s % n). Nous avons choisi d’utiliser la fonction built-in pow, qui permet d’effectuer les calculs modulo
, ce qui évitera de manipuler des nombres supérieurs à
lors de l’exponentiation.
Voici une démonstration de notre implémentation en Python : Attaque de Bleichenbacher (désormais intégrée à RSHack), dans le cas particulier d’un module de 2048 bits, avec un exposant publique égal à 65537, sur un unique bloc de 256 bytes. Les caractères non-imprimables affichés avant le message en clair correspondent au padding mis en place par openssl selon la norme PCKS#1 v1.5.
zweisamkeit@linux [ Bleichenbacher ] --> ncat -lvp 4444 -e ./oracle.py --keep-open 1>/dev/null 2>&1 &
zweisamkeit@linux [ Bleichenbacher ] --> openssl genrsa 2048 > private.key 2>/dev/null
zweisamkeit@linux [ Bleichenbacher ] --> openssl rsa -pubout < private.key > public.key 2>/dev/null
zweisamkeit@linux [ Bleichenbacher ] --> module=$(openssl rsa -pubin -in public.key -modulus 2>/dev/null | grep Modulus | cut -d '=' -f 2)
zweisamkeit@linux [ Bleichenbacher ] --> exposant=$(openssl rsa -in public.key -pubin -text -noout | grep Exponent | cut -d ' ' -f 2)
zweisamkeit@linux [ Bleichenbacher ] --> message=$(echo -ne "Elle t’a si tendrement serrée à la gorge que tu en as gardé pour toujours l’envie de pleurer.")
zweisamkeit@linux [ Bleichenbacher ] --> cryptogramme=$( echo -en $message | openssl rsautl -encrypt -inkey private.key | xxd -ps | tr -d '\n')
zweisamkeit@linux [ Bleichenbacher ] --> time ./bleichenbacher.py $module $exposant $cryptogramme localhost 4444 "Padding Error"
~~~~~~~~~~~~~~~~~~~~~~~~~
Attaque de Bleichenbacher
Zweisamkeit
Licence GNU GPL v3
~~~~~~~~~~~~~~~~~~~~~~~~~
Établissement de la connexion à l'Oracle...
Connexion à l'Oracle établie.
Lancement de l'attaque...
Brouillage en cours...
Brouillage terminé.
Construction et affinage des encadrements en cours...
Construction et affinage des encadrements terminés.
Le message en clair est :
[Random Padding] Elle t’a si tendrement serrée à la gorge que tu en as gardé pour toujours l’envie de pleurer.
Fin de l'attaque.
Connexion cloturée.
real 3m11,328s
user 0m35,287s
sys 0m1,880s