Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Tesi di Laurea
Relatore: Laureando:
Prof. Pier Luca Montessoro Pierpaolo Basso
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
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
V
VI
Introduzione
VII
VIII
1 Linguaggio e librerie utilizzate
1
Nella realizzazione della tesi è stato necessario utilizzare altre due librerie, oltre alla
già citata GMP:
2
1.2 Installazione del compilatore e delle librerie in Windows
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.
3
Vediamo un esempio in figura 1.1:
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
client server
socket() socket()
bind()
sendto() recvfrom()
recvfrom() sendto()
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.
Da notare la chiamata alla primitiva socket (linea 6), la quale crea il socket e i cui
parametri specificano:
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.
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).
9
10
2 Algoritmo Diffie-Hellman
2.1 Descrizione
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:
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
2.1.1 Sicurezza
• a = logg A mod p
• b = logg B mod p
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:
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.
13
2.2 Scambio della chiave di cifratura: classe Dh
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
};
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()
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:
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.
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
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
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
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:
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:
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:
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]).
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:
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:
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
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()
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:
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
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
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
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 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
[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.
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