Sei sulla pagina 1di 98

UNIVERSITÀ DEGLI STUDI DI UDINE

Dipartimento di Ingegneria Elettrica, Gestionale e Meccanica

Corso di Laurea in Ingegneria Gestionale

Tesi di Laurea

IMPLEMENTAZIONI DIDATTICHE DI ALGORITMI DI


CIFRATURA PER SICUREZZA INFORMATICA

Relatore: Laureando:
Prof. Pier Luca Montessoro Pierpaolo Basso

ANNO ACCADEMICO 2013/2014


Alla mia famiglia
e a Veronica
Sommario

La confidenzialità dei dati è uno dei requisiti essenziali nell'ambito della sicurezza
informatica. Oggi, infatti, milioni di persone usano la rete per fare acquisti, per
l’home banking e per molte altre applicazioni che richiedono la condivisione di
informazioni personali. In tutti questi casi le comunicazioni avvengono attraverso
canali pubblici, cioè non sicuri. La crittografia svolge, quindi, un ruolo fondamentale
nel prevenire l’estrazione non autorizzata di informazioni da tali comunicazioni.
In questa tesi viene realizzata l’implementazione di due algoritmi di cifratura che
consentono una comunicazione sicura (cioè criptata) attraverso un canale pubblico:
l’algoritmo Diffie-Hellman e l’algoritmo RSA.
Innanzitutto, entrambi gli algoritmi saranno analizzati dal punto di vista teorico allo
scopo di comprenderne le finalità e i contenuti. Dopodichè verrà presentata la loro
implementazione in linguaggio C e C++. Per ciascuno di essi sarà, inoltre, proposto
un esempio di utilizzo nella cifratura delle comunicazioni che avvengono attraverso
la rete. L’attenzione sarà focalizzata principalmente sulla versione in C++ che verrà
puntualmente confrontata con quella in linguaggio C.
Il prodotto finale della tesi è costituito dall’insieme di classi e di librerire realizzate
per implementare i due algoritmi e dai programmi per utilizzarle.

I  
II  
Indice

Sommario I
Indice III
Indice delle figure V
Introduzione VII

1 Linguaggio e librerie utilizzate 1

1.1 Scelte di progetto 1


1.2 Installazione del compilatore e delle librerie in Windows 3
1.2.1 Installazione del compilatore C/C++ in ambiente Cygwin 3
1.2.2 Installazione libreria GMP 4
1.3 Libreria udpsocketlib 6

2 Algoritmo Diffie-Hellman 11

2.1 Descrizione 11
2.1.1 Sicurezza 12
2.2 Scambio della chiave di cifratura: classe Dh 14
2.2.1 Funzioni crittografiche della classe Dh 17
2.3 Comunicazione criptata: libreria DhDataLib 19
2.4 Esempio di utilizzo 21
2.5 Confronto con la versione in C 24

3 Algoritmo RSA 27

3.1 Descrizione 27
3.1.1 Correttezza dell’algoritmo 28
3.2 Generazione delle chiavi di cifratura: classe Rsa 30
3.3 Comunicazione criptata: libreria RsaDataLib 34
3.4 Esempio di utilizzo 36
3.5 Confronto con la versione in C 39

Conclusioni 43
Bibliografia 45
Appendici 47

File in C++ 47

ClassDh.hpp 47
ClassDh.cpp 48
DhDataLib.hpp 53
DhDataLib.cpp 53
ClassRsa.hpp 56
ClassRsa.cpp 57
RsaDataLib.hpp 61

III  
RsaDataLib.cpp 61

File in C 64

DhLib.h 64
DhLib.c 65
DhDataLib.h 69
DhDataLib.c 70
Dh_c.c 73
Dh_s.c 74
RsaLib.h 75
RsaLib.c 76
RsaDataLib.h 81
RsaDataLib.c 81
Rsa_s.c 85
Rsa_c.c 86

IV  
Indice delle figure

Figura 1.1 – Cygwin Setup 4


Figura 1.2 – Schema comunicazione via UDP 6
Figura 2.1 – Scambio di chiavi Diffie-Hellman 11
Figura 2.2 – Funzioni per lo scambio della chiave 15
Figura 3.1 – Funzioni per la creazione delle chiavi 31

V  
VI  
Introduzione

Lo scopo di questa tesi è di implementare in linguaggio C e C++ gli algoritmi


crittografici Diffie-Hellman e RSA e di realizzare le librerie e i programmi necessari
al loro utilizzo per cifrare le comunicazioni che avvengono attraverso la rete. È
importante sottolineare la finalità didattica del lavoro svolto: per permettere la
comprensione degli algoritmi sopracitati, tutto il codice è stato scritto in modo tale da
presentare in dettaglio i passaggi da essi previsti.
Nel primo capitolo vengono descritte le motivazioni che hanno portato all’utilizzo
dei linguaggi di programmazione appena citati e gli strumenti necessari per la
realizzazione della tesi.
Il secondo capitolo è interamente dedicato all’algoritmo Diffie-Hellman. Innanzitutto
l’algoritmo viene descritto in dettaglio e vengono analizzati i requisiti fondamentali
per la sua sicurezza. Dopodichè viene illustrata la classe Dh che implementa in C++
sia l’algoritmo che le funzioni di cifratura e decifratura dei dati. Infine, viene
descritta la libreria DhDataLib che consente di realizzare comunicazioni criptate
grazie alla classe Dh. L’ultima parte del capitolo comprende i programmi (scritti in
C++) che utilizzano questi strumenti e il confronto con la versione realizzata in
linguaggio C.
Il terzo capitolo, infine, è dedicato all’algortimo RSA. Anche in questo caso, come
per il Diffie-Hellman, nella prima parte del capitolo si ha la descrizione
dell’algortimo RSA e l’analisi della sua sicurezza. Dopodichè vengono illustrate la
classe Rsa (scritta in linguaggio C++) che implementa l’algoritmo e la libreria
RsaDataLib che la utilizza per cifrare le comunizioni in rete. Nell’ultima parte del
capitolo vengono descritti i programmi (in C++) che utilizzano questi strumenti e
viene analizzata la versione realizzata in linguaggio C.

VII  
VIII  
1 Linguaggio e librerie utilizzate

1.1 Scelte di progetto

Il linguaggio di programmazione utilizzato per l’implementazione degli algoritmi di


crittografia trattati in questa tesi è il C++.
Questa scelta è dettata da alcune caratteristiche offerte dal tale linguaggio che
saranno illustrate brevemente in questo capitolo. Tuttavia, poiché lo scopo della tesi
è un utilizzo didattico degli algoritmi implementati, è stata realizzata una versione
parallela in C rivolta a chi non avesse studiato il C++. Data la natura didattica
dell’implementazione, inoltre, i programmi realizzati (sia in C che in C++) sono stati
sviluppati in modo tale da presentare esplicitamente tutti i passaggi degli algoritmi di
cifratura: le operazioni svolte vengono, quindi, opportunamente evidenziate per
permettere di comprendere il funzionamento degli algoritmi stessi.
Gli algoritmi di crittografia richiedono la gestione di numeri interi molto grandi, non
rappresentabili con i tipi di dato standard messi a disposizione da linguaggi di
programmazione come il C e il C++. È stato, quindi, necessario l’utilizzo della
libreria GMP (GNU Multiple Precision Arithmetic Library). Essa permette di
lavorare agevolmente con numeri interi, razionali e in virgola mobile ponendo come
unico limite la memoria disponibile del calcolatore.
L’overloading degli operatori del C++ permette una naturale integrazione della
libreria GMP. I tipi di dato introdotti da tale libreria possono, quindi, essere trattati
allo stesso modo di quelli standard senza la necessità di utilizzare funzioni specifiche
per le operazioni aritmetiche, logiche e di input/output, contrariamente a quanto
accade in C. Tutto ciò si traduce in una maggiore semplicità e leggibilità del codice
scritto in C++. Questo può essere considerato uno dei motivi per cui si è deciso di
utilizzare tale linguaggio.
Un’ulteriore motivazione alla scelta del C++ è rappresentata dalla gestione ottimale
delle stringhe di caratteri offerta da questo linguaggio, aspetto rilevante nella
programmazione connessa alla crittografia. Questa caratteristica è ancora più
evidente nel confronto col C, linguaggio in cui la gestione delle stringhe è limitata ai
soli vettori di caratteri.
La scelta del C++ è stata dettata, inoltre, da motivazioni riguardanti la sicurezza. Le
classi introdotte dal C++ supportano il principio dell'Information Hiding (nascondere
le informazioni), comune ai linguaggi di programmazione Object-Oriented.
All’interno di una classe, infatti, possono essere presenti sia sezioni pubbliche sia
private. I dati e i metodi contenuti nella sezione pubblica di una classe sono gli unici
accessibili dall’esterno. Le funzioni e i dati presenti nella sezione privata, invece,
sono accessibili solamente ai metodi di classe, mentre risultano invisibili all’esterno.
È quindi possibile proteggere le chiavi di cifratura e le altre variabili inserendole
nella sezione privata della classe. Così facendo le chiavi di cifratura non sono
direttamente accessibili dall’esterno ma possono essere generate, modificate e
utilizzate solamente attraverso i metodi pubblici della classe. Questo determina un
livello di sicurezza più elevato e una maggiore robustezza dal punto di vista
dell’ingegneria del software.

1  
Nella realizzazione della tesi è stato necessario utilizzare altre due librerie, oltre alla
già citata GMP:

• La libreria udpsocketlib, appartenente alla libreria socketlib sviluppata dal


prof. Montessoro. Quest’ultima è una libreria di funzioni per la scrittura
semplificata di applicazioni client/server con i protocolli TCP e UDP. Gli
algoritmi implementati prevedono, infatti, la presenza di due parti che
devono generare e scambiare una chiave di crittografia attraverso la rete
(quindi un canale non sicuro) per poi condividere dati cifrati. È stata
quindi utilizzata la libreria udpsocketlib per permettere alle due parti di
dialogare attraverso la rete tramite il protocollo UDP. In conformità con
tale libreria, possiamo pensare a esse come a un client e a un server che
comunicano.

• La libreria OpenSSL, utilizzata per generare numeri primi pseudo-casuali


da 512 e da 1024 bit.

Le principali funzioni utilizzate delle librerie GMP e OpenSSL saranno analizzate, se


necessario, nei capitoli successivi, non appena le si incontrerà nel codice scritto.
Viceversa, si ritiene opportuno descrivere più in dettaglio le funzioni della libreria
udpsocketlib utilizzate per lo scambio di dati in rete attraverso il protocollo UDP.
Questo argomento verrà trattato nel punto 1.3.

2  
1.2 Installazione del compilatore e delle librerie in Windows

Per l’implementazione degli algoritmi considerati in C/C++, ma più in generale per


la realizzazione di programmi scritti in tali linguaggi, un primo passo fondamentale è
l’installazione del compilatore e dei pacchetti necessari al suo funzionamento. Il
compilatore, infatti, è fondamentale per tradurre il codice scritto in un programma
eseguibile.
In ambiente Windows la scelta è stata di installare il compilatore Cygwin GNU
C/C++. Nel processo d’installazione, come vedremo tra breve, è possibile
selezionare tra i diversi pacchetti disponibili anche la libreria OpenSSL.
La presenza del compilatore, infine, è fondamentale per poter installare la libreria
GMP che rappresenta una componente di grande rilevanza per la realizzazione della
tesi.
Le operazioni fondamentali da eseguire sono, nell’ordine:

• Installazione del compilatore Cygwin GNU C/C++, compresa la libreria


OpenSSL.
• Installazione della libreria GMP.

Per quanto riguarda la libreria “socketlib”, è sufficiente scaricarla dal sito del docente
[1] e includere i file sorgenti necessari, ovvero “udpsocketlib.hpp” e
“udpsocketlib.cpp” per il C++ e “udpsocketlib.h” e “udpsocketlib.c” per il C.

1.2.1 Installazione del compilatore C/C++ in ambiente Cygwin

Innanzitutto è necessario collegarsi al sito di Cygwin [2] per ottenere i file di


installazione. È necessario, quindi, scaricare il programma d’installazione
“setup.exe” nella versione corretta per la propria architettura, 32 o 64 bit.
Fatto ciò, si deve avviare il setup e selezionare la voce “Install from Internet”
(ovviamente è richiesta una connessione a internet per scaricare i pacchetti
necessari). Dopodiché è sufficiente avanzare senza modificare nulla fino alla
schermata “Select packages”. In questa pagina è necessario selezionare alcuni
pacchetti aggiuntivi rispetto alla configurazione di default prevista dal programma
d’installazione.
In particolare, devono essere selezionati i seguenti pacchetti all’interno delle
corrispondenti categorie:

• nella categoria “Devel”: cmake, gcc-core, gcc-g++, gdb, git, make


• nella categoria “Interpreters”: m4
• nella categoria “Net”: openssl-devel

Per selezionare questi pacchetti aggiuntivi basta espandere la relativa categoria,


individuare il pacchetto desiderato e cliccare una sola volta su “Skip”, così da far
apparire il numero indicante l’ultima versione disponibile.

3  
Vediamo un esempio in figura 1.1:

Figura 1.1 - Cygwin Setup

Questa configurazione permette, quindi, di installare il compilatore GNU C/C++


(gcc-core, gcc-g++), la libreria OpenSSL (openssl-devel) e alcuni componenti
aggiuntivi necessari per un corretto funzionamento.
Una volta aggiunti i pacchetti indicati è sufficiente avanzare senza modificare nulla
fino a quando il download e l’installazione saranno terminati.
Nella pagina immediatamente successiva a quella di selezione dei pacchetti, ovvero
la pagina con titolo “Resolving dependancies”, è fondamentale che sia selezionata
l’opzione “Select required packages”.
L'installazione crea così un'icona, un collegamento indicato con "Cygwin Terminal",
che consente di eseguire una shell (finestra di comandi) in cui è possibile utilizzare
alcuni comandi Unix e il compilatore C/C++.

1.2.2 Installazione libreria GMP

La libreria GMP può essere reperita dal sito di GMP[3]. Dopo aver scaricato ed
estratto gli archivi, si deve aprire la shell “Cygwin Terminal”, installata come visto in
precedenza.
Si deve ora cambiare il direttorio corrente (tramite il comando “cd”) spostandosi
nella cartella contenente i file della libreria GMP appena estratti. Per maggior
chiarezza, si deve spostare il direttorio corrente nella cartella della libreria GMP

4  
contenente il file “configure”. A questo punto si deve digitare nel terminale il
comando “./configure --enable-cxx ”, premere invio e attendere il completamento
dell’operazione. Dopodiché si deve fare lo stesso col comando “make”.
Per verificare la corretta esecuzione delle operazioni precedenti si deve digitare
“make check” nel terminale e premere invio. Terminati i diversi test avviati dal
comando precedente, se non sono stati riscontrati errori, si deve digitare “make
install” nel terminale e attendere il completamento di quest’ultima operazione.
La libreria GMP dovrebbe, così, essere correttamente installata per entrambi i
linguaggi C e C++.

5  
1.3 Libreria udpsocketlib

Il protocollo UDP (User Datagram Protocol) è un protocollo di livello di trasporto


non orientato alla connessione. Il protocollo UDP si appoggia direttamente sopra IP,
i dati vengono inviati in forma di datagram e non ne è assicurata né l’effettiva
ricezione né l'arrivo nell'ordine in cui vengono trasmessi. Non viene gestito, inoltre,
né il riordinamento dei pacchetti né la ritrasmissione di quelli non ricevuti. Di
conseguenza il protocollo UDP è considerato non affidabile.
In compenso è molto veloce ed efficiente: minimizza i ritardi poiché non si ha
ritrasmissione e riordinamento dei dati ed essendo non connesso non presenta
overhead dovuto ai pacchetti di servizio necessari per stabilire, mantenere e chiudere
una connessione.
Queste caratteristiche fanno sì che l’UDP sia particolarmente conveniente quando si
opera su rete locale. La probabilità estremamente bassa di perdita di dati che
contraddistingue le reti locali permette, infatti, di sfruttare appieno la rapidità e
l’efficienza del protocollo.
Le reti locali rappresentano esattamente l’ambito applicativo per cui sono stati
implementati gli algoritmi trattati in questa tesi. Di conseguenza, nei programmi
realizzati si è deciso di utilizzare il protocollo UDP per lo scambio di dati in rete.
Come detto, i socket UDP supportano una comunicazione di tipo datagram. Tutto ciò
che avviene, quindi, in una comunicazione attraverso socket UDP è la trasmissione
di un pacchetto da un client a un server o viceversa, secondo lo schema in figura 1.2.

client server
socket() socket()

bind()

sendto() recvfrom()

recvfrom() sendto()

Figura 1.2 - Schema comunicazione via UDP

Come illustrato in figura 1.2, un server UDP, dopo aver creato il socket tramite la
funzione socket, esegue la primitiva bind per associare un indirizzo di rete al socket
stesso. Un client UDP, invece, deve creare solamente il socket. Dopodiché entrambe
le parti utilizzano direttamente le funzioni sendto e recvfrom per inviare e ricevere
dati.

6  
Nelle applicazioni realizzate in questa tesi, come detto in precedenza, ci si è serviti
della libreria udpsocketlib per utilizzare i socket UDP. È opportuno, quindi,
analizzare le funzioni messe a disposizione da tale libreria per permettere a un client
e a un server di comunicare in rete attraverso il protocollo UDP secondo lo schema
visto.
Innanzitutto il server esegue la funzione create_udp_server di cui riportiamo di
seguito il codice.

1      int  create_udp_server(char  *ip_address,  int  port)  


2      {  
3              int  sk;  
4              struct  sockaddr_in  srv;  
5              /*  create  a  socket  descriptor  */  
6              if  ((sk  =  socket(AF_INET,  SOCK_DGRAM,  0))  <  0)  
7              {  
8                      error_handler("socket()  [create_udp_server()]");  
9                      return  -­‐1;  
10            }  
11            /*  fill  the  (used)  fields  of  the  socket  address  with  
12                  the  server  information  (local  socket  address)  */  
13            bzero((char  *)  &srv,  sizeof(srv));  
14            srv.sin_family  =  AF_INET;  
15            srv.sin_addr.s_addr  =  inet_addr(ip_address);  
16            srv.sin_port  =  htons(port);  
17            /*  add  the  local  socket  address  to  the  socket  descriptor  */  
18            if  (bind(sk,  (struct  sockaddr  *)  &srv,  sizeof(srv))  <  0)  
19            {  
20                    error_handler("bind()  [create_udp_server()]");  
21                    return  -­‐1;  
22            }  
23            return  sk;  
24    }  

Da notare la chiamata alla primitiva socket (linea 6), la quale crea il socket e i cui
parametri specificano:

• La famiglia di protocolli da usare. “AF_INET” indica il protocollo IPv4.


• Il tipo di servizio. “SOCK_DGRAM” indica l’utilizzo dei datagram.
• Il protocollo da usare con il socket. “0” indica che il protocollo usato è
quello di default per la combinazione di dominio e tipo specificata dagli altri
due parametri.

La funzione socket, restituisce un intero (assegnato a sk) che sarà usato per i
riferimenti al socket stesso. Dopodiché viene eseguita la funzione bind (linea 21) che
associa al socket un indirizzo di rete costituito dall’indirizzo IPv4 del server e dalla
porta locale su cui attende i dati. Questi dati sono inseriti all’interno di una struct
sockaddr (linee 14÷16) passata come parametro alla funzione bind insieme al
riferimento sk al socket.
A questo punto, il client esegue la funzione create_udp_client con cui viene creato il
socket (linea 5) attraverso la primitiva socket nello stesso modo visto per il server.

7  
Il codice della funzione è riportato di seguito.

1 int  create_udp_client(void)  
2      {  
3              int  sk;  
4              /*  create  a  socket  descriptor  */  
5              if  ((sk  =  socket(AF_INET,  SOCK_DGRAM,  0))  <  0)  
6              {  
7                      error_handler("socket()  [create_udp_client()]");  
8                      return  -­‐1;  
9              }  
10            return  sk;  
11    }  
 
Client e server possono, quindi, utilizzare le funzioni di libreria udp_send e
udp_receive per scambiare dati tra loro. Riportiamo di seguito il codice di entrambe.
Da notare in udp_send l’utilizzo della funzione sendto (linea 12) i cui parametri
principali sono: una struct sockaddr_in ska in cui sono stati inseriti l’indirizzo IPv4 e
la porta del destinatario (linee 8÷10), il riferimento al socket sk e la stringa buffer da
inviare.

1      int  udp_send(int  sk,  char  *buffer,  char  *ip_address,  int  port)  


2      {  
3              struct  sockaddr_in  ska;  
4              int  msg_len  =  strlen(buffer);  
5              /*  fill  the  (used)  fields  of  the  socket  address  with  
6                  the  server  information  (remote  socket  address)  */  
7              bzero((char  *)  &ska,  sizeof(ska));  
8              ska.sin_family  =  AF_INET;  
9              ska.sin_addr.s_addr  =  inet_addr(ip_address);  
10            ska.sin_port  =  htons((unsigned  short)  port);  
11  
12            if  (sendto(sk,  buffer,  msg_len,  0,  (struct  sockaddr  *)  &ska,  
sizeof(ska))  !=  msg_len)  
13            {  
14                    error_handler("sendto()  [udp_send()]");  
15                    return  0;  
16            }  
17            return  1;  
18    }  

In udp_receive si osserva la chiamata della funzione recvfrom (linea 24). I parametri


più rilevanti sono: il numero intero sk (riferimento al socket), la stringa di caratteri
buffer in cui saranno inseriti i dati ricevuti e la struct reply_to_socket_address in
cui è memorizzato l’indirizzo del mittente.

1      int  udp_receive(int  sk,  char  *buffer)  


2      {  
3              int  dim;  
4              len_of_reply_to_socket_address  =  sizeof(reply_to_socket_address);  
5              if  ((dim  =  recvfrom(sk,  buffer,  BUFSIZ,  0,  (struct  sockaddr  *)  
&reply_to_socket_address,  (int  *)  
&len_of_reply_to_socket_address))  <  0)  
6              {  

8  
7                      error_handler("recvfrom()  [udp_receive()]");  
8                      return  -­‐1;  
9              }  
10            buffer[dim]  =  '\0';  
11            return  dim;  
12    }  
 
Quest’ultimo dato, inoltre, è usato della funzione udp_reply per rispondere al
mittente dell’ultimo pacchetto ricevuto. Nel codice si nota, infatti, come alla
funzione sendto è passata la struct reply_to_socket_address (linea 5).

1      int  udp_reply(int  sk,  char  *buffer)  


2      {  
3              int  msg_len  =  strlen(buffer);  
4              if  (sendto(sk,  buffer,  msg_len,  0,  (struct  sockaddr  *)  
&reply_to_socket_address,  len_of_reply_to_socket_address)  <  0)  
5              {  
6                      error_handler("sendto()  [udp_reply()]");  
7                      return  0;  
8              }  
9              return  1;  
10    }  

