Repository: randombit/botan
Pull request #3058 (2022-10-11 16:33:47)

Report Code Locations of test failures
This allows rudimentary reporting of unittest failure code locations. Like so:

```
tls_rfc8448:
Handshake involving Hello Retry Request (Client side) ran 52 tests in 21.09 msec all ok
Middlebox Compatibility Mode (Client side) ran 7 tests in 19.48 msec 1 FAILED
Failure 1: Close connection client connection was terminated produced unexpected result '0' expected '1' (at src/tests/test_tls_rfc8448.cpp:833)
```

The reported code location points to the respective `BOTAN_REGISTER_TEST` macro. Unfortunately, that's currently the only place where we can extract the code location conveniently (via `__FILE__` and `__LINE__`). With C++20 we'll be able to use [`std::source_location`](https://en.cppreference.com/w/cpp/utility/source_location) to obtain more specific source coordinates without the use of macros.

Still, I think this addition is worthwhile. For instance to build a convenient test result integration with VS code problem matchers. See screenshots below:

![image](https://user-images.githubusercontent.com/1562139/195149296-16329ee7-7aa8-4595-9dca-f9122f9eb6a0.png)

---

![image](https://user-images.githubusercontent.com/1562139/195149685-140ed911-2536-49ca-8692-c307e3d0f728.png)



Patch:
diff --git a/.github/actions/setup-build-agent/action.yml b/.github/actions/setup-build-agent/action.yml
index 55c53b428..4f0a8a082 100644
--- a/.github/actions/setup-build-agent/action.yml
+++ b/.github/actions/setup-build-agent/action.yml
@@ -22,14 +22,9 @@ inputs:
 runs:
   using: composite
   steps:
-    - name: Setup Build Agent (Windows)
-      run: ./src/scripts/ci/setup_gh_actions.ps1 "${{ inputs.target }}" "${{ inputs.arch }}"
-      shell: pwsh
-      if: runner.os == 'Windows'
     - name: Setup Build Agent (Unix-like)
-      run: ./src/scripts/ci/setup_gh_actions.sh "${{ inputs.target }}" "${{ inputs.arch }}"
+      run: ./src/scripts/ci/setup_gh_actions.sh ${{ inputs.target }}
       shell: bash
-      if: runner.os != 'Windows'
 
     - name: Check Availability of Compiler Cache
       run: print("::warning ::No compiler cache available, build times might suffer")
@@ -41,14 +36,3 @@ runs:
           path: ${{ env.COMPILER_CACHE_LOCATION }}
           key: ${{ inputs.cache-key }}-${{ github.run_id }}
           restore-keys: ${{ inputs.cache-key }}
-
-    - name: Setup Visual Studio Environment
-      uses: egor-tensin/vs-shell@v2
-      with:
-        arch: ${{ env.VSENV_ARCH }} # set by setup_gh_actions.ps1
-      if: runner.os == 'Windows'
-
-    - name: Install Build Dependencies # after setting up Visual Studio Environment
-      run: ./src/scripts/ci/setup_gh_actions_after_vcvars.ps1 ${{ inputs.target }}
-      shell: pwsh
-      if: runner.os == 'Windows'
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index c2bbc5495..27b488b9a 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -23,41 +23,6 @@ concurrency:
   cancel-in-progress: true
 
 jobs:
-  windows:
-    name: "Windows (Basic)"
-    strategy:
-      fail-fast: false
-
-      matrix:
-        # basic configuration combinations should run on all target platforms
-        target:    [ static, shared, amalgamation ]
-        arch:      [ x86_64, x86 ]
-        host_os:   [ windows-2022, windows-2019 ]
-
-        exclude:
-          # fails due to "abstract class marked final"
-          # see TODOs here: https://github.com/randombit/botan/pull/3007
-          - target: amalgamation
-
-          # currently failing
-          # see TODOs here: https://github.com/randombit/botan/pull/3007
-          - arch: x86
-
-    runs-on: ${{ matrix.host_os }}
-
-    steps:
-      - uses: actions/checkout@v3
-
-      - name: Setup Build Agent
-        uses: ./.github/actions/setup-build-agent
-        with:
-          target: ${{ matrix.target }}
-          cache-key: ${{ matrix.host_os }}-msvc-${{ matrix.arch }}-${{ matrix.target }}
-          arch: ${{ matrix.arch }}
-
-      - name: Build and Test Botan
-        run: python3 ./src/scripts/ci_build.py --cc='msvc' --make-tool='jom' --cpu='${{ matrix.arch }}' ${{ matrix.target }}
-
   linux:
     name: "Linux (Basic)"
     strategy:
@@ -152,17 +117,9 @@ jobs:
           - target: bsi
             compiler: gcc
             host_os: ubuntu-latest
-          - target: sanitizer
-            compiler: msvc
-            host_os: windows-latest
-            make_tool: jom
           - target: lint
             compiler: gcc
             host_os: ubuntu-latest
-          - target: static
-            compiler: gcc
-            host_os: windows-latest
-            make_tool: mingw32-make
 
     runs-on: ${{ matrix.host_os }}
 
diff --git a/configure.py b/configure.py
index 2f3743405..efcd9aeaf 100755
--- a/configure.py
+++ b/configure.py
@@ -352,12 +352,6 @@ def process_command_line(args): # pylint: disable=too-many-locals,too-many-state
     target_group.add_option('--compiler-cache',
                             help='specify a compiler cache to use')
 
-    target_group.add_option('--with-compilation-database', dest='with_compilation_database',
-                            action='store_true', default=True, help=optparse.SUPPRESS_HELP)
-
-    target_group.add_option('--without-compilation-database', dest='with_compilation_database',
-                            action='store_false', help='disable generation of compile_commands.json')
-
     target_group.add_option('--with-endian', metavar='ORDER', default=None,
                             help='override byte order guess')
 
@@ -1716,7 +1710,6 @@ def process_template_string(template_text, variables, template_source):
             self.value_pattern = re.compile(r'%{([a-z][a-z_0-9\|]+)}')
             self.cond_pattern = re.compile('%{(if|unless) ([a-z][a-z_0-9]+)}')
             self.for_pattern = re.compile('(.*)%{for ([a-z][a-z_0-9]+)}')
-            self.omitlast_pattern = re.compile('(.*)%{omitlast ([^}]*)}(.*)', re.DOTALL)
             self.join_pattern = re.compile('(.*)%{join ([a-z][a-z_0-9]+)}')
 
         def substitute(self, template):
@@ -1785,7 +1778,7 @@ def process_template_string(template_text, variables, template_source):
                         for_body += lines[idx] + "\n"
                         idx += 1
 
-                    for i, v in enumerate(var):
+                    for v in var:
                         if isinstance(v, dict):
                             for_val = for_body
                             for ik, iv in v.items():
@@ -1793,14 +1786,6 @@ def process_template_string(template_text, variables, template_source):
                             output += for_val + "\n"
                         else:
                             output += for_body.replace('%{i}', v).replace('%{i|upper}', v.upper())
-
-                        omitlast_match = self.omitlast_pattern.match(output)
-                        if omitlast_match:
-                            output = omitlast_match.group(1)
-                            if i + 1 < len(var):
-                                output += omitlast_match.group(2)
-                            output += omitlast_match.group(3)
-
                     output += "\n"
                 else:
                     output += lines[idx] + "\n"
@@ -2159,7 +2144,7 @@ def create_template_vars(source_paths, build_paths, options, modules, cc, arch,
         'handbook_output_dir': build_paths.handbook_output_dir,
         'doc_output_dir_doxygen': build_paths.doc_output_dir_doxygen,
 
-        'compiler_include_dirs': '%s %s' % (normalize_source_path(build_paths.include_dir), normalize_source_path(build_paths.external_include_dir)),
+        'compiler_include_dirs': '%s %s' % (build_paths.include_dir, build_paths.external_include_dir),
 
         'os': options.os,
         'arch': options.arch,
@@ -3350,7 +3335,7 @@ def do_io_for_build(cc, arch, osinfo, using_mods, info_modules, build_paths, sou
     with open(os.path.join(build_paths.build_dir, 'build_config.json'), 'w') as f:
         json.dump(template_vars, f, sort_keys=True, indent=2)
 
-    if options.with_compilation_database:
+    if options.compiler == 'clang':
         write_template(in_build_dir('compile_commands.json'), in_build_data('compile_commands.json.in'))
 
     if options.with_cmake:
diff --git a/doc/dev_ref/continuous_integration.rst b/doc/dev_ref/continuous_integration.rst
index b345ab43c..cf2818240 100644
--- a/doc/dev_ref/continuous_integration.rst
+++ b/doc/dev_ref/continuous_integration.rst
@@ -4,26 +4,42 @@ Continuous Integration and Automated Testing
 CI Build Script
 ----------------
 
-The Github Actions builds are orchestrated using a script
+The Github Actions, AppVeyor and Travis builds are orchestrated using a script
 ``src/scripts/ci_build.py``. This allows one to easily reproduce the CI process
 on a local machine.
 
 Github Actions
 ---------------
 
-Github Actions is the primary CI, and tests the Linux, Windows, macOS, and iOS
+Github Actions is the primary CI, and tests the Linux, macOS, and iOS
 builds. Among other things it runs tests using valgrind, cross-compilation
 for various architectures (currently including ARM and PPC64), MinGW build,
 and a build that produces the coverage report.
 
-The Github Actions configuration is in ``.github/workflows/ci.yml`` which
-executes platform dependent setup scripts ``src/scripts/ci/setup_gh_actions.sh``
-or ``src/scripts/ci/setup_gh_actions.ps1`` and ``.../setup_gh_actions_after_vcvars.ps1``
-to install needed packages and detect certain platform specifics like compiler
-cache locations.
+The Github Actions configuration is in ``.github/workflows/ci.yml``
+which executes a setup script ``src/scripts/ci/setup_gh_actions.sh`` to
+install needed packages.  Then ``src/scripts/ci_build.py`` is invoked.
 
-Then ``src/scripts/ci_build.py`` is invoked to steer the actual build and test
-runs.
+Travis CI
+----------
+
+https://travis-ci.com/github/randombit/botan
+
+This CI is primarily used to run builds which cannot be run on Github Actions,
+specifically native builds for s390x, ppc64le, aarch64 and armv7.
+
+AppVeyor
+----------
+
+https://ci.appveyor.com/project/randombit/botan
+
+Runs a build/test cycle using MSVC on Windows. Like Github Actions it uses
+``src/scripts/ci_build.py``. The AppVeyor setup script is in
+``src/scripts/ci/setup_appveyor.bat``
+
+The AppVeyor build uses `sccache <https://github.com/mozilla/sccache>`_ as a
+compiler cache. Since that is not available in the AppVeyor images, the setup
+script downloads a release binary from the upstream repository.
 
 LGTM
 ---------
diff --git a/readme.rst b/readme.rst
index 8dcd4f532..846c00c83 100644
--- a/readme.rst
+++ b/readme.rst
@@ -36,9 +36,13 @@ what is new. Botan is also available through most
 `distributions <https://github.com/randombit/botan/wiki/Distros>`_
 such as Fedora, Debian, Arch and Homebrew.
 
-.. image:: https://github.com/randombit/botan/actions/workflows/ci.yml/badge.svg?branch=master
-    :target: https://github.com/randombit/botan/actions/workflows/ci.yml
-    :alt: GitHub Actions results
+.. image:: https://api.travis-ci.com/randombit/botan.svg?branch=master
+    :target: https://travis-ci.com/github/randombit/botan
+    :alt: Travis CI status
+
+.. image:: https://ci.appveyor.com/api/projects/status/n9f94dljd03j2lce/branch/master?svg=true
+    :target: https://ci.appveyor.com/project/randombit/botan/branch/master
+    :alt: AppVeyor CI status
 
 .. image:: https://codecov.io/github/randombit/botan/coverage.svg?branch=master
     :target: https://codecov.io/github/randombit/botan
diff --git a/src/bogo_shim/bogo_shim.cpp b/src/bogo_shim/bogo_shim.cpp
index 869df18b4..b80bf4976 100644
--- a/src/bogo_shim/bogo_shim.cpp
+++ b/src/bogo_shim/bogo_shim.cpp
@@ -82,7 +82,6 @@ std::string map_to_bogo_error(const std::string& e)
          { "Application data before handshake done", ":APPLICATION_DATA_INSTEAD_OF_HANDSHAKE:" },
          { "Bad Hello_Request, has non-zero size", ":BAD_HELLO_REQUEST:" },
          { "Bad code for TLS alert level", ":UNKNOWN_ALERT_TYPE:" },
-         { "Bad encoding on signature algorithms extension", ":DECODE_ERROR:" },
          { "Bad extension size", ":DECODE_ERROR:" },
          { "Bad length in hello verify request", ":DECODE_ERROR:" },
          { "Bad lengths in DTLS header", ":BAD_HANDSHAKE_RECORD:" },
@@ -100,8 +99,6 @@ std::string map_to_bogo_error(const std::string& e)
          { "Certificate key type did not match ciphersuite", ":WRONG_CERTIFICATE_TYPE:" },
          { "Certificate usage constraints do not allow this ciphersuite", ":KEY_USAGE_BIT_INCORRECT:" },
          { "Certificate: Message malformed", ":DECODE_ERROR:" },
-         { "Certificate_Request context must be empty in the main handshake", ":DECODE_ERROR:" },
-         { "Certificate_Request message did not provide a signature_algorithms extension", ":DECODE_ERROR:" },
          { "Channel_Impl_12::key_material_export cannot export during renegotiation", "failed to export keying material" },
          { "Client cert verify failed", ":BAD_SIGNATURE:" },
          { "Client certificate does not support signing", ":KEY_USAGE_BIT_INCORRECT:" },
@@ -126,8 +123,6 @@ std::string map_to_bogo_error(const std::string& e)
          { "Empty ALPN protocol not allowed", ":PARSE_TLSEXT:" },
          { "Encoding error: Cannot encode PSS string, output length too small", ":NO_COMMON_SIGNATURE_ALGORITHMS:" },
          { "Expected TLS but got a record with DTLS version", ":WRONG_VERSION_NUMBER:" },
-         { "Failed to agree on a signature algorithm", ":NO_COMMON_SIGNATURE_ALGORITHMS:" },
-         { "Failed to negotiate a common signature algorithm for client authentication", ":NO_COMMON_SIGNATURE_ALGORITHMS:" },
          { "Finished message didn't verify", ":DIGEST_CHECK_FAILED:" },
          { "Have data remaining in buffer after ClientHello", ":EXCESS_HANDSHAKE_DATA:" },
          { "Have data remaining in buffer after Finished", ":EXCESS_HANDSHAKE_DATA:" },
@@ -829,10 +824,7 @@ class Shim_Policy final : public Botan::TLS::Policy
                {
                const Botan::TLS::Signature_Scheme scheme(pref);
                if(!scheme.is_available())
-                  {
-                  shim_log("skipping inavailable but preferred signature scheme: " + std::to_string(pref));
                   continue;
-                  }
                pref_hash.push_back(scheme.hash_function_name());
                }
 
@@ -884,17 +876,10 @@ class Shim_Policy final : public Botan::TLS::Policy
                schemes.emplace_back(static_cast<uint16_t>(pref));
                }
 
-            // The relevant tests (*-Sign-Negotiate-*) want to configure a preference
-            // for the scheme of our signing operation (-signing-prefs). However, this
-            // policy method (`allowed_signature_schemes`) also restricts the peer's
-            // signing operation. If we weren't to add a few 'common' algorithms, initial
-            // security parameter negotiation would fail.
-            // By placing the BoGo-configured scheme first we make sure our implementation
-            // meets BoGo's expectation when it is our turn to sign.
+            // BoGo gets sad if these are not included in our signature_algorithms extension
             if(!m_args.flag_set("server"))
                {
                schemes.emplace_back(Botan::TLS::Signature_Scheme::RSA_PKCS1_SHA256);
-               schemes.emplace_back(Botan::TLS::Signature_Scheme::RSA_PSS_SHA256);
                schemes.emplace_back(Botan::TLS::Signature_Scheme::ECDSA_SHA256);
                }
 
diff --git a/src/bogo_shim/config.json b/src/bogo_shim/config.json
index 7111d39c4..c00f8a309 100644
--- a/src/bogo_shim/config.json
+++ b/src/bogo_shim/config.json
@@ -12,8 +12,7 @@
         "PartialFinishedWithServerHelloDone": "Unexpected record vs excess handshake data",
         "HelloRetryRequest-DuplicateCurve-TLS13": "expects 'illegal parameter' but we want to stick with 'decode error'",
         "HelloRetryRequest-DuplicateCookie-TLS13": "expects 'illegal parameter' but we want to stick with 'decode error'",
-        "EncryptedExtensionsWithKeyShare-TLS13": "expects 'unsupported extension' but RFC requires 'illegal parameter'",
-        "ClientSkipCertificateVerify-TLS13": "would require ambiguous error mapping"
+        "EncryptedExtensionsWithKeyShare-TLS13": "expects 'unsupported extension' but RFC requires 'illegal parameter'"
     },
 
     "DisabledTests": {
@@ -114,7 +113,23 @@
         "*EarlyData*": "No TLS 1.3 Early Data, yet",
         "TLS13-1RTT-Client-*": "No TLS 1.3 Early Data, yet",
 
-        "SendNoClientCertificateExtensions-TLS13": "-signed-cert-timestamps currently not supported in the shim",
+        "FailCertCallback-Client-TLS13": "No client auth in TLS 1.3, yet",
+        "Client-Sign*-TLS13": "No client auth in TLS 1.3, yet",
+        "TLS13-Client-ClientAuth-": "No client auth in TLS 1.3, yet",
+        "ClientAuth-*-TLS13": "No client auth in TLS 1.3, yet",
+        "TLS13-Client-ClientAuth-*": "No client auth in TLS 1.3, yet",
+        "NoClientCertificate-TLS13": "No client auth in TLS 1.3, yet",
+        "NoCommonAlgorithms-TLS13": "No client auth in TLS 1.3, yet",
+        "ClientAuth-*-TLS13-*": "No client auth in TLS 1.3, yet",
+        "TrailingMessageData-TLS13-CertificateRequest-TLS": "No client auth in TLS 1.3, yet",
+        "RequestContextInHandshake-TLS13": "No client auth in TLS 1.3, yet",
+        "UnknownExtensionInCertificateRequest-TLS13": "No client auth in TLS 1.3, yet",
+        "MissingSignatureAlgorithmsInCertificateRequest-TLS13": "No client auth in TLS 1.3, yet",
+        "ClientSkipCertificateVerify-TLS13": "No client auth in TLS 1.3, yet",
+        "SendReceiveIntermediate-Client-TLS13": "No client auth in TLS 1.3, yet",
+        "TLS13-Client-CertReq-CA-List": "No client auth in TLS 1.3, yet",
+        "SendNoClientCertificateExtensions-TLS13": "No client auth in TLS 1.3, yet",
+
         "KeyUpdate-RequestACK-UnfinishedWrite": "-read-with-unfinished-write currently not supported in the shim",
 
         "*Binder*": "No TLS 1.3",
diff --git a/src/build-data/compile_commands.json.in b/src/build-data/compile_commands.json.in
index 1a29e7625..fa2f4a3e8 100644
--- a/src/build-data/compile_commands.json.in
+++ b/src/build-data/compile_commands.json.in
@@ -1,22 +1,22 @@
 [
-%{for cli_build_info}
+%{for lib_build_info}
     { "directory": "%{abs_root_dir}",
-      "command": "%{cxx}  %{cc_sysroot} %{cxx_abi_flags} %{cc_lang_flags} %{os_feature_macros} %{cc_compile_flags} -DBOTAN_IS_BEING_BUILT %{cc_warning_flags} %{isa_flags} %{include_paths} %{dash_c} %{src} %{dash_o}%{obj}",
+      "command": "%{cxx} %{lib_flags} %{cxx_abi_flags} %{cc_lang_flags} %{os_feature_macros} %{isa_flags} %{include_paths} %{dash_c} %{src} %{dash_o}%{obj}",
       "file": "%{src}"
     },
 %{endfor}
 
-%{for test_build_info}
+%{for cli_build_info}
     { "directory": "%{abs_root_dir}",
-      "command": "%{cxx}  %{cc_sysroot} %{cxx_abi_flags} %{cc_lang_flags} %{os_feature_macros} %{cc_compile_flags} -DBOTAN_IS_BEING_BUILT %{cc_warning_flags} %{isa_flags} %{include_paths} %{dash_c} %{src} %{dash_o}%{obj}",
+      "command": "%{cxx} %{cxx_abi_flags} %{cc_lang_flags} %{os_feature_macros} %{isa_flags} %{include_paths} %{dash_c} %{src} %{dash_o}%{obj}",
       "file": "%{src}"
     },
 %{endfor}
 
-%{for lib_build_info}
+%{for test_build_info}
     { "directory": "%{abs_root_dir}",
-      "command": "%{cxx} %{lib_flags} %{cc_sysroot} %{cxx_abi_flags} %{cc_lang_flags} %{os_feature_macros} %{cc_compile_flags} -DBOTAN_IS_BEING_BUILT %{cc_warning_flags} %{isa_flags} %{include_paths} %{dash_c} %{src} %{dash_o}%{obj}",
+      "command": "%{cxx} %{cxx_abi_flags} %{cc_lang_flags} %{os_feature_macros} %{isa_flags} %{include_paths} %{dash_c} %{src} %{dash_o}%{obj}",
       "file": "%{src}"
-    }%{omitlast ,}
+    },
 %{endfor}
 ]
diff --git a/src/build-data/makefile.in b/src/build-data/makefile.in
index 9b1c1ecd8..592840a16 100644
--- a/src/build-data/makefile.in
+++ b/src/build-data/makefile.in
@@ -24,9 +24,6 @@ LIB_LINKS_TO        = %{external_link_cmd} %{link_to}
 BUILD_DIR_LINK_PATH = %{build_dir_link_path}
 EXE_LINKS_TO        = %{link_to_botan} $(LIB_LINKS_TO) %{extra_libs}
 
-# Note: Remember to adapt the generation of compile_commands.json, when changing
-#       any of those variables or their associations to compilation units.
-#       See compile_commands.json.in and configure.py (generate_build_info)
 BUILD_FLAGS    = $(ABI_FLAGS) $(LANG_FLAGS) $(CXXFLAGS) $(WARN_FLAGS)
 
 SCRIPTS_DIR    = %{scripts_dir}
diff --git a/src/lib/tls/msg_cert_req.cpp b/src/lib/tls/msg_cert_req.cpp
index b613c0f80..241bfe1e5 100644
--- a/src/lib/tls/msg_cert_req.cpp
+++ b/src/lib/tls/msg_cert_req.cpp
@@ -17,7 +17,7 @@
 
 namespace Botan::TLS {
 
-Handshake_Type Certificate_Request_12::type() const
+Handshake_Type Certificate_Req::type() const
    {
    return CERTIFICATE_REQUEST;
    }
@@ -56,7 +56,7 @@ uint8_t cert_type_name_to_code(const std::string& name)
 /**
 * Create a new Certificate Request message
 */
-Certificate_Request_12::Certificate_Request_12(Handshake_IO& io,
+Certificate_Req::Certificate_Req(Handshake_IO& io,
                                  Handshake_Hash& hash,
                                  const Policy& policy,
                                  const std::vector<X509_DN>& ca_certs) :
@@ -70,7 +70,7 @@ Certificate_Request_12::Certificate_Request_12(Handshake_IO& io,
 /**
 * Deserialize a Certificate Request message
 */
-Certificate_Request_12::Certificate_Request_12(const std::vector<uint8_t>& buf)
+Certificate_Req::Certificate_Req(const std::vector<uint8_t>& buf)
    {
    if(buf.size() < 4)
       throw Decoding_Error("Certificate_Req: Bad certificate request");
@@ -115,17 +115,17 @@ Certificate_Request_12::Certificate_Request_12(const std::vector<uint8_t>& buf)
       }
    }
 
-const std::vector<std::string>& Certificate_Request_12::acceptable_cert_types() const
+const std::vector<std::string>& Certificate_Req::acceptable_cert_types() const
    {
    return m_cert_key_types;
    }
 
-const std::vector<X509_DN>& Certificate_Request_12::acceptable_CAs() const
+const std::vector<X509_DN>& Certificate_Req::acceptable_CAs() const
    {
    return m_names;
    }
 
-const std::vector<Signature_Scheme>& Certificate_Request_12::signature_schemes() const
+const std::vector<Signature_Scheme>& Certificate_Req::signature_schemes() const
    {
    return m_schemes;
    }
@@ -133,7 +133,7 @@ const std::vector<Signature_Scheme>& Certificate_Request_12::signature_schemes()
 /**
 * Serialize a Certificate Request message
 */
-std::vector<uint8_t> Certificate_Request_12::serialize() const
+std::vector<uint8_t> Certificate_Req::serialize() const
    {
    std::vector<uint8_t> buf;
 
diff --git a/src/lib/tls/msg_cert_verify.cpp b/src/lib/tls/msg_cert_verify.cpp
index 61defac14..17404e90e 100644
--- a/src/lib/tls/msg_cert_verify.cpp
+++ b/src/lib/tls/msg_cert_verify.cpp
@@ -13,7 +13,6 @@
 #include <botan/internal/tls_handshake_io.h>
 #include <botan/internal/tls_handshake_state.h>
 #include <botan/internal/tls_reader.h>
-#include <botan/internal/stl_util.h>
 #include <botan/pk_keys.h>
 #include <botan/tls_algos.h>
 #include <botan/tls_extensions.h>
@@ -21,13 +20,13 @@
 namespace Botan::TLS {
 
 /*
-* Create a new Certificate Verify message for TLS 1.2
+* Create a new Certificate Verify message
 */
-Certificate_Verify_12::Certificate_Verify_12(Handshake_IO& io,
-                                             Handshake_State& state,
-                                             const Policy& policy,
-                                             RandomNumberGenerator& rng,
-                                             const Private_Key* priv_key)
+Certificate_Verify::Certificate_Verify(Handshake_IO& io,
+                                       Handshake_State& state,
+                                       const Policy& policy,
+                                       RandomNumberGenerator& rng,
+                                       const Private_Key* priv_key)
    {
    BOTAN_ASSERT_NONNULL(priv_key);
 
@@ -107,73 +106,8 @@ bool Certificate_Verify_12::verify(const X509_Certificate& cert,
 
 #if defined(BOTAN_HAS_TLS_13)
 
-namespace {
-
-std::vector<uint8_t> message(Connection_Side side, const Transcript_Hash& hash)
-   {
-   std::vector<uint8_t> msg(64, 0x20);
-   msg.reserve(64 + 33 + 1 + hash.size());
-
-   const std::string context_string = (side == Botan::TLS::Connection_Side::SERVER)
-                                      ? "TLS 1.3, server CertificateVerify"
-                                      : "TLS 1.3, client CertificateVerify";
-
-   msg.insert(msg.end(), context_string.cbegin(), context_string.cend());
-   msg.push_back(0x00);
-
-   msg.insert(msg.end(), hash.cbegin(), hash.cend());
-   return msg;
-   }
-
-Signature_Scheme choose_signature_scheme(
-      const Private_Key& key,
-      const std::vector<Signature_Scheme>& allowed_schemes,
-      const std::vector<Signature_Scheme>& peer_allowed_schemes)
-   {
-   for(Signature_Scheme scheme : allowed_schemes)
-      {
-      if(scheme.is_available()
-         && scheme.is_suitable_for(key)
-         && value_exists(peer_allowed_schemes, scheme))
-         {
-         return scheme;
-         }
-      }
-
-   throw TLS_Exception(Alert::HANDSHAKE_FAILURE, "Failed to agree on a signature algorithm");
-   }
-
-}
-
-/*
-* Create a new Certificate Verify message for TLS 1.3
-*/
-Certificate_Verify_13::Certificate_Verify_13(
-      const std::vector<Signature_Scheme>& peer_allowed_schemes,
-      Connection_Side whoami,
-      const Private_Key& key,
-      const Policy& policy,
-      const Transcript_Hash& hash,
-      Callbacks& callbacks,
-      RandomNumberGenerator& rng)
-   : m_side(whoami)
-   {
-   m_scheme = choose_signature_scheme(key, policy.allowed_signature_schemes(), peer_allowed_schemes);
-   BOTAN_ASSERT_NOMSG(m_scheme.is_available());
-
-   // TODO: we need to verify that the provided private key is strong enough for TLS 1.3
-
-   m_signature =
-      callbacks.tls_sign_message(key,
-                                 rng,
-                                 m_scheme.padding_string(),
-                                 m_scheme.format().value(),
-                                 message(m_side, hash));
-   }
-
-
 Certificate_Verify_13::Certificate_Verify_13(const std::vector<uint8_t>& buf,
-                                             const Connection_Side side)
+      const Connection_Side side)
    : Certificate_Verify(buf)
    , m_side(side)
    {
@@ -199,12 +133,24 @@ bool Certificate_Verify_13::verify(const X509_Certificate& cert,
    if(m_scheme.algorithm_identifier() != cert.subject_public_key_algo())
       { throw TLS_Exception(Alert::ILLEGAL_PARAMETER, "Signature algorithm does not match certificate's public key"); }
 
+   std::vector<uint8_t> msg(64, 0x20);
+   msg.reserve(64 + 32 + 1 + transcript_hash.size());
+
+   const std::string context_string = (m_side == Botan::TLS::Connection_Side::SERVER)
+                                      ? "TLS 1.3, server CertificateVerify"
+                                      : "TLS 1.3, client CertificateVerify";
+
+   msg.insert(msg.end(), context_string.cbegin(), context_string.cend());
+   msg.push_back(0x00);
+
+   msg.insert(msg.end(), transcript_hash.cbegin(), transcript_hash.cend());
+
    const auto key = cert.load_subject_public_key();
    const bool signature_valid =
       callbacks.tls_verify_message(*key,
                                    m_scheme.padding_string(),
                                    m_scheme.format().value(),
-                                   message(m_side, transcript_hash),
+                                   msg,
                                    m_signature);
 
 #if defined(BOTAN_UNSAFE_FUZZER_MODE)
diff --git a/src/lib/tls/tls12/tls_client_impl_12.cpp b/src/lib/tls/tls12/tls_client_impl_12.cpp
index 65c34e51d..bb5916e55 100644
--- a/src/lib/tls/tls12/tls_client_impl_12.cpp
+++ b/src/lib/tls/tls12/tls_client_impl_12.cpp
@@ -599,7 +599,7 @@ void Client_Impl_12::process_handshake_msg(const Handshake_State* active_state,
    else if(type == CERTIFICATE_REQUEST)
       {
       state.set_expected_next(SERVER_HELLO_DONE);
-      state.cert_req(new Certificate_Request_12(contents));
+      state.cert_req(new Certificate_Req(contents));
       }
    else if(type == SERVER_HELLO_DONE)
       {
diff --git a/src/lib/tls/tls12/tls_handshake_state.cpp b/src/lib/tls/tls12/tls_handshake_state.cpp
index 9eeb52c53..54a27c63d 100644
--- a/src/lib/tls/tls12/tls_handshake_state.cpp
+++ b/src/lib/tls/tls12/tls_handshake_state.cpp
@@ -156,7 +156,7 @@ void Handshake_State::server_kex(Server_Key_Exchange* server_kex)
    note_message(*m_server_kex);
    }
 
-void Handshake_State::cert_req(Certificate_Request_12* cert_req)
+void Handshake_State::cert_req(Certificate_Req* cert_req)
    {
    m_cert_req.reset(cert_req);
    note_message(*m_cert_req);
diff --git a/src/lib/tls/tls12/tls_handshake_state.h b/src/lib/tls/tls12/tls_handshake_state.h
index 1b9ec35ed..a249940f7 100644
--- a/src/lib/tls/tls12/tls_handshake_state.h
+++ b/src/lib/tls/tls12/tls_handshake_state.h
@@ -38,7 +38,7 @@ class Server_Hello_12;
 class Certificate_12;
 class Certificate_Status;
 class Server_Key_Exchange;
-class Certificate_Request_12;
+class Certificate_Req;
 class Server_Hello_Done;
 class Client_Key_Exchange;
 class Certificate_Verify_12;
@@ -117,7 +117,7 @@ class Handshake_State
       void server_hello(Server_Hello_12* server_hello);
       void server_cert_status(Certificate_Status* server_cert_status);
       void server_kex(Server_Key_Exchange* server_kex);
-      void cert_req(Certificate_Request_12* cert_req);
+      void cert_req(Certificate_Req* cert_req);
       void server_hello_done(Server_Hello_Done* server_hello_done);
       void client_kex(Client_Key_Exchange* client_kex);
 
@@ -144,7 +144,7 @@ class Handshake_State
       const Server_Key_Exchange* server_kex() const
          { return m_server_kex.get(); }
 
-      const Certificate_Request_12* cert_req() const
+      const Certificate_Req* cert_req() const
          { return m_cert_req.get(); }
 
       const Server_Hello_Done* server_hello_done() const
@@ -207,7 +207,7 @@ class Handshake_State
       std::unique_ptr<Certificate_12> m_server_certs;
       std::unique_ptr<Certificate_Status> m_server_cert_status;
       std::unique_ptr<Server_Key_Exchange> m_server_kex;
-      std::unique_ptr<Certificate_Request_12> m_cert_req;
+      std::unique_ptr<Certificate_Req> m_cert_req;
       std::unique_ptr<Server_Hello_Done> m_server_hello_done;
       std::unique_ptr<Certificate_12> m_client_certs;
       std::unique_ptr<Client_Key_Exchange> m_client_kex;
diff --git a/src/lib/tls/tls12/tls_server_impl_12.cpp b/src/lib/tls/tls12/tls_server_impl_12.cpp
index 4b170d78a..ce307e707 100644
--- a/src/lib/tls/tls12/tls_server_impl_12.cpp
+++ b/src/lib/tls/tls12/tls_server_impl_12.cpp
@@ -916,7 +916,7 @@ void Server_Impl_12::session_create(Server_Handshake_State& pending_state,
    if(request_cert && pending_state.ciphersuite().signature_used())
       {
       pending_state.cert_req(
-         new Certificate_Request_12(pending_state.handshake_io(),
+         new Certificate_Req(pending_state.handshake_io(),
                              pending_state.hash(),
                              policy(),
                              client_auth_CAs));
diff --git a/src/lib/tls/tls13/msg_certificate_13.cpp b/src/lib/tls/tls13/msg_certificate_13.cpp
index 35b822af2..55f7359f5 100644
--- a/src/lib/tls/tls13/msg_certificate_13.cpp
+++ b/src/lib/tls/tls13/msg_certificate_13.cpp
@@ -49,12 +49,6 @@ void Certificate_13::validate_extensions(const std::set<Handshake_Extension_Type
       }
    }
 
-const X509_Certificate& Certificate_13::leaf() const
-   {
-   BOTAN_STATE_CHECK(!empty());
-   return m_entries.front().certificate;
-   }
-
 void Certificate_13::verify(Callbacks& callbacks,
                             const Policy& policy,
                             Credentials_Manager& creds,
@@ -102,17 +96,6 @@ void Certificate_13::verify(Callbacks& callbacks,
    callbacks.tls_verify_cert_chain(certs, ocsp_responses, trusted_CAs, usage, hostname, policy);
    }
 
-Certificate_13::Certificate_13(const std::vector<X509_Certificate>& certs,
-                               const Connection_Side side,
-                               const std::vector<uint8_t> &request_context)
-   : m_request_context(request_context)
-   , m_side(side)
-   {
-   // TODO: proper extensions
-   for (const auto& c : certs)
-      { m_entries.emplace_back(Certificate_Entry{c, Extensions()}); }
-   }
-
 /**
 * Deserialize a Certificate message
 */
@@ -178,7 +161,7 @@ Certificate_13::Certificate_13(const std::vector<uint8_t>& buf,
       //    message as well.
       if(entry.extensions.contains_implemented_extensions_other_than({
             TLSEXT_CERT_STATUS_REQUEST,
-            // TLSEXT_SIGNED_CERTIFICATE_TIMESTAMP
+            // SIGNED_CERTIFICATE_TIMESTAMP
          }))
          {
          throw TLS_Exception(Alert::ILLEGAL_PARAMETER, "Certificate Entry contained an extension that is not allowed");
@@ -219,20 +202,8 @@ Certificate_13::Certificate_13(const std::vector<uint8_t>& buf,
 */
 std::vector<uint8_t> Certificate_13::serialize() const
    {
-   std::vector<uint8_t> buf;
-
-   append_tls_length_value(buf, m_request_context, 1);
-
-   std::vector<uint8_t> entries;
-   for(const auto& entry : m_entries)
-      {
-      append_tls_length_value(entries, entry.certificate.BER_encode(), 3);
-      append_tls_length_value(entries, entry.extensions.serialize(m_side), 2);
-      }
-
-   append_tls_length_value(buf, entries, 3);
-
-   return buf;
+   // Needed only for server implementation or client authentication
+   throw Not_Implemented("NYI");
    }
 
 }
diff --git a/src/lib/tls/tls13/msg_certificate_req_13.cpp b/src/lib/tls/tls13/msg_certificate_req_13.cpp
deleted file mode 100644
index be783381b..000000000
--- a/src/lib/tls/tls13/msg_certificate_req_13.cpp
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
-* (C) 2022 Jack Lloyd
-* (C) 2022 Hannes Rantzsch, René Meusel - neXenio GmbH
-*
-* Botan is released under the Simplified BSD License (see license.txt)
-*/
-
-#include <botan/tls_messages.h>
-#include <botan/internal/tls_reader.h>
-#include <botan/tls_exceptn.h>
-
-namespace Botan::TLS
-{
-
-Handshake_Type Certificate_Request_13::type() const
-   {
-   return Botan::TLS::Handshake_Type::CERTIFICATE_REQUEST;
-   }
-
-Certificate_Request_13::Certificate_Request_13(const std::vector<uint8_t>& buf, const Connection_Side side)
-   {
-   TLS_Data_Reader reader("Certificate_Request_13", buf);
-
-   // RFC 8446 4.3.2
-   //    A server which is authenticating with a certificate MAY optionally
-   //    request a certificate from the client.
-   if(side != Connection_Side::SERVER)
-      {
-      throw TLS_Exception(Alert::UNEXPECTED_MESSAGE, "Received a Certificate_Request message from a client");
-      }
-
-   m_context = reader.get_tls_length_value(1);
-   m_extensions.deserialize(reader, side, type());
-
-   // RFC 8446 4.3.2
-   //    The "signature_algorithms" extension MUST be specified, and other
-   //    extensions may optionally be included if defined for this message.
-   //    Clients MUST ignore unrecognized extensions.
-
-   if(!m_extensions.has<Signature_Algorithms>())
-      {
-      throw TLS_Exception(Alert::MISSING_EXTENSION, "Certificate_Request message did not provide a signature_algorithms extension");
-      }
-
-   // RFC 8446 4.2.
-   //    The table below indicates the messages where a given extension may
-   //    appear [...].  If an implementation receives an extension which it
-   //    recognizes and which is not specified for the message in which it
-   //    appears, it MUST abort the handshake with an "illegal_parameter" alert.
-   //
-   // For Certificate Request said table states:
-   //    "status_request", "signature_algorithms", "signed_certificate_timestamp",
-   //     "certificate_authorities", "oid_filters", "signature_algorithms_cert",
-   std::set<Handshake_Extension_Type> allowed_extensions =
-      {
-      TLSEXT_CERT_STATUS_REQUEST,
-      TLSEXT_SIGNATURE_ALGORITHMS,
-      // TLSEXT_SIGNED_CERTIFICATE_TIMESTAMP,  // NYI
-      TLSEXT_CERTIFICATE_AUTHORITIES,
-      // TLSEXT_OID_FILTERS,                   // NYI
-      TLSEXT_SIGNATURE_ALGORITHMS_CERT,
-      };
-
-   if(m_extensions.contains_implemented_extensions_other_than(allowed_extensions))
-      {
-      throw TLS_Exception(Alert::ILLEGAL_PARAMETER,
-                          "Certificate Request contained an extension that is not allowed");
-      }
-   }
-
-std::vector<X509_DN> Certificate_Request_13::acceptable_CAs() const
-   {
-   if(m_extensions.has<Certificate_Authorities>())
-      return m_extensions.get<Certificate_Authorities>()->distinguished_names();
-   return {};
-   }
-
-const std::vector<Signature_Scheme>& Certificate_Request_13::signature_schemes() const
-   {
-   // RFC 8446 4.3.2
-   //    The "signature_algorithms" extension MUST be specified
-   BOTAN_ASSERT_NOMSG(m_extensions.has<Signature_Algorithms>());
-
-   return m_extensions.get<Signature_Algorithms>()->supported_schemes();
-   }
-
-std::vector<uint8_t> Certificate_Request_13::serialize() const
-   {
-   throw Botan::Not_Implemented("Certificate_Request_13::serialize");
-   }
-
-}  // namespace Botan::TLS
diff --git a/src/lib/tls/tls13/tls_channel_impl_13.cpp b/src/lib/tls/tls13/tls_channel_impl_13.cpp
index 452d18213..1baa32a63 100644
--- a/src/lib/tls/tls13/tls_channel_impl_13.cpp
+++ b/src/lib/tls/tls13/tls_channel_impl_13.cpp
@@ -202,28 +202,16 @@ size_t Channel_Impl_13::received_data(const uint8_t input[], size_t input_size)
       }
    }
 
+void Channel_Impl_13::send_handshake_message(const Handshake_Message_13_Ref message)
+   {
+   std::visit([&](const auto msg) { callbacks().tls_inspect_handshake_msg(msg.get()); }, message);
 
-Channel_Impl_13::AggregatedMessages::AggregatedMessages(Channel_Impl_13& channel,
-                                                        Handshake_Layer& handshake_layer,
-                                                        Transcript_Hash_State& transcript_hash)
-   : m_channel(channel)
-   , m_handshake_layer(handshake_layer)
-   , m_transcript_hash(transcript_hash)
-   {}
+   auto msg = m_handshake_layer.prepare_message(message, m_transcript_hash);
 
-Channel_Impl_13::AggregatedMessages&
-Channel_Impl_13::AggregatedMessages::add(const Handshake_Message_13_Ref message)
-   {
-   std::visit([&](const auto msg) { m_channel.callbacks().tls_inspect_handshake_msg(msg.get()); }, message);
-   m_message_buffer += m_handshake_layer.prepare_message(message, m_transcript_hash);
-   return *this;
-   }
+   if(expects_downgrade() && std::holds_alternative<std::reference_wrapper<Client_Hello_13>>(message))
+      { preserve_client_hello(msg); }
 
-std::vector<uint8_t> Channel_Impl_13::AggregatedMessages::send()
-   {
-   BOTAN_STATE_CHECK(!m_message_buffer.empty());
-   m_channel.send_record(Record_Type::HANDSHAKE, m_message_buffer);
-   return std::exchange(m_message_buffer, {});
+   send_record(Record_Type::HANDSHAKE, msg);
    }
 
 void Channel_Impl_13::send_post_handshake_message(const Post_Handshake_Message_13 message)
diff --git a/src/lib/tls/tls13/tls_channel_impl_13.h b/src/lib/tls/tls13/tls_channel_impl_13.h
index eddeb4d05..b060beaee 100644
--- a/src/lib/tls/tls13/tls_channel_impl_13.h
+++ b/src/lib/tls/tls13/tls_channel_impl_13.h
@@ -22,49 +22,6 @@ namespace Botan::TLS {
 */
 class Channel_Impl_13 : public Channel_Impl
    {
-   protected:
-      /**
-       * Helper class to coalesce handshake messages into a single TLS record of type
-       * 'Handshake'. This is used entirely internally in the Channel, Client and Server
-       * implementation.
-       */
-      class AggregatedMessages
-         {
-         public:
-            AggregatedMessages(Channel_Impl_13& channel,
-                               Handshake_Layer& handshake_layer,
-                               Transcript_Hash_State& transcript_hash);
-
-            AggregatedMessages(const AggregatedMessages&) = delete;
-            AggregatedMessages& operator=(const AggregatedMessages&) = delete;
-            AggregatedMessages(AggregatedMessages&&) = delete;
-            AggregatedMessages& operator=(AggregatedMessages&&) = delete;
-
-            ~AggregatedMessages() = default;
-
-            /**
-             * Adds a single handshake message to the send buffer. Note that this
-             * updates the handshake transcript hash regardless of sending the
-             * message.
-             */
-            AggregatedMessages& add(const Handshake_Message_13_Ref message);
-
-            /**
-             * Send the messages aggregated in the message buffer. The buffer
-             * is returned if the sender needs to also handle it somehow.
-             * Most notable use: book keeping for a potential protocol downgrade
-             * in the client implementation.
-             */
-            std::vector<uint8_t> send();
-
-         private:
-            std::vector<uint8_t> m_message_buffer;
-
-            Channel_Impl_13&       m_channel;
-            Handshake_Layer&       m_handshake_layer;
-            Transcript_Hash_State& m_transcript_hash;
-         };
-
    public:
       /**
       * Set up a new TLS 1.3 session
@@ -188,20 +145,10 @@ class Channel_Impl_13 : public Channel_Impl
        */
       void opportunistically_update_traffic_keys() { m_opportunistic_key_update = true; }
 
-      std::vector<uint8_t> send_handshake_message(const Handshake_Message_13_Ref message)
-         {
-         return aggregate_handshake_messages()
-                   .add(message)
-                   .send();
-         }
+      void send_handshake_message(const Handshake_Message_13_Ref message);
       void send_post_handshake_message(const Post_Handshake_Message_13 message);
       void send_dummy_change_cipher_spec();
 
-      AggregatedMessages aggregate_handshake_messages()
-         {
-         return AggregatedMessages(*this, m_handshake_layer, m_transcript_hash);
-         }
-
       Callbacks& callbacks() const { return m_callbacks; }
       Session_Manager& session_manager() { return m_session_manager; }
       Credentials_Manager& credentials_manager() { return m_credentials_manager; }
diff --git a/src/lib/tls/tls13/tls_client_impl_13.cpp b/src/lib/tls/tls13/tls_client_impl_13.cpp
index b2af75a2c..3993d579d 100644
--- a/src/lib/tls/tls13/tls_client_impl_13.cpp
+++ b/src/lib/tls/tls13/tls_client_impl_13.cpp
@@ -38,16 +38,13 @@ Client_Impl_13::Client_Impl_13(Callbacks& callbacks,
       { expect_downgrade(info); }
 #endif
 
-   auto msg = send_handshake_message(m_handshake_state.sending(Client_Hello_13(
+   send_handshake_message(m_handshake_state.sent(Client_Hello_13(
                              policy,
                              callbacks,
                              rng,
                              m_info.hostname(),
                              next_protocols)));
 
-   if(expects_downgrade())
-      { preserve_client_hello(msg); }
-
    // RFC 8446 Appendix D.4
    //    If not offering early data, the client sends a dummy change_cipher_spec
    //    record [...] immediately before its second flight. This may either be before
@@ -364,29 +361,8 @@ void Client_Impl_13::handle(const Encrypted_Extensions& encrypted_extensions_msg
       }
    }
 
-void Client_Impl_13::handle(const Certificate_Request_13& certificate_request_msg)
-   {
-   // RFC 8446 4.3.2
-   //    [The 'context' field] SHALL be zero length unless used for the
-   //    post-handshake authentication exchanges described in Section 4.6.2.
-   if(!m_handshake_state.handshake_finished() && !certificate_request_msg.context().empty())
-      {
-      throw TLS_Exception(Alert::DECODE_ERROR, "Certificate_Request context must be empty in the main handshake");
-      }
-
-   m_transitions.set_expected_next(CERTIFICATE);
-   }
-
 void Client_Impl_13::handle(const Certificate_13& certificate_msg)
    {
-   // RFC 8446 4.4.2
-   //    certificate_request_context:  [...] In the case of server authentication,
-   //    this field SHALL be zero length.
-   if(!certificate_msg.request_context().empty())
-      {
-      throw TLS_Exception(Alert::DECODE_ERROR, "Received a server certificate message with non-empty request context");
-      }
-
    certificate_msg.validate_extensions(m_handshake_state.client_hello().extensions().extension_types());
    certificate_msg.verify(callbacks(),
                           policy(),
@@ -417,7 +393,7 @@ void Client_Impl_13::handle(const Certificate_Verify_13& certificate_verify_msg)
       }
 
    bool sig_valid = certificate_verify_msg.verify(
-                       m_handshake_state.server_certificate().leaf(),
+                       m_handshake_state.certificate().cert_chain().front().certificate,
                        callbacks(),
                        m_transcript_hash.previous());
 
@@ -427,72 +403,6 @@ void Client_Impl_13::handle(const Certificate_Verify_13& certificate_verify_msg)
    m_transitions.set_expected_next(FINISHED);
    }
 
-void Client_Impl_13::send_client_authentication(Channel_Impl_13::AggregatedMessages& flight)
-   {
-   BOTAN_ASSERT_NOMSG(m_handshake_state.has_certificate_request());
-   const auto& cert_request = m_handshake_state.certificate_request();
-
-   // From all the schemes the server advertised, we need to filter for those
-   // that we can actually support and that are suitable for this protocol version.
-   std::vector<std::string> peer_allowed_signature_algos;
-   for(const Signature_Scheme& scheme : cert_request.signature_schemes())
-      {
-      if(scheme.is_available() && scheme.is_compatible_with(Protocol_Version::TLS_V13))
-         peer_allowed_signature_algos.push_back(scheme.algorithm_name());
-      }
-
-   if(peer_allowed_signature_algos.empty())
-      {
-      throw TLS_Exception(Alert::HANDSHAKE_FAILURE, "Failed to negotiate a common signature algorithm for client authentication");
-      }
-
-   // RFC 4.4.2.1
-   //    A server MAY request that a client present an OCSP response with its
-   //    certificate by sending an empty "status_request" extension in its
-   //    CertificateRequest message.
-   //
-   // TODO: Implement OCSP stapling for client certificates.
-
-   std::vector<X509_Certificate> client_certs = credentials_manager().find_cert_chain(
-         peer_allowed_signature_algos,
-         cert_request.acceptable_CAs(),
-         "tls-client",
-         m_info.hostname());
-
-   // RFC 4.4.2
-   //    certificate_request_context:  If this message is in response to a
-   //       CertificateRequest, the value of certificate_request_context in
-   //       that message.
-   flight.add(m_handshake_state.sending(
-      Certificate_13(std::move(client_certs), CLIENT, cert_request.context())));
-
-   // RFC 4.4.2
-   //    If the server requests client authentication but no suitable certificate
-   //    is available, the client MUST send a Certificate message containing no
-   //    certificates.
-   //
-   // In that case, no Certificate Verify message will be sent.
-   if(!m_handshake_state.client_certificate().cert_chain().empty())
-      {
-      Private_Key* private_key = credentials_manager().private_key_for(
-            m_handshake_state.client_certificate().leaf(),
-            "tls-client",
-            m_info.hostname());
-
-      BOTAN_ASSERT_NONNULL(private_key);
-
-      flight.add(m_handshake_state.sending(Certificate_Verify_13(
-         cert_request.signature_schemes(),
-         Connection_Side::CLIENT,
-         *private_key,
-         policy(),
-         m_transcript_hash.current(),
-         callbacks(),
-         rng()
-      )));
-      }
-   }
-
 void Client_Impl_13::handle(const Finished_13& finished_msg)
    {
    // RFC 8446 4.4.4
@@ -503,26 +413,13 @@ void Client_Impl_13::handle(const Finished_13& finished_msg)
                            m_transcript_hash.previous()))
       { throw TLS_Exception(Alert::DECRYPT_ERROR, "Finished message didn't verify"); }
 
-   // save the current transcript hash as client auth might update the hash multiple times
-   const auto th_server_finished = m_transcript_hash.current();
-
-   auto flight = aggregate_handshake_messages();
-
-   // RFC 8446 4.4.2
-   //    The client MUST send a Certificate message if and only if the server
-   //    has requested client authentication via a CertificateRequest message.
-   if(m_handshake_state.has_certificate_request())
-      { send_client_authentication(flight); }
-
    // send client finished handshake message (still using handshake traffic secrets)
-   flight.add(m_handshake_state.sending(Finished_13(m_cipher_state.get(),
-                                                 m_transcript_hash.current())));
-
-   flight.send();
+   send_handshake_message(m_handshake_state.sent(Finished_13(m_cipher_state.get(),
+                          m_transcript_hash.current())));
 
    // derives the application traffic secrets and _replaces_ the handshake traffic secrets
    // Note: this MUST happen AFTER the client finished message was sent!
-   m_cipher_state->advance_with_server_finished(th_server_finished);
+   m_cipher_state->advance_with_server_finished(m_transcript_hash.previous());
    m_cipher_state->advance_with_client_finished(m_transcript_hash.current());
 
    // TODO: save session and invoke tls_session_established callback
diff --git a/src/lib/tls/tls13/tls_client_impl_13.h b/src/lib/tls/tls13/tls_client_impl_13.h
index b1e5adeb0..03b15cb3c 100644
--- a/src/lib/tls/tls13/tls_client_impl_13.h
+++ b/src/lib/tls/tls13/tls_client_impl_13.h
@@ -75,15 +75,12 @@ class Client_Impl_13 : public Channel_Impl_13
       void handle(const Server_Hello_13& server_hello_msg);
       void handle(const Hello_Retry_Request& hrr_msg);
       void handle(const Encrypted_Extensions& encrypted_extensions_msg);
-      void handle(const Certificate_Request_13& certificate_request_msg);
       void handle(const Certificate_13& certificate_msg);
       void handle(const Certificate_Verify_13& certificate_verify_msg);
       void handle(const Finished_13& finished_msg);
       void handle(const New_Session_Ticket_13& new_session_ticket);
       void handle(const Key_Update& key_update);
 
-      void send_client_authentication(Channel_Impl_13::AggregatedMessages& flight);
-
    private:
       const Server_Information m_info;
 
diff --git a/src/lib/tls/tls13/tls_handshake_layer_13.cpp b/src/lib/tls/tls13/tls_handshake_layer_13.cpp
index f27d59bca..946d0fc4a 100644
--- a/src/lib/tls/tls13/tls_handshake_layer_13.cpp
+++ b/src/lib/tls/tls13/tls_handshake_layer_13.cpp
@@ -37,7 +37,7 @@ Handshake_Type handshake_type_from_byte(uint8_t type)
          // case END_OF_EARLY_DATA:  // NYI: needs PSK/resumption support -- won't be offered in Client Hello for now
          case ENCRYPTED_EXTENSIONS:
          case CERTIFICATE:
-         case CERTIFICATE_REQUEST:
+         // case CERTIFICATE_REQUEST:  // NYI: client auth -- server might still request, resulting in handshake failure
          case CERTIFICATE_VERIFY:
          case FINISHED:
             return Handshake_Type(type);
@@ -93,8 +93,8 @@ std::optional<Msg_Type> parse_message(TLS::TLS_Data_Reader& reader, const Policy
             return Encrypted_Extensions(msg);
          case CERTIFICATE:
             return Certificate_13(msg, policy, peer_side);
-         case CERTIFICATE_REQUEST:
-            return Certificate_Request_13(msg, peer_side);
+         // case CERTIFICATE_REQUEST:
+         //    return Certificate_Req_13(msg);
          case CERTIFICATE_VERIFY:
             return Certificate_Verify_13(msg, peer_side);
          case FINISHED:
diff --git a/src/lib/tls/tls13/tls_handshake_state_13.cpp b/src/lib/tls/tls13/tls_handshake_state_13.cpp
index 25b60b0e4..8c086fd4f 100644
--- a/src/lib/tls/tls13/tls_handshake_state_13.cpp
+++ b/src/lib/tls/tls13/tls_handshake_state_13.cpp
@@ -40,29 +40,16 @@ Encrypted_Extensions& Handshake_State_13_Base::store(Encrypted_Extensions encryp
     return m_encrypted_extensions.value();
 }
 
-Certificate_Request_13& Handshake_State_13_Base::store(Certificate_Request_13 certificate_request, const bool)
+Certificate_13& Handshake_State_13_Base::store(Certificate_13 certificate, const bool)
 {
-   m_certificate_request = std::move(certificate_request);
-   return m_certificate_request.value();
+    m_server_certs = std::move(certificate);
+    return m_server_certs.value();
 }
 
-
-Certificate_13& Handshake_State_13_Base::store(Certificate_13 certificate, const bool from_peer)
-{
-   auto& target = ((m_side == CLIENT) == from_peer)
-          ? m_server_certificate
-          : m_client_certificate;
-   target = std::move(certificate);
-   return target.value();
-}
-
-Certificate_Verify_13& Handshake_State_13_Base::store(Certificate_Verify_13 certificate_verify, const bool from_peer)
+Certificate_Verify_13& Handshake_State_13_Base::store(Certificate_Verify_13 certificate_verify, const bool)
 {
-   auto& target = ((m_side == CLIENT) == from_peer)
-          ? m_server_certificate_verify
-          : m_client_certificate_verify;
-   target = std::move(certificate_verify);
-   return target.value();
+    m_server_verify = std::move(certificate_verify);
+    return m_server_verify.value();
 }
 
 Finished_13& Handshake_State_13_Base::store(Finished_13 finished, const bool from_peer)
diff --git a/src/lib/tls/tls13/tls_handshake_state_13.h b/src/lib/tls/tls13/tls_handshake_state_13.h
index 0ccb9d82c..6abafb08d 100644
--- a/src/lib/tls/tls13/tls_handshake_state_13.h
+++ b/src/lib/tls/tls13/tls_handshake_state_13.h
@@ -26,37 +26,32 @@ class BOTAN_TEST_API Handshake_State_13_Base
    public:
       bool has_client_hello() const { return m_client_hello.has_value(); }
       bool has_hello_retry_request() const { return m_hello_retry_request.has_value(); }
-      bool has_certificate_request() const { return m_certificate_request.has_value(); }
       bool has_server_finished() const { return m_server_finished.has_value(); }
       bool has_client_finished() const { return m_client_finished.has_value(); }
 
       bool handshake_finished() const { return has_server_finished() && has_client_finished(); }
 
       // Client_Hello_13 cannot be const because it might need modification due to a Hello_Retry_Request
-      Client_Hello_13&              client_hello() { return get(m_client_hello); }
-      const Server_Hello_13&        server_hello() const { return get(m_server_hello); }
-      const Hello_Retry_Request&    hello_retry_request() const { return get(m_hello_retry_request); }
-      const Encrypted_Extensions&   encrypted_extensions() const { return get(m_encrypted_extensions); }
-      const Certificate_Request_13& certificate_request() const { return get(m_certificate_request); }
-      const Certificate_13&         server_certificate() const { return get(m_server_certificate); }
-      const Certificate_13&         client_certificate() const { return get(m_client_certificate); }
-      const Certificate_Verify_13&  server_certificate_verify() const { return get(m_server_certificate_verify); }
-      const Certificate_Verify_13&  client_certificate_verify() const { return get(m_client_certificate_verify); }
-      const Finished_13&            server_finished() const { return get(m_server_finished); }
-      const Finished_13&            client_finished() const { return get(m_client_finished); }
+      Client_Hello_13&             client_hello() { return get(m_client_hello); }
+      const Server_Hello_13&       server_hello() const { return get(m_server_hello); }
+      const Hello_Retry_Request&   hello_retry_request() const { return get(m_hello_retry_request); }
+      const Encrypted_Extensions&  encrypted_extensions() const { return get(m_encrypted_extensions); }
+      const Certificate_13&        certificate() const { return get(m_server_certs); }
+      const Certificate_Verify_13& certificate_verify() const { return get(m_server_verify); }
+      const Finished_13&           client_finished() const { return get(m_client_finished); }
+      const Finished_13&           server_finished() const { return get(m_server_finished); }
 
    protected:
       Handshake_State_13_Base(Connection_Side whoami) : m_side(whoami) {}
 
-      Client_Hello_13&        store(Client_Hello_13 client_hello, const bool from_peer);
-      Server_Hello_13&        store(Server_Hello_13 server_hello, const bool from_peer);
-      Server_Hello_12&        store(Server_Hello_12 server_hello, const bool from_peer);
-      Hello_Retry_Request&    store(Hello_Retry_Request hello_retry_request, const bool from_peer);
-      Encrypted_Extensions&   store(Encrypted_Extensions encrypted_extensions, const bool from_peer);
-      Certificate_Request_13& store(Certificate_Request_13 certificate_request, const bool from_peer);
-      Certificate_13&         store(Certificate_13 certificate, const bool from_peer);
-      Certificate_Verify_13&  store(Certificate_Verify_13 certificate_verify, const bool from_peer);
-      Finished_13&            store(Finished_13 finished, const bool from_peer);
+      Client_Hello_13&       store(Client_Hello_13 client_hello, const bool from_peer);
+      Server_Hello_13&       store(Server_Hello_13 server_hello, const bool from_peer);
+      Server_Hello_12&       store(Server_Hello_12 server_hello, const bool from_peer);
+      Hello_Retry_Request&   store(Hello_Retry_Request hello_retry_request, const bool from_peer);
+      Encrypted_Extensions&  store(Encrypted_Extensions encrypted_extensions, const bool from_peer);
+      Certificate_13&        store(Certificate_13 certificate, const bool from_peer);
+      Certificate_Verify_13& store(Certificate_Verify_13 certificate_verify, const bool from_peer);
+      Finished_13&           store(Finished_13 finished, const bool from_peer);
 
    private:
       template<typename MessageT>
@@ -82,11 +77,8 @@ class BOTAN_TEST_API Handshake_State_13_Base
       std::optional<Server_Hello_12> m_server_hello_12;
       std::optional<Hello_Retry_Request> m_hello_retry_request;
       std::optional<Encrypted_Extensions> m_encrypted_extensions;
-      std::optional<Certificate_Request_13> m_certificate_request;
-      std::optional<Certificate_13> m_server_certificate;
-      std::optional<Certificate_13> m_client_certificate;
-      std::optional<Certificate_Verify_13> m_server_certificate_verify;
-      std::optional<Certificate_Verify_13> m_client_certificate_verify;
+      std::optional<Certificate_13> m_server_certs;
+      std::optional<Certificate_Verify_13> m_server_verify;
       std::optional<Finished_13> m_server_finished;
       std::optional<Finished_13> m_client_finished;
    };
@@ -109,7 +101,7 @@ class BOTAN_TEST_API Handshake_State_13 : public Internal::Handshake_State_13_Ba
    public:
       Handshake_State_13() : Handshake_State_13_Base(whoami) {}
 
-      decltype(auto) sending(Outbound_Message_T message)
+      decltype(auto) sent(Outbound_Message_T message)
          {
          return std::visit([&](auto msg) -> Handshake_Message_13_Ref
             {
diff --git a/src/lib/tls/tls_extensions.cpp b/src/lib/tls/tls_extensions.cpp
index b224e4800..23a065553 100644
--- a/src/lib/tls/tls_extensions.cpp
+++ b/src/lib/tls/tls_extensions.cpp
@@ -12,7 +12,6 @@
 #include <botan/internal/tls_reader.h>
 #include <botan/tls_exceptn.h>
 #include <botan/tls_policy.h>
-#include <botan/ber_dec.h>
 
 #include <iterator>
 
@@ -74,9 +73,6 @@ std::unique_ptr<Extension> make_extension(TLS_Data_Reader& reader,
       case TLSEXT_PSK_KEY_EXCHANGE_MODES:
          return std::make_unique<PSK_Key_Exchange_Modes>(reader, size);
 
-      case TLSEXT_CERTIFICATE_AUTHORITIES:
-         return std::make_unique<Certificate_Authorities>(reader, size);
-
       case TLSEXT_SIGNATURE_ALGORITHMS_CERT:
          return std::make_unique<Signature_Algorithms_Cert>(reader, size);
 
@@ -801,33 +797,6 @@ PSK_Key_Exchange_Modes::PSK_Key_Exchange_Modes(TLS_Data_Reader& reader, uint16_t
       }
    }
 
-
-std::vector<uint8_t> Certificate_Authorities::serialize(Connection_Side) const
-   { throw Botan::Not_Implemented("serializing Certificate_Authorities is NYI"); }
-
-Certificate_Authorities::Certificate_Authorities(TLS_Data_Reader& reader, uint16_t extension_size)
-   {
-   if (extension_size < 2)
-      {
-      throw Decoding_Error("Empty certificate_authorities extension is illegal");
-      }
-
-   const uint16_t purported_size = reader.get_uint16_t();
-
-   if(reader.remaining_bytes() != purported_size)
-      throw Decoding_Error("Inconsistent length in certificate_authorities extension");
-
-   while(reader.has_remaining())
-      {
-      std::vector<uint8_t> name_bits = reader.get_tls_length_value(2);
-
-      BER_Decoder decoder(name_bits.data(), name_bits.size());
-      m_distinguished_names.emplace_back();
-      decoder.decode(m_distinguished_names.back());
-      }
-   }
-
-
 Signature_Algorithms_Cert::Signature_Algorithms_Cert(const std::vector<Signature_Scheme>& schemes)
       : m_siganture_algorithms(schemes)
    {
diff --git a/src/lib/tls/tls_extensions.h b/src/lib/tls/tls_extensions.h
index a79931769..57c5a8bb8 100644
--- a/src/lib/tls/tls_extensions.h
+++ b/src/lib/tls/tls_extensions.h
@@ -56,8 +56,6 @@ enum Handshake_Extension_Type {
    TLSEXT_USE_SRTP                  = 14,
    TLSEXT_ALPN                      = 16,
 
-   // TLSEXT_SIGNED_CERTIFICATE_TIMESTAMP = 18,  // NYI
-
    TLSEXT_ENCRYPT_THEN_MAC          = 22,
    TLSEXT_EXTENDED_MASTER_SECRET    = 23,
 
@@ -70,8 +68,6 @@ enum Handshake_Extension_Type {
    TLSEXT_COOKIE                    = 44,
 
    TLSEXT_PSK_KEY_EXCHANGE_MODES    = 45,
-   TLSEXT_CERTIFICATE_AUTHORITIES   = 47,
-   // TLSEXT_OID_FILTERS               = 48,  // NYI
 
    TLSEXT_SIGNATURE_ALGORITHMS_CERT = 50,
    TLSEXT_KEY_SHARE                 = 51,
@@ -562,30 +558,6 @@ class BOTAN_UNSTABLE_API PSK_Key_Exchange_Modes final : public Extension
    };
 
 
-/**
- * Certificate Authorities Extension from RFC 8446 4.2.4
- */
-class BOTAN_UNSTABLE_API Certificate_Authorities final : public Extension
-   {
-   public:
-      static Handshake_Extension_Type static_type()
-         { return TLSEXT_CERTIFICATE_AUTHORITIES; }
-
-      Handshake_Extension_Type type() const override { return static_type(); }
-
-      std::vector<uint8_t> serialize(Connection_Side whoami) const override;
-
-      bool empty() const override { return m_distinguished_names.empty(); }
-
-      const std::vector<X509_DN>& distinguished_names() const
-         { return m_distinguished_names; }
-
-      explicit Certificate_Authorities(TLS_Data_Reader& reader, uint16_t extension_size);
-
-   private:
-      std::vector<X509_DN> m_distinguished_names;
-   };
-
 /**
 * Signature_Algorithms_Cert from RFC 8446
 */
diff --git a/src/lib/tls/tls_messages.h b/src/lib/tls/tls_messages.h
index f23e955bd..1acb3e1c9 100644
--- a/src/lib/tls/tls_messages.h
+++ b/src/lib/tls/tls_messages.h
@@ -495,19 +495,6 @@ class BOTAN_UNSTABLE_API Certificate_13 final : public Handshake_Message
 
       size_t count() const { return m_entries.size(); }
       bool empty() const { return m_entries.empty(); }
-      const X509_Certificate& leaf() const;
-      const std::vector<uint8_t>& request_context() const { return m_request_context; }
-
-      /**
-       * Create a Certificate message
-       *
-       * @param certs            the certificate (chain) to be used for authentication
-       * @param request_context  the context provided in the Certificate Request
-       *                         message when performing post-handshake client auth
-       */
-      Certificate_13(const std::vector<X509_Certificate>& certs,
-                     const Connection_Side side,
-                     const std::vector<uint8_t> &request_context = {});
 
       /**
       * Deserialize a Certificate message
@@ -523,7 +510,7 @@ class BOTAN_UNSTABLE_API Certificate_13 final : public Handshake_Message
       * Validate a Certificate message regarding what extensions are expected based on
       * previous handshake messages.
       *
-      * @param requested_extensions Extensions of Client_Hello or Certificate_Request messages
+      * @param requested_extensions Extensions of Client_Hello or Certificate_Req messages
       */
       void validate_extensions(const std::set<Handshake_Extension_Type>& requested_extensions) const;
 
@@ -541,6 +528,11 @@ class BOTAN_UNSTABLE_API Certificate_13 final : public Handshake_Message
       std::vector<uint8_t> serialize() const override;
 
    private:
+      // RFC 8446 4.4.2
+      //    [...] (in the case of server authentication),
+      //    this field SHALL be zero length.
+      //
+      // TODO: implement when adding support for client certificates
       std::vector<uint8_t>           m_request_context;
       std::vector<Certificate_Entry> m_entries;
       Connection_Side                m_side;
@@ -577,9 +569,10 @@ class BOTAN_UNSTABLE_API Certificate_Status final : public Handshake_Message
    };
 
 /**
-* Certificate Request Message (TLS 1.2)
+* Certificate Request Message
+* TODO: this is 1.2 only
 */
-class BOTAN_UNSTABLE_API Certificate_Request_12 final : public Handshake_Message
+class BOTAN_UNSTABLE_API Certificate_Req final : public Handshake_Message
    {
    public:
       Handshake_Type type() const override;
@@ -590,12 +583,12 @@ class BOTAN_UNSTABLE_API Certificate_Request_12 final : public Handshake_Message
 
       const std::vector<Signature_Scheme>& signature_schemes() const;
 
-      Certificate_Request_12(Handshake_IO& io,
+      Certificate_Req(Handshake_IO& io,
                       Handshake_Hash& hash,
                       const Policy& policy,
                       const std::vector<X509_DN>& allowed_cas);
 
-      explicit Certificate_Request_12(const std::vector<uint8_t>& buf);
+      explicit Certificate_Req(const std::vector<uint8_t>& buf);
 
       std::vector<uint8_t> serialize() const override;
 
@@ -605,29 +598,6 @@ class BOTAN_UNSTABLE_API Certificate_Request_12 final : public Handshake_Message
       std::vector<Signature_Scheme> m_schemes;
    };
 
-#if defined(BOTAN_HAS_TLS_13)
-
-class BOTAN_UNSTABLE_API Certificate_Request_13 final : public Handshake_Message
-   {
-   public:
-      Handshake_Type type() const override;
-
-      Certificate_Request_13(const std::vector<uint8_t>& buf, const Connection_Side side);
-
-      std::vector<X509_DN> acceptable_CAs() const;
-      const std::vector<Signature_Scheme>& signature_schemes() const;
-
-      std::vector<uint8_t> serialize() const override;
-
-      const std::vector<uint8_t> context() const { return m_context; }
-
-   private:
-      std::vector<uint8_t> m_context;
-      Extensions m_extensions;
-   };
-
-#endif
-
 class BOTAN_UNSTABLE_API Certificate_Verify : public Handshake_Message
    {
    public:
@@ -635,8 +605,13 @@ class BOTAN_UNSTABLE_API Certificate_Verify : public Handshake_Message
 
       Signature_Scheme signature_scheme() const { return m_scheme; }
 
+      Certificate_Verify(Handshake_IO& io,
+                         Handshake_State& state,
+                         const Policy& policy,
+                         RandomNumberGenerator& rng,
+                         const Private_Key* key);
+
       Certificate_Verify(const std::vector<uint8_t>& buf);
-      Certificate_Verify() = default;
 
       std::vector<uint8_t> serialize() const override;
 
@@ -653,12 +628,6 @@ class BOTAN_UNSTABLE_API Certificate_Verify_12 final : public Certificate_Verify
    public:
       using Certificate_Verify::Certificate_Verify;
 
-      Certificate_Verify_12(Handshake_IO& io,
-                            Handshake_State& state,
-                            const Policy& policy,
-                            RandomNumberGenerator& rng,
-                            const Private_Key* key);
-
       /**
       * Check the signature on a certificate verify message
       * @param cert the purported certificate
@@ -686,15 +655,6 @@ class BOTAN_UNSTABLE_API Certificate_Verify_13 final : public Certificate_Verify
       Certificate_Verify_13(const std::vector<uint8_t>& buf,
                             const Connection_Side side);
 
-      Certificate_Verify_13(
-            const std::vector<Signature_Scheme>& peer_allowed_schemes,
-            Connection_Side whoami,
-            const Private_Key& key,
-            const Policy& policy,
-            const Transcript_Hash& hash,
-            Callbacks& callbacks,
-            RandomNumberGenerator& rng);
-
       bool verify(const X509_Certificate& cert,
                   Callbacks& callbacks,
                   const Transcript_Hash& transcript_hash) const;
@@ -932,7 +892,7 @@ using Handshake_Message_13 = std::variant<
                              // End_Of_Early_Data,
                              Encrypted_Extensions,
                              Certificate_13,
-                             Certificate_Request_13,
+                             // Certificate_Req_13,
                              Certificate_Verify_13,
                              Finished_13>;
 using Handshake_Message_13_Ref = as_wrapped_references_t<Handshake_Message_13>;
@@ -947,16 +907,18 @@ using Server_Handshake_13_Message = std::variant<
                                     Hello_Retry_Request,
                                     Encrypted_Extensions,
                                     Certificate_13,
-                                    Certificate_Request_13,
                                     Certificate_Verify_13,
                                     Finished_13>;
+                                    // Post-Handshake Messages
+                                    // New_Session_Ticket_13,
+                                    // Key_Update>;
 using Server_Handshake_13_Message_Ref = as_wrapped_references_t<Server_Handshake_13_Message>;
 
 using Client_Handshake_13_Message = std::variant<
                                     Client_Hello_13,
-                                    Certificate_13,
-                                    Certificate_Verify_13,
                                     Finished_13>;
+                                    // Post-Handshake Messages
+                                    // Key_Update>;
 using Client_Handshake_13_Message_Ref = as_wrapped_references_t<Client_Handshake_13_Message>;
 
 #endif // BOTAN_HAS_TLS_13
diff --git a/src/lib/utils/stl_util.h b/src/lib/utils/stl_util.h
index 19ebfbc0a..3c6bcf96d 100644
--- a/src/lib/utils/stl_util.h
+++ b/src/lib/utils/stl_util.h
@@ -14,8 +14,6 @@
 #include <string>
 #include <map>
 #include <set>
-#include <tuple>
-
 #include <botan/secmem.h>
 
 namespace Botan {
@@ -108,34 +106,15 @@ void map_remove_if(Pred pred, T& assoc)
       }
    }
 
-/**
- * Concatenate an arbitrary number of buffers.
- * @return the concatenation of \p buffers as the container type of the first buffer
- */
-template <typename... Ts>
-decltype(auto) concat(Ts&& ...buffers)
+template <typename T> T concat(T buffer) { return buffer; }
+template <typename T, typename... Ts>
+T concat(const T& buffer, const Ts& ...buffers)
    {
-   static_assert(sizeof...(buffers) > 0, "concat requires at least one buffer");
-
-   // TODO: C++20: use std::remove_cvref_t
-   using result_t = std::remove_cv_t<std::remove_reference_t<std::tuple_element_t<0, std::tuple<Ts...>>>>;
-   result_t result;
-   result.reserve((buffers.size() + ...));
-   (result.insert(result.end(), buffers.begin(), buffers.end()), ...);
+   auto result = concat(buffers...);
+   result.insert(result.begin(), buffer.begin(), buffer.end());
    return result;
    }
 
-/**
- * Concatenate an arbitrary number of buffers and define the output buffer
- * type as a mandatory template parameter.
- * @return the concatenation of \p buffers as the user-defined container type
- */
-template <typename ResultT, typename... Ts>
-ResultT concat_as(Ts&& ...buffers)
-   {
-   return concat(ResultT(), std::forward<Ts>(buffers)...);
-   }
-
 template<typename... Alts, typename... Ts>
 constexpr bool holds_any_of(const std::variant<Ts...>& v) noexcept {
     return (std::holds_alternative<Alts>(v) || ...);
diff --git a/src/lib/x509/pkix_enums.h b/src/lib/x509/pkix_enums.h
index 80e22cf62..9d2bbce44 100644
--- a/src/lib/x509/pkix_enums.h
+++ b/src/lib/x509/pkix_enums.h
@@ -31,6 +31,10 @@ enum class Certificate_Status_Code {
    OCSP_NO_REVOCATION_URL = 502,
    OCSP_SERVER_NOT_AVAILABLE = 503,
 
+   // Typo versions of above - will be removed in future major release
+   OSCP_NO_REVOCATION_URL = 502,
+   OSCP_SERVER_NOT_AVAILABLE = 503,
+
    // Errors
    FIRST_ERROR_STATUS = 1000,
 
diff --git a/src/scripts/ci/appveyor.yml b/src/scripts/ci/appveyor.yml
new file mode 100644
index 000000000..6503ba1dc
--- /dev/null
+++ b/src/scripts/ci/appveyor.yml
@@ -0,0 +1,58 @@
+
+# (C) 2015,2016,2017,2019,2020,2021 Jack Lloyd
+# (C) 2015,2016 Simon Warta
+# Botan is released under the Simplified BSD License (see license.txt)
+
+clone_depth: 5
+
+environment:
+  SCCACHE_CACHE_SIZE: 320M
+  SCCACHE_VERSION: 0.2.15
+  APPVEYOR_SAVE_CACHE_ON_ERROR: true
+
+  matrix:
+
+    # MSVC 2019 x86-64 w/debug iterators
+    - CC: VC2019
+      PLATFORM: x86_amd64
+      TARGET: sanitizer
+      APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
+      MAKE_TOOL: jom
+      TARGET_CC: msvc
+
+    # MSVC 2019 static x86-64 with Boost
+    - CC: VC2019
+      PLATFORM: x86_amd64
+      TARGET: static
+      APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
+      BOOST_ROOT: "C:\\Libraries\\boost_1_77_0"
+      BOOST_LIBRARYDIR: "C:\\Libraries\\boost_1_77_0\\lib64-msvc-14.2"
+      BOOST_SYSTEM_LIBRARY: "libboost_system-vc142-mt-x64-1_77"
+      MAKE_TOOL: jom
+      TARGET_CC: msvc
+      EXTRA_FLAGS: "--extra-cxxflags=/DUNICODE --extra-cxxflags=/D_UNICODE"
+
+    # MinGW GCC
+    - CC: MinGW
+      PLATFORM: x86_amd64
+      TARGET: static
+      APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
+      MAKE_TOOL: mingw32-make
+      TARGET_CC: gcc
+
+install:
+  - call src\scripts\ci\setup_appveyor.bat
+
+build_script:
+  - python src\scripts\ci_build.py --os=windows --cc=%TARGET_CC% --without-python3 --compiler-cache=sccache --make-tool=%MAKE_TOOL% --cpu=%PLATFORM% %EXTRA_FLAGS% %TARGET%
+
+# whitelist branches to avoid testing feature branches twice (as branch and as pull request)
+branches:
+  only:
+    - master
+    - release-2
+
+cache:
+  - C:\Users\appveyor\AppData\Local\Mozilla\sccache\cache
+  - C:\projects\botan\jom.zip
+  - C:\projects\botan\sccache-v%SCCACHE_VERSION%-x86_64-pc-windows-msvc.tar.gz
diff --git a/src/scripts/ci/setup_appveyor.bat b/src/scripts/ci/setup_appveyor.bat
new file mode 100644
index 000000000..d9635a2f0
--- /dev/null
+++ b/src/scripts/ci/setup_appveyor.bat
@@ -0,0 +1,26 @@
+
+rem (C) 2017,2019,2021 Jack Lloyd
+rem Botan is released under the Simplified BSD License (see license.txt)
+
+echo Current build setup CC="%CC%" PLATFORM="%PLATFORM%" TARGET="%TARGET%"
+
+if %CC% == VC2019 call "%ProgramFiles(x86)%\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" %PLATFORM%
+if %CC% == MinGW set PATH=%PATH%;C:\msys64\mingw64\bin
+
+rem check compiler version
+if %CC% == MinGW (
+   g++ -v
+) else (
+   cl
+)
+
+if not exist jom.zip (
+  appveyor DownloadFile https://download.qt.io/official_releases/jom/jom_1_1_3.zip -FileName jom.zip
+)
+7z e jom.zip
+
+if not exist sccache-v%SCCACHE_VERSION%-x86_64-pc-windows-msvc.tar.gz (
+  appveyor DownloadFile https://github.com/mozilla/sccache/releases/download/v%SCCACHE_VERSION%/sccache-v%SCCACHE_VERSION%-x86_64-pc-windows-msvc.tar.gz
+)
+tar -xf sccache-v%SCCACHE_VERSION%-x86_64-pc-windows-msvc.tar.gz
+set PATH=%PATH%;sccache-v%SCCACHE_VERSION%-x86_64-pc-windows-msvc
diff --git a/src/scripts/ci/setup_gh_actions.ps1 b/src/scripts/ci/setup_gh_actions.ps1
deleted file mode 100644
index 17c43afb6..000000000
--- a/src/scripts/ci/setup_gh_actions.ps1
+++ /dev/null
@@ -1,33 +0,0 @@
-# Setup script for Windows build hosts on GitHub Actions
-#
-# (C) 2022 Jack Lloyd
-# (C) 2022 René Meusel, Rohde & Schwarz Cybersecurity
-#
-# Botan is released under the Simplified BSD License (see license.txt)
-
-param(
-    [Parameter()]
-    [String]$TARGET,
-    [String]$ARCH
-)
-
-choco install -y jom
-choco install -y sccache
-
-# find the sccache cache location and store it in the build job's environment
-$raw_cl = (sccache --stats-format json --show-stats | ConvertFrom-Json).cache_location
-$cache_location = ([regex] 'Local disk: "(.*)"').Match($raw_cl).groups[1].value
-echo "COMPILER_CACHE_LOCATION=$cache_location" >> $env:GITHUB_ENV
-
-# define a build requirements directory (to be populated in setup_gh_actions_after_vcvars.ps1)
-$depsdir = Join-Path -Path (Get-Location) -ChildPath dependencies
-echo "DEPENDENCIES_LOCATION=$depsdir" >> $env:GITHUB_ENV
-
-# The 3rd-party action (egor-tensin/vs-shell) must be used with 'amd64' to
-# request a 64-bit build environment.
-$identifiers_for_64bit = @("x86_64", "x64", "amd64")
-if($identifiers_for_64bit -contains $ARCH ) {
-    echo "VSENV_ARCH=amd64" >> $env:GITHUB_ENV
-} else {
-    echo "VSENV_ARCH=$ARCH" >> $env:GITHUB_ENV
-}
diff --git a/src/scripts/ci/setup_gh_actions.sh b/src/scripts/ci/setup_gh_actions.sh
index c4765618b..03562d20b 100755
--- a/src/scripts/ci/setup_gh_actions.sh
+++ b/src/scripts/ci/setup_gh_actions.sh
@@ -11,8 +11,7 @@ command -v shellcheck > /dev/null && shellcheck "$0" # Run shellcheck on this if
 
 set -ex
 
-TARGET="$1"
-ARCH="$2"
+TARGET=$1
 
 if type -p "apt-get"; then
     sudo apt-get -qq update
@@ -21,9 +20,6 @@ if type -p "apt-get"; then
     if [ "$TARGET" = "valgrind" ]; then
         sudo apt-get -qq install valgrind
 
-    elif [ "$TARGET" = "static" ] || [ "$TARGET" = "amalgamation" ] || [ "$TARGET" = "shared" ]; then
-        sudo apt-get -qq install libboost-all-dev
-
     elif [ "$TARGET" = "clang" ]; then
         sudo apt-get -qq install clang
 
@@ -78,9 +74,6 @@ else
 
     if [ "$TARGET" = "emscripten" ]; then
         brew install emscripten
-
-    elif [ "$TARGET" = "static" ] || [ "$TARGET" = "amalgamation" ] || [ "$TARGET" = "shared" ]; then
-        brew install boost
     fi
 fi
 
diff --git a/src/scripts/ci/setup_gh_actions_after_vcvars.ps1 b/src/scripts/ci/setup_gh_actions_after_vcvars.ps1
deleted file mode 100644
index 6594e22ba..000000000
--- a/src/scripts/ci/setup_gh_actions_after_vcvars.ps1
+++ /dev/null
@@ -1,14 +0,0 @@
-# Secondary setup script for Windows build hosts on GitHub Actions
-# that is invoked after the build agent set up its Visual Studio environment.
-#
-# (C) 2022 Jack Lloyd
-# (C) 2022 René Meusel, Rohde & Schwarz Cybersecurity
-#
-# Botan is released under the Simplified BSD License (see license.txt)
-
-if ($args[0] -in @('static','shared','amalgamation')) {
-    nuget install -NonInteractive -OutputDirectory $env:DEPENDENCIES_LOCATION -Version 1.79.0 boost
-
-    $boostincdir = Join-Path -Path $env:DEPENDENCIES_LOCATION -ChildPath "boost.1.79.0/lib/native/include"
-    echo "BOOST_INCLUDEDIR=$boostincdir" >> $env:GITHUB_ENV
-}
diff --git a/src/scripts/ci_build.py b/src/scripts/ci_build.py
index 948076010..2ae48e262 100755
--- a/src/scripts/ci_build.py
+++ b/src/scripts/ci_build.py
@@ -2,9 +2,7 @@
 
 """
 CI build script
-(C) 2017-2022 Jack Lloyd
-    2022 René Meusel - Rohde & Schwarz Cybersecurity
-
+(C) 2017,2020 Jack Lloyd
 
 Botan is released under the Simplified BSD License (see license.txt)
 """
@@ -147,10 +145,6 @@ def determine_flags(target, target_os, target_cpu, target_cc, cc_bin,
         # this test compiles under MinGW but fails when run under Wine
         disabled_tests.append('certstor_system')
 
-    if target_os == 'mingw':
-        # make sure to link against static versions of libstdc++, libgcc* and winpthread
-        flags += ['--ldflags=-static']
-
     if target == 'coverage':
         flags += ['--with-coverage-info', '--with-debug-info', '--test-mode', '--terminate-on-asserts']
 
@@ -282,21 +276,18 @@ def determine_flags(target, target_os, target_cpu, target_cc, cc_bin,
         if target_os in ['osx', 'ios']:
             flags += ['--with-commoncrypto']
 
-        if target in ['coverage', 'shared', 'static', 'amalgamation']:
+        if target == 'coverage':
             flags += ['--with-boost']
-            if target_cc == 'clang':
-                # make sure clang ignores warnings in boost headers
-                flags += ["--extra-cxxflags=--system-header-prefix=boost/"]
-
-            if target_os in ['windows', 'mingw']:
-                # ./configure.py needs boost's location on Windows
-                assert 'BOOST_INCLUDEDIR' in os.environ, "Windows needs to know where to find boost (via BOOST_INCLUDEDIR)"
-                flags += ['--with-external-includedir', os.environ.get('BOOST_INCLUDEDIR')]
-
-            if target_os == 'mingw':
-                # apparently mingw needs this legacy socket library version for reasons
-                # as per: https://stackoverflow.com/questions/38770895/how-to-fix-undefined-reference-to-getacceptexsockaddrs-boost-asio-in-clion#comment105791579_38771260
-                flags += ['--ldflags=-static -lwsock32']
+
+        if target_os == 'windows' and target in ['shared', 'static']:
+            # ./configure.py needs extra hand-holding for boost on windows
+            boost_root = os.environ.get('BOOST_ROOT') # remove this with appveyor
+            boost_incl = os.environ.get('BOOST_INCLUDEDIR')
+
+            if boost_root:
+                flags += ['--with-external-includedir', boost_root]
+            elif boost_incl:
+                flags += ['--with-external-includedir', boost_incl]
 
         if target_os == 'linux':
             flags += ['--with-lzma']


Test patch:
diff --git a/src/tests/data/tls_13_rfc8448/transcripts.vec b/src/tests/data/tls_13_rfc8448/transcripts.vec
index b2337431a..da4d4bb68 100644
--- a/src/tests/data/tls_13_rfc8448/transcripts.vec
+++ b/src/tests/data/tls_13_rfc8448/transcripts.vec
@@ -25,18 +25,6 @@ ClientFinished = 1703030035d74f1923c662fd34137c6f502f3dd2b93d951d1b3bc97e42afe23
 Client_CloseNotify = 17030300132ea6cdf749196023e2b3a49491695536426047
 Server_CloseNotify = 1703030013519fc5075cb0884349759ff9ef6f011bb4c6f2
 
-[Client_Authentication_Handshake]
-RNG_Pool = 6a472236328b83af40386d3a3e1f1ce624fa4ed89ab865a4ff0f4144ce3ae233c040b2bb8f3addd20fd4058c547003a3c6f9c1cd915d5e535c87d8d191aaf071
-CurrentTimestamp = 1651826546000
-ClientHello_1 = 16030100c0010000bc03036a472236328b83af40386d3a3e1f1ce624fa4ed89ab865a4ff0f4144ce3ae2330000061301130313020100008d0000000b0009000006736572766572ff01000100000a00140012001d00170018001901000101010201030104003300260024001d0020089cc2671f738d9a671e5b2e464981d05b76e361aa22aea91f1d49ca10a7a362002b0003020304000d0020001e040305030603020308040805080604010501060102010402050206020202002d00020101001c00024001
-ServerHello = 160303005a0200005603033b50fdf1c3d572e40e68953e7fff4e2758459c59afa0582c0ea032874255fe6e00130100002e00330024001d00206c2e50e865919a6b5a12dfaf918f92b442567b0f89bc54478c6921366658f062002b00020304
-ServerHandshakeMessages = 17030302166d0a7ac079b32a94aa68c4e2893e8bd0d3c185f549c236fbbce3d647f08f3c94a2bf424d8708883605ad8955f97718b0213dead13dfb23ebb8381da582756612bcb5a5d40847719fbe9f179bfae656f3ecfd59a4c0d35132ce418a7e46f6b6a60622f8a6c06b28d83360163563be9c37f97eb902326924a72b3ed8c8381277d1581cab9c3715ac2401398467ad7ebfab3d0c3419e750104f7d62c5027901f2e4cd4ca5b8071eb03d3c732d83215066dfc4d291d4c1ff3b8d7e4298f677d4d51dea1168d8f16cb27ba40266313a1fedf9e23cc77f765450f9e96f05d08f3da245b14d4946f07ec81eed6d56f26bd574f0b7f7c7047037c16fce3b23754e662fad73e2b7213f6af296769c99a1d38e6232e0ec8dc4f84d6aa6f7de3887be0057862f9018e0ab396705aa4090ab5f2dff6325a557e7320d4effd46bb4f997d163207cce6665294aa4465541e3fe37ee7350659ea550d6dcb6af3c518852c7a14c3cc15bc32b3273bdf1751da184203135b117d300204fb12d58ca9ac34b68eca27030832f7a4b46d2a55757f63fe8f6e85ac47469e6198da88a64586bf23c69590de822263be75fd836847240c48f8c145cd6bd698962e7edc234ebe59231351eef8d7652cf3b08ab3af6e5ec74c58a8da34b39f9b0d6c4279a9a1f82071729e7059dd7f7b95b9433c4684ce1891a6d33432d52eddb0b8cee9181d403eccc12991f1ad4aa62c36049713a7bb135fdda6661a05a93f8c16f
-MessageToSign = 20202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020544c5320312e332c20636c69656e742043657274696669636174655665726966790044026ef3331bc864da1c08ee32e196d93cc2c5d487e47e957d980e7c3d801b9a
-MessageSignature = 186b2223b503a759c35dba0e9721b4b579138d5f0f5e6ec7feaaf27f3ad7f386c2c7bd7cb2be52fbf5ed8393f406ee79369692ec7ac695651d858219e672a8eb7b2a677b640b46ab630edc5f3f2f8272b9c0d906f81f84ddc5b8c7bcf955c78a3cf99e5016f73e04eb7dfcb28833f13e8f75ec2ff3581e2f098ad4157fd6d6ad
-ClientFinished = 1703030280b46a63934e673841abaf267403bc677f6b6d2a1e2f12bb5f62683bfe36a82673f06d6287ddd609bcf2f5fd3225923d24af3c76682c180ee571a17ca4bfbe2f510dc9a0e1fca5cff2cee87d11cb531a6ef90bf5309a6b63bbbc0b88ea45103a4304091543859fa11ec032ed873444cd5185ead5f6a76420f0f0286acef802c8e4788c23275f1b06da600f4a7decd0bc59d7bef10e649ae32690397fc3d4ed6f30f801d8cd569b71ad4fa05ea7cf2ac2dfa150d220505d4011b34d09d53853eba61a101e4f8dca47d8171a884b19259a3dd48c5ac141983edc77814d25e7f66bbbdb9096839266e06561828ecfb27eafd4e9e81a0b96e3bfa42dae5ad80359b9a6661402c3a2104177030106dbd8f65bb6a0159d512eb13af22a259f313bd58c2e21fe053d57f2a962b0a4ea682c96f70b79b560136192823b27be6a2fb7b1c751ccc0e3303615541485b7b307b423332c11efa80b72f9b80a53e53f7bb38a3af4c59f8008bad0544e5614e688ff57bccd6935f81f447f420c1c1bf4058818e90bf5dc716ccae42524856df8250bcdbd7af65f82dd53061d024f6d2ff5c11e3792a9a70e0ee2a3c20a1b968ac391f8f92831135d25242ada2fe241c2653ec996339dfa12dfae7a3373df88b07ca27aef6dc266a25f13f75c76039c1f46fd7a53ae6399c999f4b2aee18e480d6d12bfae226bbdc92a6ad50b4d3bac7abc3b3651eb5be56f33bf41127b3ca886dc714a50d1490357bd40d9fd6be42209a4ddb9ebb2987e29f120f05814614d2c79320015b461fe7324447670a1af5f65caed15b474ab7faa495016adf808e53b94ef54afbb0e0a3a2732ab597f7d5923c77386aa5124731f8cc73e703b341c175a454939a77ab64313c15cf3fe03c4f33842564976
-Client_CloseNotify = 1703030013e4ad7d44c29245339d355962c779b89ef44c58
-Server_CloseNotify = 17030300131decc5d6e64bba8a6f21b4fd077497da2a90cb
-
 [Middlebox_Compatibility_Mode]
 RNG_Pool = 4e640a3f2c2738f09c9418bd78edccd7559d0531199276d4d92a0e9ee9d77d09a80c165581a8e0d06c0018d54d3a06dd32cfd4051eb026fad3fd0ba99269e6efdea00b45695dc781f19d34a62c1afd31ab4369af1e855a3bbb258d8442cde6d7
 CurrentTimestamp = 1651826546000
diff --git a/src/tests/runner/test_runner.cpp b/src/tests/runner/test_runner.cpp
index ba7f7e746..1b931b958 100644
--- a/src/tests/runner/test_runner.cpp
+++ b/src/tests/runner/test_runner.cpp
@@ -18,6 +18,8 @@
    #include <botan/internal/rwlock.h>
 #endif
 
+#include <shared_mutex>
+
 namespace Botan_Tests {
 
 Test_Runner::Test_Runner(std::ostream& out) : m_output(out) {}
@@ -223,6 +225,15 @@ std::vector<Test::Result> run_a_test(const std::string& test_name)
       else if(std::unique_ptr<Test> test = Test::get_test(test_name))
          {
          std::vector<Test::Result> test_results = test->run();
+         for (auto& result : test_results)
+            {
+            if(!result.code_location() && test->registration_location())
+               {
+               // If a test result has no specific code location associated to it,
+               // we fall back to the test case's registration location.
+               result.set_code_location(test->registration_location().value());
+               }
+            }
          results.insert(results.end(), test_results.begin(), test_results.end());
          }
       else
@@ -282,17 +293,13 @@ bool Test_Runner::run_tests_multithreaded(const std::vector<std::string>& tests_
    std::vector<std::future<std::vector<Test::Result>>> m_fut_results;
 
    auto run_test_exclusive = [&](const std::string& test_name) {
-      rwlock.lock();
-      std::vector<Test::Result> results = run_a_test(test_name);
-      rwlock.unlock();
-      return results;
+      std::unique_lock lk(rwlock);
+      return run_a_test(test_name);
    };
 
    auto run_test_shared = [&](const std::string& test_name) {
-      rwlock.lock_shared();
-      std::vector<Test::Result> results = run_a_test(test_name);
-      rwlock.unlock_shared();
-      return results;
+      std::shared_lock lk(rwlock);
+      return run_a_test(test_name);
    };
 
    for(auto const& test_name : tests_to_run)
diff --git a/src/tests/test_tls_handshake_state_13.cpp b/src/tests/test_tls_handshake_state_13.cpp
index fc1c275a9..23149911e 100644
--- a/src/tests/test_tls_handshake_state_13.cpp
+++ b/src/tests/test_tls_handshake_state_13.cpp
@@ -56,7 +56,7 @@ std::vector<Test::Result> finished_message_handling()
 
          Finished_13 client_finished(client_finished_message);
 
-         auto client_fin = state.sending(std::move(client_finished));
+         auto client_fin = state.sent(std::move(client_finished));
          result.require("client can send client finished",
                         std::holds_alternative<std::reference_wrapper<Finished_13>>(client_fin));
          result.test_throws("not stored as server Finished", [&]
@@ -84,7 +84,7 @@ std::vector<Test::Result> handshake_message_filtering()
          {
          Client_Handshake_State_13 state;
 
-         auto filtered = state.sending(Client_Hello_13(client_hello_message));
+         auto filtered = state.sent(Client_Hello_13(client_hello_message));
          result.confirm("client can send client hello",
                         std::holds_alternative<std::reference_wrapper<Client_Hello_13>>(filtered));
 
diff --git a/src/tests/test_tls_rfc8448.cpp b/src/tests/test_tls_rfc8448.cpp
index 6d91a7198..2792e99d3 100644
--- a/src/tests/test_tls_rfc8448.cpp
+++ b/src/tests/test_tls_rfc8448.cpp
@@ -44,7 +44,6 @@
 
    #include <botan/internal/loadstor.h>
 
-   #include <botan/pk_algs.h>
    #include <botan/pkcs8.h>
    #include <botan/data_src.h>
 #endif
@@ -75,23 +74,6 @@ Botan::X509_Certificate server_certificate()
    return Botan::X509_Certificate(in);
    }
 
-std::unique_ptr<Botan::Private_Key> client_private_key()
-   {
-   // RFC 8448 does not actually provide that key. Hence we generate one on the
-   // fly as a stand-in. Instead of actually using it, the signatures generated
-   // by this private key must be hard-coded in `Callbacks::sign_message()`; see
-   // `MockSignature_Fn` for more details.
-   return create_private_key("RSA", Test::rng(), "1024");
-   }
-
-Botan::X509_Certificate client_certificate()
-   {
-   // self-signed certificate with an RSA1024 public key valid until:
-   //   Jul 30 01:23:59 2026 GMT
-   Botan::DataSource_Memory in(Test::read_data_file("tls_13_rfc8448/client_certificate.pem"));
-   return Botan::X509_Certificate(in);
-   }
-
 /**
 * Simple version of the Padding extension (RFC 7685) to reproduce the
 * 2nd Client_Hello in RFC8448 Section 5 (HelloRetryRequest)
@@ -130,25 +112,19 @@ std::chrono::system_clock::time_point from_milliseconds_since_epoch(uint64_t mse
           std::chrono::milliseconds(additional_millis);
    }
 
-using Modify_Exts_Fn = std::function<void(Botan::TLS::Extensions&, Botan::TLS::Connection_Side)>;
-using MockSignature_Fn = std::function<std::vector<uint8_t>(const std::vector<uint8_t>&,const std::string&,Signature_Format)>;
-
 /**
  * Subclass of the Botan::TLS::Callbacks instrumenting all available callbacks.
  * The invocation counts can be checked in the integration tests to make sure
  * all expected callbacks are hit. Furthermore collects the received application
  * data and sent record bytes for further inspection by the test cases.
  */
+using Modify_Exts_Fn = std::function<void(Botan::TLS::Extensions&, Botan::TLS::Connection_Side)>;
 class Test_TLS_13_Callbacks : public Botan::TLS::Callbacks
    {
    public:
-      Test_TLS_13_Callbacks(
-         Modify_Exts_Fn modify_exts_cb,
-         MockSignature_Fn mock_signature_fn,
-         uint64_t timestamp) :
+      Test_TLS_13_Callbacks(Modify_Exts_Fn modify_exts_cb, uint64_t timestamp) :
          session_activated_called(false),
          m_modify_exts(std::move(modify_exts_cb)),
-         m_mock_signature(std::move(mock_signature_fn)),
          m_timestamp(from_milliseconds_since_epoch(timestamp))
          {}
 
@@ -221,13 +197,10 @@ class Test_TLS_13_Callbacks : public Botan::TLS::Callbacks
          const std::vector<uint8_t>& msg) override
          {
          count_callback_invocation("tls_sign_message");
-
-         auto mock_signature = m_mock_signature(msg, emsa, format);
-         return (mock_signature.empty())
-            ? Callbacks::tls_sign_message(key, rng, emsa, format, msg)
-            : mock_signature;
+         return Callbacks::tls_sign_message(key, rng, emsa, format, msg);
          }
 
+
       bool tls_verify_message(
          const Public_Key& key,
          const std::string& emsa,
@@ -344,40 +317,11 @@ class Test_TLS_13_Callbacks : public Botan::TLS::Callbacks
       std::vector<uint8_t>                  receive_buffer;
       uint64_t                              received_seq_no;
       Modify_Exts_Fn                        m_modify_exts;
-      MockSignature_Fn     m_mock_signature;
       std::chrono::system_clock::time_point m_timestamp;
 
       mutable std::map<std::string, unsigned int> m_callback_invocations;
    };
 
-class Test_Client_Credentials : public Botan::Credentials_Manager
-   {
-   public:
-      Test_Client_Credentials() : m_bogus_key(client_private_key()) {}
-
-      std::vector<Botan::X509_Certificate> cert_chain(
-         const std::vector<std::string>& cert_key_types,
-         const std::string& type,
-         const std::string& context) override
-         {
-         BOTAN_UNUSED(cert_key_types, type, context);
-         return { client_certificate() };
-         }
-
-      Botan::Private_Key* private_key_for(const Botan::X509_Certificate& cert,
-                                          const std::string& type,
-                                          const std::string& context) override
-         {
-         BOTAN_UNUSED(cert, type, context);
-         // Note that this key is just a stand-in and not the actual private
-         // key for the client certificate. RFC 8448 does not reveal this key.
-         return m_bogus_key.get();
-         }
-
-   private:
-      std::unique_ptr<Botan::Private_Key> m_bogus_key;
-   };
-
 class RFC8448_Text_Policy : public Botan::TLS::Text_Policy
    {
    public:
@@ -423,9 +367,8 @@ class TLS_Context
       TLS_Context(std::unique_ptr<Botan::RandomNumberGenerator> rng_in,
                   RFC8448_Text_Policy policy,
                   Modify_Exts_Fn modify_exts_cb,
-                  MockSignature_Fn mock_signature_cb,
                   uint64_t timestamp)
-         : m_callbacks(std::move(modify_exts_cb), std::move(mock_signature_cb), timestamp)
+         : m_callbacks(std::move(modify_exts_cb), timestamp)
          , m_rng(std::move(rng_in))
          , m_session_mgr(*m_rng)
          , m_policy(std::move(policy))
@@ -485,8 +428,8 @@ class TLS_Context
       virtual void send(const std::vector<uint8_t>& data) = 0;
 
    protected:
-      Test_TLS_13_Callbacks   m_callbacks;
-      Test_Client_Credentials m_creds;
+      Test_TLS_13_Callbacks      m_callbacks;
+      Botan::Credentials_Manager m_creds;
 
       std::unique_ptr<Botan::RandomNumberGenerator> m_rng;
       Botan::TLS::Session_Manager_In_Memory         m_session_mgr;
@@ -498,10 +441,9 @@ class Client_Context : public TLS_Context
    public:
       Client_Context(std::unique_ptr<Botan::RandomNumberGenerator> rng_in,
                      RFC8448_Text_Policy policy,
-                     uint64_t timestamp,
                      Modify_Exts_Fn modify_exts_cb,
-                     MockSignature_Fn mock_signature_cb = [](auto,auto,auto) { return std::vector<uint8_t>(); })
-         : TLS_Context(std::move(rng_in), std::move(policy), std::move(modify_exts_cb), std::move(mock_signature_cb), timestamp)
+                     uint64_t timestamp)
+         : TLS_Context(std::move(rng_in), std::move(policy), std::move(modify_exts_cb), timestamp)
          , client(m_callbacks, m_session_mgr, m_creds, m_policy, *m_rng,
                   Botan::TLS::Server_Information("server"),
                   Botan::TLS::Protocol_Version::TLS_V13)
@@ -594,7 +536,7 @@ class Test_TLS_RFC8448 final : public Text_Based_Test
    {
    public:
       Test_TLS_RFC8448()
-         : Text_Based_Test("tls_13_rfc8448/transcripts.vec", "RNG_Pool,CurrentTimestamp,ClientHello_1,ServerHello,ServerHandshakeMessages,ClientFinished,Client_CloseNotify,Server_CloseNotify", "HelloRetryRequest,ClientHello_2,NewSessionTicket,Client_AppData,Client_AppData_Record,Server_AppData,Server_AppData_Record,MessageToSign,MessageSignature") {}
+         : Text_Based_Test("tls_13_rfc8448/transcripts.vec", "RNG_Pool,CurrentTimestamp,ClientHello_1,ServerHello,ServerHandshakeMessages,ClientFinished,Client_CloseNotify,Server_CloseNotify", "HelloRetryRequest,ClientHello_2,NewSessionTicket,Client_AppData,Client_AppData_Record,Server_AppData,Server_AppData_Record") {}
 
    Test::Result run_one_test(const std::string& header,
                              const VarMap& vars) override
@@ -603,8 +545,6 @@ class Test_TLS_RFC8448 final : public Text_Based_Test
          return Test::Result("Simple 1-RTT (Client side)", simple_1_rtt_client_hello(vars));
       else if(header == "HelloRetryRequest_Handshake")
          return Test::Result("Handshake involving Hello Retry Request (Client side)", hello_retry_request(vars));
-      else if(header == "Client_Authentication_Handshake")
-         return Test::Result("Client Authentication (Client side)", client_authentication(vars));
       else if(header == "Middlebox_Compatibility_Mode")
          return Test::Result("Middlebox Compatibility Mode (Client side)", middlebox_compatibility(vars));
       else
@@ -638,7 +578,7 @@ class Test_TLS_RFC8448 final : public Text_Based_Test
          return {
             Botan_Tests::CHECK("Client Hello", [&](Test::Result& result)
                {
-               ctx = std::make_unique<Client_Context>(std::move(rng), read_tls_policy("rfc8448_1rtt"), vars.get_req_u64("CurrentTimestamp"), add_extensions_and_sort);
+               ctx = std::make_unique<Client_Context>(std::move(rng), read_tls_policy("rfc8448_1rtt"), add_extensions_and_sort, vars.get_req_u64("CurrentTimestamp"));
 
                result.confirm("client not closed", !ctx->client.is_closed());
                ctx->check_callback_invocations(result, "client hello prepared", { "tls_emit_data", "tls_inspect_handshake_msg_client_hello", "tls_modify_extensions" });
@@ -767,7 +707,7 @@ class Test_TLS_RFC8448 final : public Text_Based_Test
          return {
             Botan_Tests::CHECK("Client Hello", [&](Test::Result& result)
                {
-               ctx = std::make_unique<Client_Context>(std::move(rng), read_tls_policy("rfc8448_hrr"), vars.get_req_u64("CurrentTimestamp"), add_extensions_and_sort);
+               ctx = std::make_unique<Client_Context>(std::move(rng), read_tls_policy("rfc8448_hrr"), add_extensions_and_sort, vars.get_req_u64("CurrentTimestamp"));
                result.confirm("client not closed", !ctx->client.is_closed());
 
                ctx->check_callback_invocations(result, "client hello prepared", { "tls_emit_data", "tls_inspect_handshake_msg_client_hello", "tls_modify_extensions" });
@@ -833,112 +773,6 @@ class Test_TLS_RFC8448 final : public Text_Based_Test
             };
          }
 
-      static std::vector<Test::Result> client_authentication(const VarMap& vars)
-         {
-         auto rng = std::make_unique<Botan_Tests::Fixed_Output_RNG>("");
-         rng->add_entropy(std::vector<uint8_t>(32).data(), 32);  // used by session mgr for session key
-
-         // 32 - for client hello random
-         // 32 - for eph. x25519 key pair
-         add_entropy(*rng, vars.get_req_bin("RNG_Pool"));
-
-         auto add_extensions_and_sort = [&](Botan::TLS::Extensions& exts, Botan::TLS::Connection_Side side)
-            {
-            add_psk_exchange_modes(exts);
-            add_renegotiation_extension(exts);
-            sort_extensions(exts, side);
-            };
-
-         auto sign_certificate_verify = [&](const std::vector<uint8_t>& msg,
-                                            const std::string& emsa,
-                                            Signature_Format format)
-            {
-            BOTAN_UNUSED(emsa, format);
-
-            if(msg != vars.get_req_bin("MessageToSign"))
-               {
-               throw Test_Error("Unexpected message to be signed: " + Botan::hex_encode(msg));
-               }
-
-            if(emsa != "PSSR(SHA-256,MGF1,32)" || format != Signature_Format::IEEE_1363)
-               {
-               throw Test_Error("TLS implementation selected unexpected signature format ("
-                                + std::to_string(format)
-                                + ") or EMSA: "
-                                + emsa);
-               }
-
-            return vars.get_req_bin("MessageSignature");
-            };
-
-         std::unique_ptr<Client_Context> ctx;
-
-         return {
-            Botan_Tests::CHECK("Client Hello", [&](Test::Result& result)
-               {
-               ctx = std::make_unique<Client_Context>(std::move(rng), read_tls_policy("rfc8448_1rtt"), vars.get_req_u64("CurrentTimestamp"), add_extensions_and_sort, sign_certificate_verify);
-
-               ctx->check_callback_invocations(result, "initial callbacks", {
-                  "tls_emit_data",
-                  "tls_inspect_handshake_msg_client_hello",
-                  "tls_modify_extensions",
-                  });
-
-               result.test_eq("Client Hello", ctx->pull_send_buffer(), vars.get_req_bin("ClientHello_1"));
-               }),
-
-            Botan_Tests::CHECK("Server Hello", [&](auto& result)
-               {
-               ctx->client.received_data(vars.get_req_bin("ServerHello"));
-
-               ctx->check_callback_invocations(result, "callbacks after server hello", {
-                  "tls_examine_extensions",
-                  "tls_inspect_handshake_msg_server_hello",
-                  });
-               }),
-
-            Botan_Tests::CHECK("other handshake messages and client auth", [&](Test::Result& result)
-               {
-               ctx->client.received_data(vars.get_req_bin("ServerHandshakeMessages"));
-
-               ctx->check_callback_invocations(result, "signing callbacks invoked", {
-                  "tls_sign_message",
-                  "tls_emit_data",
-                  "tls_examine_extensions",
-                  "tls_inspect_handshake_msg_certificate",
-                  "tls_inspect_handshake_msg_certificate_request",
-                  "tls_inspect_handshake_msg_certificate_verify",
-                  "tls_inspect_handshake_msg_encrypted_extensions",
-                  "tls_inspect_handshake_msg_finished",
-                  "tls_session_activated",
-                  "tls_verify_cert_chain",
-                  "tls_verify_message",
-                  });
-
-               // ClientFinished contains the entire coalesced client authentication flight
-               // Messages: Certificate, CertificateVerify, Finished
-               result.test_eq("Client Auth and Finished", ctx->pull_send_buffer(), vars.get_req_bin("ClientFinished"));
-               }),
-
-            Botan_Tests::CHECK("Close Connection", [&](Test::Result& result)
-               {
-               ctx->client.close();
-               result.test_eq("Client close_notify", ctx->pull_send_buffer(), vars.get_req_bin("Client_CloseNotify"));
-
-               ctx->check_callback_invocations(result, "after sending close notify", {
-                  "tls_emit_data",
-                  });
-
-               ctx->client.received_data(vars.get_req_bin("Server_CloseNotify"));
-               result.confirm("connection closed", ctx->client.is_closed());
-
-               ctx->check_callback_invocations(result, "after receiving close notify", {
-                  "tls_alert",
-                  });
-               }),
-         };
-      }
-
       static std::vector<Test::Result> middlebox_compatibility(const VarMap& vars)
          {
          auto rng = std::make_unique<Botan_Tests::Fixed_Output_RNG>("");
@@ -961,7 +795,7 @@ class Test_TLS_RFC8448 final : public Text_Based_Test
          return {
             Botan_Tests::CHECK("Client Hello", [&](Test::Result& result)
                {
-               ctx = std::make_unique<Client_Context>(std::move(rng), read_tls_policy("rfc8448_compat"), vars.get_req_u64("CurrentTimestamp"), add_extensions_and_sort);
+               ctx = std::make_unique<Client_Context>(std::move(rng), read_tls_policy("rfc8448_compat"), add_extensions_and_sort, vars.get_req_u64("CurrentTimestamp"));
 
                result.test_eq("Client Hello", ctx->pull_send_buffer(), vars.get_req_bin("ClientHello_1"));
                }),
diff --git a/src/tests/tests.cpp b/src/tests/tests.cpp
index 8158dcef8..827c70d18 100644
--- a/src/tests/tests.cpp
+++ b/src/tests/tests.cpp
@@ -32,9 +32,18 @@ namespace Botan_Tests {
 
 void Test::Result::merge(const Result& other, bool ignore_test_name)
    {
-   if(!ignore_test_name && who() != other.who())
+   if(who() != other.who())
       {
-      throw Test_Error("Merging tests from different sources");
+      if(!ignore_test_name)
+         throw Test_Error("Merging tests from different sources");
+
+      // When deliberately merging results with different names, the code location is
+      // likely inconsistent and must be discarded.
+      m_where.reset();
+      }
+   else
+      {
+      m_where = other.m_where;
       }
 
    m_ns_taken += other.m_ns_taken;
@@ -471,7 +480,12 @@ std::string Test::Result::result_string() const
 
    for(size_t i = 0; i != m_fail_log.size(); ++i)
       {
-      report << "Failure " << (i + 1) << ": " << m_fail_log[i] << "\n";
+      report << "Failure " << (i + 1) << ": " << m_fail_log[i];
+      if(m_where)
+         {
+         report << " (at " << m_where->path << ":" << m_where->line << ")";
+         }
+       report << "\n";
       }
 
    if(!m_fail_log.empty() || tests_run() == 0 || verbose)
diff --git a/src/tests/tests.h b/src/tests/tests.h
index df2d1be36..21634db17 100644
--- a/src/tests/tests.h
+++ b/src/tests/tests.h
@@ -134,6 +134,15 @@ class Test_Options
       bool m_abort_on_first_fail;
    };
 
+/**
+ * A code location consisting of the source file path and a line
+ */
+struct CodeLocation
+   {
+   std::string path;
+   unsigned int line;
+   };
+
 /*
 * A generic test which returns a set of results when run.
 * The tests may not all have the same type (for example test
@@ -530,8 +539,12 @@ class Test
             void start_timer();
             void end_timer();
 
+            void set_code_location(CodeLocation where) { m_where = std::move(where); }
+            const std::optional<CodeLocation>& code_location() const { return m_where; }
+
          private:
             std::string m_who;
+            std::optional<CodeLocation> m_where;
             uint64_t m_started = 0;
             uint64_t m_ns_taken = 0;
             size_t m_tests_passed = 0;
@@ -544,6 +557,9 @@ class Test
 
       virtual std::vector<std::string> possible_providers(const std::string&);
 
+      void set_registration_location(CodeLocation location) { m_registration_location = std::move(location); }
+      const std::optional<CodeLocation>& registration_location() const { return m_registration_location; }
+
       static void register_test(const std::string& category,
                                 const std::string& name,
                                 const std::function<std::unique_ptr<Test> ()>& maker_fn);
@@ -614,6 +630,8 @@ class Test
    private:
       static Test_Options m_opts;
       static std::unique_ptr<Botan::RandomNumberGenerator> m_test_rng;
+
+      std::optional<CodeLocation> m_registration_location;  /// The source file location where the test was registered
    };
 
 /*
@@ -623,15 +641,20 @@ template<typename Test_Class>
 class TestClassRegistration
    {
    public:
-      TestClassRegistration(const std::string& category, const std::string& name)
+      TestClassRegistration(std::string category, std::string name, CodeLocation registration_location)
          {
-         auto test_maker = []() -> std::unique_ptr<Test> { return std::make_unique<Test_Class>(); };
+         auto test_maker = [=]() -> std::unique_ptr<Test>
+            {
+            auto test = std::make_unique<Test_Class>();
+            test->set_registration_location(std::move(registration_location));
+            return test;
+            };
          Test::register_test(category, name, test_maker);
          }
    };
 
 #define BOTAN_REGISTER_TEST(category, name, Test_Class)                 \
-   const TestClassRegistration<Test_Class> reg_ ## Test_Class ## _tests(category, name)
+   const TestClassRegistration<Test_Class> reg_ ## Test_Class ## _tests(category, name, {__FILE__, __LINE__})
 
 typedef Test::Result(*test_fn)();
 typedef std::vector<Test::Result> (*test_fn_vec)();
@@ -696,15 +719,20 @@ class TestFnRegistration
    {
    public:
       template <typename... TestFns>
-      TestFnRegistration(const std::string& category, const std::string& name, TestFns... fn)
+      TestFnRegistration(std::string category, std::string name, CodeLocation registration_location, TestFns... fn)
          {
-         auto test_maker = [=]() -> std::unique_ptr<Test> { return std::make_unique<FnTest>(fn...); };
+         auto test_maker = [=]() -> std::unique_ptr<Test>
+            {
+            auto test = std::make_unique<FnTest>(fn...);
+            test->set_registration_location(std::move(registration_location));
+            return test;
+            };
          Test::register_test(category, name, test_maker);
          }
    };
 
 #define BOTAN_REGISTER_TEST_FN(category, name, ...) \
-   static const TestFnRegistration reg_ ## fn_name(category, name, __VA_ARGS__)
+   static const TestFnRegistration reg_ ## fn_name(category, name, {__FILE__, __LINE__}, __VA_ARGS__)
 
 class VarMap
    {