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