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