Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce option allowing TLS 1.3 server to prefer psk_ke over psk_dhe_ke #22794

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ OpenSSL 3.3

*Ahelenia Ziemiańska*

* New option `SSL_OP_PREFER_NO_DHE_KEX`, which allows configuring a TLS1.3
server to prefer session resumption using PSK-only key exchange over PSK
with DHE, if both are available.

*Markus Minichmayr, Tapkey GmbH*

OpenSSL 3.2
-----------

Expand Down
6 changes: 5 additions & 1 deletion apps/include/opt.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,8 @@
OPT_S_NOTLS1_3, OPT_S_BUGS, OPT_S_NO_COMP, OPT_S_NOTICKET, \
OPT_S_SERVERPREF, OPT_S_LEGACYRENEG, OPT_S_CLIENTRENEG, \
OPT_S_LEGACYCONN, \
OPT_S_ONRESUMP, OPT_S_NOLEGACYCONN, OPT_S_ALLOW_NO_DHE_KEX, \
OPT_S_ONRESUMP, OPT_S_NOLEGACYCONN, \
OPT_S_ALLOW_NO_DHE_KEX, OPT_S_PREFER_NO_DHE_KEX, \
OPT_S_PRIORITIZE_CHACHA, \
OPT_S_STRICT, OPT_S_SIGALGS, OPT_S_CLIENTSIGALGS, OPT_S_GROUPS, \
OPT_S_CURVES, OPT_S_NAMEDCURVE, OPT_S_CIPHER, OPT_S_CIPHERSUITES, \
Expand Down Expand Up @@ -198,6 +199,8 @@
"Disallow initial connection to servers that don't support RI"}, \
{"allow_no_dhe_kex", OPT_S_ALLOW_NO_DHE_KEX, '-', \
"In TLSv1.3 allow non-(ec)dhe based key exchange on resumption"}, \
{"prefer_no_dhe_kex", OPT_S_PREFER_NO_DHE_KEX, '-', \
"In TLSv1.3 prefer non-(ec)dhe over (ec)dhe-based key exchange on resumption"}, \
{"prioritize_chacha", OPT_S_PRIORITIZE_CHACHA, '-', \
"Prioritize ChaCha ciphers when preferred by clients"}, \
{"strict", OPT_S_STRICT, '-', \
Expand Down Expand Up @@ -248,6 +251,7 @@
case OPT_S_ONRESUMP: \
case OPT_S_NOLEGACYCONN: \
case OPT_S_ALLOW_NO_DHE_KEX: \
case OPT_S_PREFER_NO_DHE_KEX: \
case OPT_S_PRIORITIZE_CHACHA: \
case OPT_S_STRICT: \
case OPT_S_SIGALGS: \
Expand Down
1 change: 1 addition & 0 deletions doc/man1/openssl-s_client.pod.in
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ B<openssl> B<s_client>
[B<-legacy_server_connect>]
[B<-no_legacy_server_connect>]
[B<-allow_no_dhe_kex>]
[B<-prefer_no_dhe_kex>]
[B<-sigalgs> I<sigalglist>]
[B<-curves> I<curvelist>]
[B<-cipher> I<cipherlist>]
Expand Down
1 change: 1 addition & 0 deletions doc/man1/openssl-s_server.pod.in
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ B<openssl> B<s_server>
[B<-no_renegotiation>]
[B<-no_resumption_on_reneg>]
[B<-allow_no_dhe_kex>]
[B<-prefer_no_dhe_kex>]
[B<-prioritize_chacha>]
[B<-strict>]
[B<-sigalgs> I<val>]
Expand Down
13 changes: 13 additions & 0 deletions doc/man3/SSL_CONF_cmd.pod
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,12 @@ Only used by servers. Requires B<-serverpref>.
In TLSv1.3 allow a non-(ec)dhe based key exchange mode on resumption. This means
that there will be no forward secrecy for the resumed session.

=item B<-prefer_no_dhe_kex>

In TLSv1.3, on resumption let the server prefer a non-(ec)dhe based key
exchange mode over an (ec)dhe based one. Requires B<-allow_no_dhe_kex>.
Equivalent to B<SSL_OP_PREFER_NO_DHE_KEX>. Only used by servers.

=item B<-strict>

Enables strict mode protocol handling. Equivalent to setting
Expand Down Expand Up @@ -523,6 +529,11 @@ B<AllowNoDHEKEX>: In TLSv1.3 allow a non-(ec)dhe based key exchange mode on
resumption. This means that there will be no forward secrecy for the resumed
session. Equivalent to B<SSL_OP_ALLOW_NO_DHE_KEX>.

B<PreferNoDHEKEX>: In TLSv1.3, on resumption let the server prefer a
non-(ec)dhe based key exchange mode over an (ec)dhe based one. Requires
B<AllowNoDHEKEX>. Equivalent to B<SSL_OP_PREFER_NO_DHE_KEX>. Only used by
servers.

B<MiddleboxCompat>: If set then dummy Change Cipher Spec (CCS) messages are sent
in TLSv1.3. This has the effect of making TLSv1.3 look more like TLSv1.2 so that
middleboxes that do not understand TLSv1.3 will not drop the connection. This
Expand Down Expand Up @@ -778,6 +789,8 @@ OpenSSL 3.0.
The B<TxCertificateCompression> and B<RxCertificateCompression> options were
added in OpenSSL 3.2.

B<PreferNoDHEKEX> was added in OpenSSL 3.3.

=head1 COPYRIGHT

Copyright 2012-2023 The OpenSSL Project Authors. All Rights Reserved.
Expand Down
6 changes: 6 additions & 0 deletions doc/man3/SSL_CTX_set_options.pod
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,12 @@ this option to enable it.
In TLSv1.3 allow a non-(ec)dhe based key exchange mode on resumption. This means
that there will be no forward secrecy for the resumed session.

=item SSL_OP_PREFER_NO_DHE_KEX

In TLSv1.3, on resumption let the server prefer a non-(ec)dhe based key
exchange mode over an (ec)dhe based one. Ignored without B<SSL_OP_ALLOW_NO_DHE_KEX>
being set as well. Always ignored on the client.

=item SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION

Allow legacy insecure renegotiation between OpenSSL and unpatched clients or
Expand Down
4 changes: 3 additions & 1 deletion doc/perlvars.pm
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ $OpenSSL::safe::opt_s_synopsis = ""
. "[B<-no_legacy_server_connect>]\n"
. "[B<-no_etm>]\n"
. "[B<-allow_no_dhe_kex>]\n"
. "[B<-prefer_no_dhe_kex>]\n"
. "[B<-prioritize_chacha>]\n"
. "[B<-strict>]\n"
. "[B<-sigalgs> I<algs>]\n"
Expand All @@ -207,7 +208,8 @@ $OpenSSL::safe::opt_s_item = ""
. "B<-legacy_renegotiation>, B<-no_renegotiation>,\n"
. "B<-no_resumption_on_reneg>,\n"
. "B<-legacy_server_connect>, B<-no_legacy_server_connect>, B<-no_etm>\n"
. "B<-allow_no_dhe_kex>, B<-prioritize_chacha>, B<-strict>, B<-sigalgs>\n"
. "B<-allow_no_dhe_kex>, B<-prefer_no_dhe_kex>,\n"
. "B<-prioritize_chacha>, B<-strict>, B<-sigalgs>\n"
. "I<algs>, B<-client_sigalgs> I<algs>, B<-groups> I<groups>, B<-curves>\n"
. "I<curves>, B<-named_curve> I<curve>, B<-cipher> I<ciphers>, B<-ciphersuites>\n"
. "I<1.3ciphers>, B<-min_protocol> I<minprot>, B<-max_protocol> I<maxprot>,\n"
Expand Down
2 changes: 2 additions & 0 deletions include/openssl/ssl.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,8 @@ typedef int (*SSL_async_callback_fn)(SSL *s, void *arg);
/* Enable KTLS TX zerocopy on Linux */
# define SSL_OP_ENABLE_KTLS_TX_ZEROCOPY_SENDFILE SSL_OP_BIT(34)

#define SSL_OP_PREFER_NO_DHE_KEX SSL_OP_BIT(35)

/*
* Option "collections."
*/
Expand Down
4 changes: 4 additions & 0 deletions ssl/ssl_conf.c
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,7 @@ static int cmd_Options(SSL_CONF_CTX *cctx, const char *value)
SSL_FLAG_TBL_INV("EncryptThenMac", SSL_OP_NO_ENCRYPT_THEN_MAC),
SSL_FLAG_TBL("NoRenegotiation", SSL_OP_NO_RENEGOTIATION),
SSL_FLAG_TBL("AllowNoDHEKEX", SSL_OP_ALLOW_NO_DHE_KEX),
SSL_FLAG_TBL("PreferNoDHEKEX", SSL_OP_PREFER_NO_DHE_KEX),
SSL_FLAG_TBL("PrioritizeChaCha", SSL_OP_PRIORITIZE_CHACHA),
SSL_FLAG_TBL("MiddleboxCompat", SSL_OP_ENABLE_MIDDLEBOX_COMPAT),
SSL_FLAG_TBL_INV("AntiReplay", SSL_OP_NO_ANTI_REPLAY),
Expand Down Expand Up @@ -725,6 +726,7 @@ static const ssl_conf_cmd_tbl ssl_conf_cmds[] = {
SSL_CONF_CMD_SWITCH("no_resumption_on_reneg", SSL_CONF_FLAG_SERVER),
SSL_CONF_CMD_SWITCH("no_legacy_server_connect", SSL_CONF_FLAG_CLIENT),
SSL_CONF_CMD_SWITCH("allow_no_dhe_kex", 0),
SSL_CONF_CMD_SWITCH("prefer_no_dhe_kex", 0),
SSL_CONF_CMD_SWITCH("prioritize_chacha", SSL_CONF_FLAG_SERVER),
SSL_CONF_CMD_SWITCH("strict", 0),
SSL_CONF_CMD_SWITCH("no_middlebox", 0),
Expand Down Expand Up @@ -816,6 +818,8 @@ static const ssl_switch_tbl ssl_cmd_switches[] = {
{SSL_OP_LEGACY_SERVER_CONNECT, SSL_TFLAG_INV},
/* allow_no_dhe_kex */
{SSL_OP_ALLOW_NO_DHE_KEX, 0},
/* prefer_no_dhe_kex */
{SSL_OP_PREFER_NO_DHE_KEX, 0},
/* chacha reprioritization */
{SSL_OP_PRIORITIZE_CHACHA, 0},
{SSL_CERT_FLAG_TLS_STRICT, SSL_TFLAG_CERT}, /* strict */
Expand Down
17 changes: 13 additions & 4 deletions ssl/statem/extensions.c
Original file line number Diff line number Diff line change
Expand Up @@ -1393,7 +1393,8 @@ static int final_key_share(SSL_CONNECTION *s, unsigned int context, int sent)
* the client sent a key_share extension
* AND
* (we are not resuming
* OR the kex_mode allows key_share resumes)
* OR (the kex_mode allows key_share resumes
* AND (kex_mode doesn't allow non-dh resumes OR non-dh is not preferred)))
* AND
* a shared group exists
* THEN
Expand Down Expand Up @@ -1428,10 +1429,18 @@ static int final_key_share(SSL_CONNECTION *s, unsigned int context, int sent)
}
} else {
/* No suitable key_share */

/* Do DHE PSK? */
int dhe_psk =
/* kex_mode allows key_share resume */
(((s->ext.psk_kex_mode & TLSEXT_KEX_MODE_FLAG_KE_DHE) != 0)

/* and psk-only is not available or not explicitly preferred */
&& ((((s->ext.psk_kex_mode & TLSEXT_KEX_MODE_FLAG_KE) == 0)
|| (s->options & SSL_OP_PREFER_NO_DHE_KEX) == 0)));

if (s->hello_retry_request == SSL_HRR_NONE && sent
&& (!s->hit
|| (s->ext.psk_kex_mode & TLSEXT_KEX_MODE_FLAG_KE_DHE)
!= 0)) {
&& (!s->hit || dhe_psk)) {
const uint16_t *pgroups, *clntgroups;
size_t num_groups, clnt_num_groups, i;
unsigned int group_id = 0;
Expand Down
21 changes: 17 additions & 4 deletions ssl/statem/extensions_srvr.c
Original file line number Diff line number Diff line change
Expand Up @@ -1645,12 +1645,25 @@ EXT_RETURN tls_construct_stoc_key_share(SSL_CONNECTION *s, WPACKET *pkt,
}
return EXT_RETURN_NOT_SENT;
}
if (s->hit && (s->ext.psk_kex_mode & TLSEXT_KEX_MODE_FLAG_KE_DHE) == 0) {
if (s->hit) {
/* PSK ('hit') */

/*
* PSK ('hit') and explicitly not doing DHE (if the client sent the
* DHE option we always take it); don't send key share.
* If we're doing PSK ('hit') but the client doesn't support psk-dhe,
* we don't need to send a key share.
*/
return EXT_RETURN_NOT_SENT;
if ((s->ext.psk_kex_mode & TLSEXT_KEX_MODE_FLAG_KE_DHE) == 0)
return EXT_RETURN_NOT_SENT;

/*
* If both, psk_ke and psk_dh_ke are available, we do psk_dh_ke and
* send a key share by default, but not if the server is explicitly
* configured to prefer psk_ke.
*/
if (((s->ext.psk_kex_mode & TLSEXT_KEX_MODE_FLAG_KE) != 0)
&& ((s->options & SSL_OP_PREFER_NO_DHE_KEX) != 0)) {
return EXT_RETURN_NOT_SENT;
}
}

if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_key_share)
Expand Down
50 changes: 40 additions & 10 deletions test/recipes/70-test_tls13kexmodes.t
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ $proxy->clientflags("-no_rx_cert_comp -sess_out ".$session);
$proxy->serverflags("-no_rx_cert_comp -servername localhost");
$proxy->sessionfile($session);
$proxy->start() or plan skip_all => "Unable to start up Proxy for tests";
plan tests => 11;
plan tests => 13;
ok(TLSProxy::Message->success(), "Initial connection");

#Test 2: Attempt a resume with no kex modes extension. Should fail (server
Expand Down Expand Up @@ -251,10 +251,11 @@ checkhandshake($proxy, checkhandshake::DEFAULT_HANDSHAKE,
| checkhandshake::PSK_CLI_EXTENSION,
"Resume with unrecognized kex mode");

#Test 7: Attempt a resume with both non-dhe and dhe kex mode. Should resume with
# a key_share
#Test 7: Attempt a resume with both, non-dhe and dhe kex mode. Should resume with
# a key_share, even though non-dhe is allowed, but not explicitly preferred.
$proxy->clear();
$proxy->clientflags("-no_rx_cert_comp -sess_in ".$session);
$proxy->clientflags("-no_rx_cert_comp -allow_no_dhe_kex -sess_in ".$session);
$proxy->serverflags("-allow_no_dhe_kex");
$testtype = BOTH_KEX_MODES;
$proxy->start();
checkhandshake($proxy, checkhandshake::RESUME_HANDSHAKE,
Expand All @@ -263,9 +264,38 @@ checkhandshake($proxy, checkhandshake::RESUME_HANDSHAKE,
| checkhandshake::KEY_SHARE_SRV_EXTENSION
| checkhandshake::PSK_CLI_EXTENSION
| checkhandshake::PSK_SRV_EXTENSION,
"Resume with non-dhe kex mode");
"Resume with both kex modes");

#Test 8: Attempt a resume with both, non-dhe and dhe kex mode, but with server-side
# preference for non-dhe. Should resume without a key_share.
$proxy->clear();
$proxy->clientflags("-no_rx_cert_comp -allow_no_dhe_kex -sess_in ".$session);
$proxy->serverflags("-allow_no_dhe_kex -prefer_no_dhe_kex");
$testtype = BOTH_KEX_MODES;
$proxy->start();
checkhandshake($proxy, checkhandshake::RESUME_HANDSHAKE,
checkhandshake::DEFAULT_EXTENSIONS
| checkhandshake::PSK_KEX_MODES_EXTENSION
| checkhandshake::PSK_CLI_EXTENSION
| checkhandshake::PSK_SRV_EXTENSION,
"Resume with both kex modes, preference for non-dhe");

#Test 9: Attempt a resume with both, non-dhe and dhe kex mode, with server-side
# preference for non-dhe, but non-dhe not allowed. Should resume with a key_share.
$proxy->clear();
$proxy->clientflags("-no_rx_cert_comp -allow_no_dhe_kex -sess_in ".$session);
$proxy->serverflags("-prefer_no_dhe_kex");
$testtype = BOTH_KEX_MODES;
$proxy->start();
checkhandshake($proxy, checkhandshake::RESUME_HANDSHAKE,
checkhandshake::DEFAULT_EXTENSIONS
| checkhandshake::PSK_KEX_MODES_EXTENSION
| checkhandshake::KEY_SHARE_SRV_EXTENSION
| checkhandshake::PSK_CLI_EXTENSION
| checkhandshake::PSK_SRV_EXTENSION,
"Resume with both kex modes, preference for but disabled non-dhe");

#Test 8: Attempt a resume with both non-dhe and dhe kex mode, but unacceptable
#Test 10: Attempt a resume with both non-dhe and dhe kex mode, but unacceptable
# initial key_share. Should resume with a key_share following an HRR
$proxy->clear();
$proxy->clientflags("-no_rx_cert_comp -sess_in ".$session);
Expand All @@ -281,7 +311,7 @@ checkhandshake($proxy, checkhandshake::HRR_RESUME_HANDSHAKE,
| checkhandshake::PSK_SRV_EXTENSION,
"Resume with both kex modes and HRR");

#Test 9: Attempt a resume with dhe kex mode only and an unacceptable initial
#Test 11: Attempt a resume with dhe kex mode only and an unacceptable initial
# key_share. Should resume with a key_share following an HRR
$proxy->clear();
$proxy->clientflags("-no_rx_cert_comp -sess_in ".$session);
Expand All @@ -297,7 +327,7 @@ checkhandshake($proxy, checkhandshake::HRR_RESUME_HANDSHAKE,
| checkhandshake::PSK_SRV_EXTENSION,
"Resume with dhe kex mode and HRR");

#Test 10: Attempt a resume with both non-dhe and dhe kex mode, unacceptable
#Test 12: Attempt a resume with both non-dhe and dhe kex mode, unacceptable
# initial key_share and no overlapping groups. Should resume without a
# key_share
$proxy->clear();
Expand All @@ -312,7 +342,7 @@ checkhandshake($proxy, checkhandshake::RESUME_HANDSHAKE,
| checkhandshake::PSK_SRV_EXTENSION,
"Resume with both kex modes, no overlapping groups");

#Test 11: Attempt a resume with dhe kex mode only, unacceptable
#Test 13: Attempt a resume with dhe kex mode only, unacceptable
# initial key_share and no overlapping groups. Should fail
$proxy->clear();
$proxy->clientflags("-no_rx_cert_comp -curves P-384 -sess_in ".$session);
Expand Down Expand Up @@ -351,7 +381,7 @@ sub modify_kex_modes_filter
0xfe, #unknown
0xff; #unknown
} elsif ($testtype == BOTH_KEX_MODES) {
#We deliberately list psk_ke first...should still use psk_dhe_ke
#We deliberately list psk_ke first...should still use psk_dhe_ke, except if the server is configured otherwise.
$ext = pack "C3",
0x02, #List length
0x00, #psk_ke
Expand Down