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