[successivo] [precedente] [inizio] [fine] [indice generale] [parte]


Capitolo 12.   Libreria Des

Questa libreria sfrutta l'algoritmo DES per la cifratura dei messaggi. I metodi base utilizzati sono lo Cipher Block Chaning lo Electronic CodeBook e le loro varianti.

12.1   Struttura dei dati

Per sviluppare programmi scritti in C è necessario includere il file des.h. I tipi base usati in questa libreria sono il des_cblock che è un'array di 8 bit che serve per memorizzare i blocchi di input e output. La chiave può essere memorizzata in questo tipo. Il tipo des_key_schedule contiene una forma speciale della chiave DES; le funzioni della libreria richiedono, come argomento, questa precisa forma di chiave.

Per cifrare è stato definito il valore DES_ENCRYPT e per decifrare DES_DECRYPT.

12.2   des.h

Il file definisce i tipi e i prototipi delle funzioni usate dalla libreria.

Tipi definiti

#define DES_LONG unsigned long

typedef unsigned char des_cblock[8];

typedef struct des_ks_struct
        {
        union   {
                des_cblock _;
                DES_LONG pad[2];
                } ks;
#undef _
#define _       ks._
        } des_key_schedule[16];

Alcune funzioni

Prototipo Descrizione
void des_string_to_key (char*, des_cblock*) Trasforma una stringa in una chiave DES.
void des_string_to2keys (char*, des_cblock*, des_cblock*) Trasforma una stringa in due chiavi DES. Trova utilizzo con l'algoritmo Triple DES.
void des_random_seed (des_cblock) Genera il seme per la funzione des_random_key().
void des_random_key (des_cblock); Genera una chiave causale. Prima di utilizzare questa funzione generare il seme con la funzione des_random_seed.
int des_is_weak_key (des_cblock*); Restituisce il valore zero se il parametro formale della funzione è una chiave valida. Restituisce uno altrimenti.
int des_set_key (des_cblock*, des_key_schedule); Prima di utilizzare una chiave quest'ultima deve essere trasformata in una forma di tipo des_key_schedule. Se il controllo sulla validità della trasofrmazione è attivo, la funzione restituisce -1 se la parità non è corretta, o -2 se la chiave è errata. Se la trasformazione ha esito positivo restituisce zero. Per attivare il controllo bisogna impostare la variabile des_check_key a uno. La chiave così ottenuta non deve essere memorizzata né, tantomeno, distribuita.
int des_read_password (des_cblock*, char*, int); Legge dallo standard input una password. Nel primo parametro formale viene memorizzata la password cifrata, nel secondo parametro formale la stringa di prompt passata alla funzione stessa. La funzione restituisce zero se termina correttamente. Ponendo a uno il terzo parametro formale viene richiesto di inserire due volte la stessa password per la normale procedura di verifica. Questa funzione combina des_read_pw_string con des_string_to_key.
void des_ecb2_encrypt (des_cblock*, des_cblock*, des_key_schedule, des_key_schedule, int); Cifra un blocco da otto byte con il sistema ECB (Electronic Code Book). Il primo parametro formale è il testo in chiaro, il secondo il testo cifrato risultante. Il terzo e quarto parametro rappresentano le chiavi restituite dalla funzione des_set_key. L'ultimo parametro vale DES_ENCRYPT o DES_DECRYPT rispettivamente per l'operazione di cifratura o decifratura. L'algoritmo utilizzato è il Triple-DES encryption, tuttavia la terza chiave è uguale alla prima.
void des_ecb_encrypt (des_cblock*, des_cblock*, des_key_schedule, int); Come la precedente funzione, tuttavia la chiave utilizzata è una sola.
void des_ecb3_encrypt (des_cblock*, des_cblock*, des_key_schedule, des_key_schedule, des_key_schedule, int); Come la precedente funzione, tuttavia sfrutta tre chiavi per cifrare l'algoritmo. L'algoritmo utilizzato è il Triple-DES encryption: la prima chiave server per cifrare, la seconda per decifrare e la terza per cifrare nuovamente. Se le tre chiavi sono equivalenti il funzionamento è equivalente a des_ecb_encrypt.
void des_cbc_encrypt (des_cblock*, des_cblock*, long, des_key_schedule, des_cblock*, int); Cifra un blocco con il sistema CBC (Cipher Block Chaining). Il primo parametro formale è il blocco in chiaro il secondo il blocco cifrato ed entrambi devono essere un multiplo di otto byte. Il terzo parametro deve essere un multiplo di otto byte e rappresenta la lunghezza del blocco. Il quarto parametro accetta la chiave restituita da des_set_key; il quinto parametro è il vettore di inizializzazione che non viene modificato dalla funzione. Se è necessario cifrare un testo superiore agli otto byte la funzione viene chiamata più volte. Il vettore di inizializzazione, prima di ogni "giro", deve contenere gli ultimi otto byte del testo cifrato. L'ultimo parametro vale DES_ENCRYPT per cifrare il testo o DES_DECRYPT per decifrarlo.
void des_ncbc_encrypt (des_cblock*, des_cblock*, long, des_key_schedule, des_cblock*, int); Il comportamento differisce dalla funzione precedente in quanto aggiorna in modo automaitco il vettore di inizializzazione, per questo è preferibile utilizzare questa funzione.
void des_pcbc_encrypt (des_cblock*, des_cblock*, long, des_key_schedule, des_cblock*, int); L'algoritmo utilizzato è il Propagating Cipher Block Chaining mode. Questo metodo è più sensibile alla propagazione degli errori. I parametri formali sono gli stessi della funzione precedente, tuttavia il vettore di inizializzazione viene aggiornato ad ogni "giro".
void des_cfb_encrypt (unsigned char *, unsigned char *, int, long, des_key_schedule, des_cblock*, int); Utilizza l'algortimo Cipher Feedback Mode per cifrare e decifrare il testo. Il primo parametro formale è un array di caratteri da cifrare, il secondo parametro formale l'array contentente il testo cifrato. A differenza degli altri metodi la lunghezza del blocco non è prefissata ad otto byte. Il penultimo parametro formale è il vettore di inizializzazione. Il quarto parametro formale è la lunghezza in byte del testo da cifrare. L'array viene caricato e memorizzato in multipli di 8 bit. Il numero di bit è inserito nel terzo parametro formale. Se questo non fosse vero ma, il numero di bit valesse 10 e il quarto parametro formale valesse 2, allora i primi dieci bit verrebbero prelevati dal primo byte e i rimanenti dal secondo. I successivi dieci bit ancora prelevati dal terzo byte e i rimanenti dal quarto. I bit in eccedenza vengono scartati. Il numero di bit non può superare il valore superiore di 64. L'ultimo parametro, come al solito, può assumere il valore di DES_ENCRYPT o DES_DECRYPT.
char* des_fcrypt (const char*, const char*, char*); Un algoritmo veloce che realizza la funzione crypt standard di Unix. Il terzo parametro formale è il puntatore che contiene la stringa di ritorno e deve essere lungo 14 byte.
char* crypt (const char*, const char*); Chiama la funzione des_fcrypt con un array statico come terzo argomento. Il comportamento è identico all'algoritmo crypt standard Unix.