È importante, quindi, notare come, a differenza della funzione udp_send, a


udp_reply non è passato alcun indirizzo di rete come parametro. Questa può, quindi,
essere usata solamente a seguito della ricezione di un pacchetto per rispondere al
mittente.
Terminato lo scambio di dati tra client e server, entrambe chiamano la funzione
close_udp_socket. La funzione close chiude il socket creato e rilascia tutte le risorse
ad esso allocate (linea 3).

1      int  close_udp_socket(int  sk)  


2      {  
3              if  (close(sk)  !=  0)  
4              {  
5                      error_handler("close()  [close_udp_socket()]");  
6                      return  0;  
7              }  
8              return  1;  
9      }  

9  
10  
2 Algoritmo Diffie-Hellman

2.1 Descrizione

L’algoritmo Diffie-Hellman è stato proposto nel 1976 da Whitfield Diffie e Martin E.


Hellman, due crittografi statunitensi, nell’articolo “New Directions in Cryptography”
[4]. Questo algoritmo ha rappresentato una soluzione innovativa ad uno dei problemi
classici della crittografia, ovvero la distribuzione delle chiavi di cifratura.
Uno dei principali ambiti applicativi della crittografia è la garanzia della privacy. La
criptazione delle comunicazioni che avvengono attraverso un canale non sicuro
(pubblico) diventa, quindi, fondamentale per prevenire l’estrazione non autorizzata di
informazioni da esse. Di conseguenza, è necessario che le parti in comunicazione
condividano una chiave crittografica che non sia nota a nessun altro.
Si presenta, dunque, il problema della generazione e della distribuzione di tale
chiave. Questo può essere risolto inviando la chiave di cifratura in anticipo alle parti
coinvolte attraverso un canale sicuro come ad esempio un corriere o la posta
raccomandata. Tuttavia, i ritardi e i costi introdotti da questa modalità di
distribuzione non sono accettabili nella maggior parte delle applicazioni pratiche,
specialmente in ambito commerciale. L’algoritmo proposto nell’articolo [4] offre un
approccio differente al problema, eliminando, di fatto, la necessità di un canale
sicuro per la condivisione delle chiavi.
Noto come “Diffie-Hellman key exchange” (scambio di chiavi Diffie-Hellman), tale
algoritmo crittografico consente a due parti di stabilire una chiave di cifratura
condivisa e segreta attraverso un canale di comunicazione insicuro (pubblico), senza
la necessità che queste si siano scambiate informazioni in precedenza.
La figura 2.1 illustra i passi dell’algoritmo che portano allo scambio della chiave di
cifratura K condivisa fra le due parti.

Figura 2.1 - Scambio di chiavi Diffie-Hellman

11  
Come visto nel capitolo 1, possiamo pensare alle due parti come a un client e a un
server che si scambiano una chiave di cifratura.
Il client, innanzitutto, genera un numero primo p molto elevato di almeno 1024 bit
(cioè circa 300 cifre decimali) e un generatore g di Zp* (che deve esistere essendo p
primo). In aritmetica modulare un generatore modulo p (detto semplicemente
generatore) è un intero g le cui potenze modulo p sono congruenti con i numeri
coprimi a p. Ovvero g è un intero che elevato a potenza genera tutti i numeri coprimi
a p. In particolare, è possibile dimostrare che se p è primo esiste sicuramente un
generatore g che produce tutti gli elementi di Zp*, in altre parole i numeri compresi
tra 1 e (p -1). Vedremo nel seguito del capitolo il metodo implementato per trovare
un generatore di Zp*.
Il client genera quindi un numero casuale a < p e calcola A = ga mod p. Come
evidenziato in figura 2.1, i numeri p, g, A sono comunicati pubblicamente al server
mentre a non viene condiviso.
In modo analogo il server genera un numero casuale b < p, calcola B = gb mod p e lo
invia al client. Entrambe le parti possono quindi calcolare la chiave di cifratura K:

• Il client calcola K = Ba mod p.


• Il server calcola K = Ab mod p.

Come indica l’ultimo passaggio in figura 2.1, client e server trovano lo stesso valore
per K. Per le proprietà dell’aritmetica modulare, infatti, si ha che:

K = Ab mod p = (ga mod p)b mod p = gab mod p = (gb mod p)a mod p = Ba mod p

La chiave ottenuta può essere, quindi, utilizzata per crittografare le successive


comunicazioni fra le due parti mediante un algoritmo di cifratura a chiave
simmetrica. In questa tesi, a titolo di esempio applicativo, è stato implementato un
algoritmo di cifratura simmetrica che, come vedremo nel seguito del capitolo, esegue
l’XOR bit a bit fra la chiave crittografica generata e i dati in chiaro da inviare.

2.1.1 Sicurezza

La sicurezza dell’algoritmo si basa sulla complessità computazionale del calcolo del


logaritmo discreto.
Una terza parte può intercettare i parametri p, g, A, B ma non è in grado di calcolare
la chiave di cifratura K non conoscendo a o b. Non essendo stati condivisi, l’unico
modo per determinare questi numeri è risolvere il problema del logaritmo discreto,
infatti:

• a = logg A mod p
• b = logg B mod p

La sicurezza dell’algoritmo, quindi, è garantita fino a quando il calcolo del logaritmo


discreto è computazionalmente proibitivo.

12  
A tale scopo, il generatore g gioca un ruolo fondamentale. Come documentato nei siti
[5] e [6], affinché l’algoritmo Diffie-Hellman sia sicuro non è necessario che il
generatore produca tutti gli interi modulo p non nulli, ma è sufficiente che il
sottogruppo di Zp* generato da g sia tale da rendere elevata la complessità
computazionale del logaritmo discreto.
Per ottenere ciò devono essere soddisfatte le seguenti condizioni:

• p sia un numero primo generato in modo casuale e sufficientemente grande,


almeno 1024 bit.

• il generatore g ha ordine r che è il più piccolo intero per cui gr = 1 mod p. Il


più grande divisore primo di r deve essere elevato, di almeno 2k bit, dove k
bit è il livello di sicurezza desiderato.

La prima condizione può essere facilmente soddisfatta grazie alle numerose librerie
disponibili per la generazione di numeri primi casuali, come vedremo
nell’implementazione realizzata dell’algoritmo.
Fare in modo che l’ordine r del generatore scelto abbia un divisore primo
sufficientemente grande, invece, può essere più complesso. È conveniente, quindi,
procedere nel modo descritto di seguito (come indicato nel sito [6]). Si deve,
innanzitutto, generare un numero primo casuale p “sicuro”, cioè tale che (p-1)/2 sia
anch’esso primo. Indichiamo con q il numero (p-1)/2 e quindi possiamo esprimere p
come p = 2q +1. Poiché p deve essere un numero primo sufficientemente grande,
anche q lo sarà.
Si può dimostrare che un qualunque intero casuale g modulo p non nullo ha ordine r
pari a 1, 2, q o 2q. Gli ordini pari a 1 e 2 si ottengono rispettivamente per g uguale a
1 e (p-1). Qualunque altro intero casuale g compreso nell’intervallo [2, (p-2)] avrà,
quindi, ordine r pari a q o 2q e può quindi essere usato come generatore per
l’algoritmo Diffie-Hellman. Infatti, essendo q un numero primo sufficientemente
grande, l’ordine r del generatore sarà elevato, come richiesto per la sicurezza
dell’algoritmo.
Soddisfatte queste condizioni, per una terza parte che intercetta la comunicazione è
pressoché impossibile ricostruire la chiave scambiata dalle informazioni ottenute.

Purtroppo l'algoritmo Diffie-Hellman è vulnerabile all'attacco "Man in the middle"


(del terzo uomo interposto). Supponiamo che una terza parte intercetti il parametro A
che il client invia al server con cui vuole comunicare. Questa può generare,
fingendosi il server, un suo numero B e inviarlo al client scambiando così una chiave
di cifratura condivisa. In modo del tutto analogo, questa terza parte può generare una
chiave condivisa col server e, da qui in avanti, intercettare e leggere tutta la
corrispondenza tra i due.
Per prevenire simili attacchi, la soluzione è di usare un ente certificatore che
garantisca l'identità dei corrispondenti.

13  
2.2 Scambio della chiave di cifratura: classe Dh

Analizziamo, quindi, l’implementazione dell’algoritmo Diffie-Hellman realizzata in


questa tesi.
Nella versione in linguaggio C++ è stata creata la classe Dh, la quale consente lo
scambio di una chiave crittografica secondo le modalità previste dall’algoritmo e,
inoltre, gestisce la cifratura e la decifratura dei dati attraverso tale chiave.
L’interfaccia della classe Dh e l’implementazione dei suoi metodi sono interamente
riportate in appendice, rispettivamente in ClassDh.hpp e ClassDh.cpp. Tuttavia, per
semplicità di consultazione, ne richiamiamo di seguito la dichiarazione.

14    class  Dh  
15    {  
16            public:  
17                    mpz_class  ReturnKey()  const  {  return  K;  }  
18                    int  SizeOfK()  const  {  return  mpz_sizeinbase(K.get_mpz_t(),  
10);}  
19                    bool  GenerateEncryptionKey_Client(int  socket,  string  
ipAddress,  int  udpPort);  
20                    bool  GenerateEncryptionKey_Server(int  socket);  
21                    string  DhEncrypt(const  string&  toEncryptStr);  
22                    string  DhDecrypt(const  char*  toDecrypt);  
23  
24            private:  
25                    mpz_class  p,  g,  a,  b,  A,  B;  
26                    mpz_class  K;  //key  
27                    mpz_class  GetVariable(string  varName);  
28                    mpz_class  GeneratePrimeNumber(string  name,  unsigned  numBits);  
29                    mpz_class  FindGenerator();  
30                    void  GetPgaAndSetA(int  choice);  
31                    bool  SendPGA(int  socket,  string  ipAddress,  int  udpPort);  
32                    bool  ReceiveBandComputeK(int  socket);  
33                    bool  ReceivePGA(int  socket);  
34                    bool  SendB(int  socket);  
35                    void  ComputeBandKey();  
36                    int  rdtsc();  
37    };  

Da notare, innanzitutto, come le variabili dell’algoritmo p, g, a, b, A, B nonché la


chiave di cifratura K siano inserite nella parte privata, secondo il principio
dell’information hiding (linee 25 e 26). Chi utilizza la classe, quindi, non può
accedere direttamente a questi dati ma può solamente ricorrere ai metodi presenti
nella sua parte pubblica. Tutto ciò si traduce in una maggior sicurezza poiché la
chiave di cifratura e tutte le variabili che permettono di generarla non possono essere
modificate in modo indiscriminato ma solamente secondo le modalità previste dalle
funzioni pubbliche della classe.
Osserviamo, inoltre, come questi parametri siano di tipo mpz_class. Questa, infatti, è
la classe messa a disposizione dalla libreria GMP per la gestione di numeri interi
molto elevati.

14  
Lo scambio della chiave crittografica fra due parti, qui pensate come un client e un
server, è realizzato dai metodi pubblici GenerateEncryptionKey_Client() e
GenerateEncryptionKey_Server(). Essi utilizzano le funzioni della parte privata della
classe Dh per accedere alle variabili dell’algoritmo e impostarne i valori, come
evidenziato in figura 2.2.
La figura 2.2, riportata di seguito, rappresenta in dettaglio la successione delle
chiamate alle funzioni di classe.

client server

GenerateEncryptionKey_Client() GenerateEncryptionKey_Server()

GetPgaAndSetA()

SendPGA() ReceivePGA()

ComputeBandKey()

ReceiveBandComputeK() SendB()

Figura 2.2 - Funzioni per lo scambio della chiave

Lo scambio della chiave ha inizio col client che chiama il metodo pubblico
GenerateEncryptionKey_Client() (linee 182÷209 in appendice). Come evidenziato in
figura 2.2, questo metodo prevede l’esecuzione di tre funzioni private della classe
Dh, la prima delle quali è GetPgaAndSetA() che imposta i parametri p, g, a, A.
L’utente del client può lasciare che sia il calcolatore a generare i due parametri
principali p, g oppure inserirli manualmente (linee 184÷194). La seconda possibilità
è certamente utile dal punto di vista didattico poiché permette all’utente di osservare
il funzionamento dell’algoritmo con diverse combinazioni dei suoi parametri.
Tuttavia, la generazione manuale di numeri interi molto elevati che soddisfino i
criteri di sicurezza richiesti risulta estremamente complessa. A questo scopo è,
quindi, possibile selezionare la prima opzione che garantisce l’affidabilità
dell’algoritmo.
La funzione GetPgaAndSetA() riceve come parametro d’ingresso un intero choice
che rappresenta la scelta fatta dall’utente: 1 se viene scelta la prima opzione, 2 per la
seconda (linea 196).

15  
Nel primo caso, la generazione dei parametri avviene in questo modo:

• p viene creato dalla funzione GeneratePrimeNumber() (linea 24 in


appendice) a cui viene passato in ingresso un intero numBits, oltre ad una
stringa name che indica il nome della variabile (p in questo caso). La
funzione utilizza il metodo BN_generate_prime_ex() fornito dalla libreria
OpenSSL per generare un numero primo pseudo-casuale di almeno numBits
bit di lunghezza; qui è stato utilizzato un valore di 1024 bit (linea 78 in
appendice). Il terzo parametro passato a BN_generate_prime_ex() è un intero
di valore uno; questo indica alla funzione di generare un numero primo
“sicuro”. Da notare come la libreria OpenSSL utilizzi il tipo di dato BIGNUM
e sia, quindi, necessaria una conversione intermedia in una stringa per
tradurlo in un intero mpz_class (linea 80 in appendice). In C++ questo
passaggio risulta, di fatto, trasparente: grazie all’overload dell’operatore “=”,
è, infatti, possibile assegnare all’intero mpz_class la stringa restituita dalla
funzione BN_bn2dec() corrispondente al numero primo generato. Il
parametro p così creato è, quindi, un numero primo pseudo-casuale di almeno
1024 bit “sicuro”, cioè tale che anche (p-1)/2 è primo.

• g viene generato dalla funzione FindGenerator(). Come visto in precedenza,


poiché p è un numero primo “sicuro”, è possibile utilizzare come generatore
un numero intero casuale di valore compreso fra 2 e p-2. Questo è
esattamente il compito svolto dalla funzione sopraccitata. Innanzitutto viene
prodotto un intero casuale size minore o uguale al numero di cifre di p-2.
Questa sarà la dimensione del generatore. Infatti, viene generato un numero
size di interi casuali che vanno a costituire le cifre di g nel seguente modo: se
size è uguale alla dimensione di p-2, allora ogni cifra di g è generata come
intero casuale minore di quella corrispondente in p-2 altrimenti come intero
casuale compreso tra 0 e 9. Questo garantisce che il generatore sia un numero
casuale minore di p-2 (linee 48÷65 in appendice). Se si dovesse ottenere un
valore di g pari a 2, la procedura verrebbe ripetuta.
Da notare l’utilizzo del numero di cicli di clock della CPU come seme per
srand(). Questa informazione è fornita dall’istruzione assembly rdtsc
(eseguita dall’omonima funzione della classe Dh) e garantisce una migliore
randomizzazione essendo un numero che varia in modo estremamente rapido
e difficilmente prevedibile.

Nel secondo caso, invece, viene chiamata la funzione GetVariable() per permettere
all’utente del client di inserire i valori di p e g desiderati.
Dopodiché, indipendentemente da come sono stati generati i due parametri, all’utente
spetta il compito di scegliere e inserire un valore casuale a minore di p (sempre
attraverso la funzione GetVariable()); a questo punto A viene calcolato come A = ga
mod p (linee 32 e 34 in appendice).
Da notare come la funzione mpz_powm() fornita dalla libreria GMP per l’elevamento
a potenza esegua interamente il calcolo precedente, compresa l’operazione di
modulo. Come si vede nel codice in appendice, infatti, la funzione accetta quattro
parametri: rispettivamente A (in cui è salvato il risultato dell’operazione), la base g,
l’esponente a e il divisore p.

16  
Al client non resta che comunicare p, g e A al server tramite il metodo privato
SendPGA(). Come evidenziato in figura 2.2, infatti, contemporaneamente al client, il
server esegue la funzione GenerateEncryptionKey_Server() e si mette in attesa di
questi tre parametri chiamando ReceivePGA().
Se entrambe le operazioni hanno successo, il server procede generando b, calcolando
B e la chiave di cifratura K tramite la funzione privata ComputeBandKey().
All’utente del server viene chiesto, quindi, di inserire un numero intero casuale b
diverso da uno ma minore di p; dopodichè si procede al calcolo di B = gb mod p e
quindi di K = Ab mod p (linee 174÷178 in appendice).
Il server mantiene segreto il numero b scelto e la chiave K ma condivide B
chiamando la funzione SendB(). Il client tramite il metodo ReceiveBandComputeK()
può infine ricevere B e calcolare K = Ba mod p (linee 109÷118 in appendice).
Le chiavi così calcolate dalle due parti risultano identiche come dimostrato in
precedenza.
Da notare come lo scambio dei dati tra le due parti avvenga, attraverso la rete,
secondo le modalità descritte in precedenza al punto 1.3. A questo proposito si
evidenziano le chiamate alle funzioni udp_send(), udp_reply() e udp_receive() (linee
95, 109, 163 e 133 in appendice) rispettivamente per inviare e ricevere i dati.
Ricordiamo come sia necessario per il client e il server creare il socket prima di
utilizzare tali funzioni. Questo passaggio non è gestito direttamente dalla classe Dh e
deve, quindi, essere svolto dall’applicazione che la utilizza, come vedremo
nell’esempio che sarà illustrato nel seguito del capitolo.

2.2.1 Funzioni crittografiche della classe Dh

Come si nota nella sua dichiarazione (linee 21 e 22), la classe Dh comprende le due
funzioni DhEncrypt() e DhDecrypt() che utilizzano la chiave crittografica condivisa
rispettivamente per la cifratura e la decifratura dei dati. Come detto in precedenza,
nell’esempio applicativo realizzato la cifratura avviene eseguendo l’XOR tra la
chiave e i dati da trasmettere.
La funzione DhEncrypt() riceve in ingresso il parametro toEncryptStr, cioè la
stringa di dati da crittografare. Partendo dal primo, viene eseguito l’XOR tra il valore
decimale di ciascun carattere di toDecrypt e quello del carattere corrispondente della
chiave di cifratura; il risultato è poi memorizzato nella stringa encrypted (linee
230÷235 in appendice). Da notare il cast verso il tipo unsigned char (linea 233),
utilizzato per ottenere solamente valori positivi dall’operazione di XOR,
contrariamente a quanto accadrebbe se toEncryptStr contenesse caratteri non
appartenenti alla tabella ASCII standard.
Terminata l’elaborazione, la stringa encrypted che contiene i dati crittografati viene
restituita come output dalla funzione. È fondamentale osservare come la lunghezza di
toEncryptStr debba essere minore o uguale a quella della chiave, altrimenti una parte
delle informazioni non sarebbe cifrata. La classe Dh fornisce, quindi, il selettore
SizeOfK() che restituisce esattamente il numero di caratteri della chiave crittografica.
Questa informazione permette di suddividere correttamente i dati da trasmettere
prima di utilizzare DhEncrypt().

17  
La funzione DhDecrypt() esegue, invece, l’operazione di decifratura (linee 239÷258
in appendice). Essa riceve in ingresso la stringa toDecrypt che contiene i dati iniziali
crittografati, ovvero una successione di cifre decimali. Ogni terna di caratteri della
stringa costituisce, quindi, un numero intero, risultato dell’operazione di XOR
eseguita dalla funzione DhEncrypt() appena illustrata. Eseguendo nuovamente
l’XOR tra questi interi e i caratteri della chiave di cifratura si ottengono i dati di
partenza che vengono memorizzati in decrypted (linea 253).
La funzione DhDecrypt() restituisce, quindi, la stringa decrypted contenente i dati
decriptati.

18  
2.3 Comunicazione criptata: libreria DhDataLib

La libreria DhDataLib è stata creata per la trasmissione e la ricezione di dati cifrati


utilizzando la classe Dh. Il codice relativo è riportato interamente in appendice (si
faccia riferimento alle voci DhDataLib.hpp e DhDataLib.cpp).
Di seguito ne richiamiamo l’header:

1      #ifndef  DHDATALIB_HPP  
2      #define  DHDATALIB_HPP  
3  
4      #include  "ClassDh.hpp"  
5      #include  <fstream>  
6  
7      bool  ReadAndSendData(Dh&  srv,  int  socket,  string  ipAddress,  int  
udpPort);  
8      bool  ReceiveAndDecryptData(Dh&  srv,  int  socket);  
9      void  SendData(const  string&  data,  Dh&  srv,  int  socket);  
10    string  ReadFromFile();  
11    string  ReadMessage();  
12    void  printStrInDec(const  string&  toPrint);  
13    void  printStrInHex(const  string&  toPrint,  bool  crypted);  
14  
15    #endif  

Le due funzioni principali della libreria DhDataLib sono ReadAndSendData() e


ReceiveAndDecryptData(). La prima è utilizzata per inviare dati criptati mentre la
seconda è impiegata per riceverli e decifrarli.
La funzione ReadAndSendData() (linee 71÷105 in appendice) permette, innanzitutto,
di scegliere fra tre opzioni:

1) Inviare i dati inseriti da tastiera.


2) Inviare i dati contenuti in un file di testo.
3) Terminare la trasmissione.

Nel primo caso viene chiamata il metodo ReadMessage() che legge i caratteri inseriti
da testiera, accoda ad essi la stringa di controllo "EOF~EOF~EOF" che ne indica la
fine e memorizza il tutto nella stringa restituita come output. All’interno di
ReadAndSendData() i caratteri letti dal metodo appena citato sono assegnati alla
stringa message che viene poi inviata grazie alla funzione SendData(), che verrà
descritta nel seguito (linee 87 e 88 in appendice).
La seconda opzione prevede la trasmissione del contenuto di un file di testo. L’unica
differenza rispetto al caso precedente, quindi, sta nel fatto che i dati del file sono letti
dalla funzione ReadFromFile() e non da ReadMessage() (linea 93).
Nel terzo caso, invece, la stringa di controllo "EOTS~EOTS~EOTS" viene inviata
tramite SendData() per indicare al ricevitore la fine della trasmissione.
ReadAndSendData() restituisce, infine, un valore booleano che indica se è stata
scelta o meno la terza opzione, ovvero se la trasmissione deve essere interrotta (linea
104).

