MastersThesis/PQ_PROJECT_SSL_TLS/CLIENT_SERVER_SECURE/CLIENT/client.c
2024-05-07 10:46:35 +02:00

389 lines
13 KiB
C

/*
JS 2024-05-07 doplnene priklady nastavenia premennej DEFAULT_GROUPS, doplnene info o ML-KEM
*/
/*
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-05-18 - Uprava funkcie printHeader()
-> Zmazanie SSL metody
-> Upraveny vypis ako spustit program
- Uprava funkcie initSSLContext() aby realizovala fixne iba TLS spojenie
-> pridanie argumentov do funkcie na nacitanie client.key, client.pem
- Uprava hlavnej funkcie main() na zaklade predoslej upravy (nastavenie fixne TLS)
-> Zmazanie premennej ctxMethod
-> Uprava argumentov [argv] - zmazanie TLS metody, pridanie suborov s klucmi
-> Pridanie premennych na nacitanie klucov: client_key, client_pem
(vid. komentare MJ)
*/
/*
MD 2021-03-29 testovane s aktualne najnovsou verziou OpenSSL v 1.1.1k
MD 2018-11-06 upravene pre linkovanie s OpenSSL 1.1.1
(pouzita metoda TLS_client_method)
MD 2018-11-02 vlozene upravy na odstraneie varovania a autentizaciu servera
vlozene upravy na realizaciu autentizacie klienta (nacitanie potrebnych certifikatov
a kluca)
odstranenie varovania o poradi hlavickovych suborov
(vid. komentare // MD)
*/
/** SSL/TLS Client
* SSL/TLS client demonstration. This source code is cross-plateforme Windows and Linux.
*/
// __unix__ is usually defined by compilers targeting Unix systems
#ifdef __unix__
# include <unistd.h>
# include <sys/socket.h>
# include <resolv.h>
# include <netdb.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 <malloc.h>
#include <string.h>
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.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"
- PQ algoritmy: "mlkem512:mlkem768:mlkem1024:kyber512:kyber768:kyber1024:bikel1:bikel3:bikel5:hqc128:hqc192:hqc256:frodo640aes:frodo640shake:frodo976aes:frodo976shake:frodo1344aes:frodo1344shake"
- 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)
*/
#define DEFAULT_GROUPS "mlkem512:hqc128:X25519:kyber768"
#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 <hostname> <port> <client_private_key> <client_public_key>\n", bin);
return;
}
/** JS update navratove hodnoty pri chybe
* makeClientSocket function who create a traditionnal client socket to the hostname throught the port.
* @param char* hostname : the target to connect to
* @param int port : the port to connect throught
* @return int socket ; the socket number created
*/
int makeClientSocket(const char *hostname, int port){
int sock;
struct hostent *host;
struct sockaddr_in addr;
#ifdef _WIN32
WSAStartup(MAKEWORD(2,0),&wsa);
#endif
if((host = gethostbyname(hostname)) == NULL ){
perror(hostname);
return -1;
}
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 = *(long*)(host->h_addr);
if(connect(sock, (struct sockaddr*)&addr, sizeof(addr)) != 0){
CLOSESOCKET(sock);
perror(hostname);
return -1;
}
return sock;
}
/** JS update
* initSSLContext function who initialize the SSL/TLS engine with right method/protocol
* @param client_key name of file where is stored private key of client
* @param client_pem name of file where is stored public key of client
* @return SSL_CTX *ctx ; a pointer to the SSL context created
*/
SSL_CTX* initSSLContext(char* client_key, char* client_pem){
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_client_method();
// create new context from selected method
ctx = SSL_CTX_new(method);
if(ctx == NULL){
ERR_print_errors_fp(stderr);
abort();
}
// MD zabezpecienie overenia certifikatu servera pomocou CA
if (SSL_CTX_load_verify_locations(ctx, "myCA.pem", 0)) {
printf("CA certificate loaded\n");
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
}
else {
printf("\nCA certificate not loaded! Abort ...\n");
abort();
}
// MD nacitanie dat pre autentizaciu klineta
// MJ Update [Define Macro]
#ifdef AUTHENTICATION
int res = 0;
res = SSL_CTX_use_certificate_file(ctx, client_pem, SSL_FILETYPE_PEM);
if (res <= 0) {
// handle error
printf("\nCLIENT certificate not loaded! Abort ...\n");
abort();
}
res = SSL_CTX_use_PrivateKey_file(ctx, client_key, SSL_FILETYPE_PEM);
if (res <= 0) {
// handle error
printf("\nCLIENT key not loaded! Abort ...\n");
abort();
}
/* verify private key */
if ( !SSL_CTX_check_private_key(ctx) )
{
fprintf(stderr, "Private key does not match the public certificate\n");
abort();
}
printf("Certificate attached\n");
printf("\n");
#endif
return ctx;
}
/**
* showCerts function who catch and print out certificat's data from the server
* @param SSL* ssl : the SSL/TLS connection
*/
void showCerts(SSL* ssl){
X509 *cert;
char *subject, *issuer;
// get the server's certificate
cert = SSL_get_peer_certificate(ssl);
if(cert != NULL){
// JS get server's certificate algorithm name
int nid;
SSL_get_peer_signature_type_nid(ssl, &nid);
printf("Server signature algorithm: %s\n", OBJ_nid2sn(nid));
// get certificat's subject
subject = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
// get certificat's issuer
issuer = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
printf("[+] Server certificates :\n");
printf("\tSubject: %s\n", subject);
printf("\tIssuer: %s\n", issuer);
// Free memory
free(subject);
free(issuer);
X509_free(cert);
// check certificat's trust
if(SSL_get_verify_result(ssl) == X509_V_OK)
printf("[+] Server certificates X509 is trust!\n");
else
printf("[-] Server certificates X509 is not trust...\n");
}
else
printf("[-] No server's certificates\n");
return;
}
/**
* main function who coordinate the socket and SSL connection creation, then receive and emit
data to and from the server.
*/
int main(int argc, char **argv){
int sock, bytes, port;
SSL_CTX *ctx;
SSL *ssl;
char buf[1024];
char *hostname;
if(argc != 5){
printHeader(argv[0]);
exit(0);
}
// MJ Add keys for communication
char *client_key = argv[3];
char *client_pem = argv[4];
hostname = argv[1];
// Assign correct port number
port = (atoi(argv[2]) > 0 && atoi(argv[2]) < 65535) ? atoi(argv[2]) : DEFAULT_PORT;
// JS update
// Load default and OQS providers
// Default provider must be loaded before OQS provider
// Providers have to loaded before SSL/TLS engine initSSLContext()
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(client_key, client_pem);
// make a classic socket to the hostname throught the port
sock = makeClientSocket(hostname, port);
// create new SSL connection state
ssl = SSL_new(ctx);
// 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);
SSL_free(ssl);
SSL_CTX_free(ctx);
OSSL_PROVIDER_unload(provider);
OSSL_PROVIDER_unload(custom_provider);
exit(0);
}
// attach the socket descriptor
SSL_set_fd(ssl, sock);
// make the SSL connection
if(SSL_connect(ssl) == -1)
ERR_print_errors_fp(stderr);
else{
// 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)));
/*
if the server suddenly wants a new handshake,
OpenSSL handles it in the background. Without this
option, any read or write operation will return an
error if the server wants a new handshake.
*/
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
char msg[] = "ClientName";
printf("[+] Cipher used : %s\n", SSL_get_cipher(ssl));
// Show certificats data
showCerts(ssl);
// encrypt and send message
SSL_write(ssl, msg, strlen(msg));
// get response and decrypt content
bytes = SSL_read(ssl, buf, sizeof(buf));
buf[bytes] = 0;
printf("[+] Server data received : %s\n", buf);
// release SSL connection state
SSL_shutdown(ssl);
SSL_free(ssl);
}
// 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;
}