Commit dc449562f4 for openssl.org

commit dc449562f42db8d20f2d3153580a0292f07bfc3e
Author: sftcd <stephen.farrell@cs.tcd.ie>
Date:   Fri Mar 13 22:02:29 2026 +0000

    ECH: chunk-size bug fix and non-regression changes

    Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
    Reviewed-by: Matt Caswell <matt@openssl.foundation>
    MergeDate: Thu Mar 19 10:35:56 2026
    (Merged from https://github.com/openssl/openssl/pull/30417)

diff --git a/apps/s_server.c b/apps/s_server.c
index 586c47bc9b..996d762641 100644
--- a/apps/s_server.c
+++ b/apps/s_server.c
@@ -630,6 +630,8 @@ static int ssl_ech_servername_cb(SSL *s, int *ad, void *arg)
                         "ssl_ech_servername_cb: Not switching context "
                         "- no name match (%d).\n",
                         check_host);
+                if (OPENSSL_strcasecmp(servername, p->servername) != 0)
+                    return p->extension_error;
             }
         }
     } else {
@@ -637,6 +639,14 @@ static int ssl_ech_servername_cb(SSL *s, int *ad, void *arg)
             BIO_printf(p->biodebug,
                 "ssl_ech_servername_cb: Not switching context "
                 "- no ECH SUCCESS\n");
+        if (servername != NULL) {
+            if (OPENSSL_strcasecmp(servername, p->servername))
+                return p->extension_error;
+            if (ctx2 != NULL) {
+                BIO_puts(p->biodebug, "Switching server context.\n");
+                SSL_set_SSL_CTX(s, ctx2);
+            }
+        }
     }
     return SSL_TLSEXT_ERR_OK;
 }
diff --git a/ssl/ech/ech_store.c b/ssl/ech/ech_store.c
index 1c43ee6804..fb0b72bde8 100644
--- a/ssl/ech/ech_store.c
+++ b/ssl/ech/ech_store.c
@@ -120,7 +120,7 @@ static int ech_bio2buf(BIO *in, unsigned char **buf, size_t *len)
         brv = BIO_read_ex(in, lptr, OSSL_ECH_BUFCHUNK, &readbytes);
         if (brv != 1)
             goto err;
