MastersThesis/PQ_PROJECT_SSL_TLS/CLIENT_SERVER_SECURE/SERVER/server.c

458 lines
15 KiB
C
Raw Permalink Normal View History

2024-05-07 08:46:35 +00:00
/*
2024-05-07 09:17:47 +00:00
JS 2024-05-07 - doplnene priklady nastavenia premennej DEFAULT_GROUPS, doplnene info o ML-KEM
- testovane s WinLibs 13.2.0 release 8
2024-05-07 08:46:35 +00:00
*/
2024-04-14 09:18:33 +00:00
/*
JS 2024-04-11 testovane s OpenSSL 3.3.0, liboqs 0.10.0 a oqs-provider 0.6.0
*/
/*
JS 2024-04-11 testovane s OpenSSL 3.3.0, liboqs 0.10.0 a oqs-provider 0.5.3
*/
/*
JS 2024-03-04 - doplnenie funkcii pre uvolnenie nacitanych providerov
*/
/*
JS 2024-02-24 - funkcia initSSLContext() rozsirena o nacitanie OQS-providera
pre plne funkcne PQ algoritmy na kazdej platforme
- doplnene priklady nastavenia premennej DEFAULT_GROUPS
- testovane s oqsprovider 0.5.3
*/
/*
JS 2024-02-18 - testovanie PQ algoritmov s pouzitim oqs-providera
- uprava vypisu o pripojeni klienta
- pridane vypisy s informaciami o pouzivanych algoritmoch
pre KEX a certifikaty
- oprava kontroly navratovej hodnoty funkcii
SSL_CTX_use_certificate_file() a SSL_CTX_use_PrivateKey_file
*/
/*
JS 2024-02-08 testovane s aktualnou najnovsou verziou OpenSSL 3.2.1
*/
/*
MJ 2023-18-05 - Uprava funkcie printHeader()
-> Zmazanie SSL metody
-> Upraveny vypis ako spustit program
- Zmazanie funkcii spojene s generovanim certifikatov
-> callbackGeneratingKey()
-> makekCert()
- Uprava funkcie loadCertificates() aby nacitavala kluce klienta a servera zo suboru
- Uprava funkcie initSSLContext() aby realizovala fixne iba TLS spojenie
- Uprava hlavnej funkcie main() na zaklade predoslej upravy (nastavenie fixne TLS)
-> Zmazanie premennej ctxMethod
-> Uprava argumentov [argv] - zmazanie TLS metody, pridanie suborov s klucmi
-> zmena nazvoslovia premennych na ulozenie klucov
MJ 2022-12-28 - Oprava Warningu vo funckii routine() vid tag MJ (vid. komentare MJ)
*/
/*
MD 2021-03-29 testovane s aktualne najnovsou verziou OpenSSL v 1.1.1k
MD 2018-11-07 Upravene pre linkovanie s OpenSSL v. 1.1.1
(pouzitie novej funkcie TLS_server_method)
MD 2018-11-02 Odstanene varovanie
dodane nacitanie CA certifikatu
dodany kod na realizaciu autentizacie klienta
(vid //MD)
*/
/** SSL/TLS Server
* SSL/TLS server demonstration. This source code is cross-plateforme Windows and Linux.
* Compile under Linux with : g++ main.cpp -Wall -lssl -lcrypto -o main
* Certificat and private key to protect transaction can be used from :
* - External(s) file(s), created with command : openssl req -x509 -nodes -newkey rsa:2048 -keyout server.pem -out server.pem
* - Internal uniq hardcoded certificat and private key, equal into each server instance
* - Randomly generated certificat and private key, best solution to used dynamic keying material at each server lauching.
*/
// __unix__ is usually defined by compilers targeting Unix systems
#ifdef __unix__
# include <unistd.h>
# include <sys/socket.h>
# include <arpa/inet.h>
# include <resolv.h>
# define SOCKLEN_T socklen_t
# define CLOSESOCKET close
// _Win32 is usually defined by compilers targeting 32 or 64 bit Windows systems
#elif defined _WIN32
// MD, odstrani varovanie o potrebe prehodit poradie hlavickovych suborov
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# include <winsock2.h>
# define SOCKLEN_T int
# define CLOSESOCKET closesocket
#endif
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <malloc.h>
#include <string.h>
#include <openssl/crypto.h>
#include <openssl/x509v3.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/bio.h>
#include <openssl/provider.h>
#ifdef _WIN32
WSADATA wsa; // Winsock data
#endif
/* JS
Premenna na definovanie "exchange groups", algoritmov na vymenu klucov
Server moze podporovat viacero KEX/KEM algoritmov, v premennej musi byt kazdy
algoritmus oddeleny dvojbodkou, napr. "kyber512:bikel1"
Podporovane algoritmy je mozne ziskat prikazom "openssl list -kem-algorithms"
Prazdna premenna alebo naplnena neplatnymi protokolmi sposobi prerusenie aplikacie
Ak server a klient nenajdu spolocny KEX/KEM protokol, tak
SSL vyhodi chybu: "SSL routines:final_key_share:no suitable key share"
Priklad pouzitia:
- klasicke algoritmy: "x25519:x448:prime256v1:secp521r1:secp384r1:ffdhe2048:ffdhe3072"
2024-05-07 08:46:35 +00:00
- PQ algoritmy: "mlkem512:mlkem768:mlkem1024:kyber512:kyber768:kyber1024:bikel1:bikel3:bikel5:hqc128:hqc192:hqc256:frodo640aes:frodo640shake:frodo976aes:frodo976shake:frodo1344aes:frodo1344shake"
2024-04-14 09:18:33 +00:00
- hybrid algoritmy: "x25519_kyber768:x25519_frodo640aes:x25519_hqc128:x448_bikel3:x448_kyber768:p256_kyber768"
Podporovane algoritmy OQS-providerom: https://github.com/open-quantum-safe/oqs-provider/blob/0.6.0/ALGORITHMS.md
POZOR - BIKE protokol nefunguje na Windows platforme (liboqs 0.10.0, oqs-provider 0.6.0)
*/
2024-05-07 08:46:35 +00:00
#define DEFAULT_GROUPS "mlkem512:kyber768:frodo976aes:kyber1024"
2024-04-14 09:18:33 +00:00
#define DEFAULT_PORT 443
/**
* printUsage function who describe the utilisation of this script.
* @param char* bin : the name of the current binary.
*/
void printHeader(char* bin){
// JS update
printf("[?] Usage : %s <port> <server_public_key> <server_private_key>]\n", bin);
return;
}
/**
* makeServerSocket function who create a traditionnal server socket, bind it and listen to it.
* @param int port : the port to listen
* @return int socket : the socket number created
*/
int makeServerSocket(int port){
int sock;
struct sockaddr_in addr;
#ifdef _WIN32
WSAStartup(MAKEWORD(2,0),&wsa);
#endif
sock = socket(PF_INET, SOCK_STREAM, 0);
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
if(bind(sock, (struct sockaddr*)&addr, sizeof(addr)) != 0){
perror("[-] Can't bind port on indicated port...");
abort();
}
if(listen(sock, 10) != 0){
perror("[-] Can't listening on indicated port...");
abort();
}
printf("\n");
printf("[+] Server listening on the %d port...\n", port);
printf("[+] Waiting for connection\n");
printf("\n");
return sock;
}
/** JS update
* initSSLContext function who initialize the SSL/TLS engine with right method/protocol
* SSL/TLS engine provided by PQ algorithms functions of OQS provider
* @return SSL_CTX *ctx : a pointer to the SSL context created
*/
SSL_CTX* initSSLContext(){
const SSL_METHOD *method;
SSL_CTX *ctx;
// initialize the SSL library
SSL_library_init();
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
// MJ only TLS connection
method = TLS_server_method();
//printf("[+] Use TLS server method.\n");
// create new context from selected method
ctx = SSL_CTX_new(method);
if(ctx == NULL){
ERR_print_errors_fp(stderr);
abort();
}
return ctx;
}
/**
* loadCertificates function who load private key and certificat from files.
* 3 mecanisms available :
* - loading certificate and private key from file(s)
* - use embed hardcoded certificate and private key in the PEM format
* - generate random and dynamic certificate and private key at each server's launch instance.
* @param SSL_CTX* ctx : the SSL/TLS context
* @param char *server_pem : filename of the PEM certificat
* @param char *server_key : filename of the PEM private key
*/
void loadCertificates(SSL_CTX* ctx, const char* server_pem, const char* server_key){
if (SSL_CTX_use_certificate_file(ctx, server_pem, SSL_FILETYPE_PEM) != 1 ||
// namiesto MD SSL_CTX_use_RSAprivateKey_file
SSL_CTX_use_PrivateKey_file(ctx, server_key, SSL_FILETYPE_PEM) != 1)
{
ERR_print_errors_fp(stderr);
abort();
}
else
printf("[*] Server's certificat and private key loaded from file.\n");
// verify private key match the public key into the certificate
if(!SSL_CTX_check_private_key(ctx)){
fprintf(stderr, "[-] Private key does not match the public certificate...\n");
abort();
} else
printf("[+] Server's private key match public certificate\n");
// JS get server's signature algorithm name
X509 *cert = SSL_CTX_get0_certificate(ctx);
int pknid;
if (X509_get_signature_info(cert, NULL, &pknid, NULL, NULL) != 1){
printf("Certificate signature algorithm: Unknown algorithm");
}
else{
printf("Certificate signature algorithm: %s\n", OBJ_nid2sn(pknid));
}
// MD kod na autentizaciu klienta
// MJ Update [Define Macro]
#ifdef AUTHENTICATION
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
// load the trusted client CA certificate into context
if (SSL_CTX_load_verify_locations(ctx, "myCA.pem", NULL) != 1)
{
fprintf(stderr, "[-] CA certificate not loaded...\n");
abort();
}
#endif
return;
}
/**
* showCerts function who catch and print out certificate's data from the client.
* @param SSL* ssl : the SSL/TLS connection
*/
void showCerts(SSL* ssl){
X509 *cert;
char *subject, *issuer;
// get the client's certificate
cert = SSL_get_peer_certificate(ssl);
if(cert != NULL){
// get certificate's subject
subject = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
// get certificate's issuer
issuer = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
printf("[+] Client certificates :\n");
printf("\tSubject: %s\n", subject);
printf("\tIssuer: %s\n", issuer);
// Free memory
free(subject);
free(issuer);
X509_free(cert);
}
else
printf("[-] No client's certificates\n");
return;
}
/**
* routine function who treat the content of data received and reply to the client.
* this function is threadable and his context sharedable.
* @param SSL* ssl : the SSL/TLS connection
*/
void routine(SSL* ssl){
/* MJ char buf[1024], reply[1024];
-> buf must be smaller than reply array because arrays are copied in sprintf function,
which have some additional string
-> buf_size + (string in sprintf)_size > reply_size = Can do overflow
-> 1024 bytes (buf) + "Enchante %s, je suis ServerName.\n" + some string(s) > 1024 bytes
(reply)
-> solution is change size of buf array
*/
char buf[1024/2], reply[1024];
int sock, bytes;
const char* echo = "Enchante %s, je suis ServerName.\n";
// accept SSL/TLS connection
if(SSL_accept(ssl) == -1)
ERR_print_errors_fp(stderr);
else{
printf("[+] Cipher used : %s\n", SSL_get_cipher(ssl));
// JS get chosen (negotiated) key exchange/encapsulation algorithm name
printf("Used group (KEM): %s\n", SSL_group_to_name(ssl, SSL_get_negotiated_group(ssl)));
// JS get client's certificate algorithm name
int nid;
SSL_get_peer_signature_type_nid(ssl, &nid);
printf("Peer signature name: %s\n", OBJ_nid2sn(nid));
// Show certificats data
showCerts(ssl);
// read data from client request
bytes = SSL_read(ssl, buf, sizeof(buf));
if(bytes > 0){
buf[bytes] = 0;
printf("[+] Client data received : %s\n", buf);
// construct response
sprintf(reply, echo, buf);
// send response
SSL_write(ssl, reply, strlen(reply));
} else {
switch(SSL_get_error(ssl, bytes)){
case SSL_ERROR_ZERO_RETURN :
printf("SSL_ERROR_ZERO_RETURN : ");
break;
case SSL_ERROR_NONE :
printf("SSL_ERROR_NONE : ");
break;
case SSL_ERROR_SSL:
printf("SSL_ERROR_SSL : ");
break;
}
ERR_print_errors_fp(stderr);
}
}
// get traditionnal socket connection from SSL connection
sock = SSL_get_fd(ssl);
// release SSL connection state
SSL_shutdown(ssl);
SSL_free(ssl);
// close socket
CLOSESOCKET(sock);
}
/**
* main function who coordinate the socket and SSL connection creation, then receive and emit
data to and from the client.
*/
int main(int argc, char **argv){
int sock, port;
SSL_CTX *ctx;
const char *server_pem, *server_key;
if(argc != 4){
printHeader(argv[0]);
exit(0);
}
port = (atoi(argv[1]) > 0 && atoi(argv[1]) < 65535) ? atoi(argv[1]) : DEFAULT_PORT;
// JS
// Load default and OQS providers
// Default provider must be loaded before OQS provider
OSSL_PROVIDER* provider;
provider = OSSL_PROVIDER_load(NULL, "default");
if (provider == NULL) {
printf("Failed to load Default provider\n");
exit(0);
}
OSSL_PROVIDER* custom_provider = OSSL_PROVIDER_load(NULL, "oqsprovider");
if (custom_provider == NULL){
printf("Failed to load OQS-provider\n");
OSSL_PROVIDER_unload(provider);
exit(0);
}
// load SSL library and dependances
ctx = initSSLContext();
// MJ Update argv parameters
server_pem = argv[2];
server_key = argv[3];
// load certificats and keys
loadCertificates(ctx, server_pem, server_key);
// make a classic server socket
sock = makeServerSocket(port);
while(1){
struct sockaddr_in addr;
SSL *ssl;
SOCKLEN_T len = sizeof(addr);
// accept connection of client
int client = accept(sock, (struct sockaddr*)&addr, &len);
printf("[+] Connection [%s:%d]\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
// get new SSL state with context
ssl = SSL_new(ctx);
if (ssl == NULL){
ERR_print_errors_fp(stderr);
break;
}
// JS set key exchange/encapsulation protocols supported by server
// Without this function, client will use default X25519 protocol
// First protocol supported by both server and client will be used for KEX/KEM
// Empty DEFAULT_GROUPS or list of unsupported protocol by server will fail communication
if (SSL_set1_groups_list(ssl, DEFAULT_GROUPS) != 1){
printf("KEX/KEM algorithms undefined - check DEFAULT_GROUPS variable\n");
ERR_print_errors_fp(stderr);
break;
}
// set traditionnal socket to SSL
SSL_set_fd(ssl, client);
// apply routine to the socket's content
routine(ssl);
// JS remove break if you want server running in loop
break;
}
// close socket
CLOSESOCKET(sock);
#ifdef _WIN32
WSACleanup();
#endif
// release SSL's context
SSL_CTX_free(ctx);
// JS Unload both providers
OSSL_PROVIDER_unload(provider);
OSSL_PROVIDER_unload(custom_provider);
return 0;
}