Skip to content

Commit 2157c9d

Browse files
Viktor Dukhovnit8m
authored andcommitted
Fix group tuple handling in DEFAULT expansion
Also fine-tune docs and add tests. Fixes: #30109 Fixes: CVE-2026-2673 Reviewed-by: Matt Caswell <matt@openssl.foundation> Reviewed-by: Paul Dale <paul.dale@oracle.com> Reviewed-by: Tomas Mraz <tomas@openssl.org> MergeDate: Fri Mar 13 12:42:50 2026 (Merged from #30111)
1 parent 0b225a1 commit 2157c9d

5 files changed

Lines changed: 181 additions & 83 deletions

File tree

CHANGES.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,14 @@ OpenSSL 3.6
3131

3232
### Changes between 3.6.1 and 3.6.2 [xx XXX xxxx]
3333

34-
* none yet
34+
* Fixed loss of key agreement group tuple structure when the `DEFAULT` keyword
35+
is used in the server-side configuration of the key-agreement group list.
36+
This could result in accepting a less preferred than intended client
37+
keyshare.
38+
39+
([CVE-2026-2673])
40+
41+
*Viktor Dukhovni*
3542

3643
### Changes between 3.6.0 and 3.6.1 [27 Jan 2026]
3744

@@ -21854,6 +21861,7 @@ ndif
2185421861

2185521862
<!-- Links -->
2185621863

21864+
[CVE-2026-2673]: https://www.openssl.org/news/vulnerabilities.html#CVE-2026-2673
2185721865
[CVE-2026-22796]: https://www.openssl.org/news/vulnerabilities.html#CVE-2026-22796
2185821866
[CVE-2026-22795]: https://www.openssl.org/news/vulnerabilities.html#CVE-2026-22795
2185921867
[CVE-2025-69421]: https://www.openssl.org/news/vulnerabilities.html#CVE-2025-69421

NEWS.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ OpenSSL 3.6
2626

2727
### Major changes between OpenSSL 3.6.1 and OpenSSL 3.6.2 [under development]
2828

29-
* none
29+
* Fixed loss of key agreement group tuple structure when the `DEFAULT` keyword
30+
is used in the server-side configuration of the key-agreement group list.
31+
([CVE-2026-2673])
3032

3133
### Major changes between OpenSSL 3.6.0 and OpenSSL 3.6.1 [27 Jan 2026]
3234

@@ -2032,6 +2034,7 @@ OpenSSL 0.9.x
20322034
* Support for various new platforms
20332035

20342036
<!-- Links -->
2037+
[CVE-2026-2673]: https://www.openssl.org/news/vulnerabilities.html#CVE-2026-2673
20352038
[CVE-2026-22796]: https://www.openssl.org/news/vulnerabilities.html#CVE-2026-22796
20362039
[CVE-2026-22795]: https://www.openssl.org/news/vulnerabilities.html#CVE-2026-22795
20372040
[CVE-2025-69421]: https://www.openssl.org/news/vulnerabilities.html#CVE-2025-69421

doc/man3/SSL_CTX_set1_curves.pod

Lines changed: 86 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,13 @@ SSL_get1_curves, SSL_get_shared_curve, SSL_CTX_get0_implemented_groups
4040

4141
For all of the functions below that set the supported groups there must be at
4242
least one group in the list. A number of these functions identify groups via a
43-
unique integer NID value. However, support for some groups may be added by
44-
external providers. In this case there will be no NID assigned for the group.
43+
unique integer B<NID> value. However, support for some groups may be added by
44+
external providers. In this case there will be no B<NID> assigned for the group.
4545
When setting such groups applications should use the "list" form of these
4646
functions (i.e. SSL_CTX_set1_groups_list() and SSL_set1_groups_list()).
4747

4848
SSL_CTX_set1_groups() sets the supported groups for B<ctx> to B<glistlen>
49-
groups in the array B<glist>. The array consist of all NIDs of supported groups.
49+
groups in the array B<glist>. The array consist of all B<NIDs> of supported groups.
5050
The supported groups for B<TLSv1.3> include:
5151
B<NID_X9_62_prime256v1>,
5252
B<NID_secp384r1>,
@@ -73,20 +73,27 @@ B<SSL_OP_SERVER_PREFERENCE> is set, the order of the elements in the
7373
array determines the selected group. Otherwise, the order is ignored and the
7474
client's order determines the selection.
7575