-        if (readbytes < OSSL_ECH_BUFCHUNK) {
+        if (BIO_eof(in) || readbytes < OSSL_ECH_BUFCHUNK) {
             done = 1;
             break;
         }
diff --git a/test/certs/echdir/ech-b511.pem b/test/certs/echdir/ech-b511.pem
new file mode 100644
index 0000000000..2c655740ae
--- /dev/null
+++ b/test/certs/echdir/ech-b511.pem
@@ -0,0 +1,14 @@
+-----BEGIN PRIVATE KEY-----
+MC4CAQAwBQYDK2VuBCIEIFjwv041TaYyaBLXwW5i3qdRjVfp2jgDt0rjTNW+CEJw
+-----END PRIVATE KEY-----
+-----BEGIN ECHCONFIG-----
+Af3+DQA6RAAgACBaXlSMpzC72pccyR1s4ggNF6ZcoNMEatXUKlHUMtmebwAEAAIAAwALZXhhbXBs
+ZS5jb20AAP4NAEOFACAAIHoLrQGbajMQMAqajIXtnRjjHkAM4xy66Zo7OvfLJnwcAAQAAgADAA5l
+eGFtcGxlNTEyLmNvbQAGAAAAAv///g0AOt0AIAAgPj//c1cJ3yIi34Dvp8imA8ItbgXlMS9tOm+c
+K79t7U0ABAABAAEAC2V4YW1wbGUuY29tAAD+DQA6rQAgACDVBjfG9x8BtxGxkTZQdZv5cE4k2f2D
+QW3MyiVzRAxNSQAEAAIAAgALZXhhbXBsZS5jb20AAP4NADppACAAIKZcX2LKexw85KRYIchUmgZp
+HbFTXq15r7qdOgljpTtjAAQAAgADAAtleGFtcGxlLmNvbQAA/g0AukQAIAAgWl5UjKcwu9qXHMkd
+bOIIDRemXKDTBGrV1CpR1DLZnm8ABAACAAMAC2V4YW1wbGUuY29tAIAAAAB8AAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
+-----END ECHCONFIG-----
diff --git a/test/certs/echdir/ech-b512.pem b/test/certs/echdir/ech-b512.pem
new file mode 100644
index 0000000000..fbb2ee84a4
--- /dev/null
+++ b/test/certs/echdir/ech-b512.pem
@@ -0,0 +1,14 @@
+-----BEGIN PRIVATE KEY-----
+MC4CAQAwBQYDK2VuBCIEIFjwv041TaYyaBLXwW5i3qdRjVfp2jgDt0rjTNW+CEJw
+-----END PRIVATE KEY-----
+-----BEGIN ECHCONFIG-----
+Af7+DQA6RAAgACBaXlSMpzC72pccyR1s4ggNF6ZcoNMEatXUKlHUMtmebwAEAAIAAwALZXhhbXBs
+ZS5jb20AAP4NAEOFACAAIHoLrQGbajMQMAqajIXtnRjjHkAM4xy66Zo7OvfLJnwcAAQAAgADAA5l
+eGFtcGxlNTEyLmNvbQAGAAAAAv///g0AOt0AIAAgPj//c1cJ3yIi34Dvp8imA8ItbgXlMS9tOm+c
+K79t7U0ABAABAAEAC2V4YW1wbGUuY29tAAD+DQA6rQAgACDVBjfG9x8BtxGxkTZQdZv5cE4k2f2D
+QW3MyiVzRAxNSQAEAAIAAgALZXhhbXBsZS5jb20AAP4NADppACAAIKZcX2LKexw85KRYIchUmgZp
+HbFTXq15r7qdOgljpTtjAAQAAgADAAtleGFtcGxlLmNvbQAA/g0Au0QAIAAgWl5UjKcwu9qXHMkd
+bOIIDRemXKDTBGrV1CpR1DLZnm8ABAACAAMAC2V4YW1wbGUuY29tAIEAAAB9AAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
+-----END ECHCONFIG-----
diff --git a/test/certs/echdir/ech-b513.pem b/test/certs/echdir/ech-b513.pem
new file mode 100644
index 0000000000..1c1fa9acf7
--- /dev/null
+++ b/test/certs/echdir/ech-b513.pem
@@ -0,0 +1,14 @@
+-----BEGIN PRIVATE KEY-----
+MC4CAQAwBQYDK2VuBCIEIFjwv041TaYyaBLXwW5i3qdRjVfp2jgDt0rjTNW+CEJw
+-----END PRIVATE KEY-----
+-----BEGIN ECHCONFIG-----
+Af/+DQA6RAAgACBaXlSMpzC72pccyR1s4ggNF6ZcoNMEatXUKlHUMtmebwAEAAIAAwALZXhhbXBs
+ZS5jb20AAP4NAEOFACAAIHoLrQGbajMQMAqajIXtnRjjHkAM4xy66Zo7OvfLJnwcAAQAAgADAA5l
+eGFtcGxlNTEyLmNvbQAGAAAAAv///g0AOt0AIAAgPj//c1cJ3yIi34Dvp8imA8ItbgXlMS9tOm+c
+K79t7U0ABAABAAEAC2V4YW1wbGUuY29tAAD+DQA6rQAgACDVBjfG9x8BtxGxkTZQdZv5cE4k2f2D
+QW3MyiVzRAxNSQAEAAIAAgALZXhhbXBsZS5jb20AAP4NADppACAAIKZcX2LKexw85KRYIchUmgZp
+HbFTXq15r7qdOgljpTtjAAQAAgADAAtleGFtcGxlLmNvbQAA/g0AvEQAIAAgWl5UjKcwu9qXHMkd
+bOIIDRemXKDTBGrV1CpR1DLZnm8ABAACAAMAC2V4YW1wbGUuY29tAIIAAAB+AAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+-----END ECHCONFIG-----
diff --git a/test/recipes/82-test_ech_client_server.t b/test/recipes/82-test_ech_client_server.t
index dbeeb55d87..b988d8de71 100644
--- a/test/recipes/82-test_ech_client_server.t
+++ b/test/recipes/82-test_ech_client_server.t
@@ -33,7 +33,7 @@ plan skip_all => "$test_name requires TLSv1.3 enabled"
 plan skip_all => "$test_name is not available Windows or VMS"
     if $^O =~ /^(VMS|MSWin32|msys)$/;

-plan tests => 22;
+plan tests => 26;

 my $shlib_wrap   = bldtop_file("util", "shlib_wrap.sh");
 my $apps_openssl = bldtop_file("apps", "openssl");
@@ -94,6 +94,26 @@ sub start_ech_client_server
                              "-ech_noretry_dir", $ech_dir,
                              "-servername", "example.com",
                              "-tls1_3");
