[This article appeared on OndaQuadra0B Elettronic Magazine - Mar 2004 ]
OpenSSL/DES,OpenSSL/Blowfish,OpenSSL/IDEA,OpenSSL/MD5: implemtentation
UNISFED written and coded by Paolo Ardoino <paolo.ardoino@gmail.com>
0. Intro
1. Descrizione algoritmi
2. Funzioni & headers DES
3. Funzioni & headers Blowfish
4. Funzioni & headers IDEA
5. Funzioni & headers MD5
In questo articolo vedremo come implementare quattro tra gli algoritmi
piu' famosi e piu' usati nel campo della crittografia:
DES,Blowfish,IDEA,MD5
1. Descrizione algoritmi
DES[Data Encryption Standard]:
Il DES fu sviluppato dall'IBM nel 1970, divento' standard nel 1976
e fu adottato dal governo statunitense nel 1977 ed e' stato utilizzato
per lungo tempo dal governo e da aziende private.
Il DES e' un block cipher (cifrario a blocchi) che lavora con blocchi
di 64 bits (8 bytes), utilizzando chiavi a 56 bits.
Su ogni blocco vengono effettuate delle permutazioni oltre che 16
cicli di xor e permutazioni.
Nel 1998 e' stata dimostrata la vulnerabilita' di questo algoritmo,
da parte dell' EFF, utilizzando un attacco di tipo brute force.
Per ovviare a questo problema e' stato introdotto il 3DES, che e'
basato su una ripetizione del cifrario DES, utilizzando chiavi a 112
bits.
Blowfish:
Nel 1993 Bruce Schneier sviluupo l'algoritmo conosciuto come Blowfish.
Come il DES opera su blocchi di 64 bits. Blowfish puo' operare con
chiavi fino a 448 bits, anche se tipicamente si utilizzano chiavi
a 128 bits. L'utilizzo di Blowfish presenta molti vantaggi a partire
dalla sua velocita' e compattezza fino ad arrivare alla sicurezza
infatti non si conoscono attacchi efficaci, inoltre e' un algoritmo
non patentato.
IDEA[International Data Encryption Algorithm]:
L'algoritmo IDEA lavora con chiavi a 16 bytes (128 bits), su blocchi
di 64 bits. E' basato su 8 cicli identici seguiti da una trasformazione
dell'output.
MD5[Message Digest 5]
MD5, creato nel 1991 da Ronald Rivest e' una funzione one-way hashing,
cioe' prende un messaggio e ne deriva una stringa di un 16 bytes
chiamata anche message digest.
2. Funzioni & headers DES
#include <openssl/des.h>
Poiche' la password e' 64 bits (anche se poi e' ridotta a 56 bits) e
operiamo su blocchi di 8 bytes, quindi dovremmo operare su blocchi di
64 bits. Quindi dichiariamo che sia la password che i messaggi in
chiaro (plaintext) che quelli cifrati (cipher text) dovranno essere
blocchi di 64 bits; per fare questo introduciamo il tipo des_cblock.
Il tipo dello scheduling della password e' des_key_schedule.
Ora possiamo passare alle funzioni:
/* des_read_pw_string(): scrive la string contenuta in prompt,
leva l'echo sulla stdin e legge la password in input. La password
viene inserita in buf ed e' al massimo lenght caratteri. Se verfify
e' posto a 1 chiede la riconferma della password. */
int des_read_pw_string(char *buf, int lenght, const char *prompt,
int verify);
/* des_string_to_key(): converte la stringa contenuta in str in una
chiave DES a 64 bits */
void des_string_to_key(const char *str, des_cblock *key);
/* des_set_key_checked(): controlla se la chiave passata e' dispari
e imposta loschedule della chiave */
int des_set_key_checked(const_des_cblock *key,
des_key_schedule schedule);
/* des_ecb_encrypt(): e' la routine di cifratura base per il DES
offerta dalla libreria. Cifra/Decifra blocchi di 8 bytes a seconda
che mode sia DES_ENCRYPT o DES_DECRYPT. ks e' il key schedule */
void des_ecb_encrypt(const_des_cblock *input, des_cblock *output,
des_key_schedule ks, int enc);
Per informazioni sulle funzioni non illustrate qui: man des
3. Funzioni & headers Blowfish
#include <openssl/bf.h>
Per leggere la password da input usiamo la stessa funzione che
utilizziamo per il DES: des_read_pw_string() [leggete la sez. 2].
La chiave dovra' essere di tipo BF_KEY.
/* BF_set_key(): imposta la chiave (di tipo BF_KEY) usando la stringa
data di lunghezza len */
void BF_set_key(BF_KEY *key, int len, const unsigned char *data);
/* BF_ecb_encrypt(): e' la funzione base per cifrare/decifrare
offerta dalla libreria. Cifra/Decifra blocchi di 8 bytes a seconda
che mode sia BF_ENCRYPT o BF_DECRYPT. */
void BF_ecb_encrypt(const unsigned char *in, unsigned char *out,
BF_KEY *key, int enc);
4. Funzioni & headers IDEA
#include <openssl/idea.h>
Lo schedule della chiave deve essere impostato a IDEA_KEY_SCHEDULE.
Ricordo, inoltre, che il ciphertext e il plaintext devono essere
blocchi di 8 bytes.
Per leggere la password da input usiamo la stessa funzione che
utilizziamo per il DES: des_read_pw_string() [leggete la sez. 2].
/* idea_set_encrypt_key(): imposta il key schedule della chiave
per la fase di cifrazione */
void idea_set_encrypt_key(const unsigned char *key,
IDEA_KEY_SCHEDULE *ks);
/* idea_set_decrypt_key(): imposta il key schedule della chiave
per la fase di decifrazione partendo dal key schedule generato
con idea_set_encrypt_key() */
void idea_set_decrypt_key(IDEA_KEY_SCHEDULE *ek,
IDEA_KEY_SCHEDULE *dk);
/* idea_ecb_encrypt(): cifra/decifra [a seconda del key schedule
passatogli] i dati contenuti nel blocco di 8 bytes in e li mette
nel blocco di 8 bytes out. */
void idea_ecb_encrypt(const unsigned char *in, unsigned char *out,
IDEA_KEY_SCHEDULE *ks);
5. Funzioni & headers MD5
#include <openssl/md5.h>
Questa libreria include alcune funzioni per l'hashing dei messaggi.
/* MD5(): performa l'hash del messaggio passatogli */
unsigned char *MD5(const unsigned char *d, unsigned long n,
unsigned char *md);
[Se md e' NULL il message digest viene messo in un array statico]
A livello applicativo, invece di usare questa funzione conviene
utilizzare le routines EVP, che lavorano a piu' alto livello.
#include <openssl/evp.h>
/* EVP_md5(): ritorna una struttura EVP_MD per l'algoritmo md5 */
EVP_MD *EVP_md5(void);
/* EVP_DigestInit(): inizializza un context CTX per usare un digest
di tipo type*/
void EVP_DigestInit(EVP_MD_CTX *ctx, const EVP_MD *type);
/* EVP_DigestUpdate(): computa l'hash di cnt bytes di d nel CTX */
void EVP_DigestUpdate(EVP_MD_CTX *ctx, const void *d,
unsigned int cnt);
/* EVP_DigestFinal(): mette l'hash contenuto nel context CTX
in md*/
void EVP_DigestFinal(EVP_MD_CTX *ctx, unsigned char *md,
unsigned int *s);
man md5
Ora che abbiamo visto le principali funzioni potete guardarvi
il codice sorgente di UNISFED.
[Il codice contiene anche l'algoritmo di crittografia a chiave pubblica
RSA, descritte nel numero 0A di Ondaquadra]
Da compilarsi [una volta suddivisi i files] in questo modo:
gcc unisfed.c -o unisfed -lssl
Ciao a tutti e alla prossima.
-------------------block_ciphers.c-------------------------------------
#include <openssl/des.h>
#include <openssl/blowfish.h>
#include <openssl/idea.h>
#define MAXPASSLEN 31
/* DES
* If mode = 1 encrypts *filein file with DES algorithm
* If mode = 0 decrypts *filein file with DES algorithm
* output is written in *fileout file
* Returns 1 if the function runs successfully
* Works on 8 bytes blocks
*
*/
void des_encrypt_decrypt(int mode, char *filein, char *fileout)
{
FILE *fpin,*fpout;
char buf[MAXPASSLEN];
des_cblock key,inmsg,outmsg; /* key, plaintext, ciphertext must
be 8 byte blocks */
des_key_schedule sched;
if (strcmp(filein,fileout) == 0) {
fprintf(stderr,"Error: input and output files must not \
be the same file.\n");
exit(EXIT_FAILURE);
}
/* des_read_pw_string reads password from stdin and stores it
in buf this function automatically asks to re-enter password
and checks it */
memset(buf,'\0',MAXPASSLEN);
if (mode == 1) {
printf("Encrypting file '%s' with des cipher.\n", \
filein);
if (des_read_pw_string(buf,MAXPASSLEN - 1,"Enter the \
password:\n",1) != 0) {
fprintf(stderr,"Error: failed to read \
password.\n");
exit(EXIT_FAILURE);
}
} else {
printf("Decrypting file '%s' with des cipher.\n",\
filein);
if (des_read_pw_string(buf,MAXPASSLEN - 1,"Enter the \
password:\n",0) != 0) {
fprintf(stderr,"Error: failed to read \
password.\n");
exit(EXIT_FAILURE);
}
}
/* des_string to key convers the password to a key */
des_string_to_key(buf,&key);
/* des_set_key_checked checks that a key passed in of odd
parity and set up the key schedule */
des_set_key_checked(&key,sched);
fpin = fopen(filein,"r");
if ((fpout = fopen(fileout,"w")) == NULL) {
fprintf(stderr,"Error: failed to open output file.\n");
exit(EXIT_FAILURE);
}
/* reads 8 bytes at a time(block=8bytes),encrypts/decrypts
each block with ecb */
while (fread(inmsg,1,8,fpin)) {
memset(outmsg,'\0',8);
des_ecb_encrypt(&inmsg,&outmsg,sched,mode);
fwrite(outmsg,1,8,fpout);
memset(inmsg,'\0',8);
}
fclose(fpin);
fclose(fpout);
printf("Done.\n");
return;
}
/* Blowfish
* If mode = 1 encrypts *filein file with Blowfish algorithm
* If mode = 0 decrypts *filein file with Blowfish algorithm
* output is written in *fileout file
* Returns 1 if the function runs successfully
* Works on 8 bytes blocks
*
*/
void bf_encrypt_decrypt(int mode, char *filein, char *fileout)
{
FILE *fpin,*fpout;
char buf[MAXPASSLEN];
unsigned char inmsg[8],outmsg[8]; /* blowfish operates on 8
byte blocks */
BF_KEY key;
if (strcmp(filein,fileout) == 0) {
fprintf(stderr,"Error: input and output files must \
not be the same file.\n");
exit(EXIT_FAILURE);
}
/* reads password from stdin using the same function used
for des passwords */
memset(buf,'\0',MAXPASSLEN);
if (mode == 1) {
printf("Encrypting file '%s' with BlowFish cipher.\n",\
filein);
if (des_read_pw_string(buf,MAXPASSLEN - 1,"Enter the \
password:\n",1) != 0) {
fprintf(stderr,"Error: failed to read \
password.\n");
exit(EXIT_FAILURE);
}
} else {
printf("Decrypting file '%s' with BlowFish cipher.\n",\
filein);
if (des_read_pw_string(buf,MAXPASSLEN - 1,"Enter the \
password:\n",0) != 0) {
fprintf(stderr,"Error: failed to read \
password.\n");
exit(EXIT_FAILURE);
}
}
/* set up the key using password stored in buf from
des_read_pw_string */
BF_set_key(&key,strlen(buf),buf);
fpin = fopen(filein,"r");
if ((fpout = fopen(fileout,"w")) == NULL) {
fprintf(stderr,"Error: failed to open output file.\n");
exit(EXIT_FAILURE);
}
/* reads 8 bytes at a time(block=8bytes),encrypts/decrypts each
block with ecb */
while (fread(inmsg,1,8,fpin)) {
memset(outmsg,'\0',8);
BF_ecb_encrypt(inmsg,outmsg,&key,mode);
fwrite(outmsg,1,8,fpout);
memset(inmsg,'\0',8);
}
fclose(fpin);
fclose(fpout);
printf("Done.\n");
return;
}
/* IDEA
* If mode = 1 encrypts *filein file with IDEA algorithm
* If mode = 0 decrypts *filein file with IDEA algorithm
* output is written in *fileout file
* Returns 1 if the function runs successfully
* Works on 8 bytes blocks
*
*/
void idea_encrypt_decrypt(int mode, char *filein, char *fileout)
{
FILE *fpin,*fpout;
char buf[MAXPASSLEN];
unsigned char inmsg[8],outmsg[8]; /* idea operates on 8
byte blocks */
IDEA_KEY_SCHEDULE sched,dc_sched;
if (strcmp(filein,fileout) == 0) {
fprintf(stderr,"Error: input and output files must not\
be the same file.\n");
exit(EXIT_FAILURE);
}
/* reads password from stdin using the same function used for
des passwords */
memset(buf,'\0',MAXPASSLEN);
if (mode == 1) {
printf("Encrypting file '%s' with IDEA cipher.\n",\
filein);
if (des_read_pw_string(buf,MAXPASSLEN - 1,"Enter the \
password:\n",1) != 0) {
fprintf(stderr,"Error: failed to read \
password.\n");
exit(EXIT_FAILURE);
}
} else {
printf("Decrypting file '%s' with IDEA cipher.\n",\
filein);
if (des_read_pw_string(buf,MAXPASSLEN - 1,"Enter the \
password:\n",0) != 0) {
fprintf(stderr,"Error: failed to read \
password.\n");
exit(EXIT_FAILURE);
}
}
/* set up the key schedule for encryption */
idea_set_encrypt_key(buf,&sched);
/* setting up the key schedule for decryption in mode == 0 */
if (mode == 0) {
idea_set_decrypt_key(&sched,&dc_sched);
sched = dc_sched;
}
fpin = fopen(filein,"r");
if ((fpout = fopen(fileout,"w")) == NULL) {
fprintf(stderr,"Error: failed to open output file.\n");
exit(EXIT_FAILURE);
}
/* reads 8 bytes at a time(block=8bytes),encrypts/decrypts each\
block with ecb */
while (fread(inmsg,1,8,fpin)) {
memset(outmsg,'\0',8);
idea_ecb_encrypt(inmsg,outmsg,&sched);
fwrite(outmsg,1,8,fpout);
memset(inmsg,'\0',8);
}
fclose(fpin);
fclose(fpout);
printf("Done.\n");
return;
}
-------------------end block_ciphers.c---------------------------------
-------------------hash.c----------------------------------------------
#include <openssl/md5.h>
#include <openssl/evp.h>
void make_hex(u_char *in, u_char *out)
{
const char *hex = "0123456789abcdef";
int i;
for(i = 0; i < 16; i++, in++) {
*out++ = hex[*in >> 4];
*out++ = hex[*in & 0xf];
}
*out = 0x00;
return;
}
/* MD5
* Hashing algorithm
* md5hash() performs the hash of a given file.
*/
void md5hash(char *filein)
{
FILE *fp = NULL;
unsigned int tmpsize=0,linesize=0;
char *hashbuf = NULL;
unsigned char *hash = NULL;
EVP_MD_CTX ctx;
printf("MD5 hash of '%s' file is:\n",filein);
EVP_DigestInit(&ctx,EVP_md5()); /* initializes digest context*/
fp = fopen(filein,"r");
while ((linesize = getline(&hashbuf,&tmpsize,fp)) != -1) {
EVP_DigestUpdate(&ctx,hashbuf,linesize); /* hashes
linesize bytes of data at hashbuf */
linesize = 0;
}
fclose(fp);
hash = (unsigned char *)malloc(16);
EVP_DigestFinal(&ctx,(unsigned char *)hash,NULL); /* retrieves
the digest value */
make_hex(hash, hashbuf);
printf("%s\n",hashbuf);
free(hashbuf);
free(hash);
return;
}
/* MD5
* Hashing algorithm
* md5cmp() compares the hash of two given files.
*/
void md5cmp(char *file1, char *file2)
{
FILE *fp = NULL;
unsigned int tmpsize=0,linesize=0;
char *hashbuf = NULL;
unsigned char *hash1 = NULL, *hash2 = NULL;
EVP_MD_CTX ctx;
printf("Comparing MD5 hashes of '%s' and '%s' files.\n",\
file1,file2);
EVP_DigestInit(&ctx,EVP_md5());
fp = fopen(file1,"r");
while ((linesize = getline(&hashbuf,&tmpsize,fp)) != -1) {
EVP_DigestUpdate(&ctx,hashbuf,linesize);
linesize = 0;
}
fclose(fp);
hash1 = (unsigned char *)malloc(16);
EVP_DigestFinal(&ctx,(unsigned char *)hash1,NULL);
EVP_DigestInit(&ctx,EVP_md5()); /* initializes the digest
context */
linesize = 0;
tmpsize = 0;
fp = fopen(file2, "r");
while ((linesize = getline(&hashbuf,&tmpsize,fp)) != -1) {
EVP_DigestUpdate(&ctx,hashbuf,linesize); /* hashes
linesize bytes of data at hashbuf */
linesize = 0;
}
fclose(fp);
hash2 = (unsigned char *)malloc(16);
EVP_DigestFinal(&ctx,(unsigned char *)hash2,NULL); /* retrieves
the digest value */
if (strcmp(hash1,hash2) == 0)
printf("MD5 hashes match.\n");
else
printf("MD5 hashes don't match.\n");
free(hash1);
free(hash2);
free(hashbuf);
return;
}
-------------------end hash.c------------------------------------------
-------------------misc.c----------------------------------------------
void help(char *name)
{
printf("UniSFED [Simple File Encrypter/Decrypter] v%s\n",\
VERSION);
printf("Coded by Paolo Ardoino <paolo.ardoino@gmail.com>\n");
printf("Usage:\n");
printf("%s -h\t:displays this help\n",name);
printf("%s -fh <file_to_hash>\t:performs the MD5 hash of \
file_to_hash.\n",name);
printf("%s -fc <file1_to_cmp> <file2_to_cmp>\t:compares the \
MD5 hash of the two files.\n",name);
printf("%s -e <-idea|-des|-bf|-rsa> <file_to_crypt> \
<file_output> [rsa_pub.pem]]\t:encrypts file_to_crypt with \
choosen cipher.\n",name);
printf("%s -d <-idea|-des|-bf|-rsa> <file_to_decrypt> \
<file_output> [rsa_sec.pem]\t:decrypts file_to_decrypt with choosen cipher.\n",name);
printf("%s -grsa <numbits> <secfile> <pubfile>\t:generates a \
RSA key pair.\n", name);
printf("-idea: idea block cipher.\n");
printf(" -des: des block cipher.\n");
printf(" -bf: blowfish block cipher.\n");
printf("Ex: %s -e -bf passwords.txt crypto_pass.txt\n",name);
printf("Ex: %s -d -bf crypto_pass.txt pass_file.txt\n",name);
printf("Ex: %s -fc file1 file2\n",name);
printf("\nPlease report bugs to <ardoino.gnu@disi.unige.it>\n");
return;
}
/*
* fexists() checks for the existence of a given file.
* return 0 if the file doesn't exist and 1 if it exists.
*/
int fexists(char *file)
{
FILE *fp;
if ((fp = fopen(file,"r")) == NULL) {
return 0;
} else {
fclose(fp);
return 1;
}
}
-------------------end misc.c------------------------------------------
-------------------rsa.c-----------------------------------------------
#include <openssl/pem.h>