From: Joachim Vandersmissen Date: Mon, 15 Dec 2025 07:09:49 +0000 (+1100) Subject: Implement first step of RFC7919 in TLS 1.2 server X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=92131d3afc8d6efc9ff7f6b392fdb9c139fb0ddc;p=thirdparty%2Fopenssl.git Implement first step of RFC7919 in TLS 1.2 server 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 Reviewed-by: Matt Caswell Reviewed-by: Saša Nedvědický MergeDate: Thu Feb 5 09:09:40 2026 (Merged from https://github.com/openssl/openssl/pull/24551) --- diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c index fc3f5892fd2..2990e4014d7 100644 --- a/ssl/s3_lib.c +++ b/ssl/s3_lib.c @@ -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) diff --git a/ssl/ssl_local.h b/ssl/ssl_local.h index 382a6cc9023..a50ee6e1dc7 100644 --- a/ssl/ssl_local.h +++ b/ssl/ssl_local.h @@ -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); diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c index 81bf83d94b4..b0bfc92e322 100644 --- a/ssl/t1_lib.c +++ b/ssl/t1_lib.c @@ -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 diff --git a/test/sslapitest.c b/test/sslapitest.c index 1db7bddac36..35c22126f6d 100644 --- a/test/sslapitest.c +++ b/test/sslapitest.c @@ -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