12.3   Compilare il codice sorgente

Ogni progetto dovrà necessariamente caricare il file des.h mentre il linker dovrà richiamare la libreria libdes.so

Gli esempi della dispensa possono essere tutti compilati con il comando seguente:

$ gcc -o esempio esempio.c -ldes [Invio]

12.4   La programmazione con Des

Il primo esempio (vedi listato 12.2) genera una chiave casuale e la stampa in console. Ogni carattere è visualizzato in cifre esadecimale.

Il tipo des_cblock è definito come un array di otto caratteri.

Listato 12.2. Genera una chiave casuale DES.

#include <stdlib.h>
#include <des.h>

int main ()
{
        des_cblock key_rand;
        des_cblock seed_rand = "abc43asd";      // seme

        int i;

        des_check_key = 1;

        // genera una chiave casuale
        des_random_seed (key_seed);
        des_random_key (key_rand);

        // stampa la chiave casuale in formato esadecimale
        for (i = 0; i < 8; i++) {
                printf ("%02x", key_rand[i]);
        }

        printf ("\n");

        exit (EXIT_SUCCESS);
}

L'esempio proposto in listato 12.3 cifra un messaggio di testo. La chiave utlizzata è "ciaociao", il testo da cifrare è "CiaoCiao". L'algoritmo utilizzato è lo ECB, che è meno sicuro dell'algoritmo CBC in listato 12.4.