19  
Come detto, quindi, è SendData() la funzione che invia effettivamente i dati da
trasmettere, dopo averli crittografati grazie alla classe Dh (linee 140÷163 in
appendice).
Nel punto 2.2.1 del capitolo 2 abbiamo visto come i dati da cifrare debbano essere
suddivisi in blocchi di caratteri di lunghezza minore o uguale a quella della chiave
crittografica per utilizzare la funzione DhEncrypt().
SendData() deve gestire, quindi, anche questa operazione. Come si evince dal codice,
la funzione riceve in ingresso la stringa data da trasmettere e, partendo dal primo, ne
copia i caratteri in encryptedStr fino a quando la lunghezza di quest’ultima è pari a
quella della chiave crittografica. A questo punto encryptedStr viene cifrata tramite
DhEncrypt() ed inoltrata grazie alla funzione udp_reply() della libreria udpsocketlib
(linee 152 e 156). Tutti i caratteri dalla stringa encryptedStr vengono, quindi,
eliminati e la procedura viene iterata fino a quando data non è stata completamente
inviata.
La funzione ReceiveAndDecryptData() (linee 35÷69 in appendice) deve svolgere
quindi l’operazione inversa e riscostruire interamente i dati iniziali a partire dai
frammenti cifrati ricevuti, che vengono prodotti come visto sopra.
Ognuna di queste stringhe criptate è ricevuta attraverso la funzione udp_receive() e
assegnata al vettore di caratteri buffer (linea 45). Quest’ultimo viene decifrato dalla
funzione DhDecrypt() e il risultato è salvato nella stringa bufferDecrypted (linea
49) che viene poi aggiunta a messageDecripted (linea 52). Quando in
bufferDecrypted è presente la successione di caratteri "EOF~EOF~EOF" (che viene
rimossa), la stringa messageDecripted contiene i dati iniziali trasmessi
completamente ricostruiti (linee 59÷66).
La funzione ReceiveAndDecryptData() restituisce un valore booleano che sarà vero
solamente quando viene ricevuta la stringa "EOTS~EOTS~EOTS" che indica il
termine della comunicazione.
La libreria DhDataLib presenta, infine, i due metodi printStrInDec() printStrInHex()
che permettono di stampare a video rispettivamente i valori decimali ed esadecimali
dei caratteri delle stringhe fornite in input (linee 3÷33).

20  
2.4 Esempio di utilizzo

Vediamo ora un esempio di utilizzo della libreria DhDataLib e della classe Dh.
Come illustrato nei capitoli precedenti, due parti, che indichiamo come client e
server, grazie alla classe Dh generano una chiave di cifratura condivisa che viene
usata dalle funzioni della libreria DhDataLib per crittografare la comunicazione tra di
esse.
Riportiamo di seguito il codice in linguaggio C++ relativo alle applicazioni del client
e del server che utilizzano gli strumenti appena citati, insieme alla libreria
udpsocketlib.

Server

1      #include  "ClassDh.hpp"  
2      #include  "udpsocketlib.hpp"  
3      #include  "DhDataLib.hpp"  
4  
5      int  main(int  argc,  char  *argv[])  
6      {  
7              Dh  srv;  
8              int  sk,  udpPort;  
9              string  serverIpAdrress,  message;  
10            bool  done  =  false;  
11  
12            if  (argc  ==  1)  
13            {  
14                    cout<<"Server  ip  address:";  
15                    cin>>serverIpAdrress;  
16                    cout<<"Udp  port:";  
17                    cin>>udpPort;  
18            }  
19            else  if  (argc  ==  3)  
20            {  
21                    serverIpAdrress  =  argv[1];  
22                    udpPort  =  atoi(argv[2]);  
23            }  
24            else  
25            {  
26                    cout<<"Required  arguments:  server  ip  address,  udp  port"<<endl;  
27                    exit(1);  
28            }  
29  
30            if  ((sk  =  create_udp_server((char*)serverIpAdrress.c_str(),  
udpPort))  <  0)  
31            {  
32                    cout<<"Cannot  open  server  socket"<<endl;  
33                    exit(1);  
34            }  
35  
36            if  (!srv.GenerateEncryptionKey_Server(sk))  
37                    exit(1);  
38  
39            do  

21  
40            {  
41                    done  =  ReceiveAndDecryptData(srv,  sk);  
42                    if  (!done)  
43                            done  =  ReadAndSendData(srv,  sk);  
44            }while  (!done);  
45  
46            close_udp_socket(sk);  
47            cout<<"\n\nExit..\n\n";  
48            return  0;  
49    }  

Client

1      #include  "ClassDh.hpp"  
2      #include  "udpsocketlib.hpp"  
3      #include  "DhDataLib.hpp"  
4  
5      int  main(int  argc,  char  *argv[])  
6      {  
7              Dh  clnt;  
8              int  sk,  udpPort;  
9              string  serverIpAdrress;  
10            bool  done  =  false;  
11  
12            if  (argc  ==  1)  
13            {  
14                    cout<<"Server  ip  address:";  
15                    cin>>serverIpAdrress;  
16                    cout<<"Udp  port:";  
17                    cin>>udpPort;  
18            }  
19            else  if  (argc  ==  3)  
20            {  
21                    serverIpAdrress  =  argv[1];  
22                    udpPort  =  atoi(argv[2]);  
23            }  
24            else  
25            {  
26                    cout<<"Required  arguments:  server  ip  address,  udp  port"<<endl;  
27                    exit(1);  
28            }  
29  
30            if  ((sk  =  create_udp_client())  <  0)  
31            {  
32                    cout<<"Cannot  open  client  socket"<<endl;  
33                    exit(1);  
34            }  
35  
36            if  (!clnt.GenerateEncryptionKey_Client(sk,  serverIpAdrress  
,udpPort))  
37                    exit(1);  
38  
39            do  
40            {  
41                    done  =  ReadAndSendData(clnt,  sk);  
42                    if  (!done)  

22  
43                            done  =  ReceiveAndDecryptData(clnt,  sk);  
44            }while  (!done);  
45  
46            close_udp_socket(sk);  
47            cout<<"\n\nExit.."<<endl<<endl;  
48            return  0;  
49    }  

Innanzitutto, entrambe le parti creano il socket UDP grazie alle due funzioni
create_udp_server() e create_udp_client() messe a disposizione dalla libreria
udpsocketlib. Come visto nel punto 1.3 del capitolo 1, create_udp_server() riceve in
ingresso l’indirizzo IPv4 del server e la porta locale su cui attenderà i dati per
associarle al socket (linea 30, Server). Queste informazioni devono essere fornite
come argomento della funzione main del server direttamente dalla linea di comando,
altrimenti il loro inserimento viene richiesto esplicitamente dal programma (linee
12÷28, Server).
Le stesse informazioni devono essere fornite all’applicazione del client; in questo
caso però non saranno associate al socket ma saranno utilizzate, quando necessario,
solamente per inviare dati al server. Da notare, infatti, come create_udp_client() non
riceva parametri in ingresso (linea 30, Client).
Una volta creato il socket, client e server possono procedere alla generazione di una
chiave crittografica condivisa, come illustrato in precedenza in questo capitolo.
Vengono chiamate, infatti, da entrambe le parti le funzioni della classe Dh
GenerateEncryptionKey_Server() e GenerateEncryptionKey_Client() per lo scambio
della chiave (linea 36, Client e Server).
Se l’operazione ha successo, le due parti possono, infine, utilizzare la libreria
DhDataLib per comunicare in modo protetto attraverso la rete. Come si vede nel
codice, entrambe le parti entrano in un ciclo do..while in cui vengono eseguite
alternativamente le due funzioni ReadAndSendData() ReceiveAndDecryptData()
(linee 39÷44).
La comunicazione ha inizio con il client che chiama ReadAndSendData() per inviare
i dati al server, il quale a sua volta esegue ReceiveAndDecryptData() per riceverli.
Dopodiché le parti si scambiano i ruoli e il ciclo continua fino a quando una delle
due non termina la comunicazione. Ricordiamo, infatti, che entrambe le funzioni
restituiscono un valore booleano che indica il raggiungimento di tale condizione,
come visto nel punto 2.3 del capitolo 2.
A questo punto da entrambe le parti il socket viene chiuso e le applicazioni
terminano (linee 46 e 48).

23  
2.5 Confronto con la versione in C

Nella versione in linguaggio C è stata mantenuta la stessa struttura di quella in C++


in modo tale che lo scambio di una chiave crittografica così come la cifratura, la
decifratura e la trasmissione dei dati avvengano sempre secondo le modalità descritte
in precedenza. Tuttavia esistono alcune differenze fra le due versioni, dovute
principalmente alle diverse caratteristiche dei linguaggi di programmazione, che si
ritiene utile analizzare.
Innanzitutto, non essendo presente il concetto di classe, nella versione in linguaggio
C è stato necessario creare la libreria DhLib per lo scambio della chiave crittografica
secondo l’algoritmo Diffie-Hellman. Il codice è riportato interamente in appendice
(si faccia riferimento alle voci DhLib.h e DhLib.c). Di seguito ne richiamiamo
solamente l’header.

13    //  Create  type  bool  (not  present  in  c).  


14    typedef  enum  {  false,  true  }  bool;  
15  
16    struct  dhStruct  
17    {  
18            mpz_t  p,  g,  a,  b,  A,  B,  K;  
19    };  
20  
21    void  GeneratePrimeNumber(char*  name,  mpz_t  num,  unsigned  numBits);  
22    void  FindGenerator(mpz_t  q,  mpz_t  g);  
23    bool  GenerateEncryptionKey_Client(int  socket,  char*  ipAddress,  int  
udpPort,  struct  dhStruct*  dh);  
24    void  GetPgaAndSetA(struct  dhStruct*  dh,  int  choice);  
25    bool  SendPGA(int  socket,  char*  ipAddress,  int  udpPort,  struct  
dhStruct*  dh);  
26    bool  ReceiveBandComputeK(int  socket,  struct  dhStruct*  dh);  
27    bool  GenerateEncryptionKey_Server(int  socket,  struct  dhStruct*  dh);  
28    bool  ReceivePGA(int  socket,  struct  dhStruct*  dh);  
29    void  ComputeBandKey(struct  dhStruct*  dh);  
30    bool  SendB(int  socket,  struct  dhStruct*  dh);  
31    char*  DhEncrypt(char*  toEncryptStr,  char*  key);  
32    char*  DhDecrypt(char*  toDecrypt,  char*  key);  
33    void  GetVariable(char*  varName,  mpz_t  var);  
34    int  rdtsc();  

Dal confronto con la versione in C++ si nota come le funzioni della libreria DhLib
siano le stesse presenti nella classe Dh (ma opportunamente tradotte in linguaggio C)
e svolgano le stesse operazioni. Lo scambio della chiave di cifratura, quindi, avviene
sempre attraverso i due metodi GenerateEncryptionKey_Server() e
GenerateEncryptionKey_Client() e la successione delle funzioni chiamate è la stessa
della versione in C++.
All’inizio del capitolo 2, al punto 2.2 si è vista l’importanza delle classi dal punto di
vista della sicurezza e robustezza del software: essendo contenute nella parte privata,
le variabili dell’algoritmo e la chiave di cifratura sono invisibili a chi utilizza la
classe ed è possibile accedervi solo attraverso i suoi metodi pubblici.

24  
Data l’assenza delle classi in C, viene meno anche la possibilità di sfruttare il
principio dell’information hiding. Nella libreria DhLib le variabili dell’algoritmo non
sono più campi privati di una classe ma vengono solamente inserite all’interno di una
struct dhStruct (linee 16÷19). Chi utilizza la libreria può, quindi, accedervi
direttamente in modo analogo a quanto avviene per le funzioni di libreria a cui la
struct contenente i dati viene passata per riferimento. Tutto ciò si traduce in un livello
di sicurezza minore rispetto al caso precedente.
Questo aspetto è evidente nei programmi scritti come esempio di utilizzo delle
librerie DhLib e DhDataLib. Il codice è riportato in appendice alle voci Dh_s.c
Dh_c.c.
In entrambi i programmi, dopo aver scambiato la chiave crittografica, questa viene
convertita in una stringa (linea 39 in appendice). Per fare questo si accede
direttamente al dato contenuto nella struct tramite l’operatore “.”. In questo caso il
valore della chiave K viene solamente letto, ma nello stesso modo è possibile
modificare il dato. Questo vale ovviamente per tutte le altre variabili. Da notare,
quindi, la differenza con l’utilizzo della classe Dh in cui la chiave crittografica possa
essere solamente letta attraverso il selettore ReturnKey() e non sia possibile
modificare direttamente nessuna delle variabili.
Da notare, inoltre, come le variabili dell’algoritmo siano rappresentate da oggetti di
tipo mpz_t (linea 18) a differenza della classe Dh in cui sono di tipo mpz_class.
La classe mpz_class presente nella libreria GMP è stata creata, infatti, in C++ per la
gestione degli oggetti mpz_t: in altre parole la loro inizializzazione, il rilascio della
memoria loro riservata e l’overloading degli operatori. Per fare ciò tale classe utilizza
le funzioni specifiche della libreria GMP che operano su oggetti di tipo mpz_t.
Questo semplifica notevolmente l’utilizzo di tale tipo di dato in C++; nel codice
scritto, infatti, mpz_powm() è l’unica funzione specifica che è stato necessario
utilizzare esplicitamente. L’overloading degli operatori d’input/output, di quelli
relazionali e di quelli matematici ha permesso di trattare gli oggetti mpz_t come i tipi
di dato nativi del C++.
Tutto ciò non è più vero per il linguaggio C. Da notare, a questo proposito, nel codice
dei programmi Dh_c.c e Dh_s.c come le variabili mpz_t della struct dhStruct
debbano essere inizializzate tramite la funzione mpz_inits() prima di utilizzarle e la
memoria debba, infine, essere liberata chiamando mpz_clears() (linee 11 e 48 in
appendice). Nel codice della libreria DhLib si nota, inoltre, l’utilizzo delle seguenti
funzioni:

• mpz_set() che svolge il compito dell’operatore di assegnazione “=”


• mpz_sub_ui() per sottrarre un intero unsigned long ad uno mpz_t
• mpz_cmp_ui() e mpz_cmp() per comparare un intero mpz_t rispettivamente a
un intero unsigned long e a un altro mpz_t. queste funzioni svolgono, quindi,
il compito degli opertatori relazionali “>”, “>=”, “<”, “<=” e “==”
• gmp_printf() per visualizzare gli interi mpz_t sullo standard output
• gmp_scanf() per acquisire interi mpz_t dallo standard input

L’uso di queste funzioni non incrementa notevolmente la complessità nella scrittura


del codice in linguaggio C; in alcuni casi, tuttavia, la sua leggibilità può risentirne,
specialmente se confrontata al C++.

25  
È stato, infine, necessario tradurre in C anche la libreria DhDataLib, il cui codice è
riportato in appendice alle voci DhDataLib.h DhDataLib.c. Come fatto in
precedenza, richiamiamo di seguito la dichiarazione delle funzioni:

6      bool  ReadAndSendData(char*  key,  int  socket);  


7      bool  ReceiveAndDecryptData(char  *key,  int  socket);  
8      void  SendData(char*  data,  char*  key,  int  socket);  
9      char*  ResizeDinamicString(char*  toResize,  int  newDim);  
10    char*  ReadMessage();  
11    char*  ReadFromFile();  
12    void  printStrInDec(char*  toPrint);  
13    void  printStrInHex(char*  toPrint,  bool  crypted);  

Anche in questo caso sono state mantenute tutte le funzioni presenti nella versione in
C++ e quindi la comunicazione criptata fra due parti avviene esattamente nello stesso
modo descritto in precedenza.
È importante, tuttavia, sottolineare come il C++ consenta un utilizzo migliore e più
semplice delle stringhe di caratteri rispetto al C, in particolare quelle di dimensione
variabile.
In C++ abbiamo utilizzato la classe string che gestisce autonomamente il
ridimensionamento delle stringhe per l’aggiunta di singoli caratteri o di altre stringhe
tramite la funzione push_back() e l’operatore “+” sovraccaricato.
Queste caratteristiche sono state utili nelle funzioni ReadMessage() e
ReadFromFile() in quanto hanno reso particolarmente semplice l’inserimento dei
caratteri letti rispettivamente dallo standard input e da un file in una stringa e
l’aggiunta in coda della sequenza di controllo "EOF~EOF~EOF".
In C, invece, le stringhe sono trattate come vettori di caratteri ed è necessario gestire
esplicitamente il loro ridimensionamento. A questo scopo si è scelto di ricorrere
all’allocazione dinamica della memoria tramite la funzione malloc(). È stato così
possibile creare la funzione ResizeDinamicString() (linee 113÷126 in appendice) per
ridimensionare una stringa di caratteri in modo che possa contenere tutti i dati, senza
perdita di informazioni. Come si vede nel codice delle funzioni ReadMessage() e
ReadFromFile() (linee 138, 143, 165 e 170 in appendice), ResizeDinamicString()
viene chiamata tutte le volte che i caratteri letti in input devono essere aggiunti a una
stringa, prima di poter utilizzare la funzione strcat().
L’allocazione dinamica richiede, infine, di prestare particolare attenzione al corretto
rilascio della memoria occupata. Da notare a questo proposito le chiamate alla
funzione free() (linee 75, 79 e 122) che svolge quest’ultima operazione.

26  
3 Algoritmo RSA

3.1 Descrizione

Nel 1978 Ronald Rivest, Adi Shamir e Leonard Adleman (tre ricercatori del MIT)
proposero, nell’articolo “A Method for Obtaining Digital Signatures and Public-Key
Cryptosystems” [7], l’algoritmo di crittografia a chiave asimmetrica noto come
“algoritmo RSA” (il cui nome è costituito dalle iniziali dei suoi inventori).
La crittografia asimmetrica è basata sull’utilizzo di due chiavi distinte per la cifratura
e per la decifratura delle informazioni, dette rispettivamente chiave pubblica e
chiave privata. Quando due parti devono comunicare tra loro, ciascuna di esse non
deve fare altro che cifrare i dati con la chiave pubblica del destinatario, che, una volta
ricevuti, sarà in grado di decifrarli con la propria chiave privata. In questo sistema,
quindi, ogni parte possiede una coppia di chiavi: quella pubblica che deve essere,
appunto, distribuita e resa di pubblico dominio perché consente di codificare le
informazioni e quella privata (che deve essere mantenuta segreta) che consente di
decifrarle. Da notare come la chiave pubblica possa essere condivisa attraverso un
canale insicuro: chi la ottiene, infatti, può soltanto inviare informazioni cifrate al suo
proprietario, ma non è in grado di ottenere e decodificare i dati diretti a quest’ultimo
dal momento che non è a conoscenza della sua chiave privata. Poiché le due chiavi
non sono indipendenti tra loro, la sicurezza del meccanismo di cifratura è garantita
dalla difficoltà di ricostruire una delle due chiavi a partire dall’altra.
Nell’algoritmo RSA la generazione delle chiavi avviene nel seguente modo:

1. si scelgono due numeri primi casuali p e q sufficientemente grandi in modo


da garantire la sicurezza dell’algoritmo
2. si calcola n = p×q e φ = (p – 1)×(q – 1)
3. si sceglie un numero 1 < e < φ coprimo con φ. Come indicato dagli autori
dell’algoritmo nell’articolo [7], si può utilizzare, ad esempio, un numero
primo casuale maggiore del max(p, q); l’importante è che questo sia scelto da
un insieme sufficientemente grande.
4. si calcola il numero d tale che e×d = 1 mod φ

La chiave pubblica è costituita dalla coppia (n, e), mentre quella privata da (n, d).
Vediamo, quindi, come utilizzare le chiavi generate per la cifratura e decifratura
delle informazioni. Innanzitutto, i dati da codificare devono essere suddivisi in
blocchi di dimensione compatibile con l’operazione di cifratura e opportunamente
convertiti in decimale (come vedremo nel seguito del capitolo). Indicando con m il
valore decimale di ciascun blocco, la sua dimensione deve essere tale per cui 0 < m <
n. La cifratura delle informazioni avviene, grazie alla chiave pubblica, attraverso
l’operazione c = me mod n; c rappresenta, così, ciascun blocco di dati crittografato.
Per decifrare c utilizzando la chiave privata, è necessario calcolare t = cd mod n,
dove indichiamo con t il risultato dell’ultima operazione. Affinché la decifrazione
abbia successo, t deve necessariamente coincidere con c. La dimostrazione della
correttezza dell’algoritmo RSA verrà fornita nel seguito del capitolo.

27  
La sua sicurezza, invece, è basata sulla difficoltà di scomporre in fattori primi i
numeri interi molto elevati. Come evidenziato in [8], se si riuscisse a fattorizzare il
numero n (noto pubblicamente), allora sarebbe possibile trovare p e q e da questi φ.
Di conseguenza, conoscendo e, si potrebbe facilmente determinare d tramite
l’algoritmo di Euclide e violare, quindi, il meccanismo di cifratura. Tuttavia non è
stato ancora trovato un metodo per la fattorizzazione di numeri molto grandi che non
sia computazionalmente proibitivo. Per questo motivo l’algoritmo RSA è considerato
sicuro se si utilizzano p e q sufficientemente elevati, cioè di dimensioni tali per cui n
sia un numero intero di almeno 200 cifre decimali (come suggerito dagli ideatori
dell’algoritmo nell’articolo [7]).

3.1.1 Correttezza dell’algoritmo

La correttezza dell’algoritmo viene dimostrata dagli autori nell’articolo [7] sfruttando


il piccolo teorema di Fermat di cui riportiamo solamente l’enunciato. Sia p un
numero primo e a un intero non divisibile da p, il teorema afferma che:

a(p – 1) = 1 mod p

Viene utilizzato, inoltre, un importante risultato del teorema cinese del resto, il quale
afferma che la relazione a = b mod pq risulta equivalente al sistema costituito da:

• a = b mod p
• a = b mod q

Si vuole dimostrare, quindi, che i dati decifrati indicati con t coincidono con quelli
originali m. Per le proprietà dell’aritmetica modulare possiamo scrivere:

t = cd mod n = (me mod n) d mod n = (me) d mod n = med mod n

dove c indica i dati m cifrati, come visto in precedenza.

Per come sono stati costruiti e e d, si ha che:

e×d = 1 mod φ = 1 mod ((p – 1)×(q – 1))

Di conseguenza:

e×d = 1 mod (p – 1)

e analogamente

e×d = 1 mod (q – 1)

28  
Per il piccolo teorema di Fermat si ha che:

med = m mod p e med = m mod q

Siccome p e q sono numeri diversi e primi, possiamo applicare il teorema cinese del
resto, ottenendo che:
med = m mod (p×q) = m mod n

e quindi:
t = cd mod n = med mod n = m mod n

Abbiamo quindi dimostrato che, decrittando i dati cifrati con la chiave pubblica (n, e)
tramite quella privata (n, d), si ottengono nuovamente i dati originali.

29  
3.2 Generazione delle chiavi di cifratura: classe Rsa

Analizziamo, quindi, l’implementazione dell’algoritmo RSA realizzata in questa tesi.


