Date : 10/06/2003
Auteur
: Nocte
Sujet : ENCRYPTEUR
EN C .
La programmation en C style
BTS est soûlante, vous en conviendrez...
Alors, en considérant
que vous connaissiez déjà les bases élémentaires
de
programmation en C, on va s'intéresser à la pratique. Ainsi, voici
un
programme que nous allons réaliser : un encrypteur en C que nous
allons
décortiquer dans les moindres détails afin d'apprendre
par la pratique
d'une part la méthode de programmation d'un logiciels
(architecture de
celui-ci) et, d'autre part, des bases indispensables.
I.
LE PROGRAMME
----------------
Commençons à étudier le programme : il commence par une série
de #include
et puis par la fonction main:
#include "stdio.h"
#include "conio.h"
#include "dos.h"
void main(void)
{
Ceci est
aussitôt suivi des déclarations des variables :
FILE *sourc,*dest;int
code,verif,car=0,i,j,sur,oct;
char src[60],dst[60];struct date d;
struct
n'est pas tout à fait un type de variable, c'est en fait une structure
de
données : un groupe contenant plusieurs données. FILE est aussi
une
structure mais contenant des informations sur le fichier (son handle, sa
taille,
ses droits d'accès...) alors que date contient 3 données : jour,
mois,
année ; celles-ci sont toujours appelées de la même manière
(respectivement
da_day,da_mon et da_year). Mais comme elles sont comprises
dans une structure,
pour les utiliser il faut utiliser le nom de la structure
juste devant
avec un point. Ici le nom de la structure est d. Ensuite, nous
avons les
premières fonctions d'initialisation :
getdate(&d);clrscr();
La
fonction clrscr() définie dans conio.h sert à effacer l'écran
alors
que la fonction getdate( struct date) permet d'initialiser une structure
date
à la date actuelle, c'est à dire mettre dans da_day le jour, dans
da_mon
le mois et dans da_year l'année.
Ensuite une petite routine permet de
vérifier le millénaire actuel afin
de n'afficher la date qu'avec
l'année sur 2 chiffres :
if(d.da_year<2000)
{d.da_year-=1900;}
else
{d.da_year-=2000;}
Ceci fait, le programme
affiche quelques informations :
printf("\t\tEncrypteur\n");
printf("%d/%d/%d\n",d.da_day,d.da_mon,d.da_year);
On
constate que lors de l'affichage de la date, les variables sont référencées
par
d.da_day,d.da_mon et d.da_year...
Ensuite, une routine nous permet de demander
le nom du fichier que l'utilisateur
souhaite encrypter :
get_name:
printf("Name
of the file to encrypt or decrypt (with full path):\n");
scanf("%s",src);
sourc=fopen(src,"rb");
if(sourc==NULL)
{
printf("\aError
opening file:(C)hange/(Q)uit\n");
ask_erase:
car=getch();
switch(car)
{
case 99:
goto get_name;
case 113:
goto
end;
default:
goto ask_erase;
}
}
A
l'aide de la fonction scanf(), nous récupérons le nom du fichier
d'origine,
lui attribuons le pointeur de type FILE src via la fonction fopen(),
puis
vérifions l'état du pointeur src. Si celui-ci vaut NULL,
cela signifie
qu'il s'est produit une erreur lors du chargement du fichier
Si c'est le
cas, nous demandons à l'utilisateur s'il veut ressaisir
un nom ou quitter
le programme. Pour cela nous utilisons la fonction getch()
combinées à
une structure conditionnelle switch() et_code:
code=0;verif=0;
printf("Entrez votre clé de cryptage:");
get_key:
car=getch();
switch(car)
{
case 13:
goto confirmation_code;
case 27:
clrscr();
printf("Etes-vou sûr ?(Y/N)");
ask_other:
switch(sur)
{
case 121:
sur=getch();
goto end;
case
110:
clrscr();
printf("Fichier à encrypter / décrypter:\n%s\n",src);
goto get_code;
}
goto ask_other;
default:
code +=car;
printf("*");
goto get_key;
}
confirmation_code:
printf("\nConfirmez votre
clé de cryptage:");
reget_key:
car=getch();
switch(car)
{
case 13:
goto test_cod;
case 27:
clrscr();
printf("Etes-vous
sûr ?(Y/N)");
re_ask:
sur=getch();
switch(sur)
{
case 121:
goto end;
case 110:
printf("\nFIchier à
encrypter / décrypter:\n%s\n",src);
goto get_code;
}
goto re_ask;
default:
verif +=car;
printf("*");
goto reget_key;
}
Cette routine permet donc de stocker dans les variables code et vérification
la
somme des codes ASCII des touches frappées par l'utilisateur en tant
que
code. C'est une des failles du programme dans la mesure ou les mots
de passes
"bc" et "ad" seront codés de la même manière
...
test_cod:
if(code==verif)
{
printf("\nOK...starting working");
goto encrypt;
}
else
{
clrscr();
printf("Erreur
de confirmation de la clé de cryptage...");
code=0;verif=0;
printf("\nFichier à encrypter ou décrypter:\n%s\n",src);
goto get_code;
}
Cette routine permet de vérifier que le code rentré
puis confirmé sont
bien les mêmes. Si c'est le cas, le programme
poursuit l'exécution, sinon
il revient à la demande de code après
avoir réinitialisé les variables.
encrypt:
printf("\nNOm du fichier encrypté / décrypté à
générer (full path)
:\n");
scanf("%s",dst);
dest=fopen(dst,"rb");
if(dest!=NULL)
{
fclose(dest);
printf("Ce fichier existe déjà : (O)verwrite / (C)hanger
son nom?");
re_ask2:
sur=getch();
switch(sur)
{
case 99:
clrscr();
goto encrypt;
case 111:
goto crypt;
default:
goto re_ask2;
}
}
Ce passage demande à l'utilisateur le nom du fichier vers
lequel il veut
crypter ou décrypter le fichier source. Si celui-ci existe
déjà, il lui
demande s'il désire l'effacer .Pour cela,
nous essayons d'abord d'ouvrir
le fichier en mode lecture. Si le pointeur contient
NULL, cela veut dire
que le fichier n'existe pas. Sinon, le fichier existe
déjà. Pour la demande
à l'utilisateur, nous utilisons
encore une structure conditionnelle switch().
crypt:
for(car=0;code<0;code+=256);
for(car=0;code>255;code-=256);
fclose(dest);
dest=fopen(dst,"wb");
Cette
routine permet de préparer la procédure principale d'encryptage
en
ouvrant le fichier destination et en préparant la clé de cryptage
en la
remettant sur un octet c'est-à-dire entre 0 et 255 compris. Nous
le faisons
au moyen de la structure conditionnelle for();
cryptage:
oct=fgetc(sourc);
if(feof(sourc))
{goto file_end;}
oct +=code;
for(sur=0;oct<0;oct+=256);
for(sur=0;oct>255;oct-=256);
oct=255-oct;
fputc(oct,dest);
goto
cryptage;
Voici
la routine d'encryptage : celle-ci consiste a lire un octet du fichier,
à
y ajouter le code de l'utilisateur, à le rétablir sur un octet c'est-à-dire
entre
0 et 255 compris et à faire l'inverse bit-à-bit ce qui consiste
à
soustraire la valeur à 255. Le résultat de cette soustraction
est écrite
dans le fichier de destination. Si l'octet lu est le dernier,
on quitte
la rutine d'encryptage sinon on continue. Les fonctions fgetc() et
fputc()
permettent de respectivement lire et écrire un octet dans un
fichier.
file_end:
clrscr();
fcloseall();
Cette série de
2 instructions permet d'une part d'effacer l'écran mais
aussi de fermer
tous les fichiers ouverts grâce à fcloseall(). Ceci est
très
important dans la mesure ou les données ne sont écrites sur le disque
qu'après
fermeture des fichiers .
printf("Delete old file: %s (y/n)?",src);
re_ask3:
i=getch();
switch(i)
{
case 121:
sourc=fopen(src,"rb");
fputc(0,sourc);
fclose(sourc);remove(src);
goto pre_end;
case
110:
goto pre_end;
default:
goto re_ask3;
}
Ici,
nous demandons à l'utilisateur s'il souhaite effacer le fichier source.
Si
c'est le cas, nous n'allons pas directement effacer moyen de la command
remove().
En effet, celle-ci ne protège pas contre les programmes comme
undelete
qui permettrait de retrouver le fichier . Nous allons d'abord
l'ouvrir en écriture
de manière à effacer son contenu puis nous y écrivons
juste
un octet nul , nous le refermons et l'effaçons ensuite avec remove.
Il
est toujours accessible via undelete mais on accède à un fichier
vide.
pre_end:
printf("\nFin du cryptage...\nAutre fichier à encrypter / décrypter
(Y/N)?\n");
re_ask4:
car=getch();
switch(car)
{case 121:
oct=0;goto get_name;
case 110:
goto end;
default:
goto re_ask4;
}
end:
clrscr();
printf("\t
coded by Nocte\
\n\tAll rights reserved Juin 2003");
fcloseall();
}
Alors,
ça vous a plu ? Voilà ce programme fini. Ne vous inquiétez
pas,
nous irons plus loin la prochaine fois et développerons les points
qui
vous auront parus obscurs : pour cela, n'hésitez pas, balancez vos
soucis
sur le forum. J'espère que cet article vous aura appris des choses
intéressantes.