Quest'algoritmo come lo CFB utilizzano un vettore di inizializzazione lungo 8 byte che è impiegato sia per cifrare che per decifrare; il vettore deve essere unico ma non segreto.

Listato 12.3. Cifra un semplice messaggio con l'algoritmo ECB.

#define BUF 256

#include <stdlib.h>
#include <des.h>
#include <assert.h>

int main ()
{
        char *str = "ciaociao"; // chiave
        des_cblock key, key_2;
        des_key_schedule schedule, schedule_2;

        unsigned char* input_data = "CiaoCiao"; // testo da cifrare
        unsigned char* output_data = (unsigned char*)malloc (BUF *
                                                sizeof (char));

        int i;

        assert (output_data != NULL);

        des_check_key = 1;

        // genera due chiavi
        des_string_to_2keys (str, &key, &key_2);

        // restituisce zero se corretto
        des_set_key (&key, schedule);
        des_set_key (&key_2, schedule_2);

        for (i = 0; i < strlen (input_data); i += 8) {
                des_ecb2_encrypt ((des_cblock*)&input_data[i],
                  (des_cblock*)&output_data[i],
                    schedule, schedule_2, DES_ENCRYPT);
        }

        printf ("Testo cifrato: ");

        for (i = 0; i < strlen (input_data); i++) {
                printf ("%02x", output_data[i]);
        }

        printf ("\n");

        free (output_data);
        
        exit (EXIT_SUCCESS);
}

Per questo motivo potrebbe essere generato con una funzione casuale come rand.

Listato 12.4. Cifra un semplice messaggio con l'algoritmo CBC.

#define BUF 256

#include <stdlib.h>
#include <des.h>
#include <assert.h>

int main ()
{
        char *str = "ciaociao"; // chiave
        des_cblock key, iv;
        des_key_schedule schedule;

        unsigned char* input_data = "CiaoCiao"; // testo da cifrare
        unsigned char* output_data = (unsigned char*)malloc (BUF *
                                                sizeof (char));

        int i;
        long l;

        assert (output_data != NULL);

        des_check_key = 1;
        l = strlen (input_data);

         // assume nulli i bit del vettore di inizializzazione
        bzero (iv, sizeof (iv));

        // genera due chiavi
        des_string_to_key (str, &key);

        // restituisce zero se corretto
        des_set_key (&key, schedule);

        des_cbc_encrypt ((des_cblock*)input_data,
                (des_cblock*)output_data, l,
                schedule, (des_cblock*)iv, DES_ENCRYPT);

        printf ("Testo cifrato: ");

        for (i = 0; i < strlen (input_data); i++) {
                printf ("%02x", output_data[i]);
        }

        printf ("\n");

        free (ouput_data);
        
        exit (EXIT_SUCCESS);
}

L'esempio proposto in listato 12.5 utilizza l'algoritmo CFB. Questo metodo è il migliore per cifrare messaggi di testo.

L'esempio proposto in listato 12.6 aggiunge, rispetto al precedente, una parte di codice relativa anche alla decifratura del testo in codice. Il vettore di inizializzazione viene azzerato una seconda volta pena l'impossibilità di decifrare correttamente il testo in codice.

Listato 12.5. Cifra un semplice messaggio con l'algoritmo CFB.

#define BUF 256