Nella versione in linguaggio C++, la classe Rsa è stata creata per la generazione
delle chiavi di cifratura pubblica e privata e per la gestione delle operazioni di
criptazione e di decriptazione dei dati, secondo le modalità appena descritte. Come
vedremo, sono presenti, inoltre, le due funzioni necessarie per la condivisione e per
la ricezione della chiave pubblica.
L’interfaccia della classe Rsa e l’implementazione dei suoi metodi sono interamente
riportate in appendice, rispettivamente in ClassRsa.hpp e ClassRsa.cpp. Tuttavia, per
semplicità di consultazione, ne richiamiamo di seguito la dichiarazione.

14    class  Rsa  
15    {  
16            public:  
17                    mpz_class  N()  const  {  return  n;}  
18                    int  SizeOfN()  const  {  return  mpz_sizeinbase(n.get_mpz_t(),  
10);}  
19                    mpz_class  E()  const  {  return  e;}  
20                    mpz_class  D()  const  {  return  d;}  
21                    void  GenerateEncryptionKeys();  
22                    bool  SendPublicKey(int  socket,  string  ipAddress,  int  udpPort);  
23                    bool  ReceivePublicKey(int  socket);  
24                    void  RsaEncrypt(string&  toEncryptStr);  
25                    string  RsaDecrypt(const  char*  toDecrypt);  
26  
27        private:  
28                    mpz_class  p,  q,  phi;  
29                    mpz_class  n,  e,  d;  //keys  
30                    mpz_class  ComputeGCD(const  mpz_class&  x,  const  mpz_class&  y);  
31                    mpz_class  GetRandomE();  
32                    void  GeneratePQandComputeNPhi();  
33                    mpz_class  GeneratePrimeNumber(string  name,  unsigned  numBits);  
34                    void  FindE();  
35                    void  ComputeD();  
36                    int  rdtsc();  
37    };  

Innanzitutto notiamo che, come già visto per la classe Dh, le variabili dell’algoritmo
(ovvero p, q e phi) e le chiavi di cifratura (costituite da n, e e d) sono di tipo
mpz_class e sono inserite nella parte privata della classe. Questo garantisce una
maggior sicurezza poiché le chiavi non sono direttamente accessibili a chi usa la
classe e, quindi, possono essere generate e modificate solo dalle funzioni della classe
stessa. In particolare, la chiave pubblica e quella privata vengono create dalla
funzione GenerateEncryptionKeys() (linea 21). Quest’ultima, come si evince dal
codice (linee 131, 132 e 133 in appendice), sfrutta i metodi privati della classe Rsa
per impostare i valori delle variabili dell’algoritmo e ottenere, infine, le chiavi
crittografiche.

30  
Il dettaglio delle funzioni di classe chiamate all’interno di GenerateEncryptionKeys()
è riportato nella figura sottostante.

GenerateEncryptionKeys()

1) GeneratePQandComputeNPhi()

GeneratePrimeNumber()

2) FindE()

GetRandomE()

ComputeGCD()

3) ComputeD()

Figura 3.1 - Funzioni per la creazione delle chiavi

Come indicato in figura 3.1, la prima funzione che viene chiamata in


GenerateEncryptionKeys() è GeneratePQandComputeNPhi(). L’algoritmo RSA,
infatti, ha inizio con la generazione di due numeri primi elevati p e q e con il calcolo
di n e φ (nel codice quest’ultima variabile è stata indicata con phi).
Questo è esattamente il compito svolto da GeneratePQandComputeNPhi() che, come
si può notare nel codice (linee 22, 23, 26 e 27 in appendice), utilizza la funzione
GeneratePrimeNumber() per generare i due numeri primi casuali p e q e, infine,
calcola n = p×q e phi = (p – 1) × (q – 1). La funzione GeneratePrimeNumber() è la
stessa utilizzata nella classe Dh; si rimanda, quindi, al punto 2.2 del capitolo 2 per
maggiori dettagli.
Da notare, invece, come la dimensione scelta per p e q sia di 512 bit. In questo modo
n sarà un numero intero di circa 308 cifre decimali tale, quindi, da garantire la
sicurezza dell’algoritmo: la condizione di 200 cifre decimali indicata nell’articolo [7]
risulta, infatti, ampiamente soddisfatta.
A questo punto viene chiamata la funzione FindE() per calcolare il numero intero e
che costituisce, in coppia con n, la chiave pubblica e che, come visto in precedenza,
deve essere coprimo e minore di phi. La funzione FindE(), quindi, trova il numero e
nel seguente modo. Innanzitutto viene chiamata la funzione GetRandomE() (linea 64

31  
in appendice) che assegna ad e un numero intero casuale compreso tra il max(p, q) e
(phi – 1). Dopodiché, come si può notare nel codice (linee 65÷72 in appendice),
viene calcolato il massimo comun divisore fra e e phi tramite la funzione
ComputeGCD() che implementa l’algoritmo di Euclide:

• se è pari ad 1, i due numeri sono coprimi e quindi e è corretto.


• se è diverso da 1, allora i due numeri non sono coprimi. Si diminuisce e di 1 e
si ricalcola il massimo comun divisore fino ad ottenere un valore corretto per
e. Se quest’ultimo dovesse diventare pari a p o a q, allora il ciclo si
ripeterebbe ricominciando dalla chiamata a GetRandomE().

La funzione FindE() converge molto rapidamente ad un valore corretto per il numero


e, cioè tale per cui 1 < e < phi ed è coprimo con phi. Inoltre, e viene individuato
all’interno di un insieme molto ampio poiché il max(p, q) e (phi – 1) differiscono di
molti ordini di grandezza. Ciò significa che la sicurezza dell’algoritmo RSA è
garantita.
A questo punto viene chiamata la funzione ComputeD() per calcolare il numero d e
ottenere, quindi, la chiave privata costituita dalla coppia (d, n). Come detto in
precedenza, d deve essere calcolato in modo tale che e×d = 1 mod φ ovvero d è
l’inverso moltiplicativo di e. La funzione ComputeD() sfrutta, quindi, l’algoritmo di
Euclide esteso per determinare d; senza entrare nei dettagli matematici, utilizziamo
l’implementazione ripotata nel sito [9] ma opportunamente modificata per poter
lavorare con numeri interi molto grandi, ovvero con dati di tipo mpz_class. Trovato
d, la funzione GenerateEncryptionKeys() giunge al termine. Sono state generate,
quindi, entrambe le chiavi di cifratura: la chiave pubblica (n, e) e quella privata (n,
d).
Tuttavia, per poterle utilizzare nella cifratura di una comunicazione che avviene tra
due parti, è necessario condividere la chiave pubblica. Per questo motivo la classe
Rsa comprende le due funzioni SendPublicKey() e ReceivePublicKey() che possono
essere usate rispettivamente per inviare e ricevere la chiave pubblica. Come si vede
dal codice (linee 142÷147 in appendice), la prima non fa altro che convertire n ed e
in due stringhe che poi vengono inviate grazie alla funzione udp_send(); la seconda
(linee 165÷173 in appendice) esegue l’operazione inversa ricevendo le due stringhe
tramite udp_receive() e convertendole in numeri interi mpz_class. Un esempio di
utilizzo delle funzioni della classe Rsa verrà proposto nel seguito del capitolo.
La classe Rsa comprende, infine, i due metodi pubblici RsaEncrypt() e RsaDecrypt()
(line 24 e 25) per la cifratura e la decifratura dei dati.
La prima riceve in ingresso la stringa toEncryptStr da criptare, la converte nel
numero intero encrypted di tipo mpz_class e calcola, grazie alla funzione
mpz_powm() della libreria GMP, encrypted = encryptede mod n (linee 187 e 190 in
appendice). È importante notare come la funzione RsaEncrypt() richieda che
toEncryptStr contenga il valore numerico dei dati da cifrare e che questo sia minore
di n (come visto in precedenza nel capitolo). Di conseguenza, per poter utilizzare
correttamente tale funzione, è necessario che i dati siano già stati convertiti in
decimale e opportunamente suddivisi. A questo punto encrypted, dopo essere stato
tradotto in una stringa, viene memorizzato in toEncryptStr (linea 192 in appendice)
che, in questo modo, conterrà i dati iniziali cifrati.

32  
Per decifrare i dati, la funzione RsaDecrypt() deve necessariamente svolgere
l’operazione inversa. Essa riceve in ingresso la stringa toDecrypt che viene
convertita nel numero intero decrypted (linea 200 in appendice). Dopodiché la
funzione calcola, per mezzo di mpz_powm(), decrypted = decryptedd mod n e
restituisce il risultato di tale operazione sotto forma di stringa di caratteri. Poiché i
dati vengono convertiti in decimale e suddivisi prima di essere cifrati, dopo la
decifratura dovrà essere gestita la loro ricostruzione. Di seguito vedremo come tutte
queste operazioni siano realizzate dalle funzioni della libreria RsaDataLib.

33  
3.3 Comunicazione criptata: libreria RsaDataLib

La libreria RsaDataLib utilizza la classe Rsa per permettere la trasmissione e la


ricezione di dati cifrati grazie alle chiavi crittografiche generate. Il codice relativo è
riportato interamente in appendice (si faccia riferimento alle voci RsaDataLib.hpp e
RsaDataLib.cpp).
Di seguito ne richiamiamo l’header:

1      #ifndef  RSADATALIB_HPP  
2      #define  RSADATALIB_HPP  
3  
4      #include  "ClassRsa.hpp"  
5      #include  <fstream>  
6  
7      void  ReceiveAndDecryptData(Rsa&  clt,  int  socket);  
8      bool  ReadAndSendData(Rsa&  srv,    int  socket);  
9      void  SendData(const  string&  data,  Rsa&  srv,  int  socket);  
10    string  ReadFromFile();  
11    string  ReadMessage();  
12    string  RebuildData(string&  decryptedStr);  
13  
14    #endif  

Le due funzioni principali della libreria sono ReadAndSendData() e


ReceiveAndDecryptData() che permettono, rispettivamente, di inviare dati criptati e
di riceverli e decifrarli. Come si evince dal codice (linee 3÷27 e 56÷90 in appendice),
sia queste due funzioni che ReadFromFile() e ReadMessage() sono sostanzialmente
le stesse presenti nella libreria DhDataLib, con la sola differenza che in questo caso
vengono utilizzati oggetti di classe Rsa e non Dh. Si rimanda, quindi, al punto 2.3 del
capitolo 2 per la loro descrizione.
È opportuno, però, analizzare le differenze, dovute principalmente alla diversa
modalità di cifratura dei dati, rispetto alla libreria DhDataLib che sono. Ciò che
varia, infatti, è il modo in cui devono essere suddivisi i dati da trasmettere prima di
poterli criptare (grazie alle funzioni della classe Rsa) e la loro ricostruzione una volta
decifrati. La prima operazione è svolta dalla funzione SendData() mentre la seconda
da RebuildData().
SendData() (linee 124÷150 in appendice) riceve in ingresso la stringa data da
trasmettere, traduce (partendo dal primo) un carattere nel corrispondente valore
decimale e lo accoda alla stringa encryptedStr (linee 132 e 133). Dopodiché
encryptedStr viene convertita nel numero intero encrypted di tipo mpz_class (linea
134). Se encrypted è minore di n, l’inserimento dei caratteri in encryptedStr
prosegue, altrimenti quest’ultima viene cifrata grazie alla funzione RsaEncrypt()
della classe Rsa e, quindi, trasmessa tramite udp_reply() (linee 140, 141 e 142).
encryptedStr viene, quindi, svuotata e il procedimento ripetuto fino a quando la
stringa data non è stata interamente trasmessa.
La funzione ReceiveAndDecryptData() riceve, quindi, i dati originali (ovvero quelli
contenuti nella stringa data) criptati e suddivisi in blocchi nel modo appena descritto.
L’operazione di decifrazione è svolta dal metodo RsaDecrypt() della classe Rsa che
restituisce la stringa aux in cui è presente il valore numerico dei dati originali

34  
(ovvero il contenuto di encryptedStr). Quest’ultima contiene, quindi, una sequenza
di cifre decimali in cui ogni terna di numeri costituisce un carattere dei dati iniziali.
Di conseguenza, è necessario utilizzare la funzione RebuildData() (linee 29÷54 in
appendice) che converte il valore decimale di ciascuna terna di cifre della stringa aux
in un carattere. In questo modo i dati originali vengono ricostruiti.
Nel codice di ReceiveAndDecryptData() si notano le chiamate a RsaDecrypt() e
RebuildData() (linee 11 e 12 in appendice). Il loro utilizzo rappresenta l’unica
differenza rispetto all’omonima funzione della libreria DhDataLib in cui vengono
usate le funzioni della classe Dh. Di seguito viene proposto un esempio di utilizzo
dei metodi della libreria RsaDataLib.

35  
3.4 Esempio di utilizzo

Vediamo ora un esempio di utilizzo della libreria RsaDataLib e della classe Rsa in
cui si realizza una comunicazione criptata tra due parti che indichiamo come client e
server. Come illustrato nei capitoli precedenti, la classe Rsa permette di generare le
chiavi di cifratura che vengono usate dalle funzioni della libreria RsaDataLib per
crittografare la comunicazione tra le due parti.
Riportiamo di seguito il codice in linguaggio C++ relativo alle applicazioni del client
e del server che utilizzano gli strumenti appena citati (insieme alla libreria
udpsocketlib).

Server

1      #include  "ClassRsa.hpp"  
2      #include  "udpsocketlib.hpp"  
3      #include  "RsaDataLib.hpp"  
4  
5      int  main(int  argc,  char  *argv[])  
6      {  
7              Rsa  srv;  
8              int  sk,  udpPort;  
9              bool  done;  
10            string  serverIpAdrress;  
11            if  (argc  ==  1)  
12            {  
13                    cout<<"Server  ip  address:";  
14                    cin>>serverIpAdrress;  
15                    cout<<"Udp  port:";  
16                    cin>>udpPort;  
17            }  
18            else  if  (argc  ==  3)  
19            {  
20                    serverIpAdrress  =  argv[1];  
21                    udpPort  =  atoi(argv[2]);  
22            }  
23            else  
24            {  
25                    cout<<"Required  arguments:  server  ip  address,  udp  port"<<endl;  
26                    exit(1);  
27            }  
28            if  ((sk  =  create_udp_server((char*)serverIpAdrress.c_str(),  
udpPort))  <  0)  
29            {  
30                    cout<<"Cannot  open  server  socket"<<endl;  
31                    exit(1);  
32            }  
33            if  (!srv.ReceivePublicKey(sk))  
34                    exit(1);  
35            do  
36            {  
37                    done  =  ReadAndSendData(srv,  sk);  
38            }while  (!done);  
39            cout<<"\n\nExit..\n\n";  

36  
40            close_udp_socket(sk);  
41            return  0;  
42    }  

Client

1      #include  "ClassRsa.hpp"  
2      #include  "udpsocketlib.hpp"  
3      #include  "RsaDataLib.hpp"  
4  
5      int  main(int  argc,  char  *argv[])  
6      {  
7              Rsa  clnt;  
8              int  sk,  udpPort;  
9              string  serverIpAdrress;  
10  
11            if  (argc  ==  1)  
12            {  
13                    cout<<"Server  ip  address:";  
14                    cin>>serverIpAdrress;  
15                    cout<<"Udp  port:";  
16                    cin>>udpPort;  
17            }  
18            else  if  (argc  ==  3)  
19            {  
20                    serverIpAdrress  =  argv[1];  
21                    udpPort  =  atoi(argv[2]);  
22            }  
23            else  
24            {  
25                    cout<<  "Required  arguments:  server  ip  address,  udp  
port"<<endl;  
26                    exit(1);  
27            }  
28            if  ((sk  =  create_udp_client())  <  0)  
29            {  
30                    cout<<"Cannot  open  client  socket"<<endl;  
31                    exit(1);  
32            }  
33            clnt.GenerateEncryptionKeys();  
34            if  (!clnt.SendPublicKey(sk,  serverIpAdrress,  udpPort))  
35                    exit(1);  
36            ReceiveAndDecryptData(clnt,  sk);  
37            close_udp_socket(sk);  
38            return  0;  
39    }  

Osservando il codice di entrambi i programmi, si vede che la creazione del socket per
mezzo delle funzioni messe a disposizione dalla libreria udpsocketlib (linee 11÷32
server e client) avviene in modo analogo a quello visto nell’esempio di utilizzo della
classe Dh. Si rimanda quindi ai capitoli 1.3 e 2.4 per una descrizione dettagliata.
È opportuno analizzare, invece, l’uso delle funzioni della classe Rsa. Come si evince
dal codice, dopo che entrambe le parti hanno creato il socket, il server si mette in
attesa di ricevere la chiave pubblica del client grazie alla funzione
ReceivePublicKey() (linee 33 e 34, server). Ovviamente, nel caso in cui venga

37  
riscontrato qualche errore nella ricezione della chiave, il programma del server
termina.
Contemporaneamente, il client prima genera la chiave privata e quella pubblica
chiamando la funzione GenerateEncryptionKeys() (linea 33, client) e poi condivide
quest’ultima grazie a SendPublicKey() (linee 34 e 35, client). Anche in questo caso,
se si riscontra qualche errore nella condivisione della chiave, il programma del client
termina.
Se lo scambio della chiave pubblica tra le due parti avviene correttamente, il server
può utilizzare quest’ultima per trasmettere dati cifrati al client attraverso la libreria
RsaDataLib. Nell’esempio realizzato vediamo, infatti, che il client si mette in attesa
di ricevere informazioni dal server attraverso la funzione ReceiveAndDecryptData()
(linea 36, client). Quest’ultimo, invece, entra in un ciclo do..while per trasmettere
dati al client grazie a ReadAndSendData() (linee 35÷38, server).
Quando la trasmissione di dati da parte del server finisce, da entrambi i lati viene
chiuso il socket e i programmi terminano.

38  
3.5 Confronto con la versione in C

Anche in questo caso, come per il Diffie-Hellman, nella versione in linguaggio C è


stata mantenuta la stessa struttura di quella in C++. Di conseguenza la creazione
delle chiavi crittografiche così come la cifratura, la decifratura e la trasmissione dei
dati avvengono nello stesso modo e attraverso le stesse funzioni descritte in
precedenza nel capitolo. Tuttavia, come nel caso precedente, esistono alcune
differenze fra le due versioni, dovute alle diverse caratteristiche dei due linguaggi di
programmazione, che si ritiene utile analizzare.
Poiché nel linguaggio C non esiste il concetto di classe è stato necessario creare la
libreria RsaLib per la generazione delle chiavi di cifratura dell’algoritmo Rsa. Il
codice è riportato interamente in appendice (si faccia riferimento alle voci RsaLib.h e
RsaLib.c). Di seguito ne richiamiamo l’header.

13    typedef  enum  {  false,  true  }  bool;  


14  
15    struct  keysRsa  
16    {  
17            mpz_t  p,  q,  phi;  
18            mpz_t  n,  e,  d;  //keys  
19    };  
20  
21    struct  publicKeyRsa  
22    {  
23            mpz_t  e,  n;  
24    };  
25  
26    bool  ReceivePublicKey(struct  publicKeyRsa*  pbk,  int  socket);  
27    char*  RsaEncrypt(char*  toEncryptStr,  struct  publicKeyRsa*  pbk);  
28    char*  RsaDecrypt(char*  toDecrypt,  struct  keysRsa*  key);  
29    void  GenerateEncryptionKeys(struct  keysRsa*  keys);  
30    void  GeneratePrimeNumber(char*  name,  mpz_t  num,  int  numBits);  
31    void  GeneratePQandComputeNPhi(struct  keysRsa*  var);  
32    void  GetRandomE(struct  keysRsa*  var);  
33    void  FindE(struct  keysRsa*  var);  
34    void  ComputeGCD(mpz_t  r,  mpz_t  e,  mpz_t  phi);  
35    void  ComputeD(struct  keysRsa*  var);  
36    bool  SendPublicKey(int  socket,  char*  ipAddress,  int  udpPort,  struct  
keysRsa*  pubK);  
37    int  rdtsc();  

L’analisi del codice conferma quanto appena detto e cioè che le funzioni della
libreria RsaLib sono le stesse presenti nella classe Rsa (ma opportunamente tradotte
in linguaggio C). La generazione della chiave di cifratura pubblica e di quella
privata, quindi, avviene sempre attraverso il metodo GenerateEncryptionKeys()   e la
successione delle funzioni chiamate è la stessa della versione in C++.
L’importanza delle classi dal punto di vista della sicurezza e robustezza del software
è già stata ampiamente discussa ai punti 2.2 e 2.5 del capitolo 2. Di conseguenza
ricordiamo solamente che l’assenza delle classi in C comporta l’impossibilità di
sfruttare il principio dell’information hiding. Come nella libreria DhLib, anche in
RsaLib le variabili dell’algoritmo non sono più campi privati di una classe ma

39  
vengono inserite all’interno di una struct keysRsa (linee 15÷19) e, quindi, chi utilizza
la libreria può accedervi direttamente. Come detto, questo determina un livello di
sicurezza minore rispetto alle classi del C++.
Nella libreria è stata definita, inoltre, la struct publicKeyRsa (linee 21÷24) per
contenere la chiave pubblica costituita dalla coppia (n, e). Abbiamo visto che questi
due numeri sono le uniche informazioni condivise pubblicamente e, quindi, chi le
riceve non ha bisogno di utilizzare tutti i campi della struct keysRsa (contrariamente
a chi genera entrambe le chiavi). Di conseguenza la funzione ReceivePublicKey()
riceve come parametro una struct publicKeyRsa (linea 26) in cui memorizzerà la
chiave pubblica ricevuta. Poiché quest’ultima viene utilizzata anche nell’operazione
di cifratura, anche la funzione RsaEncrypt() ha come parametro d’ingresso una struct
publicKeyRsa (linea 27).
Al punto 2.5 del capitolo 2 si è parlato, inoltre, dell’effetto che l’overloading degli
operatori del C++ determina sulla leggibilità del codice. Abbiamo visto che in C++
le variabili dell’algoritmo sono oggetti della classe mpz_class, che quest’ultima
realizza l’overloading degli operatori (relazionali, matematici e di input/output) e che
ciò semplifica notevolmente l’utilizzo di tale tipo di dato e la leggibilità del codice.
In C, invece, le variabili sono di tipo mpz_t ed è necessario utilizzare le funzioni
specifiche della libreria GMP per lavorare su di esse. Questo può peggiorare di molto
la leggibilità del codice se confrontato con il C++, come risulta evidente per i metodi
ComputeGCD() e ComputeD(). In entrambe le funzioni le operazioni svolte sui dati
possono risultare di difficile comprensione, a differenza di quanto accade per le
stesse funzioni scritte in C++. Si ritiene, quindi, opportuno evidenziare le funzioni
della libreria GMP utilizzate in RsaLib (oltre a quelle già citate in precedenza):

• mpz_sub() che esegue la sottrazione tra due interi mpz_t, ovvero svolge il
compito dell’operatore “-” per questo tipo di dati
• mpz_ui_sub() per sottrarre un intero mpz_t ad uno unsigned long
• mpz_mul() che esegue la sottrazione tra due interi mpz_t (operatore “ * ”)
• mpz_add() che svolge il compito dell’operatore “+” per gli interi mpz_t
• mpz_fdiv_r() che esegue l’operazione “%” tra due interi mpz_t
• mpz_fdiv_q() che esegue l’operazione “ / ” tra due interi mpz_t

