Commit e23b33a995 for openssl.org
commit e23b33a9956b45b61bd447169ddd3841ca9dfd61
Author: Joachim Vandersmissen <git@jvdsn.com>
Date: Mon Dec 15 17:46:12 2025 +1100
Support FFDHE groups in tls1_shared_group
Update tls1_shared_group to allow filtering for FFDHE and/or ECDHE
groups. This will be used for implementing RFC 7919 groups support in
the TLS 1.2 server. As defined in RFC 7919:
Codepoints in the "Supported Groups Registry" with a high byte of
0x01 (that is, between 256 and 511, inclusive) are set aside for
FFDHE groups
Reviewed-by: Viktor Dukhovni <viktor@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Saša NedvÄ›dický <sashan@openssl.org>
MergeDate: Thu Feb 5 09:09:38 2026
(Merged from https://github.com/openssl/openssl/pull/24551)
diff --git a/include/internal/tlsgroups.h b/include/internal/tlsgroups.h
index 1fd41993ea..7c1eb26e3e 100644
--- a/include/internal/tlsgroups.h
+++ b/include/internal/tlsgroups.h
@@ -63,4 +63,23 @@
#define OSSL_TLS_GROUP_ID_X25519MLKEM768 0x11EC
#define OSSL_TLS_GROUP_ID_SecP384r1MLKEM1024 0x11ED
+/*
+ * RFC 7919: "Codepoints ... between 256 and 511, inclusive ... are set aside
+ * for FFDHE groups"
+ */
+#define OSSL_TLS_GROUP_ID_FFDHE_START 0x0100 /* inclusive */
+#define OSSL_TLS_GROUP_ID_FFDHE_END 0x01FF /* inclusive */
+
+static ossl_inline int is_ecdhe_group(const uint16_t group_id)
+{
+ /* This includes the usual EC groups, and also ECX, GOST ... */
+ return group_id < OSSL_TLS_GROUP_ID_FFDHE_START;
+}
+
+static ossl_inline int is_ffdhe_group(const uint16_t group_id)
+{
+ return group_id >= OSSL_TLS_GROUP_ID_FFDHE_START
+ && group_id <= OSSL_TLS_GROUP_ID_FFDHE_END;
+}
+
#endif
diff --git a/providers/common/capabilities.c b/providers/common/capabilities.c
index eb96627a67..e1521dc9d8 100644
--- a/providers/common/capabilities.c
+++ b/providers/common/capabilities.c
@@ -80,11 +80,11 @@ static const TLS_GROUP_CONSTANTS group_list[] = {
/* 30 */ { OSSL_TLS_GROUP_ID_brainpoolP256r1_tls13, 128, TLS1_3_VERSION, 0, -1, -1, 0 },
/* 31 */ { OSSL_TLS_GROUP_ID_brainpoolP384r1_tls13, 192, TLS1_3_VERSION, 0, -1, -1, 0 },
/* 32 */ { OSSL_TLS_GROUP_ID_brainpoolP512r1_tls13, 256, TLS1_3_VERSION, 0, -1, -1, 0 },
- /* 33 */ { OSSL_TLS_GROUP_ID_ffdhe2048, 112, TLS1_3_VERSION, 0, -1, -1, 0 },
- /* 34 */ { OSSL_TLS_GROUP_ID_ffdhe3072, 128, TLS1_3_VERSION, 0, -1, -1, 0 },
- /* 35 */ { OSSL_TLS_GROUP_ID_ffdhe4096, 128, TLS1_3_VERSION, 0, -1, -1, 0 },
- /* 36 */ { OSSL_TLS_GROUP_ID_ffdhe6144, 128, TLS1_3_VERSION, 0, -1, -1, 0 },
- /* 37 */ { OSSL_TLS_GROUP_ID_ffdhe8192, 192, TLS1_3_VERSION, 0, -1, -1, 0 },
+ /* 33 */ { OSSL_TLS_GROUP_ID_ffdhe2048, 112, TLS1_VERSION, 0, -1, -1, 0 },
+ /* 34 */ { OSSL_TLS_GROUP_ID_ffdhe3072, 128, TLS1_VERSION, 0, -1, -1, 0 },
+ /* 35 */ { OSSL_TLS_GROUP_ID_ffdhe4096, 128, TLS1_VERSION, 0, -1, -1, 0 },
+ /* 36 */ { OSSL_TLS_GROUP_ID_ffdhe6144, 128, TLS1_VERSION, 0, -1, -1, 0 },
+ /* 37 */ { OSSL_TLS_GROUP_ID_ffdhe8192, 192, TLS1_VERSION, 0, -1, -1, 0 },
/* 38 */ { OSSL_TLS_GROUP_ID_mlkem512, ML_KEM_512_SECBITS, TLS1_3_VERSION, 0, -1, -1, 1 },
/* 39 */ { OSSL_TLS_GROUP_ID_mlkem768, ML_KEM_768_SECBITS, TLS1_3_VERSION, 0, -1, -1, 1 },
/* 40 */ { OSSL_TLS_GROUP_ID_mlkem1024, ML_KEM_1024_SECBITS, TLS1_3_VERSION, 0, -1, -1, 1 },
diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c
index 872812e4f4..fc3f5892fd 100644
--- a/ssl/s3_lib.c
+++ b/ssl/s3_lib.c
@@ -4214,7 +4214,7 @@ long ssl3_ctrl(SSL *s, int cmd, long larg, void *parg)
parg);
case SSL_CTRL_GET_SHARED_GROUP: {
- uint16_t id = tls1_shared_group(sc, larg);
+ uint16_t id = tls1_shared_group(sc, larg, TLS1_GROUPS_ALL_GROUPS);
if (larg != -1)
return tls1_group_id2nid(id, 1);
diff --git a/ssl/ssl_local.h b/ssl/ssl_local.h
index 606fded111..382a6cc902 100644
--- a/ssl/ssl_local.h
+++ b/ssl/ssl_local.h
@@ -2772,6 +2772,12 @@ __owur int ssl_check_srvr_ecc_cert_and_alg(X509 *x, SSL_CONNECTION *s);
SSL_COMP *ssl3_comp_find(STACK_OF(SSL_COMP) *sk, int n);
+#define TLS1_GROUPS_RETURN_NUMBER -1
+#define TLS1_GROUPS_RETURN_TMP_ID -2
+#define TLS1_GROUPS_FFDHE_GROUPS 0
+#define TLS1_GROUPS_NON_FFDHE_GROUPS 1
+#define TLS1_GROUPS_ALL_GROUPS 2
+
__owur const TLS_GROUP_INFO *tls1_group_id_lookup(SSL_CTX *ctx, uint16_t curve_id);
__owur const char *tls1_group_id2name(SSL_CTX *ctx, uint16_t group_id);
__owur int tls1_group_id2nid(uint16_t group_id, int include_unknown);
@@ -2783,7 +2789,7 @@ __owur int tls1_get0_implemented_groups(int min_proto_version,
TLS_GROUP_INFO *grps,
size_t num, long all,
STACK_OF(OPENSSL_CSTRING) *out);
-__owur uint16_t tls1_shared_group(SSL_CONNECTION *s, int nmatch);
+__owur uint16_t tls1_shared_group(SSL_CONNECTION *s, int nmatch, int groups);
__owur int tls1_set_groups(uint16_t **grpext, size_t *grpextlen,
uint16_t **ksext, size_t *ksextlen,
size_t **tplext, size_t *tplextlen,
diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c
index bdb46716ef..b648e79d15 100644
--- a/ssl/statem/statem_srvr.c
+++ b/ssl/statem/statem_srvr.c
@@ -2666,7 +2666,8 @@ CON_FUNC_RETURN tls_construct_server_key_exchange(SSL_CONNECTION *s,
}
/* Get NID of appropriate shared curve */
- curve_id = tls1_shared_group(s, -2);
+ curve_id = tls1_shared_group(s, TLS1_GROUPS_RETURN_TMP_ID,
+ TLS1_GROUPS_NON_FFDHE_GROUPS);
if (curve_id == 0) {
SSLfatal(s, SSL_AD_HANDSHAKE_FAILURE,
SSL_R_UNSUPPORTED_ELLIPTIC_CURVE);
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index 02f5d43055..81bf83d94b 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -1001,11 +1001,18 @@ end:
/*-
* For nmatch >= 0, return the id of the |nmatch|th shared group or 0
* if there is no match.
- * For nmatch == -1, return number of matches
- * For nmatch == -2, return the id of the group to use for
- * a tmp key, or 0 if there is no match.
+ * For nmatch == TLS1_GROUPS_RETURN_NUMBER, return number of matches
+ * For nmatch == TLS1_GROUPS_RETURN_TMP_ID, return the id of the group to use
+ * for a tmp key, or 0 if there is no match.
+ * If groups == TLS1_GROUPS_FFDHE_GROUPS, only shared groups that are FFDHE
+ * groups (i.e., between OSSL_TLS_GROUP_ID_FFDHE_START and
+ * OSSL_TLS_GROUP_ID_FFDHE_END, inclusive) will be included in the search.
+ * If groups == TLS1_GROUPS_NON_FFDHE_GROUPS, only shared groups that are not
+ * FFDHE groups will be included in the search.
+ * If groups == TLS1_GROUPS_ALL_GROUPS, all groups will be included in the
+ * search.
*/
-uint16_t tls1_shared_group(SSL_CONNECTION *s, int nmatch)
+uint16_t tls1_shared_group(SSL_CONNECTION *s, int nmatch, int groups)
{
const uint16_t *pref, *supp;
size_t num_pref, num_supp, i;
@@ -1015,8 +1022,8 @@ uint16_t tls1_shared_group(SSL_CONNECTION *s, int nmatch)
/* Can't do anything on client side */
if (s->server == 0)
return 0;
- if (nmatch == -2) {
- if (tls1_suiteb(s)) {
+ if (nmatch == TLS1_GROUPS_RETURN_TMP_ID) {
+ if (groups != TLS1_GROUPS_FFDHE_GROUPS && tls1_suiteb(s)) {
/*
* For Suite B ciphersuite determines curve: we already know
* these are acceptable due to previous checks.
@@ -1051,6 +1058,8 @@ uint16_t tls1_shared_group(SSL_CONNECTION *s, int nmatch)
int minversion, maxversion;
if (!tls1_in_list(id, supp, num_supp)
+ || (groups == TLS1_GROUPS_NON_FFDHE_GROUPS && is_ffdhe_group(id))
+ || (groups == TLS1_GROUPS_FFDHE_GROUPS && !is_ffdhe_group(id))
|| !tls_group_allowed(s, id, SSL_SECOP_CURVE_SHARED))
continue;
inf = tls1_group_id_lookup(ctx, id);
@@ -1074,7 +1083,7 @@ uint16_t tls1_shared_group(SSL_CONNECTION *s, int nmatch)
return id;
k++;
}
- if (nmatch == -1)
+ if (nmatch == TLS1_GROUPS_RETURN_NUMBER)
return k;
/* Out of range (nmatch > k). */
return 0;
@@ -1907,7 +1916,7 @@ int tls1_check_ec_tmp_key(SSL_CONNECTION *s, unsigned long cid)
{
/* If not Suite B just need a shared group */
if (!tls1_suiteb(s))
- return tls1_shared_group(s, 0) != 0;
+ return tls1_shared_group(s, 0, TLS1_GROUPS_NON_FFDHE_GROUPS) != 0;
/*
* If Suite B, AES128 MUST use P-256 and AES256 MUST use P-384, no other
* curves permitted.