]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
Implement first step of RFC7919 in TLS 1.2 server
authorJoachim Vandersmissen <git@jvdsn.com>
Mon, 15 Dec 2025 07:09:49 +0000 (18:09 +1100)
committerAlexandr Nedvedicky <sashan@openssl.org>
Thu, 5 Feb 2026 09:09:18 +0000 (10:09 +0100)
RFC 7919 states:
    If a compatible TLS server receives a Supported Groups extension from
    a client that includes any FFDHE group (i.e., any codepoint between
    256 and 511, inclusive, even if unknown to the server), and if none
    of the client-proposed FFDHE groups are known and acceptable to the
    server, then the server MUST NOT select an FFDHE cipher suite.

We implement this behavior by adding a new function that checks this
condition as its inverse: only select FFDHE cipher suites if at least
one of the client-proposed FFDHE groups is known and acceptable, or
if the client did _not_ send any FFDHE groups.

Also add a test to verify two possible outcomes:
1) The client proposes FFDHE and non-FFDHE ciphersuites -> the server
will select a non-FFDHE ciphersuite.
2) The client only proposes FFDHE ciphersuites -> the server will end
the connection.

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:40 2026
(Merged from https://github.com/openssl/openssl/pull/24551)

ssl/s3_lib.c
ssl/ssl_local.h
ssl/t1_lib.c
test/sslapitest.c

index fc3f5892fd201b38fab4cd215a4e6ab261b0593f..2990e4014d733c4fb1a4a0cb78806632a5485ddd 100644 (file)
@@ -4892,11 +4892,18 @@ const SSL_CIPHER *ssl3_choose_cipher(SSL_CONNECTION *s, STACK_OF(SSL_CIPHER) *cl
                 "%d:[%08lX:%08lX:%08lX:%08lX]%p:%s\n",
                 ok, alg_k, alg_a, mask_k, mask_a, (void *)c, c->name);
 
+            /*
+             * if we are considering a DHE cipher suite that uses an ephemeral
+             * FFDHE key check it
+             */
+            if (alg_k & (SSL_kDHE | SSL_kDHEPSK))
+                ok = ok && tls1_check_ffdhe_tmp_key(s, c->id);
+
             /*
              * if we are considering an ECC cipher suite that uses an ephemeral
              * EC key check it
              */
-            if (alg_k & SSL_kECDHE)
+            if (alg_k & (SSL_kECDHE | SSL_kECDHEPSK))
                 ok = ok && tls1_check_ec_tmp_key(s, c->id);
 
             if (!ok)
index 382a6cc902303f2889b7aa6f283521bb15ecf9a0..a50ee6e1dc71b037aa77b89af8ab6b6642486561 100644 (file)
@@ -2805,6 +2805,7 @@ __owur int tls_valid_group(SSL_CONNECTION *s, uint16_t group_id, int minversion,
 __owur EVP_PKEY *ssl_generate_param_group(SSL_CONNECTION *s, uint16_t id);
 void tls1_get_formatlist(SSL_CONNECTION *s, const unsigned char **pformats,
     size_t *num_formats);
+__owur int tls1_check_ffdhe_tmp_key(SSL_CONNECTION *s, unsigned long id);
 __owur int tls1_check_ec_tmp_key(SSL_CONNECTION *s, unsigned long id);
 
 __owur int tls_group_allowed(SSL_CONNECTION *s, uint16_t curve, int op);
index 81bf83d94b49426496620b741223c1661eeb1666..b0bfc92e32282545c9420aa1435a1070588e412d 100644 (file)
@@ -1902,6 +1902,46 @@ static int tls1_check_cert_param(SSL_CONNECTION *s, X509 *x, int check_ee_md)
     return 1;
 }
 
+/*
+ * tls1_check_ffdhe_tmp_key - Check FFDHE temporary key compatibility
+ * @s: SSL connection
+ * @cid: Cipher ID we're considering using
+ *
+ * Checks that the kDHE cipher suite we're considering using
+ * is compatible with the client extensions.
+ *
+ * Returns 0 when the cipher can't be used or 1 when it can.
+ */
+int tls1_check_ffdhe_tmp_key(SSL_CONNECTION *s, unsigned long cid)
+{
+    const uint16_t *peer_groups;
+    size_t num_peer_groups;
+
+    /* If we have a shared FFDHE group, we can certainly use it. */
+    if (tls1_shared_group(s, 0, TLS1_GROUPS_FFDHE_GROUPS) != 0)
+        return 1;
+
+    /*
+     * Otherwise, we follow RFC 7919:
+     *     If a compatible TLS server receives a Supported Groups extension from
+     *     a client that includes any FFDHE group (i.e., any codepoint between
+     *     256 and 511, inclusive, even if unknown to the server), and if none
+     *     of the client-proposed FFDHE groups are known and acceptable to the
+     *     server, then the server MUST NOT select an FFDHE cipher suite.
+     */
+    tls1_get_peer_groups(s, &peer_groups, &num_peer_groups);
+    for (size_t i = 0; i < num_peer_groups; i++) {
+        if (is_ffdhe_group(peer_groups[i]))
+            return 0;
+    }
+
+    /*
+     * The client did not send any FFDHE groups, so we can use this ciphersuite
+     * using any group we like.
+     */
+    return 1;
+}
+
 /*
  * tls1_check_ec_tmp_key - Check EC temporary key compatibility
  * @s: SSL connection
index 1db7bddac36e4c50c876c29e96be9db1a660341f..35c22126f6d0b1e070c30295da2cc3afa0289f9c 100644 (file)
@@ -11431,6 +11431,139 @@ end:
 
     return testresult;
 }
+
+#ifndef OPENSSL_NO_TLS1_3
+/*
+ * Test the server will reject FFDHE ciphersuites if no supported FFDHE group is
+ * advertised by the client.
+ */
+static int test_no_shared_ffdhe_group(int idx)
+{
+    SSL_CTX *cctx = SSL_CTX_new_ex(libctx, NULL, TLS_client_method());
+    SSL_CTX *sctx = SSL_CTX_new_ex(libctx, NULL, TLS_server_method());
+    SSL *clientssl = NULL, *serverssl = NULL;
+    int testresult = 0, ret, expected = 1;
+    char *clientgroup = NULL, *servergroup = NULL, *ciphersuite = NULL;
+    int want_error = SSL_ERROR_NONE;
+
+    if (!TEST_ptr(sctx) || !TEST_ptr(cctx))
+        goto end;
+
+    switch (idx) {
+    case 0:
+        clientgroup = "ffdhe2048";
+        servergroup = "ffdhe3072";
+        ciphersuite = "DHE-RSA-AES128-SHA256:AES128-SHA256";
+        break;
+    case 1:
+        clientgroup = "ffdhe3072";
+        servergroup = "ffdhe4096";
+        ciphersuite = "DHE-RSA-AES128-SHA256:AES128-SHA256";
+        break;
+    case 2:
+        clientgroup = "ffdhe4096";
+        servergroup = "ffdhe6144";
+        ciphersuite = "DHE-RSA-AES128-SHA256:AES128-SHA256";
+        break;
+    case 3:
+        clientgroup = "ffdhe6144";
+        servergroup = "ffdhe8192";
+        ciphersuite = "DHE-RSA-AES128-SHA256:AES128-SHA256";
+        break;
+    case 4:
+        clientgroup = "ffdhe8192";
+        servergroup = "ffdhe2048";
+        ciphersuite = "DHE-RSA-AES128-SHA256:AES128-SHA256";
+        break;
+    case 5:
+        clientgroup = "ffdhe2048";
+        servergroup = "ffdhe3072";
+        ciphersuite = "DHE-RSA-AES128-SHA256";
+        expected = 0;
+        want_error = SSL_ERROR_SSL;
+        break;
+    case 6:
+        clientgroup = "ffdhe3072";
+        servergroup = "ffdhe4096";
+        ciphersuite = "DHE-RSA-AES128-SHA256";
+        expected = 0;
+        want_error = SSL_ERROR_SSL;
+        break;
+    case 7:
+        clientgroup = "ffdhe4096";
+        servergroup = "ffdhe6144";
+        ciphersuite = "DHE-RSA-AES128-SHA256";
+        expected = 0;
+        want_error = SSL_ERROR_SSL;
+        break;
+    case 8:
+        clientgroup = "ffdhe6144";
+        servergroup = "ffdhe8192";
+        ciphersuite = "DHE-RSA-AES128-SHA256";
+        expected = 0;
+        want_error = SSL_ERROR_SSL;
+        break;
+    case 9:
+        clientgroup = "ffdhe8192";
+        servergroup = "ffdhe2048";
+        ciphersuite = "DHE-RSA-AES128-SHA256";
+        expected = 0;
+        want_error = SSL_ERROR_SSL;
+        break;
+    default:
+        TEST_error("Invalid text index");
+        goto end;
+    }
+
+    if (!TEST_true(create_ssl_ctx_pair(libctx, NULL,
+            NULL,
+            0,
+            0,
+            &sctx, &cctx, cert, privkey)))
+        goto end;
+
+    if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl,
+            NULL, NULL)))
+        goto end;
+
+    if (!TEST_true(SSL_set_dh_auto(serverssl, 1))
+        || !TEST_true(SSL_set_min_proto_version(serverssl, TLS1_2_VERSION))
+        || !TEST_true(SSL_set_max_proto_version(serverssl, TLS1_2_VERSION))
+        || !TEST_true(SSL_set_cipher_list(serverssl, ciphersuite))
+        || !TEST_true(SSL_set_cipher_list(clientssl, ciphersuite))
+        || !TEST_true(SSL_set1_groups_list(serverssl, servergroup))
+        || !TEST_true(SSL_set1_groups_list(clientssl, clientgroup)))
+        goto end;
+
+    ret = create_ssl_connection(serverssl, clientssl, want_error);
+    if (!TEST_int_eq(expected, ret))
+        goto end;
+
+    if (expected <= 0) {
+        testresult = 1;
+        goto end;
+    }
+
+    /*
+     * Note that the server should not select the DHE ciphersuite if there are
+     * no shared FFDHE groups, so if it was selected, that is an error.
+     */
+    if (TEST_int_eq(TLS1_CK_DHE_RSA_WITH_AES_128_SHA256,
+            SSL_CIPHER_get_id(SSL_get_current_cipher(clientssl))))
+        goto end;
+
+    testresult = 1;
+
+end:
+    SSL_free(serverssl);
+    SSL_free(clientssl);
+    SSL_CTX_free(sctx);
+    SSL_CTX_free(cctx);
+
+    return testresult;
+}
+
+#endif /* OPENSSL_NO_TLS1_3 */
 #endif /* OPENSSL_NO_DH */
 #endif /* OPENSSL_NO_TLS1_2 */
 
@@ -14257,6 +14390,9 @@ int setup_tests(void)
 #ifndef OPENSSL_NO_DH
     ADD_ALL_TESTS(test_set_tmp_dh, 11);
     ADD_ALL_TESTS(test_dh_auto, 7);
+#ifndef OPENSSL_NO_TLS1_3
+    ADD_ALL_TESTS(test_no_shared_ffdhe_group, 10);
+#endif
 #endif
 #endif
 #ifndef OSSL_NO_USABLE_TLS1_3