/* 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 # include # include # include # 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 # include # define SOCKLEN_T int # define CLOSESOCKET closesocket #endif #include #include #include #include #include #include #include #include #include #include #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: "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 "kyber512: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 \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; }