xref: /openbsd-src/regress/lib/libssl/unit/ssl_get_shared_ciphers.c (revision fc736411e73545a5e5673eff971472a6875878c8)
1 /*	$OpenBSD: ssl_get_shared_ciphers.c,v 1.6 2021/01/11 18:33:43 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/SSL_shutdown once and loop while hitting
308 	 * WANT_WRITE.  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 	/* Ignore erroneous error - see SSL_shutdown(3)... */
317 	if (func == SSL_shutdown && ssl_err == SSL_ERROR_SYSCALL)
318 		return 1;
319 
320 	if (*ret <= 0 && ssl_err != SSL_ERROR_WANT_READ) {
321 		fprintf(stderr, "%s: %s failed\n", description, func_name);
322 		ERR_print_errors_fp(stderr);
323 		return 0;
324 	}
325 
326 	return 1;
327 }
328 
329 /*
330  * Alternate between loops of SSL_connect() and SSL_accept() as long as only
331  * WANT_READ and WANT_WRITE situations are encountered. A function is repeated
332  * until WANT_READ is returned or it succeeds, then it's the other functions
333  * turn to make progress. Success: both functions returned 1.
334  */
335 static int
336 handshake(SSL *client_ssl, SSL *server_ssl, const char *description)
337 {
338 	int loops = 0, client_ret = 0, server_ret = 0;
339 
340 	while (loops++ < 10 && (client_ret <= 0 || server_ret <= 0)) {
341 		if (!push_data_to_peer(client_ssl, &client_ret, SSL_connect,
342 		    "SSL_connect", description))
343 			return 0;
344 
345 		if (!push_data_to_peer(server_ssl, &server_ret, SSL_accept,
346 		    "SSL_accept", description))
347 			return 0;
348 	}
349 
350 	if (client_ret != 1 || server_ret != 1) {
351 		fprintf(stderr, "%s: failed\n", __func__);
352 		return 0;
353 	}
354 
355 	return 1;
356 }
357 
358 static int
359 shutdown(SSL *client_ssl, SSL *server_ssl, const char *description)
360 {
361 	int loops = 0, client_ret = 0, server_ret = 0;
362 
363 	while (loops++ < 10 && (client_ret <= 0 || server_ret <= 0)) {
364 		if (!push_data_to_peer(client_ssl, &client_ret, SSL_shutdown,
365 		    "client shutdown", description))
366 			return 0;
367 
368 		if (!push_data_to_peer(server_ssl, &server_ret, SSL_shutdown,
369 		    "server shutdown", description))
370 			return 0;
371 	}
372 
373 	if (client_ret != 1 || server_ret != 1) {
374 		fprintf(stderr, "%s: failed\n", __func__);
375 		return 0;
376 	}
377 
378 	return 1;
379 }
380 
381 /* from ssl_ciph.c */
382 static inline int
383 ssl_aes_is_accelerated(void)
384 {
385 #if defined(__i386__) || defined(__x86_64__)
386 	return ((OPENSSL_cpu_caps() & (1ULL << 57)) != 0);
387 #else
388 	return (0);
389 #endif
390 }
391 
392 static int
393 check_shared_ciphers(const struct ssl_shared_ciphers_test_data *test,
394     const char *got)
395 {
396 	const char *want = test->shared_ciphers;
397 	int failed;
398 
399 	failed = strcmp(want, got);
400 
401 	if (failed && !ssl_aes_is_accelerated() &&
402 	    test->shared_ciphers_without_aesni != NULL) {
403 		want = test->shared_ciphers_without_aesni;
404 		failed = strcmp(want, got);
405 	}
406 
407 	if (failed)
408 		fprintf(stderr, "%s: want \"%s\", got \"%s\"\n",
409 		    test->description, want, got);
410 
411 	return failed;
412 }
413 
414 static int
415 test_get_shared_ciphers(const struct ssl_shared_ciphers_test_data *test)
416 {
417 	SSL_CTX *client_ctx = NULL, *server_ctx = NULL;
418 	SSL *client_ssl = NULL, *server_ssl = NULL;
419 	char buf[4096];
420 	int failed = 1;
421 
422 	if ((client_ctx = peer_config_to_ssl_ctx(&test->client_config)) == NULL)
423 		goto err;
424 	if ((server_ctx = peer_config_to_ssl_ctx(&test->server_config)) == NULL)
425 		goto err;
426 
427 	if ((client_ssl = SSL_new(client_ctx)) == NULL) {
428 		fprintf(stderr, "%s: failed to create client SSL\n",
429 		    test->description);
430 		goto err;
431 	}
432 	if ((server_ssl = SSL_new(server_ctx)) == NULL) {
433 		fprintf(stderr, "%s: failed to create server SSL\n",
434 		    test->description);
435 		goto err;
436 	}
437 
438 	if (!connect_peers(client_ssl, server_ssl, test->description))
439 		goto err;
440 
441 	if (!handshake(client_ssl, server_ssl, test->description))
442 		goto err;
443 
444 	if (SSL_get_shared_ciphers(server_ssl, buf, sizeof(buf)) == NULL) {
445 		fprintf(stderr, "%s: failed to get shared ciphers\n",
446 		    test->description);
447 		goto err;
448 	}
449 
450 	if (!shutdown(client_ssl, server_ssl, test->description))
451 		goto err;
452 
453 	failed = check_shared_ciphers(test, buf);
454 
455  err:
456 	SSL_CTX_free(client_ctx);
457 	SSL_CTX_free(server_ctx);
458 	SSL_free(client_ssl);
459 	SSL_free(server_ssl);
460 
461 	return failed;
462 }
463 
464 int
465 main(int argc, char **argv)
466 {
467 	size_t i;
468 	int failed = 0;
469 
470 	if (asprintf(&server_cert, "%s/../certs/server.pem", CURDIR) == -1) {
471 		fprintf(stderr, "asprintf server_cert failed\n");
472 		failed = 1;
473 		goto err;
474 	}
475 	server_key = server_cert;
476 
477 	for (i = 0; i < N_SHARED_CIPHERS_TESTS; i++)
478 		failed |= test_get_shared_ciphers(&ssl_shared_ciphers_tests[i]);
479 
480 	if (failed == 0)
481 		printf("PASS %s\n", __FILE__);
482 
483  err:
484 	free(server_cert);
485 
486 	return failed;
487 }
488