Anche la libreria RsaDataLib è stata opportunamente tradotta in linguaggio C (il


codice è riportato in appendice alle voci RsaDataLib.h RsaDataLib.c). Come fatto in
precedenza, ne richiamiamo di seguito la dichiarazione delle funzioni:

6      void  ReceiveAndDecryptData(struct  keysRsa*  privateKey,  int  socket);  


7      char*  ReadFromFile();  
8      char*  ReadMessage();  
9      void  SendData(char*  data,  struct  publicKeyRsa*  pbk,  int  socket);  
10    bool  ReadAndSendData(struct  publicKeyRsa*  rk,  int  socket);  
11    char*  ResizeDinamicString(char*  toResize,  int  newDim);  
12    void  RebuildData(char*  decryptedStr);  

Anche in questo caso sono state mantenute tutte le funzioni presenti nella versione in
C++ e quindi la comunicazione cifrata fra due parti avviene esattamente nello stesso
modo descritto in precedenza.

40  
Anche in questo caso valgono le considerazioni fatte al punto 2.5 del capitolo 2 per
quanto riguarda la gestione delle stringhe di dimensione dinamica. Per maggiori
dettagli si rimanda a tale capitolo. Qui ci limitiamo solamente a evidenziare il ricorso
all’allocazione dinamica della memoria. Da notare, a questo proposito, la presenza
della funzione ResizeDinamicString() nella libreria (linea11) utilizzata per il
ridimensionamento delle stringhe (già descritta nel capitolo 2) e delle chiamate a
malloc() e free() nel codice (rispettivamente linee 54, 201 e 32, 36, 90, 98, 189 e 208
in appendice).
Il codice relativo ai programmi scritti come esempio di utilizzo delle librerie RsaLib
e RsaDataLib è riportato in appendice alle voci Rsa_s.c e Rsa_c.c.

41  
42  
Conclusioni

In questa tesi sono stati realizzati tutti gli strumenti e i programmi necessari ad
effettuare comunicazioni cifrate attraverso la rete grazie agli algoritmi considerati. Il
raggiungimento di tale risultato ha richiesto lo studio di nuovi argomenti o,
comunque, l’approfondimento di argomenti trattati solo parzialmente durante i corsi.
Questo ha permesso, quindi, l’apprendimento di importanti nozioni riguardanti la
crittografia, la sicurezza informatica e l’utilizzo di strumenti per la programmazione
in linguaggio C e C++ che estendono le conoscenze acquisite durante i corsi.

43  
44  
Bibliografia

[1] «Corso di Reti di Calcolatori», 18/12/2014,


http://web.diegm.uniud.it/pierluca/public_html/teaching/reti_di_calcolatori/reti_d
i_calcolatori.html

[2] «Cygwin», 18/12/2014, https://cygwin.com/install.html

[3] «GMP», 18/12/2014, https://gmplib.org/#DOWNLOAD

[4] W. Diffie and M.E. Hellman, “New Directions in Cryptography”, IEEE


Transactions on Information Theory, Vol. IT-22, No. 6, November 1976, pp.
644-654.

[5] «Diffie-Hellman: choosing wrong generator “g” parameter and its implications of
practical attacks», 18/12/2014,
http://crypto.stackexchange.com/questions/10025/diffie-hellman-choosing-
wrong-generator-g-parameter-and-its-implications-of-p

[6] «How does one calculate a primitive root for Diffie-Hellman?», 18/12/2014,
http://crypto.stackexchange.com/questions/820/how-does-one-calculate-a-
primitive-root-for-diffie-hellman

[7] R.L. Rivest, A. Shamir and L. Adelman “A Method for Obtaining Digital
Signatures and Public-Key Cryptosystems”, Communications of the ACM, Vol.
21, No 2, February 1978, pp. 120-126.

[8] A.S. Tanenbaum, D.J Wetherall, “Reti Di Calcolatori”, Editore Pearson,


settembre 2011, pp 758-759.

[9] «Modular inverse», 18/12/2014, http://rosettacode.org/wiki/Modular_inverse

45  
46  
Appendici

File in C++

ClassDh.hpp

1      #ifndef  DH_HPP  
2      #define  DH_HPP  
3  
4      #include  <cassert>  
5      #include  <iostream>  
6      #include  <gmpxx.h>  
7      #include  <string>  
8      #include  <unistd.h>  
9      #include  "udpsocketlib.hpp"  
10    #include  "openssl/bn.h"  
11  
12    using  namespace  std;  
13  
14    class  Dh  
15    {  
16            public:  
17                    mpz_class  ReturnKey()  const  {  return  K;  }  
18                    int  SizeOfK()  const  {  return  mpz_sizeinbase(K.get_mpz_t(),  
10);  }  
19                    bool  GenerateEncryptionKey_Client(int  socket,  string  
ipAddress,  int  udpPort);  
20                    bool  GenerateEncryptionKey_Server(int  socket);  
21                    string  DhEncrypt(const  string&  toEncryptStr);  
22                    string  DhDecrypt(const  char*  toDecrypt);  
23  
24            private:  
25                    mpz_class  p,  g,  a,  b,  A,  B;  
26                    mpz_class  K;  //key  
27                    mpz_class  GetVariable(string  varName);  
28                    mpz_class  GeneratePrimeNumber(string  name,  unsigned  numBits);  
29                    mpz_class  FindGenerator();  
30                    void  GetPgaAndSetA(int  choice);  
31                    bool  SendPGA(int  socket,  string  ipAddress,  int  udpPort);  
32                    bool  ReceiveBandComputeK(int  socket);  
33                    bool  ReceivePGA(int  socket);  
34                    bool  SendB(int  socket);  
35                    void  ComputeBandKey();  
36                    int  rdtsc();  
37    };  
38    #endif  

47  
ClassDh.cpp

1      #include  "ClassDh.hpp"  
2  
3      mpz_class  Dh::GetVariable(string  varName)  
4      {  
5              mpz_class  var;  
6              string  varStr;  
7  
8              do  
9              {  
10                    cout<<"\nEnter  "<<varName<<":  ";  
11                    cin>>varStr;  
12                    var.set_str(varStr,  10);  
13                    if  (var  <=  0)  
14                            cout<<"\nError.  Please  insert  a  number  >  0"<<endl;  
15            }while  (var  <=  0);  
16            return  var;  
17    }  
18  
19    void  Dh::GetPgaAndSetA(int  choice)  
20    {  
21            if  (choice  ==  1)  
22            {  
23                    cout<<"\n\nGenerating  'p'  and  'g'...\n\n";  
24                    p  =  GeneratePrimeNumber("p",  1024);  
25                    g  =  FindGenerator();  
26            }  
27            else  if  (choice  ==  2)  
28            {  
29                    p  =  GetVariable("'p'");  
30                    g  =  GetVariable("'g'");  
31            }  
32            a  =  GetVariable("'a',  such  that  1<a<p");  
33            assert(a  <  p  &&  a  >  1);  
34            mpz_powm(A.get_mpz_t(),  g.get_mpz_t(),  a.get_mpz_t(),  
p.get_mpz_t());  
35            cout<<"\n\tA=g^a  (mod  p)="<<A<<endl;  
36    }  
37  
38    mpz_class  Dh::FindGenerator()  
39    {  
40            mpz_class  g;  
41            string  pStr,  gStr;  
42            int  size,  temp;  
43            bool  sameSize;  
44            g  =  p  –  2;  
45            pStr  =  p.get_str(10);  
46            do  
47            {  
48                    srand(rdtsc());  
49                    size  =  (rand()  %  (pStr.size()  –  1  ))  +  1;  
50                    sameSize  =  (size  ==  (int)pStr.size());  
51                    for  (int  i  =  0;  i  <  size;  i++)  
52                    {  
53                            srand(rdtsc());  

48  
54                            if  (sameSize)  
55                            {  
56                                    temp  =  pStr[i]  -­‐  '0';  
57                                    if  (temp  !=  0)  
58                                            temp  =  rand()  %  temp;  
59                            }  
60                            else  
61                                    temp  =  rand()  %  9;  
62                            gStr.push_back((char)  temp  +  '0');  
63                    }  
64                    g.set_str(gStr,  10);  
65            }while  (g  ==  2);  
66            cout<<"\n\tg="<<g<<endl;  
67            return  g;  
68    }  
69  
70    mpz_class  Dh::GeneratePrimeNumber(string  name,  unsigned  numBits)  
71    {  
72            bool  ctrl  =  false;  
73            mpz_class  num;  
74            BIGNUM  primeNum;  
75            BN_init(&primeNum);  
76            do  
77            {  
78                    ctrl  =  BN_generate_prime_ex(&primeNum,  numBits,  1,  NULL,  NULL,  
NULL);  
79            }while  (!ctrl);  
80            num  =  BN_bn2dec(&primeNum);  
81            BN_free(&primeNum);  
82            cout<<"\n\t"<<name<<"="<<num<<endl;  
83            return  num;  
84    }  
85  
86    bool  Dh::SendPGA(int  socket,  string  ipAddress,  int  udpPort)  
87    {  
88            string  toSend[3];  
89            toSend[0]  =  p.get_str(10);  
90            toSend[1]  =  g.get_str(10);  
91            toSend[2]  =  A.get_str(10);  
92            cout<<"\nSending  p,g  and  A..."<<endl<<endl;  
93            for  (unsigned  count  =  0;  count  <  3;  count++)  
94            {  
95                    if  (!udp_send(socket,  (char*)toSend[count].c_str(),  
(char*)ipAddress.c_str(),  udpPort))  
96                    {  
97                            cout<<"Unable  to  send  p,  g  and  A..."<<endl<<endl;  
98                            return  false;  
99                    }  
100                    sleep(1);  
101          }  
102          return  true;  
103  }  
104  
105  bool  Dh::ReceiveBandComputeK(int  socket)  
106  {  
107          char  strB[BUFSIZ+1];  
108  

49  
109          if  (udp_receive(socket,  strB)  ==  -­‐1)  
110          {  
111                  cout<<"Unable  to  receive  B!"<<endl<<endl;  
112                  return  false;  
113          }  
114          cout<<"B  received!\n\n\tB="<<strB<<endl<<endl;  
115          B.set_str(strB,  10);  
116          if  (B  >  0)  
117          {  
118                  mpz_powm(K.get_mpz_t(),  B.get_mpz_t(),  a.get_mpz_t(),  
p.get_mpz_t());  
119                  return  true;  
120          }  
121          else  
122                  return  false;  
123  }  
124  
125  bool  Dh::ReceivePGA(int  socket)  
126  {  
127          char  strPGA[3][BUFSIZ+1];  
128          unsigned  count  =  0;  
129  
130          cout<<"\n\nWaiting  for  p,  g  and  A...\n\n";  
131          do  
132          {  
133                  if  (udp_receive(socket,  strPGA[count])  ==  -­‐1)  
134                  {  
135                          cout<<"Unable  to  receive  p,g  and  A!"<<endl<<endl;  
136                          return  false;  
137                  }  
138                  count++;  
139          }while  (count  <  3);  
140  
141          p.set_str(strPGA[0],  10);  
142          g.set_str(strPGA[1],  10);  
143          A.set_str(strPGA[2],  10);  
144  
145          if  (p  !=  0  &&  g  !=  0  &&  A  !=0)  
146          {  
147                  cout<<"Data  received:"<<endl<<endl;  
148                  cout<<"\n\tp="<<p<<"\n\n\tg="<<g<<"\n\n\tA="<<A<<endl<<endl;  
149                return  true;  
150          }  
151          else  
152          {  
153                  cout<<"Error.  Wrong  data."<<endl<<endl;  
154                  return  false;  
155          }  
156  }  
157  
158  bool  Dh::SendB(int  socket)  
159  {  
160          string  toSend  =  B.get_str(10);  
161  
162          cout<<"Sending  B..."<<endl<<endl;  
163          if  (!udp_reply(socket,  (char*)toSend.c_str()))  
164          {  

50  
165                  cout<<"Unable  to  send  B..."<<endl<<endl;  
166                  return  false;  
167          }  
168          cout<<"Data  sent!"<<endl<<endl;  
169          return  true;  
170  }  
171  
172  void  Dh::ComputeBandKey()  
173  {  
174          b  =  GetVariable("'b',  such  that  1<b<p");  
175          assert(b  >  1  &&  b  <  p);  
176  
177          mpz_powm(B.get_mpz_t(),  g.get_mpz_t(),  b.get_mpz_t(),  
p.get_mpz_t());  
178          mpz_powm(K.get_mpz_t(),  A.get_mpz_t(),  b.get_mpz_t(),  
p.get_mpz_t());  
179          cout<<"\n\tB=g^b  (mod  p)="<<B<<endl<<endl;  
180  }  
181  
182  bool  Dh::GenerateEncryptionKey_Client(int  socket,  string  ipAddress,  
int  udpPort)  
183  {  
184          int  choice;  
185          cout<<"\n\nAutomatically  generate  p,g?\n";  
186          cout<<"1)Yes.\n";  
187          cout<<"2)No,  I  insert  them.\n";  
188          do  
189          {  
190                  cout<<"\nChoose  an  option:";  
191                  cin>>choice;  
192                  if  (choice  !=  1  &&  choice  !=  2)  
193                          cout<<"\nInvalid  choice!\n\n";  
194          }while  (choice  !=  1  &&  choice  !=  2);  
195  
196          GetPgaAndSetA(choice);  
197  
198          if  (SendPGA(socket,  (char*)ipAddress.c_str(),  udpPort))  
199          {  
200                  cout<<"Data  sent!\n\nWaiting  for  B..."<<endl<<endl;  
201                  if  (ReceiveBandComputeK(socket))  
202                  {  
203                          cout<<"\n\nThe  encryption  key  is:\n\n\tK=B^a  (mod  
p)="<<K<<endl<<endl;  
204                          return  true;  
205                  }  
206          }  
207          cout<<"\nUnable  to  create  the  key."<<endl;  
208          return  false;  
209  }  
210  
211  bool  Dh::GenerateEncryptionKey_Server(int  socket)  
212  {  
213          if  (ReceivePGA(socket))  
214          {  
215                  ComputeBandKey();  
216                  if  (SendB(socket))  
217                  {  

51  
218                          cout<<"\n\nThe  encryption  key  is:\n\n\tK=A^b  (mod  
p)="<<K<<endl<<endl;  
219                          return  true;  
220                  }  
221          }  
222          cout<<"\nUnable  to  create  the  key."<<endl;  
223          return  false;  
224  }  
225  
226  string  Dh::DhEncrypt(const  string&  toEncryptStr)  
227  {  
228          string  key,  encrypted;  
229          char  ch[4];  
230          key  =  K.get_str(10);  
231          for  (unsigned  i  =  0;  i  <  toEncryptStr.size();  i++)  
232          {  
233                  sprintf(ch,  "%.3d",  (unsigned  char)toEncryptStr[i]^  (unsigned  
char)key[i]);  
234                  encrypted  +=  ch;  
235          }  
236          return  encrypted;  
237  }  
238  
239  string  Dh::DhDecrypt(const  char*  toDecrypt)  
240  {  
241          string  key,  decrypted;  
242          char  ch[4];  
243          unsigned  aux  =  0,  count  =  0;  
244          key  =  K.get_str(10);  
245          decrypted.append(toDecrypt);  
246          for  (unsigned  i  =  0;  i  <  strlen(toDecrypt);  i++)  
247          {  
248                  ch[aux]  =  toDecrypt[i];  
249                  aux++;  
250                  if  (aux  ==  3)  
251                  {  
252                          ch[3]  =  '\0';  
253                          decrypted.push_back((unsigned  char)  atoi(ch)^key[count]);  
254                          aux  =  0,  count++;  
255                  }  
256          }  
257          return  decrypted;  
258  }  
259  
260  int  Dh::rdtsc()  
261  {  
262          __asm__  __volatile__("rdtsc");  
263  }  
264  
265  void  error_handler(char  *message)  
266  {  
267          cout<<"fatal  error:  "<<message<<endl;  
268          return;  
269  }  

52  
DhDataLib.hpp

1      #ifndef  DHDATALIB_HPP  
2      #define  DHDATALIB_HPP  
3  
4      #include  "ClassDh.hpp"  
5      #include  <fstream>  
6  
7      bool  ReadAndSendData(Dh&  srv,  int  socket);  
8      bool  ReceiveAndDecryptData(Dh&  srv,  int  socket);  
9      void  SendData(const  string&  data,  Dh&  srv,  int  socket);  
10    string  ReadFromFile();  
11    string  ReadMessage();  
12    void  printStrInDec(const  string&  toPrint);  
13    void  printStrInHex(const  string&  toPrint,  bool  crypted);  
14  
15    #endif  

DhDataLib.cpp

1      #include  "DhDataLib.hpp"  
2  
3      void  printStrInDec(const  string&  toPrint)  
4      {  
5              for  (unsigned  i  =  0;  i  <  toPrint.size();  i++)  
6              {  
7                      cout<<toPrint[i];  
8                      if  (((i  +  1)  %  3)  ==  0)  
9                              cout<<”  “;  
10            }  
11            cout<<endl;  
12    }  
13  
14    void  printStrInHex(const  string&  toPrint,  bool  crypted)  
15    {  
16            char  ch[4];  
17            unsigned  k  =  0;  
18            cout<<"\nIn  hex:  ";  
19            for  (unsigned  i  =  0;  i  <  toPrint.size();  i++)  
20                    if  (crypted)  
21                    {  
22                            ch[k]  =  toPrint[i],  k++;  
23                            if  (k  ==  3)  
24                            {  
25                                    ch[3]  =  '\n',  k  =  0;  
26                                    cout<<hex<<atoi(ch)<<”  “;  
27                            }  
28                    }  
29                    else  
30                    cout<<hex<<(int)((unsigned  char)toPrint[i])<<"  ";  
31  
32            cout<<endl;  
33    }  
34  
 

53  
35    bool  ReceiveAndDecryptData(Dh&  srv,  int  socket)  
36    {  
37            bool  exitFunction  =  false;  
38            bool  exitProgram  =  false;  
39            char  buffer[BUFSIZ+1];  
40            string  messageDecripted,  bufferDecrypted;  
41  
42            cout<<"\n\n\nWaiting  for  data.."<<endl<<endl;  
43            while  (!exitFunction)  
44            {  
45                    udp_receive(socket,  buffer);  
46                    cout<<"\n\n\nEncrypted  data  received  (in  decimal):  ";  
47                    printStrInDec(buffer);  
48                    printStrInHex(buffer,  true);  
49                    bufferDecrypted  =  srv.DhDecrypt(buffer);  
50                    cout<<"\n\nDecrypted  data  received:  "<<bufferDecrypted<<endl;  
51                    printStrInHex(bufferDecrypted,  false);  
52                    messageDecripted  +=  bufferDecrypted;  
53  
54                    if  (messageDecripted.find("EOTS~EOTS~EOTS",  
messageDecripted.size()  -­‐14)!=  string::npos)  
55                    {  
56                            exitProgram  =  true;  
57                            exitFunction  =  true;  
58                    }  
59                    else  if  (messageDecripted.find("EOF~EOF~EOF",  
messageDecripted.size()  -­‐  11)!=  string::npos)  
60                    {  
61                            messageDecripted  =  
messageDecripted.erase(messageDecripted.size()  -­‐  11,  11);  
62                            cout<<"\n\n\nDecripted  data:"<<endl<<endl;  
63                            cout<<messageDecripted<<endl<<endl;  
64                            messageDecripted.clear();  
65                            exitFunction  =  true;  
66                    }  
67            }  
68            return  exitProgram;  
69    }  
70  
71    bool  ReadAndSendData(Dh&  srv,  int  socket)  
72    {  
73            string  message;  
74            int  choice;  
75            cout<<"\n\nWhat  do  you  want  to  do?"<<endl<<endl;  
76            cout<<"1)Send  a  message"<<endl;  
77            cout<<"2)Send  data  from  file"<<endl;  
78            cout<<"3)Exit"<<endl;  
79            do  
80            {  
81                    cout<<"Choose  an  option:";  
82                    cin>>choice;  
83                    switch  (choice)  
84                    {  
85                            case  1:  
86                            {  
87                                    message  =  ReadMessage();  
88                                    SendData(message,  srv,  socket);  

54  
89                                    break;  
90                            }  
91                            case  2:  
92                            {  
93                                    message  =  ReadFromFile();  
94                                    SendData(message,  srv,  socket);  
95                                    break;  
96                            }  
97                          case  3:  
98                                  SendData("EOTS~EOTS~EOTS",  srv,  socket);  
99                                  break;  
100                          default:  
101                                  cout<<"\nInvalid  choice!"<<endl<<endl;  
102                  }  
103          }while  (choice  !=  1  &&  choice  !=  2  &&  choice  !=  3);  
104          return  choice  ==  3;  
105  }  
106  
107  string  ReadMessage()  
108  {  
109          string  message;  
110          char  ch;  
111          cout<<"\nEnter  the  message  to  send:  ";  
112          ch  =  cin.get();  //  clean  the  stream.  
113          do  
114          {  
115                  ch  =  cin.get();  
116                  message  +=  ch;  
117          }  
118          while  (ch  !=  '\n');  
119                  message[message.size()  -­‐  1]=  '\0';  
120          message  +=  "EOF~EOF~EOF";  
121          return  message;  
122  }  
123  
124  string  ReadFromFile()  
125  {  
126          ifstream  is;  
127          string  nameFile,  fileData;  
128          char  ch;  
129          cout<<"\nEnter  file  name  with  extension:  ";  
130          cin>>nameFile;  
131          is.open(nameFile.c_str());  
132          while  (is.get(ch))  
133                  fileData  +=  ch;  
134  
135          is.close();  
136          fileData  +=  "EOF~EOF~EOF";  
137          return  fileData;  
138  }  
139  
140  void  SendData(const  string&  data,  Dh&  srv,  int  socket)  
141  {  
142          string  encryptedStr;  
143          int  keyLen  =  srv.SizeOfK();  
144          int  counter  =  0;  
145          for  (unsigned  i  =  0;  i  <  data.size();  i++)  

55  
146          {  
147                  encryptedStr.push_back(data[i]);  
148                  if  (counter  ==  keyLen  -­‐  1  ||  i  ==  data.size()  -­‐  1)  
149                  {  
150                          cout<<"\n\n\nData  to  send:  "<<encryptedStr<<endl;  
151                          printStrInHex(encryptedStr,  false);  
152                          encryptedStr  =  srv.DhEncrypt(encryptedStr);  
153                          cout<<"\n\nEncrypted  data  to  send  (in  decimal):  ";  
154                          printStrInDec(encryptedStr);  
155                          printStrInHex(encryptedStr,  true);  
156                          udp_reply(socket,  (char*)encryptedStr.c_str());  
157                          sleep(1);  
158                          encryptedStr.clear();  
159                          counter  =  0;  
160                  }  
161                  counter++;  
162          }  
163  }  

