457 lines
15 KiB
C
457 lines
15 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-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"
|
|
- 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:kyber768:frodo976aes:kyber1024"
|
|
|
|
#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;
|
|
}
|