1*521ba2f2Sbeck /* $OpenBSD: tlstest.c,v 1.2 2023/07/02 17:21:33 beck Exp $ */
28b1f2e0eSjsing /*
38b1f2e0eSjsing * Copyright (c) 2020, 2021 Joel Sing <jsing@openbsd.org>
48b1f2e0eSjsing *
58b1f2e0eSjsing * Permission to use, copy, modify, and distribute this software for any
68b1f2e0eSjsing * purpose with or without fee is hereby granted, provided that the above
78b1f2e0eSjsing * copyright notice and this permission notice appear in all copies.
88b1f2e0eSjsing *
98b1f2e0eSjsing * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
108b1f2e0eSjsing * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
118b1f2e0eSjsing * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
128b1f2e0eSjsing * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
138b1f2e0eSjsing * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
148b1f2e0eSjsing * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
158b1f2e0eSjsing * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
168b1f2e0eSjsing */
178b1f2e0eSjsing
188b1f2e0eSjsing #include <err.h>
198b1f2e0eSjsing
208b1f2e0eSjsing #include <openssl/bio.h>
218b1f2e0eSjsing #include <openssl/err.h>
228b1f2e0eSjsing #include <openssl/ssl.h>
238b1f2e0eSjsing
248b1f2e0eSjsing const char *server_ca_file;
258b1f2e0eSjsing const char *server_cert_file;
268b1f2e0eSjsing const char *server_key_file;
278b1f2e0eSjsing
288b1f2e0eSjsing int debug = 0;
298b1f2e0eSjsing
308b1f2e0eSjsing static void
hexdump(const unsigned char * buf,size_t len)318b1f2e0eSjsing hexdump(const unsigned char *buf, size_t len)
328b1f2e0eSjsing {
338b1f2e0eSjsing size_t i;
348b1f2e0eSjsing
358b1f2e0eSjsing for (i = 1; i <= len; i++)
368b1f2e0eSjsing fprintf(stderr, " 0x%02hhx,%s", buf[i - 1], i % 8 ? "" : "\n");
378b1f2e0eSjsing
388b1f2e0eSjsing if (len % 8)
398b1f2e0eSjsing fprintf(stderr, "\n");
408b1f2e0eSjsing }
418b1f2e0eSjsing
428b1f2e0eSjsing static SSL *
tls_client(BIO * rbio,BIO * wbio)438b1f2e0eSjsing tls_client(BIO *rbio, BIO *wbio)
448b1f2e0eSjsing {
458b1f2e0eSjsing SSL_CTX *ssl_ctx = NULL;
468b1f2e0eSjsing SSL *ssl = NULL;
478b1f2e0eSjsing
488b1f2e0eSjsing if ((ssl_ctx = SSL_CTX_new(TLS_method())) == NULL)
498b1f2e0eSjsing errx(1, "client context");
508b1f2e0eSjsing
518b1f2e0eSjsing if ((ssl = SSL_new(ssl_ctx)) == NULL)
528b1f2e0eSjsing errx(1, "client ssl");
538b1f2e0eSjsing
548b1f2e0eSjsing BIO_up_ref(rbio);
558b1f2e0eSjsing BIO_up_ref(wbio);
568b1f2e0eSjsing
578b1f2e0eSjsing SSL_set_bio(ssl, rbio, wbio);
588b1f2e0eSjsing
598b1f2e0eSjsing SSL_CTX_free(ssl_ctx);
608b1f2e0eSjsing
618b1f2e0eSjsing return ssl;
628b1f2e0eSjsing }
638b1f2e0eSjsing
648b1f2e0eSjsing static SSL *
tls_server(BIO * rbio,BIO * wbio)658b1f2e0eSjsing tls_server(BIO *rbio, BIO *wbio)
668b1f2e0eSjsing {
678b1f2e0eSjsing SSL_CTX *ssl_ctx = NULL;
688b1f2e0eSjsing SSL *ssl = NULL;
698b1f2e0eSjsing
708b1f2e0eSjsing if ((ssl_ctx = SSL_CTX_new(TLS_method())) == NULL)
718b1f2e0eSjsing errx(1, "server context");
728b1f2e0eSjsing
738b1f2e0eSjsing SSL_CTX_set_dh_auto(ssl_ctx, 2);
748b1f2e0eSjsing
758b1f2e0eSjsing if (SSL_CTX_use_certificate_file(ssl_ctx, server_cert_file,
768b1f2e0eSjsing SSL_FILETYPE_PEM) != 1) {
778b1f2e0eSjsing fprintf(stderr, "FAIL: Failed to load server certificate");
788b1f2e0eSjsing goto failure;
798b1f2e0eSjsing }
808b1f2e0eSjsing if (SSL_CTX_use_PrivateKey_file(ssl_ctx, server_key_file,
818b1f2e0eSjsing SSL_FILETYPE_PEM) != 1) {
828b1f2e0eSjsing fprintf(stderr, "FAIL: Failed to load server private key");
838b1f2e0eSjsing goto failure;
848b1f2e0eSjsing }
858b1f2e0eSjsing
868b1f2e0eSjsing if ((ssl = SSL_new(ssl_ctx)) == NULL)
878b1f2e0eSjsing errx(1, "server ssl");
888b1f2e0eSjsing
898b1f2e0eSjsing BIO_up_ref(rbio);
908b1f2e0eSjsing BIO_up_ref(wbio);
918b1f2e0eSjsing
928b1f2e0eSjsing SSL_set_bio(ssl, rbio, wbio);
938b1f2e0eSjsing
948b1f2e0eSjsing failure:
958b1f2e0eSjsing SSL_CTX_free(ssl_ctx);
968b1f2e0eSjsing
978b1f2e0eSjsing return ssl;
988b1f2e0eSjsing }
998b1f2e0eSjsing
1008b1f2e0eSjsing static int
ssl_error(SSL * ssl,const char * name,const char * desc,int ssl_ret)1018b1f2e0eSjsing ssl_error(SSL *ssl, const char *name, const char *desc, int ssl_ret)
1028b1f2e0eSjsing {
1038b1f2e0eSjsing int ssl_err;
1048b1f2e0eSjsing
1058b1f2e0eSjsing ssl_err = SSL_get_error(ssl, ssl_ret);
1068b1f2e0eSjsing
1078b1f2e0eSjsing if (ssl_err == SSL_ERROR_WANT_READ) {
1088b1f2e0eSjsing return 1;
1098b1f2e0eSjsing } else if (ssl_err == SSL_ERROR_WANT_WRITE) {
1108b1f2e0eSjsing return 1;
1118b1f2e0eSjsing } else if (ssl_err == SSL_ERROR_SYSCALL && errno == 0) {
1128b1f2e0eSjsing /* Yup, this is apparently a thing... */
1138b1f2e0eSjsing } else {
1148b1f2e0eSjsing fprintf(stderr, "FAIL: %s %s failed - ssl err = %d, errno = %d\n",
1158b1f2e0eSjsing name, desc, ssl_err, errno);
1168b1f2e0eSjsing ERR_print_errors_fp(stderr);
1178b1f2e0eSjsing return 0;
1188b1f2e0eSjsing }
1198b1f2e0eSjsing
1208b1f2e0eSjsing return 1;
1218b1f2e0eSjsing }
1228b1f2e0eSjsing
1238b1f2e0eSjsing static int
do_connect(SSL * ssl,const char * name,int * done)1248b1f2e0eSjsing do_connect(SSL *ssl, const char *name, int *done)
1258b1f2e0eSjsing {
1268b1f2e0eSjsing int ssl_ret;
1278b1f2e0eSjsing
1288b1f2e0eSjsing if ((ssl_ret = SSL_connect(ssl)) == 1) {
1298b1f2e0eSjsing fprintf(stderr, "INFO: %s connect done\n", name);
1308b1f2e0eSjsing *done = 1;
1318b1f2e0eSjsing return 1;
1328b1f2e0eSjsing }
1338b1f2e0eSjsing
1348b1f2e0eSjsing return ssl_error(ssl, name, "connect", ssl_ret);
1358b1f2e0eSjsing }
1368b1f2e0eSjsing
1378b1f2e0eSjsing static int
do_accept(SSL * ssl,const char * name,int * done)1388b1f2e0eSjsing do_accept(SSL *ssl, const char *name, int *done)
1398b1f2e0eSjsing {
1408b1f2e0eSjsing int ssl_ret;
1418b1f2e0eSjsing
1428b1f2e0eSjsing if ((ssl_ret = SSL_accept(ssl)) == 1) {
1438b1f2e0eSjsing fprintf(stderr, "INFO: %s accept done\n", name);
1448b1f2e0eSjsing *done = 1;
1458b1f2e0eSjsing return 1;
1468b1f2e0eSjsing }
1478b1f2e0eSjsing
1488b1f2e0eSjsing return ssl_error(ssl, name, "accept", ssl_ret);
1498b1f2e0eSjsing }
1508b1f2e0eSjsing
1518b1f2e0eSjsing static int
do_read(SSL * ssl,const char * name,int * done)1528b1f2e0eSjsing do_read(SSL *ssl, const char *name, int *done)
1538b1f2e0eSjsing {
1548b1f2e0eSjsing uint8_t buf[512];
1558b1f2e0eSjsing int ssl_ret;
1568b1f2e0eSjsing
1578b1f2e0eSjsing if ((ssl_ret = SSL_read(ssl, buf, sizeof(buf))) > 0) {
1588b1f2e0eSjsing fprintf(stderr, "INFO: %s read done\n", name);
1598b1f2e0eSjsing if (debug > 1)
1608b1f2e0eSjsing hexdump(buf, ssl_ret);
1618b1f2e0eSjsing *done = 1;
1628b1f2e0eSjsing return 1;
1638b1f2e0eSjsing }
1648b1f2e0eSjsing
1658b1f2e0eSjsing return ssl_error(ssl, name, "read", ssl_ret);
1668b1f2e0eSjsing }
1678b1f2e0eSjsing
1688b1f2e0eSjsing static int
do_write(SSL * ssl,const char * name,int * done)1698b1f2e0eSjsing do_write(SSL *ssl, const char *name, int *done)
1708b1f2e0eSjsing {
1718b1f2e0eSjsing const uint8_t buf[] = "Hello, World!\n";
1728b1f2e0eSjsing int ssl_ret;
1738b1f2e0eSjsing
1748b1f2e0eSjsing if ((ssl_ret = SSL_write(ssl, buf, sizeof(buf))) > 0) {
1758b1f2e0eSjsing fprintf(stderr, "INFO: %s write done\n", name);
1768b1f2e0eSjsing *done = 1;
1778b1f2e0eSjsing return 1;
1788b1f2e0eSjsing }
1798b1f2e0eSjsing
1808b1f2e0eSjsing return ssl_error(ssl, name, "write", ssl_ret);
1818b1f2e0eSjsing }
1828b1f2e0eSjsing
1838b1f2e0eSjsing static int
do_shutdown(SSL * ssl,const char * name,int * done)1848b1f2e0eSjsing do_shutdown(SSL *ssl, const char *name, int *done)
1858b1f2e0eSjsing {
1868b1f2e0eSjsing int ssl_ret;
1878b1f2e0eSjsing
1888b1f2e0eSjsing ssl_ret = SSL_shutdown(ssl);
1898b1f2e0eSjsing if (ssl_ret == 1) {
1908b1f2e0eSjsing fprintf(stderr, "INFO: %s shutdown done\n", name);
1918b1f2e0eSjsing *done = 1;
1928b1f2e0eSjsing return 1;
1938b1f2e0eSjsing }
1948b1f2e0eSjsing return ssl_error(ssl, name, "shutdown", ssl_ret);
1958b1f2e0eSjsing }
1968b1f2e0eSjsing
1978b1f2e0eSjsing typedef int (*ssl_func)(SSL *ssl, const char *name, int *done);
1988b1f2e0eSjsing
1998b1f2e0eSjsing static int
do_client_server_loop(SSL * client,ssl_func client_func,SSL * server,ssl_func server_func)2008b1f2e0eSjsing do_client_server_loop(SSL *client, ssl_func client_func, SSL *server,
2018b1f2e0eSjsing ssl_func server_func)
2028b1f2e0eSjsing {
2038b1f2e0eSjsing int client_done = 0, server_done = 0;
2048b1f2e0eSjsing int i = 0;
2058b1f2e0eSjsing
2068b1f2e0eSjsing do {
2078b1f2e0eSjsing if (!client_done) {
2088b1f2e0eSjsing if (debug)
2098b1f2e0eSjsing fprintf(stderr, "DEBUG: client loop\n");
2108b1f2e0eSjsing if (!client_func(client, "client", &client_done))
2118b1f2e0eSjsing return 0;
2128b1f2e0eSjsing }
2138b1f2e0eSjsing if (!server_done) {
2148b1f2e0eSjsing if (debug)
2158b1f2e0eSjsing fprintf(stderr, "DEBUG: server loop\n");
2168b1f2e0eSjsing if (!server_func(server, "server", &server_done))
2178b1f2e0eSjsing return 0;
2188b1f2e0eSjsing }
2198b1f2e0eSjsing } while (i++ < 100 && (!client_done || !server_done));
2208b1f2e0eSjsing
2218b1f2e0eSjsing if (!client_done || !server_done)
2228b1f2e0eSjsing fprintf(stderr, "FAIL: gave up\n");
2238b1f2e0eSjsing
2248b1f2e0eSjsing return client_done && server_done;
2258b1f2e0eSjsing }
2268b1f2e0eSjsing
2278b1f2e0eSjsing struct tls_test {
2288b1f2e0eSjsing const unsigned char *desc;
2298b1f2e0eSjsing const SSL_METHOD *(*client_method)(void);
2308b1f2e0eSjsing uint16_t client_min_version;
2318b1f2e0eSjsing uint16_t client_max_version;
2328b1f2e0eSjsing const char *client_ciphers;
2338b1f2e0eSjsing const SSL_METHOD *(*server_method)(void);
2348b1f2e0eSjsing uint16_t server_min_version;
2358b1f2e0eSjsing uint16_t server_max_version;
2368b1f2e0eSjsing const char *server_ciphers;
2378b1f2e0eSjsing };
2388b1f2e0eSjsing
2398b1f2e0eSjsing static const struct tls_test tls_tests[] = {
2408b1f2e0eSjsing {
2418b1f2e0eSjsing .desc = "Default client and server",
2428b1f2e0eSjsing },
2438b1f2e0eSjsing {
2448b1f2e0eSjsing .desc = "Default client and TLSv1.2 server",
2458b1f2e0eSjsing .server_max_version = TLS1_2_VERSION,
2468b1f2e0eSjsing },
2478b1f2e0eSjsing {
2488b1f2e0eSjsing .desc = "Default client and default server with ECDHE KEX",
2498b1f2e0eSjsing .server_ciphers = "ECDHE-RSA-AES128-SHA",
2508b1f2e0eSjsing },
2518b1f2e0eSjsing {
2528b1f2e0eSjsing .desc = "Default client and TLSv1.2 server with ECDHE KEX",
2538b1f2e0eSjsing .server_max_version = TLS1_2_VERSION,
2548b1f2e0eSjsing .server_ciphers = "ECDHE-RSA-AES128-SHA",
2558b1f2e0eSjsing },
2568b1f2e0eSjsing {
2578b1f2e0eSjsing .desc = "Default client and default server with DHE KEX",
2588b1f2e0eSjsing .server_ciphers = "DHE-RSA-AES128-SHA",
2598b1f2e0eSjsing },
2608b1f2e0eSjsing {
2618b1f2e0eSjsing .desc = "Default client and TLSv1.2 server with DHE KEX",
2628b1f2e0eSjsing .server_max_version = TLS1_2_VERSION,
2638b1f2e0eSjsing .server_ciphers = "DHE-RSA-AES128-SHA",
2648b1f2e0eSjsing },
2658b1f2e0eSjsing {
2668b1f2e0eSjsing .desc = "Default client and default server with RSA KEX",
2678b1f2e0eSjsing .server_ciphers = "AES128-SHA",
2688b1f2e0eSjsing },
2698b1f2e0eSjsing {
2708b1f2e0eSjsing .desc = "Default client and TLSv1.2 server with RSA KEX",
2718b1f2e0eSjsing .server_max_version = TLS1_2_VERSION,
2728b1f2e0eSjsing .server_ciphers = "AES128-SHA",
2738b1f2e0eSjsing },
2748b1f2e0eSjsing {
2758b1f2e0eSjsing .desc = "TLSv1.2 client and default server",
2768b1f2e0eSjsing .client_max_version = TLS1_2_VERSION,
2778b1f2e0eSjsing },
2788b1f2e0eSjsing {
2798b1f2e0eSjsing .desc = "TLSv1.2 client and default server with ECDHE KEX",
2808b1f2e0eSjsing .client_max_version = TLS1_2_VERSION,
2818b1f2e0eSjsing .client_ciphers = "ECDHE-RSA-AES128-SHA",
2828b1f2e0eSjsing },
2838b1f2e0eSjsing {
2848b1f2e0eSjsing .desc = "TLSv1.2 client and default server with DHE KEX",
2858b1f2e0eSjsing .server_max_version = TLS1_2_VERSION,
2868b1f2e0eSjsing .client_ciphers = "DHE-RSA-AES128-SHA",
2878b1f2e0eSjsing },
2888b1f2e0eSjsing {
2898b1f2e0eSjsing .desc = "TLSv1.2 client and default server with RSA KEX",
2908b1f2e0eSjsing .client_max_version = TLS1_2_VERSION,
2918b1f2e0eSjsing .client_ciphers = "AES128-SHA",
2928b1f2e0eSjsing },
2938b1f2e0eSjsing };
2948b1f2e0eSjsing
2958b1f2e0eSjsing #define N_TLS_TESTS (sizeof(tls_tests) / sizeof(*tls_tests))
2968b1f2e0eSjsing
2978b1f2e0eSjsing static int
tlstest(const struct tls_test * tt)2988b1f2e0eSjsing tlstest(const struct tls_test *tt)
2998b1f2e0eSjsing {
3008b1f2e0eSjsing BIO *client_wbio = NULL, *server_wbio = NULL;
3018b1f2e0eSjsing SSL *client = NULL, *server = NULL;
3028b1f2e0eSjsing int failed = 1;
3038b1f2e0eSjsing
3048b1f2e0eSjsing fprintf(stderr, "\n== Testing %s... ==\n", tt->desc);
3058b1f2e0eSjsing
3068b1f2e0eSjsing if ((client_wbio = BIO_new(BIO_s_mem())) == NULL)
3078b1f2e0eSjsing goto failure;
3088b1f2e0eSjsing if (BIO_set_mem_eof_return(client_wbio, -1) <= 0)
3098b1f2e0eSjsing goto failure;
3108b1f2e0eSjsing
3118b1f2e0eSjsing if ((server_wbio = BIO_new(BIO_s_mem())) == NULL)
3128b1f2e0eSjsing goto failure;
3138b1f2e0eSjsing if (BIO_set_mem_eof_return(server_wbio, -1) <= 0)
3148b1f2e0eSjsing goto failure;
3158b1f2e0eSjsing
3168b1f2e0eSjsing if ((client = tls_client(server_wbio, client_wbio)) == NULL)
3178b1f2e0eSjsing goto failure;
3188b1f2e0eSjsing if (tt->client_min_version != 0) {
3198b1f2e0eSjsing if (!SSL_set_min_proto_version(client, tt->client_min_version))
3208b1f2e0eSjsing goto failure;
3218b1f2e0eSjsing }
3228b1f2e0eSjsing if (tt->client_max_version != 0) {
3238b1f2e0eSjsing if (!SSL_set_max_proto_version(client, tt->client_max_version))
3248b1f2e0eSjsing goto failure;
3258b1f2e0eSjsing }
3268b1f2e0eSjsing if (tt->client_ciphers != NULL) {
3278b1f2e0eSjsing if (!SSL_set_cipher_list(client, tt->client_ciphers))
3288b1f2e0eSjsing goto failure;
3298b1f2e0eSjsing }
3308b1f2e0eSjsing
3318b1f2e0eSjsing if ((server = tls_server(client_wbio, server_wbio)) == NULL)
3328b1f2e0eSjsing goto failure;
3338b1f2e0eSjsing if (tt->server_min_version != 0) {
3348b1f2e0eSjsing if (!SSL_set_min_proto_version(server, tt->server_min_version))
3358b1f2e0eSjsing goto failure;
3368b1f2e0eSjsing }
3378b1f2e0eSjsing if (tt->server_max_version != 0) {
3388b1f2e0eSjsing if (!SSL_set_max_proto_version(server, tt->server_max_version))
3398b1f2e0eSjsing goto failure;
3408b1f2e0eSjsing }
3418b1f2e0eSjsing if (tt->server_ciphers != NULL) {
3428b1f2e0eSjsing if (!SSL_set_cipher_list(server, tt->server_ciphers))
3438b1f2e0eSjsing goto failure;
3448b1f2e0eSjsing }
3458b1f2e0eSjsing
3468b1f2e0eSjsing if (!do_client_server_loop(client, do_connect, server, do_accept)) {
3478b1f2e0eSjsing fprintf(stderr, "FAIL: client and server handshake failed\n");
3488b1f2e0eSjsing goto failure;
3498b1f2e0eSjsing }
3508b1f2e0eSjsing
3518b1f2e0eSjsing if (!do_client_server_loop(client, do_write, server, do_read)) {
3528b1f2e0eSjsing fprintf(stderr, "FAIL: client write and server read I/O failed\n");
3538b1f2e0eSjsing goto failure;
3548b1f2e0eSjsing }
3558b1f2e0eSjsing
3568b1f2e0eSjsing if (!do_client_server_loop(client, do_read, server, do_write)) {
3578b1f2e0eSjsing fprintf(stderr, "FAIL: client read and server write I/O failed\n");
3588b1f2e0eSjsing goto failure;
3598b1f2e0eSjsing }
3608b1f2e0eSjsing
3618b1f2e0eSjsing if (!do_client_server_loop(client, do_shutdown, server, do_shutdown)) {
3628b1f2e0eSjsing fprintf(stderr, "FAIL: client and server shutdown failed\n");
3638b1f2e0eSjsing goto failure;
3648b1f2e0eSjsing }
3658b1f2e0eSjsing
3668b1f2e0eSjsing fprintf(stderr, "INFO: Done!\n");
3678b1f2e0eSjsing
3688b1f2e0eSjsing failed = 0;
3698b1f2e0eSjsing
3708b1f2e0eSjsing failure:
3718b1f2e0eSjsing BIO_free(client_wbio);
3728b1f2e0eSjsing BIO_free(server_wbio);
3738b1f2e0eSjsing
3748b1f2e0eSjsing SSL_free(client);
3758b1f2e0eSjsing SSL_free(server);
3768b1f2e0eSjsing
3778b1f2e0eSjsing return failed;
3788b1f2e0eSjsing }
3798b1f2e0eSjsing
3808b1f2e0eSjsing int
main(int argc,char ** argv)3818b1f2e0eSjsing main(int argc, char **argv)
3828b1f2e0eSjsing {
3838b1f2e0eSjsing int failed = 0;
3848b1f2e0eSjsing size_t i;
3858b1f2e0eSjsing
3868b1f2e0eSjsing if (argc != 4) {
3878b1f2e0eSjsing fprintf(stderr, "usage: %s keyfile certfile cafile\n",
3888b1f2e0eSjsing argv[0]);
3898b1f2e0eSjsing exit(1);
3908b1f2e0eSjsing }
3918b1f2e0eSjsing
3928b1f2e0eSjsing server_key_file = argv[1];
3938b1f2e0eSjsing server_cert_file = argv[2];
3948b1f2e0eSjsing server_ca_file = argv[3];
3958b1f2e0eSjsing
3968b1f2e0eSjsing for (i = 0; i < N_TLS_TESTS; i++)
3978b1f2e0eSjsing failed |= tlstest(&tls_tests[i]);
3988b1f2e0eSjsing
3998b1f2e0eSjsing return failed;
4008b1f2e0eSjsing }
401