1*a75d20e9Sjsing /* $OpenBSD: ssl_get_shared_ciphers.c,v 1.13 2024/08/31 12:47:24 jsing Exp $ */ 2373185a5Stb /* 3373185a5Stb * Copyright (c) 2021 Theo Buehler <tb@openbsd.org> 4373185a5Stb * 5373185a5Stb * Permission to use, copy, modify, and distribute this software for any 6373185a5Stb * purpose with or without fee is hereby granted, provided that the above 7373185a5Stb * copyright notice and this permission notice appear in all copies. 8373185a5Stb * 9373185a5Stb * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10373185a5Stb * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11373185a5Stb * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12373185a5Stb * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13373185a5Stb * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14373185a5Stb * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15373185a5Stb * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16373185a5Stb */ 17373185a5Stb 18373185a5Stb #include <stdint.h> 19d3f60055Stb #include <stdio.h> 20d3f60055Stb #include <stdlib.h> 21d3f60055Stb #include <string.h> 22d3f60055Stb 23d3f60055Stb #include <openssl/bio.h> 24d3f60055Stb #include <openssl/crypto.h> 25551179fdStb #include <openssl/err.h> 26373185a5Stb #include <openssl/ssl.h> 27373185a5Stb 28373185a5Stb struct peer_config { 29373185a5Stb const char *name; 30373185a5Stb int server; 31373185a5Stb uint16_t max_version; 32373185a5Stb uint16_t min_version; 33373185a5Stb const char *ciphers; 34373185a5Stb }; 35373185a5Stb 36373185a5Stb struct ssl_shared_ciphers_test_data { 37373185a5Stb const char *description; 38373185a5Stb struct peer_config client_config; 39373185a5Stb struct peer_config server_config; 40373185a5Stb const char *shared_ciphers; 41373185a5Stb const char *shared_ciphers_without_aesni; 42373185a5Stb }; 43373185a5Stb 44373185a5Stb char *server_cert; 45373185a5Stb char *server_key; 46373185a5Stb 47373185a5Stb static const struct ssl_shared_ciphers_test_data ssl_shared_ciphers_tests[] = { 48373185a5Stb { 49373185a5Stb .description = "TLSv1.3 defaults", 50373185a5Stb .client_config = { 51373185a5Stb .name = "client", 52373185a5Stb .server = 0, 53373185a5Stb .max_version = TLS1_3_VERSION, 54373185a5Stb .min_version = TLS1_3_VERSION, 55373185a5Stb .ciphers = 5657e596eeStb "TLS_AES_256_GCM_SHA384:" 5757e596eeStb "TLS_CHACHA20_POLY1305_SHA256:" 5857e596eeStb "TLS_AES_128_GCM_SHA256", 59373185a5Stb }, 60373185a5Stb .server_config = { 61373185a5Stb .name = "server", 62373185a5Stb .server = 1, 63373185a5Stb .max_version = TLS1_3_VERSION, 64373185a5Stb .min_version = TLS1_3_VERSION, 65373185a5Stb .ciphers = 6657e596eeStb "TLS_AES_256_GCM_SHA384:" 6757e596eeStb "TLS_CHACHA20_POLY1305_SHA256:" 6857e596eeStb "TLS_AES_128_GCM_SHA256", 69373185a5Stb }, 70373185a5Stb .shared_ciphers = 7157e596eeStb "TLS_AES_256_GCM_SHA384:" 7257e596eeStb "TLS_CHACHA20_POLY1305_SHA256:" 7357e596eeStb "TLS_AES_128_GCM_SHA256", 74373185a5Stb }, 75373185a5Stb 76373185a5Stb { 77373185a5Stb .description = "TLSv1.3, client without ChaCha", 78373185a5Stb .client_config = { 79373185a5Stb .name = "client", 80373185a5Stb .server = 0, 81373185a5Stb .max_version = TLS1_3_VERSION, 82373185a5Stb .min_version = TLS1_3_VERSION, 83373185a5Stb .ciphers = 8457e596eeStb "TLS_AES_256_GCM_SHA384:" 8557e596eeStb "TLS_AES_128_GCM_SHA256", 86373185a5Stb }, 87373185a5Stb .server_config = { 88373185a5Stb .name = "server", 89373185a5Stb .server = 1, 90373185a5Stb .max_version = TLS1_3_VERSION, 91373185a5Stb .min_version = TLS1_3_VERSION, 92373185a5Stb .ciphers = 9357e596eeStb "TLS_AES_256_GCM_SHA384:" 9457e596eeStb "TLS_CHACHA20_POLY1305_SHA256:" 9557e596eeStb "TLS_AES_128_GCM_SHA256", 96373185a5Stb }, 97373185a5Stb .shared_ciphers = 9857e596eeStb "TLS_AES_256_GCM_SHA384:" 9957e596eeStb "TLS_AES_128_GCM_SHA256", 100373185a5Stb }, 101373185a5Stb 102373185a5Stb { 103373185a5Stb .description = "TLSv1.2", 104373185a5Stb .client_config = { 105373185a5Stb .name = "client", 106373185a5Stb .server = 0, 107373185a5Stb .max_version = TLS1_2_VERSION, 108373185a5Stb .min_version = TLS1_2_VERSION, 109373185a5Stb .ciphers = 110373185a5Stb "ECDHE-RSA-AES256-GCM-SHA384:" 111373185a5Stb "ECDHE-ECDSA-AES256-GCM-SHA384:" 112373185a5Stb "ECDHE-RSA-AES256-SHA384:" 113373185a5Stb "ECDHE-ECDSA-AES256-SHA384:" 114373185a5Stb "ECDHE-RSA-AES256-SHA:" 115373185a5Stb "ECDHE-ECDSA-AES256-SHA", 116373185a5Stb }, 117373185a5Stb .server_config = { 118373185a5Stb .name = "server", 119373185a5Stb .server = 1, 120373185a5Stb .max_version = TLS1_2_VERSION, 121373185a5Stb .min_version = TLS1_2_VERSION, 122373185a5Stb .ciphers = 123373185a5Stb "ECDHE-RSA-AES256-GCM-SHA384:" 124373185a5Stb "ECDHE-ECDSA-AES256-GCM-SHA384:" 125373185a5Stb "ECDHE-RSA-AES256-SHA384:" 126373185a5Stb "ECDHE-ECDSA-AES256-SHA384:" 127373185a5Stb "ECDHE-RSA-AES256-SHA:" 128373185a5Stb "ECDHE-ECDSA-AES256-SHA", 129373185a5Stb }, 130373185a5Stb .shared_ciphers = 131373185a5Stb "ECDHE-RSA-AES256-GCM-SHA384:" 132373185a5Stb "ECDHE-ECDSA-AES256-GCM-SHA384:" 133373185a5Stb "ECDHE-RSA-AES256-SHA384:" 134373185a5Stb "ECDHE-ECDSA-AES256-SHA384:" 135373185a5Stb "ECDHE-RSA-AES256-SHA:" 136373185a5Stb "ECDHE-ECDSA-AES256-SHA", 137373185a5Stb }, 138373185a5Stb 139373185a5Stb { 140373185a5Stb .description = "TLSv1.2, server without ECDSA", 141373185a5Stb .client_config = { 142373185a5Stb .name = "client", 143373185a5Stb .server = 0, 144373185a5Stb .max_version = TLS1_2_VERSION, 145373185a5Stb .min_version = TLS1_2_VERSION, 146373185a5Stb .ciphers = 147373185a5Stb "ECDHE-RSA-AES256-GCM-SHA384:" 148373185a5Stb "ECDHE-ECDSA-AES256-GCM-SHA384:" 149373185a5Stb "ECDHE-RSA-AES256-SHA384:" 150373185a5Stb "ECDHE-ECDSA-AES256-SHA384:" 151373185a5Stb "ECDHE-RSA-AES256-SHA:" 152373185a5Stb "ECDHE-ECDSA-AES256-SHA", 153373185a5Stb }, 154373185a5Stb .server_config = { 155373185a5Stb .name = "server", 156373185a5Stb .server = 1, 157373185a5Stb .max_version = TLS1_2_VERSION, 158373185a5Stb .min_version = TLS1_2_VERSION, 159373185a5Stb .ciphers = 160373185a5Stb "ECDHE-RSA-AES256-GCM-SHA384:" 161373185a5Stb "ECDHE-RSA-AES256-SHA384:" 162373185a5Stb "ECDHE-RSA-AES256-SHA", 163373185a5Stb }, 164373185a5Stb .shared_ciphers = 165373185a5Stb "ECDHE-RSA-AES256-GCM-SHA384:" 166373185a5Stb "ECDHE-RSA-AES256-SHA384:" 167373185a5Stb "ECDHE-RSA-AES256-SHA", 168373185a5Stb }, 169373185a5Stb 170373185a5Stb { 171373185a5Stb .description = "TLSv1.3 ciphers are prepended", 172373185a5Stb .client_config = { 173373185a5Stb .name = "client", 174373185a5Stb .server = 0, 175373185a5Stb .max_version = TLS1_3_VERSION, 176373185a5Stb .min_version = TLS1_2_VERSION, 177373185a5Stb .ciphers = 178373185a5Stb "ECDHE-RSA-AES256-GCM-SHA384", 179373185a5Stb }, 180373185a5Stb .server_config = { 181373185a5Stb .name = "server", 182373185a5Stb .server = 1, 183373185a5Stb .max_version = TLS1_3_VERSION, 184373185a5Stb .min_version = TLS1_2_VERSION, 185373185a5Stb .ciphers = 186373185a5Stb "ECDHE-RSA-AES256-GCM-SHA384", 187373185a5Stb }, 188373185a5Stb .shared_ciphers = 18957e596eeStb "TLS_AES_256_GCM_SHA384:" 19057e596eeStb "TLS_CHACHA20_POLY1305_SHA256:" 19157e596eeStb "TLS_AES_128_GCM_SHA256:" 192373185a5Stb "ECDHE-RSA-AES256-GCM-SHA384", 193373185a5Stb .shared_ciphers_without_aesni = 19457e596eeStb "TLS_CHACHA20_POLY1305_SHA256:" 19557e596eeStb "TLS_AES_256_GCM_SHA384:" 19657e596eeStb "TLS_AES_128_GCM_SHA256:" 197373185a5Stb "ECDHE-RSA-AES256-GCM-SHA384", 198373185a5Stb }, 199373185a5Stb }; 200373185a5Stb 201373185a5Stb static const size_t N_SHARED_CIPHERS_TESTS = 202373185a5Stb sizeof(ssl_shared_ciphers_tests) / sizeof(ssl_shared_ciphers_tests[0]); 203373185a5Stb 204373185a5Stb static SSL_CTX * 205373185a5Stb peer_config_to_ssl_ctx(const struct peer_config *config) 206373185a5Stb { 207373185a5Stb SSL_CTX *ctx; 208373185a5Stb 209373185a5Stb if ((ctx = SSL_CTX_new(TLS_method())) == NULL) { 210373185a5Stb fprintf(stderr, "SSL_CTX_new(%s) failed\n", config->name); 211373185a5Stb goto err; 212373185a5Stb } 213373185a5Stb if (!SSL_CTX_set_max_proto_version(ctx, config->max_version)) { 214373185a5Stb fprintf(stderr, "max_proto_version(%s) failed\n", config->name); 215373185a5Stb goto err; 216373185a5Stb } 217373185a5Stb if (!SSL_CTX_set_min_proto_version(ctx, config->min_version)) { 218373185a5Stb fprintf(stderr, "min_proto_version(%s) failed\n", config->name); 219373185a5Stb goto err; 220373185a5Stb } 221373185a5Stb if (!SSL_CTX_set_cipher_list(ctx, config->ciphers)) { 222551179fdStb fprintf(stderr, "set_cipher_list(%s) failed\n", config->name); 223373185a5Stb goto err; 224373185a5Stb } 225373185a5Stb 226373185a5Stb if (config->server) { 227373185a5Stb if (!SSL_CTX_use_certificate_file(ctx, server_cert, 228373185a5Stb SSL_FILETYPE_PEM)) { 229373185a5Stb fprintf(stderr, "use_certificate_file(%s) failed\n", 230373185a5Stb config->name); 231373185a5Stb goto err; 232373185a5Stb } 233373185a5Stb if (!SSL_CTX_use_PrivateKey_file(ctx, server_key, 234373185a5Stb SSL_FILETYPE_PEM)) { 235373185a5Stb fprintf(stderr, "use_PrivateKey_file(%s) failed\n", 236373185a5Stb config->name); 237373185a5Stb goto err; 238373185a5Stb } 239373185a5Stb } 240373185a5Stb 241373185a5Stb return ctx; 242373185a5Stb 243373185a5Stb err: 244373185a5Stb SSL_CTX_free(ctx); 245373185a5Stb return NULL; 246373185a5Stb } 247373185a5Stb 248373185a5Stb /* Connect client and server via a pair of "nonblocking" memory BIOs. */ 249373185a5Stb static int 250373185a5Stb connect_peers(SSL *client_ssl, SSL *server_ssl, const char *description) 251373185a5Stb { 252373185a5Stb BIO *client_wbio = NULL, *server_wbio = NULL; 253373185a5Stb int ret = 0; 254373185a5Stb 255373185a5Stb if ((client_wbio = BIO_new(BIO_s_mem())) == NULL) { 256373185a5Stb fprintf(stderr, "%s: failed to create client BIO\n", 257373185a5Stb description); 258373185a5Stb goto err; 259373185a5Stb } 260373185a5Stb if ((server_wbio = BIO_new(BIO_s_mem())) == NULL) { 261373185a5Stb fprintf(stderr, "%s: failed to create server BIO\n", 262373185a5Stb description); 263373185a5Stb goto err; 264373185a5Stb } 265373185a5Stb if (BIO_set_mem_eof_return(client_wbio, -1) <= 0) { 266373185a5Stb fprintf(stderr, "%s: failed to set client eof return\n", 267373185a5Stb description); 268373185a5Stb goto err; 269373185a5Stb } 270373185a5Stb if (BIO_set_mem_eof_return(server_wbio, -1) <= 0) { 271373185a5Stb fprintf(stderr, "%s: failed to set server eof return\n", 272373185a5Stb description); 273373185a5Stb goto err; 274373185a5Stb } 275373185a5Stb 276373185a5Stb /* Avoid double free. SSL_set_bio() takes ownership of the BIOs. */ 277373185a5Stb BIO_up_ref(client_wbio); 278373185a5Stb BIO_up_ref(server_wbio); 279373185a5Stb 280373185a5Stb SSL_set_bio(client_ssl, server_wbio, client_wbio); 281373185a5Stb SSL_set_bio(server_ssl, client_wbio, server_wbio); 282373185a5Stb client_wbio = NULL; 283373185a5Stb server_wbio = NULL; 284373185a5Stb 285373185a5Stb ret = 1; 286373185a5Stb 287373185a5Stb err: 288373185a5Stb BIO_free(client_wbio); 289373185a5Stb BIO_free(server_wbio); 290373185a5Stb 291373185a5Stb return ret; 292373185a5Stb } 293373185a5Stb 294373185a5Stb static int 295373185a5Stb push_data_to_peer(SSL *ssl, int *ret, int (*func)(SSL *), const char *func_name, 296373185a5Stb const char *description) 297373185a5Stb { 298373185a5Stb int ssl_err = 0; 299373185a5Stb 300373185a5Stb if (*ret == 1) 301373185a5Stb return 1; 302373185a5Stb 303373185a5Stb /* 30483cacfdaStb * Do SSL_connect/SSL_accept/SSL_shutdown once and loop while hitting 30583cacfdaStb * WANT_WRITE. If done or on WANT_READ hand off to peer. 306373185a5Stb */ 307373185a5Stb 308373185a5Stb do { 309373185a5Stb if ((*ret = func(ssl)) <= 0) 310373185a5Stb ssl_err = SSL_get_error(ssl, *ret); 311373185a5Stb } while (*ret <= 0 && ssl_err == SSL_ERROR_WANT_WRITE); 312373185a5Stb 31383cacfdaStb /* Ignore erroneous error - see SSL_shutdown(3)... */ 31483cacfdaStb if (func == SSL_shutdown && ssl_err == SSL_ERROR_SYSCALL) 31583cacfdaStb return 1; 31683cacfdaStb 317373185a5Stb if (*ret <= 0 && ssl_err != SSL_ERROR_WANT_READ) { 318373185a5Stb fprintf(stderr, "%s: %s failed\n", description, func_name); 319373185a5Stb ERR_print_errors_fp(stderr); 320373185a5Stb return 0; 321373185a5Stb } 322373185a5Stb 323373185a5Stb return 1; 324373185a5Stb } 325373185a5Stb 326373185a5Stb /* 327373185a5Stb * Alternate between loops of SSL_connect() and SSL_accept() as long as only 328373185a5Stb * WANT_READ and WANT_WRITE situations are encountered. A function is repeated 329551179fdStb * until WANT_READ is returned or it succeeds, then it's the other function's 330551179fdStb * turn to make progress. Succeeds if SSL_connect() and SSL_accept() return 1. 331373185a5Stb */ 332373185a5Stb static int 333373185a5Stb handshake(SSL *client_ssl, SSL *server_ssl, const char *description) 334373185a5Stb { 335373185a5Stb int loops = 0, client_ret = 0, server_ret = 0; 336373185a5Stb 337373185a5Stb while (loops++ < 10 && (client_ret <= 0 || server_ret <= 0)) { 3388510c913Stb if (!push_data_to_peer(client_ssl, &client_ret, SSL_connect, 3398510c913Stb "SSL_connect", description)) 3408510c913Stb return 0; 3418510c913Stb 3428510c913Stb if (!push_data_to_peer(server_ssl, &server_ret, SSL_accept, 3438510c913Stb "SSL_accept", description)) 344373185a5Stb return 0; 345373185a5Stb } 346373185a5Stb 347fc736411Stb if (client_ret != 1 || server_ret != 1) { 348fc736411Stb fprintf(stderr, "%s: failed\n", __func__); 349fc736411Stb return 0; 350fc736411Stb } 351fc736411Stb 352fc736411Stb return 1; 353373185a5Stb } 354373185a5Stb 35583cacfdaStb static int 3560dfc6687Stb shutdown_peers(SSL *client_ssl, SSL *server_ssl, const char *description) 35783cacfdaStb { 35883cacfdaStb int loops = 0, client_ret = 0, server_ret = 0; 35983cacfdaStb 36083cacfdaStb while (loops++ < 10 && (client_ret <= 0 || server_ret <= 0)) { 36183cacfdaStb if (!push_data_to_peer(client_ssl, &client_ret, SSL_shutdown, 36283cacfdaStb "client shutdown", description)) 36383cacfdaStb return 0; 36483cacfdaStb 36583cacfdaStb if (!push_data_to_peer(server_ssl, &server_ret, SSL_shutdown, 36683cacfdaStb "server shutdown", description)) 36783cacfdaStb return 0; 36883cacfdaStb } 36983cacfdaStb 370fc736411Stb if (client_ret != 1 || server_ret != 1) { 371fc736411Stb fprintf(stderr, "%s: failed\n", __func__); 372fc736411Stb return 0; 373fc736411Stb } 374fc736411Stb 375fc736411Stb return 1; 37683cacfdaStb } 37783cacfdaStb 378373185a5Stb /* from ssl_ciph.c */ 379373185a5Stb static inline int 380373185a5Stb ssl_aes_is_accelerated(void) 381373185a5Stb { 382*a75d20e9Sjsing return (OPENSSL_cpu_caps() & CRYPTO_CPU_CAPS_ACCELERATED_AES) != 0; 383373185a5Stb } 384373185a5Stb 385373185a5Stb static int 386373185a5Stb check_shared_ciphers(const struct ssl_shared_ciphers_test_data *test, 387373185a5Stb const char *got) 388373185a5Stb { 389373185a5Stb const char *want = test->shared_ciphers; 390373185a5Stb int failed; 391373185a5Stb 392551179fdStb if (!ssl_aes_is_accelerated() && 393551179fdStb test->shared_ciphers_without_aesni != NULL) 394373185a5Stb want = test->shared_ciphers_without_aesni; 395551179fdStb 396373185a5Stb failed = strcmp(want, got); 397373185a5Stb 398373185a5Stb if (failed) 399373185a5Stb fprintf(stderr, "%s: want \"%s\", got \"%s\"\n", 400373185a5Stb test->description, want, got); 401373185a5Stb 402373185a5Stb return failed; 403373185a5Stb } 404373185a5Stb 405373185a5Stb static int 406373185a5Stb test_get_shared_ciphers(const struct ssl_shared_ciphers_test_data *test) 407373185a5Stb { 408373185a5Stb SSL_CTX *client_ctx = NULL, *server_ctx = NULL; 409373185a5Stb SSL *client_ssl = NULL, *server_ssl = NULL; 410373185a5Stb char buf[4096]; 411373185a5Stb int failed = 1; 412373185a5Stb 413373185a5Stb if ((client_ctx = peer_config_to_ssl_ctx(&test->client_config)) == NULL) 414373185a5Stb goto err; 415373185a5Stb if ((server_ctx = peer_config_to_ssl_ctx(&test->server_config)) == NULL) 416373185a5Stb goto err; 417373185a5Stb 418373185a5Stb if ((client_ssl = SSL_new(client_ctx)) == NULL) { 419373185a5Stb fprintf(stderr, "%s: failed to create client SSL\n", 420373185a5Stb test->description); 421373185a5Stb goto err; 422373185a5Stb } 423373185a5Stb if ((server_ssl = SSL_new(server_ctx)) == NULL) { 424373185a5Stb fprintf(stderr, "%s: failed to create server SSL\n", 425373185a5Stb test->description); 426373185a5Stb goto err; 427373185a5Stb } 428373185a5Stb 429373185a5Stb if (!connect_peers(client_ssl, server_ssl, test->description)) 430373185a5Stb goto err; 431373185a5Stb 432373185a5Stb if (!handshake(client_ssl, server_ssl, test->description)) 433373185a5Stb goto err; 434373185a5Stb 435373185a5Stb if (SSL_get_shared_ciphers(server_ssl, buf, sizeof(buf)) == NULL) { 436373185a5Stb fprintf(stderr, "%s: failed to get shared ciphers\n", 437373185a5Stb test->description); 438373185a5Stb goto err; 439373185a5Stb } 440373185a5Stb 4410dfc6687Stb if (!shutdown_peers(client_ssl, server_ssl, test->description)) 44283cacfdaStb goto err; 44383cacfdaStb 444373185a5Stb failed = check_shared_ciphers(test, buf); 445373185a5Stb 446373185a5Stb err: 447373185a5Stb SSL_CTX_free(client_ctx); 448373185a5Stb SSL_CTX_free(server_ctx); 449373185a5Stb SSL_free(client_ssl); 450373185a5Stb SSL_free(server_ssl); 451373185a5Stb 452373185a5Stb return failed; 453373185a5Stb } 454373185a5Stb 455373185a5Stb int 456373185a5Stb main(int argc, char **argv) 457373185a5Stb { 458373185a5Stb size_t i; 459373185a5Stb int failed = 0; 460373185a5Stb 4610ba45550Sjsing if (asprintf(&server_cert, "%s/server1-rsa.pem", CERTSDIR) == -1) { 462373185a5Stb fprintf(stderr, "asprintf server_cert failed\n"); 463373185a5Stb failed = 1; 464373185a5Stb goto err; 465373185a5Stb } 466373185a5Stb server_key = server_cert; 467373185a5Stb 468373185a5Stb for (i = 0; i < N_SHARED_CIPHERS_TESTS; i++) 469373185a5Stb failed |= test_get_shared_ciphers(&ssl_shared_ciphers_tests[i]); 470373185a5Stb 471373185a5Stb if (failed == 0) 472373185a5Stb printf("PASS %s\n", __FILE__); 473373185a5Stb 474373185a5Stb err: 475373185a5Stb free(server_cert); 476373185a5Stb 477373185a5Stb return failed; 478373185a5Stb } 479