#include <stdlib.h>
#include <des.h>
#include <assert.h>

int main ()
{
        char *str = "ciaociao"; // chiave
        des_cblock key, iv;
        des_key_schedule schedule;

        unsigned char* input_data = "CiaoCiao"; // testo da cifrare
        unsigned char* output_data = (unsigned char*)malloc (BUF *
                                                sizeof (char));

        int i, numbits;
        long l;

        assert (output_data != NULL);

        des_check_key = 1;
        l = strlen (input_data);
        numbits = 16;

        // assume nulli i bit del vettore di inizializzazione
        bzero (iv, sizeof (iv));

        // genera due chiavi
        des_string_to_key (str, &key);

        // restituisce zero se corretto
        des_set_key (&key, schedule);

        des_cfb_encrypt (input_data, output_data, numbits, l,
                schedule, (des_cblock*)iv, DES_ENCRYPT);

        printf ("Testo cifrato: ");

        for (i = 0; i < strlen (input_data); i++) {
                printf ("%02x", output_data[i]);
        }

        printf ("\n");

        free (output_data);
        
        exit (EXIT_SUCCESS);
}

Gli esempi fin qui proposti utilizzano parole chiave e testi da cifrare di lunghezza multipla di 8 byte. Il DES per cifrare correttamente ha bisogno di blocchi esattamente di questa dimensione. I bit rimanenti vanno riempiti, preferibilmente, con byte casuali.

Adatteremo l'esempio in listato 12.5 con un numero di bit non multiplo di 64 bit.

L'utilizzo di funzioni C standard per generare numeri casuali è da evitare categoricamente perché sono realizzate con algoritmi pseudo casuali. In ambiente GNU/Linux è possibile prelevare un numero casuale sfruttando il file di dispositivo /dev/random o /dev/urandom (vedi cap. 13.1). Una scelta approriata, e in linea di massima più semplice da utilizzare, è generare le chiavi con funzioni MD5. L'utilizzo di queste funzioni è trattato nel capitolo.

Listato 12.6. Cifra e decifra un semplice messaggio con l'algoritmo CFB.

#define BUF 256

#include <des.h>
#include <stdlib.h>
#include <assert.h>

int main ()
{
        char *str = "ciaociao"; // chiave
        des_cblock key, iv;
        des_key_schedule schedule;

        unsigned char* input_data = "CiaoCiaoCiao"; // testo da cifrare
        unsigned char* output_data = (unsigned char*)malloc (BUF *
                                                sizeof (char));
        unsigned char* new_data = (unsigned char*)malloc (BUF *
                                                sizeof (char));

        int i, numbits;
        long l;

        assert (output_data != NULL);
        assert (new_data != NULL);

        des_check_key = 1;
        l = strlen (input_data);
        numbits = 16;

        bzero (iv, sizeof (iv));

        // genera due chiavi
        des_string_to_key (str, &key);

        // restituisce zero se corretto
        des_set_key (&key, schedule);

        // cifra il testo
        for (i = 0; i < strlen (input_data); i += 2) {
                des_cfb_encrypt (&input_data[i], &output_data[i], numbits, 2,
                        schedule, (des_cblock*)iv, DES_ENCRYPT);
        }

        printf ("Testo cifrato  : ");

        for (i = 0; i < strlen (input_data); i++) {
                printf ("%02x", output_data[i]);
        }

        printf ("\n");

        bzero (iv, sizeof (iv));

        // decifra il testo
        for (i = 0; i < strlen (input_data); i += 2) {
                des_cfb_encrypt (&output_data[i], &new_data[i], numbits, 2,
                        schedule, (des_cblock*)iv, DES_DECRYPT);
        }

        printf ("Testo decifrato: ");
        printf ("%s", new_data);
        printf ("\n");

        free (output_data);
        free (new_data);

        exit (EXIT_SUCCESS);
}

Dovrebbe essere possibile fare riferimento a questa pagina anche con il nome libreria_des.html

[successivo] [precedente] [inizio] [fine] [indice generale]