config.py (17082B)
1 #!/usr/bin/env python3 2 3 """Mbed TLS and PSA configuration file manipulation library and tool 4 5 Basic usage, to read the Mbed TLS configuration: 6 config = MbedTLSConfig() 7 if 'MBEDTLS_RSA_C' in config: print('RSA is enabled') 8 """ 9 10 ## Copyright The Mbed TLS Contributors 11 ## SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later 12 ## 13 14 import os 15 import sys 16 17 import framework_scripts_path # pylint: disable=unused-import 18 from mbedtls_framework import config_common 19 20 21 def is_boolean_setting(name, value): 22 """Is this a boolean setting? 23 24 Mbed TLS boolean settings are enabled if the preprocessor macro is 25 defined, and disabled if the preprocessor macro is not defined. The 26 macro definition line in the configuration file has an empty expansion. 27 28 PSA_WANT_xxx settings are also boolean, but when they are enabled, 29 they expand to a nonzero value. We leave them undefined when they 30 are disabled. (Setting them to 0 currently means to enable them, but 31 this might change to mean disabling them. Currently we just never set 32 them to 0.) 33 """ 34 if name.startswith('PSA_WANT_'): 35 return True 36 if not value: 37 return True 38 return False 39 40 def realfull_adapter(_name, _value, _active): 41 """Activate all symbols. 42 43 This is intended for building the documentation, including the 44 documentation of settings that are activated by defining an optional 45 preprocessor macro. There is no expectation that the resulting 46 configuration can be built. 47 """ 48 return True 49 50 PSA_UNSUPPORTED_FEATURE = frozenset([ 51 'PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_DERIVE', 52 'PSA_WANT_KEY_TYPE_DH_KEY_PAIR_DERIVE' 53 ]) 54 55 PSA_DEPRECATED_FEATURE = frozenset([ 56 'PSA_WANT_KEY_TYPE_ECC_KEY_PAIR', 57 'PSA_WANT_KEY_TYPE_RSA_KEY_PAIR' 58 ]) 59 60 EXCLUDE_FROM_CRYPTO = PSA_UNSUPPORTED_FEATURE | \ 61 PSA_DEPRECATED_FEATURE 62 63 # The goal of the full configuration is to have everything that can be tested 64 # together. This includes deprecated or insecure options. It excludes: 65 # * Options that require additional build dependencies or unusual hardware. 66 # * Options that make testing less effective. 67 # * Options that are incompatible with other options, or more generally that 68 # interact with other parts of the code in such a way that a bulk enabling 69 # is not a good way to test them. 70 # * Options that remove features. 71 EXCLUDE_FROM_FULL = frozenset([ 72 #pylint: disable=line-too-long 73 'MBEDTLS_AES_ONLY_128_BIT_KEY_LENGTH', # interacts with CTR_DRBG_128_BIT_KEY 74 'MBEDTLS_AES_USE_HARDWARE_ONLY', # hardware dependency 75 'MBEDTLS_BLOCK_CIPHER_NO_DECRYPT', # incompatible with ECB in PSA, CBC/XTS/NIST_KW/DES 76 'MBEDTLS_CTR_DRBG_USE_128_BIT_KEY', # interacts with ENTROPY_FORCE_SHA256 77 'MBEDTLS_DEPRECATED_REMOVED', # conflicts with deprecated options 78 'MBEDTLS_DEPRECATED_WARNING', # conflicts with deprecated options 79 'MBEDTLS_ECDH_VARIANT_EVEREST_ENABLED', # influences the use of ECDH in TLS 80 'MBEDTLS_ECP_NO_FALLBACK', # removes internal ECP implementation 81 'MBEDTLS_ECP_WITH_MPI_UINT', # disables the default ECP and is experimental 82 'MBEDTLS_ENTROPY_FORCE_SHA256', # interacts with CTR_DRBG_128_BIT_KEY 83 'MBEDTLS_HAVE_SSE2', # hardware dependency 84 'MBEDTLS_MEMORY_BACKTRACE', # depends on MEMORY_BUFFER_ALLOC_C 85 'MBEDTLS_MEMORY_BUFFER_ALLOC_C', # makes sanitizers (e.g. ASan) less effective 86 'MBEDTLS_MEMORY_DEBUG', # depends on MEMORY_BUFFER_ALLOC_C 87 'MBEDTLS_NO_64BIT_MULTIPLICATION', # influences anything that uses bignum 88 'MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES', # removes a feature 89 'MBEDTLS_NO_PLATFORM_ENTROPY', # removes a feature 90 'MBEDTLS_NO_UDBL_DIVISION', # influences anything that uses bignum 91 'MBEDTLS_PSA_P256M_DRIVER_ENABLED', # influences SECP256R1 KeyGen/ECDH/ECDSA 92 'MBEDTLS_PLATFORM_NO_STD_FUNCTIONS', # removes a feature 93 'MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS', # removes a feature 94 'MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG', # behavior change + build dependency 95 'MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER', # interface and behavior change 96 'MBEDTLS_PSA_CRYPTO_SPM', # platform dependency (PSA SPM) 97 'MBEDTLS_PSA_INJECT_ENTROPY', # conflicts with platform entropy sources 98 'MBEDTLS_RSA_NO_CRT', # influences the use of RSA in X.509 and TLS 99 'MBEDTLS_SHA256_USE_A64_CRYPTO_ONLY', # interacts with *_USE_A64_CRYPTO_IF_PRESENT 100 'MBEDTLS_SHA256_USE_ARMV8_A_CRYPTO_ONLY', # interacts with *_USE_ARMV8_A_CRYPTO_IF_PRESENT 101 'MBEDTLS_SHA512_USE_A64_CRYPTO_ONLY', # interacts with *_USE_A64_CRYPTO_IF_PRESENT 102 'MBEDTLS_SHA256_USE_A64_CRYPTO_IF_PRESENT', # setting *_USE_ARMV8_A_CRYPTO is sufficient 103 'MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN', # build dependency (clang+memsan) 104 'MBEDTLS_TEST_CONSTANT_FLOW_VALGRIND', # build dependency (valgrind headers) 105 'MBEDTLS_X509_REMOVE_INFO', # removes a feature 106 'MBEDTLS_PSA_STATIC_KEY_SLOTS', # only relevant for embedded devices 107 'MBEDTLS_PSA_STATIC_KEY_SLOT_BUFFER_SIZE', # only relevant for embedded devices 108 ]) 109 110 def is_seamless_alt(name): 111 """Whether the xxx_ALT symbol should be included in the full configuration. 112 113 Include alternative implementations of platform functions, which are 114 configurable function pointers that default to the built-in function. 115 This way we test that the function pointers exist and build correctly 116 without changing the behavior, and tests can verify that the function 117 pointers are used by modifying those pointers. 118 119 Exclude alternative implementations of library functions since they require 120 an implementation of the relevant functions and an xxx_alt.h header. 121 """ 122 if name in ( 123 'MBEDTLS_PLATFORM_GMTIME_R_ALT', 124 'MBEDTLS_PLATFORM_SETUP_TEARDOWN_ALT', 125 'MBEDTLS_PLATFORM_MS_TIME_ALT', 126 'MBEDTLS_PLATFORM_ZEROIZE_ALT', 127 ): 128 # Similar to non-platform xxx_ALT, requires platform_alt.h 129 return False 130 return name.startswith('MBEDTLS_PLATFORM_') 131 132 def include_in_full(name): 133 """Rules for symbols in the "full" configuration.""" 134 if name in EXCLUDE_FROM_FULL: 135 return False 136 if name.endswith('_ALT'): 137 return is_seamless_alt(name) 138 return True 139 140 def full_adapter(name, value, active): 141 """Config adapter for "full".""" 142 if not is_boolean_setting(name, value): 143 return active 144 return include_in_full(name) 145 146 # The baremetal configuration excludes options that require a library or 147 # operating system feature that is typically not present on bare metal 148 # systems. Features that are excluded from "full" won't be in "baremetal" 149 # either (unless explicitly turned on in baremetal_adapter) so they don't 150 # need to be repeated here. 151 EXCLUDE_FROM_BAREMETAL = frozenset([ 152 #pylint: disable=line-too-long 153 'MBEDTLS_ENTROPY_NV_SEED', # requires a filesystem and FS_IO or alternate NV seed hooks 154 'MBEDTLS_FS_IO', # requires a filesystem 155 'MBEDTLS_HAVE_TIME', # requires a clock 156 'MBEDTLS_HAVE_TIME_DATE', # requires a clock 157 'MBEDTLS_NET_C', # requires POSIX-like networking 158 'MBEDTLS_PLATFORM_FPRINTF_ALT', # requires FILE* from stdio.h 159 'MBEDTLS_PLATFORM_NV_SEED_ALT', # requires a filesystem and ENTROPY_NV_SEED 160 'MBEDTLS_PLATFORM_TIME_ALT', # requires a clock and HAVE_TIME 161 'MBEDTLS_PSA_CRYPTO_SE_C', # requires a filesystem and PSA_CRYPTO_STORAGE_C 162 'MBEDTLS_PSA_CRYPTO_STORAGE_C', # requires a filesystem 163 'MBEDTLS_PSA_ITS_FILE_C', # requires a filesystem 164 'MBEDTLS_THREADING_C', # requires a threading interface 165 'MBEDTLS_THREADING_PTHREAD', # requires pthread 166 'MBEDTLS_TIMING_C', # requires a clock 167 'MBEDTLS_SHA256_USE_A64_CRYPTO_IF_PRESENT', # requires an OS for runtime-detection 168 'MBEDTLS_SHA256_USE_ARMV8_A_CRYPTO_IF_PRESENT', # requires an OS for runtime-detection 169 'MBEDTLS_SHA512_USE_A64_CRYPTO_IF_PRESENT', # requires an OS for runtime-detection 170 ]) 171 172 def keep_in_baremetal(name): 173 """Rules for symbols in the "baremetal" configuration.""" 174 if name in EXCLUDE_FROM_BAREMETAL: 175 return False 176 return True 177 178 def baremetal_adapter(name, value, active): 179 """Config adapter for "baremetal".""" 180 if not is_boolean_setting(name, value): 181 return active 182 if name == 'MBEDTLS_NO_PLATFORM_ENTROPY': 183 # No OS-provided entropy source 184 return True 185 return include_in_full(name) and keep_in_baremetal(name) 186 187 # This set contains options that are mostly for debugging or test purposes, 188 # and therefore should be excluded when doing code size measurements. 189 # Options that are their own module (such as MBEDTLS_ERROR_C) are not listed 190 # and therefore will be included when doing code size measurements. 191 EXCLUDE_FOR_SIZE = frozenset([ 192 'MBEDTLS_DEBUG_C', # large code size increase in TLS 193 'MBEDTLS_SELF_TEST', # increases the size of many modules 194 'MBEDTLS_TEST_HOOKS', # only useful with the hosted test framework, increases code size 195 ]) 196 197 def baremetal_size_adapter(name, value, active): 198 if name in EXCLUDE_FOR_SIZE: 199 return False 200 return baremetal_adapter(name, value, active) 201 202 def include_in_crypto(name): 203 """Rules for symbols in a crypto configuration.""" 204 if name.startswith('MBEDTLS_X509_') or \ 205 name.startswith('MBEDTLS_SSL_') or \ 206 name.startswith('MBEDTLS_KEY_EXCHANGE_'): 207 return False 208 if name in [ 209 'MBEDTLS_DEBUG_C', # part of libmbedtls 210 'MBEDTLS_NET_C', # part of libmbedtls 211 'MBEDTLS_PKCS7_C', # part of libmbedx509 212 ]: 213 return False 214 return True 215 216 def crypto_adapter(adapter): 217 """Modify an adapter to disable non-crypto symbols. 218 219 ``crypto_adapter(adapter)(name, value, active)`` is like 220 ``adapter(name, value, active)``, but unsets all X.509 and TLS symbols. 221 """ 222 def continuation(name, value, active): 223 if not include_in_crypto(name): 224 return False 225 if adapter is None: 226 return active 227 return adapter(name, value, active) 228 return continuation 229 230 DEPRECATED = frozenset([ 231 'MBEDTLS_PSA_CRYPTO_SE_C', 232 'MBEDTLS_SSL_CLI_ALLOW_WEAK_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME', 233 ]) 234 def no_deprecated_adapter(adapter): 235 """Modify an adapter to disable deprecated symbols. 236 237 ``no_deprecated_adapter(adapter)(name, value, active)`` is like 238 ``adapter(name, value, active)``, but unsets all deprecated symbols 239 and sets ``MBEDTLS_DEPRECATED_REMOVED``. 240 """ 241 def continuation(name, value, active): 242 if name == 'MBEDTLS_DEPRECATED_REMOVED': 243 return True 244 if name in DEPRECATED: 245 return False 246 if adapter is None: 247 return active 248 return adapter(name, value, active) 249 return continuation 250 251 def no_platform_adapter(adapter): 252 """Modify an adapter to disable platform symbols. 253 254 ``no_platform_adapter(adapter)(name, value, active)`` is like 255 ``adapter(name, value, active)``, but unsets all platform symbols other 256 ``than MBEDTLS_PLATFORM_C. 257 """ 258 def continuation(name, value, active): 259 # Allow MBEDTLS_PLATFORM_C but remove all other platform symbols. 260 if name.startswith('MBEDTLS_PLATFORM_') and name != 'MBEDTLS_PLATFORM_C': 261 return False 262 if adapter is None: 263 return active 264 return adapter(name, value, active) 265 return continuation 266 267 268 class MbedTLSConfigFile(config_common.ConfigFile): 269 """Representation of an MbedTLS configuration file.""" 270 271 _path_in_tree = 'include/mbedtls/mbedtls_config.h' 272 default_path = [_path_in_tree, 273 os.path.join(os.path.dirname(__file__), 274 os.pardir, 275 _path_in_tree), 276 os.path.join(os.path.dirname(os.path.abspath(os.path.dirname(__file__))), 277 _path_in_tree)] 278 279 def __init__(self, filename=None): 280 super().__init__(self.default_path, 'Mbed TLS', filename) 281 self.current_section = 'header' 282 283 284 class CryptoConfigFile(config_common.ConfigFile): 285 """Representation of a Crypto configuration file.""" 286 287 # Temporary, while Mbed TLS does not just rely on the TF-PSA-Crypto 288 # build system to build its crypto library. When it does, the 289 # condition can just be removed. 290 _path_in_tree = 'include/psa/crypto_config.h' 291 default_path = [_path_in_tree, 292 os.path.join(os.path.dirname(__file__), 293 os.pardir, 294 _path_in_tree), 295 os.path.join(os.path.dirname(os.path.abspath(os.path.dirname(__file__))), 296 _path_in_tree)] 297 298 def __init__(self, filename=None): 299 super().__init__(self.default_path, 'Crypto', filename) 300 301 302 class MbedTLSConfig(config_common.Config): 303 """Representation of the Mbed TLS configuration. 304 305 See the documentation of the `Config` class for methods to query 306 and modify the configuration. 307 """ 308 309 def __init__(self, filename=None): 310 """Read the Mbed TLS configuration file.""" 311 312 super().__init__() 313 configfile = MbedTLSConfigFile(filename) 314 self.configfiles.append(configfile) 315 self.settings.update({name: config_common.Setting(configfile, active, name, value, section) 316 for (active, name, value, section) 317 in configfile.parse_file()}) 318 319 def set(self, name, value=None): 320 """Set name to the given value and make it active.""" 321 322 if name not in self.settings: 323 self._get_configfile().templates.append((name, '', '#define ' + name + ' ')) 324 325 super().set(name, value) 326 327 328 class CryptoConfig(config_common.Config): 329 """Representation of the PSA crypto configuration. 330 331 See the documentation of the `Config` class for methods to query 332 and modify the configuration. 333 """ 334 335 def __init__(self, filename=None): 336 """Read the PSA crypto configuration file.""" 337 338 super().__init__() 339 configfile = CryptoConfigFile(filename) 340 self.configfiles.append(configfile) 341 self.settings.update({name: config_common.Setting(configfile, active, name, value, section) 342 for (active, name, value, section) 343 in configfile.parse_file()}) 344 345 def set(self, name, value='1'): 346 """Set name to the given value and make it active.""" 347 348 if name in PSA_UNSUPPORTED_FEATURE: 349 raise ValueError(f'Feature is unsupported: \'{name}\'') 350 351 if name not in self.settings: 352 self._get_configfile().templates.append((name, '', '#define ' + name + ' ')) 353 354 super().set(name, value) 355 356 357 class MbedTLSConfigTool(config_common.ConfigTool): 358 """Command line mbedtls_config.h and crypto_config.h manipulation tool.""" 359 360 def __init__(self): 361 super().__init__(MbedTLSConfigFile.default_path) 362 self.config = MbedTLSConfig(self.args.file) 363 364 def custom_parser_options(self): 365 """Adds MbedTLS specific options for the parser.""" 366 367 self.parser.add_argument( 368 '--cryptofile', '-c', 369 help="""Crypto file to read (and modify if requested). Default: {}.""" 370 .format(CryptoConfigFile.default_path)) 371 372 self.add_adapter( 373 'baremetal', baremetal_adapter, 374 """Like full, but exclude features that require platform features 375 such as file input-output. 376 """) 377 self.add_adapter( 378 'baremetal_size', baremetal_size_adapter, 379 """Like baremetal, but exclude debugging features. Useful for code size measurements. 380 """) 381 self.add_adapter( 382 'full', full_adapter, 383 """Uncomment most features. 384 Exclude alternative implementations and platform support options, as well as 385 some options that are awkward to test. 386 """) 387 self.add_adapter( 388 'full_no_deprecated', no_deprecated_adapter(full_adapter), 389 """Uncomment most non-deprecated features. 390 Like "full", but without deprecated features. 391 """) 392 self.add_adapter( 393 'full_no_platform', no_platform_adapter(full_adapter), 394 """Uncomment most non-platform features. Like "full", but without platform features. 395 """) 396 self.add_adapter( 397 'realfull', realfull_adapter, 398 """Uncomment all boolean #defines. 399 Suitable for generating documentation, but not for building. 400 """) 401 self.add_adapter( 402 'crypto', crypto_adapter(None), 403 """Only include crypto features. Exclude X.509 and TLS.""") 404 self.add_adapter( 405 'crypto_baremetal', crypto_adapter(baremetal_adapter), 406 """Like baremetal, but with only crypto features, excluding X.509 and TLS.""") 407 self.add_adapter( 408 'crypto_full', crypto_adapter(full_adapter), 409 """Like full, but with only crypto features, excluding X.509 and TLS.""") 410 411 412 if __name__ == '__main__': 413 sys.exit(MbedTLSConfigTool().main())