1 /* $OpenBSD: ssl_get_shared_ciphers.c,v 1.3 2021/01/10 23:59:32 tb Exp $ */ 2 /* 3 * Copyright (c) 2021 Theo Buehler <tb@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <stdint.h> 19 #include <openssl/err.h> 20 #include <openssl/ssl.h> 21 22 struct peer_config { 23 const char *name; 24 int server; 25 uint16_t max_version; 26 uint16_t min_version; 27 const char *ciphers; 28 }; 29 30 struct ssl_shared_ciphers_test_data { 31 const char *description; 32 struct peer_config client_config; 33 struct peer_config server_config; 34 const char *shared_ciphers; 35 const char *shared_ciphers_without_aesni; 36 }; 37 38 char *server_cert; 39 char *server_key; 40 41 static const struct ssl_shared_ciphers_test_data ssl_shared_ciphers_tests[] = { 42 { 43 .description = "TLSv1.3 defaults", 44 .client_config = { 45 .name = "client", 46 .server = 0, 47 .max_version = TLS1_3_VERSION, 48 .min_version = TLS1_3_VERSION, 49 .ciphers = 50 "AEAD-AES256-GCM-SHA384:" 51 "AEAD-CHACHA20-POLY1305-SHA256:" 52 "AEAD-AES128-GCM-SHA256", 53 }, 54 .server_config = { 55 .name = "server", 56 .server = 1, 57 .max_version = TLS1_3_VERSION, 58 .min_version = TLS1_3_VERSION, 59 .ciphers = 60 "AEAD-AES256-GCM-SHA384:" 61 "AEAD-CHACHA20-POLY1305-SHA256:" 62 "AEAD-AES128-GCM-SHA256", 63 }, 64 .shared_ciphers = 65 "AEAD-AES256-GCM-SHA384:" 66 "AEAD-CHACHA20-POLY1305-SHA256:" 67 "AEAD-AES128-GCM-SHA256", 68 }, 69 70 { 71 .description = "TLSv1.3, client without ChaCha", 72 .client_config = { 73 .name = "client", 74 .server = 0, 75 .max_version = TLS1_3_VERSION, 76 .min_version = TLS1_3_VERSION, 77 .ciphers = 78 "AEAD-AES256-GCM-SHA384:" 79 "AEAD-AES128-GCM-SHA256", 80 }, 81 .server_config = { 82 .name = "server", 83 .server = 1, 84 .max_version = TLS1_3_VERSION, 85 .min_version = TLS1_3_VERSION, 86 .ciphers = 87 "AEAD-AES256-GCM-SHA384:" 88 "AEAD-CHACHA20-POLY1305-SHA256:" 89 "AEAD-AES128-GCM-SHA256", 90 }, 91 .shared_ciphers = 92 "AEAD-AES256-GCM-SHA384:" 93 "AEAD-AES128-GCM-SHA256", 94 }, 95 96 { 97 .description = "TLSv1.2", 98 .client_config = { 99 .name = "client", 100 .server = 0, 101 .max_version = TLS1_2_VERSION, 102 .min_version = TLS1_2_VERSION, 103 .ciphers = 104 "ECDHE-RSA-AES256-GCM-SHA384:" 105 "ECDHE-ECDSA-AES256-GCM-SHA384:" 106 "ECDHE-RSA-AES256-SHA384:" 107 "ECDHE-ECDSA-AES256-SHA384:" 108 "ECDHE-RSA-AES256-SHA:" 109 "ECDHE-ECDSA-AES256-SHA", 110 }, 111 .server_config = { 112 .name = "server", 113 .server = 1, 114 .max_version = TLS1_2_VERSION, 115 .min_version = TLS1_2_VERSION, 116 .ciphers = 117 "ECDHE-RSA-AES256-GCM-SHA384:" 118 "ECDHE-ECDSA-AES256-GCM-SHA384:" 119 "ECDHE-RSA-AES256-SHA384:" 120 "ECDHE-ECDSA-AES256-SHA384:" 121 "ECDHE-RSA-AES256-SHA:" 122 "ECDHE-ECDSA-AES256-SHA", 123 }, 124 .shared_ciphers = 125 "ECDHE-RSA-AES256-GCM-SHA384:" 126 "ECDHE-ECDSA-AES256-GCM-SHA384:" 127 "ECDHE-RSA-AES256-SHA384:" 128 "ECDHE-ECDSA-AES256-SHA384:" 129 "ECDHE-RSA-AES256-SHA:" 130 "ECDHE-ECDSA-AES256-SHA", 131 }, 132 133 { 134 .description = "TLSv1.2, server without ECDSA", 135 .client_config = { 136 .name = "client", 137 .server = 0, 138 .max_version = TLS1_2_VERSION, 139 .min_version = TLS1_2_VERSION, 140 .ciphers = 141 "ECDHE-RSA-AES256-GCM-SHA384:" 142 "ECDHE-ECDSA-AES256-GCM-SHA384:" 143 "ECDHE-RSA-AES256-SHA384:" 144 "ECDHE-ECDSA-AES256-SHA384:" 145 "ECDHE-RSA-AES256-SHA:" 146 "ECDHE-ECDSA-AES256-SHA", 147 }, 148 .server_config = { 149 .name = "server", 150 .server = 1, 151 .max_version = TLS1_2_VERSION, 152 .min_version = TLS1_2_VERSION, 153 .ciphers = 154 "ECDHE-RSA-AES256-GCM-SHA384:" 155 "ECDHE-RSA-AES256-SHA384:" 156 "ECDHE-RSA-AES256-SHA", 157 }, 158 .shared_ciphers = 159 "ECDHE-RSA-AES256-GCM-SHA384:" 160 "ECDHE-RSA-AES256-SHA384:" 161 "ECDHE-RSA-AES256-SHA", 162 }, 163 164 { 165 .description = "TLSv1.3 ciphers are prepended", 166 .client_config = { 167 .name = "client", 168 .server = 0, 169 .max_version = TLS1_3_VERSION, 170 .min_version = TLS1_2_VERSION, 171 .ciphers = 172 "ECDHE-RSA-AES256-GCM-SHA384", 173 }, 174 .server_config = { 175 .name = "server", 176 .server = 1, 177 .max_version = TLS1_3_VERSION, 178 .min_version = TLS1_2_VERSION, 179 .ciphers = 180 "ECDHE-RSA-AES256-GCM-SHA384", 181 }, 182 .shared_ciphers = 183 "AEAD-AES256-GCM-SHA384:" 184 "AEAD-CHACHA20-POLY1305-SHA256:" 185 "AEAD-AES128-GCM-SHA256:" 186 "ECDHE-RSA-AES256-GCM-SHA384", 187 .shared_ciphers_without_aesni = 188 "AEAD-CHACHA20-POLY1305-SHA256:" 189 "AEAD-AES256-GCM-SHA384:" 190 "AEAD-AES128-GCM-SHA256:" 191 "ECDHE-RSA-AES256-GCM-SHA384", 192 }, 193 }; 194 195 static const size_t N_SHARED_CIPHERS_TESTS = 196 sizeof(ssl_shared_ciphers_tests) / sizeof(ssl_shared_ciphers_tests[0]); 197 198 static SSL_CTX * 199 peer_config_to_ssl_ctx(const struct peer_config *config) 200 { 201 SSL_CTX *ctx; 202 203 if ((ctx = SSL_CTX_new(TLS_method())) == NULL) { 204 fprintf(stderr, "SSL_CTX_new(%s) failed\n", config->name); 205 goto err; 206 } 207 if (!SSL_CTX_set_max_proto_version(ctx, config->max_version)) { 208 fprintf(stderr, "max_proto_version(%s) failed\n", config->name); 209 goto err; 210 } 211 if (!SSL_CTX_set_min_proto_version(ctx, config->min_version)) { 212 fprintf(stderr, "min_proto_version(%s) failed\n", config->name); 213 goto err; 214 } 215 if (!SSL_CTX_set_cipher_list(ctx, config->ciphers)) { 216 fprintf(stderr, "set_cipher_list(%s) failed\n", 217 config->name); 218 goto err; 219 } 220 221 SSL_CTX_clear_mode(ctx, SSL_MODE_AUTO_RETRY); 222 223 if (config->server) { 224 if (!SSL_CTX_use_certificate_file(ctx, server_cert, 225 SSL_FILETYPE_PEM)) { 226 fprintf(stderr, "use_certificate_file(%s) failed\n", 227 config->name); 228 goto err; 229 } 230 if (!SSL_CTX_use_PrivateKey_file(ctx, server_key, 231 SSL_FILETYPE_PEM)) { 232 fprintf(stderr, "use_PrivateKey_file(%s) failed\n", 233 config->name); 234 goto err; 235 } 236 } 237 238 return ctx; 239 240 err: 241 SSL_CTX_free(ctx); 242 return NULL; 243 } 244 245 /* Connect client and server via a pair of "nonblocking" memory BIOs. */ 246 static int 247 connect_peers(SSL *client_ssl, SSL *server_ssl, const char *description) 248 { 249 BIO *client_wbio = NULL, *server_wbio = NULL; 250 int ret = 0; 251 252 if ((client_wbio = BIO_new(BIO_s_mem())) == NULL) { 253 fprintf(stderr, "%s: failed to create client BIO\n", 254 description); 255 goto err; 256 } 257 if ((server_wbio = BIO_new(BIO_s_mem())) == NULL) { 258 fprintf(stderr, "%s: failed to create server BIO\n", 259 description); 260 goto err; 261 } 262 if (BIO_set_mem_eof_return(client_wbio, -1) <= 0) { 263 fprintf(stderr, "%s: failed to set client eof return\n", 264 description); 265 goto err; 266 } 267 if (BIO_set_mem_eof_return(server_wbio, -1) <= 0) { 268 fprintf(stderr, "%s: failed to set server eof return\n", 269 description); 270 goto err; 271 } 272 273 /* Avoid double free. SSL_set_bio() takes ownership of the BIOs. */ 274 BIO_up_ref(client_wbio); 275 BIO_up_ref(server_wbio); 276 277 SSL_set_bio(client_ssl, server_wbio, client_wbio); 278 SSL_set_bio(server_ssl, client_wbio, server_wbio); 279 client_wbio = NULL; 280 server_wbio = NULL; 281 282 ret = 1; 283 284 err: 285 BIO_free(client_wbio); 286 BIO_free(server_wbio); 287 288 return ret; 289 } 290 291 static int 292 push_data_to_peer(SSL *ssl, int *ret, int (*func)(SSL *), const char *func_name, 293 const char *description) 294 { 295 int ssl_err = 0; 296 297 if (*ret == 1) 298 return 1; 299 300 /* 301 * Do SSL_connect/SSL_accept once and loop while hitting WANT_WRITE. 302 * If done or on WANT_READ hand off to peer. 303 */ 304 305 do { 306 if ((*ret = func(ssl)) <= 0) 307 ssl_err = SSL_get_error(ssl, *ret); 308 } while (*ret <= 0 && ssl_err == SSL_ERROR_WANT_WRITE); 309 310 if (*ret <= 0 && ssl_err != SSL_ERROR_WANT_READ) { 311 fprintf(stderr, "%s: %s failed\n", description, func_name); 312 ERR_print_errors_fp(stderr); 313 return 0; 314 } 315 316 return 1; 317 } 318 319 /* 320 * Alternate between loops of SSL_connect() and SSL_accept() as long as only 321 * WANT_READ and WANT_WRITE situations are encountered. A function is repeated 322 * until WANT_READ is returned or it succeeds, then it's the other functions 323 * turn to make progress. Success: both functions returned 1. 324 */ 325 static int 326 handshake(SSL *client_ssl, SSL *server_ssl, const char *description) 327 { 328 int loops = 0, client_ret = 0, server_ret = 0; 329 330 while (loops++ < 10 && (client_ret <= 0 || server_ret <= 0)) { 331 if (!push_data_to_peer(client_ssl, &client_ret, SSL_connect, 332 "SSL_connect", description)) 333 return 0; 334 335 if (!push_data_to_peer(server_ssl, &server_ret, SSL_accept, 336 "SSL_accept", description)) 337 return 0; 338 } 339 340 return client_ret == 1 && server_ret == 1; 341 } 342 343 /* from ssl_ciph.c */ 344 static inline int 345 ssl_aes_is_accelerated(void) 346 { 347 #if defined(__i386__) || defined(__x86_64__) 348 return ((OPENSSL_cpu_caps() & (1ULL << 57)) != 0); 349 #else 350 return (0); 351 #endif 352 } 353 354 static int 355 check_shared_ciphers(const struct ssl_shared_ciphers_test_data *test, 356 const char *got) 357 { 358 const char *want = test->shared_ciphers; 359 int failed; 360 361 failed = strcmp(want, got); 362 363 if (failed && !ssl_aes_is_accelerated() && 364 test->shared_ciphers_without_aesni != NULL) { 365 want = test->shared_ciphers_without_aesni; 366 failed = strcmp(want, got); 367 } 368 369 if (failed) 370 fprintf(stderr, "%s: want \"%s\", got \"%s\"\n", 371 test->description, want, got); 372 373 return failed; 374 } 375 376 static int 377 test_get_shared_ciphers(const struct ssl_shared_ciphers_test_data *test) 378 { 379 SSL_CTX *client_ctx = NULL, *server_ctx = NULL; 380 SSL *client_ssl = NULL, *server_ssl = NULL; 381 char buf[4096]; 382 int failed = 1; 383 384 if ((client_ctx = peer_config_to_ssl_ctx(&test->client_config)) == NULL) 385 goto err; 386 if ((server_ctx = peer_config_to_ssl_ctx(&test->server_config)) == NULL) 387 goto err; 388 389 if ((client_ssl = SSL_new(client_ctx)) == NULL) { 390 fprintf(stderr, "%s: failed to create client SSL\n", 391 test->description); 392 goto err; 393 } 394 if ((server_ssl = SSL_new(server_ctx)) == NULL) { 395 fprintf(stderr, "%s: failed to create server SSL\n", 396 test->description); 397 goto err; 398 } 399 400 if (!connect_peers(client_ssl, server_ssl, test->description)) 401 goto err; 402 403 if (!handshake(client_ssl, server_ssl, test->description)) 404 goto err; 405 406 if (SSL_get_shared_ciphers(server_ssl, buf, sizeof(buf)) == NULL) { 407 fprintf(stderr, "%s: failed to get shared ciphers\n", 408 test->description); 409 goto err; 410 } 411 412 failed = check_shared_ciphers(test, buf); 413 414 err: 415 SSL_CTX_free(client_ctx); 416 SSL_CTX_free(server_ctx); 417 SSL_free(client_ssl); 418 SSL_free(server_ssl); 419 420 return failed; 421 } 422 423 int 424 main(int argc, char **argv) 425 { 426 size_t i; 427 int failed = 0; 428 429 if (asprintf(&server_cert, "%s/../certs/server.pem", CURDIR) == -1) { 430 fprintf(stderr, "asprintf server_cert failed\n"); 431 failed = 1; 432 goto err; 433 } 434 server_key = server_cert; 435 436 for (i = 0; i < N_SHARED_CIPHERS_TESTS; i++) 437 failed |= test_get_shared_ciphers(&ssl_shared_ciphers_tests[i]); 438 439 if (failed == 0) 440 printf("PASS %s\n", __FILE__); 441 442 err: 443 free(server_cert); 444 445 return failed; 446 } 447