Explication de N-Alpha

Ecrit par N-0-X, le 15 Jan 2006 dans la catégorie Cryptographie / Stéganographie

Tout a commencé un beau matin, il y a deux jours, lorsque je lisais un disque de cryptage pour enfants sur une petite boîte de carton. J'y ai vu un premier cercle dans lequel était inscrit notre alphabet habituel, composé de 26 caractères, et, dans un cercle plus petit à l'intérieur du premier cercle, un autre alphabet das lequel les lettres étaient placées aléatoirement. C'était l'illumination, bien qu'habituellement je ne sois pas un spécialiste de cryptologie.
Bref, je me suis lancé dans un projet : faire un code qui génèrerait une clé et à partir d'un alphabet choisi, crypter un message. En fait, la clé en question est un nouvel alphabet dans lequel les lettres sont placées aléatoirement.


Idées de bases

Pour ceux qui liraient en diagonale, voici un récapitulatif des buts du programme (le 14/01/2006):

  1. générer un nouvel alphabet via des nombres aléatoires
  2. sélectionner un alphabet (avec/sans majuscules, etc.)
  3. crypter le message
Si vous mettez le nom d'un fichier en argument avec une option spéciale, alors le programme ira chercher dans le fichier le clé de cryptage, le message à crypter et l'alphabet puis écrira le message crypté à la fin du fichier en question.
Nous verrons au cours de cet article qui explique le code (certains m'ont dit qu'il était illisible ... n'est-ce pas Warfo :P) toutes les options que j'y ai intégré et leur fonctionnement.
J'ai choisi de le code en Python parce que ce langage est extrêment pratique pour la manipulation de chaînes de caractères (strings).
Pour le code complet et un historique du programme, voir la page de projets.


Les variables utilisées

VERSION = 0.06 # le 14/01/2006
import sys, random, string, os

types = ['--long-numeric','--numeric','--long-alpha','--alpha','--all-alpha',
\'--g-key-short','--g-key-long','--g-key-all','--g-alpha','--file'] a_low = string.lowercase a_ltr = string.letters d_all = string.digits a_all = (string.printable)[:95] # on ne sélectionne que les 95premiers caractères pour
ne pas avoir '\t\n\r\x0b\x0c'

Un module de Python très pratique est string, qui a notamment des fonctions prédéfinies telles lowercase qui transforme toutes les lettres en minuscules (les lettres avec des accents sont des caractères spéciaux). À partir d'ici, nous avons :
a_low = 'abcdefghijklmnopqrstuvwxyz'
a_ltr = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
d_all = '0123456789'
a_all = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*\
+,-./:;<=>?@[\\]^_`{|}~ '

Dans la liste types, il y a toutes les options possibles. On m'a conseillé sur le canal #python sur irc.freenode.net d'utiliser un module permettant de faire toutes les options avec leur aide respective etc. plus facilement mais ne l'ayant jamais utilisé, je ne m'en suis pas servi, ce sera pour la version suivante =).

Les défintions

help()

Je ne pense pas que je vais beaucoup m'attarder sur cette défintion qui est assez explicite de nom puisqu'elle affiche simplement l'aide en tuant le programme. Je l'affiche s'il y a une erreur.

raise SystemExit(die)
Cette ligne est une manière pratique de tuer un programme Python. Il existe bien d'autres raise qui sont utilisables pour différentes raisons (par exemple SystemError retournera la ligne, la classe et la défintion dans laquelle le raise a été appelé).


f_help()

Cette définition n'est appelée que si un fichier comportant une clé, un alphabet et un message n'a pas le bon format. En effet, pour simplifier la tâche, voici comment le fichier devrait être :

---ALPHABET---
abcdefghijklmnopqrstuvwxyz

---KEY---
uigkjtpmfbcryhwxvsqlazndoe

---MESSAGE---
thisisasimplemessage


plist(ltp,sp)

Cette défintion, très simple, permet simplement d'afficher des listes lisiblement. La variable ltp est la variable contenant la liste et la variable sp qui est un nombre entier (int[eger]) ne sert qu'à savoir s'il faut ajouter ou non un espace entre chaque élément de la liste.


def newkey(ct_)

Ca se complique enfin !
neykey sert dans deux cas : lorsqu'on appelle le générateur de clés (en utilisant l'option --g-key-*) ou lorsqu'on veut crypter un message. La variable ct contient l'option passée au script (nous verrons les options à la fin de l'article). Comme vous avez pu le voir dans la liste types, il y a des sortes de sous-options : "long" et "all". Cela permet simplement de choisir l'alphabet. En effet, si 'long' est choisi, alors la clé est de 52 caractères (voir la variable a_ltr); si 'all' est choisi, alors la clé comporte 95 caractères (si rien d'autre n'est choisi, option '--numeric' par exemple).

global num_key; num_key = list() #creates an empty list
On met global devant num_key pour que la clé numérique soit considérée comme globale à tout le script. Cela signife que l'on n'aura pas besoin de la passer en tant qu'argument dans la définition de cryptage.
while x < kl:
	rand = int(random.uniform(0,kl))
	if rand not in num_key: num_key.append(rand); x=x+1
C'est là où les nombres aléatoires jouent leur rôle : on génère des nombres aléatoires puis on vérifie si le nombre généré est dans la liste. Si le nombre ne l'est pas, alors on l'ajoute à la liste. On fait ensuite de même avec la clé alphabétique si 'alpha' est dans l'option.


crypt(mss,ct_)

On commence encore une fois par vérifier quel alphabet utiliser, toujours en vérifiant si "long" est dans l'option.

if 'alpha' in ct_: # si un cryptage alphabétique est demandé alors ...
	alpha_c = list(); # on créé une liste
	for x in range(len(mss)):
		alpha_new = alpha_key[ltr.index(mss[x])] # voir plus bas
		alpha_c.append(alpha_new) # on ajoute à la liste
	plist(alpha_c,0)
Explication de la ligne compliquée du code ci-dessus. Tout d'abord, d'après la définition newkey, alpha_key est la clé alphabétique. On va donc chercher dans le bon alphabet ltr la position de chacune des lettres du message mss (voir les lignes du code précédant le listing pour comprendre d'où vient ltr).
num_c = list();
for jt in range(len(mss)):
	num_new = num_key.index(ltr.index(mss[jt]))
	num_c.append(num_new)
plist(num_c,0)
On va crypter le message numériquement, ce qui signifie que le message sera toujours crypté deux fois : une fois numériquement (cela retournera une chaîne de chiffres) et une fois aplhabétiquement si spécifié. Je pense que vous avez trouvé la ligne compliquée que je vais expliquer. num_key.index() permet d'avoir la place du caractère demandé dans la clé numérique. Ainsi, on prend une première fois la position du caractère du message dans l'alphabet, puis la position de ce même caractère dans la clé. On ajoute enfin le tout à la liste qui sera, via la définition plist, la chaîne cryptée.


fcrypt(file)

fcrypt à la même fonction que crypt à la différence près que le fcrypt va tout piocher (message, clé, alphabet) dans le fichier spécifié. Vous aurez remarqué que je ne vérifie pas l'existence du fichier dans cette définition puisque je le fait dans le code hors-définition que nous verrons plus tard.

if lines[0] != '---ALPHABET---\n' or lines[3] != '---KEY---\n' or \
lines[6] != '---MESSAGE---\n': print "Error: wrong file format"; f_help() alpha = lines[1]; key = lines[4]; mess = lines[7] # attribution aux variables

On vérifie le bon formattage du fichier (voir l'explication de f_help()).
for x in range(len(mess)):
	  if mess[x] not in alpha or mess[x] not in key: print "Error:\tplease be sure \
that every character in your message is in the alphabet and in the key"
;help() else: new = key[alpha.index(mess[x])]; cyph_m.append(new)

À la deuxième ligne de ce listing, on vérifie si tous les caractères du message existent dans la clé et dans l'alphabet. Dans le cas contraire, on arrête le script avant qu'une erreur se produise. Si tout va bien, on crypte le message de la même façon que l'on a crypté dans la définition crypt en mode alphabétique.
f.seek(0,2)
print >> f, '\n---CRYPTED MESSAGE---\n'+ciphered
f.close()

Avec f.seek(0,2), on se promène jusqu'à la fin du fichier, puis, avec la ligne suivante, on ajoute au fichier le message crypté (le ficiher était ouvert avec l'option "a+" nous permettant d'ajouter des bytes au fichier et d'y lire (c'est là l'utilité du signe +)).

Fin des défintions, passons au code même, très court et très simple ;).


Le code

On commence par vérifier la présence d'arguments : s'il n'y en a aucun, on arrête le script en affichant l'aide. Puis, dans le else, on commence par définir la variable ct comme l'option (argument n°1). Ensuite viennent les étapes de vérification de l'option : si c'est une option de génération de clé ou d'alphabet, on affiche ce qu'il faut =). Si l'utilisateur demande une clé, alors on ajoute discrètement "alpha" à la variable 'ct' pour ne pas avoir à refaire une définition identique à new_key. Ensuite, si un fichier est proposé en tant que second argument, alors on vérifie l'existence du fichier, puis on affiche un message prévenant que des erreurs peuvent se produire dans le cadre de l'utilisation d'une clé personalisée.
Enfin, si ce n'est rien de tout ça, on fait le script normalement.

N'oubliez pas d'aller voir la page de projets pour plus d'infos sur l'avancée du code.

Si vous avez un commentaire à faire à l'auteur de cet article, cliquez ici !