J’ai récemment eu l’occasion de mettre la main sur l’ouvrage d’André Langié : De la cryptographie, dans son édition de 1914.
Il y détaille un certain nombre de méthodes de chiffrement, puis des exemples de déchiffrements, dont la lecture est une délectation comparée à ce qu’on trouve maintenant sur le sujet (je parle du style, qui est vraiment très plaisant (en tous cas il me plaît… ).
Je propose ici quelques pages scannées sur une méthode utilisant une grille de chiffrement (chiffrement par transposition : les lettres ne sont pas modifiées, mais mélangées) qui n’est pas terriblement perfectionnée ni efficace, comme le faisait déjà remarquer Langié au début du siècle dernier, mais est assez amusante à programmer (exemples en Python 3)
Commençons par l’extrait de l’ouvrage :
Pour retrouver le message caché, on utilise donc, dans l’exemple de Langié, une grille 6×6 (pour un message de taille 36), comportant 16 trous (qui ne peuvent pas être disposés n’importe comment). Cette grille est apposée sur le texte chiffré et on recopie simplement le contenu des cases visibles qui constituent les 16 premiers caractères du message en clair. Puis, on tourne la grille trouée d’un quart de tour et on copie les 16 lettres suivantes. Lorsque cette opération de recopie aura été faite 4 fois, et si la grille percée est correcte, chaque lettre aura été utilisée une et une seule fois et le message sera complètement déchiffré.
Nous allons représenter une grille par la liste ordonnée des trous (il faut aussi conserver la taille d’un côté, 6 dans l’exemple). La case située en haut à gauche est la case 0, puis on numérote en se déplaçant vers la droite, puis vers le bas.
Voici la représentation de la grille utilisée dans l’ouvrage de Langié :
g=[0, 4, 7, 14, 16, 23, 24, 26, 33]
Nous aurons besoin d’une fonction permettant de calculer la position d’un trou après une rotation d’un quart de tour. Ce calcul dépend du côté de la grille. La formule a l’air un peu compliquée, mais elle traduit simplement le fait que lors d’une rotation, le numéro de colonne devient numéro de ligne et le numéro de ligne devient le numéro de colonne en partant de la fin (en comptant à partir de la colonne de droite) :
def rotate(i,n):
"""
Calcule l'image de la case i par une
rotation d'un quart de tour (grille nxn)
"""
return n * (i % n) - i // n + n - 1
On vérifie facilement que la rotation fonctionne :
>>> rotate(0, 6)
5 # case en haut à droite
>>> rotate(5, 6)
35 # case en bas à droite
>>> g=[0, 4, 7, 14, 16, 23, 24, 26, 33]
>>> sorted([rotate(k, 6) for k in g])
[1, 5, 10, 13, 15, 18, 27, 29, 32]
La dernière liste donne la position des 9 trous de la grille après une rotation d’un quart de tour (on pourra comparer avec les illustration de l’ouvrage ci-dessus).
Le déchiffrement se fait donc en lisant les caractères de la chaîne chiffrée dont le numéro est dans la liste des trous.
Puis on calcule la nouvelle liste par une rotation d’un quart de trou et on recommence (dans cette fonction j’utilise les annotations, définies dans la PEP 3107, pour préciser le type des arguments et le type de retour) :
def dechiffre(txt: str, grille: list) -> str:
""" Déchiffre le text <txt> avec la <grille> """
nb = len(g) * 4
sg = int(nb ** .5)
chif = []
while len(chif) < len(txt):
chif.extend([txt[c] for c in grille])
grille = sorted([rotate(k, sg) for k in grille])
return "".join(chif)
Si la chaîne passée en paramètre est trop courte (ça ne devrait pas être le cas), elles est complétée ici avec des espaces.
On peut maintenant tester sur l’exemple donné par Langié :
>>> g = [0,4,7,14,16,23,24,26,33]
>>> s = 'ALOERENOTIIRSNMSESPDIUXLEMSORUOAVPII'
>>> dechiffre(s,g)
'AROMELESPLEINSPOUVOIRSDURAIENTSIXMOI'
Je ne résiste pas à la tentation de vous laisser lire le passage où Langié explique comment il a retrouvé le texte d’un message chiffré avec une grille (si ça vous simplifie la lecture, il y a un fichier pdf à télécharger).
Bonne lecture,