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