76-
For a TLS 1.3 server, the groups determine the selected group, but
77-
selection is more complex. A TLS 1.3 client sends both a group list as well as a
78-
predicted subset of groups. Choosing a group outside the predicted subset incurs
79-
an extra roundtrip. However, in some situations, the most preferred group may
80-
not be predicted. OpenSSL considers all supported groups in I<clist> to be comparable
81-
in security and prioritizes avoiding roundtrips above either client or server
82-
preference order. If an application uses an external provider to extend OpenSSL
83-
with, e.g., a post-quantum algorithm, this behavior may allow a network attacker
84-
to downgrade connections to a weaker algorithm. It is therefore recommended
85-
to use SSL_CTX_set1_groups_list() with the ability to specify group tuples.
76+
For a TLS 1.3 server, the groups determine the selected group, but selection is
77+
more complex.
78+
A TLS 1.3 client sends both a group list and predicted keyshares for a subset
79+
of groups.
80+
A server choosing a group outside the client's predicted subset incurs an extra
81+
roundtrip.
82+
However, in some situations, the most preferred group may not be predicted.
83+
84+
When groups are specified via SSL_CTX_set1_groups() as a list of B<NID>
85+
values, OpenSSL considers all supported groups in I<clist> to be comparable in
86+
security and prioritises avoiding roundtrips above either client or server
87+
preference order.
88+
If an application uses an external provider to extend OpenSSL with, e.g., a
89+
post-quantum algorithm, this behavior may allow a network attacker to downgrade
90+
connections to a weaker algorithm.
91+
It is therefore recommended to use SSL_CTX_set1_groups_list() instead, making
92+
it possible to specify group tuples as described below.
8693

8794
SSL_CTX_set1_groups_list() sets the supported groups for B<ctx> to
8895
string I<list>. In contrast to SSL_CTX_set1_groups(), the names of the
89-
groups, rather than their NIDs, are used.
96+
groups, rather than their B<NIDs>, are used.
9097

9198
The commands below list the available groups for TLS 1.2 and TLS 1.3,
9299
respectively:
@@ -102,30 +109,72 @@ The preferred group names are those defined by
102109
L<IANA|https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8>.
103110

