Commit 9e0d3c0698 for openssl.org
commit 9e0d3c0698de7befb99da664a14c6fb5253c78be
Author: Mounir IDRASSI <mounir.idrassi@idrix.fr>
Date: Sat Apr 18 11:06:29 2026 +0900
Reject empty TLS 1.3 HRR cookie
RFC 8446 defines the Cookie extension as containing a non-empty cookie
vector. The client-side HRR parser accepted a zero-length cookie
because PACKET_memdup() treats an empty packet as success, which
deferred failure until later in the handshake.
Reject an empty cookie during HRR parsing with decode_error and add a
regression test.
Fixes #30868
Fixes: cfef5027bf27 "Add basic TLSv1.3 cookie support"
Reviewed-by: Eugene Syromiatnikov <esyr@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.foundation>
Reviewed-by: Frederik Wedel-Heinen <fwh.openssl@gmail.com>
MergeDate: Wed Apr 22 07:50:25 2026
(Merged from https://github.com/openssl/openssl/pull/30892)
diff --git a/ssl/statem/extensions_clnt.c b/ssl/statem/extensions_clnt.c
index b2a5de6a87..f9ece09df3 100644
--- a/ssl/statem/extensions_clnt.c
+++ b/ssl/statem/extensions_clnt.c
@@ -2372,6 +2372,7 @@ int tls_parse_stoc_cookie(SSL_CONNECTION *s, PACKET *pkt, unsigned int context,
PACKET cookie;
if (!PACKET_as_length_prefixed_2(pkt, &cookie)
+ || PACKET_remaining(&cookie) == 0
|| !PACKET_memdup(&cookie, &s->ext.tls13_cookie,
&s->ext.tls13_cookie_len)) {
SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_MISMATCH);
diff --git a/test/recipes/70-test_tls13cookie.t b/test/recipes/70-test_tls13cookie.t
index dfbe7edc33..994f2945db 100644
--- a/test/recipes/70-test_tls13cookie.t
+++ b/test/recipes/70-test_tls13cookie.t
@@ -31,7 +31,8 @@ plan skip_all => "$test_name needs TLS1.3 enabled"
use constant {
COOKIE_ONLY => 0,
- COOKIE_AND_KEY_SHARE => 1
+ COOKIE_AND_KEY_SHARE => 1,
+ EMPTY_COOKIE => 2
};
my $proxy = TLSProxy::Proxy->new(
@@ -43,6 +44,7 @@ my $proxy = TLSProxy::Proxy->new(
);
my $cookieseen = 0;
+my $fatal_alert = 0;
my $testtype;
#Test 1: Inserting a cookie into an HRR should see it echoed in the ClientHello
@@ -57,7 +59,7 @@ if (disabled("ecx")) {
$proxy->serverflags("-curves X25519");
}
$proxy->start() or plan skip_all => "Unable to start up Proxy for tests";
-plan tests => 2;
+plan tests => 3;
ok(TLSProxy::Message->success() && $cookieseen == 1, "Cookie seen");
#Test 2: Inserting a cookie into an HRR should see it echoed in the ClientHello
@@ -72,18 +74,41 @@ SKIP: {
ok(TLSProxy::Message->success() && $cookieseen == 1, "Cookie seen");
}
+#Test 3: A client should reject an empty cookie in an HRR
+$testtype = EMPTY_COOKIE;
+$fatal_alert = 0;
+$proxy->clear();
+if (disabled("ecx")) {
+ $proxy->clientflags("-curves ffdhe3072:ffdhe2048");
+ $proxy->serverflags("-curves ffdhe2048");
+} else {
+ $proxy->clientflags("-curves P-256:X25519");
+ $proxy->serverflags("-curves X25519");
+}
+$proxy->start();
+ok($fatal_alert, "Empty cookie rejected");
+
sub cookie_filter
{
my $proxy = shift;
+ if ($testtype == EMPTY_COOKIE && $proxy->flight == 2) {
+ $fatal_alert = 1
+ if @{$proxy->record_list}[-1]->is_fatal_alert(0)
+ == TLSProxy::Message::AL_DESC_DECODE_ERROR;
+ return;
+ }
+
# We're only interested in the HRR and both ClientHellos
return if ($proxy->flight > 2);
- my $ext = pack "C8",
- 0x00, 0x06, #Cookie Length
- 0x00, 0x01, #Dummy cookie data (6 bytes)
- 0x02, 0x03,
- 0x04, 0x05;
+ my $ext = $testtype == EMPTY_COOKIE
+ ? pack("n", 0)
+ : pack("C8",
+ 0x00, 0x06, #Cookie Length
+ 0x00, 0x01, #Dummy cookie data (6 bytes)
+ 0x02, 0x03,
+ 0x04, 0x05);
foreach my $message (@{$proxy->message_list}) {
if ($message->mt == TLSProxy::Message::MT_SERVER_HELLO