Cryptage rc5 32/12/16

Description

Voici un code source pour le cryptage de chaines de caractères au moyen d'un algorithme RC5 (32 bits, 12 itérations, 16 octets de cle), sous forme d'une DLL créée avec lcc.

Je l'ai écrit en adaptant le code présenté dans un document de référence donnée en PJ écrit par Ronald L. Rivest, en utilisant les paramètres 'nominaux':
- mots de 32 bits
- 12 itérations
- 16 octets maxi pour la clé

Les deux fonctions exportées 'crypte' et 'decrypte' reçoivent trois pointeurs de caractères :
- PText (plain-text) : le texte à crypter
- CText (cypher-text): le résultat du cryptage
- Cle : la clé de cryptage(maximum 16 octets)

Les chaines sont traitées par séquences de caractères représentant chacune deux mots de 32 bits, passés aux fonctions RC5_ENCRYPT et RC5_DECRYPT telles qu'elles figurent dans le document de référence (je me suis contenté de remplacer le type 'WORD' par 'W32', du fait que mon compilateur avait déja une définition pour 'WORD).

En entrée du cryptage: les 8 octets sont transférés un à un à l'adresse du tableau de 2 mots, en passant par un casting *int > *char. Ceci pour éviter les problèmes liés aux différences d'implémentation des entiers selon les systèmes.

En sortie du cryptage, on a à nouveau deux mots binaires, que l'on transforme en séquence de 10 octets pour obtenir une clé relativement éditable:
- chaque octet est ramené à 7 bits pour rester dans le jeu standard ASCII 128
- si le résultat est inférieur à 48 on lui rajoute ce qu'il faut pour éviter d'avoir un code de controle ASCII ou un blanc.
- l'information nécessaire pour décoder (2 bits par caractère) est stockée dans un octet de report (un octet de plus à la suite de chaque groupe de 4 octets):
-- le bit de poids fort, à restituer
-- le bit indiquant si on doit ou non soustraire 48
La chaine cryptée comporte donc 10 octets par groupe de 8 caractères en entrée.

Traitement de la cle:
L'algorithme est basé sur la construction d'un tableau de codage utilisé pour le cryptage, calculé à partir de la clé et de 2 'magic numbers',par la fonction RC5_SETUP.
Lorsqu'une clé est passée, elle est étendue et conservée pour les appels ultérieurs. Lorsque la clé passée est vide ("") on travaille avec la clé enregistrée auparavant, si du moins on en a une (flag FlCle).

L'archive en téléchargement comprend:
- le document de référence en anglais (Rivest-rc5rev.pdf)
- un résumé que j'ai fait en français (Cryptage RC5.doc)
- le source C (inforom.c)
- la dll (inforom.dll)
- un exemple de code VBA pour test des appels depuis Excel (module1.bas)

Source / Exemple :


#include <windows.h>
/*----------------------------------------------------------------------*/
/* RC5REF.C  -- Reference implementation of RC5-32/12/16 in C    		*/
/* Copyright © 1995 RSA Data security. Inc.                             */
/*----------------------------------------------------------------------*/
/* les fonctions crypte et decrypte utilisent les algorithmes de cyptage*/
/* définis dans ce document de référence.								*/
/* 		RC5_SETUP: extension de la clé passée en argument sous forme de	*/
/*			chaine de caractères. Si l'appel de crypte ou decrypte ne 	*/
/*			ne comporte pas de cle ("") c'est la cle précdémment		*/
/*			traitée qui est utilisée									*/
/*			s'il n' y a pas de texte à crypter la fonction crypte ou 	*/
/*			decrypte se contente de réaliser l'expansion de la clé		*/
/*		RC5_ENCRYPT: cryptage d'un double mot							*/
/*		RC5_DECRYPT: decryptage d'un double mot							*/
/*	dans cette implémentation, on fixe les paramétres de cryptage		*/
/*		taille du mot: 32 bits											*/
/*		nombre d'itérations: 12											*/
/*		taille (maxi) de clé en octets: 16								*/
/*		(paramètres nominaux choisis par Ronald L. Rivest)				*/
/*																		*/
/*----------------------------------------------------------------------*/
/* crypte et decrypte travaillent sur des textes de longueur variable	*/
/* alloués par le module appelant.										*/
/* le résultat du cryptage est codé sous la forme d'une chaine de 		*/
/* caractères sur 7 bits (pour rester dans le jeu standard ASCII)		*/
/* et supérieurs à 48 (pour éviter les codes non éditables et blancs)	*/
/* la taille à allouer pour la chaine cryptée doit être au moins de 	*/
/* 5 fois le nombre de groupes de 4 caractères dans le texte à crypter	*/

/* typedef unsigned long int WORD ;  	/* should be 32 bits = 4 bytes	*/
/* on modifie à cause de win.h qui définit WORD sur 16 bits				*/
typedef unsigned int W32;
# define w		32		/*  word size in bits					*/
# define r		12		/* number of rounds						*/
# define b		16		/* number of bytes in key				*/
# define c		4		/* number of words in key				*/
						/* c =max(1,ceil(8*b/w)					*/
