From e2c2dfaab3139c127164d7f58d6765aa27166577 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bence=20Sz=C3=A9pk=C3=BAti?= Date: Mon, 17 Nov 2025 16:53:01 +0100 Subject: [PATCH 01/34] Move abi_check.py into the framework MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bence Szépkúti --- scripts/abi_check.py | 711 ------------------------------------------- 1 file changed, 711 deletions(-) delete mode 100755 scripts/abi_check.py diff --git a/scripts/abi_check.py b/scripts/abi_check.py deleted file mode 100755 index 4fe7f54fc0..0000000000 --- a/scripts/abi_check.py +++ /dev/null @@ -1,711 +0,0 @@ -#!/usr/bin/env python3 -"""This script compares the interfaces of two versions of Mbed TLS, looking -for backward incompatibilities between two different Git revisions within -an Mbed TLS repository. It must be run from the root of a Git working tree. - -### How the script works ### - -For the source (API) and runtime (ABI) interface compatibility, this script -is a small wrapper around the abi-compliance-checker and abi-dumper tools, -applying them to compare the header and library files. - -For the storage format, this script compares the automatically generated -storage tests and the manual read tests, and complains if there is a -reduction in coverage. A change in test data will be signaled as a -coverage reduction since the old test data is no longer present. A change in -how test data is presented will be signaled as well; this would be a false -positive. - -The results of the API/ABI comparison are either formatted as HTML and stored -at a configurable location, or are given as a brief list of problems. -Returns 0 on success, 1 on non-compliance, and 2 if there is an error -while running the script. - -### How to interpret non-compliance ### - -This script has relatively common false positives. In many scenarios, it only -reports a pass if there is a strict textual match between the old version and -the new version, and it reports problems where there is a sufficient semantic -match but not a textual match. This section lists some common false positives. -This is not an exhaustive list: in the end what matters is whether we are -breaking a backward compatibility goal. - -**API**: the goal is that if an application works with the old version of the -library, it can be recompiled against the new version and will still work. -This is normally validated by comparing the declarations in `include/*/*.h`. -A failure is a declaration that has disappeared or that now has a different -type. - - * It's ok to change or remove macros and functions that are documented as - for internal use only or as experimental. - * It's ok to rename function or macro parameters as long as the semantics - has not changed. - * It's ok to change or remove structure fields that are documented as - private. - * It's ok to add fields to a structure that already had private fields - or was documented as extensible. - -**ABI**: the goal is that if an application was built against the old version -of the library, the same binary will work when linked against the new version. -This is normally validated by comparing the symbols exported by `libmbed*.so`. -A failure is a symbol that is no longer exported by the same library or that -now has a different type. - - * All ABI changes are acceptable if the library version is bumped - (see `scripts/bump_version.sh`). - * ABI changes that concern functions which are declared only inside the - library directory, and not in `include/*/*.h`, are acceptable only if - the function was only ever used inside the same library (libmbedcrypto, - libmbedx509, libmbedtls). As a counter example, if the old version - of libmbedtls calls mbedtls_foo() from libmbedcrypto, and the new version - of libmbedcrypto no longer has a compatible mbedtls_foo(), this does - require a version bump for libmbedcrypto. - -**Storage format**: the goal is to check that persistent keys stored by the -old version can be read by the new version. This is normally validated by -comparing the `*read*` test cases in `test_suite*storage_format*.data`. -A failure is a storage read test case that is no longer present with the same -function name and parameter list. - - * It's ok if the same test data is present, but its presentation has changed, - for example if a test function is renamed or has different parameters. - * It's ok if redundant tests are removed. - -**Generated test coverage**: the goal is to check that automatically -generated tests have as much coverage as before. This is normally validated -by comparing the test cases that are automatically generated by a script. -A failure is a generated test case that is no longer present with the same -function name and parameter list. - - * It's ok if the same test data is present, but its presentation has changed, - for example if a test function is renamed or has different parameters. - * It's ok if redundant tests are removed. - -""" - -# Copyright The Mbed TLS Contributors -# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later - -import glob -import os -import re -import sys -import traceback -import shutil -import subprocess -import argparse -import logging -import tempfile -import fnmatch -from types import SimpleNamespace - -import xml.etree.ElementTree as ET - -import framework_scripts_path # pylint: disable=unused-import -from mbedtls_framework import build_tree - - -class AbiChecker: - """API and ABI checker.""" - - def __init__(self, old_version, new_version, configuration): - """Instantiate the API/ABI checker. - - old_version: RepoVersion containing details to compare against - new_version: RepoVersion containing details to check - configuration.report_dir: directory for output files - configuration.keep_all_reports: if false, delete old reports - configuration.brief: if true, output shorter report to stdout - configuration.check_abi: if true, compare ABIs - configuration.check_api: if true, compare APIs - configuration.check_storage: if true, compare storage format tests - configuration.skip_file: path to file containing symbols and types to skip - """ - self.repo_path = "." - self.log = None - self.verbose = configuration.verbose - self._setup_logger() - self.report_dir = os.path.abspath(configuration.report_dir) - self.keep_all_reports = configuration.keep_all_reports - self.can_remove_report_dir = not (os.path.exists(self.report_dir) or - self.keep_all_reports) - self.old_version = old_version - self.new_version = new_version - self.skip_file = configuration.skip_file - self.check_abi = configuration.check_abi - self.check_api = configuration.check_api - if self.check_abi != self.check_api: - raise Exception('Checking API without ABI or vice versa is not supported') - self.check_storage_tests = configuration.check_storage - self.brief = configuration.brief - self.git_command = "git" - self.make_command = "make" - - def _setup_logger(self): - self.log = logging.getLogger() - if self.verbose: - self.log.setLevel(logging.DEBUG) - else: - self.log.setLevel(logging.INFO) - self.log.addHandler(logging.StreamHandler()) - - @staticmethod - def check_abi_tools_are_installed(): - for command in ["abi-dumper", "abi-compliance-checker"]: - if not shutil.which(command): - raise Exception("{} not installed, aborting".format(command)) - - def _get_clean_worktree_for_git_revision(self, version): - """Make a separate worktree with version.revision checked out. - Do not modify the current worktree.""" - git_worktree_path = tempfile.mkdtemp() - if version.repository: - self.log.debug( - "Checking out git worktree for revision {} from {}".format( - version.revision, version.repository - ) - ) - fetch_output = subprocess.check_output( - [self.git_command, "fetch", - version.repository, version.revision], - cwd=self.repo_path, - stderr=subprocess.STDOUT - ) - self.log.debug(fetch_output.decode("utf-8")) - worktree_rev = "FETCH_HEAD" - else: - self.log.debug("Checking out git worktree for revision {}".format( - version.revision - )) - worktree_rev = version.revision - worktree_output = subprocess.check_output( - [self.git_command, "worktree", "add", "--detach", - git_worktree_path, worktree_rev], - cwd=self.repo_path, - stderr=subprocess.STDOUT - ) - self.log.debug(worktree_output.decode("utf-8")) - version.commit = subprocess.check_output( - [self.git_command, "rev-parse", "HEAD"], - cwd=git_worktree_path, - stderr=subprocess.STDOUT - ).decode("ascii").rstrip() - self.log.debug("Commit is {}".format(version.commit)) - return git_worktree_path - - def _update_git_submodules(self, git_worktree_path, version): - """If the crypto submodule is present, initialize it. - if version.crypto_revision exists, update it to that revision, - otherwise update it to the default revision""" - submodule_output = subprocess.check_output( - [self.git_command, "submodule", "foreach", "--recursive", - f'git worktree add --detach "{git_worktree_path}/$displaypath" HEAD'], - cwd=self.repo_path, - stderr=subprocess.STDOUT - ) - self.log.debug(submodule_output.decode("utf-8")) - - try: - # Try to update the submodules using local commits - # (Git will sometimes insist on fetching the remote without --no-fetch - # if the submodules are shallow clones) - update_output = subprocess.check_output( - [self.git_command, "submodule", "update", "--init", '--recursive', '--no-fetch'], - cwd=git_worktree_path, - stderr=subprocess.STDOUT - ) - except subprocess.CalledProcessError as err: - self.log.debug(err.stdout.decode("utf-8")) - - # Checkout with --no-fetch failed, falling back to fetching from origin - update_output = subprocess.check_output( - [self.git_command, "submodule", "update", "--init", '--recursive'], - cwd=git_worktree_path, - stderr=subprocess.STDOUT - ) - self.log.debug(update_output.decode("utf-8")) - if not (os.path.exists(os.path.join(git_worktree_path, "crypto")) - and version.crypto_revision): - return - - if version.crypto_repository: - fetch_output = subprocess.check_output( - [self.git_command, "fetch", version.crypto_repository, - version.crypto_revision], - cwd=os.path.join(git_worktree_path, "crypto"), - stderr=subprocess.STDOUT - ) - self.log.debug(fetch_output.decode("utf-8")) - crypto_rev = "FETCH_HEAD" - else: - crypto_rev = version.crypto_revision - - checkout_output = subprocess.check_output( - [self.git_command, "checkout", crypto_rev], - cwd=os.path.join(git_worktree_path, "crypto"), - stderr=subprocess.STDOUT - ) - self.log.debug(checkout_output.decode("utf-8")) - - def _build_shared_libraries(self, git_worktree_path, version): - """Build the shared libraries in the specified worktree.""" - my_environment = os.environ.copy() - my_environment["CFLAGS"] = "-g -Og" - my_environment["SHARED"] = "1" - if os.path.exists(os.path.join(git_worktree_path, "crypto")): - my_environment["USE_CRYPTO_SUBMODULE"] = "1" - - if os.path.exists(os.path.join(git_worktree_path, "scripts", "legacy.make")): - command = [self.make_command, "-f", "scripts/legacy.make", "lib"] - else: - command = [self.make_command, "lib"] - - make_output = subprocess.check_output( - command, - env=my_environment, - cwd=git_worktree_path, - stderr=subprocess.STDOUT - ) - self.log.debug(make_output.decode("utf-8")) - for root, _dirs, files in os.walk(git_worktree_path): - for file in fnmatch.filter(files, "*.so"): - version.modules[os.path.splitext(file)[0]] = ( - os.path.join(root, file) - ) - - @staticmethod - def _pretty_revision(version): - if version.revision == version.commit: - return version.revision - else: - return "{} ({})".format(version.revision, version.commit) - - def _get_abi_dumps_from_shared_libraries(self, version): - """Generate the ABI dumps for the specified git revision. - The shared libraries must have been built and the module paths - present in version.modules.""" - for mbed_module, module_path in version.modules.items(): - output_path = os.path.join( - self.report_dir, "{}-{}-{}.dump".format( - mbed_module, version.revision, version.version - ) - ) - abi_dump_command = [ - "abi-dumper", - module_path, - "-o", output_path, - "-lver", self._pretty_revision(version), - ] - abi_dump_output = subprocess.check_output( - abi_dump_command, - stderr=subprocess.STDOUT - ) - self.log.debug(abi_dump_output.decode("utf-8")) - version.abi_dumps[mbed_module] = output_path - - @staticmethod - def _normalize_storage_test_case_data(line): - """Eliminate cosmetic or irrelevant details in storage format test cases.""" - line = re.sub(r'\s+', r'', line) - return line - - def _read_storage_tests(self, - directory, - filename, - is_generated, - storage_tests): - """Record storage tests from the given file. - - Populate the storage_tests dictionary with test cases read from - filename under directory. - """ - at_paragraph_start = True - description = None - full_path = os.path.join(directory, filename) - with open(full_path) as fd: - for line_number, line in enumerate(fd, 1): - line = line.strip() - if not line: - at_paragraph_start = True - continue - if line.startswith('#'): - continue - if at_paragraph_start: - description = line.strip() - at_paragraph_start = False - continue - if line.startswith('depends_on:'): - continue - # We've reached a test case data line - test_case_data = self._normalize_storage_test_case_data(line) - if not is_generated: - # In manual test data, only look at read tests. - function_name = test_case_data.split(':', 1)[0] - if 'read' not in function_name.split('_'): - continue - metadata = SimpleNamespace( - filename=filename, - line_number=line_number, - description=description - ) - storage_tests[test_case_data] = metadata - - @staticmethod - def _list_generated_test_data_files(git_worktree_path): - """List the generated test data files.""" - generate_psa_tests = 'framework/scripts/generate_psa_tests.py' - if not os.path.isfile(git_worktree_path + '/' + generate_psa_tests): - # The checked-out revision is from before generate_psa_tests.py - # was moved to the framework submodule. Use the old location. - generate_psa_tests = 'tests/scripts/generate_psa_tests.py' - - output = subprocess.check_output( - [generate_psa_tests, '--list'], - cwd=git_worktree_path, - ).decode('ascii') - return [line for line in output.split('\n') if line] - - def _get_storage_format_tests(self, version, git_worktree_path): - """Record the storage format tests for the specified git version. - - The storage format tests are the test suite data files whose name - contains "storage_format". - - The version must be checked out at git_worktree_path. - - This function creates or updates the generated data files. - """ - # Existing test data files. This may be missing some automatically - # generated files if they haven't been generated yet. - if os.path.isdir(os.path.join(git_worktree_path, 'tf-psa-crypto', - 'tests', 'suites')): - storage_data_files = set(glob.glob( - 'tf-psa-crypto/tests/suites/test_suite_*storage_format*.data' - )) - else: - storage_data_files = set(glob.glob( - 'tests/suites/test_suite_*storage_format*.data' - )) - # Discover and (re)generate automatically generated data files. - to_be_generated = set() - for filename in self._list_generated_test_data_files(git_worktree_path): - if 'storage_format' in filename: - storage_data_files.add(filename) - to_be_generated.add(filename) - - generate_psa_tests = 'framework/scripts/generate_psa_tests.py' - if not os.path.isfile(git_worktree_path + '/' + generate_psa_tests): - # The checked-out revision is from before generate_psa_tests.py - # was moved to the framework submodule. Use the old location. - generate_psa_tests = 'tests/scripts/generate_psa_tests.py' - subprocess.check_call( - [generate_psa_tests] + sorted(to_be_generated), - cwd=git_worktree_path, - ) - for test_file in sorted(storage_data_files): - self._read_storage_tests(git_worktree_path, - test_file, - test_file in to_be_generated, - version.storage_tests) - - def _cleanup_worktree(self, git_worktree_path): - """Remove the specified git worktree.""" - shutil.rmtree(git_worktree_path) - submodule_output = subprocess.check_output( - [self.git_command, "submodule", "foreach", "--recursive", - f'git worktree remove "{git_worktree_path}/$displaypath"'], - cwd=self.repo_path, - stderr=subprocess.STDOUT - ) - self.log.debug(submodule_output.decode("utf-8")) - worktree_output = subprocess.check_output( - [self.git_command, "worktree", "remove", git_worktree_path], - cwd=self.repo_path, - stderr=subprocess.STDOUT - ) - self.log.debug(worktree_output.decode("utf-8")) - - def _get_abi_dump_for_ref(self, version): - """Generate the interface information for the specified git revision.""" - git_worktree_path = self._get_clean_worktree_for_git_revision(version) - self._update_git_submodules(git_worktree_path, version) - if self.check_abi: - self._build_shared_libraries(git_worktree_path, version) - self._get_abi_dumps_from_shared_libraries(version) - if self.check_storage_tests: - self._get_storage_format_tests(version, git_worktree_path) - self._cleanup_worktree(git_worktree_path) - - def _remove_children_with_tag(self, parent, tag): - children = parent.getchildren() - for child in children: - if child.tag == tag: - parent.remove(child) - else: - self._remove_children_with_tag(child, tag) - - def _remove_extra_detail_from_report(self, report_root): - for tag in ['test_info', 'test_results', 'problem_summary', - 'added_symbols', 'affected']: - self._remove_children_with_tag(report_root, tag) - - for report in report_root: - for problems in report.getchildren()[:]: - if not problems.getchildren(): - report.remove(problems) - - def _abi_compliance_command(self, mbed_module, output_path): - """Build the command to run to analyze the library mbed_module. - The report will be placed in output_path.""" - abi_compliance_command = [ - "abi-compliance-checker", - "-l", mbed_module, - "-old", self.old_version.abi_dumps[mbed_module], - "-new", self.new_version.abi_dumps[mbed_module], - "-strict", - "-report-path", output_path, - ] - if self.skip_file: - abi_compliance_command += ["-skip-symbols", self.skip_file, - "-skip-types", self.skip_file] - if self.brief: - abi_compliance_command += ["-report-format", "xml", - "-stdout"] - return abi_compliance_command - - def _is_library_compatible(self, mbed_module, compatibility_report): - """Test if the library mbed_module has remained compatible. - Append a message regarding compatibility to compatibility_report.""" - output_path = os.path.join( - self.report_dir, "{}-{}-{}.html".format( - mbed_module, self.old_version.revision, - self.new_version.revision - ) - ) - try: - subprocess.check_output( - self._abi_compliance_command(mbed_module, output_path), - stderr=subprocess.STDOUT - ) - except subprocess.CalledProcessError as err: - if err.returncode != 1: - raise err - if self.brief: - self.log.info( - "Compatibility issues found for {}".format(mbed_module) - ) - report_root = ET.fromstring(err.output.decode("utf-8")) - self._remove_extra_detail_from_report(report_root) - self.log.info(ET.tostring(report_root).decode("utf-8")) - else: - self.can_remove_report_dir = False - compatibility_report.append( - "Compatibility issues found for {}, " - "for details see {}".format(mbed_module, output_path) - ) - return False - compatibility_report.append( - "No compatibility issues for {}".format(mbed_module) - ) - if not (self.keep_all_reports or self.brief): - os.remove(output_path) - return True - - @staticmethod - def _is_storage_format_compatible(old_tests, new_tests, - compatibility_report): - """Check whether all tests present in old_tests are also in new_tests. - - Append a message regarding compatibility to compatibility_report. - """ - missing = frozenset(old_tests.keys()).difference(new_tests.keys()) - for test_data in sorted(missing): - metadata = old_tests[test_data] - compatibility_report.append( - 'Test case from {} line {} "{}" has disappeared: {}'.format( - metadata.filename, metadata.line_number, - metadata.description, test_data - ) - ) - compatibility_report.append( - 'FAIL: {}/{} storage format test cases have changed or disappeared.'.format( - len(missing), len(old_tests) - ) if missing else - 'PASS: All {} storage format test cases are preserved.'.format( - len(old_tests) - ) - ) - compatibility_report.append( - 'Info: number of storage format tests cases: {} -> {}.'.format( - len(old_tests), len(new_tests) - ) - ) - return not missing - - def get_abi_compatibility_report(self): - """Generate a report of the differences between the reference ABI - and the new ABI. ABI dumps from self.old_version and self.new_version - must be available.""" - compatibility_report = ["Checking evolution from {} to {}".format( - self._pretty_revision(self.old_version), - self._pretty_revision(self.new_version) - )] - compliance_return_code = 0 - - if self.check_abi: - shared_modules = list(set(self.old_version.modules.keys()) & - set(self.new_version.modules.keys())) - for mbed_module in shared_modules: - if not self._is_library_compatible(mbed_module, - compatibility_report): - compliance_return_code = 1 - - if self.check_storage_tests: - if not self._is_storage_format_compatible( - self.old_version.storage_tests, - self.new_version.storage_tests, - compatibility_report): - compliance_return_code = 1 - - for version in [self.old_version, self.new_version]: - for mbed_module, mbed_module_dump in version.abi_dumps.items(): - os.remove(mbed_module_dump) - if self.can_remove_report_dir: - os.rmdir(self.report_dir) - self.log.info("\n".join(compatibility_report)) - return compliance_return_code - - def check_for_abi_changes(self): - """Generate a report of ABI differences - between self.old_rev and self.new_rev.""" - build_tree.check_repo_path() - if self.check_api or self.check_abi: - self.check_abi_tools_are_installed() - self._get_abi_dump_for_ref(self.old_version) - self._get_abi_dump_for_ref(self.new_version) - return self.get_abi_compatibility_report() - - -def run_main(): - try: - parser = argparse.ArgumentParser( - description=__doc__ - ) - parser.add_argument( - "-v", "--verbose", action="store_true", - help="set verbosity level", - ) - parser.add_argument( - "-r", "--report-dir", type=str, default="reports", - help="directory where reports are stored, default is reports", - ) - parser.add_argument( - "-k", "--keep-all-reports", action="store_true", - help="keep all reports, even if there are no compatibility issues", - ) - parser.add_argument( - "-o", "--old-rev", type=str, help="revision for old version.", - required=True, - ) - parser.add_argument( - "-or", "--old-repo", type=str, help="repository for old version." - ) - parser.add_argument( - "-oc", "--old-crypto-rev", type=str, - help="revision for old crypto submodule." - ) - parser.add_argument( - "-ocr", "--old-crypto-repo", type=str, - help="repository for old crypto submodule." - ) - parser.add_argument( - "-n", "--new-rev", type=str, help="revision for new version", - required=True, - ) - parser.add_argument( - "-nr", "--new-repo", type=str, help="repository for new version." - ) - parser.add_argument( - "-nc", "--new-crypto-rev", type=str, - help="revision for new crypto version" - ) - parser.add_argument( - "-ncr", "--new-crypto-repo", type=str, - help="repository for new crypto submodule." - ) - parser.add_argument( - "-s", "--skip-file", type=str, - help=("path to file containing symbols and types to skip " - "(typically \"-s identifiers\" after running " - "\"tests/scripts/list-identifiers.sh --internal\")") - ) - parser.add_argument( - "--check-abi", - action='store_true', default=True, - help="Perform ABI comparison (default: yes)" - ) - parser.add_argument("--no-check-abi", action='store_false', dest='check_abi') - parser.add_argument( - "--check-api", - action='store_true', default=True, - help="Perform API comparison (default: yes)" - ) - parser.add_argument("--no-check-api", action='store_false', dest='check_api') - parser.add_argument( - "--check-storage", - action='store_true', default=True, - help="Perform storage tests comparison (default: yes)" - ) - parser.add_argument("--no-check-storage", action='store_false', dest='check_storage') - parser.add_argument( - "-b", "--brief", action="store_true", - help="output only the list of issues to stdout, instead of a full report", - ) - abi_args = parser.parse_args() - if os.path.isfile(abi_args.report_dir): - print("Error: {} is not a directory".format(abi_args.report_dir)) - parser.exit() - old_version = SimpleNamespace( - version="old", - repository=abi_args.old_repo, - revision=abi_args.old_rev, - commit=None, - crypto_repository=abi_args.old_crypto_repo, - crypto_revision=abi_args.old_crypto_rev, - abi_dumps={}, - storage_tests={}, - modules={} - ) - new_version = SimpleNamespace( - version="new", - repository=abi_args.new_repo, - revision=abi_args.new_rev, - commit=None, - crypto_repository=abi_args.new_crypto_repo, - crypto_revision=abi_args.new_crypto_rev, - abi_dumps={}, - storage_tests={}, - modules={} - ) - configuration = SimpleNamespace( - verbose=abi_args.verbose, - report_dir=abi_args.report_dir, - keep_all_reports=abi_args.keep_all_reports, - brief=abi_args.brief, - check_abi=abi_args.check_abi, - check_api=abi_args.check_api, - check_storage=abi_args.check_storage, - skip_file=abi_args.skip_file - ) - abi_check = AbiChecker(old_version, new_version, configuration) - return_code = abi_check.check_for_abi_changes() - sys.exit(return_code) - except Exception: # pylint: disable=broad-except - # Print the backtrace and exit explicitly so as to exit with - # status 2, not 1. - traceback.print_exc() - sys.exit(2) - - -if __name__ == "__main__": - run_main() From 57009f3911a5324ec9ca03f992a1ffa8ef808f50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bence=20Sz=C3=A9pk=C3=BAti?= Date: Wed, 22 Oct 2025 00:52:06 +0200 Subject: [PATCH 02/34] Add abi_check.py bridge script MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bulk of the script that was moved to the framework is now a pure python module - bridge scripts like this one will remain in each individual branch, and continue to be the way to invoke the ABI checks. This way we can use the bridge scripts to encode branch-specific information in a more convenient way. Signed-off-by: Bence Szépkúti --- scripts/abi_check.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100755 scripts/abi_check.py diff --git a/scripts/abi_check.py b/scripts/abi_check.py new file mode 100755 index 0000000000..517d40e355 --- /dev/null +++ b/scripts/abi_check.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +"""Bridge script +See framework/scripts/mbedtls_framework/interface_checks.py for detailed documentation. + +This is a convenient place to encode any branch-specific information we might want to add +in the future. +""" + +# Copyright The Mbed TLS Contributors +# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later + +import framework_scripts_path # pylint: disable=unused-import +from mbedtls_framework import interface_checks + +if __name__ == "__main__": + interface_checks.run_main() From 39813964ef542eb7ff684bc61a40e4015ce7b7c6 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Tue, 13 Jan 2026 15:42:11 +0100 Subject: [PATCH 03/34] ssl_tls.c: Allow client hello fragmentation Signed-off-by: Ronald Cron --- library/ssl_tls.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/library/ssl_tls.c b/library/ssl_tls.c index 24ac3cec4d..6df6c4bd88 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -2892,13 +2892,6 @@ size_t mbedtls_ssl_get_output_max_frag_len(const mbedtls_ssl_context *ssl) #if defined(MBEDTLS_SSL_PROTO_DTLS) size_t mbedtls_ssl_get_current_mtu(const mbedtls_ssl_context *ssl) { - /* Return unlimited mtu for client hello messages to avoid fragmentation. */ - if (ssl->conf->endpoint == MBEDTLS_SSL_IS_CLIENT && - (ssl->state == MBEDTLS_SSL_CLIENT_HELLO || - ssl->state == MBEDTLS_SSL_SERVER_HELLO)) { - return 0; - } - if (ssl->handshake == NULL || ssl->handshake->mtu == 0) { return ssl->mtu; } From fa5e75d6f6d50de7d74d2e64b14f87c51277fd45 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Wed, 14 Jan 2026 16:32:48 +0100 Subject: [PATCH 04/34] ssl-opt.sh: Relax deps of handshake defrag tests Relax the dependencies of the tests about handshake message defragmentation/reassembly on server side. TLS 1.3 does not need to be enable anymore for this to work for TLS 1.2 handshake messages. Signed-off-by: Ronald Cron --- scripts/generate_tls_handshake_tests.py | 3 --- tests/ssl-opt.sh | 3 --- 2 files changed, 6 deletions(-) diff --git a/scripts/generate_tls_handshake_tests.py b/scripts/generate_tls_handshake_tests.py index 30f27b1b37..76c8e45d57 100755 --- a/scripts/generate_tls_handshake_tests.py +++ b/scripts/generate_tls_handshake_tests.py @@ -6,12 +6,9 @@ Generate miscellaneous TLS test cases relating to the handshake. # Copyright The Mbed TLS Contributors # SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later -import sys - import framework_scripts_path # pylint: disable=unused-import from mbedtls_framework import tls_handshake_tests if __name__ == '__main__': - sys.argv[1:1] = ["--no-tls12-client-hello-defragmentation-support"] tls_handshake_tests.main() diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh index 9b5987188f..cef8bfab48 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -13826,7 +13826,6 @@ run_test "Handshake defragmentation on server: len=256, client-initiated rene -s "Consume: waiting for more handshake fragments 256/" \ requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 -requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_3 requires_config_enabled MBEDTLS_SSL_RENEGOTIATION run_test "Handshake defragmentation on server: len=128, client-initiated renegotiation" \ "$P_SRV debug_level=4 exchanges=2 renegotiation=1 auth_mode=required" \ @@ -13843,7 +13842,6 @@ run_test "Handshake defragmentation on server: len=128, client-initiated rene -s "Consume: waiting for more handshake fragments 128/" \ requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 -requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_3 requires_config_enabled MBEDTLS_SSL_RENEGOTIATION run_test "Handshake defragmentation on server: len=4, client-initiated renegotiation" \ "$P_SRV debug_level=4 exchanges=2 renegotiation=1 auth_mode=required" \ @@ -13860,7 +13858,6 @@ run_test "Handshake defragmentation on server: len=4, client-initiated renego -s "Consume: waiting for more handshake fragments 4/" \ requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 -requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_3 requires_config_enabled MBEDTLS_SSL_RENEGOTIATION run_test "Handshake defragmentation on server: len=4, client-initiated server-rejected renegotiation" \ "$P_SRV debug_level=4 exchanges=2 renegotiation=0 auth_mode=required" \ From 2e9b9681e60ff52d69a3a68b4c7be0bcbab9191b Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Wed, 21 Jan 2026 11:33:35 +0100 Subject: [PATCH 05/34] ssl_server2.c: DTLS: Attempt to read the response to the close notification Signed-off-by: Ronald Cron --- programs/ssl/ssl_server2.c | 50 ++++++++++++++++++- tests/compat.sh | 1 + tests/scripts/components-configuration-tls.sh | 1 + 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/programs/ssl/ssl_server2.c b/programs/ssl/ssl_server2.c index 0ae2f79303..f262542377 100644 --- a/programs/ssl/ssl_server2.c +++ b/programs/ssl/ssl_server2.c @@ -4133,7 +4133,55 @@ close_notify: } while (ret == MBEDTLS_ERR_SSL_WANT_WRITE); ret = 0; - mbedtls_printf(" done\n"); + /* + * In the DTLS case, attempt to read a possible response to the close + * notification. This avoids reconnecting to the same client when we + * reset and later receive its close-notification response during + * step 3 (waiting for a client to connect). + * + * Stop waiting for the response if the connection has already ended. + * + * The waiting loop below relies on mbedtls_ssl_read() returning regularly + * in order to keep the total waiting time approximately bounded to 1s. If + * no read timeout is configured (see the read_timeout option), or if the + * configured timeout is close to or larger than 1s, the total waiting time + * may exceed 1s by a significant margin. + */ +#if defined(MBEDTLS_SSL_PROTO_DTLS) && defined(MBEDTLS_HAVE_TIME) + if (opt.transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + mbedtls_ms_time_t start = mbedtls_ms_time(); + for (;;) { + ret = mbedtls_ssl_read(&ssl, buf, opt.buffer_size); + /* + * mbedtls_ssl_read() returned some data or timed out, loop if we + * have not spent already too much time, quite arbitrarily 1s. + */ + if ((ret > 0) || (ret == MBEDTLS_ERR_SSL_TIMEOUT)) { + if ((mbedtls_ms_time() - start) < 1000) { + continue; + } + } + + if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { + mbedtls_printf(" done, received client close notification.\n"); + } else { + /* ret = 0, silent transport EOF or ret < 0 except + * MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY. Note that we do not + * handle specifically the non-fatal error codes like + * MBEDTLS_ERR_SSL_WANT_READ as we do not really expect them + * here. + */ + mbedtls_printf(" done\n"); + } + break; + } + ret = 0; + } else +#endif /* MBEDTLS_SSL_PROTO_DTLS && MBEDTLS_HAVE_TIME */ + { + mbedtls_printf(" done\n"); + } + fflush(stdout); #if defined(MBEDTLS_SSL_CACHE_C) if (opt.cache_remove > 0) { diff --git a/tests/compat.sh b/tests/compat.sh index 2b6f454127..3f44c984fb 100755 --- a/tests/compat.sh +++ b/tests/compat.sh @@ -557,6 +557,7 @@ setup_arguments() # with OpenSSL 1.0.1h, -www, -WWW and -HTTP break DTLS handshakes if is_dtls "$MODE"; then O_SERVER_ARGS="$O_SERVER_ARGS" + M_SERVER_ARGS="$M_SERVER_ARGS read_timeout=1000" else O_SERVER_ARGS="$O_SERVER_ARGS -www" fi diff --git a/tests/scripts/components-configuration-tls.sh b/tests/scripts/components-configuration-tls.sh index 5a77c4defc..d017eef182 100644 --- a/tests/scripts/components-configuration-tls.sh +++ b/tests/scripts/components-configuration-tls.sh @@ -165,6 +165,7 @@ component_test_tls1_2_ccm_psk_dtls () { msg "build: configs/config-ccm-psk-dtls1_2.h" MBEDTLS_CONFIG="configs/config-ccm-psk-dtls1_2.h" CRYPTO_CONFIG="configs/crypto-config-ccm-psk-tls1_2.h" + tf-psa-crypto/scripts/config.py -f "$CRYPTO_CONFIG" set MBEDTLS_HAVE_TIME CC=$ASAN_CC cmake -DMBEDTLS_CONFIG_FILE="$MBEDTLS_CONFIG" -DTF_PSA_CRYPTO_CONFIG_FILE="$CRYPTO_CONFIG" -D CMAKE_BUILD_TYPE:String=Asan . make From 516e74ca5c43bf142dc3c4e7e0b25e3ab27df44f Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Mon, 12 Jan 2026 12:45:40 +0100 Subject: [PATCH 06/34] ssl_tls12_server.c: Document replay check and update in ssl_parse_client_hello() Signed-off-by: Ronald Cron --- library/ssl_tls12_server.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/ssl_tls12_server.c b/library/ssl_tls12_server.c index ec4446c1b4..8f724d31ef 100644 --- a/library/ssl_tls12_server.c +++ b/library/ssl_tls12_server.c @@ -938,6 +938,9 @@ read_record_header: memcpy(&ssl->cur_out_ctr[2], ssl->in_ctr + 2, sizeof(ssl->cur_out_ctr) - 2); + /* Check for record replay and then update the window. This replicates what + * is done in `ssl_get_next_record()` when the record is not fetched through + * `mbedtls_ssl_read_record()`. */ #if defined(MBEDTLS_SSL_DTLS_ANTI_REPLAY) if (mbedtls_ssl_dtls_replay_check(ssl) != 0) { MBEDTLS_SSL_DEBUG_MSG(1, ("replayed record, discarding")); From a50110be7135e0ab54531e0247b3fba1b3f9ee82 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Wed, 14 Jan 2026 13:48:52 +0100 Subject: [PATCH 07/34] ssl_tls12_server.c: Use mbedtls_ssl_read_record() only to read the ClientHello In ssl_tls12_server.c:ssl_parse_client_hello(), remove the code that directly reads the received data to read the record expected to contain the ClientHello message. The function already supported handling a ClientHello read via mbedtls_ssl_read_record() in the following cases: - when the ClientHello was read as a post-handshake message (renegotiation). - when the ClientHello was read by ssl_tls13_process_client_hello() during TLS 1.3 or TLS 1.2 version negotiation. Signed-off-by: Ronald Cron --- library/ssl_msg.c | 5 ++ library/ssl_tls12_server.c | 120 ++++++------------------------------- 2 files changed, 22 insertions(+), 103 deletions(-) diff --git a/library/ssl_msg.c b/library/ssl_msg.c index d159f8fd33..64e3de795d 100644 --- a/library/ssl_msg.c +++ b/library/ssl_msg.c @@ -5504,6 +5504,11 @@ static int ssl_tls12_handle_hs_message_post_handshake(mbedtls_ssl_context *ssl) ssl->renego_status = MBEDTLS_SSL_RENEGOTIATION_PENDING; } #endif + + /* Keep the ClientHello message for ssl_parse_client_hello() */ + if (ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER) { + ssl->keep_current_message = 1; + } ret = mbedtls_ssl_start_renegotiation(ssl); if (ret != MBEDTLS_ERR_SSL_WAITING_SERVER_HELLO_RENEGO && ret != 0) { diff --git a/library/ssl_tls12_server.c b/library/ssl_tls12_server.c index 8f724d31ef..15f78486e6 100644 --- a/library/ssl_tls12_server.c +++ b/library/ssl_tls12_server.c @@ -869,29 +869,22 @@ static int ssl_parse_client_hello(mbedtls_ssl_context *ssl) MBEDTLS_SSL_DEBUG_MSG(2, ("=> parse client hello")); - int renegotiating; - -#if defined(MBEDTLS_SSL_DTLS_ANTI_REPLAY) -read_record_header: -#endif /* - * If renegotiating, then the input was read with mbedtls_ssl_read_record(), - * otherwise read it ourselves manually in order to support SSLv2 - * ClientHello, which doesn't use the same record layer format. - * Otherwise in a scenario of TLS 1.3/TLS 1.2 version negotiation, the - * ClientHello has been already fully fetched by the TLS 1.3 code and the - * flag ssl->keep_current_message is raised. + * Fetch the expected ClientHello handshake message. Do not ask + * mbedtls_ssl_read_record() to update the handshake digest, to align + * with cases where the ClientHello may already have been fetched in + * ssl_tls13_process_client_hello() or as a post-handshake message + * (renegotiation). */ - renegotiating = 0; -#if defined(MBEDTLS_SSL_RENEGOTIATION) - renegotiating = (ssl->renego_status != MBEDTLS_SSL_INITIAL_HANDSHAKE); -#endif - if (!renegotiating && !ssl->keep_current_message) { - if ((ret = mbedtls_ssl_fetch_input(ssl, 5)) != 0) { - /* No alert on a read error. */ - MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_fetch_input", ret); - return ret; - } + if ((ret = mbedtls_ssl_read_record(ssl, 0)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_read_record ", ret); + return ret; + } + + ret = mbedtls_ssl_update_handshake_status(ssl); + if (0 != ret) { + MBEDTLS_SSL_DEBUG_RET(1, ("mbedtls_ssl_update_handshake_status"), ret); + return ret; } buf = ssl->in_hdr; @@ -910,7 +903,8 @@ read_record_header: MBEDTLS_SSL_DEBUG_MSG(3, ("client hello, message type: %d", buf[0])); - if (buf[0] != MBEDTLS_SSL_MSG_HANDSHAKE) { + if ((ssl->in_msgtype != MBEDTLS_SSL_MSG_HANDSHAKE) || + (buf[0] != MBEDTLS_SSL_MSG_HANDSHAKE)) { MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); return MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE; } @@ -937,66 +931,11 @@ read_record_header: memcpy(&ssl->cur_out_ctr[2], ssl->in_ctr + 2, sizeof(ssl->cur_out_ctr) - 2); - - /* Check for record replay and then update the window. This replicates what - * is done in `ssl_get_next_record()` when the record is not fetched through - * `mbedtls_ssl_read_record()`. */ -#if defined(MBEDTLS_SSL_DTLS_ANTI_REPLAY) - if (mbedtls_ssl_dtls_replay_check(ssl) != 0) { - MBEDTLS_SSL_DEBUG_MSG(1, ("replayed record, discarding")); - ssl->next_record_offset = 0; - ssl->in_left = 0; - goto read_record_header; - } - - /* No MAC to check yet, so we can update right now */ - mbedtls_ssl_dtls_replay_update(ssl); -#endif } #endif /* MBEDTLS_SSL_PROTO_DTLS */ - msg_len = MBEDTLS_GET_UINT16_BE(ssl->in_len, 0); - -#if defined(MBEDTLS_SSL_RENEGOTIATION) - if (ssl->renego_status != MBEDTLS_SSL_INITIAL_HANDSHAKE) { - /* Set by mbedtls_ssl_read_record() */ - msg_len = ssl->in_hslen; - } else -#endif - { - if (ssl->keep_current_message) { - ssl->keep_current_message = 0; - } else { - if (msg_len > MBEDTLS_SSL_IN_CONTENT_LEN) { - MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); - return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; - } - - if ((ret = mbedtls_ssl_fetch_input(ssl, - mbedtls_ssl_in_hdr_len(ssl) + msg_len)) != 0) { - MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_fetch_input", ret); - return ret; - } - - /* Done reading this record, get ready for the next one */ -#if defined(MBEDTLS_SSL_PROTO_DTLS) - if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { - ssl->next_record_offset = msg_len + mbedtls_ssl_in_hdr_len(ssl); - } else -#endif - ssl->in_left = 0; - } - } - buf = ssl->in_msg; - - MBEDTLS_SSL_DEBUG_BUF(4, "record contents", buf, msg_len); - - ret = ssl->handshake->update_checksum(ssl, buf, msg_len); - if (0 != ret) { - MBEDTLS_SSL_DEBUG_RET(1, ("update_checksum"), ret); - return ret; - } + msg_len = ssl->in_hslen; /* * Handshake layer: @@ -1006,13 +945,6 @@ read_record_header: * 6 . 8 DTLS only: fragment offset * 9 . 11 DTLS only: fragment length */ - if (msg_len < mbedtls_ssl_hs_hdr_len(ssl)) { - MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); - return MBEDTLS_ERR_SSL_DECODE_ERROR; - } - - MBEDTLS_SSL_DEBUG_MSG(3, ("client hello v3, handshake type: %d", buf[0])); - if (buf[0] != MBEDTLS_SSL_HS_CLIENT_HELLO) { MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); return MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE; @@ -1043,24 +975,6 @@ read_record_header: ssl->handshake->out_msg_seq = cli_msg_seq; ssl->handshake->in_msg_seq = cli_msg_seq + 1; } - { - /* - * For now we don't support fragmentation, so make sure - * fragment_offset == 0 and fragment_length == length - */ - size_t fragment_offset, fragment_length, length; - fragment_offset = MBEDTLS_GET_UINT24_BE(ssl->in_msg, 6); - fragment_length = MBEDTLS_GET_UINT24_BE(ssl->in_msg, 9); - length = MBEDTLS_GET_UINT24_BE(ssl->in_msg, 1); - MBEDTLS_SSL_DEBUG_MSG( - 4, ("fragment_offset=%u fragment_length=%u length=%u", - (unsigned) fragment_offset, (unsigned) fragment_length, - (unsigned) length)); - if (fragment_offset != 0 || length != fragment_length) { - MBEDTLS_SSL_DEBUG_MSG(1, ("ClientHello fragmentation not supported")); - return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; - } - } } #endif /* MBEDTLS_SSL_PROTO_DTLS */ From 943c1071bb7f2585ab96adaced51eda3809fb837 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Thu, 8 Jan 2026 09:15:40 +0100 Subject: [PATCH 08/34] ssl_tls12_server.c: Move ClientHello message_seq adjustment Move ClientHello message_seq adjustment to the record layer. Signed-off-by: Ronald Cron --- library/ssl_msg.c | 21 +++++++++++++++++++++ library/ssl_tls12_server.c | 28 ---------------------------- 2 files changed, 21 insertions(+), 28 deletions(-) diff --git a/library/ssl_msg.c b/library/ssl_msg.c index 64e3de795d..fb5327a709 100644 --- a/library/ssl_msg.c +++ b/library/ssl_msg.c @@ -2967,6 +2967,27 @@ int mbedtls_ssl_prepare_handshake_record(mbedtls_ssl_context *ssl) return MBEDTLS_ERR_SSL_INVALID_RECORD; } + /* + * When establishing the connection, the client may go through a series + * of ClientHello and HelloVerifyRequest requests and responses. The + * server does not keep any trace of these initial round trips as + * intended: minimum allocated ressources as long as the reachability + * of the client has not been confirmed. When receiving the "first + * ClientHello" from server perspective, we may thus need to adapt + * the next expected `message_seq` for the incoming and outgoing + * handshake messages. + */ + if (ssl->in_msg[0] == MBEDTLS_SSL_HS_CLIENT_HELLO && + ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER && + ssl->state == MBEDTLS_SSL_CLIENT_HELLO +#if defined(MBEDTLS_SSL_RENEGOTIATION) + && ssl->renego_status == MBEDTLS_SSL_INITIAL_HANDSHAKE +#endif + ) { + ssl->handshake->in_msg_seq = recv_msg_seq; + ssl->handshake->out_msg_seq = recv_msg_seq; + } + if (ssl->handshake != NULL && ((mbedtls_ssl_is_handshake_over(ssl) == 0 && recv_msg_seq != ssl->handshake->in_msg_seq) || diff --git a/library/ssl_tls12_server.c b/library/ssl_tls12_server.c index 15f78486e6..a6c36420c6 100644 --- a/library/ssl_tls12_server.c +++ b/library/ssl_tls12_server.c @@ -950,34 +950,6 @@ static int ssl_parse_client_hello(mbedtls_ssl_context *ssl) return MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE; } -#if defined(MBEDTLS_SSL_PROTO_DTLS) - if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { - /* - * Copy the client's handshake message_seq on initial handshakes, - * check sequence number on renego. - */ -#if defined(MBEDTLS_SSL_RENEGOTIATION) - if (ssl->renego_status == MBEDTLS_SSL_RENEGOTIATION_IN_PROGRESS) { - /* This couldn't be done in ssl_prepare_handshake_record() */ - unsigned int cli_msg_seq = (unsigned int) MBEDTLS_GET_UINT16_BE(ssl->in_msg, 4); - if (cli_msg_seq != ssl->handshake->in_msg_seq) { - MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message_seq: " - "%u (expected %u)", cli_msg_seq, - ssl->handshake->in_msg_seq)); - return MBEDTLS_ERR_SSL_DECODE_ERROR; - } - - ssl->handshake->in_msg_seq++; - } else -#endif - { - unsigned int cli_msg_seq = (unsigned int) MBEDTLS_GET_UINT16_BE(ssl->in_msg, 4); - ssl->handshake->out_msg_seq = cli_msg_seq; - ssl->handshake->in_msg_seq = cli_msg_seq + 1; - } - } -#endif /* MBEDTLS_SSL_PROTO_DTLS */ - buf += mbedtls_ssl_hs_hdr_len(ssl); msg_len -= mbedtls_ssl_hs_hdr_len(ssl); From 00160b910a3c342248f781b3e5122e89744cf354 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Thu, 22 Jan 2026 18:43:57 +0100 Subject: [PATCH 09/34] ssl_tls12_server.c: Move ClientHello record sequence_number init Signed-off-by: Ronald Cron --- library/ssl_msg.c | 9 +++++++++ library/ssl_tls12_server.c | 19 ------------------- 2 files changed, 9 insertions(+), 19 deletions(-) diff --git a/library/ssl_msg.c b/library/ssl_msg.c index fb5327a709..a9fe96ffa6 100644 --- a/library/ssl_msg.c +++ b/library/ssl_msg.c @@ -2986,6 +2986,15 @@ int mbedtls_ssl_prepare_handshake_record(mbedtls_ssl_context *ssl) ) { ssl->handshake->in_msg_seq = recv_msg_seq; ssl->handshake->out_msg_seq = recv_msg_seq; + + /* Epoch should be 0 for initial handshakes */ + if (ssl->in_ctr[0] != 0 || ssl->in_ctr[1] != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + + memcpy(&ssl->cur_out_ctr[2], ssl->in_ctr + 2, + sizeof(ssl->cur_out_ctr) - 2); } if (ssl->handshake != NULL && diff --git a/library/ssl_tls12_server.c b/library/ssl_tls12_server.c index a6c36420c6..8ece1f7b33 100644 --- a/library/ssl_tls12_server.c +++ b/library/ssl_tls12_server.c @@ -915,25 +915,6 @@ static int ssl_parse_client_hello(mbedtls_ssl_context *ssl) MBEDTLS_SSL_DEBUG_MSG(3, ("client hello, protocol version: [%d:%d]", buf[1], buf[2])); - /* For DTLS if this is the initial handshake, remember the client sequence - * number to use it in our next message (RFC 6347 4.2.1) */ -#if defined(MBEDTLS_SSL_PROTO_DTLS) - if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM -#if defined(MBEDTLS_SSL_RENEGOTIATION) - && ssl->renego_status == MBEDTLS_SSL_INITIAL_HANDSHAKE -#endif - ) { - /* Epoch should be 0 for initial handshakes */ - if (ssl->in_ctr[0] != 0 || ssl->in_ctr[1] != 0) { - MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); - return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; - } - - memcpy(&ssl->cur_out_ctr[2], ssl->in_ctr + 2, - sizeof(ssl->cur_out_ctr) - 2); - } -#endif /* MBEDTLS_SSL_PROTO_DTLS */ - buf = ssl->in_msg; msg_len = ssl->in_hslen; From 0db3a49330f4ede94c4eb4175384e4e0d1973905 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Thu, 22 Jan 2026 18:46:53 +0100 Subject: [PATCH 10/34] ssl_tls12_server.c: parse_client_hello: Remove remaining record level code Signed-off-by: Ronald Cron --- library/ssl_tls12_server.c | 31 ++----------------------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/library/ssl_tls12_server.c b/library/ssl_tls12_server.c index 8ece1f7b33..36d15c50a7 100644 --- a/library/ssl_tls12_server.c +++ b/library/ssl_tls12_server.c @@ -887,34 +887,6 @@ static int ssl_parse_client_hello(mbedtls_ssl_context *ssl) return ret; } - buf = ssl->in_hdr; - - MBEDTLS_SSL_DEBUG_BUF(4, "record header", buf, mbedtls_ssl_in_hdr_len(ssl)); - - /* - * TLS Client Hello - * - * Record layer: - * 0 . 0 message type - * 1 . 2 protocol version - * 3 . 11 DTLS: epoch + record sequence number - * 3 . 4 message length - */ - MBEDTLS_SSL_DEBUG_MSG(3, ("client hello, message type: %d", - buf[0])); - - if ((ssl->in_msgtype != MBEDTLS_SSL_MSG_HANDSHAKE) || - (buf[0] != MBEDTLS_SSL_MSG_HANDSHAKE)) { - MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); - return MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE; - } - - MBEDTLS_SSL_DEBUG_MSG(3, ("client hello, message len.: %d", - MBEDTLS_GET_UINT16_BE(ssl->in_len, 0))); - - MBEDTLS_SSL_DEBUG_MSG(3, ("client hello, protocol version: [%d:%d]", - buf[1], buf[2])); - buf = ssl->in_msg; msg_len = ssl->in_hslen; @@ -926,7 +898,8 @@ static int ssl_parse_client_hello(mbedtls_ssl_context *ssl) * 6 . 8 DTLS only: fragment offset * 9 . 11 DTLS only: fragment length */ - if (buf[0] != MBEDTLS_SSL_HS_CLIENT_HELLO) { + if ((ssl->in_msgtype != MBEDTLS_SSL_MSG_HANDSHAKE) || + (buf[0] != MBEDTLS_SSL_HS_CLIENT_HELLO)) { MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); return MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE; } From d718a35a1ff61af29bcae73c607848bc23ad24d4 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Fri, 23 Jan 2026 15:36:49 +0100 Subject: [PATCH 11/34] ssl_msg.c: Remove some now unnecessary code Signed-off-by: Ronald Cron --- library/ssl_msg.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/library/ssl_msg.c b/library/ssl_msg.c index a9fe96ffa6..207f4b7e1c 100644 --- a/library/ssl_msg.c +++ b/library/ssl_msg.c @@ -4873,14 +4873,9 @@ static int ssl_get_next_record(mbedtls_ssl_context *ssl) /* The record content type may change during decryption, * so re-read it. */ ssl->in_msgtype = rec.type; - /* Also update the input buffer, because unfortunately - * the server-side ssl_parse_client_hello() reparses the - * record header when receiving a ClientHello initiating - * a renegotiation. */ - ssl->in_hdr[0] = rec.type; + ssl->in_msg = rec.buf + rec.data_offset; ssl->in_msglen = rec.data_len; - MBEDTLS_PUT_UINT16_BE(rec.data_len, ssl->in_len, 0); return 0; } From c1cbfdd0722e257e1abb820d0e9adc775760d3cb Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Wed, 14 Jan 2026 09:42:27 +0100 Subject: [PATCH 12/34] ssl-opt.sh: Add interop test of DTLS defragmentation on server side Signed-off-by: Ronald Cron --- tests/ssl-opt.sh | 151 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh index cef8bfab48..2c87e20278 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -9971,6 +9971,51 @@ run_test "DTLS reassembly: fragmentation, nbio, renego (gnutls server)" \ -C "error" \ -s "Extra-header:" +requires_gnutls +requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 +run_test "DTLS reassembly: no fragmentation (gnutls client)" \ + "$P_SRV debug_level=2 dtls=1" \ + "$G_NEXT_CLI -u --mtu 2048 --insecure 127.0.0.1" \ + 0 \ + -S "found fragmented DTLS handshake message" \ + -S "error" + +requires_gnutls +requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 +run_test "DTLS reassembly: some fragmentation (gnutls client)" \ + "$P_SRV debug_level=2 dtls=1 auth_mode=required" \ + "$G_NEXT_CLI -u --mtu 256 --insecure 127.0.0.1 --x509certfile $DATA_FILES_PATH/server5.crt --x509keyfile $DATA_FILES_PATH/server5.key" \ + 0 \ + -s "found fragmented DTLS handshake message" \ + -s "Certificate handshake message has been buffered and reassembled" \ + -S "error" + +# Set the MTU to 128 bytes. The minimum size of a DTLS 1.2 record +# containing a ClientHello handshake message is 69 bytes, without any cookie, +# ciphersuite, or extension. With an MTU of 128 bytes, the ClientHello handshake +# message is therefore very likely to be fragmented in most library +# configurations. +requires_gnutls +requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 +run_test "DTLS reassembly: more fragmentation (gnutls client)" \ + "$P_SRV debug_level=2 dtls=1" \ + "$G_NEXT_CLI -u --mtu 128 --insecure 127.0.0.1" \ + 0 \ + -s "ClientHello handshake message has been buffered and reassembled" \ + -S "error" + +requires_gnutls +requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 +run_test "DTLS reassembly: more fragmentation, nbio (gnutls client)" \ + "$P_SRV debug_level=2 dtls=1 nbio=2" \ + "$G_NEXT_CLI -u --mtu 128 --insecure 127.0.0.1" \ + 0 \ + -s "ClientHello handshake message has been buffered and reassembled" \ + -S "error" + +# No fragmentation and renegotiation tests with GnuTLS client as the feature +# does not work properly. + requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS reassembly: no fragmentation (openssl server)" \ "$O_SRV -dtls -mtu 2048" \ @@ -10001,6 +10046,37 @@ run_test "DTLS reassembly: fragmentation, nbio (openssl server)" \ -c "Certificate handshake message has been buffered and reassembled" \ -C "error" +requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 +run_test "DTLS reassembly: no fragmentation (openssl client)" \ + "$P_SRV debug_level=2 dtls=1 auth_mode=required" \ + "$O_NEXT_CLI -dtls -mtu 2048 -cert $DATA_FILES_PATH/server5.crt -key $DATA_FILES_PATH/server5.key" \ + 0 \ + -S "found fragmented DTLS handshake message" \ + -S "error" + +# Minimum possible MTU for OpenSSL server: 256 bytes. +# We expect the server Certificate handshake to be fragmented and verify that +# this is the case. Depending on the configuration, other handshake messages may +# also be fragmented like the ClientHello, ClientKeyExchange or +# CertificateVerify messages. +requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 +run_test "DTLS reassembly: some fragmentation (openssl client)" \ + "$P_SRV debug_level=2 dtls=1 auth_mode=required" \ + "$O_NEXT_CLI -dtls -mtu 256 -cert $DATA_FILES_PATH/server5.crt -key $DATA_FILES_PATH/server5.key" \ + 0 \ + -s "found fragmented DTLS handshake message" \ + -s "Certificate handshake message has been buffered and reassembled" \ + -S "error" + +requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 +run_test "DTLS reassembly: fragmentation, nbio (openssl client)" \ + "$P_SRV debug_level=2 dtls=1 auth_mode=required nbio=2" \ + "$O_NEXT_CLI -dtls -mtu 256 -cert $DATA_FILES_PATH/server5.crt -key $DATA_FILES_PATH/server5.key" \ + 0 \ + -s "found fragmented DTLS handshake message" \ + -s "Certificate handshake message has been buffered and reassembled" \ + -S "error" + # Tests for sending fragmented handshake messages with DTLS # # Use client auth when we need the client to send large messages, @@ -11881,6 +11957,43 @@ run_test "DTLS proxy: 3d, openssl server, fragmentation, nbio" \ -c "HTTP/1.0 200 OK" \ -c "Certificate handshake message has been buffered and reassembled" +requires_openssl_next +client_needs_more_time 6 +not_with_valgrind # risk of non-mbedtls peer timing out +requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 +run_test "DTLS proxy: 3d, openssl client" \ + -p "$P_PXY drop=5 delay=5 duplicate=5" \ + "$P_SRV dgram_packing=0 dtls=1 hs_timeout=500-60000 tickets=0" \ + "$O_NEXT_CLI -dtls1_2 -mtu 2048" \ + 0 \ + -s "HTTP/1.0 200 OK" + +requires_openssl_next +client_needs_more_time 8 +not_with_valgrind # risk of non-mbedtls peer timing out +requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 +run_test "DTLS proxy: 3d, openssl client, fragmentation" \ + -p "$P_PXY drop=5 delay=5 duplicate=5" \ + "$P_SRV debug_level=2 dgram_packing=0 auth_mode=required dtls=1 hs_timeout=500-60000 tickets=0" \ + "$O_NEXT_CLI -dtls1_2 -mtu 256 -cert $DATA_FILES_PATH/server5.crt -key $DATA_FILES_PATH/server5.key" \ + 0 \ + -s "HTTP/1.0 200 OK" \ + -s "found fragmented DTLS handshake message" \ + -s "Certificate handshake message has been buffered and reassembled" + +requires_openssl_next +client_needs_more_time 8 +not_with_valgrind # risk of non-mbedtls peer timing out +requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 +run_test "DTLS proxy: 3d, openssl client, fragmentation, nbio" \ + -p "$P_PXY drop=5 delay=5 duplicate=5" \ + "$P_SRV debug_level=2 dgram_packing=0 auth_mode=required dtls=1 hs_timeout=500-60000 nbio=2 tickets=0" \ + "$O_NEXT_CLI -dtls1_2 -mtu 256 -cert $DATA_FILES_PATH/server5.crt -key $DATA_FILES_PATH/server5.key" \ + 0 \ + -s "HTTP/1.0 200 OK" \ + -s "found fragmented DTLS handshake message" \ + -s "Certificate handshake message has been buffered and reassembled" + requires_gnutls client_needs_more_time 6 not_with_valgrind # risk of non-mbedtls peer timing out @@ -11919,6 +12032,44 @@ run_test "DTLS proxy: 3d, gnutls server, fragmentation, nbio" \ -c "Extra-header:" \ -c "Certificate handshake message has been buffered and reassembled" +requires_gnutls +client_needs_more_time 6 +not_with_valgrind # risk of non-mbedtls peer timing out +requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 +run_test "DTLS proxy: 3d, gnutls client" \ + -p "$P_PXY drop=5 delay=5 duplicate=5" \ + "$P_SRV dgram_packing=0 dtls=1" \ + "$G_NEXT_CLI -u --mtu 2048 --insecure 127.0.0.1" \ + 0 \ + -s "HTTP/1.0 200 OK" + +# Set the MTU to 128 bytes. The ClientHello is not surely fragmented but very +# likely. Do not set it to 56 bytes where we would be sure that the ClientHello +# is fragmented as then experimentally the handshake fails too often. +requires_gnutls +client_needs_more_time 8 +not_with_valgrind # risk of non-mbedtls peer timing out +requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 +run_test "DTLS proxy: 3d, gnutls client, fragmentation" \ + -p "$P_PXY drop=5 delay=5 duplicate=5" \ + "$P_SRV dgram_packing=0 dtls=1 debug_level=2" \ + "$G_NEXT_CLI -u --mtu 128 --insecure 127.0.0.1" \ + 0 \ + -s "HTTP/1.0 200 OK" \ + -s "ClientHello handshake message has been buffered and reassembled" + +requires_gnutls +client_needs_more_time 8 +not_with_valgrind # risk of non-mbedtls peer timing out +requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 +run_test "DTLS proxy: 3d, gnutls client, fragmentation, nbio=2" \ + -p "$P_PXY drop=5 delay=5 duplicate=5" \ + "$P_SRV dgram_packing=0 dtls=1 debug_level=2 nbio=2" \ + "$G_NEXT_CLI -u --mtu 128 --insecure 127.0.0.1" \ + 0 \ + -s "HTTP/1.0 200 OK" \ + -s "ClientHello handshake message has been buffered and reassembled" + requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "export keys functionality" \ "$P_SRV eap_tls=1 debug_level=3" \ From 6e270c0465b876484ffed1218bf293f38a904655 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Fri, 16 Jan 2026 16:50:39 +0100 Subject: [PATCH 13/34] ssl-opt.sh: Add tests with CH fragmented with DTLS in default config Signed-off-by: Ronald Cron --- tests/scripts/analyze_outcomes.py | 2 +- tests/ssl-opt.sh | 120 ++++++++++++++++++++++++++++-- 2 files changed, 113 insertions(+), 9 deletions(-) diff --git a/tests/scripts/analyze_outcomes.py b/tests/scripts/analyze_outcomes.py index 29c41beba2..2bd4bd8162 100755 --- a/tests/scripts/analyze_outcomes.py +++ b/tests/scripts/analyze_outcomes.py @@ -43,7 +43,7 @@ class CoverageTask(outcome_analysis.CoverageTask): 'DTLS cookie: enabled, IPv6', # Disabled due to OpenSSL bug. # https://github.com/openssl/openssl/issues/18887 - 'DTLS fragmenting: 3d, openssl client, DTLS 1.2', + 'DTLS fragmenting: 3d, MTU=512, openssl client, DTLS 1.2', # We don't run ssl-opt.sh with Valgrind on the CI because # it's extremely slow. We don't intend to change this. 'DTLS fragmenting: proxy MTU: auto-reduction (with valgrind)', diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh index 2c87e20278..7f36ab5c14 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -10360,6 +10360,37 @@ run_test "DTLS fragmenting: both (MTU=512)" \ -c "found fragmented DTLS handshake message" \ -C "error" +# Depending on the ciphersuite selected to encrypt the application data, the +# maximum application data payload per record may be small with an MTU of 128. +# For example, with TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA384, this maximum is +# 35 bytes. We therefore reduce the size of the client request and the server +# response in this test. +requires_config_enabled MBEDTLS_SSL_PROTO_DTLS +requires_max_content_len 2048 +run_test "DTLS fragmenting: both (MTU=128)" \ + -p "$P_PXY mtu=128" \ + "$P_SRV dtls=1 debug_level=5 auth_mode=required \ + crt_file=$DATA_FILES_PATH/server7_int-ca.crt \ + key_file=$DATA_FILES_PATH/server7.key \ + response_size=8 \ + hs_timeout=2500-60000 \ + mtu=128" \ + "$P_CLI dtls=1 debug_level=2 \ + crt_file=$DATA_FILES_PATH/server8_int-ca2.crt \ + key_file=$DATA_FILES_PATH/server8.key \ + request_size=8 \ + hs_timeout=2500-60000 \ + mtu=128" \ + 0 \ + -s "found fragmented DTLS handshake message" \ + -s "fragmenting Certificate handshake message" \ + -s "fragmenting ServerKeyExchange handshake message" \ + -c "found fragmented DTLS handshake message" \ + -c "fragmenting ClientHello handshake message" \ + -c "fragmenting Certificate handshake message" \ + -c "fragmenting CertificateVerify handshake message" \ + -C "error" + # Test for automatic MTU reduction on repeated resend. # Forcing ciphersuite for this test to fit the MTU of 508 with full config. # The ratio of max/min timeout should ideally equal 4 to accept two @@ -10736,7 +10767,7 @@ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC requires_gnutls requires_max_content_len 2048 -run_test "DTLS fragmenting: gnutls server, DTLS 1.2" \ +run_test "DTLS fragmenting: MTU=512, gnutls server, DTLS 1.2" \ "$G_SRV -u" \ "$P_CLI dtls=1 debug_level=2 \ crt_file=$DATA_FILES_PATH/server8_int-ca2.crt \ @@ -10746,6 +10777,21 @@ run_test "DTLS fragmenting: gnutls server, DTLS 1.2" \ -c "fragmenting Certificate handshake message" \ -C "error" +requires_config_enabled MBEDTLS_SSL_PROTO_DTLS +requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC +requires_gnutls +requires_max_content_len 2048 +run_test "DTLS fragmenting: MTU=128, gnutls server, DTLS 1.2" \ + "$G_NEXT_SRV -u" \ + "$P_CLI dtls=1 debug_level=2 \ + crt_file=$DATA_FILES_PATH/server8_int-ca2.crt \ + key_file=$DATA_FILES_PATH/server8.key \ + mtu=128 force_version=dtls12" \ + 0 \ + -c "fragmenting ClientHello handshake message" \ + -c "fragmenting Certificate handshake message" \ + -C "error" + # We use --insecure for the GnuTLS client because it expects # the hostname / IP it connects to to be the name used in the # certificate obtained from the server. Here, however, it @@ -10758,7 +10804,7 @@ requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC requires_gnutls requires_not_i686 requires_max_content_len 2048 -run_test "DTLS fragmenting: gnutls client, DTLS 1.2" \ +run_test "DTLS fragmenting: MTU=512, gnutls client, DTLS 1.2" \ "$P_SRV dtls=1 debug_level=2 \ crt_file=$DATA_FILES_PATH/server7_int-ca.crt \ key_file=$DATA_FILES_PATH/server7.key \ @@ -10770,7 +10816,7 @@ run_test "DTLS fragmenting: gnutls client, DTLS 1.2" \ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC requires_max_content_len 2048 -run_test "DTLS fragmenting: openssl server, DTLS 1.2" \ +run_test "DTLS fragmenting: MTU=512, openssl server, DTLS 1.2" \ "$O_SRV -dtls1_2 -verify 10" \ "$P_CLI dtls=1 debug_level=2 \ crt_file=$DATA_FILES_PATH/server8_int-ca2.crt \ @@ -10780,10 +10826,29 @@ run_test "DTLS fragmenting: openssl server, DTLS 1.2" \ -c "fragmenting Certificate handshake message" \ -C "error" +# Depending on the ciphersuite selected to encrypt the application data, the +# maximum application data payload per record may be small with an MTU of 128. +# For example, with TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA384, this maximum is +# 35 bytes. We therefore reduce the size of the client request in this test. requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC requires_max_content_len 2048 -run_test "DTLS fragmenting: openssl client, DTLS 1.2" \ +run_test "DTLS fragmenting: MTU=128, openssl server, DTLS 1.2" \ + "$O_NEXT_SRV -dtls1_2 -verify 10" \ + "$P_CLI dtls=1 debug_level=2 \ + crt_file=$DATA_FILES_PATH/server8_int-ca2.crt \ + key_file=$DATA_FILES_PATH/server8.key \ + request_size=8 \ + mtu=128 force_version=dtls12" \ + 0 \ + -c "fragmenting ClientHello handshake message" \ + -c "fragmenting Certificate handshake message" \ + -C "error" + +requires_config_enabled MBEDTLS_SSL_PROTO_DTLS +requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC +requires_max_content_len 2048 +run_test "DTLS fragmenting: MTU=512, openssl client, DTLS 1.2" \ "$P_SRV dtls=1 debug_level=2 \ crt_file=$DATA_FILES_PATH/server7_int-ca.crt \ key_file=$DATA_FILES_PATH/server7.key \ @@ -10801,7 +10866,7 @@ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC client_needs_more_time 4 requires_max_content_len 2048 -run_test "DTLS fragmenting: 3d, gnutls server, DTLS 1.2" \ +run_test "DTLS fragmenting: 3d, MTU=512, gnutls server, DTLS 1.2" \ -p "$P_PXY drop=8 delay=8 duplicate=8" \ "$G_NEXT_SRV -u" \ "$P_CLI dgram_packing=0 dtls=1 debug_level=2 \ @@ -10812,12 +10877,29 @@ run_test "DTLS fragmenting: 3d, gnutls server, DTLS 1.2" \ -c "fragmenting Certificate handshake message" \ -C "error" +requires_gnutls_next +requires_config_enabled MBEDTLS_SSL_PROTO_DTLS +requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC +client_needs_more_time 6 +requires_max_content_len 2048 +run_test "DTLS fragmenting: 3d, MTU=128, gnutls server, DTLS 1.2" \ + -p "$P_PXY drop=8 delay=8 duplicate=8" \ + "$G_NEXT_SRV -u" \ + "$P_CLI dgram_packing=0 dtls=1 debug_level=2 \ + crt_file=$DATA_FILES_PATH/server8_int-ca2.crt \ + key_file=$DATA_FILES_PATH/server8.key \ + hs_timeout=250-60000 mtu=128 force_version=dtls12" \ + 0 \ + -c "fragmenting ClientHello handshake message" \ + -c "fragmenting Certificate handshake message" \ + -C "error" + requires_gnutls_next requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC client_needs_more_time 4 requires_max_content_len 2048 -run_test "DTLS fragmenting: 3d, gnutls client, DTLS 1.2" \ +run_test "DTLS fragmenting: 3d, MTU=512, gnutls client, DTLS 1.2" \ -p "$P_PXY drop=8 delay=8 duplicate=8" \ "$P_SRV dtls=1 debug_level=2 \ crt_file=$DATA_FILES_PATH/server7_int-ca.crt \ @@ -10834,7 +10916,7 @@ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC client_needs_more_time 4 requires_max_content_len 2048 -run_test "DTLS fragmenting: 3d, openssl server, DTLS 1.2" \ +run_test "DTLS fragmenting: 3d, MTU=512, openssl server, DTLS 1.2" \ -p "$P_PXY drop=8 delay=8 duplicate=8" \ "$O_NEXT_SRV -dtls1_2 -verify 10" \ "$P_CLI dtls=1 debug_level=2 \ @@ -10845,6 +10927,28 @@ run_test "DTLS fragmenting: 3d, openssl server, DTLS 1.2" \ -c "fragmenting Certificate handshake message" \ -C "error" +# Depending on the ciphersuite selected to encrypt the application data, the +# maximum application data payload per record may be small with an MTU of 128. +# For example, with TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA384, this maximum is +# 35 bytes. We therefore reduce the size of the client request in this test. +requires_openssl_next +requires_config_enabled MBEDTLS_SSL_PROTO_DTLS +requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC +client_needs_more_time 4 +requires_max_content_len 2048 +run_test "DTLS fragmenting: 3d, MTU=128, openssl server, DTLS 1.2" \ + -p "$P_PXY drop=8 delay=8 duplicate=8" \ + "$O_NEXT_SRV -dtls1_2 -verify 10" \ + "$P_CLI dtls=1 debug_level=2 \ + crt_file=$DATA_FILES_PATH/server8_int-ca2.crt \ + key_file=$DATA_FILES_PATH/server8.key \ + request_size=8 \ + hs_timeout=250-60000 mtu=128 force_version=dtls12" \ + 0 \ + -c "fragmenting ClientHello handshake message" \ + -c "fragmenting Certificate handshake message" \ + -C "error" + ## the test below will time out with certain seed. ## The cause is an openssl bug (https://github.com/openssl/openssl/issues/18887) skip_next_test @@ -10852,7 +10956,7 @@ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC client_needs_more_time 4 requires_max_content_len 2048 -run_test "DTLS fragmenting: 3d, openssl client, DTLS 1.2" \ +run_test "DTLS fragmenting: 3d, MTU=512, openssl client, DTLS 1.2" \ -p "$P_PXY drop=8 delay=8 duplicate=8" \ "$P_SRV dtls=1 debug_level=2 \ crt_file=$DATA_FILES_PATH/server7_int-ca.crt \ From e436f74576770436392d1a1702ff151706fc71c5 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Tue, 10 Feb 2026 19:12:21 +0100 Subject: [PATCH 14/34] ssl-opt.sh: Fix/improve comments Signed-off-by: Ronald Cron --- tests/ssl-opt.sh | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh index 7f36ab5c14..c460505a8c 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -9993,8 +9993,9 @@ run_test "DTLS reassembly: some fragmentation (gnutls client)" \ # Set the MTU to 128 bytes. The minimum size of a DTLS 1.2 record # containing a ClientHello handshake message is 69 bytes, without any cookie, # ciphersuite, or extension. With an MTU of 128 bytes, the ClientHello handshake -# message is therefore very likely to be fragmented in most library -# configurations. +# message is therefore very likely to be fragmented, regardless of the +# GnuTLS client version. For example, the ClientHello sent by the GnuTLS 3.7.2 +# client is 206 bytes in this test. requires_gnutls requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS reassembly: more fragmentation (gnutls client)" \ @@ -10055,10 +10056,11 @@ run_test "DTLS reassembly: no fragmentation (openssl client)" \ -S "error" # Minimum possible MTU for OpenSSL server: 256 bytes. -# We expect the server Certificate handshake to be fragmented and verify that -# this is the case. Depending on the configuration, other handshake messages may -# also be fragmented like the ClientHello, ClientKeyExchange or -# CertificateVerify messages. +# We expect the client Certificate handshake message to be fragmented and +# verify that this is the case. With OpenSSL 3.0.13, the ClientHello handshake +# message is 224 bytes and also fragmented. However, it may not hold across +# OpenSSL version updates. Therefore, we do not verify that the ClientHello is +# reassembled by the server. requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS reassembly: some fragmentation (openssl client)" \ "$P_SRV debug_level=2 dtls=1 auth_mode=required" \ @@ -12147,9 +12149,12 @@ run_test "DTLS proxy: 3d, gnutls client" \ 0 \ -s "HTTP/1.0 200 OK" -# Set the MTU to 128 bytes. The ClientHello is not surely fragmented but very -# likely. Do not set it to 56 bytes where we would be sure that the ClientHello -# is fragmented as then experimentally the handshake fails too often. +# Set the MTU to 128 bytes. The ClientHello is not guaranteed to be surely +# fragmented but it is very likely. For example, the ClientHello sent by the +# GnuTLS 3.7.2 client is 206 bytes in this test. We expect ClientHello +# fragmentation to remain the case across GnuTLS version updates. Avoid using a +# smaller MTU, as the smaller the MTU, the more likely the handshake is to fail +# in this very unreliable connection emulation. requires_gnutls client_needs_more_time 8 not_with_valgrind # risk of non-mbedtls peer timing out From 3ddc63d74e48eb911ac1569418a2212e64b71b5c Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Wed, 11 Feb 2026 09:19:18 +0100 Subject: [PATCH 15/34] ssl-opt.sh: DTLS reassembly: Improve max_content_len requirements Signed-off-by: Ronald Cron --- tests/ssl-opt.sh | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh index c460505a8c..7e5d30387b 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -10320,7 +10320,7 @@ run_test "DTLS fragmenting: server (MTU)" \ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC -requires_max_content_len 2048 +requires_max_content_len 1024 requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS fragmenting: both (MTU=1024)" \ -p "$P_PXY mtu=1024" \ @@ -10343,7 +10343,7 @@ run_test "DTLS fragmenting: both (MTU=1024)" \ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC requires_hash_alg SHA_256 -requires_max_content_len 2048 +requires_max_content_len 512 run_test "DTLS fragmenting: both (MTU=512)" \ -p "$P_PXY mtu=512" \ "$P_SRV dtls=1 debug_level=2 auth_mode=required \ @@ -10368,7 +10368,7 @@ run_test "DTLS fragmenting: both (MTU=512)" \ # 35 bytes. We therefore reduce the size of the client request and the server # response in this test. requires_config_enabled MBEDTLS_SSL_PROTO_DTLS -requires_max_content_len 2048 +requires_max_content_len 128 run_test "DTLS fragmenting: both (MTU=128)" \ -p "$P_PXY mtu=128" \ "$P_SRV dtls=1 debug_level=5 auth_mode=required \ @@ -10446,7 +10446,7 @@ run_test "DTLS fragmenting: proxy MTU: auto-reduction (with valgrind)" \ not_with_valgrind # spurious autoreduction due to timeout requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC -requires_max_content_len 2048 +requires_max_content_len 1024 requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS fragmenting: proxy MTU, simple handshake (MTU=1024)" \ -p "$P_PXY mtu=1024" \ @@ -10473,7 +10473,7 @@ run_test "DTLS fragmenting: proxy MTU, simple handshake (MTU=1024)" \ not_with_valgrind # spurious autoreduction due to timeout requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC -requires_max_content_len 2048 +requires_max_content_len 512 run_test "DTLS fragmenting: proxy MTU, simple handshake (MTU=512)" \ -p "$P_PXY mtu=512" \ "$P_SRV dtls=1 debug_level=2 auth_mode=required \ @@ -10496,7 +10496,7 @@ run_test "DTLS fragmenting: proxy MTU, simple handshake (MTU=512)" \ not_with_valgrind # spurious autoreduction due to timeout requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC -requires_max_content_len 2048 +requires_max_content_len 1024 requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS fragmenting: proxy MTU, simple handshake, nbio (MTU=1024)" \ -p "$P_PXY mtu=1024" \ @@ -10520,7 +10520,7 @@ run_test "DTLS fragmenting: proxy MTU, simple handshake, nbio (MTU=1024)" \ not_with_valgrind # spurious autoreduction due to timeout requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC -requires_max_content_len 2048 +requires_max_content_len 512 run_test "DTLS fragmenting: proxy MTU, simple handshake, nbio (MTU=512)" \ -p "$P_PXY mtu=512" \ "$P_SRV dtls=1 debug_level=2 auth_mode=required \ @@ -10553,7 +10553,7 @@ run_test "DTLS fragmenting: proxy MTU, simple handshake, nbio (MTU=512)" \ not_with_valgrind # spurious autoreduction due to timeout requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC -requires_max_content_len 2048 +requires_max_content_len 1450 run_test "DTLS fragmenting: proxy MTU, resumed handshake" \ -p "$P_PXY mtu=1450" \ "$P_SRV dtls=1 debug_level=2 auth_mode=required \ @@ -10580,7 +10580,7 @@ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC requires_hash_alg SHA_256 requires_config_enabled MBEDTLS_SSL_RENEGOTIATION -requires_max_content_len 2048 +requires_max_content_len 512 run_test "DTLS fragmenting: proxy MTU, ChachaPoly renego" \ -p "$P_PXY mtu=512" \ "$P_SRV dtls=1 debug_level=2 auth_mode=required \ @@ -10609,7 +10609,7 @@ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC requires_hash_alg SHA_256 requires_config_enabled MBEDTLS_SSL_RENEGOTIATION -requires_max_content_len 2048 +requires_max_content_len 512 run_test "DTLS fragmenting: proxy MTU, AES-GCM renego" \ -p "$P_PXY mtu=512" \ "$P_SRV dtls=1 debug_level=2 auth_mode=required \ @@ -10638,7 +10638,7 @@ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC requires_hash_alg SHA_256 requires_config_enabled MBEDTLS_SSL_RENEGOTIATION -requires_max_content_len 2048 +requires_max_content_len 1024 run_test "DTLS fragmenting: proxy MTU, AES-CCM renego" \ -p "$P_PXY mtu=1024" \ "$P_SRV dtls=1 debug_level=2 auth_mode=required \ @@ -10668,7 +10668,7 @@ requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC requires_hash_alg SHA_256 requires_config_enabled MBEDTLS_SSL_RENEGOTIATION requires_config_enabled MBEDTLS_SSL_ENCRYPT_THEN_MAC -requires_max_content_len 2048 +requires_max_content_len 1024 run_test "DTLS fragmenting: proxy MTU, AES-CBC EtM renego" \ -p "$P_PXY mtu=1024" \ "$P_SRV dtls=1 debug_level=2 auth_mode=required \ @@ -10697,7 +10697,7 @@ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC requires_hash_alg SHA_256 requires_config_enabled MBEDTLS_SSL_RENEGOTIATION -requires_max_content_len 2048 +requires_max_content_len 1024 run_test "DTLS fragmenting: proxy MTU, AES-CBC non-EtM renego" \ -p "$P_PXY mtu=1024" \ "$P_SRV dtls=1 debug_level=2 auth_mode=required \ @@ -10723,7 +10723,7 @@ run_test "DTLS fragmenting: proxy MTU, AES-CBC non-EtM renego" \ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC client_needs_more_time 2 -requires_max_content_len 2048 +requires_max_content_len 512 run_test "DTLS fragmenting: proxy MTU + 3d" \ -p "$P_PXY mtu=512 drop=8 delay=8 duplicate=8" \ "$P_SRV dgram_packing=0 dtls=1 debug_level=2 auth_mode=required \ @@ -10744,7 +10744,7 @@ run_test "DTLS fragmenting: proxy MTU + 3d" \ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC client_needs_more_time 2 -requires_max_content_len 2048 +requires_max_content_len 512 run_test "DTLS fragmenting: proxy MTU + 3d, nbio" \ -p "$P_PXY mtu=512 drop=8 delay=8 duplicate=8" \ "$P_SRV dtls=1 debug_level=2 auth_mode=required \ From 814f5da61a004146b7f942609202cf034b461b50 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Wed, 11 Feb 2026 09:08:03 +0100 Subject: [PATCH 16/34] ssl-opt.sh: Use more diverse MTUs Do not use only power of 2 MTUs. Use diverse MTUs in DTLS reassembly/ fragmenting/proxy tests. Signed-off-by: Ronald Cron --- tests/ssl-opt.sh | 189 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 142 insertions(+), 47 deletions(-) diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh index 7e5d30387b..a999c94f5b 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -9931,7 +9931,7 @@ run_test "DTLS reassembly: more fragmentation (gnutls server)" \ requires_gnutls requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS reassembly: more fragmentation, nbio (gnutls server)" \ - "$G_SRV -u --mtu 128" \ + "$G_SRV -u --mtu 109" \ "$P_CLI dtls=1 nbio=2 debug_level=2" \ 0 \ -c "found fragmented DTLS handshake message" \ @@ -9943,7 +9943,7 @@ requires_gnutls requires_config_enabled MBEDTLS_SSL_RENEGOTIATION requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS reassembly: fragmentation, renego (gnutls server)" \ - "$G_SRV -u --mtu 256" \ + "$G_SRV -u --mtu 241" \ "$P_CLI debug_level=3 dtls=1 renegotiation=1 renegotiate=1" \ 0 \ -c "found fragmented DTLS handshake message" \ @@ -9984,7 +9984,7 @@ requires_gnutls requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS reassembly: some fragmentation (gnutls client)" \ "$P_SRV debug_level=2 dtls=1 auth_mode=required" \ - "$G_NEXT_CLI -u --mtu 256 --insecure 127.0.0.1 --x509certfile $DATA_FILES_PATH/server5.crt --x509keyfile $DATA_FILES_PATH/server5.key" \ + "$G_NEXT_CLI -u --mtu 211 --insecure 127.0.0.1 --x509certfile $DATA_FILES_PATH/server5.crt --x509keyfile $DATA_FILES_PATH/server5.key" \ 0 \ -s "found fragmented DTLS handshake message" \ -s "Certificate handshake message has been buffered and reassembled" \ @@ -10000,7 +10000,7 @@ requires_gnutls requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS reassembly: more fragmentation (gnutls client)" \ "$P_SRV debug_level=2 dtls=1" \ - "$G_NEXT_CLI -u --mtu 128 --insecure 127.0.0.1" \ + "$G_NEXT_CLI -u --mtu 103 --insecure 127.0.0.1" \ 0 \ -s "ClientHello handshake message has been buffered and reassembled" \ -S "error" @@ -10009,7 +10009,7 @@ requires_gnutls requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS reassembly: more fragmentation, nbio (gnutls client)" \ "$P_SRV debug_level=2 dtls=1 nbio=2" \ - "$G_NEXT_CLI -u --mtu 128 --insecure 127.0.0.1" \ + "$G_NEXT_CLI -u --mtu 103 --insecure 127.0.0.1" \ 0 \ -s "ClientHello handshake message has been buffered and reassembled" \ -S "error" @@ -10040,7 +10040,7 @@ run_test "DTLS reassembly: fragmentation (openssl server)" \ requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS reassembly: fragmentation, nbio (openssl server)" \ - "$O_SRV -dtls -mtu 256" \ + "$O_SRV -dtls -mtu 273" \ "$P_CLI dtls=1 nbio=2 debug_level=2" \ 0 \ -c "found fragmented DTLS handshake message" \ @@ -10073,7 +10073,7 @@ run_test "DTLS reassembly: some fragmentation (openssl client)" \ requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS reassembly: fragmentation, nbio (openssl client)" \ "$P_SRV debug_level=2 dtls=1 auth_mode=required nbio=2" \ - "$O_NEXT_CLI -dtls -mtu 256 -cert $DATA_FILES_PATH/server5.crt -key $DATA_FILES_PATH/server5.key" \ + "$O_NEXT_CLI -dtls -mtu 269 -cert $DATA_FILES_PATH/server5.crt -key $DATA_FILES_PATH/server5.key" \ 0 \ -s "found fragmented DTLS handshake message" \ -s "Certificate handshake message has been buffered and reassembled" \ @@ -10320,20 +10320,20 @@ run_test "DTLS fragmenting: server (MTU)" \ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC -requires_max_content_len 1024 +requires_max_content_len 1038 requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 -run_test "DTLS fragmenting: both (MTU=1024)" \ - -p "$P_PXY mtu=1024" \ +run_test "DTLS fragmenting: both (MTU=1038)" \ + -p "$P_PXY mtu=1038" \ "$P_SRV dtls=1 debug_level=2 auth_mode=required \ crt_file=$DATA_FILES_PATH/server7_int-ca.crt \ key_file=$DATA_FILES_PATH/server7.key \ hs_timeout=2500-60000 \ - mtu=1024" \ + mtu=1038" \ "$P_CLI dtls=1 debug_level=2 \ crt_file=$DATA_FILES_PATH/server8_int-ca2.crt \ key_file=$DATA_FILES_PATH/server8.key \ hs_timeout=2500-60000 \ - mtu=1024" \ + mtu=1038" \ 0 \ -s "found fragmented DTLS handshake message" \ -c "found fragmented DTLS handshake message" \ @@ -10343,20 +10343,20 @@ run_test "DTLS fragmenting: both (MTU=1024)" \ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC requires_hash_alg SHA_256 -requires_max_content_len 512 -run_test "DTLS fragmenting: both (MTU=512)" \ - -p "$P_PXY mtu=512" \ +requires_max_content_len 509 +run_test "DTLS fragmenting: both (MTU=509)" \ + -p "$P_PXY mtu=509" \ "$P_SRV dtls=1 debug_level=2 auth_mode=required \ crt_file=$DATA_FILES_PATH/server7_int-ca.crt \ key_file=$DATA_FILES_PATH/server7.key \ hs_timeout=2500-60000 \ - mtu=512" \ + mtu=509" \ "$P_CLI dtls=1 debug_level=2 \ crt_file=$DATA_FILES_PATH/server8_int-ca2.crt \ key_file=$DATA_FILES_PATH/server8.key \ force_ciphersuite=TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256 \ hs_timeout=2500-60000 \ - mtu=512" \ + mtu=509" \ 0 \ -s "found fragmented DTLS handshake message" \ -c "found fragmented DTLS handshake message" \ @@ -10366,7 +10366,7 @@ run_test "DTLS fragmenting: both (MTU=512)" \ # maximum application data payload per record may be small with an MTU of 128. # For example, with TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA384, this maximum is # 35 bytes. We therefore reduce the size of the client request and the server -# response in this test. +# response in this test and the two following tests. requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_max_content_len 128 run_test "DTLS fragmenting: both (MTU=128)" \ @@ -10393,6 +10393,58 @@ run_test "DTLS fragmenting: both (MTU=128)" \ -c "fragmenting CertificateVerify handshake message" \ -C "error" +requires_config_enabled MBEDTLS_SSL_PROTO_DTLS +requires_max_content_len 2048 +run_test "DTLS fragmenting: both (MTU=107)" \ + -p "$P_PXY mtu=107" \ + "$P_SRV dtls=1 debug_level=5 auth_mode=required \ + crt_file=$DATA_FILES_PATH/server7_int-ca.crt \ + key_file=$DATA_FILES_PATH/server7.key \ + response_size=8 \ + hs_timeout=2500-60000 \ + mtu=107" \ + "$P_CLI dtls=1 debug_level=2 \ + crt_file=$DATA_FILES_PATH/server8_int-ca2.crt \ + key_file=$DATA_FILES_PATH/server8.key \ + request_size=8 \ + hs_timeout=2500-60000 \ + mtu=107" \ + 0 \ + -s "found fragmented DTLS handshake message" \ + -s "fragmenting Certificate handshake message" \ + -s "fragmenting ServerKeyExchange handshake message" \ + -c "found fragmented DTLS handshake message" \ + -c "fragmenting ClientHello handshake message" \ + -c "fragmenting Certificate handshake message" \ + -c "fragmenting CertificateVerify handshake message" \ + -C "error" + +requires_config_enabled MBEDTLS_SSL_PROTO_DTLS +requires_max_content_len 2048 +run_test "DTLS fragmenting: both (MTU=133)" \ + -p "$P_PXY mtu=133" \ + "$P_SRV dtls=1 debug_level=5 auth_mode=required \ + crt_file=$DATA_FILES_PATH/server7_int-ca.crt \ + key_file=$DATA_FILES_PATH/server7.key \ + response_size=8 \ + hs_timeout=2500-60000 \ + mtu=133" \ + "$P_CLI dtls=1 debug_level=2 \ + crt_file=$DATA_FILES_PATH/server8_int-ca2.crt \ + key_file=$DATA_FILES_PATH/server8.key \ + request_size=8 \ + hs_timeout=2500-60000 \ + mtu=133" \ + 0 \ + -s "found fragmented DTLS handshake message" \ + -s "fragmenting Certificate handshake message" \ + -s "fragmenting ServerKeyExchange handshake message" \ + -c "found fragmented DTLS handshake message" \ + -c "fragmenting ClientHello handshake message" \ + -c "fragmenting Certificate handshake message" \ + -c "fragmenting CertificateVerify handshake message" \ + -C "error" + # Test for automatic MTU reduction on repeated resend. # Forcing ciphersuite for this test to fit the MTU of 508 with full config. # The ratio of max/min timeout should ideally equal 4 to accept two @@ -10769,12 +10821,12 @@ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC requires_gnutls requires_max_content_len 2048 -run_test "DTLS fragmenting: MTU=512, gnutls server, DTLS 1.2" \ +run_test "DTLS fragmenting: MTU=501, gnutls server, DTLS 1.2" \ "$G_SRV -u" \ "$P_CLI dtls=1 debug_level=2 \ crt_file=$DATA_FILES_PATH/server8_int-ca2.crt \ key_file=$DATA_FILES_PATH/server8.key \ - mtu=512 force_version=dtls12" \ + mtu=501 force_version=dtls12" \ 0 \ -c "fragmenting Certificate handshake message" \ -C "error" @@ -10783,12 +10835,13 @@ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC requires_gnutls requires_max_content_len 2048 -run_test "DTLS fragmenting: MTU=128, gnutls server, DTLS 1.2" \ +run_test "DTLS fragmenting: MTU=110, gnutls server, DTLS 1.2" \ "$G_NEXT_SRV -u" \ "$P_CLI dtls=1 debug_level=2 \ crt_file=$DATA_FILES_PATH/server8_int-ca2.crt \ key_file=$DATA_FILES_PATH/server8.key \ - mtu=128 force_version=dtls12" \ + request_size=35 \ + mtu=110 force_version=dtls12" \ 0 \ -c "fragmenting ClientHello handshake message" \ -c "fragmenting Certificate handshake message" \ @@ -10806,11 +10859,25 @@ requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC requires_gnutls requires_not_i686 requires_max_content_len 2048 -run_test "DTLS fragmenting: MTU=512, gnutls client, DTLS 1.2" \ +run_test "DTLS fragmenting: MTU=536, gnutls client, DTLS 1.2" \ "$P_SRV dtls=1 debug_level=2 \ crt_file=$DATA_FILES_PATH/server7_int-ca.crt \ key_file=$DATA_FILES_PATH/server7.key \ - mtu=512 force_version=dtls12" \ + mtu=536 force_version=dtls12" \ + "$G_CLI -u --insecure 127.0.0.1" \ + 0 \ + -s "fragmenting Certificate handshake message" + +requires_config_enabled MBEDTLS_SSL_PROTO_DTLS +requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC +requires_gnutls +requires_not_i686 +requires_max_content_len 2048 +run_test "DTLS fragmenting: MTU=149, gnutls client, DTLS 1.2" \ + "$P_SRV dtls=1 debug_level=2 \ + crt_file=$DATA_FILES_PATH/server7_int-ca.crt \ + key_file=$DATA_FILES_PATH/server7.key \ + mtu=149 force_version=dtls12" \ "$G_CLI -u --insecure 127.0.0.1" \ 0 \ -s "fragmenting Certificate handshake message" @@ -10818,12 +10885,12 @@ run_test "DTLS fragmenting: MTU=512, gnutls client, DTLS 1.2" \ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC requires_max_content_len 2048 -run_test "DTLS fragmenting: MTU=512, openssl server, DTLS 1.2" \ +run_test "DTLS fragmenting: MTU=525, openssl server, DTLS 1.2" \ "$O_SRV -dtls1_2 -verify 10" \ "$P_CLI dtls=1 debug_level=2 \ crt_file=$DATA_FILES_PATH/server8_int-ca2.crt \ key_file=$DATA_FILES_PATH/server8.key \ - mtu=512 force_version=dtls12" \ + mtu=525 force_version=dtls12" \ 0 \ -c "fragmenting Certificate handshake message" \ -C "error" @@ -10835,13 +10902,13 @@ run_test "DTLS fragmenting: MTU=512, openssl server, DTLS 1.2" \ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC requires_max_content_len 2048 -run_test "DTLS fragmenting: MTU=128, openssl server, DTLS 1.2" \ +run_test "DTLS fragmenting: MTU=130, openssl server, DTLS 1.2" \ "$O_NEXT_SRV -dtls1_2 -verify 10" \ "$P_CLI dtls=1 debug_level=2 \ crt_file=$DATA_FILES_PATH/server8_int-ca2.crt \ key_file=$DATA_FILES_PATH/server8.key \ request_size=8 \ - mtu=128 force_version=dtls12" \ + mtu=130 force_version=dtls12" \ 0 \ -c "fragmenting ClientHello handshake message" \ -c "fragmenting Certificate handshake message" \ @@ -10859,6 +10926,18 @@ run_test "DTLS fragmenting: MTU=512, openssl client, DTLS 1.2" \ 0 \ -s "fragmenting Certificate handshake message" +requires_config_enabled MBEDTLS_SSL_PROTO_DTLS +requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC +requires_max_content_len 2048 +run_test "DTLS fragmenting: MTU=131, openssl client, DTLS 1.2" \ + "$P_SRV dtls=1 debug_level=2 \ + crt_file=$DATA_FILES_PATH/server7_int-ca.crt \ + key_file=$DATA_FILES_PATH/server7.key \ + mtu=131 force_version=dtls12" \ + "$O_CLI -dtls1_2" \ + 0 \ + -s "fragmenting Certificate handshake message" + # interop tests for DTLS fragmentating with unreliable connection # # again we just want to test that the we fragment in a way that @@ -10868,13 +10947,13 @@ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC client_needs_more_time 4 requires_max_content_len 2048 -run_test "DTLS fragmenting: 3d, MTU=512, gnutls server, DTLS 1.2" \ +run_test "DTLS fragmenting: 3d, MTU=434, gnutls server, DTLS 1.2" \ -p "$P_PXY drop=8 delay=8 duplicate=8" \ "$G_NEXT_SRV -u" \ "$P_CLI dgram_packing=0 dtls=1 debug_level=2 \ crt_file=$DATA_FILES_PATH/server8_int-ca2.crt \ key_file=$DATA_FILES_PATH/server8.key \ - hs_timeout=250-60000 mtu=512 force_version=dtls12" \ + hs_timeout=250-60000 mtu=434 force_version=dtls12" \ 0 \ -c "fragmenting Certificate handshake message" \ -C "error" @@ -10884,13 +10963,14 @@ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC client_needs_more_time 6 requires_max_content_len 2048 -run_test "DTLS fragmenting: 3d, MTU=128, gnutls server, DTLS 1.2" \ +run_test "DTLS fragmenting: 3d, MTU=103, gnutls server, DTLS 1.2" \ -p "$P_PXY drop=8 delay=8 duplicate=8" \ "$G_NEXT_SRV -u" \ "$P_CLI dgram_packing=0 dtls=1 debug_level=2 \ crt_file=$DATA_FILES_PATH/server8_int-ca2.crt \ key_file=$DATA_FILES_PATH/server8.key \ - hs_timeout=250-60000 mtu=128 force_version=dtls12" \ + request_size=35 \ + hs_timeout=250-60000 mtu=103 force_version=dtls12" \ 0 \ -c "fragmenting ClientHello handshake message" \ -c "fragmenting Certificate handshake message" \ @@ -10901,12 +10981,27 @@ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC client_needs_more_time 4 requires_max_content_len 2048 -run_test "DTLS fragmenting: 3d, MTU=512, gnutls client, DTLS 1.2" \ +run_test "DTLS fragmenting: 3d, MTU=614, gnutls client, DTLS 1.2" \ -p "$P_PXY drop=8 delay=8 duplicate=8" \ "$P_SRV dtls=1 debug_level=2 \ crt_file=$DATA_FILES_PATH/server7_int-ca.crt \ key_file=$DATA_FILES_PATH/server7.key \ - hs_timeout=250-60000 mtu=512 force_version=dtls12" \ + hs_timeout=250-60000 mtu=614 force_version=dtls12" \ + "$G_NEXT_CLI -u --insecure 127.0.0.1" \ + 0 \ + -s "fragmenting Certificate handshake message" + +requires_gnutls_next +requires_config_enabled MBEDTLS_SSL_PROTO_DTLS +requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC +client_needs_more_time 4 +requires_max_content_len 2048 +run_test "DTLS fragmenting: 3d, MTU=116, gnutls client, DTLS 1.2" \ + -p "$P_PXY drop=8 delay=8 duplicate=8" \ + "$P_SRV dtls=1 debug_level=2 \ + crt_file=$DATA_FILES_PATH/server7_int-ca.crt \ + key_file=$DATA_FILES_PATH/server7.key \ + hs_timeout=250-60000 mtu=116 force_version=dtls12" \ "$G_NEXT_CLI -u --insecure 127.0.0.1" \ 0 \ -s "fragmenting Certificate handshake message" @@ -10918,13 +11013,13 @@ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC client_needs_more_time 4 requires_max_content_len 2048 -run_test "DTLS fragmenting: 3d, MTU=512, openssl server, DTLS 1.2" \ +run_test "DTLS fragmenting: 3d, MTU=541, openssl server, DTLS 1.2" \ -p "$P_PXY drop=8 delay=8 duplicate=8" \ "$O_NEXT_SRV -dtls1_2 -verify 10" \ "$P_CLI dtls=1 debug_level=2 \ crt_file=$DATA_FILES_PATH/server8_int-ca2.crt \ key_file=$DATA_FILES_PATH/server8.key \ - hs_timeout=250-60000 mtu=512 force_version=dtls12" \ + hs_timeout=250-60000 mtu=541 force_version=dtls12" \ 0 \ -c "fragmenting Certificate handshake message" \ -C "error" @@ -10938,14 +11033,14 @@ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC client_needs_more_time 4 requires_max_content_len 2048 -run_test "DTLS fragmenting: 3d, MTU=128, openssl server, DTLS 1.2" \ +run_test "DTLS fragmenting: 3d, MTU=108, openssl server, DTLS 1.2" \ -p "$P_PXY drop=8 delay=8 duplicate=8" \ "$O_NEXT_SRV -dtls1_2 -verify 10" \ "$P_CLI dtls=1 debug_level=2 \ crt_file=$DATA_FILES_PATH/server8_int-ca2.crt \ key_file=$DATA_FILES_PATH/server8.key \ request_size=8 \ - hs_timeout=250-60000 mtu=128 force_version=dtls12" \ + hs_timeout=250-60000 mtu=108 force_version=dtls12" \ 0 \ -c "fragmenting ClientHello handshake message" \ -c "fragmenting Certificate handshake message" \ @@ -12045,7 +12140,7 @@ not_with_valgrind # risk of non-mbedtls peer timing out requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS proxy: 3d, openssl server, fragmentation" \ -p "$P_PXY drop=5 delay=5 duplicate=5 protect_hvr=1" \ - "$O_NEXT_SRV -dtls1_2 -mtu 256" \ + "$O_NEXT_SRV -dtls1_2 -mtu 277" \ "$P_CLI dgram_packing=0 dtls=1 debug_level=2 hs_timeout=500-60000 tickets=0" \ 0 \ -c "HTTP/1.0 200 OK" \ @@ -12057,7 +12152,7 @@ not_with_valgrind # risk of non-mbedtls peer timing out requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS proxy: 3d, openssl server, fragmentation, nbio" \ -p "$P_PXY drop=5 delay=5 duplicate=5 protect_hvr=1" \ - "$O_NEXT_SRV -dtls1_2 -mtu 256" \ + "$O_NEXT_SRV -dtls1_2 -mtu 268" \ "$P_CLI dgram_packing=0 dtls=1 debug_level=2 hs_timeout=500-60000 nbio=2 tickets=0" \ 0 \ -c "HTTP/1.0 200 OK" \ @@ -12081,7 +12176,7 @@ requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS proxy: 3d, openssl client, fragmentation" \ -p "$P_PXY drop=5 delay=5 duplicate=5" \ "$P_SRV debug_level=2 dgram_packing=0 auth_mode=required dtls=1 hs_timeout=500-60000 tickets=0" \ - "$O_NEXT_CLI -dtls1_2 -mtu 256 -cert $DATA_FILES_PATH/server5.crt -key $DATA_FILES_PATH/server5.key" \ + "$O_NEXT_CLI -dtls1_2 -mtu 260 -cert $DATA_FILES_PATH/server5.crt -key $DATA_FILES_PATH/server5.key" \ 0 \ -s "HTTP/1.0 200 OK" \ -s "found fragmented DTLS handshake message" \ @@ -12094,7 +12189,7 @@ requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS proxy: 3d, openssl client, fragmentation, nbio" \ -p "$P_PXY drop=5 delay=5 duplicate=5" \ "$P_SRV debug_level=2 dgram_packing=0 auth_mode=required dtls=1 hs_timeout=500-60000 nbio=2 tickets=0" \ - "$O_NEXT_CLI -dtls1_2 -mtu 256 -cert $DATA_FILES_PATH/server5.crt -key $DATA_FILES_PATH/server5.key" \ + "$O_NEXT_CLI -dtls1_2 -mtu 259 -cert $DATA_FILES_PATH/server5.crt -key $DATA_FILES_PATH/server5.key" \ 0 \ -s "HTTP/1.0 200 OK" \ -s "found fragmented DTLS handshake message" \ @@ -12118,7 +12213,7 @@ not_with_valgrind # risk of non-mbedtls peer timing out requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS proxy: 3d, gnutls server, fragmentation" \ -p "$P_PXY drop=5 delay=5 duplicate=5" \ - "$G_NEXT_SRV -u --mtu 512" \ + "$G_NEXT_SRV -u --mtu 499" \ "$P_CLI dgram_packing=0 dtls=1 debug_level=2 hs_timeout=500-60000" \ 0 \ -s "Extra-header:" \ @@ -12131,7 +12226,7 @@ not_with_valgrind # risk of non-mbedtls peer timing out requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS proxy: 3d, gnutls server, fragmentation, nbio" \ -p "$P_PXY drop=5 delay=5 duplicate=5" \ - "$G_NEXT_SRV -u --mtu 512" \ + "$G_NEXT_SRV -u --mtu 528" \ "$P_CLI dgram_packing=0 dtls=1 debug_level=2 hs_timeout=500-60000 nbio=2" \ 0 \ -s "Extra-header:" \ @@ -12149,7 +12244,7 @@ run_test "DTLS proxy: 3d, gnutls client" \ 0 \ -s "HTTP/1.0 200 OK" -# Set the MTU to 128 bytes. The ClientHello is not guaranteed to be surely +# Set the MTU to 131 bytes. The ClientHello is not guaranteed to be surely # fragmented but it is very likely. For example, the ClientHello sent by the # GnuTLS 3.7.2 client is 206 bytes in this test. We expect ClientHello # fragmentation to remain the case across GnuTLS version updates. Avoid using a @@ -12162,7 +12257,7 @@ requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS proxy: 3d, gnutls client, fragmentation" \ -p "$P_PXY drop=5 delay=5 duplicate=5" \ "$P_SRV dgram_packing=0 dtls=1 debug_level=2" \ - "$G_NEXT_CLI -u --mtu 128 --insecure 127.0.0.1" \ + "$G_NEXT_CLI -u --mtu 131 --insecure 127.0.0.1" \ 0 \ -s "HTTP/1.0 200 OK" \ -s "ClientHello handshake message has been buffered and reassembled" @@ -12174,7 +12269,7 @@ requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS proxy: 3d, gnutls client, fragmentation, nbio=2" \ -p "$P_PXY drop=5 delay=5 duplicate=5" \ "$P_SRV dgram_packing=0 dtls=1 debug_level=2 nbio=2" \ - "$G_NEXT_CLI -u --mtu 128 --insecure 127.0.0.1" \ + "$G_NEXT_CLI -u --mtu 135 --insecure 127.0.0.1" \ 0 \ -s "HTTP/1.0 200 OK" \ -s "ClientHello handshake message has been buffered and reassembled" From 3771c17a0b02cb9e80c0d6c4de90c3c9ec78d59c Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Fri, 13 Feb 2026 10:52:28 +0100 Subject: [PATCH 17/34] Update mbedtls_ssl_handshake() documentation Signed-off-by: Ronald Cron --- include/mbedtls/ssl.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h index 1425896976..8f58b3e9c0 100644 --- a/include/mbedtls/ssl.h +++ b/include/mbedtls/ssl.h @@ -4788,13 +4788,6 @@ int mbedtls_ssl_get_session(const mbedtls_ssl_context *ssl, * supported with some limitations (those limitations do * not apply to DTLS, where defragmentation is fully * supported): - * - On an Mbed TLS server that only accepts TLS 1.2, - * the initial ClientHello message must not be fragmented. - * A TLS 1.2 ClientHello may be fragmented if the server - * also accepts TLS 1.3 connections (meaning - * that #MBEDTLS_SSL_PROTO_TLS1_3 enabled, and the - * accepted versions have not been restricted with - * mbedtls_ssl_conf_max_tls_version() or the like). * - The first fragment of a handshake message must be * at least 4 bytes long. * - Non-handshake records must not be interleaved between From a5f45bb93578c668a2547a28bfeacf72f757a410 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Fri, 13 Feb 2026 11:03:13 +0100 Subject: [PATCH 18/34] Add change log Signed-off-by: Ronald Cron --- ChangeLog.d/dtls-client-hello-defragmentation.txt | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 ChangeLog.d/dtls-client-hello-defragmentation.txt diff --git a/ChangeLog.d/dtls-client-hello-defragmentation.txt b/ChangeLog.d/dtls-client-hello-defragmentation.txt new file mode 100644 index 0000000000..f5ff0b754c --- /dev/null +++ b/ChangeLog.d/dtls-client-hello-defragmentation.txt @@ -0,0 +1,5 @@ +Bugfix + * Support re-assembly of fragmented DTLS 1.2 ClientHello in Mbed TLS server. + * Support re-assembly of fragmented TLS 1.2 ClientHello in Mbed TLS server + even if TLS 1.3 support is disabled. This removes the main limitation on + support for re-assembly of fragmented handshake messages in TLS 1.2. From 53dd7d0dce5e16d21fd2bcb4e5454c6533590360 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Mon, 23 Feb 2026 14:09:13 +0100 Subject: [PATCH 19/34] ssl_tls12_server.c: Update hs status after some validations of the ClientHello Signed-off-by: Ronald Cron --- library/ssl_tls12_server.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/library/ssl_tls12_server.c b/library/ssl_tls12_server.c index 36d15c50a7..54fb8669ae 100644 --- a/library/ssl_tls12_server.c +++ b/library/ssl_tls12_server.c @@ -881,12 +881,6 @@ static int ssl_parse_client_hello(mbedtls_ssl_context *ssl) return ret; } - ret = mbedtls_ssl_update_handshake_status(ssl); - if (0 != ret) { - MBEDTLS_SSL_DEBUG_RET(1, ("mbedtls_ssl_update_handshake_status"), ret); - return ret; - } - buf = ssl->in_msg; msg_len = ssl->in_hslen; @@ -1092,6 +1086,21 @@ static int ssl_parse_client_hello(mbedtls_ssl_context *ssl) ext_len = 0; } + /* + * Update the handshake checksum after performing preliminary + * validation of the ClientHello and before parsing its extensions. + * + * The checksum must be updated before parsing the extensions because + * ssl_parse_session_ticket_ext() may decrypt the ticket in place and + * therefore modify the ClientHello message. This occurs when using + * the Mbed TLS ssl_ticket.c implementation. + */ + ret = mbedtls_ssl_update_handshake_status(ssl); + if (0 != ret) { + MBEDTLS_SSL_DEBUG_RET(1, ("mbedtls_ssl_update_handshake_status"), ret); + return ret; + } + ext = buf + ext_offset + 2; MBEDTLS_SSL_DEBUG_BUF(3, "client hello extensions", ext, ext_len); @@ -1233,7 +1242,11 @@ static int ssl_parse_client_hello(mbedtls_ssl_context *ssl) #if defined(MBEDTLS_SSL_SESSION_TICKETS) case MBEDTLS_TLS_EXT_SESSION_TICKET: MBEDTLS_SSL_DEBUG_MSG(3, ("found session ticket extension")); - + /* + * If the Mbed TLS ssl_ticket.c implementation is used, the + * ticket is decrypted in place. This modifies the ClientHello + * message in the input buffer. + */ ret = ssl_parse_session_ticket_ext(ssl, ext + 4, ext_size); if (ret != 0) { return ret; From 1ef64a73e4b67459029cbf3332ec1816675615c1 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Fri, 13 Feb 2026 17:01:19 +0100 Subject: [PATCH 20/34] programs: metatest: Remove include path duplication Signed-off-by: Ronald Cron --- programs/Makefile | 2 +- programs/test/CMakeLists.txt | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/programs/Makefile b/programs/Makefile index 36679dcb0f..323e33ec56 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -199,7 +199,7 @@ endif test/metatest$(EXEXT): $(FRAMEWORK)/tests/programs/metatest.c $(DEP) echo " CC $(FRAMEWORK)/tests/programs/metatest.c" - $(CC) $(LOCAL_CFLAGS) $(CFLAGS) -I../library -I../tf-psa-crypto/core -I../tf-psa-crypto/drivers/builtin/include -I../tf-psa-crypto/drivers/builtin/src $(FRAMEWORK)/tests/programs/metatest.c $(LOCAL_LDFLAGS) $(LDFLAGS) -o $@ + $(CC) $(LOCAL_CFLAGS) $(CFLAGS) -I../library -I../tf-psa-crypto/core -I../tf-psa-crypto/drivers/builtin/src $(FRAMEWORK)/tests/programs/metatest.c $(LOCAL_LDFLAGS) $(LDFLAGS) -o $@ test/query_config.o: test/query_config.c $(FRAMEWORK)/tests/programs/query_config.h $(DEP) echo " CC test/query_config.c" diff --git a/programs/test/CMakeLists.txt b/programs/test/CMakeLists.txt index 8a5d6ba822..1b371d30b9 100644 --- a/programs/test/CMakeLists.txt +++ b/programs/test/CMakeLists.txt @@ -103,8 +103,7 @@ foreach(exe IN LISTS executables) endforeach() target_include_directories(metatest - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../tf-psa-crypto/drivers/builtin/include - ${CMAKE_CURRENT_SOURCE_DIR}/../../tf-psa-crypto/drivers/builtin/src) + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../tf-psa-crypto/drivers/builtin/src) install(TARGETS ${executables} DESTINATION "bin" From ae822174766eeec502961c7e2b70a216a76cad08 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Sat, 14 Feb 2026 10:42:33 +0100 Subject: [PATCH 21/34] programs: metatest: Add tf-psa-crypto/platform as include dir Signed-off-by: Ronald Cron --- programs/Makefile | 2 +- programs/test/CMakeLists.txt | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/programs/Makefile b/programs/Makefile index 323e33ec56..846361d788 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -199,7 +199,7 @@ endif test/metatest$(EXEXT): $(FRAMEWORK)/tests/programs/metatest.c $(DEP) echo " CC $(FRAMEWORK)/tests/programs/metatest.c" - $(CC) $(LOCAL_CFLAGS) $(CFLAGS) -I../library -I../tf-psa-crypto/core -I../tf-psa-crypto/drivers/builtin/src $(FRAMEWORK)/tests/programs/metatest.c $(LOCAL_LDFLAGS) $(LDFLAGS) -o $@ + $(CC) $(LOCAL_CFLAGS) $(CFLAGS) -I../library -I../tf-psa-crypto/core -I../tf-psa-crypto/drivers/builtin/src -I../tf-psa-crypto/platform $(FRAMEWORK)/tests/programs/metatest.c $(LOCAL_LDFLAGS) $(LDFLAGS) -o $@ test/query_config.o: test/query_config.c $(FRAMEWORK)/tests/programs/query_config.h $(DEP) echo " CC test/query_config.c" diff --git a/programs/test/CMakeLists.txt b/programs/test/CMakeLists.txt index 1b371d30b9..0de71a9b03 100644 --- a/programs/test/CMakeLists.txt +++ b/programs/test/CMakeLists.txt @@ -103,8 +103,9 @@ foreach(exe IN LISTS executables) endforeach() target_include_directories(metatest - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../tf-psa-crypto/drivers/builtin/src) - + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../tf-psa-crypto/drivers/builtin/src + ${CMAKE_CURRENT_SOURCE_DIR}/../../tf-psa-crypto/platform +) install(TARGETS ${executables} DESTINATION "bin" PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) From 03ee085e104587b8719903880dce2081d9056f02 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Sat, 14 Feb 2026 10:26:02 +0100 Subject: [PATCH 22/34] Remove duplicated recursion tests The recursion tests for the crypto code are run in a TF-PSA-Crypto component. No need to run them in an Mbed TLS component as well. Signed-off-by: Ronald Cron --- tests/scripts/components-basic-checks.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/scripts/components-basic-checks.sh b/tests/scripts/components-basic-checks.sh index 6a5bc3a1d7..1e480dd12b 100644 --- a/tests/scripts/components-basic-checks.sh +++ b/tests/scripts/components-basic-checks.sh @@ -12,8 +12,6 @@ component_check_recursion () { msg "Check: recursion.pl" # < 1s ./framework/scripts/recursion.pl library/*.c - ./framework/scripts/recursion.pl ${PSA_CORE_PATH}/*.c - ./framework/scripts/recursion.pl ${BUILTIN_SRC_PATH}/*.c } component_check_generated_files () { From 24bf98156ac7d0146ac824f30ce51ebad2bac584 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Sat, 14 Feb 2026 10:40:44 +0100 Subject: [PATCH 23/34] components*.sh: Handle current and upcoming object paths Signed-off-by: Ronald Cron --- .../components-configuration-crypto.sh | 31 ++++++-- tests/scripts/components-platform.sh | 72 +++++++++++++++++++ 2 files changed, 97 insertions(+), 6 deletions(-) diff --git a/tests/scripts/components-configuration-crypto.sh b/tests/scripts/components-configuration-crypto.sh index baa59fb5f5..6a6b0a70de 100644 --- a/tests/scripts/components-configuration-crypto.sh +++ b/tests/scripts/components-configuration-crypto.sh @@ -10,6 +10,7 @@ ################################################################ CMAKE_BUILTIN_BUILD_DIR="tf-psa-crypto/drivers/builtin/CMakeFiles/builtin.dir/src" +CMAKE_EXTRAS_BUILD_DIR="tf-psa-crypto/extras/CMakeFiles/extras.dir" component_test_psa_crypto_key_id_encodes_owner () { msg "build: full config + PSA_CRYPTO_KEY_ID_ENCODES_OWNER, cmake, gcc, ASan" @@ -280,8 +281,13 @@ component_full_no_pkparse_pkwrite () { cmake --build . # Ensure that PK_[PARSE|WRITE]_C were not re-enabled accidentally (additive config). - not grep mbedtls_pk_parse_key ${CMAKE_BUILTIN_BUILD_DIR}/pkparse.c.o - not grep mbedtls_pk_write_key_der ${CMAKE_BUILTIN_BUILD_DIR}/pkwrite.c.o + if [ -f ${TF_PSA_CRYPTO_ROOT_DIR}/extras/pkparse.c ]; then + not grep mbedtls_pk_parse_key ${CMAKE_EXTRAS_BUILD_DIR}/pkparse.c.o + not grep mbedtls_pk_write_key_der ${CMAKE_EXTRAS_BUILD_DIR}/pkwrite.c.o + else + not grep mbedtls_pk_parse_key ${CMAKE_BUILTIN_BUILD_DIR}/pkparse.c.o + not grep mbedtls_pk_write_key_der ${CMAKE_BUILTIN_BUILD_DIR}/pkwrite.c.o + fi msg "test: full without pkparse and pkwrite" ctest @@ -302,7 +308,11 @@ component_full_no_pkwrite () { make # Ensure that PK_WRITE_C was not re-enabled accidentally (additive config). - not grep mbedtls_pk_write_key_der ${CMAKE_BUILTIN_BUILD_DIR}/pkwrite.c.o + if [ -f ${TF_PSA_CRYPTO_ROOT_DIR}/extras/pkwrite.c ]; then + not grep mbedtls_pk_write_key_der ${CMAKE_EXTRAS_BUILD_DIR}/pkwrite.c.o + else + not grep mbedtls_pk_write_key_der ${CMAKE_BUILTIN_BUILD_DIR}/pkwrite.c.o + fi msg "test: full without pkwrite" make test @@ -329,8 +339,13 @@ component_test_crypto_full_md_light_only () { cmake --build . # Make sure we don't have the HMAC functions, but the hashing functions - not grep mbedtls_md_hmac ${CMAKE_BUILTIN_BUILD_DIR}/md.c.o - grep mbedtls_md ${CMAKE_BUILTIN_BUILD_DIR}/md.c.o + if [ -f ${TF_PSA_CRYPTO_ROOT_DIR}/extras/md.c ]; then + not grep mbedtls_md_hmac ${CMAKE_EXTRAS_BUILD_DIR}/md.c.o + grep mbedtls_md ${CMAKE_EXTRAS_BUILD_DIR}/md.c.o + else + not grep mbedtls_md_hmac ${CMAKE_BUILTIN_BUILD_DIR}/md.c.o + grep mbedtls_md ${CMAKE_BUILTIN_BUILD_DIR}/md.c.o + fi msg "test: crypto_full with only the light subset of MD" ctest @@ -1668,7 +1683,11 @@ component_test_psa_crypto_config_accel_hmac () { helper_libtestdriver1_make_main "$loc_accel_list" # Ensure that built-in support for HMAC is disabled. - not grep mbedtls_md_hmac ${BUILTIN_SRC_PATH}/md.o + if [ -f ${TF_PSA_CRYPTO_ROOT_DIR}/extras/md.c ]; then + not grep mbedtls_md_hmac ${TF_PSA_CRYPTO_ROOT_DIR}/extras/md.o + else + not grep mbedtls_md_hmac ${BUILTIN_SRC_PATH}/md.o + fi # Run the tests # ------------- diff --git a/tests/scripts/components-platform.sh b/tests/scripts/components-platform.sh index d6eef6f781..2f3becf983 100644 --- a/tests/scripts/components-platform.sh +++ b/tests/scripts/components-platform.sh @@ -439,6 +439,18 @@ component_build_arm_none_eabi_gcc () { ${ARM_NONE_EABI_GCC_PREFIX}size -t library/*.o ${ARM_NONE_EABI_GCC_PREFIX}size -t ${PSA_CORE_PATH}/*.o ${ARM_NONE_EABI_GCC_PREFIX}size -t ${BUILTIN_SRC_PATH}/*.o + if compgen -G "${TF_PSA_CRYPTO_ROOT_DIR}/dispatch/*.o" > /dev/null; then + ${ARM_NONE_EABI_GCC_PREFIX}size -t ${TF_PSA_CRYPTO_ROOT_DIR}/dispatch/*.o + fi + if compgen -G "${TF_PSA_CRYPTO_ROOT_DIR}/extras/*.o" > /dev/null; then + ${ARM_NONE_EABI_GCC_PREFIX}size -t ${TF_PSA_CRYPTO_ROOT_DIR}/extras/*.o + fi + if compgen -G "${TF_PSA_CRYPTO_ROOT_DIR}/platform/*.o" > /dev/null; then + ${ARM_NONE_EABI_GCC_PREFIX}size -t ${TF_PSA_CRYPTO_ROOT_DIR}/platform/*.o + fi + if compgen -G "${TF_PSA_CRYPTO_ROOT_DIR}/utilities/*.o" > /dev/null; then + ${ARM_NONE_EABI_GCC_PREFIX}size -t ${TF_PSA_CRYPTO_ROOT_DIR}/utilities/*.o + fi } component_build_arm_linux_gnueabi_gcc_arm5vte () { @@ -455,6 +467,18 @@ component_build_arm_linux_gnueabi_gcc_arm5vte () { ${ARM_LINUX_GNUEABI_GCC_PREFIX}size -t library/*.o ${ARM_LINUX_GNUEABI_GCC_PREFIX}size -t ${PSA_CORE_PATH}/*.o ${ARM_LINUX_GNUEABI_GCC_PREFIX}size -t ${BUILTIN_SRC_PATH}/*.o + if compgen -G "${TF_PSA_CRYPTO_ROOT_DIR}/dispatch/*.o" > /dev/null; then + ${ARM_NONE_EABI_GCC_PREFIX}size -t ${TF_PSA_CRYPTO_ROOT_DIR}/dispatch/*.o + fi + if compgen -G "${TF_PSA_CRYPTO_ROOT_DIR}/extras/*.o" > /dev/null; then + ${ARM_NONE_EABI_GCC_PREFIX}size -t ${TF_PSA_CRYPTO_ROOT_DIR}/extras/*.o + fi + if compgen -G "${TF_PSA_CRYPTO_ROOT_DIR}/platform/*.o" > /dev/null; then + ${ARM_NONE_EABI_GCC_PREFIX}size -t ${TF_PSA_CRYPTO_ROOT_DIR}/platform/*.o + fi + if compgen -G "${TF_PSA_CRYPTO_ROOT_DIR}/utilities/*.o" > /dev/null; then + ${ARM_NONE_EABI_GCC_PREFIX}size -t ${TF_PSA_CRYPTO_ROOT_DIR}/utilities/*.o + fi } support_build_arm_linux_gnueabi_gcc_arm5vte () { @@ -473,6 +497,18 @@ component_build_arm_none_eabi_gcc_arm5vte () { ${ARM_NONE_EABI_GCC_PREFIX}size -t library/*.o ${ARM_NONE_EABI_GCC_PREFIX}size -t ${PSA_CORE_PATH}/*.o ${ARM_NONE_EABI_GCC_PREFIX}size -t ${BUILTIN_SRC_PATH}/*.o + if compgen -G "${TF_PSA_CRYPTO_ROOT_DIR}/dispatch/*.o" > /dev/null; then + ${ARM_NONE_EABI_GCC_PREFIX}size -t ${TF_PSA_CRYPTO_ROOT_DIR}/dispatch/*.o + fi + if compgen -G "${TF_PSA_CRYPTO_ROOT_DIR}/extras/*.o" > /dev/null; then + ${ARM_NONE_EABI_GCC_PREFIX}size -t ${TF_PSA_CRYPTO_ROOT_DIR}/extras/*.o + fi + if compgen -G "${TF_PSA_CRYPTO_ROOT_DIR}/platform/*.o" > /dev/null; then + ${ARM_NONE_EABI_GCC_PREFIX}size -t ${TF_PSA_CRYPTO_ROOT_DIR}/platform/*.o + fi + if compgen -G "${TF_PSA_CRYPTO_ROOT_DIR}/utilities/*.o" > /dev/null; then + ${ARM_NONE_EABI_GCC_PREFIX}size -t ${TF_PSA_CRYPTO_ROOT_DIR}/utilities/*.o + fi } component_build_arm_none_eabi_gcc_m0plus () { @@ -484,6 +520,18 @@ component_build_arm_none_eabi_gcc_m0plus () { ${ARM_NONE_EABI_GCC_PREFIX}size -t library/*.o ${ARM_NONE_EABI_GCC_PREFIX}size -t ${PSA_CORE_PATH}/*.o ${ARM_NONE_EABI_GCC_PREFIX}size -t ${BUILTIN_SRC_PATH}/*.o + if compgen -G "${TF_PSA_CRYPTO_ROOT_DIR}/dispatch/*.o" > /dev/null; then + ${ARM_NONE_EABI_GCC_PREFIX}size -t ${TF_PSA_CRYPTO_ROOT_DIR}/dispatch/*.o + fi + if compgen -G "${TF_PSA_CRYPTO_ROOT_DIR}/extras/*.o" > /dev/null; then + ${ARM_NONE_EABI_GCC_PREFIX}size -t ${TF_PSA_CRYPTO_ROOT_DIR}/extras/*.o + fi + if compgen -G "${TF_PSA_CRYPTO_ROOT_DIR}/platform/*.o" > /dev/null; then + ${ARM_NONE_EABI_GCC_PREFIX}size -t ${TF_PSA_CRYPTO_ROOT_DIR}/platform/*.o + fi + if compgen -G "${TF_PSA_CRYPTO_ROOT_DIR}/utilities/*.o" > /dev/null; then + ${ARM_NONE_EABI_GCC_PREFIX}size -t ${TF_PSA_CRYPTO_ROOT_DIR}/utilities/*.o + fi for lib in library/*.a; do echo "$lib:" ${ARM_NONE_EABI_GCC_PREFIX}size -t $lib | grep TOTALS @@ -499,6 +547,18 @@ component_build_arm_none_eabi_gcc_no_udbl_division () { not grep __aeabi_uldiv library/*.o not grep __aeabi_uldiv ${PSA_CORE_PATH}/*.o not grep __aeabi_uldiv ${BUILTIN_SRC_PATH}/*.o + if compgen -G "${TF_PSA_CRYPTO_ROOT_DIR}/dispatch/*.o" > /dev/null; then + not grep __aeabi_uldiv ${TF_PSA_CRYPTO_ROOT_DIR}/dispatch/*.o + fi + if compgen -G "${TF_PSA_CRYPTO_ROOT_DIR}/extras/*.o" > /dev/null; then + not grep __aeabi_uldiv ${TF_PSA_CRYPTO_ROOT_DIR}/extras/*.o + fi + if compgen -G "${TF_PSA_CRYPTO_ROOT_DIR}/platform/*.o" > /dev/null; then + not grep __aeabi_uldiv ${TF_PSA_CRYPTO_ROOT_DIR}/platform/*.o + fi + if compgen -G "${TF_PSA_CRYPTO_ROOT_DIR}/utilities/*.o" > /dev/null; then + not grep __aeabi_uldiv ${TF_PSA_CRYPTO_ROOT_DIR}/utilities/*.o + fi } component_build_arm_none_eabi_gcc_no_64bit_multiplication () { @@ -510,6 +570,18 @@ component_build_arm_none_eabi_gcc_no_64bit_multiplication () { not grep __aeabi_lmul library/*.o not grep __aeabi_lmul ${PSA_CORE_PATH}/*.o not grep __aeabi_lmul ${BUILTIN_SRC_PATH}/*.o + if compgen -G "${TF_PSA_CRYPTO_ROOT_DIR}/dispatch/*.o" > /dev/null; then + not grep __aeabi_lmul ${TF_PSA_CRYPTO_ROOT_DIR}/dispatch/*.o + fi + if compgen -G "${TF_PSA_CRYPTO_ROOT_DIR}/extras/*.o" > /dev/null; then + not grep __aeabi_lmul ${TF_PSA_CRYPTO_ROOT_DIR}/extras/*.o + fi + if compgen -G "${TF_PSA_CRYPTO_ROOT_DIR}/platform/*.o" > /dev/null; then + not grep __aeabi_lmul ${TF_PSA_CRYPTO_ROOT_DIR}/platform/*.o + fi + if compgen -G "${TF_PSA_CRYPTO_ROOT_DIR}/utilities/*.o" > /dev/null; then + not grep __aeabi_lmul ${TF_PSA_CRYPTO_ROOT_DIR}/utilities/*.o + fi } component_build_arm_clang_thumb () { From 325170b96297eb21c17a523ac913cc220d3ee926 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Sat, 14 Feb 2026 10:49:40 +0100 Subject: [PATCH 24/34] legacy.make: Add globs and paths for tags and cscope Signed-off-by: Ronald Cron --- scripts/legacy.make | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/scripts/legacy.make b/scripts/legacy.make index b22b8ef8bf..5726f9e82a 100644 --- a/scripts/legacy.make +++ b/scripts/legacy.make @@ -180,6 +180,10 @@ C_SOURCE_FILES = $(wildcard \ tf-psa-crypto/drivers/*/*/*.c \ tf-psa-crypto/drivers/*/*/*/*.c \ tf-psa-crypto/drivers/*/*/*/*/*.c \ + tf-psa-crypto/dispatch/*.[hc] \ + tf-psa-crypto/extras/*.[hc] \ + tf-psa-crypto/platform/*.[hc] \ + tf-psa-crypto/utilities/*.[hc] \ programs/*/*.[hc] \ framework/tests/include/*/*.h framework/tests/include/*/*/*.h \ framework/tests/src/*.c framework/tests/src/*/*.c \ @@ -200,5 +204,9 @@ cscope.in.out cscope.po.out cscope.out: $(C_SOURCE_FILES) cscope -bq -u -Iinclude -Ilibrary -Itf-psa-crypto/core \ -Itf-psa-crypto/include \ -Itf-psa-crypto/drivers/builtin/src \ + -Itf-psa-crypto/dispatch \ + -Itf-psa-crypto/extras \ + -Itf-psa-crypto/platform \ + -Itf-psa-crypto/utilities \ $(patsubst %,-I%,$(wildcard tf-psa-crypto/drivers/*/include)) -Iframework/tests/include $(C_SOURCE_FILES) .PHONY: cscope global From f3a20d25d618ba21a969f205198c6147411ef51d Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Sat, 14 Feb 2026 10:51:38 +0100 Subject: [PATCH 25/34] Prepare libtestdriver1 build for upcoming directory changes Signed-off-by: Ronald Cron --- tests/Makefile | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/tests/Makefile b/tests/Makefile index 745a09d240..a34bc95f99 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -252,7 +252,19 @@ libtestdriver1.a: cp -Rf ../scripts ./libtestdriver1 cp -Rf ../tf-psa-crypto/core ./libtestdriver1/tf-psa-crypto cp -Rf ../tf-psa-crypto/include ./libtestdriver1/tf-psa-crypto - cp -Rf ../tf-psa-crypto/drivers/builtin ./libtestdriver1/tf-psa-crypto/drivers + cp -Rf ../tf-psa-crypto/drivers/builtin ./libtestdriver1/tf-psa-crypto/drivers/builtin + if [ -d ../tf-psa-crypto/dispatch ]; then \ + cp -Rf ../tf-psa-crypto/dispatch ./libtestdriver1/tf-psa-crypto/dispatch; \ + fi + if [ -d ../tf-psa-crypto/extras ]; then \ + cp -Rf ../tf-psa-crypto/extras ./libtestdriver1/tf-psa-crypto/extras; \ + fi + if [ -d ../tf-psa-crypto/platform ]; then \ + cp -Rf ../tf-psa-crypto/platform ./libtestdriver1/tf-psa-crypto/platform; \ + fi + if [ -d ../tf-psa-crypto/utilities ]; then \ + cp -Rf ../tf-psa-crypto/utilities ./libtestdriver1/tf-psa-crypto/utilities; \ + fi cp -Rf ../tf-psa-crypto/scripts ./libtestdriver1/tf-psa-crypto # Set the test driver base (minimal) configuration. @@ -285,7 +297,18 @@ libtestdriver1.a: perl -i ./scripts/libtestdriver1_rewrite.pl ./libtestdriver1/tf-psa-crypto/drivers/builtin/include/*/*.h perl -i ./scripts/libtestdriver1_rewrite.pl ./libtestdriver1/tf-psa-crypto/drivers/builtin/include/*/*/*.h perl -i ./scripts/libtestdriver1_rewrite.pl ./libtestdriver1/tf-psa-crypto/drivers/builtin/src/*.[ch] - + if [ -d ../tf-psa-crypto/dispatch ]; then \ + perl -i ./scripts/libtestdriver1_rewrite.pl ./libtestdriver1/tf-psa-crypto/dispatch/*.[ch]; \ + fi + if [ -d ../tf-psa-crypto/extras ]; then \ + perl -i ./scripts/libtestdriver1_rewrite.pl ./libtestdriver1/tf-psa-crypto/extras/*.[ch]; \ + fi + if [ -d ../tf-psa-crypto/platform ]; then \ + perl -i ./scripts/libtestdriver1_rewrite.pl ./libtestdriver1/tf-psa-crypto/platform/*.[ch]; \ + fi + if [ -d ../tf-psa-crypto/utilities ]; then \ + perl -i ./scripts/libtestdriver1_rewrite.pl ./libtestdriver1/tf-psa-crypto/utilities/*.[ch]; \ + fi $(MAKE) -C ./libtestdriver1/library CFLAGS="-I../../ $(CFLAGS)" LDFLAGS="$(LDFLAGS)" libmbedcrypto.a cp ./libtestdriver1/library/libmbedcrypto.a ../library/libtestdriver1.a From a400a3bb8aecc2f88a7dcd814d17f719777184e7 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Sat, 14 Feb 2026 10:53:14 +0100 Subject: [PATCH 26/34] Adapt list_internal_identifiers.py for upcoming directory changes Signed-off-by: Ronald Cron --- tests/scripts/list_internal_identifiers.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/scripts/list_internal_identifiers.py b/tests/scripts/list_internal_identifiers.py index 2cbfdce8d5..445aeda352 100755 --- a/tests/scripts/list_internal_identifiers.py +++ b/tests/scripts/list_internal_identifiers.py @@ -37,7 +37,9 @@ def main(): "include/mbedtls/*_internal.h", "library/*.h", "tf-psa-crypto/core/*.h", - "tf-psa-crypto/drivers/builtin/src/*.h" + "tf-psa-crypto/drivers/builtin/src/*.h", + "tf-psa-crypto/platform/*.h", + "tf-psa-crypto/utilities/*.h", ])[0] result.sort(key=lambda x: x.name) From 32479c6a725762fcba58e0e2b59dc471c716060e Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Sat, 14 Feb 2026 10:53:23 +0100 Subject: [PATCH 27/34] cmake: Introduce TF_PSA_CRYPTO_PRIVATE_INCLUDE_DIRS Signed-off-by: Ronald Cron --- CMakeLists.txt | 53 ++++++++++++++++++++++++++---------------- library/CMakeLists.txt | 5 ++-- tests/CMakeLists.txt | 10 ++++---- 3 files changed, 40 insertions(+), 28 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bc122f5167..42e4ccb34d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -373,6 +373,20 @@ foreach(target IN LISTS tf_psa_crypto_library_targets) endif() endforeach(target) +# +# TF-PSA-Crypto directories with internal headers that are currently still +# consumed by Mbed TLS. When we have finished cleaning up this list should be +# empty. +# +set(TF_PSA_CRYPTO_PRIVATE_INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/tf-psa-crypto/core + ${CMAKE_CURRENT_SOURCE_DIR}/tf-psa-crypto/dispatch + ${CMAKE_CURRENT_SOURCE_DIR}/tf-psa-crypto/drivers/builtin/src + ${CMAKE_CURRENT_SOURCE_DIR}/tf-psa-crypto/extras + ${CMAKE_CURRENT_SOURCE_DIR}/tf-psa-crypto/platform + ${CMAKE_CURRENT_SOURCE_DIR}/tf-psa-crypto/utilities +) + add_subdirectory(library) add_subdirectory(pkgconfig) @@ -416,16 +430,16 @@ if(ENABLE_TESTING OR ENABLE_PROGRAMS) endif() target_include_directories(mbedtls_test PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/tests/include - PRIVATE ${MBEDTLS_FRAMEWORK_DIR}/tests/include - PRIVATE tests/include - PRIVATE include - PRIVATE tf-psa-crypto/include - PRIVATE tf-psa-crypto/drivers/builtin/include - PRIVATE tf-psa-crypto/drivers/everest/include - PRIVATE tf-psa-crypto/drivers/pqcp/include - PRIVATE library - PRIVATE tf-psa-crypto/core - PRIVATE tf-psa-crypto/drivers/builtin/src) + ${MBEDTLS_FRAMEWORK_DIR}/tests/include + tests/include + include + tf-psa-crypto/include + tf-psa-crypto/drivers/builtin/include + tf-psa-crypto/drivers/everest/include + tf-psa-crypto/drivers/pqcp/include + library + ${TF_PSA_CRYPTO_PRIVATE_INCLUDE_DIRS} + ) # Request C11, needed for memory poisoning tests set_target_properties(mbedtls_test PROPERTIES C_STANDARD 11) set_config_files_compile_definitions(mbedtls_test) @@ -453,16 +467,15 @@ if(ENABLE_TESTING OR ENABLE_PROGRAMS) endif() target_include_directories(mbedtls_test_helpers PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/tests/include - PRIVATE ${MBEDTLS_FRAMEWORK_DIR}/tests/include - PRIVATE tests/include - PRIVATE include - PRIVATE tf-psa-crypto/include - PRIVATE tf-psa-crypto/drivers/builtin/include - PRIVATE library - PRIVATE tf-psa-crypto/core - PRIVATE tf-psa-crypto/drivers/builtin/src - PRIVATE tf-psa-crypto/drivers/everest/include - PRIVATE tf-psa-crypto/drivers/pqcp/include + ${MBEDTLS_FRAMEWORK_DIR}/tests/include + tests/include + include + tf-psa-crypto/include + tf-psa-crypto/drivers/builtin/include + library + tf-psa-crypto/drivers/everest/include + tf-psa-crypto/drivers/pqcp/include + ${TF_PSA_CRYPTO_PRIVATE_INCLUDE_DIRS} ) set_config_files_compile_definitions(mbedtls_test_helpers) diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 5474e2cacf..f1b7c74aba 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -236,15 +236,14 @@ foreach(target IN LISTS target_libraries) add_library(MbedTLS::${target} ALIAS ${target}) # add_subdirectory support # Include public header files from /include, /tf-psa-crypto/include/ and # tf-psa-crypto/drivers/builtin/include/. Include private header files - # from /library, tf-psa-crypto/core/ and tf-psa-crypto/drivers/builtin/src/. + # from /library and ${TF_PSA_CRYPTO_PRIVATE_INCLUDE_DIRS}. target_include_directories(${target} PUBLIC $ $ $ $ PRIVATE ${MBEDTLS_DIR}/library/ - ${MBEDTLS_DIR}/tf-psa-crypto/core - ${MBEDTLS_DIR}/tf-psa-crypto/drivers/builtin/src + ${TF_PSA_CRYPTO_PRIVATE_INCLUDE_DIRS} # needed for generated headers ${CMAKE_CURRENT_BINARY_DIR}) set_config_files_compile_definitions(${target}) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ec625234dc..09913b56e7 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -177,11 +177,11 @@ function(add_test_suite suite_name) # them as PUBLIC. target_include_directories(test_suite_${data_name} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/include - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../framework/tests/include - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../library - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../tf-psa-crypto/core - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../tf-psa-crypto/drivers/builtin/src) + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${PROJECT_SOURCE_DIR}/framework/tests/include + ${PROJECT_SOURCE_DIR}/library + ${TF_PSA_CRYPTO_PRIVATE_INCLUDE_DIRS} + ) # Request C11, which is needed for memory poisoning tests set_target_properties(test_suite_${data_name} PROPERTIES C_STANDARD 11) From aa024253d6b3713bfba9ea1d163b4f8b6407b595 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Fri, 27 Feb 2026 17:30:50 +0100 Subject: [PATCH 28/34] tests: make: Fix spaces instead of tab Signed-off-by: Ronald Cron --- tests/Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/Makefile b/tests/Makefile index a34bc95f99..62a7b82e61 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -255,16 +255,16 @@ libtestdriver1.a: cp -Rf ../tf-psa-crypto/drivers/builtin ./libtestdriver1/tf-psa-crypto/drivers/builtin if [ -d ../tf-psa-crypto/dispatch ]; then \ cp -Rf ../tf-psa-crypto/dispatch ./libtestdriver1/tf-psa-crypto/dispatch; \ - fi + fi if [ -d ../tf-psa-crypto/extras ]; then \ cp -Rf ../tf-psa-crypto/extras ./libtestdriver1/tf-psa-crypto/extras; \ - fi + fi if [ -d ../tf-psa-crypto/platform ]; then \ cp -Rf ../tf-psa-crypto/platform ./libtestdriver1/tf-psa-crypto/platform; \ - fi + fi if [ -d ../tf-psa-crypto/utilities ]; then \ cp -Rf ../tf-psa-crypto/utilities ./libtestdriver1/tf-psa-crypto/utilities; \ - fi + fi cp -Rf ../tf-psa-crypto/scripts ./libtestdriver1/tf-psa-crypto # Set the test driver base (minimal) configuration. From 269b390bb4cb5303188af9b862780412933d670b Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Mon, 9 Mar 2026 08:22:51 +0100 Subject: [PATCH 29/34] components-platform.sh: Fix path of compiler directory Signed-off-by: Ronald Cron --- tests/scripts/components-platform.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/scripts/components-platform.sh b/tests/scripts/components-platform.sh index 2f3becf983..b68a4aeafc 100644 --- a/tests/scripts/components-platform.sh +++ b/tests/scripts/components-platform.sh @@ -468,16 +468,16 @@ component_build_arm_linux_gnueabi_gcc_arm5vte () { ${ARM_LINUX_GNUEABI_GCC_PREFIX}size -t ${PSA_CORE_PATH}/*.o ${ARM_LINUX_GNUEABI_GCC_PREFIX}size -t ${BUILTIN_SRC_PATH}/*.o if compgen -G "${TF_PSA_CRYPTO_ROOT_DIR}/dispatch/*.o" > /dev/null; then - ${ARM_NONE_EABI_GCC_PREFIX}size -t ${TF_PSA_CRYPTO_ROOT_DIR}/dispatch/*.o + ${ARM_LINUX_GNUEABI_GCC_PREFIX}size -t ${TF_PSA_CRYPTO_ROOT_DIR}/dispatch/*.o fi if compgen -G "${TF_PSA_CRYPTO_ROOT_DIR}/extras/*.o" > /dev/null; then - ${ARM_NONE_EABI_GCC_PREFIX}size -t ${TF_PSA_CRYPTO_ROOT_DIR}/extras/*.o + ${ARM_LINUX_GNUEABI_GCC_PREFIX}size -t ${TF_PSA_CRYPTO_ROOT_DIR}/extras/*.o fi if compgen -G "${TF_PSA_CRYPTO_ROOT_DIR}/platform/*.o" > /dev/null; then - ${ARM_NONE_EABI_GCC_PREFIX}size -t ${TF_PSA_CRYPTO_ROOT_DIR}/platform/*.o + ${ARM_LINUX_GNUEABI_GCC_PREFIX}size -t ${TF_PSA_CRYPTO_ROOT_DIR}/platform/*.o fi if compgen -G "${TF_PSA_CRYPTO_ROOT_DIR}/utilities/*.o" > /dev/null; then - ${ARM_NONE_EABI_GCC_PREFIX}size -t ${TF_PSA_CRYPTO_ROOT_DIR}/utilities/*.o + ${ARM_LINUX_GNUEABI_GCC_PREFIX}size -t ${TF_PSA_CRYPTO_ROOT_DIR}/utilities/*.o fi } From bef136e607ee0a436ea9d0b56bf37d6401c81507 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Mon, 9 Mar 2026 08:23:53 +0100 Subject: [PATCH 30/34] Update framework pointer to the merge of PR 280 Signed-off-by: Ronald Cron --- framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework b/framework index e07b6643e8..9b92164c47 160000 --- a/framework +++ b/framework @@ -1 +1 @@ -Subproject commit e07b6643e8db5fe2fdc20be288b91a2194316862 +Subproject commit 9b92164c47fdaecb2600b417733507e2a105c3a5 From 65a038198ed4ab94e9201e3dff624df7a0c6fb57 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Mon, 9 Mar 2026 23:28:50 +0100 Subject: [PATCH 31/34] Improve comments Signed-off-by: Ronald Cron --- library/ssl_msg.c | 4 ++-- library/ssl_tls12_server.c | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/library/ssl_msg.c b/library/ssl_msg.c index 207f4b7e1c..f6bef7cbc2 100644 --- a/library/ssl_msg.c +++ b/library/ssl_msg.c @@ -2970,8 +2970,8 @@ int mbedtls_ssl_prepare_handshake_record(mbedtls_ssl_context *ssl) /* * When establishing the connection, the client may go through a series * of ClientHello and HelloVerifyRequest requests and responses. The - * server does not keep any trace of these initial round trips as - * intended: minimum allocated ressources as long as the reachability + * server intentionally does not keep trace of these initial round + * trips: minimum allocated ressources as long as the reachability * of the client has not been confirmed. When receiving the "first * ClientHello" from server perspective, we may thus need to adapt * the next expected `message_seq` for the incoming and outgoing diff --git a/library/ssl_tls12_server.c b/library/ssl_tls12_server.c index 54fb8669ae..5c6832e044 100644 --- a/library/ssl_tls12_server.c +++ b/library/ssl_tls12_server.c @@ -871,10 +871,11 @@ static int ssl_parse_client_hello(mbedtls_ssl_context *ssl) /* * Fetch the expected ClientHello handshake message. Do not ask - * mbedtls_ssl_read_record() to update the handshake digest, to align - * with cases where the ClientHello may already have been fetched in - * ssl_tls13_process_client_hello() or as a post-handshake message - * (renegotiation). + * mbedtls_ssl_read_record() to update the handshake digest, because the + * ClientHello may already have been read in ssl_tls13_process_client_hello() + * or as a post-handshake message (renegotiation). In those cases we need + * to update the digest ourselves, and it is simpler to do so + * unconditionally than to track whether it is needed. */ if ((ret = mbedtls_ssl_read_record(ssl, 0)) != 0) { MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_read_record ", ret); From 7f40da187cbedb89c7027289db720825aaab9b08 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Tue, 10 Mar 2026 08:25:14 +0100 Subject: [PATCH 32/34] ssl_tls12_server.c: Move back the digest update Move back the digest update just after the call to mbedtls_ssl_read_record(). It fits well here as we explain in the comment associated to the call to mbedtls_ssl_read_record() that we update it manually. Signed-off-by: Ronald Cron --- library/ssl_tls12_server.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/library/ssl_tls12_server.c b/library/ssl_tls12_server.c index 5c6832e044..94e61a8aca 100644 --- a/library/ssl_tls12_server.c +++ b/library/ssl_tls12_server.c @@ -882,6 +882,20 @@ static int ssl_parse_client_hello(mbedtls_ssl_context *ssl) return ret; } + /* + * Update the handshake checksum. + * + * Note that the checksum must be updated before parsing the extensions + * because ssl_parse_session_ticket_ext() may decrypt the ticket in place + * and therefore modify the ClientHello message. This occurs when using + * the Mbed TLS ssl_ticket.c implementation. + */ + ret = mbedtls_ssl_update_handshake_status(ssl); + if (0 != ret) { + MBEDTLS_SSL_DEBUG_RET(1, ("mbedtls_ssl_update_handshake_status"), ret); + return ret; + } + buf = ssl->in_msg; msg_len = ssl->in_hslen; @@ -1087,21 +1101,6 @@ static int ssl_parse_client_hello(mbedtls_ssl_context *ssl) ext_len = 0; } - /* - * Update the handshake checksum after performing preliminary - * validation of the ClientHello and before parsing its extensions. - * - * The checksum must be updated before parsing the extensions because - * ssl_parse_session_ticket_ext() may decrypt the ticket in place and - * therefore modify the ClientHello message. This occurs when using - * the Mbed TLS ssl_ticket.c implementation. - */ - ret = mbedtls_ssl_update_handshake_status(ssl); - if (0 != ret) { - MBEDTLS_SSL_DEBUG_RET(1, ("mbedtls_ssl_update_handshake_status"), ret); - return ret; - } - ext = buf + ext_offset + 2; MBEDTLS_SSL_DEBUG_BUF(3, "client hello extensions", ext, ext_len); From 09210ea54ff627d22c0efade7e1ba6c16225a959 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Tue, 10 Mar 2026 08:49:25 +0100 Subject: [PATCH 33/34] Restore seq number check of post-handshake ClientHello msg The check was wrongly removed by the commit "ssl_tls12_server.c: Move ClientHello message_seq adjustment". Signed-off-by: Ronald Cron --- library/ssl_msg.c | 59 +++++++++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/library/ssl_msg.c b/library/ssl_msg.c index f6bef7cbc2..abb5a5696f 100644 --- a/library/ssl_msg.c +++ b/library/ssl_msg.c @@ -2967,34 +2967,47 @@ int mbedtls_ssl_prepare_handshake_record(mbedtls_ssl_context *ssl) return MBEDTLS_ERR_SSL_INVALID_RECORD; } - /* - * When establishing the connection, the client may go through a series - * of ClientHello and HelloVerifyRequest requests and responses. The - * server intentionally does not keep trace of these initial round - * trips: minimum allocated ressources as long as the reachability - * of the client has not been confirmed. When receiving the "first - * ClientHello" from server perspective, we may thus need to adapt - * the next expected `message_seq` for the incoming and outgoing - * handshake messages. - */ if (ssl->in_msg[0] == MBEDTLS_SSL_HS_CLIENT_HELLO && - ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER && - ssl->state == MBEDTLS_SSL_CLIENT_HELLO + ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER) { + if (ssl->state == MBEDTLS_SSL_CLIENT_HELLO #if defined(MBEDTLS_SSL_RENEGOTIATION) - && ssl->renego_status == MBEDTLS_SSL_INITIAL_HANDSHAKE + && ssl->renego_status == MBEDTLS_SSL_INITIAL_HANDSHAKE #endif - ) { - ssl->handshake->in_msg_seq = recv_msg_seq; - ssl->handshake->out_msg_seq = recv_msg_seq; + ) { + /* + * When establishing the connection, the client may go through + * a series of ClientHello and HelloVerifyRequest requests and + * responses. The server intentionally does not keep trace of + * these initial round trips: minimum allocated ressources as + * long as the reachability of the client has not been + * confirmed. When receiving the "first ClientHello" from + * server perspective, we may thus need to adapt the next + * expected `message_seq` for the incoming and outgoing + * handshake messages. + */ + ssl->handshake->in_msg_seq = recv_msg_seq; + ssl->handshake->out_msg_seq = recv_msg_seq; - /* Epoch should be 0 for initial handshakes */ - if (ssl->in_ctr[0] != 0 || ssl->in_ctr[1] != 0) { - MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); - return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + /* Epoch should be 0 for initial handshakes */ + if (ssl->in_ctr[0] != 0 || ssl->in_ctr[1] != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + + memcpy(&ssl->cur_out_ctr[2], ssl->in_ctr + 2, + sizeof(ssl->cur_out_ctr) - 2); + } else if (mbedtls_ssl_is_handshake_over(ssl) == 1) { + /* In case of a post-handshake ClientHello that initiates a + * renegotiation check that the handshake message sequence + * number is zero. + */ + if (recv_msg_seq != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message_seq: " + "%u (expected 0)", + recv_msg_seq)); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } } - - memcpy(&ssl->cur_out_ctr[2], ssl->in_ctr + 2, - sizeof(ssl->cur_out_ctr) - 2); } if (ssl->handshake != NULL && From f51e72ab2da9bbdb619ce6e0f8a2958d0e7f220b Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Wed, 11 Mar 2026 12:35:30 +0100 Subject: [PATCH 34/34] Update tf-psa-crypto to development Signed-off-by: Gilles Peskine --- tf-psa-crypto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tf-psa-crypto b/tf-psa-crypto index 293cfe5ece..125474d4e0 160000 --- a/tf-psa-crypto +++ b/tf-psa-crypto @@ -1 +1 @@ -Subproject commit 293cfe5eceed98a2ee75d5241a78657b466750c7 +Subproject commit 125474d4e05965a6dfe2af350b5462ce62bed4cd