104111
The I<list> can be used to define several group tuples of comparable security
105-
levels, and can specify which key shares should be sent by a client.
106-
The specified list elements can optionally be ignored, if not implemented
112+
levels, and can specify which predicted key shares should be sent by a client.
113+
Group tuples are used by OpenSSL TLS servers to decide whether to request a
114+
stronger keyshare than those predicted by sending a Hello Retry Request
115+
(B<HRR>) even if some of the predicted groups are supported.
116+
OpenSSL clients ignore tuple boundaries, and pay attenion only to the overall
117+
order of I<list> elements and which groups are selected as predicted keyshares
118+
as described below.
119+
120+
The specified list elements can optionally be ignored if not implemented
107121
(listing unknown groups otherwise results in error).
108-
It is also possible to specify the built-in default set of groups, and to explicitly
109-
remove a group from that list.
110-
111-
In its simplest form, the string I<list> is just a colon separated list
112-
of group names, for example "P-521:P-384:P-256:X25519:ffdhe2048". The first
113-
group listed will also be used for the B<key_share> sent by a client in a
114-
TLSv1.3 B<ClientHello>. For servers note the discussion above. The list should
115-
be in order of preference with the most preferred group first.
116-
117-
Group tuples of comparable security are defined by separating them from each
118-
other by a tuple separator C</>. Keyshares to be sent by a client are specified
119-
by prepending a C<*> to the group name, while any C<*> will be ignored by a
120-
server. The following string I<list> for example defines three tuples when
121-
used on the server-side, and triggers the generation of three key shares
122-
when used on the client-side: P-521:*P-256/*P-384/*X25519:P-384:ffdhe2048.
123-
124-
If a group name is preceded with the C<?> character, it will be ignored if an
125-
implementation is missing. If a group name is preceded with the C<-> character, it
126-
will be removed from the list of groups if present (including not sending a
127-
key share for this group), ignored otherwise. The pseudo group name
128-
C<DEFAULT> can be used to select the OpenSSL built-in default list of groups.
122+
It is also possible to specify the built-in default set of groups, and to
123+
explicitly remove a group from that list.
124+
125+
In its simplest legacy form, the string I<list> is just a colon separated list
126+
of group names, for example "P-521:P-384:P-256:X25519:ffdhe2048".
127+
The first group listed will in this case be used as the sole predicted
128+
B<key_share> sent by a client in a TLSv1.3 B<ClientHello>.
129+
The list should be in order of preference with the most preferred group first.
130+
131+
A more expressive syntax supports definition of group tuples of comparable
132+
security by separating them from each other with C</> characters.
133+
134+
The predicted keyshares to be sent by clients can be explicitly specified by
135+
adding a C<*> prefix to the associated group name.
136+
These C<*> prefixes are ignored by servers.
137+
138+
If a group name is prefixed with the C<?> character, it will be ignored if an
139+
implementation is missing.
140+
Otherwise, listing an unknown group name will cause a failure to parse the
141+
I<list>.
142+
Note that whether a group is known or not may depend on the OpenSSL version,
143+
how OpenSSL was compiled and/or which providers are loaded.
144+
Make sure you have the correct spelling of the group name and when in doubt
145+
prefix it with a C<?> to handle configurations in which it might nevertheless
146+
be unknown.
147+
148+
If a group name is prefixed with the C<-> character, it will be removed from
149+
the list of groups specified up to that point.
150+
It can be added again if specified later.
151+
Removal of groups that have not been included earlier in the list is silently
152+
ignored.
153+
154+
The pseudo group name C<DEFAULT> can be used to select the OpenSSL built-in
155+
default list of groups.
156+
Prepending one or more groups to C<DEFAULT> using only C<:> separators prepends those
157+
groups to the built-in default list's first tuple.
158+
Additional tuples can be prepended by use of the C</> separator.
159+
Appending a set of groups to C<DEFAULT> using only C<:> separators appends those
160+
groups to the built-in default list's last tuple.
161+
Additional tuples can be appended by use of the C</> separator.
162+
163+
The B<DEFAULT> list selects B<X25519MLKEM768> as one of the predicted keyshares.
164+
In rare cases this can lead to failures or timeouts because the resulting
165+
larger TLS Client Hello message may no longer fit in a single TCP segment and
166+
firewall software may erroneously disrupt the TLS handshake.
167+
If this is an issue or concern, prepending C<?X25519MLKEM768:> without a C<*>
168+
prefix leads to its occurrence in the default list to be ignored as a duplicate,
169+
and along with that also the keyshare prediction.
170+
The group will then only be selected by servers that specifically expect it,
171+
after a Hello Retry Request (HRR).
172+
Servers that specifically prefer B<X25519MLKEM768>, are much less likely to be
173+
found behind problematic firewalls.
174+
175+
The following string I<list> for example defines three tuples when used on the
176+
server-side, and triggers the generation of three key shares when used on the
177+
client-side: P-521:*P-256/*P-384/*X25519:P-384:ffdhe2048.
129178

130179
For a TLS 1.3 client, all the groups in the string I<list> are added to the
131180
supported groups extension of a C<ClientHello>, in the order in which they are listed,

ssl/t1_lib.c

Lines changed: 50 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ static const uint16_t suiteb_curves[] = {
215215

216216
/* Group list string of the built-in pseudo group DEFAULT_SUITE_B */
217217
#define SUITE_B_GROUP_NAME "DEFAULT_SUITE_B"
218-
#define SUITE_B_GROUP_LIST "secp256r1:secp384r1",
218+
#define SUITE_B_GROUP_LIST "?secp256r1:?secp384r1",
219219

