xref: /freebsd/crypto/openssl/test/tls13groupselection_test.c (revision 10a428653ee7216475f1ddce3fb4cbf1200319f8)
1 /*
2  * Copyright 2025-2026 The OpenSSL Project Authors. All Rights Reserved.
3  *
4  * Licensed under the Apache License 2.0 (the "License").  You may not use
5  * this file except in compliance with the License.  You can obtain a copy
6  * in the file LICENSE in the source distribution or at
7  * https://www.openssl.org/source/license.html
8  */
9 
10 #include "testutil.h"
11 #include "helpers/ssltestlib.h"
12 #include <openssl/objects.h>
13 
14 #define TEST_true_or_end(a) \
15     if (!TEST_true(a))      \
16         goto end;
17 
18 #define TEST_false_or_end(a) \
19     if (!TEST_false(a))      \
20         goto end;
21 
22 #define SERVER_PREFERENCE 1
23 #define CLIENT_PREFERENCE 0
24 
25 #define WORK_ON_SSL_OBJECT 1
26 #define WORK_ON_CONTEXT 0
27 
28 #define SYNTAX_FAILURE "SYNTAX_FAILURE"
29 #define NEGOTIATION_FAILURE "NEGOTIATION_FAILURE"
30 
31 typedef enum TEST_TYPE {
32     TEST_NEGOTIATION_FAILURE = 0,
33     TEST_NEGOTIATION_SUCCESS = 1,
34     TEST_SYNTAX_FAILURE = 2
35 } TEST_TYPE;
36 
37 typedef enum SERVER_RESPONSE {
38     HRR = 0,
39     INIT = 1,
40     SH = 2
41 } SERVER_RESPONSE;
42 
43 static const char *response_desc[] = {
44     "HRR",
45     "INIT",
46     "SH",
47 };
48 
49 static char *cert = NULL;
50 static char *privkey = NULL;
51 
52 struct tls13groupselection_test_st {
53     const char *client_groups;
54     const char *server_groups;
55     const int preference;
56     const char *expected_group;
57     const enum SERVER_RESPONSE expected_server_response;
58 };
59 
60 static const struct tls13groupselection_test_st tls13groupselection_tests[] = {
61 
62     /*
63      * (A) Test with no explicit key share (backward compatibility)
64      * Key share is implicitly sent for first client group
65      * Test (implicitly) that the key share group is used
66      */
67     { "secp384r1:secp521r1:X25519:prime256v1:X448", /* test 0 */
68         "X25519:secp521r1:secp384r1:prime256v1:X448",
69         CLIENT_PREFERENCE,
70         "secp384r1", SH },
71     { "secp521r1:secp384r1:X25519:prime256v1:X448", /* test 1 */
72         "X25519:secp521r1:secp384r1:prime256v1:X448",
73         SERVER_PREFERENCE,
74         "secp521r1", SH },
75 
76     /*
77      * (B) No explicit key share test (backward compatibility)
78      * Key share is implicitly sent for first client group
79      * Check HRR if server does not support key share group
80      */
81     { "secp521r1:secp384r1:X25519:prime256v1:X448", /* test 2 */
82         "X25519:secp384r1:prime256v1",
83         CLIENT_PREFERENCE,
84         "secp384r1", HRR },
85     { "secp521r1:secp384r1:X25519:prime256v1:X448", /* test 3 */
86         "X25519:secp384r1:prime256v1",
87         SERVER_PREFERENCE,
88         "x25519", HRR },
89 
90     /*
91      * (C) Explicit key shares, SH tests
92      * Test key share selection as function of client-/server-preference
93      * Test (implicitly) that multiple key shares are generated
94      * Test (implicitly) that multiple tuples don't influence the client
95      * Test (implicitly) that key share prefix doesn't influence the server
96      */
97     { "secp521r1:secp384r1:*X25519/*prime256v1:X448", /* test 4 */
98         "secp521r1:*prime256v1:X25519:X448",
99         CLIENT_PREFERENCE,
100         "x25519", SH },
101     { "secp521r1:secp384r1:*X25519/*prime256v1:X448", /* test 5 */
102         "secp521r1:*prime256v1:X25519:X448",
103         SERVER_PREFERENCE,
104         "secp256r1", SH },
105 
106     /*
107      * (D) Explicit key shares, HRR tests
108      * Check that HRR is issued if group in first tuple
109      * is supported but no key share is available for the tuple
110      */
111     { "secp521r1:secp384r1:*X25519:prime256v1:*X448", /* test 6 */
112         "secp384r1:secp521r1:prime256v1/X25519:X448",
113         CLIENT_PREFERENCE,
114         "secp521r1", HRR },
115     { "secp521r1:secp384r1:*X25519:prime256v1:*X448", /* test 7 */
116         "secp384r1:secp521r1:prime256v1/X25519:X448",
117         SERVER_PREFERENCE,
118         "secp384r1", HRR },
119 
120     /*
121      * (E) Multiple tuples tests, client without tuple delimiters
122      * Check that second tuple is evaluated if there isn't any match
123      * first tuple
124      */
125     { "*X25519:prime256v1:*X448", /* test 8 */
126         "secp521r1:secp384r1/X448:X25519",
127         CLIENT_PREFERENCE,
128         "x25519", SH },
129     { "*X25519:prime256v1:*X448", /* test 9 */
130         "secp521r1:secp384r1/X448:X25519",
131         SERVER_PREFERENCE,
132         "x448", SH },
133 
134     /* (F) Check that '?' will ignore unknown group but use known group */
135     { "*X25519:?unknown_group_123:prime256v1:*X448", /* test 10 */
136         "secp521r1:secp384r1/X448:?unknown_group_456:?X25519",
137         CLIENT_PREFERENCE,
138         "x25519", SH },
139     { "*X25519:prime256v1:*X448:?*unknown_group_789", /* test 11 */
140         "secp521r1:secp384r1/?X448:?unknown_group_456:X25519",
141         SERVER_PREFERENCE,
142         "x448", SH },
143 
144     /*
145      * (G) Check full backward compatibility (= don't explicitly set any groups)
146      */
147     { NULL, /* test 12 */
148         NULL,
149         CLIENT_PREFERENCE,
150 #ifndef OPENSSL_NO_ML_KEM
151         "X25519MLKEM768", SH
152 #else
153         "x25519", SH
154 #endif
155     },
156     { NULL, /* test 13 */
157         NULL,
158         SERVER_PREFERENCE,
159 #ifndef OPENSSL_NO_ML_KEM
160         "X25519MLKEM768", SH
161 #else
162         "x25519", SH
163 #endif
164     },
165 
166     /*
167      * (H) Check that removal of group is 'active'
168      */
169     { "*X25519:*X448", /* test 14 */
170         "secp521r1:X25519:prime256v1:-X25519:secp384r1/X448",
171         CLIENT_PREFERENCE,
172         "x448", SH },
173     { "*X25519:*X448", /* test 15 */
174         "secp521r1:X25519:prime256v1:-X25519:secp384r1/X448",
175         SERVER_PREFERENCE,
176         "x448", SH },
177     { "*X25519:prime256v1:*X448", /* test 16 */
178         "X25519:prime256v1/X448:-X25519",
179         CLIENT_PREFERENCE,
180         "secp256r1", HRR },
181     { "*X25519:prime256v1:*X448", /* test 17 */
182         "X25519:prime256v1/X448:-X25519",
183         SERVER_PREFERENCE,
184         "secp256r1", HRR },
185     /*
186      * (I) Check handling of the "DEFAULT" 'pseudo group name'
187      */
188     { "*X25519:DEFAULT:-prime256v1:-X448", /* test 18 */
189         "DEFAULT:-X25519:-?X25519MLKEM768",
190         CLIENT_PREFERENCE,
191         "secp384r1", HRR },
192     { "*X25519:DEFAULT:-prime256v1:-X448", /* test 19 */
193         "DEFAULT:-X25519:-?X25519MLKEM768",
194         SERVER_PREFERENCE,
195         "secp384r1", HRR },
196     /*
197      * (J) Deduplication check
198      */
199     { "secp521r1:X25519:prime256v1/X25519:prime256v1/X448", /* test 20 */
200         "secp521r1:X25519:prime256v1/X25519:prime256v1/X448",
201         CLIENT_PREFERENCE,
202         "secp521r1", SH },
203     { "secp521r1:X25519:prime256v1/X25519:prime256v1/X448", /* test 21 */
204         "secp521r1:X25519:prime256v1/X25519:prime256v1/X448",
205         SERVER_PREFERENCE,
206         "secp521r1", SH },
207     /*
208      * (K) Check group removal when first entry requested a keyshare
209      */
210     { "*X25519:*prime256v1:-X25519", /* test 22 */
211         "X25519:prime256v1",
212         CLIENT_PREFERENCE,
213         "secp256r1", SH },
214     /*
215      * (L) Syntax errors
216      */
217     { "*X25519:*prime256v1:NOTVALID", /* test 23 */
218         "",
219         CLIENT_PREFERENCE,
220         SYNTAX_FAILURE },
221     { "X25519//prime256v1", /* test 24 */
222         "",
223         CLIENT_PREFERENCE,
224         SYNTAX_FAILURE },
225     { "**X25519:*prime256v1", /* test 25 */
226         "",
227         CLIENT_PREFERENCE,
228         SYNTAX_FAILURE },
229     { "*X25519:*secp256r1:*X448:*secp521r1:*secp384r1", /* test 26 */
230         "",
231         CLIENT_PREFERENCE,
232         SYNTAX_FAILURE },
233     { "*X25519:*secp256r1:?:*secp521r1", /* test 27 */
234         "",
235         CLIENT_PREFERENCE,
236         SYNTAX_FAILURE },
237     { "*X25519:*secp256r1::secp521r1", /* test 28 */
238         "",
239         CLIENT_PREFERENCE,
240         SYNTAX_FAILURE },
241     { ":*secp256r1:secp521r1", /* test 29 */
242         "",
243         CLIENT_PREFERENCE,
244         SYNTAX_FAILURE },
245     { "*secp256r1:secp521r1:", /* test 30 */
246         "",
247         CLIENT_PREFERENCE,
248         SYNTAX_FAILURE },
249     { "/secp256r1/secp521r1", /* test 31 */
250         "",
251         CLIENT_PREFERENCE,
252         SYNTAX_FAILURE },
253     { "secp256r1/secp521r1/", /* test 32 */
254         "",
255         CLIENT_PREFERENCE,
256         SYNTAX_FAILURE },
257     { "X25519:??secp256r1:X448", /* test 33 */
258         "",
259         CLIENT_PREFERENCE,
260         SYNTAX_FAILURE },
261     { "X25519:secp256r1:**X448", /* test 34 */
262         "",
263         CLIENT_PREFERENCE,
264         SYNTAX_FAILURE },
265     { "--X25519:secp256r1:X448", /* test 35 */
266         "",
267         CLIENT_PREFERENCE,
268         SYNTAX_FAILURE },
269     { "-DEFAULT", /* test 36 */
270         "",
271         CLIENT_PREFERENCE,
272         SYNTAX_FAILURE },
273     { "?DEFAULT", /* test 37 */
274         "",
275         CLIENT_PREFERENCE,
276         SYNTAX_FAILURE },
277     /*
278      * Negotiation Failures
279      * No overlapping groups between client and server
280      */
281     /* test 38 remove all groups */
282     { "X25519:secp256r1:X448:secp521r1:-X448:-secp256r1:-X25519:-secp521r1",
283         "",
284         CLIENT_PREFERENCE,
285         NEGOTIATION_FAILURE, INIT },
286     { "secp384r1:secp521r1:X25519", /* test 39 */
287         "prime256v1:X448",
288         CLIENT_PREFERENCE,
289         NEGOTIATION_FAILURE, INIT },
290     { "secp521r1:secp384r1:X25519", /* test 40 */
291         "prime256v1:X448",
292         SERVER_PREFERENCE,
293         NEGOTIATION_FAILURE, INIT },
294     /*
295      * These are allowed
296      * "X25519/prime256v1:-X448", "X25519:-*X25519:*prime256v1, "*DEFAULT"
297      */
298     /*
299      * Tests to show that spaces between tuples are allowed
300      */
301     { "secp521r1:X25519 / prime256v1/X25519 / prime256v1/X448", /* test 41 */
302         "secp521r1:X25519 / prime256v1/X25519 / prime256v1/X448",
303         CLIENT_PREFERENCE,
304         "secp521r1", SH },
305     { "secp521r1 / prime256v1:X25519 / prime256v1/X448", /* test 42 */
306         "secp521r1 / prime256v1:X25519 / prime256v1/X448",
307         SERVER_PREFERENCE,
308         "secp521r1", SH },
309     /*
310      * Not a syntax error, but invalid because brainpoolP256r1 is the only
311      * key share and is not valid in TLSv1.3
312      */
313     { "*brainpoolP256r1:X25519", /* test 43 */
314         "X25519",
315         SERVER_PREFERENCE,
316         NEGOTIATION_FAILURE, INIT },
317 
318     /* DEFAULT retains tuple structure */
319     { "*X25519:secp256r1",
320         "secp256r1:DEFAULT", /* test 44 */
321         SERVER_PREFERENCE,
322         "secp256r1", HRR },
323 #ifndef OPENSSL_NO_DH
324     { "*ffdhe2048:secp256r1",
325         "DEFAULT:ffdhe4096", /* test 45 */
326         CLIENT_PREFERENCE,
327         "secp256r1", HRR },
328     { "x25519:ffdhe2048:*ffdhe4096",
329         "DEFAULT:ffdhe4096", /* test 46 */
330         SERVER_PREFERENCE,
331         "x25519", HRR },
332 #endif
333 };
334 
server_response_check_cb(int write_p,int version,int content_type,const void * buf,size_t len,SSL * ssl,void * arg)335 static void server_response_check_cb(int write_p, int version,
336     int content_type, const void *buf,
337     size_t len, SSL *ssl, void *arg)
338 {
339     /* Cast arg to SERVER_RESPONSE */
340     enum SERVER_RESPONSE *server_response = (enum SERVER_RESPONSE *)arg;
341     /* Prepare check for HRR */
342     const uint8_t *incoming_random = (uint8_t *)buf + 6;
343     const uint8_t magic_HRR_random[32] = {
344         0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11,
345         0xBE, 0x1D, 0x8C, 0x02, 0x1E, 0x65, 0xB8, 0x91,
346         0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E,
347         0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C
348     };
349 
350     /* Did a server hello arrive? */
351     if (write_p == 0 && /* Incoming data... */
352         content_type == SSL3_RT_HANDSHAKE && /* carrying a handshake record type ... */
353         version == TLS1_3_VERSION && /* for TLSv1.3 ... */
354         ((uint8_t *)buf)[0] == SSL3_MT_SERVER_HELLO) { /* with message type "ServerHello" */
355         /* Check what it is: SH or HRR (compare the 'random' data field with HRR magic number) */
356         if (memcmp((void *)incoming_random, (void *)magic_HRR_random, 32) == 0)
357             *server_response *= HRR;
358         else
359             *server_response *= SH;
360     }
361 }
362 
test_invalidsyntax(const struct tls13groupselection_test_st * current_test_vector,int ssl_or_ctx)363 static int test_invalidsyntax(const struct tls13groupselection_test_st *current_test_vector,
364     int ssl_or_ctx)
365 {
366     int ok = 0;
367     SSL_CTX *client_ctx = NULL, *server_ctx = NULL;
368     SSL *clientssl = NULL, *serverssl = NULL;
369 
370     if (!TEST_ptr(current_test_vector->client_groups)
371         || !TEST_size_t_ne(strlen(current_test_vector->client_groups), 0))
372         goto end;
373 
374     /* Creation of the contexts */
375     TEST_true_or_end(create_ssl_ctx_pair(NULL, TLS_server_method(),
376         TLS_client_method(),
377         TLS1_VERSION, 0,
378         &server_ctx, &client_ctx,
379         cert, privkey));
380 
381     /* Customization of the contexts */
382     if (ssl_or_ctx == WORK_ON_CONTEXT)
383         TEST_false_or_end(SSL_CTX_set1_groups_list(client_ctx,
384             current_test_vector->client_groups));
385     /* Creation of the SSL objects */
386     TEST_true_or_end(create_ssl_objects(server_ctx, client_ctx,
387         &serverssl, &clientssl,
388         NULL, NULL));
389 
390     /* Customization of the SSL objects */
391     if (ssl_or_ctx == WORK_ON_SSL_OBJECT)
392         TEST_false_or_end(SSL_set1_groups_list(clientssl, current_test_vector->client_groups));
393 
394     ok = 1;
395 
396 end:
397     SSL_free(serverssl);
398     SSL_free(clientssl);
399     SSL_CTX_free(server_ctx);
400     SSL_CTX_free(client_ctx);
401     return ok;
402 }
403 
test_groupnegotiation(const struct tls13groupselection_test_st * current_test_vector,int ssl_or_ctx,TEST_TYPE test_type)404 static int test_groupnegotiation(const struct tls13groupselection_test_st *current_test_vector,
405     int ssl_or_ctx, TEST_TYPE test_type)
406 {
407     int ok = 0;
408     int negotiated_group_client = 0;
409     int negotiated_group_server = 0;
410     const char *group_name_client;
411     SSL_CTX *client_ctx = NULL, *server_ctx = NULL;
412     SSL *clientssl = NULL, *serverssl = NULL;
413     enum SERVER_RESPONSE server_response;
414 
415     /* Creation of the contexts */
416     TEST_true_or_end(create_ssl_ctx_pair(NULL, TLS_server_method(),
417         TLS_client_method(),
418         TLS1_VERSION, 0,
419         &server_ctx, &client_ctx,
420         cert, privkey));
421 
422     /* Customization of the contexts */
423     if (ssl_or_ctx == WORK_ON_CONTEXT) {
424         if (current_test_vector->client_groups != NULL) {
425             TEST_true_or_end(SSL_CTX_set1_groups_list(client_ctx,
426                 current_test_vector->client_groups));
427         }
428         if (current_test_vector->server_groups != NULL) {
429             TEST_true_or_end(SSL_CTX_set1_groups_list(server_ctx,
430                 current_test_vector->server_groups));
431         }
432         TEST_true_or_end(SSL_CTX_set_min_proto_version(client_ctx, TLS1_3_VERSION));
433         TEST_true_or_end(SSL_CTX_set_min_proto_version(server_ctx, TLS1_3_VERSION));
434         if (current_test_vector->preference == SERVER_PREFERENCE)
435             SSL_CTX_set_options(server_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
436     }
437     /* Creation of the SSL objects */
438     if (!TEST_true(create_ssl_objects(server_ctx, client_ctx,
439             &serverssl, &clientssl,
440             NULL, NULL)))
441         goto end;
442 
443     /* Customization of the SSL objects */
444     if (ssl_or_ctx == WORK_ON_SSL_OBJECT) {
445         if (current_test_vector->client_groups != NULL)
446             TEST_true_or_end(SSL_set1_groups_list(clientssl, current_test_vector->client_groups));
447 
448         if (current_test_vector->server_groups != NULL)
449             TEST_true_or_end(SSL_set1_groups_list(serverssl, current_test_vector->server_groups));
450 
451         TEST_true_or_end(SSL_set_min_proto_version(clientssl, TLS1_3_VERSION));
452         TEST_true_or_end(SSL_set_min_proto_version(serverssl, TLS1_3_VERSION));
453 
454         if (current_test_vector->preference == SERVER_PREFERENCE)
455             SSL_set_options(serverssl, SSL_OP_CIPHER_SERVER_PREFERENCE);
456     }
457 
458     /* We set the message callback on the client side (which checks SH/HRR) */
459     server_response = INIT; /* Variable to hold server response info */
460     SSL_set_msg_callback_arg(clientssl, &server_response); /* add it to the callback */
461     SSL_set_msg_callback(clientssl, server_response_check_cb); /* and activate callback */
462 
463     /* Creating a test connection */
464     if (test_type == TEST_NEGOTIATION_SUCCESS) {
465         TEST_true_or_end(create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE));
466 
467         /*
468          * Checking that the negotiated group matches our expectation
469          * and must be identical on server and client
470          * and must be expected SH or HRR
471          */
472         negotiated_group_client = SSL_get_negotiated_group(clientssl);
473         negotiated_group_server = SSL_get_negotiated_group(serverssl);
474         group_name_client = SSL_group_to_name(clientssl, negotiated_group_client);
475         if (!TEST_int_eq(negotiated_group_client, negotiated_group_server))
476             goto end;
477         if (!TEST_str_eq(response_desc[current_test_vector->expected_server_response],
478                 response_desc[server_response]))
479             goto end;
480         if (TEST_str_eq(group_name_client, current_test_vector->expected_group))
481             ok = 1;
482     } else {
483         TEST_false_or_end(create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE));
484         if (test_type == TEST_NEGOTIATION_FAILURE
485             && !TEST_str_eq(response_desc[current_test_vector->expected_server_response],
486                 response_desc[server_response]))
487             goto end;
488         ok = 1;
489     }
490 
491 end:
492     SSL_free(serverssl);
493     SSL_free(clientssl);
494     SSL_CTX_free(server_ctx);
495     SSL_CTX_free(client_ctx);
496     return ok;
497 }
498 
tls13groupselection_test(int i)499 static int tls13groupselection_test(int i)
500 {
501     int testresult = 1; /* Assume the test will succeed */
502     int res = 0;
503     TEST_TYPE test_type = TEST_NEGOTIATION_SUCCESS;
504 
505     /*
506      * Call the code under test, once such that the ssl object is used and
507      * once such that the ctx is used. If any of the tests fail (= return 0),
508      * the end result will be 0 thanks to multiplication
509      */
510     TEST_info("==> Running TLSv1.3 test %d", i);
511 
512     if (strncmp(tls13groupselection_tests[i].expected_group,
513             SYNTAX_FAILURE, sizeof(SYNTAX_FAILURE))
514         == 0)
515         test_type = TEST_SYNTAX_FAILURE;
516     else if (strncmp(tls13groupselection_tests[i].expected_group,
517                  NEGOTIATION_FAILURE, sizeof(NEGOTIATION_FAILURE))
518         == 0)
519         test_type = TEST_NEGOTIATION_FAILURE;
520 
521     if (test_type == TEST_SYNTAX_FAILURE)
522         res = test_invalidsyntax(&tls13groupselection_tests[i],
523             WORK_ON_SSL_OBJECT);
524     else
525         res = test_groupnegotiation(&tls13groupselection_tests[i],
526             WORK_ON_SSL_OBJECT, test_type);
527 
528     if (!res)
529         TEST_error("====> [ERROR] TLSv1.3 test %d with WORK_ON_SSL_OBJECT failed", i);
530     testresult *= res;
531 
532     if (test_type == TEST_SYNTAX_FAILURE)
533         res = test_invalidsyntax(&tls13groupselection_tests[i],
534             WORK_ON_CONTEXT);
535     else
536         res = test_groupnegotiation(&tls13groupselection_tests[i],
537             WORK_ON_CONTEXT, test_type);
538 
539     if (!res)
540         TEST_error("====> [ERROR] TLSv1.3 test %d with WORK_ON_CONTEXT failed", i);
541     testresult *= res;
542 
543     return testresult;
544 }
545 
setup_tests(void)546 int setup_tests(void)
547 {
548     if (!TEST_ptr(cert = test_get_argument(0))
549         || !TEST_ptr(privkey = test_get_argument(1)))
550         return 0;
551 
552     ADD_ALL_TESTS(tls13groupselection_test, OSSL_NELEM(tls13groupselection_tests));
553     return 1;
554 }
555