ClassRsa.hpp

1      #ifndef  RSA_HPP  
2      #define  RSA_HPP  
3  
4      #include  <cassert>  
5      #include  <iostream>  
6      #include  <gmpxx.h>  
7      #include  <string>  
8      #include  <unistd.h>  
9      #include  "udpsocketlib.hpp"  
10    #include  "openssl/bn.h"  
11  
12    using  namespace  std;  
13  
14    class  Rsa  
15    {  
16            public:  
17                    mpz_class  N()  const  {  return  n;}  
18                    int  SizeOfN()  const  {  return  mpz_sizeinbase(n.get_mpz_t(),  
10);}  
19                    mpz_class  E()  const  {  return  e;}  
20                    mpz_class  D()  const  {  return  d;}  
21                    void  GenerateEncryptionKeys();  
22                    bool  SendPublicKey(int  socket,  string  ipAddress,  int  udpPort);  
23                    bool  ReceivePublicKey(int  socket);  
24                    void  RsaEncrypt(string&  toEncryptStr);  
25                    string  RsaDecrypt  (const  char*  toDecrypt);  
26  
27        private:  
28                    mpz_class  p,  q,  phi;  
29                    mpz_class  n,  e,  d;  //keys  
30                    mpz_class  ComputeGCD(const  mpz_class&  x,  const  mpz_class&  y);  
31                    mpz_class  GetRandomE();  
32                    void  GeneratePQandComputeNPhi();  
33                    mpz_class  GeneratePrimeNumber(string  name,  unsigned  numBits);  
34                    void  FindE();  

56  
35                    void  ComputeD();  
36                    int  rdtsc();  
37    };  
38  
39    #endif  

ClassRsa.cpp

1      #include  "ClassRsa.hpp"  
2  
3      mpz_class  Rsa::GeneratePrimeNumber(string  name,  unsigned  numBits)  
4      {  
5              bool  ctrl  =  false;  
6              mpz_class  num;  
7              BIGNUM  primeNum;  
8              BN_init(&primeNum);  
9              do  
10            {  
11                    ctrl  =  BN_generate_prime_ex(&primeNum,  numBits,  1,  NULL,  NULL,  
NULL);  
12            }while  (!ctrl);  
13            num  =  BN_bn2dec(&primeNum);  
14            BN_free(&primeNum);  
15            cout<<"\t"<<name<<"="<<num<<endl<<endl;  
16            return  num;  
17    }  
18  
19    void  Rsa::GeneratePQandComputeNPhi()  
20    {  
21            cout<<"\n\nGenerating  prime  numbers  'p'  and  'q'.."<<endl<<endl;  
22            p  =  GeneratePrimeNumber("p",  512);  
23            q  =  GeneratePrimeNumber("q",  512);  
24            cout<<"\n\nComputing  'n'  and  'phi'.."<<endl<<endl;  
25            assert(p  !=  0  &&  q  !=  0);  
26            n  =  p  *  q;  
27            phi  =  (p  -­‐  1)  *  (q  –  1);  
28            cout<<"\tn=p*q="<<n<<"\n\n\tphi=(p-­‐1)*(q-­‐1)="<<phi<<endl<<endl;  
29    }  
30  
31    mpz_class  Rsa::GetRandomE()  
32    {  
33            mpz_class  aux;  
34            string  str;  
35            int  max,  min,  temp,  size;  
36            aux  =  phi  -­‐  1;  
37            max  =  mpz_sizeinbase(aux.get_mpz_t(),  10);  
38            if  (p  >=  q)  
39                    min  =  mpz_sizeinbase(p.get_mpz_t(),  10)  +  1;  
40            else  
41                    min  =  mpz_sizeinbase(q.get_mpz_t(),  10)  +  1;  
42            do  
43            {  
44                    srand(rdtsc());  
45                    size  =  (rand()  %  (max  -­‐  min))  +  min;  
46                    for  (int  i  =  0;  i  <  size;  i++)  
47                    {  

57  
48                            srand(rdtsc());  
49                            temp  =  rand()  %  9;  
50                            str.push_back((char)temp  +  '0');  
51                    }  
52                    aux.set_str(str,  10);  
53            }while  (aux  >=  phi  -­‐  1);  
54            return  aux;  
55    }  
56  
57    void  Rsa::FindE()  
58    {  
59            mpz_class  temp  =  0;  
60            bool  eFound  =  false;  
61            assert(phi  !=  0);  
62            do  
63            {  
64                    e  =  GetRandomE();  
65                    while  (temp!=  1  &&  e  >  p  &&  e  >  q)  
66                    {  
67                            temp  =  ComputeGCD(e,  phi);  
68                            if  (temp  ==  1)  
69                                    eFound  =  true;  
70                            else  
71                                    e  -­‐=  1;  
72                    }  
73            }while  (!eFound);  
74            cout<<"\n\n\t'e'  such  that  GCD(e,phi)=1  is:"<<e<<endl<<endl;  
75    }  
76  
77    mpz_class  Rsa::ComputeGCD(const  mpz_class&  x,  const  mpz_class&  y)  
78    {  
79            mpz_class  m  =  0,  n  =  0,  r  =  0;  
80            m  =  x;  
81            n  =  y;  
82            if  (m  >  n)  
83                    r  =  n;  
84            else  
85            {  
86                    r  =  m;  
87                    m  =  n;  
88                    n  =  r;  
89            }  
90            while  (r  !=  0)  
91            {  
92                    r  =  m  %  n;  
93                    m  =  n;  
94                    n  =  r;  
95            }  
96            r  =  m;  
97            return  r;  
98    }  
99  
100  void  Rsa::ComputeD()  
101  {  
102          mpz_class  t,  nt,  r,  nr,  q,  tmp,  a,  b;  
103          a  =  e;  b  =  phi;  
104          if  (b  <  0)  

58  
105                  b  =  -­‐b;  
106          if  (a  <  0)  
107                  a  =  b  -­‐  (-­‐a  %  b);  
108          t  =  0;  
109          nt  =  1;  
110          r  =  b;  
111          nr  =  a  %  b;  
112          while  (nr  !=  0)  
113          {      
114                  q  =  r  /  nr;  
115                  tmp  =  nt;  
116                  nt  =  t  –  (q  *  nt);  
117                  t  =  tmp;  
118                  tmp  =  nr;  
119                  nr  =  r  –  (q  *  nr);  
120                  r  =  tmp;  
121          }  
122          assert(r  <=  1);  /*  No  inverse  */      
123          if  (t  <  0)  
124                  t  +=  b;  
125          d  =  t;  
126          cout<<"\t'd'  such  that  (e*d=  1  mod  phi)  is:"<<d<<endl<<endl;  
127  }  
128  
129  void  Rsa::GenerateEncryptionKeys()  
130  {  
131          GeneratePQandComputeNPhi();  
132          FindE();  
133          ComputeD();  
134          cout<<"\nThe  private  key  
is(d,n):\n\n\td="<<d<<"\n\n\tn="<<n<<endl<<endl;  
135          cout<<"\nThe  public  key  
is(e,n):\n\n\te="<<e<<"\n\n\tn="<<n<<endl<<endl;  
136  }  
137  
138  bool  Rsa::SendPublicKey(int  socket,  string  ipAddress,  int  udpPort)  
139  {  
140          string  public_key[2];  
141          assert(e  !=  0  &&  n  !=  0);  
142          public_key[0]  =  e.get_str(10);  
143          public_key[1]  =  n.get_str(10);  
144          cout<<"Sharing  the  public  key..Sending  'e'  and  
'n'..."<<endl<<endl;  
145          for  (unsigned  count  =  0;  count  <  2;  count++)  
146          {  
147                  if  (!udp_send(socket,  (char*)public_key[count].c_str(),  
(char*)ipAddress.c_str(),  udpPort))  
148                  {  
149                          cout<<"Unable  to  send  the  public  key..."<<endl<<endl;  
150                          return  false;  
151                  }  
152                  sleep(1);  
153          }  
154          cout<<"Data  sent!\n\nWaiting..."<<endl<<endl;  
155          return  true;  
156  }  
157  

59  
158  bool  Rsa::ReceivePublicKey(int  socket)  
159  {  
160          char  public_key[2][BUFSIZ+1];  
161          unsigned  count  =  0;  
162          cout<<"\n\nWaiting  for  public  key..."<<endl<<endl;  
163          do  
164          {  
165                  if  (udp_receive(socket,  public_key[count])  ==  -­‐1)  
166                  {  
167                          cout<<"Unable  to  receive  the  public  key..."<<endl<<endl;  
168                          return  false;  
169                  }  
170                  count++;  
171          }while  (count  <  2);  
172          e.set_str(public_key[0],  10);  
173          n.set_str(public_key[1],  10);  
174          if  (n  ==  0  ||  e  ==  0)  
175          {  
176                  cout<<"Error.  Wrong  public  key."<<endl<<endl;  
177                  return  false;  
178          }  
179          cout<<"The  public  key(e,n)  is:"<<endl<<endl;  
180          cout<<"\te="<<e<<"\n\n\tn="<<n<<endl<<endl;  
181          return  true;  
182 }  
183  
184  void  Rsa::RsaEncrypt(string&  toEncryptStr)  
185  {  
186          mpz_class  encrypted;  
187          encrypted.set_str(toEncryptStr,  10);  
188          cout<<"\n\nData  to  send(in  
decimal):"<<endl<<toEncryptStr<<endl<<endl;  
189  
190          mpz_powm(encrypted.get_mpz_t(),  encrypted.get_mpz_t()  ,  
e.get_mpz_t(),  n.get_mpz_t());  
191  
192          toEncryptStr  =  encrypted.get_str(10);  
193          cout<<"Encrypted  data  to  send(in  
decimal):"<<endl<<toEncryptStr<<endl<<endl;  
194  }  
195  
196  string  Rsa::RsaDecrypt(const  char*  toDecrypt)  
197  {  
198          mpz_class  decrypted;  
199          cout<<"\nEncrypted  data  received(in  
decimal):"<<endl<<toDecrypt<<endl<<endl;  
200          decrypted.set_str(toDecrypt,  10);  
201          mpz_powm(decrypted.get_mpz_t(),  decrypted.get_mpz_t()  ,  
d.get_mpz_t(),  n.get_mpz_t());  
202          return  decrypted.get_str(10);  
203  }  
204  
205  void  error_handler(char  *message)  
206  {  
207          cout<<"fatal  error:  "<<message<<endl;  
208          return;  
209  }  

60  
210  
211  int  Rsa::rdtsc()  
212  {  
213          __asm__  __volatile__("rdtsc");  
214  }  

RsaDataLib.hpp

1      #ifndef  RSADATALIB_HPP  
2      #define  RSADATALIB_HPP  
3  
4      #include  "ClassRsa.hpp"  
5      #include  <fstream>  
6  
7      void  ReceiveAndDecryptData(Rsa&  clt,  int  socket);  
8      bool  ReadAndSendData(Rsa&  srv,    int  socket);  
9      void  SendData(const  string&  data,  Rsa&  srv,  int  socket);  
10    string  ReadFromFile();  
11    string  ReadMessage();  
12    string  RebuildData(string&  decryptedStr);  
13  
14    #endif  

RsaDataLib.cpp

1      #include  "RsaDataLib.hpp"  
2  
3      void  ReceiveAndDecryptData(Rsa&  clt,  int  socket)  
4      {  
5              bool  exit  =  false;  
6              char  buffer[BUFSIZ  +  1];  
7              string  messageDecripted,  aux;  
8              while  (!exit)  
9              {  
10                    udp_receive(socket,  buffer);  
11                    aux  =  clt.RsaDecrypt(buffer);  
12                    messageDecripted  +=  RebuildData(aux);  
13                    if  (messageDecripted.find("EOTS~EOTS~EOTS",  
messageDecripted.size()  -­‐  14)!=  string::npos)  
14                    {  
15                            cout<<"\n\nExit.."<<endl<<endl;  
16                            exit  =  true;  
17                    }      
18                    else  if  (messageDecripted.find("EOF~EOF~EOF",  
messageDecripted.size()  -­‐  14)!=  string::npos)  
19                    {  
20                            messageDecripted  =  
messageDecripted.erase(messageDecripted.size()  -­‐  11,  11);  
21                            cout<<"\n\nDecripted  data:"<<endl<<endl;  
22                            cout<<messageDecripted<<endl<<endl;  
23                            messageDecripted.clear();  
24                            cout<<"\nWaiting  for  new  data.."<<endl<<endl;  
25                    }  
26              }  
27    }  

61  
28  
29    string  RebuildData(string&  decryptedStr)  
30    {  
31            int  r,  strLen,  k  =  0;  
32            char  ch[4];  
33            string  finalStr;  
34            strLen  =  decryptedStr.size();  
35            r  =  strLen  %  3;  
36            if  (r  !=0)  
37                    for  (int  i  =  0;  i  <  (3  –  r);  i++)  
38                            decryptedStr  =  "0"  +  decryptedStr;  
39            cout<<"Received  data  decrypted(in  
decimal):"<<endl<<decryptedStr<<endl<<endl;  
40            do  
41            {  
42                    int  length  =  0;  
43                    if  (strLen  >=  3)  
44                            length  =  decryptedStr.copy(ch,  3,  k);  
45                    else  
46                            length  =  decryptedStr.copy(ch,  strLen,  k);  
47                    ch[length]=  '\0';  
48                    char  c  =  (char)(atoi(ch));  
49                    finalStr  +=  c;  
50                    k  +=  3;  
51            }while  (k  <  strLen);  
52            cout<<"Received  data  decrypted:"<<endl<<finalStr<<endl<<endl;  
53            return  finalStr;  
54    }  
55  
56    bool  ReadAndSendData(Rsa&  srv,    int  socket)  
57    {  
58            string  message;  
59            int  choice;  
60            cout<<"\n\nWhat  do  you  want  to  do?"<<endl<<endl;  
61            cout<<"1)Send  a  message"<<endl;  
62            cout<<"2)Send  data  from  file"<<endl;  
63            cout<<"3)Exit"<<endl;  
64            do  
65            {  
66                    cout<<"Choose  an  option:";  
67                    cin>>choice;  
68                    switch  (choice)  
69                    {  
70                            case  1:  
71                            {  
72                                    message  =  ReadMessage();  
73                                    SendData(message,  srv,  socket);  
74                                    break;  
75                            }  
76                            case  2:  
77                            {  
78                                    message  =  ReadFromFile();  
79                                    SendData(message,  srv,  socket);  
80                                    break;  
81                            }  
82                            case  3:  
83                                    SendData("EOTS~EOTS~EOTS",  srv,  socket);  

62  
84                                    break;  
85                            default:  
86                                    cout<<"\nInvalid  choice!"<<endl<<endl;  
87                    }  
88            }while  (choice  !=  1  &&  choice  !=  2  &&  choice  !=  3);  
89            return  choice  ==  3;  
90    }  
91  
92    string  ReadMessage()  
93    {  
94            string  message;  
95            char  ch;  
96            cout<<"\nEnter  the  message  to  send:  ";  
97            ch  =  cin.get();  //  Clean  the  stream  
98            do  
99            {  
100                  ch  =  cin.get();  
101                  message  +=  ch;  
102          }  
103          while  (ch  !=  '\n');  
104          message[message.size()  -­‐  1]=  '\0';  
105          message  +=  "EOF~EOF~EOF";  
106          return  message;  
107  }  
108  
109  string  ReadFromFile()  
110  {  
111          ifstream  is;  
112          string  nameFile,  fileData;  
113          char  ch;  
114          cout<<"\nEnter  file  name  with  extension:  ";  
115          cin>>nameFile;  
116          is.open(nameFile.c_str());  
117          while  (is.get(ch))  
118                  fileData  +=  ch;  
119          is.close();  
120          fileData  +=  "EOF~EOF~EOF";  
121          return  fileData;  
122  }  
123  
124  void  SendData(const  string&  data,  Rsa&  srv,  int  socket)  
125  {  
126          string  encryptedStr,  toSend;  
127          mpz_class  encrypted,  n;  
128          char  ch[4];  
129          n  =  srv.N();  
130          for  (unsigned  i  =  0;  i  <=  data.size();  i++)  
131          {  
132                  sprintf(ch,  "%.3d",  (unsigned  char)data[i]);  
133                  encryptedStr  +=  ch;  
134                  encrypted.set_str(encryptedStr,  10);  
135                  toSend  +=  data[i];  
136                  if  (encrypted  >  n  ||  i  ==  data.size())  
137                  {  
138                          toSend.erase(toSend.size()  -­‐  1);  
139                          cout<<"\n\nData  to  send:"<<endl<<toSend;  

63  
140                          encryptedStr  =  encryptedStr.erase(encryptedStr.size()  -­‐  3,  
3);  
141                          srv.RsaEncrypt(encryptedStr);  
142                          udp_reply(socket,  (char*)encryptedStr.c_str());  
143                          sleep  (1);  
144                          encryptedStr.clear();  
145                          toSend.clear();  
146                          toSend  +=  data[i];  
147                          encryptedStr  +=  ch;  
148                  }  
149          }  
150  }  

File in C

DhLib.h

1      #ifndef  DHLIB_H  
2      #define  DHLIB_H  
3  
4      #include  <stdio.h>  
5      #include  <stdlib.h>  
6      #include  <gmp.h>  
7      #include  <string.h>  
8      #include  <unistd.h>  
9      #include  <openssl/bn.h>  
10    #include  <assert.h>  
11    #include  "udpsocketlib.h"  
12  
13    //  Create  type  bool  (not  present  in  c).  
14    typedef  enum  {  false,  true  }  bool;  
15  
16    struct  dhStruct  
17    {  
18            mpz_t  p,  g,  a,  b,  A,  B,  K;  
19    };  
20  
21    void  GeneratePrimeNumber(char*  name,  mpz_t  num,  unsigned  numBits);  
22    void  FindGenerator(mpz_t  q,  mpz_t  g);  
23    bool  GenerateEncryptionKey_Client(int  socket,  char*  ipAddress,  int  
udpPort,  struct  dhStruct*  dh);  
24    void  GetPgaAndSetA(struct  dhStruct*  dh,  int  choice);  
25    bool  SendPGA(int  socket,  char*  ipAddress,  int  udpPort,  struct  
dhStruct*  dh);  
26    bool  ReceiveBandComputeK(int  socket,  struct  dhStruct*  dh);  
27    bool  GenerateEncryptionKey_Server(int  socket,  struct  dhStruct*  dh);  
28    bool  ReceivePGA(int  socket,  struct  dhStruct*  dh);  
29    void  ComputeBandKey(struct  dhStruct*  dh);  
30    bool  SendB(int  socket,  struct  dhStruct*  dh);  
31    char*  DhEncrypt(char*  toEncryptStr,  char*  key);  
32    char*  DhDecrypt(char*  toDecrypt,  char*  key);  
33    void  GetVariable(char*  varName,  mpz_t  var);  
34    int  rdtsc();  

64  
35  
36    #endif  

DhLib.c

1      #include  "DhLib.h"  
2  
3      bool  GenerateEncryptionKey_Server(int  socket,  struct  dhStruct*  dh)  
4      {  
5              if  (ReceivePGA(socket,  dh))  
6              {  
7                      ComputeBandKey(dh);  
8                      if  (SendB(socket,  dh))  
9                      {  
10                            gmp_printf("\n\nThe  encryption  key  is:\n\n\tK=A^b  (mod  
p)=%Zd\n\n",  dh-­‐>K);  
11                            return  true;  
12                    }  
13            }  
14            printf("\nUnable  to  create  the  key.\n");  
15            return  false;  
16    }  
17  
18    bool  ReceivePGA(int  socket,  struct  dhStruct*  dh)  
19    {  
20            char  strPGA  [3][BUFSIZ+1];  
21            int  count  =  0;  
22            printf("\n\nWaiting  for  p,  g  and  A...\n\n");  
23            do  
24            {  
25                    if  (udp_receive(socket,  strPGA  [count])  ==  -­‐1)  
26                    {  
27                            printf("Unable  to  receive  p,g  and  A!\n\n");  
28                            return  false;  
29                    }  
30                    count++;  
31            }while  (count  <  3);  
32            mpz_set_str(dh-­‐>p,  strPGA  [0],  10);  
33            mpz_set_str(dh-­‐>g,  strPGA  [1],  10);  
34            mpz_set_str(dh-­‐>A,  strPGA  [2],  10);  
35            if  (mpz_cmp_ui(dh-­‐>p,  0)  !=0  &&  mpz_cmp_ui(dh-­‐>g,  0)  !=0  &&  
mpz_cmp_ui(dh-­‐>A,  0)  !=0)  
36            {  
37                    printf("Data  received:\n\n");  
38                    gmp_printf("\n\tp=%Zd\n\n\tg=%Zd\n\n\tA=%Zd\n\n",  dh-­‐>p,  dh-­‐
>g,  dh-­‐>A);  
39                    return  true;  
40            }  
41            else  
42            {  
43                    printf("Error.  Wrong  data.\n\n");  
44                    return  false;  
45            }  
46    }  
47  
 

