Ceci est une ancienne révision du document !
Warning: Undefined array key 1 in /home/goupillf/wiki.goupill.fr/lib/plugins/codeprettify/syntax/code.php on line 172
Warning: Undefined array key 1 in /home/goupillf/wiki.goupill.fr/lib/plugins/codeprettify/syntax/code.php on line 172
Warning: Undefined array key 1 in /home/goupillf/wiki.goupill.fr/lib/plugins/codeprettify/syntax/code.php on line 172
Warning: Undefined array key 1 in /home/goupillf/wiki.goupill.fr/lib/plugins/codeprettify/syntax/code.php on line 172
Warning: Undefined array key 1 in /home/goupillf/wiki.goupill.fr/lib/plugins/codeprettify/syntax/code.php on line 172
Warning: Undefined array key 1 in /home/goupillf/wiki.goupill.fr/lib/plugins/codeprettify/syntax/code.php on line 172
Warning: Undefined array key 2 in /home/goupillf/wiki.goupill.fr/lib/plugins/codeprettify/syntax/code.php on line 214
Warning: Undefined array key 2 in /home/goupillf/wiki.goupill.fr/lib/plugins/codeprettify/syntax/code.php on line 214
Warning: Undefined array key 2 in /home/goupillf/wiki.goupill.fr/lib/plugins/codeprettify/syntax/code.php on line 214
Warning: Undefined array key 2 in /home/goupillf/wiki.goupill.fr/lib/plugins/codeprettify/syntax/code.php on line 214
Warning: Undefined array key 2 in /home/goupillf/wiki.goupill.fr/lib/plugins/codeprettify/syntax/code.php on line 214
Warning: Undefined array key 2 in /home/goupillf/wiki.goupill.fr/lib/plugins/codeprettify/syntax/code.php on line 214
Warning: Undefined array key 2 in /home/goupillf/wiki.goupill.fr/lib/plugins/codeprettify/syntax/code.php on line 214
Warning: Undefined array key 2 in /home/goupillf/wiki.goupill.fr/lib/plugins/codeprettify/syntax/code.php on line 214
Warning: Undefined array key 2 in /home/goupillf/wiki.goupill.fr/lib/plugins/codeprettify/syntax/code.php on line 214
Warning: Undefined array key 2 in /home/goupillf/wiki.goupill.fr/lib/plugins/codeprettify/syntax/code.php on line 214
Warning: Undefined array key 2 in /home/goupillf/wiki.goupill.fr/lib/plugins/codeprettify/syntax/code.php on line 214
Warning: Undefined array key 2 in /home/goupillf/wiki.goupill.fr/lib/plugins/codeprettify/syntax/code.php on line 214
Table des matières
Chiffrement RSA
Nommé selon les initiales de ses inventeurs, en 1977 : Ronald Rivest, Adi Shamir et Leonard Adleman. Il s'agit d'un chiffre asymétrique, c'est à dire que la clé pour chiffrer est différente de la clé pour chiffrer.
Comme pour le chiffre de ElGamal, RSA exploite des propriétés arithmétiques des nombres premiers.
Cette technique avait déjà été découverte à Bletchley Park lors de la seconde guerre mondiale. Il s'agit du centre de renseignement où a été créé le fameux Colossus, calculateur destiné à casser le code Enigma utilisé par l'armée allemande. Mais cette invention, couverte par le secret défense, n'a pu être revendiquée par les Britanniques.
Création des clés publique et privée
Nombres premiers
Nous allons utiliser des nombres premiers. Pour les démonstrations, des nombres premiers peuvent suffire. Vous en trouverez une liste ici.
Mais la solidité de RSA est garantie par l'utilisation de nombres premiers très grands : de l'ordre de 500 à 1000 bits. $2^{1000} \approx 1000^{100} = 1\overbrace{000\cdots0}^{300\times}$.
À cette échelle, les nombres premiers sont trop nombreux pour être catalogués ou même recherchés de faaçon systématique. On se contente d'en choisir au hasard – voir cet exercice.
L'idée est de choisir aléatoirement un nombre de 512 bits et de faire une série de tests pour savoir s'il est premier. La série de tests pour chaque essai est suffisamment rapide et on sait qu'on a aux alentours de $\frac{1}{300}$ chance de trouver un premier dans cet zone. On trouve donc un premier assez vite au hasard.
Ces premiers étant très nombreux, la probabilité que deux personnes trouvent une même paire de premiers est négligeable.
Méthode
- Choisir $p$ et $q$ deux premiers.
Il existe quelques conditions pour que le chiffrement reste solide et évite certaines techniques de déchiffrement. Nous n'entrerons pas dans ces détails mais si vous êtes curieux, voir sur Wikipedia. - Calculer $n = p\times q$.
- Calculer $\varphi = (p-1)\times(q-1)$.
- Choisir $e$ premier avec $\varphi$ et inférieur.
On peut choisir $e$ premier, ce qui garantit que $e$ sera premier avec $\varphi$.
Pour faciliter les calculs, on préfère un $e$ dont l'écriture binaire n'ait pas trop de1. On peut choisir 3, 17 – pour les expérimentations – ou encore 65537. - Calculer $d$, inverse modulaire de $e \mod \varphi$.
Clé publique
La paire $(n,e)$ constitue la clé publique.
Clé privée
La paire $(n,d)$ constitue la clé privée.
À faire
Quelques questions
- Si je vous donne $(n, e) = (143, 17)$,
- trouvez $p$ et $q$,
- déduisez-en $\varphi$,
- déduisez-en $d$.
- Si je vous donne $(n,e) = (261\,589, 17)$, même question.
Vous voyez que c'est beaucoup plus long dans le second cas. Ce serait infaisable avec un $n$ de grande taille comme ce qu'on utilise avec RSA.
Implémentation
Écrivez une fonction Python make_keys(p, q) qui reçoit les premiers p et q et renvoie Kpu = (n,e) et Kpr = (n,d).
Optionnel : Pour aller plus loin, vous pouvez utiliser cet exercice pour produire des premiers de taille voulue et écrire make_keys(nbits) où nbits est un entier pair, par exemple 100. Dans cette version, on génère 2 premiers de nbits//2 bits puis on produit les paires Kpu et Kpr. Vous pouvez toujours choisir e parmi 3, 17, 65537 selon la taille des premiers considérés.
Chiffrer
On veut chiffrer l'entier $K$ connaissant $Kpu = (n,e)$
Découper en blocs
En binaire, on découpe $K$ en blocs $B$ dont les valeurs doivent être plus petites que $n$.
Par exemple, si $n > 255$,
- on pourra découper $K$ en octets (chaque octet sera $<n$)
- Avec $n= 1\,763$ et $K = 12\,596$, $K$ s'écrit en binaire $11000100110100_b$.
- $K$ utilise 14 bits, on complète par des $0$ à gauche :
$K = 00110001\,\, 00110100_b$ ce qui produit deux blocs $00110001$ et $00110100$. - Soit $B$ l'un des blocs. On a donc $0 \leqslant B < n$.
Notre clé sera un bytes obtenu directement par lecture de fichier. Le découpage sera donc déjà fait.
Exponentiation modulaire
Vous utiliserez la fonctionexponentiation_moddéfinie dans exponentiation_modulaire.
Pour chaque bloc $B$, on calcule $C \overset{n}{=} B^e$.
On prend soin d'écrire $C$ en binaire en utilisant la taille d'un bloc.
C'est à dire que si $C = 17 = 10001_b$ et que l'on a choisi des blocs de taille 8, il faudra écrire $C = 00010001_b$.
Regroupement
On joint les blocs dans un tableau.
À faire
Écrivez une fonction chiffrer_RSA(K, Kpu) où K est déjà découpée et qui renvoie la version chiffrée de K en ayant chiffré l'un après l'autre chaque constituant de K.
Pour simplifier, on souhaite que n > 255 et alors K est de type bytes.
>>> K = bytes([13, 151, 12]) >>> Kpu = (8383, 17) >>> chiffrer_RSA(K, Kpu) 7854, 4984, 5986
Si K est de type bytes, alors K est comme un découpage en octets. Si n > 255, on est tranquilles.
C'est plus compliqué pour le résultat : Suite au calcul $C \overset{n}{=} B^e$, même si $0 \leqslant B < 256$, on ne peut rien affirmer sur $C$, seulement que $C < n$. Donc $C$ ne tient pas forcément sur un octet. Par exemple :
>>> K = bytes([13, 151, 12]) >>> Kpu = (8383, 17) >>> chiffrer_RSA(K, Kpu) [7854, 4984, 5986]
Mais on voudrait que la réponse soit de type bytes. Il suffit pour cela de prévoir le nombre d'octets suffisant pour chaque morceau de réponse. Ici, n = 8383 tient sur 2 octets. On prévoit donc 2 octets pour chaque élément de la réponse. Par exemple
>>> n, e = Kpu >>> B = 13 >>> C = B**e % n # C = 7854 >>> C.to_bytes(2,'big') b'\x1e\xae' # c'est à dire, écrit en décimal, bytes([30, 174])
De sorte qu'avec ce choix :
>>> K = bytes([13, 151, 12]) >>> Kpu = (8383, 17) >>> chiffrer_RSA(K, Kpu) b'\x1e\xae\x13x\x17b' # soit bytes([30, 174, 19, 120, 23, 98])
Puisque K comptait 3 octets et que chacun de ces octets en a produit 2, on obtient 6 octets dans la réponse.
Idéalement, il faudrait que le nombre d'octets de la réponse s'adapte automatiquement à la valeur de n.
Déchiffrer
C'est exactement la même chose mais en utilisant $Kpr$ au lieu de $Kpu$.
Mais il faut tenir compte du problème de taille évoqué dans le cadre ci-dessus.
Prenons un exemple :
>>> Kc = bytes([30, 174, 19, 120, 23, 98]) # K chiffré >>> Kpr = (8383, 6753) # Kpr = n, d >>> chiffrer_RSA(Kc, Kpr) b'\r\x97\x0c' # c'est à dire bytes([13, 151, 12])
Nous sommes allés dans le sens contraire du cas précédent : d'éléments C sur 2 octets, nous obtenons des éléments B = C**d % n sur un seul octet. Donc de Kc sur 6 octets, nous retrouvons K sur 3 octets.
Lors de la lecture de Kc, vous devez donc prendre les octets 2 par 2.
Encore une fois, 2 n'est qu'un exemple pour notre n = 8383, mais pour un n plus grand il pourrait falloir 3 octets, ou 4, ou… 128 !
Pour ce faire, voici une manipulation possible :
>>> Kc = bytes([30, 174, 19, 120, 23, 98]) >>> int.from_bytes(Kc[2:4],'big') 4984
Ici Kc[2:4] permet de récupérer les octets d'indices 2 et 3 dans Kc. La fonction int.from_bytes permet d'assembler ces deux octets pour former un entier.
À faire
- Écrire une fonction
dechiffrer_RSA(Kc, Kpr) - Placer un texte quelconque dans un fichier
cle.txt Kest obtenu en lisant le contenu decle.txten mode binaire.- Choisissez
KpuetKprde sorte que255 < n < 65536
Vous pourrez faire plus grand si cela fonctionne. - Calculez
Kc = chiffrer_RSA(K, Kpu) - Vérifiez :
assert dechiffrer_RSA(Kc, Kpr) == K.
Justification
D'après le petit théorème de Fermat, on sait que si $q$ premier, pour $a$ non divisible par $q$, alors $a^{q-1} \overset{q}{\equiv} 1$.
Donc, prenons $B$ un élément de $K$.
- $C \overset{q}{\equiv} B^e$
- donc $C^d \overset{q}{\equiv} \left(B^e\right)^d = B^{e\cdot d}$
- on sait que $e\cdot d \overset{(p-1)(q-1)}{\equiv} 1$ donc $e\cdot d = 1 + k\cdot(p-1)(q-1)$ pour un certain $k$ (dont la valeur n'a pas d'importance)
- donc $C^d \overset{q}{\equiv} B^{1 + k\cdot(p-1)(q-1)}$
- On sait que $B$ ne peut être divisible à la fois par $p$ et $q$ à la fois. On peut supposer que $B$ n'est pas divisible par $q$ (même raisonnement si pas divisible par $p$)
- alors $C^d \overset{q}{\equiv} B \cdot \left(B^{q-1}\right)^{k\cdot(p-1)}$
- ce qui nous donne, d'après le petit théorème de Fermat :
$C^d \overset{q}{\equiv} B \cdot \left(1\right)^{k\cdot(p-1)} = B$
