Version complète du protocole de Diffie-Hellman⚓︎
Gérer des messages plus grands
Le problème de la version précédente, c'est qu'avec une clef de 1024 bits, le texte ne peut pas dépasser 128 caractères. Pour palier cela, il faut découper le texte en plusieurs morceaux et les chiffrer indépendamment. On convertit le texte en binaire, que l'on découpe en blocs de 512 bits (ou la moitié de la longueur de la clef). Le dernier bloc peut être plus petit. Pour chacun des blocs, on complète les 512 bits manquants de la manière suivante :
- On rajoute un octet à gauche correspondant au nombre d'octets du message d'origine (512 bits = 64 octets).
- On complète entre cet octet et ceux du messages avec des bits tirés au hasard.
Puisque le premier octet vaut au maximum 64, le premier bit est forcément 0, ce qui garantit que le bloc obtenu est inférieur à la clef. L'ajout de bits aléatoires permet d'assurer que si deux blocs du messages sont identiques, leurs chiffrements seront différents.
Le padding
Le fait d'ajouter des bits aléatoires à des données pour obtenir un bloc d'une taille donnée s'appelle le padding (ou remplissage en français). La fonction suivante permet d'effectuer cette opération, avec binaire
qui est un morceau du message dont la longueur est inférieure ou égale à 512 bits.
def padding(binaire, taille_bloc=1024):
reste = taille_bloc - len(binaire)
nb_octets = len(binaire) // 8 # il faut que ce soit un multiple de 8
tete = entier_binaire(nb_octets)
padd = "".join([random.choice("01") for _ in range(reste-8)])
return tete + padd + binaire
Chacun des blocs peut alors être chiffré avec la clef, ce qui produira des blocs de 1024 bits qui peuvent être recollés.
Pour enlever le padding, il faut regarder le premier octet pour savoir combien d'octets garder à la fin. La fonction suivante permet de faire cette dernière opération :
def enleve_padding(bloc):
tete = bloc[:8]
nb_octets = binaire_entier(tete)
return bloc[len(bloc)-8*nb_octets:]
La nouvelle fonction de chiffrement
Pour chiffrer un texte, on chiffre donc le padding de chaque morceau du texte.
import math
def chiffrement(texte, clef, p, taille=1024):
binaire = texte_binaire(texte)
taille2 = taille//2
n = math.ceil(len(binaire)/taille2) # on arrondit au dessus
final = ""
for i in range(n):
bloc = binaire[i*taille2:(i+1)*taille2]
nb = binaire_entier(padding(bloc, taille))
nb2 = (nb * clef) % p
final = final + entier_binaire(nb2, taille)
return final
La nouvelle fonction de déchiffrement
Pour déchiffer, il faut découper le message chiffré en blocs de 1024 bits, le déchiffrer avec l'inverse de la clef puis le reconvertir en binaire, qui correspond au padding, qu'on retire. Il suffit de recoller les blocs pour retrouver le message original.
def dechiffrement(binaire, clef, p, taille=1024):
inv = inverse(clef, p)
n = len(binaire)//taille
final = ""
for i in range(n):
bloc = binaire[i*taille:(i+1)*taille]
nb = binaire_entier(bloc)
nb2 = nb * inv % p
bloc2 = entier_binaire(nb2, taille)
final = final + enleve_padding(bloc2)
return binaire_texte(final)
Exercice 24
Tester les fonctions de chiffrement et de déchiffrement avec des messages longs. Si vous n'avez pas d'idées, vous pouvez, par exemple, prendre des choses comme "bonjour "*200
Pour aller plus loin
Le fichier client_dh.py
permet d'échanger des messages sur le réseau en utilisant un échange de clef de Diffie-Hellman. L'ordinateur qui reçoit les messages doit utiliser l'instruction serveur()
.
Pour envoyer un message, il faut utiliser l'instruction envoyer(message, ip)
, où ip
est un texte correspondant à l'adresse IP du serveur.
# Tests
(insensible à la casse)(Ctrl+I)