220220
struct provider_ctx_data_st {
221221
SSL_CTX *ctx;
@@ -1248,8 +1248,8 @@ typedef struct {
12481248
size_t ksidcnt; /* Number of key shares */
12491249
uint16_t *ksid_arr; /* The IDs of the key share groups (flat list) */
12501250
/* Variable to keep state between execution of callback or helper functions */
1251-
size_t tuple_mode; /* Keeps track whether tuple_cb called from 'the top' or from gid_cb */
1252-
int ignore_unknown_default; /* Flag such that unknown groups for DEFAULT[_XYZ] are ignored */
1251+
int inner; /* Are we expanding a DEFAULT list */
1252+
int first; /* First tuple of possibly nested expansion? */
12531253
} gid_cb_st;
12541254

12551255
/* Forward declaration of tuple callback function */
@@ -1324,16 +1324,16 @@ static int gid_cb(const char *elem, int len, void *arg)
13241324
for (i = 0; i < OSSL_NELEM(default_group_strings); i++) {
13251325
if ((size_t)len == (strlen(default_group_strings[i].list_name))
13261326
&& OPENSSL_strncasecmp(default_group_strings[i].list_name, elem, len) == 0) {
1327+
int saved_first;
1328+
13271329
/*
13281330
* We're asked to insert an entire list of groups from a
13291331
* DEFAULT[_XYZ] 'pseudo group' which we do by
13301332
* recursively calling this function (indirectly via
13311333
* CONF_parse_list and tuple_cb); essentially, we treat a DEFAULT
13321334
* group string like a tuple which is appended to the current tuple
1333-
* rather then starting a new tuple. Variable tuple_mode is the flag which
1334-
* controls append tuple vs start new tuple.
1335+
* rather then starting a new tuple.
13351336
*/
1336-
13371337
if (ignore_unknown || remove_group)
13381338
return -1; /* removal or ignore not allowed here -> syntax error */
13391339

@@ -1354,15 +1354,17 @@ static int gid_cb(const char *elem, int len, void *arg)
13541354
default_group_strings[i].group_string,
13551355
strlen(default_group_strings[i].group_string));
13561356
restored_default_group_string[strlen(default_group_strings[i].group_string) + restored_prefix_index] = '\0';
1357-
/* We execute the recursive call */
1358-
garg->ignore_unknown_default = 1; /* We ignore unknown groups for DEFAULT_XYZ */
1359-
/* we enforce group mode (= append tuple) for DEFAULT_XYZ group lists */
1360-
garg->tuple_mode = 0;
1361-
/* We use the tuple_cb callback to process the pseudo group tuple */
1357+
/*
1358+
* Append first tuple of result to current tuple, and don't
1359+
* terminate the last tuple until we return to a top-level
1360+
* tuple_cb.
1361+
*/
1362+
saved_first = garg->first;
1363+
garg->inner = garg->first = 1;
13621364
retval = CONF_parse_list(restored_default_group_string,
13631365
TUPLE_DELIMITER_CHARACTER, 1, tuple_cb, garg);
1364-
garg->tuple_mode = 1; /* next call to tuple_cb will again start new tuple */
1365-
garg->ignore_unknown_default = 0; /* reset to original value */
1366+
garg->inner = 0;
1367+
garg->first = saved_first;
13661368
/* We don't need the \0-terminated string anymore */
13671369
OPENSSL_free(restored_default_group_string);
13681370

@@ -1382,9 +1384,6 @@ static int gid_cb(const char *elem, int len, void *arg)
13821384
if (len == 0)
13831385
return -1; /* Seems we have prefxes without a group name -> syntax error */
13841386

1385-
if (garg->ignore_unknown_default == 1) /* Always ignore unknown groups for DEFAULT[_XYZ] */
1386-
ignore_unknown = 1;
1387-
13881387
/* Memory management in case more groups are present compared to initial allocation */
13891388
if (garg->gidcnt == garg->gidmax) {
13901389
uint16_t *tmp = OPENSSL_realloc_array(garg->gid_arr,
@@ -1520,7 +1519,7 @@ static int gid_cb(const char *elem, int len, void *arg)
15201519
/* and update the book keeping for the number of groups in current tuple */
15211520
garg->tuplcnt_arr[garg->tplcnt]++;
15221521

1523-
/* We memorize if needed that we want to add a key share for the current group */
1522+
/* We want to add a key share for the current group */
15241523
if (add_keyshare)
15251524
garg->ksid_arr[garg->ksidcnt++] = gid;
15261525
}
@@ -1529,6 +1528,35 @@ static int gid_cb(const char *elem, int len, void *arg)
15291528
return retval;
15301529
}
15311530

1531+
static int grow_tuples(gid_cb_st *garg)
1532+
{
1533+
if (garg->tplcnt == garg->tplmax) {
1534+
size_t newcnt = garg->tplmax + GROUPLIST_INCREMENT;
1535+
size_t *tmp = OPENSSL_realloc_array(garg->tuplcnt_arr,
1536+
newcnt, sizeof(*garg->tuplcnt_arr));
1537+
1538+
if (tmp == NULL)
1539+
return 0;
1540+
1541+
garg->tplmax = newcnt;
1542+
garg->tuplcnt_arr = tmp;
1543+
}
1544+
return 1;
1545+
}
1546+
1547+
static int close_tuple(gid_cb_st *garg)
1548+
{
1549+
size_t gidcnt = garg->tuplcnt_arr[garg->tplcnt];
1550+
1551+
if (gidcnt == 0)
1552+
return 1;
1553+
if (!grow_tuples(garg))
1554+
return 0;
1555+
1556+
garg->tuplcnt_arr[++garg->tplcnt] = 0;
1557+
return 1;
1558+
}
1559+
15321560
/* Extract and process a tuple of groups */
15331561
static int tuple_cb(const char *tuple, int len, void *arg)
15341562
{
@@ -1542,17 +1570,9 @@ static int tuple_cb(const char *tuple, int len, void *arg)
15421570
return 0;
15431571
}
15441572

1545-
/* Memory management for tuples */
1546-
if (garg->tplcnt == garg->tplmax) {
1547-
size_t *tmp = OPENSSL_realloc_array(garg->tuplcnt_arr,
1548-
garg->tplmax + GROUPLIST_INCREMENT,
1549-
sizeof(*garg->tuplcnt_arr));
1550-
1551-
if (tmp == NULL)
1552-
return 0;
1553-
garg->tplmax += GROUPLIST_INCREMENT;
1554-
garg->tuplcnt_arr = tmp;
1555-
}
1573+
if (garg->inner && !garg->first && !close_tuple(garg))
1574+
return 0;
1575+
garg->first = 0;
15561576

15571577
/* Convert to \0-terminated string */
15581578
restored_tuple_string = OPENSSL_malloc(len + 1 /* \0 */);
@@ -1567,15 +1587,8 @@ static int tuple_cb(const char *tuple, int len, void *arg)
15671587
/* We don't need the \o-terminated string anymore */
15681588
OPENSSL_free(restored_tuple_string);
15691589

1570-
if (garg->tuplcnt_arr[garg->tplcnt] > 0) { /* Some valid groups are present in current tuple... */
1571-
if (garg->tuple_mode) {
1572-
/* We 'close' the tuple */
1573-
garg->tplcnt++;
1574-
garg->tuplcnt_arr[garg->tplcnt] = 0; /* Next tuple is initialized to be empty */
1575-
garg->tuple_mode = 1; /* next call will start a tuple (unless overridden in gid_cb) */
1576-
}
1577-
}
1578-
1590+
if (!garg->inner && !close_tuple(garg))
1591+
return 0;
15791592
return retval;
15801593
}
15811594

@@ -1606,8 +1619,6 @@ int tls1_set_groups_list(SSL_CTX *ctx,
16061619
}
16071620

16081621
memset(&gcb, 0, sizeof(gcb));
1609-
gcb.tuple_mode = 1; /* We prepare to collect the first tuple */
1610-
gcb.ignore_unknown_default = 0;
16111622
gcb.gidmax = GROUPLIST_INCREMENT;
16121623
gcb.tplmax = GROUPLIST_INCREMENT;
16131624
gcb.ksidmax = GROUPLIST_INCREMENT;

0 commit comments

Comments
 (0)