65  
48    void  ComputeBandKey(struct  dhStruct*  dh)  
49    {  
50            GetVariable("'b',  such  that  1<b<p",  dh-­‐>b);  
51            assert(mpz_cmp_ui(dh-­‐>b,  1)  >  0  &&  mpz_cmp(dh-­‐>p,  dh-­‐>b)  >  0);  
52            mpz_powm(dh-­‐>B,  dh-­‐>g,  dh-­‐>b,  dh-­‐>p);  
53            mpz_powm(dh-­‐>K,  dh-­‐>A,  dh-­‐>b,  dh-­‐>p);    
54            gmp_printf("\n\tB=g^b  (mod  p)=%Zd\n\n",  dh-­‐>B);  
55    }  
56  
57    bool  SendB(int  socket,  struct  dhStruct*  dh)  
58    {  
59            char  *toSend  =  mpz_get_str(NULL,  10,  dh-­‐>B);  
60            printf("Sending  B...\n\n");  
61            if  (!udp_reply(socket,  toSend))  
62            {  
63                    printf("Unable  to  send  B...\n\n");  
64                    return  false;  
65            }  
66            printf("Data  sent!\n\n");  
67            return  true;  
68    }  
69  
70    bool  GenerateEncryptionKey_Client(int  socket,  char*  ipAddress,  int  
udpPort,  struct  dhStruct*  dh)  
71    {  
72            int  choice;  
73            printf("\n\nAutomatically  generate  p,g?\n");  
74            printf("1)Yes.\n");  
75            printf("2)No,  I  insert  them.\n");  
76            do  
77            {  
78                    printf("\nChoose  an  option:");  
79                    scanf("%d",  &choice);  
80                    if  (choice  !=  1  &&  choice  !=  2)  
81                            printf("\nInvalid  choice!\n\n");  
82            }while  (choice  !=  1  &&  choice  !=  2);  
83            GetPgaAndSetA(dh,  choice);  
84            if  (SendPGA(socket,  ipAddress,  udpPort,  dh))  
85            {  
86                    printf("Data  sent!\n\nWaiting  for  B...\n\n");  
87                    if  (ReceiveBandComputeK(socket,  dh))  
88                    {  
89                            gmp_printf("\n\nThe  encryption  key  is:\n\n\tK=B^a  (mod  
p)=%Zd\n\n",  dh-­‐>K);  
90                            return  true;  
91                    }  
92            }  
93            printf("\nUnable  to  create  the  key.\n");  
94            return  false;  
95    }  
96  
97    void  FindGenerator(mpz_t  p,  mpz_t  g)  
98    {  
99            char  *pStr;  
100          int  size,  temp,  i;  
101          bool  sameSize;  
102          mpz_set(g,  p);  

66  
103          mpz_sub_ui(g,  g,  2);  
104          pStr  =  mpz_get_str(NULL,  10,  p);  
105          do  
106          {  
107                  srand(rdtsc());  
108                  size  =  (rand()  %  ((int)  strlen(pStr)  -­‐  1))  +  1;  
109                  sameSize  =  (size  ==  (int)  strlen(pStr));  
110                  for  (i  =  0;  i  <  size;  i++)  
111                  {  
112                          srand(rdtsc());  
113                          if  (sameSize)  
114                          {  
115                                  temp  =  pStr[i]  -­‐  '0';  
116                                  if  (temp  !=  0)  
117                                          temp  =  rand()  %  temp;  
118                          }  
119                          else  
120                                  temp  =  rand()  %  9;  
121                          pStr[i]  =  ((char)  temp  +  '0');  
122                  }  
123                  pStr[size]  =  '\0';  
124                  mpz_set_str(g,  pStr,  10);  
125          }while  (mpz_cmp_ui(g,  2)  ==  0);  
126          gmp_printf("\n\tg=%Zd\n",  g);  
127  }  
128  
129  void  GeneratePrimeNumber(char*  name,  mpz_t  num,  unsigned  numBits)  
130  {  
131          bool  ctrl  =  false;  
132          BIGNUM  primeNum;  
133          BN_init(&primeNum);  
134          do  
135          {  
136                  ctrl  =  BN_generate_prime_ex(&primeNum,  numBits,  1,  NULL,  NULL,  
NULL);  
137          }while  (!ctrl);  
138          mpz_set_str(num,  BN_bn2dec(&primeNum),  10);  
139          BN_free(&primeNum);  
140          gmp_printf("\n\t%s=%Zd\n",  name,  num);  
141  }  
142  
143  void  GetPgaAndSetA(struct  dhStruct*  dh,  int  choice)  
144  {  
145          if  (choice  ==  1)  
146          {  
147                  printf("\n\nGenerating  'p'  and  'g'...\n\n");  
148                  GeneratePrimeNumber("p",  dh-­‐>p,  1024);  
149                  FindGenerator(dh-­‐>p,  dh-­‐>g);  
150          }  
151          else  if  (choice  ==  2)  
152          {  
153                  GetVariable("'p'",  dh-­‐>p);  
154                  GetVariable("'g'",  dh-­‐>g);  
155          }  
156          GetVariable("'a',  such  that  1<a<p",  dh-­‐>a);  
157          assert(mpz_cmp_ui(dh-­‐>a,  1)  >  0  &&  mpz_cmp(dh-­‐>p,  dh-­‐>a)  >  0);  
158          mpz_powm(dh-­‐>A,  dh-­‐>g,  dh-­‐>a,  dh-­‐>p);  

67  
159          gmp_printf("\n\tA=g^a  (mod  p)=%Zd\n",  dh-­‐>A);  
160  }  
161  
162  bool  SendPGA(int  socket,  char*  ipAddress,  int  udpPort,  struct  
dhStruct*  dh)  
163  {  
164          char  *toSend[3];  
165          int  count  =  0;  
166          toSend[0]  =  mpz_get_str(NULL,  10,  dh-­‐>p);  
167          toSend[1]  =  mpz_get_str(NULL,  10,  dh-­‐>g);  
168          toSend[2]  =  mpz_get_str(NULL,  10,  dh-­‐>A);  
169          printf("\nSending  p,g  and  A...\n\n");  
170          do  
171          {  
172                  if  (!udp_send(socket,  toSend[count],  ipAddress,  udpPort))  
173                  {  
174                          printf("Unable  to  send  p,  g  and  A...\n\n");  
175                          return  false;  
176                  }  
177                  count++;  
178                  sleep(1);  
179          }while  (count  <  3);  
180          return  true;  
181  }  
182  
183  bool  ReceiveBandComputeK(int  socket,  struct  dhStruct*  dh)  
184  {  
185          char  strB[BUFSIZ+1];  
186          if  (udp_receive(socket,  strB)  ==  -­‐1)  
187          {  
188                  printf("Unable  to  receive  B!\n\n");  
189                  return  false;  
190          }  
191          printf("B  received!\n\n\tB=%s\n\n",  strB);  
192          mpz_set_str(dh-­‐>B,  strB,  10);  
193          if  (mpz_cmp_ui(dh-­‐>B,  0)  >  0)  
194          {  
195                  mpz_powm(dh-­‐>K,  dh-­‐>B,  dh-­‐>a,  dh-­‐>p);  
196                  return  true;  
197          }  
198          else  
199                  return  false;  
200  }  
201  
202  void  error_handler(char  *message)  
203  {  
204          printf("fatal  error:  %s\n",  message);  
205          return;  
206  }  
207  
208  char*  DhEncrypt(char*  toEncryptStr,  char*  key)  
209  {  
210          char  ch[4];  
211          char  *aux  =  malloc((strlen(toEncryptStr)  *  3  +  2)*sizeof(char));  
212          int  i;  
213          aux[0]  =  '\0';  
214          for  (i  =  0;  i  <  strlen  (toEncryptStr);  i++)  

68  
215          {  
216                  sprintf(ch,"%.3d",  (unsigned  char)toEncryptStr[i]^(unsigned  
char)key[i]);  
217                  strcat(aux,  ch);  
218          }  
219          return  aux;  
220  }  
221  
222  char*  DhDecrypt  (char*  toDecrypt,  char*  key)  
223  {  
224          int  i,  aux  =  0,  count=  0;  
225          char  ch[4];  
226          char  *decrypted  =  malloc  ((strlen(key)  +  2)*sizeof(char));  
227          for  (i  =  0;  i  <  strlen(toDecrypt);  i++)  
228          {  
229                  ch[aux]  =  toDecrypt[i];  
230                  aux++;  
231                  if  (aux  ==  3)  
232                  {  
233                          ch[aux]='\0';  
234                          decrypted[count]  =  (unsigned  char)  atoi(ch)^key[count];  
235                          aux  =  0,  count++;  
236                  }  
237          }  
238          decrypted[count]  =  '\0';  
239          return  decrypted;  
240  }  
241  
242  void  GetVariable(char*  varName,  mpz_t  var)  
243  {  
244          do  
245          {  
246                  printf("\nEnter  %s:  ",  varName);  
247                  gmp_scanf("%Zd",  var);  
248                  if  (mpz_cmp_ui(var,  0)  <=  0)  
249                          printf("\nError.  Please  insert  a  number  >  0\n");  
250          }while  (var  <=  0);  
251  }  
252  
253  int  rdtsc()  
254  {  
255          __asm__  __volatile__("rdtsc");  
256  }  

DhDataLib.h

1      #ifndef  DHDATALIB_HPP  
2      #define  DHDATALIB_HPP  
3  
4      #include  "DhLib.h"  
5  
6      bool  ReadAndSendData(char*  key,  int  socket);  
7      bool  ReceiveAndDecryptData(char  *key,  int  socket);  
8      void  SendData(char*  data,  char*  key,  int  socket);  
9      char*  ResizeDinamicString(char*  toResize,  int  newDim);  
10    char*  ReadMessage();  

69  
11    char*  ReadFromFile();  
12    void  printStrInDec(char*  toPrint);  
13    void  printStrInHex(char*  toPrint,  bool  crypted);  
14    #endif  

DhDataLib.c

1      #include  "DhDataLib.h"  
2  
3      bool  ReadAndSendData(char*  key,  int  socket)  
4      {  
5              char*  message;  
6              int  choice;  
7              printf("\n\nWhat  do  you  want  to  do?\n\n");  
8              printf("1)Send  a  message\n");  
9              printf("2)Send  data  from  file\n");  
10            printf("3)Exit\n");  
11            do  
12            {  
13                    printf("Choose  an  option:");  
14                    scanf("%d",  &choice);  
15                    switch  (choice)  
16                    {  
17                            case  1:  
18                            {  
19                                    message  =  ReadMessage();  
20                                    SendData(message,  key,  socket);  
21                                    free(message);  
22                                    message  =  NULL;  
23                                    break;  
24                            }  
25                            case  2:  
26                            {  
27                                    message  =  ReadFromFile();  
28                                    SendData(message,  key,  socket);  
29                                    free(message);  
30                                    message  =  NULL;  
31                                    break;  
32                            }  
33                            case  3:  
34                                    SendData("EOTS~EOTS~EOTS",  key,  socket);  
35                                    break;  
36                            default:  
37                                    printf("\nInvalid  choice!\n\n");  
38                    }  
39            }while  (choice  !=  1  &&  choice  !=  2  &&  choice  !=  3);  
40            return  choice  ==  3;  
41    }  
42  
43    bool  ReceiveAndDecryptData(char  *key,  int  socket)  
44    {  
45            bool  exitFunction  =  false,  exitProgram  =  false;  
46            char  buffer[BUFSIZ  +  1],  *decrypted,  *messageDecript  =  NULL;  
47            int  messLen  =  0;  
48  
49            printf("\n\n\nWaiting  for  data...\n\n");  

70  
50            while  (!exitFunction)  
51            {  
52                    udp_receive(socket,  buffer);  
53                    printf("\n\n\nEncrypted  data  received  (in  decimal):  ");  
54                    printStrInDec(buffer);  
55                    printStrInHex(buffer,  true);  
56                    decrypted  =  DhDecrypt(buffer,  key);  
57                    messageDecript  =  ResizeDinamicString(messageDecript,  messLen  +  
strlen(decrypted));  
58                    printf("\n\nDecrypted  data  received:  %s\n",  decrypted);  
59                    printStrInHex(decrypted,  false);  
60                    strcat(messageDecript,  decrypted);  
61                    messLen  =  strlen(messageDecript);  
62  
63                    if  (strstr(messageDecript,  "EOTS~EOTS~EOTS")  !=  NULL)  
64                    {  
65                            exitProgram  =  true;  
66                            exitFunction  =  true;  
67                    }  
68                    else  if  (strstr(messageDecript,  "EOF~EOF~EOF")  !=  NULL)  
69                    {  
70                            messageDecript[strlen(messageDecript)  -­‐  11]  =  '\0';  
71                            printf("\n\nDecripted  data:\n\n%s\n\n",  messageDecript);  
72                            exitFunction  =  true;  
73                    }  
74                    if  (decrypted  !=  NULL)  
75                            free(decrypted);  
76                    decrypted  =  NULL;  
77            }  
78            if  (messageDecript  !=  NULL)  
79                    free(messageDecript);  
80            messageDecript  =  NULL;  
81  
82            return  exitProgram;  
83    }  
84  
85    void  SendData(char*  data,  char*  key,  int  socket)  
86    {  
87            int  dataLen  =  strlen(data),  keyLen  =  strlen(key),  i,  counter  =  0;  
88            char  encryptedStr[keyLen  +  1],  *encrypted;  
89            for  (i  =  0;  i  <  dataLen;  i++)  
90            {  
91                    encryptedStr[counter]  =  data[i];  
92                    counter++;  
93                    if  (counter  ==  keyLen  ||  i  ==  dataLen  -­‐  1)  
94                    {  
95                            encryptedStr[counter]  =  '\0';  
96                            printf("\n\n\nData  to  send:  %s\n",  encryptedStr);  
97                            printStrInHex(encryptedStr,  false);  
98                            encrypted  =  DhEncrypt(encryptedStr,  key);  
99                            printf("\n\nEncrypted  data  to  send  (in  decimal):  ");  
100                            printStrInDec(encrypted);  
101                          printStrInHex(encrypted,  true);  
102                          udp_reply(socket,  encrypted);  
103                          encryptedStr[0]  =  '\0';  
104                          counter  =  0;  
105                          if  (encrypted  !=  NULL)  

71  
106                                  free(encrypted);  
107                          encrypted  =  NULL;  
108                          sleep(1);  
109                  }  
110          }  
111  }  
112  
113  char*  ResizeDinamicString(char*  toResize,  int  newDim)  
114  {  
115          char*resized  =  malloc((newDim  +  1)*sizeof(char));  
116          if  (resized  ==  NULL)  
117                  printf("\n\nFailed  to  realloc.\n\n");  
118          resized[0]  =  '\0';  
119          if  (toResize  !=  NULL)  
120          {  
121                  strcpy(resized,  toResize);  
122                  free(toResize);  
123                  toResize  =  NULL;  
124          }  
125          return  resized;  
126  }  
127  
128  char*  ReadMessage()  
129  {  
130          char  *message  =  NULL;  
131          char  buffer[(BUFSIZ  +  1)];  
132          int  messLen  =  0;  
133          printf("\nEnter  the  message  to  send:  ");  
134          getchar();  
135          do  
136          {  
137                  fgets(buffer,  BUFSIZ,  stdin);  
138                  message  =  ResizeDinamicString(message,  messLen  +  
strlen(buffer));  
139                  strcat(message,  buffer);  
140                  messLen  =  strlen(message);  
141          }while  (buffer[strlen(buffer)  -­‐  1]  !=  '\n');  
142          message[messLen-­‐1]=  '\0';  
143          message  =  ResizeDinamicString(message,  strlen(message)  +  11);  
144          strcat(message,  "EOF~EOF~EOF");  
145          return  message;  
146  }  
147  
148  char*  ReadFromFile()  
149  {  
150          FILE*  source;  
151          char  fileName[1024],  buffer[(BUFSIZ  +  1)];  
152          char  *fileData  =  NULL;  
153          int  dataLen  =  0;  
154          do  
155          {  
156                  printf("\nInsert  file  name  with  extension:  ");  
157                  scanf("%s",fileName);  
158                  if  ((source  =  fopen(fileName,  "r"))  ==  NULL)  
159                          printf("\nCannot  read  the  file...Try  again\n");  
160          }while  (  source  ==  NULL  );    
161  

72  
162          while  (!feof(source))  
163          {  
164                  fgets(buffer,BUFSIZ,  source);  
165                  fileData  =  ResizeDinamicString(fileData,  dataLen  +  
strlen(buffer));  
166                  strcat(  fileData,  buffer  );  
167                  dataLen  =  strlen(fileData);  
168          }  
169          fileData[strlen(fileData)]=  '\0';  
170          fileData  =  ResizeDinamicString(fileData,  strlen(fileData)  +  11);  
171          strcat(fileData,  "EOF~EOF~EOF");  
172          fclose(source);  
173          return  fileData;  
174  }  
175  
176  void  printStrInDec(  char*  toPrint  )  
177  {  
178          int  i;  
179          for  (i  =  0;  i  <  strlen(toPrint);  i++)  
180          {  
181                  printf("%c",  toPrint[i]);  
182                  if  (((i  +  1)  %  3)  ==  0)  
183                          printf("  ");  
184          }  
185          printf("\n");  
186  }  
187  
 
188  void  printStrInHex(char*  toPrint,  bool  crypted)  
189  {  
190          int  i;  
191          char  ch[4];  
192          unsigned  k  =  0;  
193          printf("\nIn  hex:  ");  
194          for  (i  =  0;  i  <  strlen(toPrint);  i++)  
195                  if  (crypted)  
196                  {  
197                          ch[k]  =  toPrint[i],  k++;  
198                          if  (k  ==  3)  
199                          {  
200                                  ch[3]  =  '\0',  k  =0;  
201                                  printf("%x  ",  atoi(ch));  
202                          }  
203                  }  
204                  else  
205                          printf("%x  ",  (unsigned  char)  toPrint[i]);  
206          printf  (  "\n"  );  
207  }  

Dh_c.c

1      #include  "DhLib.h"  
2      #include  "DhDataLib.h"  
3  
4      int  main(int  argc,  char  *argv[])  
5      {  

73  
6              struct  dhStruct  clnt;  
7              int  sk,  udpPort;  
8              char  serverIpAdrress[1024];  
9              char  *key;  
10            bool  done  =  false;  
11            mpz_inits(clnt.p,  clnt.g,  clnt.a,  clnt.b,  clnt.A,  clnt.B,  clnt.K,  
NULL);  
12  
13            if  (argc  ==  1)  
14            {  
15                    printf("Server  ip  address:  ");  
16                    scanf("%s",  serverIpAdrress);  
17                    printf("Udp  port:");  
18                    scanf("%d",  &udpPort);  
19            }  
20            else  if  (argc  ==  3)  
21            {  
22                    strcpy(serverIpAdrress,  argv[1]);  
23                    udpPort  =  atoi(argv[2]);  
24            }  
25            else  
26            {  
27                    printf("Required  arguments:  server  ip  address,  udp  port\n");  
28                    exit(1);  
29            }  
30  
31            if  ((sk  =  create_udp_client())  <  0)  
32            {  
33                    printf("Cannot  open  client  socket\n");  
34                    exit(1);  
35            }  
36  
37            if  (!GenerateEncryptionKey_Client(sk,  serverIpAdrress  ,udpPort,  
&clnt))  
38                    exit(1);  
39            key  =  mpz_get_str(NULL,  10,  clnt.K);  
40            do  
41            {  
42                    done  =  ReadAndSendData(key,  sk);  
43                    if  (!done)  
44                            done  =  ReceiveAndDecryptData(key,  sk);  
45            }while  (!done);  
46  
47            printf("\n\nExit...\n\n");  
48            close_udp_socket(sk);  
49            mpz_clears(clnt.p,  clnt.g,  clnt.a,  clnt.b,  clnt.A,  clnt.B,  clnt.K,  
NULL);  
50            return  EXIT_SUCCESS;  
51  }  

Dh_s.c

1      #include  "DhLib.h"  
2      #include  "DhDataLib.h"  
3  
4      int  main(int  argc,  char  *argv[])  

74  
5      {  
6              struct  dhStruct  srv;  
7              int  sk,  udpPort;  
8              char  serverIpAdrress[1024];  
9              char  *key;  
10            bool  done  =  false;  
11            mpz_inits(srv.p,  srv.g,  srv.b,  srv.B,  srv.K,  srv.A,  NULL);  
12  
13            if  (argc  ==  1)  
14            {  
15                    printf("Server  ip  address:  ");  
16                    scanf("%s",  serverIpAdrress);  
17                    printf("Udp  port:");  
18                    scanf("%d",  &udpPort);  
19            }  
20            else  if  (argc  ==  3)  
21            {  
22                    strcpy(serverIpAdrress,  argv[1]);  
23                    udpPort  =  atoi(argv[2]);  
24            }  
25            else  
26            {  
27                    printf("Required  arguments:  server  ip  address,  udp  port\n");  
28                    exit(1);  
29            }  
30  
31            if  ((sk  =  create_udp_server(serverIpAdrress,  udpPort))  <  0)  
32            {  
33                    printf("Cannot  open  server  socket\n");  
34                    exit(1);  
35            }  
36  
37            if  (!GenerateEncryptionKey_Server(sk,  &srv))  
38                    exit(1);  
39            key  =  mpz_get_str(NULL,  10,  srv.K);  
40            do  
41            {  
42                    done  =  ReceiveAndDecryptData(key,  sk);  
43                    if  (!done)  
44                            done  =  ReadAndSendData(key,  sk);  
45            }while  (!done);  
46  
47            printf("\n\nExit...\n\n");  
48            close_udp_socket(sk);  
49            mpz_clears(srv.p,  srv.g,  srv.b,  srv.B,  srv.K,  srv.A,  NULL);  
50            return  EXIT_SUCCESS;  
51  }  

RsaLib.h

1      #ifndef  RSALIB_H  
2      #define  RSALIB_H  
3  
4      #include  <stdio.h>  
5      #include  <stdlib.h>  
6      #include  <gmp.h>  

75  
7      #include  <string.h>  
8      #include  <openssl/bn.h>  
9      #include  <unistd.h>  
10    #include  "udpsocketlib.h"  
11    #include  <assert.h>  
12    //  Create  type  bool  (not  present  in  c).  
13    typedef  enum  {  false,  true  }  bool;  
14  
15    struct  keysRsa  
16    {  
17            mpz_t  p,  q,  phi;  
18            mpz_t  n,  e,  d;  //keys  
19    };  
20  
21    struct  publicKeyRsa  
22    {  
23            mpz_t  e,  n;  
24    };  
25  
26    bool  ReceivePublicKey(struct  publicKeyRsa*  pbk,  int  socket);  
27    char*  RsaEncrypt(char*  toEncryptStr,  struct  publicKeyRsa*  pbk);  
28    char*  RsaDecrypt(char*  toDecrypt,  struct  keysRsa*  key);  
29    void  GenerateEncryptionKeys(struct  keysRsa*  keys);  
30    void  GeneratePrimeNumber(char*  name,  mpz_t  num,  int  numBits);  
31    void  GeneratePQandComputeNPhi(struct  keysRsa*  var);  
32    void  GetRandomE(struct  keysRsa*  var);  
33    void  FindE(struct  keysRsa*  var);  
34    void  ComputeGCD(mpz_t  r,  mpz_t  e,  mpz_t  phi);  
35    void  ComputeD(struct  keysRsa*  var);  
36    bool  SendPublicKey(int  socket,  char*  ipAddress,  int  udpPort,  struct  
keysRsa*  pubK);  
37    int  rdtsc();  
38  
39    #endif  

RsaLib.c

