From 5639bf9a6983911b5bc11b838354b7a9dbf3e5da Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Tue, 20 Jan 2026 10:15:45 +0100 Subject: [PATCH 1/4] Make generate_tls_handshake_tests.py a module Signed-off-by: Ronald Cron --- scripts/generate_tls_handshake_tests.py | 213 +---------------- .../mbedtls_framework/tls_handshake_tests.py | 214 ++++++++++++++++++ 2 files changed, 218 insertions(+), 209 deletions(-) create mode 100644 scripts/mbedtls_framework/tls_handshake_tests.py diff --git a/scripts/generate_tls_handshake_tests.py b/scripts/generate_tls_handshake_tests.py index 1e9dbb944..966098ec6 100755 --- a/scripts/generate_tls_handshake_tests.py +++ b/scripts/generate_tls_handshake_tests.py @@ -1,219 +1,14 @@ #!/usr/bin/env python3 - """ Generate miscellaneous TLS test cases relating to the handshake. + +Transitional wrapper to facilitate the migration of consuming branches. """ # Copyright The Mbed TLS Contributors # SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later -import argparse -import os -import sys -from typing import Optional - -from mbedtls_framework import tls_test_case -from mbedtls_framework import typing_util -from mbedtls_framework.tls_test_case import Side, Version -import translate_ciphers - - -# Assume that a TLS 1.2 ClientHello used in these tests will be at most -# this many bytes long. -TLS12_CLIENT_HELLO_ASSUMED_MAX_LENGTH = 255 - -# Minimum handshake fragment length that Mbed TLS supports. -TLS_HANDSHAKE_FRAGMENT_MIN_LENGTH = 4 - -def write_tls_handshake_defragmentation_test( - #pylint: disable=too-many-arguments - out: typing_util.Writable, - side: Side, - length: Optional[int], - version: Optional[Version] = None, - cipher: Optional[str] = None, - etm: Optional[bool] = None, #encrypt-then-mac (only relevant for CBC) - variant: str = '' -) -> None: - """Generate one TLS handshake defragmentation test. - - :param out: file to write to. - :param side: which side is Mbed TLS. - :param length: fragment length, or None to not fragment. - :param version: protocol version, if forced. - """ - #pylint: disable=chained-comparison,too-many-branches,too-many-statements - - our_args = '' - their_args = '' - - if length is None: - description = 'no fragmentation, for reference' - else: - description = 'len=' + str(length) - if version is not None: - description += ', TLS 1.' + str(version.value) - description = f'Handshake defragmentation on {side.name.lower()}: {description}' - tc = tls_test_case.TestCase(description) - - if version is not None: - their_args += ' ' + version.openssl_option() - # Emit a version requirement, because we're forcing the version via - # OpenSSL, not via Mbed TLS, and the automatic depdendencies in - # ssl-opt.sh only handle forcing the version via Mbed TLS. - tc.requirements.append(version.requires_command()) - if side == Side.SERVER and version == Version.TLS12 and \ - length is not None and \ - length <= TLS12_CLIENT_HELLO_ASSUMED_MAX_LENGTH: - # Server-side ClientHello defragmentation is only supported in - # the TLS 1.3 message parser. When that parser sees an 1.2-only - # ClientHello, it forwards the reassembled record to the - # TLS 1.2 ClientHello parser so the ClientHello can be fragmented. - # When TLS 1.3 support is disabled in the server (at compile-time - # or at runtime), the TLS 1.2 ClientHello parser only sees - # the first fragment of the ClientHello. - tc.requirements.append('requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_3') - tc.description += ' with 1.3 support' - - # To guarantee that the handhake messages are large enough and need to be - # split into fragments, the tests require certificate authentication. - # The party in control of the fragmentation operations is OpenSSL and - # will always use server5.crt (548 Bytes). - if length is not None and \ - length >= TLS_HANDSHAKE_FRAGMENT_MIN_LENGTH: - tc.requirements.append('requires_certificate_authentication') - if version == Version.TLS12 and side == Side.CLIENT: - #The server uses an ECDSA cert, so make sure we have a compatible key exchange - tc.requirements.append( - 'requires_config_enabled MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED') - else: - # This test case may run in a pure-PSK configuration. OpenSSL doesn't - # allow this by default with TLS 1.3. - their_args += ' -allow_no_dhe_kex' - - if length is None: - forbidden_patterns = [ - 'waiting for more fragments', - ] - wanted_patterns = [] - elif length < TLS_HANDSHAKE_FRAGMENT_MIN_LENGTH: - their_args += ' -split_send_frag ' + str(length) - tc.exit_code = 1 - forbidden_patterns = [] - wanted_patterns = [ - 'handshake message too short: ' + str(length), - 'SSL - An invalid SSL record was received', - ] - if side == Side.SERVER: - wanted_patterns[0:0] = ['<= parse client hello'] - elif version == Version.TLS13: - wanted_patterns[0:0] = ['=> ssl_tls13_process_server_hello'] - else: - their_args += ' -split_send_frag ' + str(length) - forbidden_patterns = [] - wanted_patterns = [ - 'reassembled record', - fr'initial handshake fragment: {length}, 0\.\.{length} of [0-9]\+', - fr'subsequent handshake fragment: [0-9]\+, {length}\.\.', - fr'Prepare: waiting for more handshake fragments {length}/', - fr'Consume: waiting for more handshake fragments {length}/', - ] - - if cipher is not None: - mbedtls_cipher = translate_ciphers.translate_mbedtls(cipher) - if side == Side.CLIENT: - our_args += ' force_ciphersuite=' + mbedtls_cipher - if 'NULL' in cipher: - their_args += ' -cipher ALL@SECLEVEL=0:COMPLEMENTOFALL@SECLEVEL=0' - else: - # For TLS 1.2, when Mbed TLS is the server, we must force the - # cipher suite on the client side, because passing - # force_ciphersuite to ssl_server2 would force a TLS-1.2-only - # server, which does not support a fragmented ClientHello. - tc.requirements.append('requires_ciphersuite_enabled ' + mbedtls_cipher) - their_args += ' -cipher ' + translate_ciphers.translate_ossl(cipher) - if 'NULL' in cipher: - their_args += '@SECLEVEL=0' - - if etm is not None: - if etm: - tc.requirements.append('requires_config_enabled MBEDTLS_SSL_ENCRYPT_THEN_MAC') - our_args += ' etm=' + str(int(etm)) - (wanted_patterns if etm else forbidden_patterns)[0:0] = [ - 'using encrypt then mac', - ] - - tc.description += variant - - if side == Side.CLIENT: - tc.client = '$P_CLI debug_level=4' + our_args - tc.server = '$O_NEXT_SRV' + their_args - tc.wanted_client_patterns = wanted_patterns - tc.forbidden_client_patterns = forbidden_patterns - else: - their_args += ' -cert $DATA_FILES_PATH/server5.crt -key $DATA_FILES_PATH/server5.key' - our_args += ' auth_mode=required' - tc.client = '$O_NEXT_CLI' + their_args - tc.server = '$P_SRV debug_level=4' + our_args - tc.wanted_server_patterns = wanted_patterns - tc.forbidden_server_patterns = forbidden_patterns - tc.write(out) - - -CIPHERS_FOR_TLS12_HANDSHAKE_DEFRAGMENTATION = [ - (None, 'default', None), - ('TLS_ECDHE_ECDSA_WITH_NULL_SHA', 'null', None), - ('TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256', 'ChachaPoly', None), - ('TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256', 'GCM', None), - ('TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256', 'CBC, etm=n', False), - ('TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256', 'CBC, etm=y', True), -] - -def write_tls_handshake_defragmentation_tests(out: typing_util.Writable) -> None: - """Generate TLS handshake defragmentation tests.""" - for side in Side.CLIENT, Side.SERVER: - write_tls_handshake_defragmentation_test(out, side, None) - for length in [512, 513, 256, 128, 64, 36, 32, 16, 13, 5, 4, 3]: - write_tls_handshake_defragmentation_test(out, side, length, - Version.TLS13) - if length == 4: - for (cipher_suite, nickname, etm) in \ - CIPHERS_FOR_TLS12_HANDSHAKE_DEFRAGMENTATION: - write_tls_handshake_defragmentation_test( - out, side, length, Version.TLS12, - cipher=cipher_suite, etm=etm, - variant=', '+nickname) - else: - write_tls_handshake_defragmentation_test(out, side, length, - Version.TLS12) - - -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 {os.path.basename(sys.argv[0])}. Do not edit! - -# Copyright The Mbed TLS Contributors -# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later - -""") - write_tls_handshake_defragmentation_tests(out) - out.write("""\ -# End of automatically generated file. -""") - -def main() -> None: - """Command line entry point.""" - parser = argparse.ArgumentParser() - parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument('-o', '--output', - default='tests/opt-testcases/handshake-generated.sh', - help='Output file (default: tests/opt-testcases/handshake-generated.sh)') - args = parser.parse_args() - with open(args.output, 'w') as out: - write_handshake_tests(out) +from mbedtls_framework import tls_handshake_tests if __name__ == '__main__': - main() + tls_handshake_tests.main() diff --git a/scripts/mbedtls_framework/tls_handshake_tests.py b/scripts/mbedtls_framework/tls_handshake_tests.py new file mode 100644 index 000000000..e4c9b238c --- /dev/null +++ b/scripts/mbedtls_framework/tls_handshake_tests.py @@ -0,0 +1,214 @@ +""" +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 os +import sys +from typing import Optional + +from mbedtls_framework import tls_test_case +from mbedtls_framework import typing_util +from mbedtls_framework.tls_test_case import Side, Version +import translate_ciphers + + +# Assume that a TLS 1.2 ClientHello used in these tests will be at most +# this many bytes long. +TLS12_CLIENT_HELLO_ASSUMED_MAX_LENGTH = 255 + +# Minimum handshake fragment length that Mbed TLS supports. +TLS_HANDSHAKE_FRAGMENT_MIN_LENGTH = 4 + +def write_tls_handshake_defragmentation_test( + #pylint: disable=too-many-arguments + out: typing_util.Writable, + side: Side, + length: Optional[int], + version: Optional[Version] = None, + cipher: Optional[str] = None, + etm: Optional[bool] = None, #encrypt-then-mac (only relevant for CBC) + variant: str = '' +) -> None: + """Generate one TLS handshake defragmentation test. + + :param out: file to write to. + :param side: which side is Mbed TLS. + :param length: fragment length, or None to not fragment. + :param version: protocol version, if forced. + """ + #pylint: disable=chained-comparison,too-many-branches,too-many-statements + + our_args = '' + their_args = '' + + if length is None: + description = 'no fragmentation, for reference' + else: + description = 'len=' + str(length) + if version is not None: + description += ', TLS 1.' + str(version.value) + description = f'Handshake defragmentation on {side.name.lower()}: {description}' + tc = tls_test_case.TestCase(description) + + if version is not None: + their_args += ' ' + version.openssl_option() + # Emit a version requirement, because we're forcing the version via + # OpenSSL, not via Mbed TLS, and the automatic depdendencies in + # ssl-opt.sh only handle forcing the version via Mbed TLS. + tc.requirements.append(version.requires_command()) + if side == Side.SERVER and version == Version.TLS12 and \ + length is not None and \ + length <= TLS12_CLIENT_HELLO_ASSUMED_MAX_LENGTH: + # Server-side ClientHello defragmentation is only supported in + # the TLS 1.3 message parser. When that parser sees an 1.2-only + # ClientHello, it forwards the reassembled record to the + # TLS 1.2 ClientHello parser so the ClientHello can be fragmented. + # When TLS 1.3 support is disabled in the server (at compile-time + # or at runtime), the TLS 1.2 ClientHello parser only sees + # the first fragment of the ClientHello. + tc.requirements.append('requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_3') + tc.description += ' with 1.3 support' + + # To guarantee that the handhake messages are large enough and need to be + # split into fragments, the tests require certificate authentication. + # The party in control of the fragmentation operations is OpenSSL and + # will always use server5.crt (548 Bytes). + if length is not None and \ + length >= TLS_HANDSHAKE_FRAGMENT_MIN_LENGTH: + tc.requirements.append('requires_certificate_authentication') + if version == Version.TLS12 and side == Side.CLIENT: + #The server uses an ECDSA cert, so make sure we have a compatible key exchange + tc.requirements.append( + 'requires_config_enabled MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED') + else: + # This test case may run in a pure-PSK configuration. OpenSSL doesn't + # allow this by default with TLS 1.3. + their_args += ' -allow_no_dhe_kex' + + if length is None: + forbidden_patterns = [ + 'waiting for more fragments', + ] + wanted_patterns = [] + elif length < TLS_HANDSHAKE_FRAGMENT_MIN_LENGTH: + their_args += ' -split_send_frag ' + str(length) + tc.exit_code = 1 + forbidden_patterns = [] + wanted_patterns = [ + 'handshake message too short: ' + str(length), + 'SSL - An invalid SSL record was received', + ] + if side == Side.SERVER: + wanted_patterns[0:0] = ['<= parse client hello'] + elif version == Version.TLS13: + wanted_patterns[0:0] = ['=> ssl_tls13_process_server_hello'] + else: + their_args += ' -split_send_frag ' + str(length) + forbidden_patterns = [] + wanted_patterns = [ + 'reassembled record', + fr'initial handshake fragment: {length}, 0\.\.{length} of [0-9]\+', + fr'subsequent handshake fragment: [0-9]\+, {length}\.\.', + fr'Prepare: waiting for more handshake fragments {length}/', + fr'Consume: waiting for more handshake fragments {length}/', + ] + + if cipher is not None: + mbedtls_cipher = translate_ciphers.translate_mbedtls(cipher) + if side == Side.CLIENT: + our_args += ' force_ciphersuite=' + mbedtls_cipher + if 'NULL' in cipher: + their_args += ' -cipher ALL@SECLEVEL=0:COMPLEMENTOFALL@SECLEVEL=0' + else: + # For TLS 1.2, when Mbed TLS is the server, we must force the + # cipher suite on the client side, because passing + # force_ciphersuite to ssl_server2 would force a TLS-1.2-only + # server, which does not support a fragmented ClientHello. + tc.requirements.append('requires_ciphersuite_enabled ' + mbedtls_cipher) + their_args += ' -cipher ' + translate_ciphers.translate_ossl(cipher) + if 'NULL' in cipher: + their_args += '@SECLEVEL=0' + + if etm is not None: + if etm: + tc.requirements.append('requires_config_enabled MBEDTLS_SSL_ENCRYPT_THEN_MAC') + our_args += ' etm=' + str(int(etm)) + (wanted_patterns if etm else forbidden_patterns)[0:0] = [ + 'using encrypt then mac', + ] + + tc.description += variant + + if side == Side.CLIENT: + tc.client = '$P_CLI debug_level=4' + our_args + tc.server = '$O_NEXT_SRV' + their_args + tc.wanted_client_patterns = wanted_patterns + tc.forbidden_client_patterns = forbidden_patterns + else: + their_args += ' -cert $DATA_FILES_PATH/server5.crt -key $DATA_FILES_PATH/server5.key' + our_args += ' auth_mode=required' + tc.client = '$O_NEXT_CLI' + their_args + tc.server = '$P_SRV debug_level=4' + our_args + tc.wanted_server_patterns = wanted_patterns + tc.forbidden_server_patterns = forbidden_patterns + tc.write(out) + + +CIPHERS_FOR_TLS12_HANDSHAKE_DEFRAGMENTATION = [ + (None, 'default', None), + ('TLS_ECDHE_ECDSA_WITH_NULL_SHA', 'null', None), + ('TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256', 'ChachaPoly', None), + ('TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256', 'GCM', None), + ('TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256', 'CBC, etm=n', False), + ('TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256', 'CBC, etm=y', True), +] + +def write_tls_handshake_defragmentation_tests(out: typing_util.Writable) -> None: + """Generate TLS handshake defragmentation tests.""" + for side in Side.CLIENT, Side.SERVER: + write_tls_handshake_defragmentation_test(out, side, None) + for length in [512, 513, 256, 128, 64, 36, 32, 16, 13, 5, 4, 3]: + write_tls_handshake_defragmentation_test(out, side, length, + Version.TLS13) + if length == 4: + for (cipher_suite, nickname, etm) in \ + CIPHERS_FOR_TLS12_HANDSHAKE_DEFRAGMENTATION: + write_tls_handshake_defragmentation_test( + out, side, length, Version.TLS12, + cipher=cipher_suite, etm=etm, + variant=', '+nickname) + else: + write_tls_handshake_defragmentation_test(out, side, length, + Version.TLS12) + + +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 {os.path.basename(sys.argv[0])}. Do not edit! + +# Copyright The Mbed TLS Contributors +# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later + +""") + write_tls_handshake_defragmentation_tests(out) + out.write("""\ +# End of automatically generated file. +""") + +def main() -> None: + """Command line entry point.""" + parser = argparse.ArgumentParser() + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('-o', '--output', + default='tests/opt-testcases/handshake-generated.sh', + help='Output file (default: tests/opt-testcases/handshake-generated.sh)') + args = parser.parse_args() + with open(args.output, 'w') as out: + write_handshake_tests(out) From 650159869a381d4c47695620a49dd85824f9c254 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Tue, 20 Jan 2026 11:12:21 +0100 Subject: [PATCH 2/4] tls_handshake_tests.py: Add no-tls12-client-hello-defragmentation-support arg Add a no-tls12-client-hello-defragmentation-support argument to specify if TLS 1.2 Client Hello defragmentation is fully supported or not. This is temporary while the support is being added in Mbed TLS development and 3.6 branches. Signed-off-by: Ronald Cron --- scripts/generate_tls_handshake_tests.py | 2 + .../mbedtls_framework/tls_handshake_tests.py | 40 +++++++++++++------ 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/scripts/generate_tls_handshake_tests.py b/scripts/generate_tls_handshake_tests.py index 966098ec6..53cb79be3 100755 --- a/scripts/generate_tls_handshake_tests.py +++ b/scripts/generate_tls_handshake_tests.py @@ -8,7 +8,9 @@ Transitional wrapper to facilitate the migration of consuming branches. # Copyright The Mbed TLS Contributors # SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later +import sys 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/scripts/mbedtls_framework/tls_handshake_tests.py b/scripts/mbedtls_framework/tls_handshake_tests.py index e4c9b238c..00f0fcd4f 100644 --- a/scripts/mbedtls_framework/tls_handshake_tests.py +++ b/scripts/mbedtls_framework/tls_handshake_tests.py @@ -31,6 +31,7 @@ def write_tls_handshake_defragmentation_test( version: Optional[Version] = None, cipher: Optional[str] = None, etm: Optional[bool] = None, #encrypt-then-mac (only relevant for CBC) + tls12_client_hello_defragmentation: Optional[bool] = True, variant: str = '' ) -> None: """Generate one TLS handshake defragmentation test. @@ -62,14 +63,17 @@ def write_tls_handshake_defragmentation_test( tc.requirements.append(version.requires_command()) if side == Side.SERVER and version == Version.TLS12 and \ length is not None and \ - length <= TLS12_CLIENT_HELLO_ASSUMED_MAX_LENGTH: - # Server-side ClientHello defragmentation is only supported in - # the TLS 1.3 message parser. When that parser sees an 1.2-only - # ClientHello, it forwards the reassembled record to the - # TLS 1.2 ClientHello parser so the ClientHello can be fragmented. + length <= TLS12_CLIENT_HELLO_ASSUMED_MAX_LENGTH and \ + not tls12_client_hello_defragmentation: + # If Server-side ClientHello defragmentation is only supported in + # the TLS 1.3 message parser, not in the TLS 1.2 message parser, + # a TLS 1.2 fragmented ClientHello is handled properly only if it + # is first reassembled by the TLS 1.3 parser before to be passed to + # the TLS 1.2 ClientHello parser in a TLS 1.3 or TLS 1.2 version + # negotiation scenario. # When TLS 1.3 support is disabled in the server (at compile-time # or at runtime), the TLS 1.2 ClientHello parser only sees - # the first fragment of the ClientHello. + # the first fragment of a fragmented ClientHello. tc.requirements.append('requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_3') tc.description += ' with 1.3 support' @@ -167,7 +171,7 @@ CIPHERS_FOR_TLS12_HANDSHAKE_DEFRAGMENTATION = [ ('TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256', 'CBC, etm=y', True), ] -def write_tls_handshake_defragmentation_tests(out: typing_util.Writable) -> None: +def write_tls_handshake_defragmentation_tests(args, out: typing_util.Writable) -> None: """Generate TLS handshake defragmentation tests.""" for side in Side.CLIENT, Side.SERVER: write_tls_handshake_defragmentation_test(out, side, None) @@ -180,13 +184,17 @@ def write_tls_handshake_defragmentation_tests(out: typing_util.Writable) -> None write_tls_handshake_defragmentation_test( out, side, length, Version.TLS12, cipher=cipher_suite, etm=etm, - variant=', '+nickname) + variant=', '+nickname, + tls12_client_hello_defragmentation= \ + args.tls12_client_hello_defragmentation) else: - write_tls_handshake_defragmentation_test(out, side, length, - Version.TLS12) + write_tls_handshake_defragmentation_test( + out, side, length, Version.TLS12, + tls12_client_hello_defragmentation= + args.tls12_client_hello_defragmentation) -def write_handshake_tests(out: typing_util.Writable) -> None: +def write_handshake_tests(args, out: typing_util.Writable) -> None: """Generate handshake tests.""" out.write(f"""\ # Miscellaneous tests related to the TLS handshake layer. @@ -197,7 +205,7 @@ def write_handshake_tests(out: typing_util.Writable) -> None: # SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later """) - write_tls_handshake_defragmentation_tests(out) + write_tls_handshake_defragmentation_tests(args, out) out.write("""\ # End of automatically generated file. """) @@ -209,6 +217,12 @@ def main() -> None: parser.add_argument('-o', '--output', default='tests/opt-testcases/handshake-generated.sh', help='Output file (default: tests/opt-testcases/handshake-generated.sh)') + parser.add_argument('--no-tls12-client-hello-defragmentation-support', + action="store_false", + dest="tls12_client_hello_defragmentation", + help="Whether the TLS 1.2 ClientHello defragmentation is " + "fully supported or not (default: True)") args = parser.parse_args() + with open(args.output, 'w') as out: - write_handshake_tests(out) + write_handshake_tests(args, out) From c678de61dc77baea356d881b0928b794ef5eaa0b Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Mon, 26 Jan 2026 15:36:54 +0100 Subject: [PATCH 3/4] tls_handshake_tests.py: Fix expected server log The TLS 1.2 ClientHello function does not return at the end in case of parsing error. Thus adapt the server expected log in case of too small fragments. Signed-off-by: Ronald Cron --- scripts/mbedtls_framework/tls_handshake_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/mbedtls_framework/tls_handshake_tests.py b/scripts/mbedtls_framework/tls_handshake_tests.py index 00f0fcd4f..5de91299b 100644 --- a/scripts/mbedtls_framework/tls_handshake_tests.py +++ b/scripts/mbedtls_framework/tls_handshake_tests.py @@ -107,7 +107,7 @@ def write_tls_handshake_defragmentation_test( 'SSL - An invalid SSL record was received', ] if side == Side.SERVER: - wanted_patterns[0:0] = ['<= parse client hello'] + wanted_patterns[0:0] = ['=> parse client hello'] elif version == Version.TLS13: wanted_patterns[0:0] = ['=> ssl_tls13_process_server_hello'] else: From 042e954d7dd1853b271d33c3a841a7551867edf2 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Tue, 20 Jan 2026 15:25:36 +0100 Subject: [PATCH 4/4] make_generated_files.py: Split data from code Initial work to eventually have the list of scripts that generate files in the branches that consume the framework. Signed-off-by: Ronald Cron --- scripts/check-python-files.sh | 7 +- scripts/make_generated_files.py | 168 +------------------ scripts/mbedtls_framework/generated_files.py | 167 ++++++++++++++++++ 3 files changed, 179 insertions(+), 163 deletions(-) create mode 100644 scripts/mbedtls_framework/generated_files.py diff --git a/scripts/check-python-files.sh b/scripts/check-python-files.sh index 83767fc38..d15647b47 100755 --- a/scripts/check-python-files.sh +++ b/scripts/check-python-files.sh @@ -58,8 +58,13 @@ echo 'Running pylint ...' # Temporary workaround while moving the bulk of abi_check.py to the framework # Check abi_check.py separately from the rest of the files, so it's not flagged # for code duplication. +# Temporary workaround for the transitional wrapper framework/scripts/make_generated_files.py +# as well. Once make_generated_files.py exists in both MbedTLS:development and +# TF-PSA-Crypto:development branches, we will be able to remove +# framework/scripts/make_generated_files.py and, consequently, this exception. find framework/scripts/*.py framework/scripts/mbedtls_framework/*.py scripts/*.py tests/scripts/*.py \ - -path scripts/abi_check.py \ + ! -path scripts/abi_check.py \ + ! -path framework/scripts/make_generated_files.py \ -exec $PYTHON -m pylint {} + \ -o -exec $PYTHON -m pylint {} + || { echo >&2 "pylint reported errors" diff --git a/scripts/make_generated_files.py b/scripts/make_generated_files.py index 99a0512d5..ad8c5b8a9 100755 --- a/scripts/make_generated_files.py +++ b/scripts/make_generated_files.py @@ -1,77 +1,17 @@ #!/usr/bin/env python3 +"""Generate, check and list the generated files +Transitional wrapper to facilitate the migration of consuming branches. +""" -# make_generated_files.py -# # Copyright The Mbed TLS Contributors # SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later -""" -Generate the TF-PSA-Crypto generated files -""" -import argparse -import filecmp -import shutil -import subprocess import sys - from pathlib import Path -from typing import List, Optional from mbedtls_framework import build_tree - -class GenerationScript: - """ - Representation of a script generating a configuration independent file. - """ - # pylint: disable=too-few-public-methods,too-many-arguments - def __init__(self, script: Path, files: List[Path], - output_dir_option: Optional[str] = None, - output_file_option: Optional[str] = None, - optional: bool = False) -> None: - # Path from the root of Mbed TLS or TF-PSA-Crypto of the generation script - self.script = script - - # Executable to run the script, needed for Windows - if script.suffix == ".py": - self.exe = sys.executable - elif script.suffix == ".pl": - self.exe = "perl" - - # List of the default paths from the Mbed TLS or TF-PSA-Crypto root of the - # files the script generates. - self.files = files - - # Output directory script argument. Can be an empty string in case it is a - # positional argument. - self.output_dir_option = output_dir_option - - # Output file script argument. Can be an empty string in case it is a - # positional argument. - self.output_file_option = output_file_option - - # Optional files are skipped in --check mode if they don't exist. - # This normally shouldn't happen, but it can happen during transition - # periods where we're adding a new script or a new file, and a - # consuming repository hasn't been updated yet. - self.optional = optional - -def get_generation_script_files(generation_script: str) -> List[Path]: - """ - Get the list of the default paths of the files that a given script - generates. It is assumed that the script supports the "--list" option. - """ - files = [] - if generation_script.endswith(".py"): - cmd = [sys.executable] - elif generation_script.endswith(".pl"): - cmd = ["perl"] - cmd += [generation_script, "--list"] - - output = subprocess.check_output(cmd, universal_newlines=True) - for line in output.splitlines(): - files.append(Path(line)) - - return files +from mbedtls_framework import generated_files +from mbedtls_framework.generated_files import GenerationScript, get_generation_script_files COMMON_GENERATION_SCRIPTS = [ GenerationScript( @@ -182,93 +122,7 @@ if build_tree.looks_like_mbedtls_root(".") and not build_tree.is_mbedtls_3_6(): get_generation_script_files("scripts/generate_visualc_files.pl"), "--directory", None)) -def get_generated_files(generation_scripts: List[GenerationScript]) -> List[Path]: - """ - List the generated files in Mbed TLS or TF-PSA-Crypto. The path from root - is returned for each generated files. - """ - files = [] - for generation_script in generation_scripts: - files += generation_script.files - - return files - -def make_generated_files(generation_scripts: List[GenerationScript]) -> None: - """ - Generate the configuration independent files in their default location in - the Mbed TLS or TF-PSA-Crypto tree. - """ - for generation_script in generation_scripts: - subprocess.run([generation_script.exe, str(generation_script.script)], check=True) - -def check_generated_files(generation_scripts: List[GenerationScript], - root: Path) -> bool: - """ - Check that the given root directory contains the generated files as expected/ - generated by this script. - """ - ok = True - for generation_script in generation_scripts: - for file in generation_script.files: - file = root / file - if not file.exists(): - # If the script is just being added, allow its files not - # to exist. This can happen, at least, when adding a new - # generation script in crypto: until mbedtls is updated, - # the files from that script won't be present when - # the updated crypto is built from mbedtls development. - if generation_script.optional: - continue - raise Exception(f"Expected generated file does not exist: {file}") - bak_file = file.with_name(file.name + ".bak") - if bak_file.exists(): - bak_file.unlink() - file.rename(bak_file) - - command = [generation_script.exe, str(generation_script.script)] - if generation_script.output_dir_option is not None: - command += [generation_script.output_dir_option, - str(root / Path(generation_script.files[0].parent))] - elif generation_script.output_file_option is not None: - command += generation_script.output_file_option.split() - command += [str(root / Path(generation_script.files[0]))] - subprocess.run([item for item in command if item.strip()], check=True) - - for file in generation_script.files: - file = root / file - bak_file = file.with_name(file.name + ".bak") - if generation_script.optional and not bak_file.exists(): - # This file is optional and didn't exist before, so - # there's nothing to compare to, or clean up. - continue - if not filecmp.cmp(file, bak_file): - ok = False - ref_file = file.with_name(file.name + ".ref") - ref_file = root / ref_file - if ref_file.exists(): - ref_file.unlink() - shutil.copy(file, ref_file) - print(f"Generated file {file} not identical to the reference one {ref_file}.") - file.unlink() - bak_file.rename(file) - return ok - def main() -> int: - """ - Main function of this program - """ - parser = argparse.ArgumentParser() - - parser.add_argument('--list', action='store_true', - default=False, help='List generated files.') - parser.add_argument('--root', metavar='DIR', - help='Root of the tree containing the generated files \ - to check (default: Mbed TLS or TF-PSA-Cryto root.)') - parser.add_argument('--check', action='store_true', - default=False, help='Check the generated files in root') - - args = parser.parse_args() - if not build_tree.looks_like_root("."): raise RuntimeError("This script must be run from Mbed TLS or TF-PSA-Crypto root.") @@ -280,17 +134,7 @@ def main() -> int: raise Exception("No support for Mbed TLS 3.6") generation_scripts += COMMON_GENERATION_SCRIPTS - if args.list: - files = get_generated_files(generation_scripts) - for file in files: - print(str(file)) - return 0 - elif args.check: - ok = check_generated_files(generation_scripts, Path(args.root or ".")) - return 0 if ok else 1 - else: - make_generated_files(generation_scripts) - return 0 # Any error causes an exception + return generated_files.main(generation_scripts) if __name__ == "__main__": sys.exit(main()) diff --git a/scripts/mbedtls_framework/generated_files.py b/scripts/mbedtls_framework/generated_files.py new file mode 100644 index 000000000..2ceb45dea --- /dev/null +++ b/scripts/mbedtls_framework/generated_files.py @@ -0,0 +1,167 @@ +"""Generic code to generate, check and list the generated files +""" + +# Copyright The Mbed TLS Contributors +# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later + +import argparse +import filecmp +import shutil +import subprocess +import sys + +from pathlib import Path +from typing import List, Optional + +class GenerationScript: + """ + Representation of a script generating a configuration independent file. + """ + # pylint: disable=too-few-public-methods,too-many-arguments + def __init__(self, script: Path, files: List[Path], + output_dir_option: Optional[str] = None, + output_file_option: Optional[str] = None, + optional: bool = False) -> None: + # Path from the root of Mbed TLS or TF-PSA-Crypto of the generation script + self.script = script + + # Executable to run the script, needed for Windows + if script.suffix == ".py": + self.exe = sys.executable + elif script.suffix == ".pl": + self.exe = "perl" + + # List of the default paths from the Mbed TLS or TF-PSA-Crypto root of the + # files the script generates. + self.files = files + + # Output directory script argument. Can be an empty string in case it is a + # positional argument. + self.output_dir_option = output_dir_option + + # Output file script argument. Can be an empty string in case it is a + # positional argument. + self.output_file_option = output_file_option + + # Optional files are skipped in --check mode if they don't exist. + # This normally shouldn't happen, but it can happen during transition + # periods where we're adding a new script or a new file, and a + # consuming repository hasn't been updated yet. + self.optional = optional + +def get_generation_script_files(generation_script: str) -> List[Path]: + """ + Get the list of the default paths of the files that a given script + generates. It is assumed that the script supports the "--list" option. + """ + files = [] + if generation_script.endswith(".py"): + cmd = [sys.executable] + elif generation_script.endswith(".pl"): + cmd = ["perl"] + cmd += [generation_script, "--list"] + + output = subprocess.check_output(cmd, universal_newlines=True) + for line in output.splitlines(): + files.append(Path(line)) + + return files + +def get_generated_files(generation_scripts: List[GenerationScript]) -> List[Path]: + """ + List the generated files in Mbed TLS or TF-PSA-Crypto. The path from root + is returned for each generated files. + """ + files = [] + for generation_script in generation_scripts: + files += generation_script.files + + return files + +def make_generated_files(generation_scripts: List[GenerationScript]) -> None: + """ + Generate the configuration independent files in their default location in + the Mbed TLS or TF-PSA-Crypto tree. + """ + for generation_script in generation_scripts: + subprocess.run([generation_script.exe, str(generation_script.script)], check=True) + +def check_generated_files(generation_scripts: List[GenerationScript], + root: Path) -> bool: + """ + Check that the given root directory contains the generated files as expected/ + generated by this script. + """ + ok = True + for generation_script in generation_scripts: + for file in generation_script.files: + file = root / file + if not file.exists(): + # If the script is just being added, allow its files not + # to exist. This can happen, at least, when adding a new + # generation script in crypto: until mbedtls is updated, + # the files from that script won't be present when + # the updated crypto is built from mbedtls development. + if generation_script.optional: + continue + raise Exception(f"Expected generated file does not exist: {file}") + bak_file = file.with_name(file.name + ".bak") + if bak_file.exists(): + bak_file.unlink() + file.rename(bak_file) + + command = [generation_script.exe, str(generation_script.script)] + if generation_script.output_dir_option is not None: + command += [generation_script.output_dir_option, + str(root / Path(generation_script.files[0].parent))] + elif generation_script.output_file_option is not None: + command += generation_script.output_file_option.split() + command += [str(root / Path(generation_script.files[0]))] + subprocess.run([item for item in command if item.strip()], check=True) + + for file in generation_script.files: + file = root / file + bak_file = file.with_name(file.name + ".bak") + if generation_script.optional and not bak_file.exists(): + # This file is optional and didn't exist before, so + # there's nothing to compare to, or clean up. + continue + if not filecmp.cmp(file, bak_file): + ok = False + ref_file = file.with_name(file.name + ".ref") + ref_file = root / ref_file + if ref_file.exists(): + ref_file.unlink() + shutil.copy(file, ref_file) + print(f"Generated file {file} not identical to the reference one {ref_file}.") + file.unlink() + bak_file.rename(file) + return ok + +def main(generation_scripts: List[GenerationScript]) -> int: + """ + Main function of this program + """ + parser = argparse.ArgumentParser() + + parser.add_argument('--list', action='store_true', + default=False, help='List generated files.') + parser.add_argument('--root', metavar='DIR', + help='Root of the tree containing the generated files \ + to check (default: Mbed TLS or TF-PSA-Cryto root.)') + parser.add_argument('--check', action='store_true', + default=False, help='Check the generated files in root') + + args = parser.parse_args() + + if args.list: + files = get_generated_files(generation_scripts) + for file in files: + print(str(file)) + return 0 + elif args.check: + ok = check_generated_files(generation_scripts, Path(args.root or ".")) + return 0 if ok else 1 + else: + make_generated_files(generation_scripts) + return 0 # Any error causes an exception