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.