mirror of
https://github.com/Mbed-TLS/mbedtls.git
synced 2025-04-19 01:04:04 +03:00
220 lines
8.9 KiB
Python
Executable File
220 lines
8.9 KiB
Python
Executable File
#!/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 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)
|
|
|
|
if __name__ == '__main__':
|
|
main()
|