xref: /openbsd-src/regress/lib/libssl/unit/ssl_get_shared_ciphers.c (revision 8510c9139214f587bb139bafdbfae624dc43a917)
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