blob: 7b62a65530791b6b26744ce1cdd61a8641c438aa [file] [log] [blame]
Gilles Peskine693611e2024-06-11 19:32:22 +02001#!/usr/bin/env python3
2"""Generate test data for configuration reporting.
3"""
4
5# Copyright The Mbed TLS Contributors
6# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
7
8import re
9import sys
10from typing import Iterable, Iterator, List, Optional, Tuple
11
Gilles Peskine744741b2024-06-26 20:05:10 +020012import scripts_path # pylint: disable=unused-import
Gilles Peskine693611e2024-06-11 19:32:22 +020013import config
Gilles Peskine744741b2024-06-26 20:05:10 +020014from mbedtls_dev import test_case
15from mbedtls_dev import test_data_generation
Gilles Peskine693611e2024-06-11 19:32:22 +020016
17
Gilles Peskine5454a842024-05-29 16:37:38 +020018def single_setting_case(setting: config.Setting, when_on: bool,
19 dependencies: List[str],
20 note: Optional[str]) -> test_case.TestCase:
Gilles Peskine693611e2024-06-11 19:32:22 +020021 """Construct a test case for a boolean setting.
22
23 This test case passes if the setting and its dependencies are enabled,
24 and is skipped otherwise.
25
26 * setting: the setting to be tested.
27 * when_on: True to test with the setting enabled, or False to test
28 with the setting disabled.
29 * dependencies: extra dependencies for the test case.
Gilles Peskine5454a842024-05-29 16:37:38 +020030 * note: a note to add after the setting name in the test description.
Gilles Peskine693611e2024-06-11 19:32:22 +020031 This is generally a summary of dependencies, and is generally empty
32 if the given setting is only tested once.
33 """
34 base = setting.name if when_on else '!' + setting.name
35 tc = test_case.TestCase()
36 tc.set_function('pass')
37 description_suffix = ' (' + note + ')' if note else ''
38 tc.set_description('Config: ' + base + description_suffix)
39 tc.set_dependencies([base] + dependencies)
40 return tc
41
42
Gilles Peskine5454a842024-05-29 16:37:38 +020043# If foo is a setting that is only meaningful when bar is enabled, set
Gilles Peskine1afb7032024-05-29 16:44:52 +020044# SIMPLE_DEPENDENCIES[foo]=bar. More generally, bar can be a colon-separated
Gilles Peskine5454a842024-05-29 16:37:38 +020045# list of settings, meaning that all the settings must be enabled. Each setting
Gilles Peskine1afb7032024-05-29 16:44:52 +020046# in bar can be prefixed with '!' to negate it. This is the same syntax as a
Gilles Peskinec79ecea2024-05-23 16:32:39 +020047# depends_on directive in test data.
Gilles Peskine1afb7032024-05-29 16:44:52 +020048# See also `dependencies_of_settting`.
49SIMPLE_DEPENDENCIES = {
Gilles Peskinec79ecea2024-05-23 16:32:39 +020050 'MBEDTLS_AESNI_C': 'MBEDTLS_AES_C',
51 'MBEDTLS_ERROR_STRERROR_DUMMY': '!MBEDTLS_ERROR_C',
52 'MBEDTLS_GENPRIME': 'MBEDTLS_RSA_C',
53 'MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES': 'MBEDTLS_ENTROPY_C',
54 'MBEDTLS_NO_PLATFORM_ENTROPY': 'MBEDTLS_ENTROPY_C',
55 'MBEDTLS_PKCS1_V15': 'MBEDTLS_RSA_C',
56 'MBEDTLS_PKCS1_V21': 'MBEDTLS_RSA_C',
Gilles Peskinec08d5bf2024-05-28 19:18:31 +020057 'MBEDTLS_PSA_CRYPTO_CLIENT': '!MBEDTLS_PSA_CRYPTO_C',
Gilles Peskinec79ecea2024-05-23 16:32:39 +020058 'MBEDTLS_PSA_INJECT_ENTROPY': 'MBEDTLS_PSA_CRYPTO_C',
59 'MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS': 'MBEDTLS_PSA_CRYPTO_C',
Gilles Peskinea76a6ff2024-07-01 11:32:33 +020060 'MBEDTLS_SSL_PROTO_TLS1_3_EXPERIMENTAL': 'MBEDTLS_SSL_CLI_C:MBEDTLS_SSL_SRV_C',
Gilles Peskinec79ecea2024-05-23 16:32:39 +020061}
62
Gilles Peskine1afb7032024-05-29 16:44:52 +020063def dependencies_of_setting(cfg: config.Config,
64 setting: config.Setting) -> Optional[str]:
65 """Return dependencies without which a setting is not meaningful.
66
67 The dependencies of a setting express when a setting can be enabled and
68 is relevant. For example, if ``check_config.h`` errors out when
69 ``defined(FOO) && !defined(BAR)``, then ``BAR`` is a dependency of ``FOO``.
70 If ``FOO`` has no effect when ``CORGE`` is disabled, then ``CORGE``
71 is a dependency of ``FOO``.
Gilles Peskinec79ecea2024-05-23 16:32:39 +020072
Gilles Peskine5454a842024-05-29 16:37:38 +020073 The return value can be a colon-separated list of settings, if the setting
Gilles Peskine1afb7032024-05-29 16:44:52 +020074 is only meaningful when all of these settings are enabled. Each setting can
75 be negated by prefixing them with '!'. This is the same syntax as a
Gilles Peskinec79ecea2024-05-23 16:32:39 +020076 depends_on directive in test data.
77 """
78 #pylint: disable=too-many-return-statements
79 name = setting.name
Gilles Peskine1afb7032024-05-29 16:44:52 +020080 if name in SIMPLE_DEPENDENCIES:
81 return SIMPLE_DEPENDENCIES[name]
Gilles Peskinec79ecea2024-05-23 16:32:39 +020082 if name.startswith('MBEDTLS_') and not name.endswith('_C'):
83 if name.startswith('MBEDTLS_CIPHER_PADDING_'):
84 return 'MBEDTLS_CIPHER_C:MBEDTLS_CIPHER_MODE_CBC'
85 if name.startswith('MBEDTLS_PK_PARSE_EC_'):
86 return 'MBEDTLS_PK_C:MBEDTLS_PK_HAVE_ECC_KEYS'
Gilles Peskine5454a842024-05-29 16:37:38 +020087 # For TLS settings, insist on having them once off and once on in
Gilles Peskinef75c70b2024-05-28 19:18:46 +020088 # a configuration where both client support and server support are
Gilles Peskine5454a842024-05-29 16:37:38 +020089 # enabled. The settings are also meaningful when only one side is
Gilles Peskinef75c70b2024-05-28 19:18:46 +020090 # enabled, but there isn't much point in having separate records
91 # for client-side and server-side, so we keep things simple.
92 # Requiring both sides to be enabled also means we know we'll run
93 # tests that only run Mbed TLS against itself, which only run in
94 # configurations with both sides enabled.
Gilles Peskinecfba2472024-06-26 20:11:59 +020095 if name.startswith('MBEDTLS_SSL_TLS1_3_'):
Gilles Peskinea76a6ff2024-07-01 11:32:33 +020096 return 'MBEDTLS_SSL_CLI_C:MBEDTLS_SSL_SRV_C:MBEDTLS_SSL_PROTO_TLS1_3_EXPERIMENTAL'
Gilles Peskinec79ecea2024-05-23 16:32:39 +020097 if name.startswith('MBEDTLS_SSL_DTLS_'):
98 return 'MBEDTLS_SSL_CLI_C:MBEDTLS_SSL_SRV_C:MBEDTLS_SSL_PROTO_DTLS'
99 if name.startswith('MBEDTLS_SSL_'):
100 return 'MBEDTLS_SSL_CLI_C:MBEDTLS_SSL_SRV_C'
Gilles Peskine556249e2024-05-23 19:37:20 +0200101 for pos in re.finditer(r'_', name):
102 super_name = name[:pos.start()] + '_C'
Gilles Peskinec79ecea2024-05-23 16:32:39 +0200103 if cfg.known(super_name):
104 return super_name
Gilles Peskinec79ecea2024-05-23 16:32:39 +0200105 return None
106
Gilles Peskine5454a842024-05-29 16:37:38 +0200107def conditions_for_setting(cfg: config.Config,
108 setting: config.Setting
109 ) -> Iterator[Tuple[List[str], str]]:
Gilles Peskine693611e2024-06-11 19:32:22 +0200110 """Enumerate the conditions under which to test the given setting.
111
Gilles Peskine5454a842024-05-29 16:37:38 +0200112 * cfg: all configuration settings.
Gilles Peskine693611e2024-06-11 19:32:22 +0200113 * setting: the setting to be tested.
114
115 Generate a stream of conditions, i.e. extra dependencies to test with
116 together with a human-readable explanation of each dependency. Some
117 typical cases:
118
119 * By default, generate a one-element stream with no extra dependencies.
Gilles Peskine5454a842024-05-29 16:37:38 +0200120 * If the setting is ignored unless some other setting is enabled, generate
121 a one-element stream with that other setting as an extra dependency.
122 * If the setting is known to interact with some other setting, generate
123 a stream with one element where this setting is on and one where it's off.
Gilles Peskine693611e2024-06-11 19:32:22 +0200124 * To skip the setting altogether, generate an empty stream.
125 """
126 name = setting.name
127 if name.endswith('_ALT') and not config.is_seamless_alt(name):
128 # We don't test alt implementations, except (most) platform alts
129 return
Gilles Peskine1afb7032024-05-29 16:44:52 +0200130 dependencies = dependencies_of_setting(cfg, setting)
131 if dependencies:
132 yield [dependencies], ''
Gilles Peskinec79ecea2024-05-23 16:32:39 +0200133 return
Gilles Peskine693611e2024-06-11 19:32:22 +0200134 yield [], ''
135
136
Gilles Peskine5454a842024-05-29 16:37:38 +0200137def enumerate_boolean_setting_cases(cfg: config.Config
Gilles Peskine693611e2024-06-11 19:32:22 +0200138 ) -> Iterable[test_case.TestCase]:
Gilles Peskine5454a842024-05-29 16:37:38 +0200139 """Emit test cases for all boolean settings."""
Gilles Peskine693611e2024-06-11 19:32:22 +0200140 for name in sorted(cfg.settings.keys()):
141 setting = cfg.settings[name]
142 if not name.startswith('PSA_WANT_') and setting.value:
143 continue # non-boolean setting
144 for when_on in True, False:
Gilles Peskine5454a842024-05-29 16:37:38 +0200145 for deps, note in conditions_for_setting(cfg, setting):
146 yield single_setting_case(setting, when_on, deps, note)
Gilles Peskine693611e2024-06-11 19:32:22 +0200147
148
149
150class ConfigTestGenerator(test_data_generation.TestGenerator):
151 """Generate test cases for configuration reporting."""
152
Gilles Peskine5454a842024-05-29 16:37:38 +0200153 def __init__(self, settings):
Gilles Peskine693611e2024-06-11 19:32:22 +0200154 self.mbedtls_config = config.ConfigFile()
155 self.targets['test_suite_config.mbedtls_boolean'] = \
Gilles Peskine5454a842024-05-29 16:37:38 +0200156 lambda: enumerate_boolean_setting_cases(self.mbedtls_config)
Gilles Peskine693611e2024-06-11 19:32:22 +0200157 self.psa_config = config.ConfigFile('include/psa/crypto_config.h')
158 self.targets['test_suite_config.psa_boolean'] = \
Gilles Peskine5454a842024-05-29 16:37:38 +0200159 lambda: enumerate_boolean_setting_cases(self.psa_config)
160 super().__init__(settings)
Gilles Peskine693611e2024-06-11 19:32:22 +0200161
162
163if __name__ == '__main__':
164 test_data_generation.main(sys.argv[1:], __doc__, ConfigTestGenerator)