# define t		26		/* size of table S =2*(r+1) words		*/
W32 S[t] ;				/* expanded key table					*/
W32 pt[2],ct[2];		/* pour le passage des deux mots		*/
W32 P = 0xb7e15163, Q = 0x9e3779b9;	/* magic constants		*/
int FlCle =0;

/* Rotation operators, x must be unsigned, to get logical right shift		*/
# define ROTL(x,y)	(((x) << (y & (w-1))) | ((x) >>(w-(y&(w-1)))))
# define ROTR(x,y)	(((x) >> (y & (w-1))) | ((x) <<(w-(y&(w-1)))))

BOOL WINAPI __declspec(dllexport) LibMain(HINSTANCE hDLLInst, DWORD fdwReason, LPVOID lpvReserved)
{
	/* traitement des événements liés au chargement/déchargement			*/
/*  	 switch (fdwReason)
	    {
    	    case DLL_PROCESS_ATTACH:
			etc...
	    }

  • /
return TRUE; } /* fonction de cryptage, sur 2 mots binaires en entrée et en sortie */ void RC5_ENCRYPT( W32 *pt, W32 *ct) { W32 i , A= pt[0] + S[0] , B= pt[1] + S[1] ; for (i=1 ; i<=r ; i++ ) { A = ROTL ( A^B,B) + S[2*i] ; B = ROTL ( B^A,A) + S[2*i+1] ; } ct[0]= A ; ct[1]= B ; } /* fonction de décryptage, sur 2 mots binaires en entrée et en sortie */ void RC5_DECRYPT( W32 *ct, W32 *pt) { W32 i , B= ct[1] , A= ct[0] ; for (i=r ; i>0 ; i-- ) { B = ROTR (B - S[2*i+1], A) ^ A ; A = ROTR (A - S[2*i], B) ^ B ; } pt[1]= B - S[1] ; pt[0]= A - S[0] ; } /* fonction d'installation de la clé étendue */ void RC5_SETUP(unsigned char *K) { W32 i, j, k, u=w/8, A, B, L[c] ; int lc; /* initialize L, then S, then mix key into S */ lc=strlen(K); if (lc>b) lc=b; for (i=b-1, L[c-1]=0 ; i != -1 ; i--) { if (i>=lc) { L[i/u] = (L[i/u] << 8) + 0 ;} else { L[i/u] = (L[i/u] << 8) + K[i] ;} } for (S[0]=P, i=1 ; i<t ; i++ ) S[i] = S[i-1] + Q ; for (A=B=i=j=k=0 ; k< 3*t ; k++, i =(i+1)%t, j =(j+1)%c ) /* 3*t > 3*c */ { A = S[i] = ROTL ( S[i] + (A + B) ,3) ; B = L[j] = ROTL ( L[j] + (A + B), (A + B)) ; } FlCle=1; } /* fonction exportée pour le cryptage de PText, vers CText, selon Clé... */ BOOL WINAPI __declspec(dllexport) crypte(char *PText, char *CText, char *Cle) { /* fontion de cryptage */ /* le programme passe la chaine à crypter, et une adresse pour le résultat */ /* si une cle est passée, on fait le SETUP. Sinon on utilise la cle stockée */ int lt; /* longueur de la chaine en octets */ int ipt=0; /* indice du caractère courant dans le texte en entrée */ int ict=0; /* indice du caractère courant dans la chaine en sortie */ int i; unsigned char *sw; /* pointeur de caractères de travail */ int cx; /* octet de report pour le codage du résultat */ /* Mise en place de la cle si elle est donnée */ if (strlen(Cle)>0) RC5_SETUP(Cle); /* sinon on travaillera avec la cle courante, si elle existe */ if (FlCle ==0 ) return FALSE; /* taille du texte à crypter*/ lt=strlen(PText); /* on travaille par groupes de 8 octets pour le cryptage */ while (ipt<lt) { /* valeur à crypter*/ /* transfert des octets sur l'adresse du tableau d'entiers pt... */ sw= (unsigned char *)pt; for (i=0;i<8;i++) { if (ipt>=lt) sw[i]=0; else sw[i]=PText[ipt]; ipt++; } /* cryptage dans ct */ RC5_ENCRYPT(pt,ct); /* report dans la chaine résultat sous forme de caractères éditables */ /* chaque groupe de 4 octets est complété par un octet de report */ /* pour chaque caractère on prend les 7 bits de poids faible, */ /* et on rajoute 48 si nécessaire (pour éviter les codes de controle ASCII) */ /* on stocke l'info dans les 2 bits correspondants dans l'octet de report */ sw=(unsigned char *)ct; cx=0; for (i=0;i<8;i++) { /* on prend les 7 bits de droite */ CText[ict]=sw[i] & 0x7F; /* on stocke le bit de gauche dans le bit 1 de l'octet de report */ cx=cx+((sw[i] & 0x80)>>6); /* si le caractère est non imprimable on rajoute 48 et on marque */ /* le bit 0 de l'octet de report, pus on décale de 2 */ if (CText[ict]<48) { CText[ict]+=48; cx=cx+1;} ict++; if ((i==3) || (i==7)) { /* rajout de l'octet de report */ CText[ict]=cx; ict++;cx=0; } else { cx=cx<<2; /* on décale de 2 bits l'octet de report */ } } } CText[ict]=0; return TRUE; } /* fonction exportée pour le décryptage de PText, vers CText, selon Clé... */ BOOL WINAPI __declspec(dllexport) decrypte(char *CText, char *PText,char *Cle) { /* fontion de décryptage */ /* le programme passe la chaine à décrypter, et une adresse pour le résultat */ /* si une cle est passée, on fait le SETUP. Sinon on utilise la cle stockée */ int lc; /* longueur de la chaine cryptée en octets */ int ipt=0; /* indice du caractère courant dans le texte en entrée */ int ict=0; /* indice du caractère courant dans la chaine en sortie */ int i,j; int x=0; unsigned char xv; /* octet de report pour le décodage */ unsigned char *sw; /* pointeur de caractères de travail */ /* Mise en place de la cle si elle est donnée */ if (strlen(Cle)>0) RC5_SETUP(Cle); /* sinon on travaillera avec la cle courante, si elle existe */ if (FlCle ==0 ) return FALSE; lc=strlen(CText); /* on travaille par groupes de 10 octets (2* (4+1) ) */ /* ict est l'offset du début de groupe dans la chaine */ while (ict<lc) { /* valeur à décrypter*/ /* décodage de la chaine CText vers l'adresse du tableau d'entiers ct... */ /* chaque groupe de 5 de caractères est traansformé en un entier */ /* boucle sur le nombre de double-mots (10 octets à chaque fois) */ /* pour remplir les entiers ct[] octet par octet on s'appuie sur un ptr sw */ sw= (unsigned char *)ct; /* première phase: on prend tels quels les octets. */ /* On traitera ensuite les octets de contrôle */ j=0; for (i=0;i<10;i++) { if ((i!=4) && (i!=9)) { /* on prend l' octet tel quel */ sw[j]=CText[ict+i]; /* l'indice j permet d'avancer en sautant les octets de contrôle */ j++; } } /* traitement du premier octet de report */ xv=CText[ict+4]; /* les couples de bits sont traités de droite à gauche */ /* on traite un par un et on décale au fur et à mesure */ for (i=3;i>=0;i--) { /* bit de droite pour enlever 48 */ x=xv & 1; if (x==1) sw[i]=sw[i]-48; xv=xv>>1; /* nouveau but de droite: reporté en poids fort */ x=xv & 1; if (x==1) sw[i]=sw[i] | 0x80; xv=xv>>1; } /* deuxième octet de controle: même chose */ xv=CText[ict+9]; for (i=7;i>=4;i--) { /* bit de droite pour enlever 48 */ x=xv & 1; if (x==1) sw[i]=sw[i]-48; xv=xv>>1; /* nouveau but de droite: reporté en poids fort */ x=xv & 1; if (x==1) sw[i]=sw[i] | 0x80; xv=xv>>1; } /* saut vers le prochain groupe de 10 octets */ ict+=10; /* decryptage dans pt */ RC5_DECRYPT(ct,pt); /* report dans la chaine de départ, octet par octet */ /* ipt est l'offset courant du groupe de 8 caractères dans la chaine PText */ sw=(unsigned char *)pt; for (i=0;i<8;i++) { PText[ipt]=sw[i]; ipt++; } } PText[ipt]=0; return TRUE; } unsigned long WINAPI __declspec(dllexport) teste( int i, int j) { if (i==0) return pt[j]; if (i==1) return ct[j]; return 0; }

Conclusion :


AVERTISSEMENT: le document de référence que j'utilise a été péché sur le NET, je n'ai PAS VERIFIE LA SOURCE!. Si quelqu'un peut s'en charger ... cependant il existe déja pas mal de documentation sur le sujet, et notamment sur l'auteur.

L'implémentation que j'ai faite n'admet pas de paramètre supplémentaire. Je trouve que c'est largement suffisant pour mes besoins, d'autant plus que ce sont les paramètres que Rivest lui-même a choisis pour les essais officiels. On peut éventuellemtn améliorer le code pour permettre uen extension des paramètres.

Enfin.. j'ai utilisé le C à très forte dose pendant toute la décennie 80, mais aujourd'hui je m'y remets et... j'ai l'impression de repartir à zéro. Par ailleurs je n'ai pas cherché à optimiser le code. Cela pou rm'excuser à l'avance vis à vis de tous ceux qui trouveront à redire...

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.