1      #include  "RsaLib.h"  
2  
3      bool  ReceivePublicKey(struct  publicKeyRsa*  pbk,  int  socket)  
4      {  
5              char  public_key[2][BUFSIZ  +  1];  
6              unsigned  count  =  0;  
7              printf("\n\nWaiting  for  public  key...\n\n");  
8              do  
9              {  
10                    if  (udp_receive(socket,  public_key[count])  ==  -­‐1)  
11                    {  
12                            printf("Unable  to  receive  the  public  key...\n\n");  
13                            return  false;  
14                    }  
15                    count++;  
16            }while  (count  <  2);  
17            mpz_set_str(pbk-­‐>e,  public_key[0],  10);  
18            mpz_set_str(pbk-­‐>n,  public_key[1],  10);  
19            if  (mpz_cmp_ui(pbk-­‐>n,  0)  <=  0  ||  mpz_cmp_ui(pbk-­‐>e,  0)  <=  0)  

76  
20            {  
21                    printf("Error.  Wrong  public  key.\n\n");  
22                    return  false;  
23            }  
24            printf("The  public  key(e,n)  is:\n\n");  
25            gmp_printf("\te=%Zd\n\n\tn=%Zd\n\n",  pbk-­‐>e,  pbk-­‐>n);  
26            return  true;  
27    }  
28      
29  
30    void  GenerateEncryptionKeys(struct  keysRsa*  keys)  
31    {  
32            GeneratePQandComputeNPhi(keys);  
33            FindE(keys);  
34            ComputeD(keys);  
35            gmp_printf("\nThe  private  key  is(d,n):\n\n\td=%Zd\n\n\tn=%Zd\n",  
keys-­‐>d,  keys-­‐>n);  
36            gmp_printf("\n\nThe  public  key  is(e,n):\n\n\te=%Zd\n\n\tn=%Zd\n",  
keys-­‐>e,  keys-­‐>n);  
37    }  
38  
39    void  GeneratePrimeNumber(char*  name,  mpz_t  num,  int  numBits)  
40    {  
41            bool  ctrl  =  false;  
42            BIGNUM  primeNum;  
43            BN_init(&primeNum);  
44            do  
45            {  
46                    ctrl  =  BN_generate_prime_ex(&primeNum,  numBits,  1,  NULL,  NULL,  
NULL);  
47            }while  (!ctrl);  
48            mpz_set_str(num,  BN_bn2dec(&primeNum),  10);  
49            BN_free(&primeNum);  
50            gmp_printf("\t%s=%Zd\n\n",  name,  num);  
51    }  
52  
53    void  GeneratePQandComputeNPhi(struct  keysRsa*  var)  
54    {  
55            mpz_t  aux,  temp;  
56            mpz_inits(aux,  temp,  NULL);  
57            printf("\n\nGenerating  prime  numbers  'p'  and  'q'...\n\n");  
58            GeneratePrimeNumber("p",  var-­‐>p,  512);  
59            GeneratePrimeNumber("q",  var-­‐>q,  512);  
60            printf("\n\nComputing  'n'  and  'phi'...\n\n");  
61            mpz_mul(var-­‐>n,  var-­‐>p,  var-­‐>q);  
62            mpz_sub_ui(aux,  var-­‐>p,  1);  
63            mpz_sub_ui(temp,  var-­‐>q,  1);  
64            mpz_mul(var-­‐>phi,  aux,  temp);  
65            gmp_printf("\tn=p*q=%Zd\n\n\tphi=(p-­‐1)*(q-­‐1)=%Zd\n\n",  var-­‐>n,  
var-­‐>phi);  
66            mpz_clears(aux,  temp,  NULL);  
67    }  
68  
69    void  GetRandomE(struct  keysRsa*  var)  
70    {  
71            mpz_t  aux;  
72            char  str[mpz_sizeinbase(var-­‐>phi,  10)];  

77  
73            int  max,  min,  temp,  size,  i;  
74            mpz_init(aux);  
75            mpz_set(aux,  var-­‐>phi);  
76            mpz_sub_ui(aux,  aux,  1);  
77            max  =  mpz_sizeinbase(aux,  10);  
78            if  (  mpz_cmp(var-­‐>p,  var-­‐>q)  >=  0)  
79                    min  =  mpz_sizeinbase(var-­‐>p,  10)  +  1;  
80            else  
81                    min  =  mpz_sizeinbase(var-­‐>q,  10)  +  1;  
82            do  
83            {  
84                    srand(rdtsc());  
85                    size  =  (rand()  %  (max  -­‐    min))  +  min;  
86                    for  (i  =  0;  i  <  size;  i++)  
87                    {  
88                            srand(rdtsc());  
89                            temp  =  rand()  %  9;  
90                            str[i]  =  (char)temp  +  '0';  
91                    }  
92                    str[i]  =  '\0';  
93                    mpz_set_str(var-­‐>e,  str,  10);  
94            }while  (mpz_cmp(var-­‐>e,  aux)  >=  0);  
95            mpz_clears(aux,  NULL);  
96    }  
97  
98    void  FindE(struct  keysRsa*  var)  
99    {  
100            bool  eFound  =  false;  
101            mpz_t  t,  random;  
102            mpz_inits(t,  random,  NULL);  
103          do  
104          {  
105                  GetRandomE(var);  
106                  while  (mpz_cmp_ui(t,  1)  !=  0  &&  mpz_cmp(var-­‐>e,  var-­‐>p)  >  0  &&  
mpz_cmp(var-­‐>e,  var-­‐>q)  >  0)  
107                  {  
108                          ComputeGCD(t,  var-­‐>e,  var-­‐>phi);  
109                          if  (mpz_cmp_ui(t,  1)  ==  0)  
110                                  eFound  =  true;  
111                          else  
112                                  mpz_sub_ui(var-­‐>e,  var-­‐>e,  1);  
113                  }  
114          }while  (!eFound);  
115          gmp_printf("\n\n\t'e'  such  that  GCD(e,phi)=1  is:%Zd\n\n",  var-­‐>e);  
116          mpz_clears(t,  random,  NULL);  
117  }  
118  
119  void  ComputeGCD(mpz_t  r,  mpz_t  e,  mpz_t  phi)  
120  {  
121          mpz_t  m,  n;  
122          mpz_inits(m,  n,  NULL);  
123          mpz_set(m,  e);  
124          mpz_set(n,  phi);  
125          if  (mpz_cmp(m,  n)  >  0)  
126                  mpz_set(r,  n);  
127          else  
128          {  

78  
129                  mpz_set(r,  m);  
130                  mpz_set(m,  n);  
131                  mpz_set(n,  r);  
132          }  
133          while  (mpz_cmp_ui(r,  0)  !=  0)  
134          {  
135                  mpz_fdiv_r(r,  m,  n);  
136                  mpz_set(m,  n);  
137                  mpz_set(n,  r);  
138          }  
139          mpz_set(r,  m);  
140          mpz_clears(m,  n,  NULL);  
141  }  
142  
143  void  ComputeD(struct  keysRsa*  var)  
144  {  
145          mpz_t  t,  nt,  r,  nr,  q,  tmp,  a,  b;  
146          mpz_inits(t,  nt,  r,  nr,  q,  tmp,  a,  b,  NULL);  
147          mpz_set(a,  var-­‐>e);  
148          mpz_set(b,  var-­‐>phi);  
149          if  (mpz_cmp_ui(b,  0)  <  0)  
150                  mpz_ui_sub(b,  0,  b);  
151          if  (mpz_cmp_ui(a,  0)  <  0)  
152          {  
153                  mpz_ui_sub(a,  0,  a);  
154                  mpz_fdiv_r(a,  a,  b);  
155                  mpz_sub(a,  b,  a);  
156          }  
157          mpz_set_ui(t,  0);  
158          mpz_set_ui(nt,  1);  
159          mpz_set(r,  b);  
160          mpz_fdiv_r(nr,  a,  b);  
161          while  (mpz_cmp_ui(nr  ,  0)  !=  0)  
162          {  
163                  mpz_fdiv_q(q,  r,  nr);  
164                  mpz_set(tmp,  nt);  
165                  mpz_mul(nt,  q,  nt);  
166                  mpz_sub(nt,  t,  nt);  
167                  mpz_set(t,  tmp);  
168                  mpz_set(tmp,  nr);  
169                  mpz_mul(nr,  q,  nr);  
170                  mpz_sub(nr,  r,  nr);  
171                  mpz_set(r,  tmp);  
172          }  
173          assert(mpz_cmp_ui(r  ,  1)  <=  0);  /*  No  inverse  */  
174          if  (mpz_cmp_ui(t  ,  0)  <  0)  
175                  mpz_add(t,  t,  b);  
176          mpz_set(var-­‐>d,  t);  
177          mpz_clears(t,  nt,  r,  nr,  q,  tmp,  a,  b,  NULL);  
178          gmp_printf("  \t'd'  such  that  (e*d=  1  mod  phi)  is:%Zd\n\n  ",  var-­‐
>d);  
179  }  
180  
181  bool  SendPublicKey(int  socket,  char*  ipAddress,  int  udpPort  ,  struct  
keysRsa*  pubK)  
182  {  
183          int  count  =  0;  

79  
184          char  *public_key[2];  
185          public_key[0]  =  mpz_get_str(NULL,  10,  pubK-­‐>e);  
186          public_key[1]  =  mpz_get_str(NULL,  10,  pubK-­‐>n);  
187          printf("\nSharing  the  public  key...Sending  'e'  and  'n'...\n\n");  
188          do  
189          {  
190                  if  (!udp_send(socket,  public_key[count],  ipAddress,  udpPort))  
191                  {  
192                          printf("Unable  to  send  the  public  key...\n\n");  
193                          return  false;  
194                  }  
195                  sleep(1);  
196                  count++;  
197          }while  (count  <  2);  
198          printf("Data  sent!\n\nWaiting...\n\n");  
199          return  true;  
200  }  
201  
202  char*  RsaEncrypt(char*  toEncryptStr,  struct  publicKeyRsa*  pbk)  
203  {  
204          mpz_t  encrypted;  
205          char  *encryptedStr;  
206          mpz_init(encrypted);  
207          mpz_set_str(encrypted,  toEncryptStr,  10);  
208          mpz_powm(encrypted,  encrypted,  pbk-­‐>e,  pbk-­‐>n);  
209          encryptedStr  =  malloc((mpz_sizeinbase(encrypted,  10)  +  
1)*sizeof(char));  
210          encryptedStr  =  mpz_get_str(NULL,  10,  encrypted);  
211          mpz_clear(encrypted);  
212          return  encryptedStr;  
213  }  
214  
215  char*  RsaDecrypt(char*  toDecrypt,  struct  keysRsa*  key)  
216  {  
217          mpz_t  decrypted;  
218          char  *decryptedStr;  
219          mpz_init(decrypted);  
220          mpz_set_str(decrypted,  toDecrypt,  10);  
221          mpz_powm(decrypted,  decrypted,  key-­‐>d,  key-­‐>n);  
222          decryptedStr  =  mpz_get_str(NULL,  10,  decrypted);  
223          mpz_clear(decrypted);  
224          return  decryptedStr;  
225  }  
226  
227  int  rdtsc()  
228  {  
229          __asm__  __volatile__("rdtsc");  
230  }  
231  
232  void  error_handler(char  *message)  
233  {  
234          printf("fatal  error:  %s\n",  message);  
235          return;  
236  }  

80  
RsaDataLib.h

1      #ifndef  RSADATALIB_HPP  
2      #define  RSADATALIB_HPP  
3  
4      #include  "RsaLib.h"  
5  
6      void  ReceiveAndDecryptData(struct  keysRsa*  privateKey,  int  socket);  
7      char*  ReadFromFile();  
8      char*  ReadMessage();  
9      void  SendData(char*  data,  struct  publicKeyRsa*  pbk,  int  socket);  
10    bool  ReadAndSendData(struct  publicKeyRsa*  rk,  int  socket);  
11    char*  ResizeDinamicString(char*  toResize,  int  newDim);  
12    void  RebuildData(char*  decryptedStr);  
13    #endif  

RsaDataLib.c

1      #include  "RsaDataLib.h"  
2  
3      void  ReceiveAndDecryptData(struct  keysRsa*  privateKey,  int  socket)  
4      {  
5              bool  exit  =  false;  
6              int  messLen  =  0;  
7              char  buffer[BUFSIZ  +  1];  
8              char  *messageDecript  =  NULL,  *decrypted;  
9              while  (!exit)  
10            {  
11                    udp_receive(socket,  buffer);  
12                    printf("\nEncrypted  data  received(in  decimal):\n%s\n\n",  
buffer);  
13                    decrypted  =  RsaDecrypt(buffer,  privateKey);  
14                    RebuildData(decrypted);  
15                    printf("Received  data  decrypted:\n%s\n\n",  decrypted);  
16                    messageDecript  =  ResizeDinamicString(messageDecript,  messLen  +  
strlen(decrypted));  
17                    strcat(messageDecript,  decrypted);  
18                    messLen  =  strlen(messageDecript);  
19                    if  (strstr(messageDecript,  "EOTS~EOTS~EOTS")  !=  NULL)  
20                    {  
21                            printf("\n\nExit...\n\n");  
22                            exit  =  true;  
23                    }  
24                    else  if  (strstr(messageDecript,  "EOF~EOF~EOF")  !=  NULL)  
25                    {  
26                            messageDecript[strlen(messageDecript)  -­‐  11]  =  '\0';  
27                            printf("\n\nDecripted  data:\n\n%s\n\n",  messageDecript);  
28                            messageDecript[0]  =  '\0';  
29                            printf("\nWaiting  for  new  data...\n\n");  
30                    }  
31                    if  (decrypted  !=  NULL)  
32                            free(decrypted);  
33                    decrypted  =  NULL;  
34            }  
35            if  (messageDecript  !=  NULL)  

81  
36                    free(messageDecript);  
37            messageDecript  =  NULL;  
38    }  
39  
40    void  RebuildData(char*  decryptedStr)  
41    {  
42            char  ch[4],  *toReturn;  
43            int  r,  len,  i  =  0,  j  =  0,  k;  
44            len  =  strlen(decryptedStr);  
45            r  =  len  %  3;  
46            if  (r  !=0)  
47            {  
48                    i  =  3  -­‐  r;  
49                    for  (k  =  0;  k    <  i;  k  ++)  
50                            ch[k]  =  '0';  
51            }  
52            printf("Received  data  decrypted(in  decimal):\n%s\n\n",  
decryptedStr);  
53            len  =  strlen(decryptedStr);  
54            toReturn  =  malloc(len*sizeof(char));  
55            k  =  0;  
56            while  (k  <  len)  
57            {  
58                    ch[i]  =  decryptedStr[k];  
59                    k++,  i++;  
60                    if  (i  ==  3  ||  k  ==  len)  
61                    {  
62                            ch[3]  =  '\0';  
63                            char  c  =  (char)(atoi(ch));  
64                            toReturn[j]  =  c;  
65                            j++,  i  =  0;  
66                    }  
67            }  
68            toReturn[j]  =  '\0';  
69            strcpy(decryptedStr,  toReturn);  
70    }  
71  
72    bool  ReadAndSendData(struct  publicKeyRsa*  rk,  int  socket)  
73    {  
74            char*  message;  
75            int  choice;  
76            printf("\n\nWhat  do  you  want  to  do?\n\n");  
77            printf("1)Send  a  message\n");  
78            printf("2)Send  data  from  file\n");  
79            printf("3)Exit\n");  
80            do  
81            {  
82                    printf("Choose  an  option:");  
83                    scanf("%d",  &choice);  
84                    switch  (choice)  
85                    {  
86                            case  1:  
87                            {  
88                                    message  =  ReadMessage();  
89                                    SendData(message,  rk,  socket);  
90                                    free(message);  
91                                    message  =  NULL;  

82  
92                                    break;  
93                            }  
94                            case  2:  
95                            {  
96                                    message  =  ReadFromFile();  
97                                    SendData(message,  rk,  socket);  
98                                    free(message);  
99                                    message  =  NULL;  
100                                  break;  
101                          }  
102                          case  3:  
103                                  SendData("EOTS~EOTS~EOTS",  rk,  socket);  
104                                  break;  
105                          default:  
106                                  printf("\nInvalid  choice!\n\n");  
107                  }  
108          }while  (choice  !=  1  &&  choice  !=  2  &&  choice  !=  3);  
109          return  choice  ==  3;  
110  }  
111  
112  char*  ReadMessage()  
113  {  
114          char  *message  =  NULL;  
115          char  buffer[BUFSIZ  +  1];  
116          int  messLen  =  0;  
117          printf("\nEnter  the  message  to  send:  ");  
118          getchar();  
119          do  
120          {  
121                  fgets(buffer,  BUFSIZ,  stdin);  
122                  message  =  ResizeDinamicString(message,  messLen  +  
strlen(buffer));  
123                  strcat(message,  buffer);  
124                  messLen  =  strlen(message);  
125          }while  (buffer[strlen(buffer)  -­‐  1]  !=  '\n');  
126          message[messLen  -­‐  1]=  '\0';  
127          message  =  ResizeDinamicString(message,  strlen(message)  +  11);  
128          strcat(message,  "EOF~EOF~EOF");  
129          return  message;  
130  }  
131  
132  char*  ReadFromFile()  
133  {  
134          FILE*  source;  
135          char  fileName[1024],  buffer[BUFSIZ  +  1];  
136          char  *fileData  =  NULL;  
137          int  dataLen  =  0;  
138          do  
139          {  
140                  printf("\nInsert  file  name  with  extension:  ");  
141                  scanf("%s",  fileName);  
142                  if  ((source  =  fopen(fileName,  "r"))  ==  NULL)  
143                          printf("\nCannot  read  the  file...Try  again\n");  
144          }while  (source  ==  NULL);  
145  
146          while  (!feof(source))  
147          {  

83  
148                  fgets(buffer,  BUFSIZ,  source);  
149                  fileData  =  ResizeDinamicString(fileData,  dataLen  +  
strlen(buffer));  
150                  strcat(fileData,  buffer);  
151                  dataLen  =  strlen(fileData);  
152          }  
153          fileData[strlen(fileData)]=  '\0';  
154          fileData  =  ResizeDinamicString(fileData,  strlen(fileData)  +  11);  
155          strcat(fileData,  "EOF~EOF~EOF");  
156          fclose(source);  
157          return  fileData;  
158  }  
159  
160  void  SendData(char*  data,  struct  publicKeyRsa*  pbk,  int  socket)  
161  {  
162          int  i  =  0,  ii  =  0;  
163          int  nSize  =  mpz_sizeinbase(pbk-­‐>n,  10);  
164          char  toEncrypt[(nSize  *  4)  +  1  ],  *encryptedStr;  
165          char  toSend[nSize  +  1];  
166          mpz_t  encrypted;  
167          char  ch[4];  
168          mpz_init(encrypted);  
169          toEncrypt[0]  =  '\0';  
170          while  (i  <=  strlen(data))  
171          {  
172                  sprintf(ch,  "%.3d",  (unsigned  char)data[i]);  
173                  strcat(toEncrypt,  ch);  
174                  mpz_set_str(encrypted,  toEncrypt,  10);  
175                  toSend[ii]  =  data[i];  
176                  if  (mpz_cmp(encrypted,  pbk-­‐>n)  >=  0  ||  i  ==  strlen(data))  
177                  {  
178                          toSend[ii]  =  '\0';  
179                          printf("\n\nData  to  send:\n%s",  toSend);  
180                          toEncrypt[strlen(toEncrypt)  -­‐  3]  =  '\0';  
181                          printf("\n\nData  to  send(in  decimal):\n%s\n\n",  
toEncrypt);  
182                          encryptedStr  =  RsaEncrypt(toEncrypt,  pbk);  
183                          printf("Encrypted  data  to  send(in  decimal):\n%s\n\n",  
encryptedStr);  
184                          udp_reply(socket,  encryptedStr);  
185                          ii=0;  
186                          toSend[ii]  =  data[i];  
187                          strcpy(toEncrypt,  ch);  
188                          if  (encryptedStr  !=  NULL)  
189                                  free(encryptedStr);  
190                          encryptedStr  =  NULL;  
191                          sleep(1);  
192                  }  
193                  ii++;  
194                  i++;  
195          }  
196          mpz_clear(encrypted);  
197  }  
198  
199  char*  ResizeDinamicString(char*  toResize,  int  newDim)  
200  {  
201          char*resized  =  malloc((newDim  +  1)*sizeof(char));  

84  
202          if  (resized  ==  NULL)  
203                  printf("\n\nFailed  to  realloc.\n\n");  
204          resized[0]  =  '\0';  
205          if  (toResize  !=  NULL)  
206          {  
207                  strcpy(resized,  toResize);  
208                  free(toResize);  
209                  toResize  =  NULL;  
210          }  
211          return  resized;  
212  }  

Rsa_s.c

1      #include  "RsaLib.h"  
2      #include  "RsaDataLib.h"  
3  
4      int  main(int  argc,  char  *argv[])  
5      {  
6              struct  publicKeyRsa  srv;  
7              int  sk,  udpPort;  
8              bool  done;  
9              char  serverIpAdrress[1024];  
10            mpz_inits(srv.e,  srv.n,  NULL);  
11            if  (argc  ==  1)  
12            {  
13                    printf("Server  ip  address:  ");  
14                    scanf("%s",  serverIpAdrress);  
15                    printf("Udp  port:");  
16                    scanf("%d",  &udpPort);  
17            }  
18            else  if  (argc  ==  3)  
19            {  
20                    strcpy(serverIpAdrress,  argv[1]);  
21                    udpPort  =  atoi(argv[2]);  
22            }  
23            else  
24            {  
25                    printf("Required  arguments:  server  ip  address,  udp  port\n");  
26                    exit(1);  
27            }  
28            if  ((sk  =  create_udp_server(serverIpAdrress,  udpPort))  <  0)  
29            {  
30                    printf("Cannot  open  server  socket\n");  
31                    exit(1);  
32            }  
33            if  (!ReceivePublicKey(&srv,sk))  
34                    exit(1);  
35            do  
36            {  
37                    done  =  ReadAndSendData(&srv,  sk);  
38            }while  (!done);  
39            printf("\n\nExit...\n\n");  
40            close_udp_socket(sk);  
41            mpz_clears(srv.e,  srv.n,  NULL);  
42            return  EXIT_SUCCESS;  

85  
43    }  

Rsa_c.c

1      #include  "RsaLib.h"  
2      #include  "RsaDataLib.h"  
3  
4      int  main(int  argc,  char  *argv[])  
5      {  
6              struct  keysRsa  clnt;  
7              int  sk;  
8              int  udpPort;  
9              char  serverIpAdrress[1024];  
10            mpz_inits(clnt.p,  clnt.q,  clnt.n,  clnt.phi,  clnt.e,  clnt.d,  NULL);  
11            if  (argc  ==  1)  
12            {  
13                    printf("Server  ip  address:  ");  
14                    scanf("%s",  serverIpAdrress);  
15                    printf("Udp  port:");  
16                    scanf("%d",  &udpPort);  
17            }  
18            else  if  (argc  ==  3)  
19            {  
20                    strcpy(serverIpAdrress,argv[1]);  
21                    udpPort  =  atoi(argv[2]);  
22            }  
23            else  
24            {  
25                    printf("Required  arguments:  server  ip  address,  udp  port\n");  
26                    exit(1);  
27            }  
28            if  ((sk  =  create_udp_client())  <  0)  
29            {  
30                    printf("Cannot  open  client  socket\n");  
31                    exit(1);  
32            }  
33            GenerateEncryptionKeys(&clnt);  
34            if  (!SendPublicKey(sk,  serverIpAdrress,  udpPort,  &clnt))  
35                    exit(1);  
36            ReceiveAndDecryptData(&clnt,  sk);  
37            close_udp_socket(sk);  
38            mpz_clears(clnt.p,  clnt.q,  clnt.n,  clnt.phi,  clnt.e,  clnt.d,  
NULL);  
39            return  EXIT_SUCCESS;  
40    }  

86