+        } elsif ($test_type eq "servername_fatal" ) {
+            # load keys from key dir (some will fail)
+            @s_server_cmd = ("s_server", "-accept", "0", "-naccept", "1",
+                            "-cert", $server_pem, "-key", $server_key,
+                            "-cert2", $server_pem, "-key2", $server_key,
+                            "-ech_dir", $ech_dir,
+                            "-ech_noretry_dir", $ech_dir,
+                            "-servername", "example.com",
+                            "-servername_fatal",
+                            "-tls1_3");
+        } elsif ($test_type eq "servername_fatal2" ) {
+            # load keys from key dir (some will fail)
+            @s_server_cmd = ("s_server", "-accept", "0", "-naccept", "1",
+                            "-cert", $server_pem, "-key", $server_key,
+                            "-cert2", $server_pem, "-key2", $server_key,
+                            "-ech_dir", $ech_dir,
+                            "-ech_noretry_dir", $ech_dir,
+                            "-servername", "example.com",
+                            "-servername_fatal",
+                            "-tls1_3");
         } else {
             # default for all other tests (for now)
             @s_server_cmd = ("s_server", "-accept", "0", "-naccept", "1",
@@ -222,6 +242,15 @@ sub start_ech_client_server
                              "-ech_config_list", $good_b64,
                              "-ech_ignore_cid",
                              "-prexit");
+
+        } elsif ($test_type eq "servername_fatal2" ) {
+            # Real ECH, but mismatching servername
+            @s_client_cmd = ("s_client",
+                            "-connect", "localhost:$s_server_port",
+                            "-servername", "server.not-the-example",
+                            "-CAfile", $root_pem,
+                            "-ech_config_list", $good_b64,
+                            "-prexit");
         } else {
             # Real ECH, and default
             @s_client_cmd = ("s_client",
@@ -380,6 +409,26 @@ sub keydir_test {
     ok($s_client_match == 1, "s_server using ech keydir on command line");
 }

+sub servernamefatal_test {
+    print("\n\nServer using servername_fatal test.\n");
+    my $tt = "servername_fatal";
+    my $win = "^ECH: success";
+    start_ech_client_server($tt, $win);
+    ok($s_server_port ne "0", "s_server port check");
+    print("s_server ready, on port $s_server_port pid: $s_server_pid\n");
+    ok($s_client_match == 1, "s_server using ech servername_fatal on command line");
+}
+
+sub servernamefatal_test2 {
+    print("\n\nServer using servername_fatal test.\n");
+    my $tt = "servername_fatal2";
+    my $win = "^ECH: tried but failed";
+    start_ech_client_server($tt, $win);
+    ok($s_server_port ne "0", "s_server port check");
+    print("s_server ready, on port $s_server_port pid: $s_server_pid\n");
+    ok($s_client_match == 1, "s_server using ech servername_fatal and bad name on command line");
+}
+
 basic_test();
 wrong_test();
 grease_test();
@@ -391,4 +440,6 @@ no_outer_test();
 cid_free_test();
 cid_wrong_test();
 keydir_test();
+servernamefatal_test();
+servernamefatal_test2();