From 11e4f5ac1c71fe7d803fa5193236560b2e176cea Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Sat, 1 Mar 2025 16:09:27 +0100 Subject: [PATCH] New script to generate handshake tests for ssl-opt.sh Signed-off-by: Gilles Peskine --- scripts/generate_tls_handshake_tests.py | 42 ++++++++++++ scripts/mbedtls_framework/tls_test_case.py | 74 ++++++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100755 scripts/generate_tls_handshake_tests.py create mode 100644 scripts/mbedtls_framework/tls_test_case.py diff --git a/scripts/generate_tls_handshake_tests.py b/scripts/generate_tls_handshake_tests.py new file mode 100755 index 000000000..78c7a42c3 --- /dev/null +++ b/scripts/generate_tls_handshake_tests.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 + +""" +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 argparse +import sys +from typing import Optional + +from mbedtls_framework import tls_test_case +from mbedtls_framework import typing_util +def write_handshake_tests(out: typing_util.Writable) -> None: + """Generate handshake tests.""" + out.write(f"""\ +# Miscellaneous tests related to the TLS handshake layer. +# +# Automatically generated by {sys.argv[0]}. Do not edit! + +# Copyright The Mbed TLS Contributors +# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later + +""") + out.write("""\ +# End of automatically generated file. +""") + +def main() -> None: + """Command line entry point.""" + parser = argparse.ArgumentParser() + parser.add_argument('-o', '--output', + default='tests/opt-testcases/handshake-generated.sh', + help='Output file') + args = parser.parse_args() + with open(args.output, 'w') as out: + write_handshake_tests(out) + +if __name__ == '__main__': + main() diff --git a/scripts/mbedtls_framework/tls_test_case.py b/scripts/mbedtls_framework/tls_test_case.py new file mode 100644 index 000000000..214a7ed46 --- /dev/null +++ b/scripts/mbedtls_framework/tls_test_case.py @@ -0,0 +1,74 @@ +"""Library for constructing an Mbed TLS ssl-opt test case. +""" + +# Copyright The Mbed TLS Contributors +# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later + +import enum +import re +from typing import List + +from . import typing_util + + +class TestCase: + """Data about an ssl-opt test case.""" + #pylint: disable=too-few-public-methods + + def __init__(self, description: str) -> None: + # List of shell snippets to call before run_test, typically + # calls to requires_xxx functions. + self.requirements = [] #type: List[str] + # Test case description (first argument to run_test). + self.description = description + # Client command line. + # This will be placed directly inside double quotes in the shell script. + self.client = '$P_CLI' + # Server command line. + # This will be placed directly inside double quotes in the shell script. + self.server = '$P_SRV' + # Expected client exit code. + self.exit_code = 0 + # BRE for text that must be present in the client log (run_test -c). + self.wanted_client_patterns = [] #type: List[str] + # BRE for text that must be present in the server log (run_test -s). + self.wanted_server_patterns = [] #type: List[str] + # BRE for text that must not be present in the client log (run_test -C). + self.forbidden_client_patterns = [] #type: List[str] + # BRE for text that must not be present in the server log (run_test -S). + self.forbidden_server_patterns = [] #type: List[str] + + @staticmethod + def _quote(raw: str) -> str: + """Quote the given string for sh. + + Use double quotes, because that's currently the norm in ssl-opt.sh. + """ + return '"' + re.sub(r'([$"\\`])', r'\\\1', raw) + '"' + + def write(self, out: typing_util.Writable) -> None: + """Write the test case to the specified file.""" + for req in self.requirements: + out.write(req + '\n') + out.write(f'run_test {self._quote(self.description)} \\\n') + out.write(f' "{self.server}" \\\n') + out.write(f' "{self.client}" \\\n') + out.write(f' {self.exit_code}') + for pat in self.wanted_server_patterns: + out.write(' \\\n -s ' + self._quote(pat)) + for pat in self.forbidden_server_patterns: + out.write(' \\\n -S ' + self._quote(pat)) + for pat in self.wanted_client_patterns: + out.write(' \\\n -c ' + self._quote(pat)) + for pat in self.forbidden_client_patterns: + out.write(' \\\n -C ' + self._quote(pat)) + out.write('\n\n') + + +class Side(enum.Enum): + CLIENT = 0 + SERVER = 1 + +class Version(enum.Enum): + TLS12 = 2 + TLS13 = 3