Commit f1be37eac8 for openssl.org

commit f1be37eac8f6ee72fa5ff5832a469f28827c78dd
Author: Anuj Ramesh <37349553+anujramesh@users.noreply.github.com>
Date:   Wed Apr 15 14:02:05 2026 -0400

    Fix TLS 1.3 alert for missing key_share

    Fixes #30818

    Co-authored-by: Anuj Ramesh <37349553+anujramesh@users.noreply.github.com>
    Assisted-by: Claude:claude-fable-5
    Reviewed-by: Matt Caswell <matt@openssl.foundation>
    Reviewed-by: Frederik Wedel-Heinen <fwh.openssl@gmail.com>
    Merge-date: Wed Oct 21 16:29:21 2026
    Merged-from: https://github.com/openssl/openssl/pull/32527

diff --git a/ssl/statem/extensions.c b/ssl/statem/extensions.c
index bc00da04d1..be2d52bb08 100644
--- a/ssl/statem/extensions.c
+++ b/ssl/statem/extensions.c
@@ -1732,14 +1732,20 @@ static int final_key_share(SSL_CONNECTION *s, unsigned int context, int sent)
      */
     if (!s->server
         && !sent) {
-        if ((s->ext.psk_kex_mode & TLSEXT_KEX_MODE_FLAG_KE) == 0) {
-            SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_NO_SUITABLE_KEY_SHARE);
-            return 0;
-        }
+        /*
+         * In a full handshake the ServerHello must carry a key_share, so its
+         * absence is a missing_extension alert (RFC 8446, sections 4.2.8
+         * and 6.2). Only when resuming may the server legitimately omit it,
+         * and then only if we offered psk_ke.
+         */
         if (!s->hit) {
             SSLfatal(s, SSL_AD_MISSING_EXTENSION, SSL_R_NO_SUITABLE_KEY_SHARE);
             return 0;
         }
+        if ((s->ext.psk_kex_mode & TLSEXT_KEX_MODE_FLAG_KE) == 0) {
+            SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_NO_SUITABLE_KEY_SHARE);
+            return 0;
+        }
     }
     /*
      * IF
diff --git a/test/recipes/70-test_key_share.t b/test/recipes/70-test_key_share.t
index afd769c9b0..8a1e0c52d2 100644
--- a/test/recipes/70-test_key_share.t
+++ b/test/recipes/70-test_key_share.t
@@ -90,7 +90,7 @@ if (disabled("ec")) {
     $proxy->serverflags("-groups P-384");
 }
 $proxy->start() or plan skip_all => "Unable to start up Proxy for tests";
-plan tests => 25;
+plan tests => 26;
 ok(TLSProxy::Message->success(), "Success after HRR");

 #Test 2: The server sending an HRR requesting a group the client already sent
@@ -345,6 +345,27 @@ SKIP: {
     ok(TLSProxy::Message->success(), "Large number of supported groups");
 }

+#Test 26: A ServerHello without a key_share in a full (non-resumption)
+#         handshake must fail with a missing_extension alert (RFC 8446
+#         sections 4.2.8 and 6.2), not illegal_parameter
+$proxy->clear();
+$direction = SERVER_TO_CLIENT;
+$testtype = MISSING_EXTENSION;
+$proxy->start();
+ok(is_missing_extension_client_alert(),
+   "Missing key_share in ServerHello gives missing_extension alert");
+
+sub is_missing_extension_client_alert
+{
+    return 0 unless TLSProxy::Message->fail();
+
+    my $alert = TLSProxy::Message->alert();
+    return 1 if !$alert->server()
+                && $alert->description()
+                   == TLSProxy::Message::AL_DESC_MISSING_EXTENSION;
+    return 0;
+}
+
 sub modify_key_shares_filter
 {
     my $proxy = shift;
@@ -517,6 +538,11 @@ sub modify_key_shares_filter
                 $message->repack();
                 return;
             }
+            if ($testtype == MISSING_EXTENSION) {
+                $message->delete_extension(TLSProxy::Message::EXT_KEY_SHARE);
+                $message->repack();
+                return;
+            }
             if ($testtype == SELECT_X25519) {
                 $ext = pack "C4H64",
                     0x00, 0x1d, #x25519