1a8e1175bSopenharmony_ci#!/usr/bin/env python3 2a8e1175bSopenharmony_ci 3a8e1175bSopenharmony_ci"""Mbed TLS configuration file manipulation library and tool 4a8e1175bSopenharmony_ci 5a8e1175bSopenharmony_ciBasic usage, to read the Mbed TLS configuration: 6a8e1175bSopenharmony_ci config = ConfigFile() 7a8e1175bSopenharmony_ci if 'MBEDTLS_RSA_C' in config: print('RSA is enabled') 8a8e1175bSopenharmony_ci""" 9a8e1175bSopenharmony_ci 10a8e1175bSopenharmony_ci# Note that as long as Mbed TLS 2.28 LTS is maintained, the version of 11a8e1175bSopenharmony_ci# this script in the mbedtls-2.28 branch must remain compatible with 12a8e1175bSopenharmony_ci# Python 3.4. The version in development may only use more recent features 13a8e1175bSopenharmony_ci# in parts that are not backported to 2.28. 14a8e1175bSopenharmony_ci 15a8e1175bSopenharmony_ci## Copyright The Mbed TLS Contributors 16a8e1175bSopenharmony_ci## SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later 17a8e1175bSopenharmony_ci## 18a8e1175bSopenharmony_ci 19a8e1175bSopenharmony_ciimport os 20a8e1175bSopenharmony_ciimport re 21a8e1175bSopenharmony_ci 22a8e1175bSopenharmony_ciclass Setting: 23a8e1175bSopenharmony_ci """Representation of one Mbed TLS mbedtls_config.h setting. 24a8e1175bSopenharmony_ci 25a8e1175bSopenharmony_ci Fields: 26a8e1175bSopenharmony_ci * name: the symbol name ('MBEDTLS_xxx'). 27a8e1175bSopenharmony_ci * value: the value of the macro. The empty string for a plain #define 28a8e1175bSopenharmony_ci with no value. 29a8e1175bSopenharmony_ci * active: True if name is defined, False if a #define for name is 30a8e1175bSopenharmony_ci present in mbedtls_config.h but commented out. 31a8e1175bSopenharmony_ci * section: the name of the section that contains this symbol. 32a8e1175bSopenharmony_ci """ 33a8e1175bSopenharmony_ci # pylint: disable=too-few-public-methods 34a8e1175bSopenharmony_ci def __init__(self, active, name, value='', section=None): 35a8e1175bSopenharmony_ci self.active = active 36a8e1175bSopenharmony_ci self.name = name 37a8e1175bSopenharmony_ci self.value = value 38a8e1175bSopenharmony_ci self.section = section 39a8e1175bSopenharmony_ci 40a8e1175bSopenharmony_ciclass Config: 41a8e1175bSopenharmony_ci """Representation of the Mbed TLS configuration. 42a8e1175bSopenharmony_ci 43a8e1175bSopenharmony_ci In the documentation of this class, a symbol is said to be *active* 44a8e1175bSopenharmony_ci if there is a #define for it that is not commented out, and *known* 45a8e1175bSopenharmony_ci if there is a #define for it whether commented out or not. 46a8e1175bSopenharmony_ci 47a8e1175bSopenharmony_ci This class supports the following protocols: 48a8e1175bSopenharmony_ci * `name in config` is `True` if the symbol `name` is active, `False` 49a8e1175bSopenharmony_ci otherwise (whether `name` is inactive or not known). 50a8e1175bSopenharmony_ci * `config[name]` is the value of the macro `name`. If `name` is inactive, 51a8e1175bSopenharmony_ci raise `KeyError` (even if `name` is known). 52a8e1175bSopenharmony_ci * `config[name] = value` sets the value associated to `name`. `name` 53a8e1175bSopenharmony_ci must be known, but does not need to be set. This does not cause 54a8e1175bSopenharmony_ci name to become set. 55a8e1175bSopenharmony_ci """ 56a8e1175bSopenharmony_ci 57a8e1175bSopenharmony_ci def __init__(self): 58a8e1175bSopenharmony_ci self.settings = {} 59a8e1175bSopenharmony_ci 60a8e1175bSopenharmony_ci def __contains__(self, name): 61a8e1175bSopenharmony_ci """True if the given symbol is active (i.e. set). 62a8e1175bSopenharmony_ci 63a8e1175bSopenharmony_ci False if the given symbol is not set, even if a definition 64a8e1175bSopenharmony_ci is present but commented out. 65a8e1175bSopenharmony_ci """ 66a8e1175bSopenharmony_ci return name in self.settings and self.settings[name].active 67a8e1175bSopenharmony_ci 68a8e1175bSopenharmony_ci def all(self, *names): 69a8e1175bSopenharmony_ci """True if all the elements of names are active (i.e. set).""" 70a8e1175bSopenharmony_ci return all(self.__contains__(name) for name in names) 71a8e1175bSopenharmony_ci 72a8e1175bSopenharmony_ci def any(self, *names): 73a8e1175bSopenharmony_ci """True if at least one symbol in names are active (i.e. set).""" 74a8e1175bSopenharmony_ci return any(self.__contains__(name) for name in names) 75a8e1175bSopenharmony_ci 76a8e1175bSopenharmony_ci def known(self, name): 77a8e1175bSopenharmony_ci """True if a #define for name is present, whether it's commented out or not.""" 78a8e1175bSopenharmony_ci return name in self.settings 79a8e1175bSopenharmony_ci 80a8e1175bSopenharmony_ci def __getitem__(self, name): 81a8e1175bSopenharmony_ci """Get the value of name, i.e. what the preprocessor symbol expands to. 82a8e1175bSopenharmony_ci 83a8e1175bSopenharmony_ci If name is not known, raise KeyError. name does not need to be active. 84a8e1175bSopenharmony_ci """ 85a8e1175bSopenharmony_ci return self.settings[name].value 86a8e1175bSopenharmony_ci 87a8e1175bSopenharmony_ci def get(self, name, default=None): 88a8e1175bSopenharmony_ci """Get the value of name. If name is inactive (not set), return default. 89a8e1175bSopenharmony_ci 90a8e1175bSopenharmony_ci If a #define for name is present and not commented out, return 91a8e1175bSopenharmony_ci its expansion, even if this is the empty string. 92a8e1175bSopenharmony_ci 93a8e1175bSopenharmony_ci If a #define for name is present but commented out, return default. 94a8e1175bSopenharmony_ci """ 95a8e1175bSopenharmony_ci if name in self.settings: 96a8e1175bSopenharmony_ci return self.settings[name].value 97a8e1175bSopenharmony_ci else: 98a8e1175bSopenharmony_ci return default 99a8e1175bSopenharmony_ci 100a8e1175bSopenharmony_ci def __setitem__(self, name, value): 101a8e1175bSopenharmony_ci """If name is known, set its value. 102a8e1175bSopenharmony_ci 103a8e1175bSopenharmony_ci If name is not known, raise KeyError. 104a8e1175bSopenharmony_ci """ 105a8e1175bSopenharmony_ci self.settings[name].value = value 106a8e1175bSopenharmony_ci 107a8e1175bSopenharmony_ci def set(self, name, value=None): 108a8e1175bSopenharmony_ci """Set name to the given value and make it active. 109a8e1175bSopenharmony_ci 110a8e1175bSopenharmony_ci If value is None and name is already known, don't change its value. 111a8e1175bSopenharmony_ci If value is None and name is not known, set its value to the empty 112a8e1175bSopenharmony_ci string. 113a8e1175bSopenharmony_ci """ 114a8e1175bSopenharmony_ci if name in self.settings: 115a8e1175bSopenharmony_ci if value is not None: 116a8e1175bSopenharmony_ci self.settings[name].value = value 117a8e1175bSopenharmony_ci self.settings[name].active = True 118a8e1175bSopenharmony_ci else: 119a8e1175bSopenharmony_ci self.settings[name] = Setting(True, name, value=value) 120a8e1175bSopenharmony_ci 121a8e1175bSopenharmony_ci def unset(self, name): 122a8e1175bSopenharmony_ci """Make name unset (inactive). 123a8e1175bSopenharmony_ci 124a8e1175bSopenharmony_ci name remains known if it was known before. 125a8e1175bSopenharmony_ci """ 126a8e1175bSopenharmony_ci if name not in self.settings: 127a8e1175bSopenharmony_ci return 128a8e1175bSopenharmony_ci self.settings[name].active = False 129a8e1175bSopenharmony_ci 130a8e1175bSopenharmony_ci def adapt(self, adapter): 131a8e1175bSopenharmony_ci """Run adapter on each known symbol and (de)activate it accordingly. 132a8e1175bSopenharmony_ci 133a8e1175bSopenharmony_ci `adapter` must be a function that returns a boolean. It is called as 134a8e1175bSopenharmony_ci `adapter(name, active, section)` for each setting, where `active` is 135a8e1175bSopenharmony_ci `True` if `name` is set and `False` if `name` is known but unset, 136a8e1175bSopenharmony_ci and `section` is the name of the section containing `name`. If 137a8e1175bSopenharmony_ci `adapter` returns `True`, then set `name` (i.e. make it active), 138a8e1175bSopenharmony_ci otherwise unset `name` (i.e. make it known but inactive). 139a8e1175bSopenharmony_ci """ 140a8e1175bSopenharmony_ci for setting in self.settings.values(): 141a8e1175bSopenharmony_ci setting.active = adapter(setting.name, setting.active, 142a8e1175bSopenharmony_ci setting.section) 143a8e1175bSopenharmony_ci 144a8e1175bSopenharmony_ci def change_matching(self, regexs, enable): 145a8e1175bSopenharmony_ci """Change all symbols matching one of the regexs to the desired state.""" 146a8e1175bSopenharmony_ci if not regexs: 147a8e1175bSopenharmony_ci return 148a8e1175bSopenharmony_ci regex = re.compile('|'.join(regexs)) 149a8e1175bSopenharmony_ci for setting in self.settings.values(): 150a8e1175bSopenharmony_ci if regex.search(setting.name): 151a8e1175bSopenharmony_ci setting.active = enable 152a8e1175bSopenharmony_ci 153a8e1175bSopenharmony_cidef is_full_section(section): 154a8e1175bSopenharmony_ci """Is this section affected by "config.py full" and friends?""" 155a8e1175bSopenharmony_ci return section.endswith('support') or section.endswith('modules') 156a8e1175bSopenharmony_ci 157a8e1175bSopenharmony_cidef realfull_adapter(_name, active, section): 158a8e1175bSopenharmony_ci """Activate all symbols found in the global and boolean feature sections. 159a8e1175bSopenharmony_ci 160a8e1175bSopenharmony_ci This is intended for building the documentation, including the 161a8e1175bSopenharmony_ci documentation of settings that are activated by defining an optional 162a8e1175bSopenharmony_ci preprocessor macro. 163a8e1175bSopenharmony_ci 164a8e1175bSopenharmony_ci Do not activate definitions in the section containing symbols that are 165a8e1175bSopenharmony_ci supposed to be defined and documented in their own module. 166a8e1175bSopenharmony_ci """ 167a8e1175bSopenharmony_ci if section == 'Module configuration options': 168a8e1175bSopenharmony_ci return active 169a8e1175bSopenharmony_ci return True 170a8e1175bSopenharmony_ci 171a8e1175bSopenharmony_ci# The goal of the full configuration is to have everything that can be tested 172a8e1175bSopenharmony_ci# together. This includes deprecated or insecure options. It excludes: 173a8e1175bSopenharmony_ci# * Options that require additional build dependencies or unusual hardware. 174a8e1175bSopenharmony_ci# * Options that make testing less effective. 175a8e1175bSopenharmony_ci# * Options that are incompatible with other options, or more generally that 176a8e1175bSopenharmony_ci# interact with other parts of the code in such a way that a bulk enabling 177a8e1175bSopenharmony_ci# is not a good way to test them. 178a8e1175bSopenharmony_ci# * Options that remove features. 179a8e1175bSopenharmony_ciEXCLUDE_FROM_FULL = frozenset([ 180a8e1175bSopenharmony_ci #pylint: disable=line-too-long 181a8e1175bSopenharmony_ci 'MBEDTLS_AES_ONLY_128_BIT_KEY_LENGTH', # interacts with CTR_DRBG_128_BIT_KEY 182a8e1175bSopenharmony_ci 'MBEDTLS_AES_USE_HARDWARE_ONLY', # hardware dependency 183a8e1175bSopenharmony_ci 'MBEDTLS_BLOCK_CIPHER_NO_DECRYPT', # incompatible with ECB in PSA, CBC/XTS/NIST_KW/DES 184a8e1175bSopenharmony_ci 'MBEDTLS_CTR_DRBG_USE_128_BIT_KEY', # interacts with ENTROPY_FORCE_SHA256 185a8e1175bSopenharmony_ci 'MBEDTLS_DEPRECATED_REMOVED', # conflicts with deprecated options 186a8e1175bSopenharmony_ci 'MBEDTLS_DEPRECATED_WARNING', # conflicts with deprecated options 187a8e1175bSopenharmony_ci 'MBEDTLS_ECDH_VARIANT_EVEREST_ENABLED', # influences the use of ECDH in TLS 188a8e1175bSopenharmony_ci 'MBEDTLS_ECP_NO_FALLBACK', # removes internal ECP implementation 189a8e1175bSopenharmony_ci 'MBEDTLS_ECP_WITH_MPI_UINT', # disables the default ECP and is experimental 190a8e1175bSopenharmony_ci 'MBEDTLS_ENTROPY_FORCE_SHA256', # interacts with CTR_DRBG_128_BIT_KEY 191a8e1175bSopenharmony_ci 'MBEDTLS_HAVE_SSE2', # hardware dependency 192a8e1175bSopenharmony_ci 'MBEDTLS_MEMORY_BACKTRACE', # depends on MEMORY_BUFFER_ALLOC_C 193a8e1175bSopenharmony_ci 'MBEDTLS_MEMORY_BUFFER_ALLOC_C', # makes sanitizers (e.g. ASan) less effective 194a8e1175bSopenharmony_ci 'MBEDTLS_MEMORY_DEBUG', # depends on MEMORY_BUFFER_ALLOC_C 195a8e1175bSopenharmony_ci 'MBEDTLS_NO_64BIT_MULTIPLICATION', # influences anything that uses bignum 196a8e1175bSopenharmony_ci 'MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES', # removes a feature 197a8e1175bSopenharmony_ci 'MBEDTLS_NO_PLATFORM_ENTROPY', # removes a feature 198a8e1175bSopenharmony_ci 'MBEDTLS_NO_UDBL_DIVISION', # influences anything that uses bignum 199a8e1175bSopenharmony_ci 'MBEDTLS_PSA_P256M_DRIVER_ENABLED', # influences SECP256R1 KeyGen/ECDH/ECDSA 200a8e1175bSopenharmony_ci 'MBEDTLS_PLATFORM_NO_STD_FUNCTIONS', # removes a feature 201a8e1175bSopenharmony_ci 'MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS', # removes a feature 202a8e1175bSopenharmony_ci 'MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG', # behavior change + build dependency 203a8e1175bSopenharmony_ci 'MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER', # incompatible with USE_PSA_CRYPTO 204a8e1175bSopenharmony_ci 'MBEDTLS_PSA_CRYPTO_SPM', # platform dependency (PSA SPM) 205a8e1175bSopenharmony_ci 'MBEDTLS_PSA_INJECT_ENTROPY', # conflicts with platform entropy sources 206a8e1175bSopenharmony_ci 'MBEDTLS_RSA_NO_CRT', # influences the use of RSA in X.509 and TLS 207a8e1175bSopenharmony_ci 'MBEDTLS_SHA256_USE_A64_CRYPTO_ONLY', # interacts with *_USE_A64_CRYPTO_IF_PRESENT 208a8e1175bSopenharmony_ci 'MBEDTLS_SHA256_USE_ARMV8_A_CRYPTO_ONLY', # interacts with *_USE_ARMV8_A_CRYPTO_IF_PRESENT 209a8e1175bSopenharmony_ci 'MBEDTLS_SHA512_USE_A64_CRYPTO_ONLY', # interacts with *_USE_A64_CRYPTO_IF_PRESENT 210a8e1175bSopenharmony_ci 'MBEDTLS_SHA256_USE_A64_CRYPTO_IF_PRESENT', # setting *_USE_ARMV8_A_CRYPTO is sufficient 211a8e1175bSopenharmony_ci 'MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN', # build dependency (clang+memsan) 212a8e1175bSopenharmony_ci 'MBEDTLS_TEST_CONSTANT_FLOW_VALGRIND', # build dependency (valgrind headers) 213a8e1175bSopenharmony_ci 'MBEDTLS_X509_REMOVE_INFO', # removes a feature 214a8e1175bSopenharmony_ci]) 215a8e1175bSopenharmony_ci 216a8e1175bSopenharmony_cidef is_seamless_alt(name): 217a8e1175bSopenharmony_ci """Whether the xxx_ALT symbol should be included in the full configuration. 218a8e1175bSopenharmony_ci 219a8e1175bSopenharmony_ci Include alternative implementations of platform functions, which are 220a8e1175bSopenharmony_ci configurable function pointers that default to the built-in function. 221a8e1175bSopenharmony_ci This way we test that the function pointers exist and build correctly 222a8e1175bSopenharmony_ci without changing the behavior, and tests can verify that the function 223a8e1175bSopenharmony_ci pointers are used by modifying those pointers. 224a8e1175bSopenharmony_ci 225a8e1175bSopenharmony_ci Exclude alternative implementations of library functions since they require 226a8e1175bSopenharmony_ci an implementation of the relevant functions and an xxx_alt.h header. 227a8e1175bSopenharmony_ci """ 228a8e1175bSopenharmony_ci if name in ( 229a8e1175bSopenharmony_ci 'MBEDTLS_PLATFORM_GMTIME_R_ALT', 230a8e1175bSopenharmony_ci 'MBEDTLS_PLATFORM_SETUP_TEARDOWN_ALT', 231a8e1175bSopenharmony_ci 'MBEDTLS_PLATFORM_MS_TIME_ALT', 232a8e1175bSopenharmony_ci 'MBEDTLS_PLATFORM_ZEROIZE_ALT', 233a8e1175bSopenharmony_ci ): 234a8e1175bSopenharmony_ci # Similar to non-platform xxx_ALT, requires platform_alt.h 235a8e1175bSopenharmony_ci return False 236a8e1175bSopenharmony_ci return name.startswith('MBEDTLS_PLATFORM_') 237a8e1175bSopenharmony_ci 238a8e1175bSopenharmony_cidef include_in_full(name): 239a8e1175bSopenharmony_ci """Rules for symbols in the "full" configuration.""" 240a8e1175bSopenharmony_ci if name in EXCLUDE_FROM_FULL: 241a8e1175bSopenharmony_ci return False 242a8e1175bSopenharmony_ci if name.endswith('_ALT'): 243a8e1175bSopenharmony_ci return is_seamless_alt(name) 244a8e1175bSopenharmony_ci return True 245a8e1175bSopenharmony_ci 246a8e1175bSopenharmony_cidef full_adapter(name, active, section): 247a8e1175bSopenharmony_ci """Config adapter for "full".""" 248a8e1175bSopenharmony_ci if not is_full_section(section): 249a8e1175bSopenharmony_ci return active 250a8e1175bSopenharmony_ci return include_in_full(name) 251a8e1175bSopenharmony_ci 252a8e1175bSopenharmony_ci# The baremetal configuration excludes options that require a library or 253a8e1175bSopenharmony_ci# operating system feature that is typically not present on bare metal 254a8e1175bSopenharmony_ci# systems. Features that are excluded from "full" won't be in "baremetal" 255a8e1175bSopenharmony_ci# either (unless explicitly turned on in baremetal_adapter) so they don't 256a8e1175bSopenharmony_ci# need to be repeated here. 257a8e1175bSopenharmony_ciEXCLUDE_FROM_BAREMETAL = frozenset([ 258a8e1175bSopenharmony_ci #pylint: disable=line-too-long 259a8e1175bSopenharmony_ci 'MBEDTLS_ENTROPY_NV_SEED', # requires a filesystem and FS_IO or alternate NV seed hooks 260a8e1175bSopenharmony_ci 'MBEDTLS_FS_IO', # requires a filesystem 261a8e1175bSopenharmony_ci 'MBEDTLS_HAVE_TIME', # requires a clock 262a8e1175bSopenharmony_ci 'MBEDTLS_HAVE_TIME_DATE', # requires a clock 263a8e1175bSopenharmony_ci 'MBEDTLS_NET_C', # requires POSIX-like networking 264a8e1175bSopenharmony_ci 'MBEDTLS_PLATFORM_FPRINTF_ALT', # requires FILE* from stdio.h 265a8e1175bSopenharmony_ci 'MBEDTLS_PLATFORM_NV_SEED_ALT', # requires a filesystem and ENTROPY_NV_SEED 266a8e1175bSopenharmony_ci 'MBEDTLS_PLATFORM_TIME_ALT', # requires a clock and HAVE_TIME 267a8e1175bSopenharmony_ci 'MBEDTLS_PSA_CRYPTO_SE_C', # requires a filesystem and PSA_CRYPTO_STORAGE_C 268a8e1175bSopenharmony_ci 'MBEDTLS_PSA_CRYPTO_STORAGE_C', # requires a filesystem 269a8e1175bSopenharmony_ci 'MBEDTLS_PSA_ITS_FILE_C', # requires a filesystem 270a8e1175bSopenharmony_ci 'MBEDTLS_THREADING_C', # requires a threading interface 271a8e1175bSopenharmony_ci 'MBEDTLS_THREADING_PTHREAD', # requires pthread 272a8e1175bSopenharmony_ci 'MBEDTLS_TIMING_C', # requires a clock 273a8e1175bSopenharmony_ci 'MBEDTLS_SHA256_USE_A64_CRYPTO_IF_PRESENT', # requires an OS for runtime-detection 274a8e1175bSopenharmony_ci 'MBEDTLS_SHA256_USE_ARMV8_A_CRYPTO_IF_PRESENT', # requires an OS for runtime-detection 275a8e1175bSopenharmony_ci 'MBEDTLS_SHA512_USE_A64_CRYPTO_IF_PRESENT', # requires an OS for runtime-detection 276a8e1175bSopenharmony_ci]) 277a8e1175bSopenharmony_ci 278a8e1175bSopenharmony_cidef keep_in_baremetal(name): 279a8e1175bSopenharmony_ci """Rules for symbols in the "baremetal" configuration.""" 280a8e1175bSopenharmony_ci if name in EXCLUDE_FROM_BAREMETAL: 281a8e1175bSopenharmony_ci return False 282a8e1175bSopenharmony_ci return True 283a8e1175bSopenharmony_ci 284a8e1175bSopenharmony_cidef baremetal_adapter(name, active, section): 285a8e1175bSopenharmony_ci """Config adapter for "baremetal".""" 286a8e1175bSopenharmony_ci if not is_full_section(section): 287a8e1175bSopenharmony_ci return active 288a8e1175bSopenharmony_ci if name == 'MBEDTLS_NO_PLATFORM_ENTROPY': 289a8e1175bSopenharmony_ci # No OS-provided entropy source 290a8e1175bSopenharmony_ci return True 291a8e1175bSopenharmony_ci return include_in_full(name) and keep_in_baremetal(name) 292a8e1175bSopenharmony_ci 293a8e1175bSopenharmony_ci# This set contains options that are mostly for debugging or test purposes, 294a8e1175bSopenharmony_ci# and therefore should be excluded when doing code size measurements. 295a8e1175bSopenharmony_ci# Options that are their own module (such as MBEDTLS_ERROR_C) are not listed 296a8e1175bSopenharmony_ci# and therefore will be included when doing code size measurements. 297a8e1175bSopenharmony_ciEXCLUDE_FOR_SIZE = frozenset([ 298a8e1175bSopenharmony_ci 'MBEDTLS_DEBUG_C', # large code size increase in TLS 299a8e1175bSopenharmony_ci 'MBEDTLS_SELF_TEST', # increases the size of many modules 300a8e1175bSopenharmony_ci 'MBEDTLS_TEST_HOOKS', # only useful with the hosted test framework, increases code size 301a8e1175bSopenharmony_ci]) 302a8e1175bSopenharmony_ci 303a8e1175bSopenharmony_cidef baremetal_size_adapter(name, active, section): 304a8e1175bSopenharmony_ci if name in EXCLUDE_FOR_SIZE: 305a8e1175bSopenharmony_ci return False 306a8e1175bSopenharmony_ci return baremetal_adapter(name, active, section) 307a8e1175bSopenharmony_ci 308a8e1175bSopenharmony_cidef include_in_crypto(name): 309a8e1175bSopenharmony_ci """Rules for symbols in a crypto configuration.""" 310a8e1175bSopenharmony_ci if name.startswith('MBEDTLS_X509_') or \ 311a8e1175bSopenharmony_ci name.startswith('MBEDTLS_SSL_') or \ 312a8e1175bSopenharmony_ci name.startswith('MBEDTLS_KEY_EXCHANGE_'): 313a8e1175bSopenharmony_ci return False 314a8e1175bSopenharmony_ci if name in [ 315a8e1175bSopenharmony_ci 'MBEDTLS_DEBUG_C', # part of libmbedtls 316a8e1175bSopenharmony_ci 'MBEDTLS_NET_C', # part of libmbedtls 317a8e1175bSopenharmony_ci 'MBEDTLS_PKCS7_C', # part of libmbedx509 318a8e1175bSopenharmony_ci ]: 319a8e1175bSopenharmony_ci return False 320a8e1175bSopenharmony_ci return True 321a8e1175bSopenharmony_ci 322a8e1175bSopenharmony_cidef crypto_adapter(adapter): 323a8e1175bSopenharmony_ci """Modify an adapter to disable non-crypto symbols. 324a8e1175bSopenharmony_ci 325a8e1175bSopenharmony_ci ``crypto_adapter(adapter)(name, active, section)`` is like 326a8e1175bSopenharmony_ci ``adapter(name, active, section)``, but unsets all X.509 and TLS symbols. 327a8e1175bSopenharmony_ci """ 328a8e1175bSopenharmony_ci def continuation(name, active, section): 329a8e1175bSopenharmony_ci if not include_in_crypto(name): 330a8e1175bSopenharmony_ci return False 331a8e1175bSopenharmony_ci if adapter is None: 332a8e1175bSopenharmony_ci return active 333a8e1175bSopenharmony_ci return adapter(name, active, section) 334a8e1175bSopenharmony_ci return continuation 335a8e1175bSopenharmony_ci 336a8e1175bSopenharmony_ciDEPRECATED = frozenset([ 337a8e1175bSopenharmony_ci 'MBEDTLS_PSA_CRYPTO_SE_C', 338a8e1175bSopenharmony_ci]) 339a8e1175bSopenharmony_cidef no_deprecated_adapter(adapter): 340a8e1175bSopenharmony_ci """Modify an adapter to disable deprecated symbols. 341a8e1175bSopenharmony_ci 342a8e1175bSopenharmony_ci ``no_deprecated_adapter(adapter)(name, active, section)`` is like 343a8e1175bSopenharmony_ci ``adapter(name, active, section)``, but unsets all deprecated symbols 344a8e1175bSopenharmony_ci and sets ``MBEDTLS_DEPRECATED_REMOVED``. 345a8e1175bSopenharmony_ci """ 346a8e1175bSopenharmony_ci def continuation(name, active, section): 347a8e1175bSopenharmony_ci if name == 'MBEDTLS_DEPRECATED_REMOVED': 348a8e1175bSopenharmony_ci return True 349a8e1175bSopenharmony_ci if name in DEPRECATED: 350a8e1175bSopenharmony_ci return False 351a8e1175bSopenharmony_ci if adapter is None: 352a8e1175bSopenharmony_ci return active 353a8e1175bSopenharmony_ci return adapter(name, active, section) 354a8e1175bSopenharmony_ci return continuation 355a8e1175bSopenharmony_ci 356a8e1175bSopenharmony_cidef no_platform_adapter(adapter): 357a8e1175bSopenharmony_ci """Modify an adapter to disable platform symbols. 358a8e1175bSopenharmony_ci 359a8e1175bSopenharmony_ci ``no_platform_adapter(adapter)(name, active, section)`` is like 360a8e1175bSopenharmony_ci ``adapter(name, active, section)``, but unsets all platform symbols other 361a8e1175bSopenharmony_ci ``than MBEDTLS_PLATFORM_C. 362a8e1175bSopenharmony_ci """ 363a8e1175bSopenharmony_ci def continuation(name, active, section): 364a8e1175bSopenharmony_ci # Allow MBEDTLS_PLATFORM_C but remove all other platform symbols. 365a8e1175bSopenharmony_ci if name.startswith('MBEDTLS_PLATFORM_') and name != 'MBEDTLS_PLATFORM_C': 366a8e1175bSopenharmony_ci return False 367a8e1175bSopenharmony_ci if adapter is None: 368a8e1175bSopenharmony_ci return active 369a8e1175bSopenharmony_ci return adapter(name, active, section) 370a8e1175bSopenharmony_ci return continuation 371a8e1175bSopenharmony_ci 372a8e1175bSopenharmony_ciclass ConfigFile(Config): 373a8e1175bSopenharmony_ci """Representation of the Mbed TLS configuration read for a file. 374a8e1175bSopenharmony_ci 375a8e1175bSopenharmony_ci See the documentation of the `Config` class for methods to query 376a8e1175bSopenharmony_ci and modify the configuration. 377a8e1175bSopenharmony_ci """ 378a8e1175bSopenharmony_ci 379a8e1175bSopenharmony_ci _path_in_tree = 'include/mbedtls/mbedtls_config.h' 380a8e1175bSopenharmony_ci default_path = [_path_in_tree, 381a8e1175bSopenharmony_ci os.path.join(os.path.dirname(__file__), 382a8e1175bSopenharmony_ci os.pardir, 383a8e1175bSopenharmony_ci _path_in_tree), 384a8e1175bSopenharmony_ci os.path.join(os.path.dirname(os.path.abspath(os.path.dirname(__file__))), 385a8e1175bSopenharmony_ci _path_in_tree)] 386a8e1175bSopenharmony_ci 387a8e1175bSopenharmony_ci def __init__(self, filename=None): 388a8e1175bSopenharmony_ci """Read the Mbed TLS configuration file.""" 389a8e1175bSopenharmony_ci if filename is None: 390a8e1175bSopenharmony_ci for candidate in self.default_path: 391a8e1175bSopenharmony_ci if os.path.lexists(candidate): 392a8e1175bSopenharmony_ci filename = candidate 393a8e1175bSopenharmony_ci break 394a8e1175bSopenharmony_ci else: 395a8e1175bSopenharmony_ci raise Exception('Mbed TLS configuration file not found', 396a8e1175bSopenharmony_ci self.default_path) 397a8e1175bSopenharmony_ci super().__init__() 398a8e1175bSopenharmony_ci self.filename = filename 399a8e1175bSopenharmony_ci self.current_section = 'header' 400a8e1175bSopenharmony_ci with open(filename, 'r', encoding='utf-8') as file: 401a8e1175bSopenharmony_ci self.templates = [self._parse_line(line) for line in file] 402a8e1175bSopenharmony_ci self.current_section = None 403a8e1175bSopenharmony_ci 404a8e1175bSopenharmony_ci def set(self, name, value=None): 405a8e1175bSopenharmony_ci if name not in self.settings: 406a8e1175bSopenharmony_ci self.templates.append((name, '', '#define ' + name + ' ')) 407a8e1175bSopenharmony_ci super().set(name, value) 408a8e1175bSopenharmony_ci 409a8e1175bSopenharmony_ci _define_line_regexp = (r'(?P<indentation>\s*)' + 410a8e1175bSopenharmony_ci r'(?P<commented_out>(//\s*)?)' + 411a8e1175bSopenharmony_ci r'(?P<define>#\s*define\s+)' + 412a8e1175bSopenharmony_ci r'(?P<name>\w+)' + 413a8e1175bSopenharmony_ci r'(?P<arguments>(?:\((?:\w|\s|,)*\))?)' + 414a8e1175bSopenharmony_ci r'(?P<separator>\s*)' + 415a8e1175bSopenharmony_ci r'(?P<value>.*)') 416a8e1175bSopenharmony_ci _section_line_regexp = (r'\s*/?\*+\s*[\\@]name\s+SECTION:\s*' + 417a8e1175bSopenharmony_ci r'(?P<section>.*)[ */]*') 418a8e1175bSopenharmony_ci _config_line_regexp = re.compile(r'|'.join([_define_line_regexp, 419a8e1175bSopenharmony_ci _section_line_regexp])) 420a8e1175bSopenharmony_ci def _parse_line(self, line): 421a8e1175bSopenharmony_ci """Parse a line in mbedtls_config.h and return the corresponding template.""" 422a8e1175bSopenharmony_ci line = line.rstrip('\r\n') 423a8e1175bSopenharmony_ci m = re.match(self._config_line_regexp, line) 424a8e1175bSopenharmony_ci if m is None: 425a8e1175bSopenharmony_ci return line 426a8e1175bSopenharmony_ci elif m.group('section'): 427a8e1175bSopenharmony_ci self.current_section = m.group('section') 428a8e1175bSopenharmony_ci return line 429a8e1175bSopenharmony_ci else: 430a8e1175bSopenharmony_ci active = not m.group('commented_out') 431a8e1175bSopenharmony_ci name = m.group('name') 432a8e1175bSopenharmony_ci value = m.group('value') 433a8e1175bSopenharmony_ci template = (name, 434a8e1175bSopenharmony_ci m.group('indentation'), 435a8e1175bSopenharmony_ci m.group('define') + name + 436a8e1175bSopenharmony_ci m.group('arguments') + m.group('separator')) 437a8e1175bSopenharmony_ci self.settings[name] = Setting(active, name, value, 438a8e1175bSopenharmony_ci self.current_section) 439a8e1175bSopenharmony_ci return template 440a8e1175bSopenharmony_ci 441a8e1175bSopenharmony_ci def _format_template(self, name, indent, middle): 442a8e1175bSopenharmony_ci """Build a line for mbedtls_config.h for the given setting. 443a8e1175bSopenharmony_ci 444a8e1175bSopenharmony_ci The line has the form "<indent>#define <name> <value>" 445a8e1175bSopenharmony_ci where <middle> is "#define <name> ". 446a8e1175bSopenharmony_ci """ 447a8e1175bSopenharmony_ci setting = self.settings[name] 448a8e1175bSopenharmony_ci value = setting.value 449a8e1175bSopenharmony_ci if value is None: 450a8e1175bSopenharmony_ci value = '' 451a8e1175bSopenharmony_ci # Normally the whitespace to separate the symbol name from the 452a8e1175bSopenharmony_ci # value is part of middle, and there's no whitespace for a symbol 453a8e1175bSopenharmony_ci # with no value. But if a symbol has been changed from having a 454a8e1175bSopenharmony_ci # value to not having one, the whitespace is wrong, so fix it. 455a8e1175bSopenharmony_ci if value: 456a8e1175bSopenharmony_ci if middle[-1] not in '\t ': 457a8e1175bSopenharmony_ci middle += ' ' 458a8e1175bSopenharmony_ci else: 459a8e1175bSopenharmony_ci middle = middle.rstrip() 460a8e1175bSopenharmony_ci return ''.join([indent, 461a8e1175bSopenharmony_ci '' if setting.active else '//', 462a8e1175bSopenharmony_ci middle, 463a8e1175bSopenharmony_ci value]).rstrip() 464a8e1175bSopenharmony_ci 465a8e1175bSopenharmony_ci def write_to_stream(self, output): 466a8e1175bSopenharmony_ci """Write the whole configuration to output.""" 467a8e1175bSopenharmony_ci for template in self.templates: 468a8e1175bSopenharmony_ci if isinstance(template, str): 469a8e1175bSopenharmony_ci line = template 470a8e1175bSopenharmony_ci else: 471a8e1175bSopenharmony_ci line = self._format_template(*template) 472a8e1175bSopenharmony_ci output.write(line + '\n') 473a8e1175bSopenharmony_ci 474a8e1175bSopenharmony_ci def write(self, filename=None): 475a8e1175bSopenharmony_ci """Write the whole configuration to the file it was read from. 476a8e1175bSopenharmony_ci 477a8e1175bSopenharmony_ci If filename is specified, write to this file instead. 478a8e1175bSopenharmony_ci """ 479a8e1175bSopenharmony_ci if filename is None: 480a8e1175bSopenharmony_ci filename = self.filename 481a8e1175bSopenharmony_ci with open(filename, 'w', encoding='utf-8') as output: 482a8e1175bSopenharmony_ci self.write_to_stream(output) 483a8e1175bSopenharmony_ci 484a8e1175bSopenharmony_ciif __name__ == '__main__': 485a8e1175bSopenharmony_ci def main(): 486a8e1175bSopenharmony_ci """Command line mbedtls_config.h manipulation tool.""" 487a8e1175bSopenharmony_ci parser = argparse.ArgumentParser(description=""" 488a8e1175bSopenharmony_ci Mbed TLS configuration file manipulation tool. 489a8e1175bSopenharmony_ci """) 490a8e1175bSopenharmony_ci parser.add_argument('--file', '-f', 491a8e1175bSopenharmony_ci help="""File to read (and modify if requested). 492a8e1175bSopenharmony_ci Default: {}. 493a8e1175bSopenharmony_ci """.format(ConfigFile.default_path)) 494a8e1175bSopenharmony_ci parser.add_argument('--force', '-o', 495a8e1175bSopenharmony_ci action='store_true', 496a8e1175bSopenharmony_ci help="""For the set command, if SYMBOL is not 497a8e1175bSopenharmony_ci present, add a definition for it.""") 498a8e1175bSopenharmony_ci parser.add_argument('--write', '-w', metavar='FILE', 499a8e1175bSopenharmony_ci help="""File to write to instead of the input file.""") 500a8e1175bSopenharmony_ci subparsers = parser.add_subparsers(dest='command', 501a8e1175bSopenharmony_ci title='Commands') 502a8e1175bSopenharmony_ci parser_get = subparsers.add_parser('get', 503a8e1175bSopenharmony_ci help="""Find the value of SYMBOL 504a8e1175bSopenharmony_ci and print it. Exit with 505a8e1175bSopenharmony_ci status 0 if a #define for SYMBOL is 506a8e1175bSopenharmony_ci found, 1 otherwise. 507a8e1175bSopenharmony_ci """) 508a8e1175bSopenharmony_ci parser_get.add_argument('symbol', metavar='SYMBOL') 509a8e1175bSopenharmony_ci parser_set = subparsers.add_parser('set', 510a8e1175bSopenharmony_ci help="""Set SYMBOL to VALUE. 511a8e1175bSopenharmony_ci If VALUE is omitted, just uncomment 512a8e1175bSopenharmony_ci the #define for SYMBOL. 513a8e1175bSopenharmony_ci Error out of a line defining 514a8e1175bSopenharmony_ci SYMBOL (commented or not) is not 515a8e1175bSopenharmony_ci found, unless --force is passed. 516a8e1175bSopenharmony_ci """) 517a8e1175bSopenharmony_ci parser_set.add_argument('symbol', metavar='SYMBOL') 518a8e1175bSopenharmony_ci parser_set.add_argument('value', metavar='VALUE', nargs='?', 519a8e1175bSopenharmony_ci default='') 520a8e1175bSopenharmony_ci parser_set_all = subparsers.add_parser('set-all', 521a8e1175bSopenharmony_ci help="""Uncomment all #define 522a8e1175bSopenharmony_ci whose name contains a match for 523a8e1175bSopenharmony_ci REGEX.""") 524a8e1175bSopenharmony_ci parser_set_all.add_argument('regexs', metavar='REGEX', nargs='*') 525a8e1175bSopenharmony_ci parser_unset = subparsers.add_parser('unset', 526a8e1175bSopenharmony_ci help="""Comment out the #define 527a8e1175bSopenharmony_ci for SYMBOL. Do nothing if none 528a8e1175bSopenharmony_ci is present.""") 529a8e1175bSopenharmony_ci parser_unset.add_argument('symbol', metavar='SYMBOL') 530a8e1175bSopenharmony_ci parser_unset_all = subparsers.add_parser('unset-all', 531a8e1175bSopenharmony_ci help="""Comment out all #define 532a8e1175bSopenharmony_ci whose name contains a match for 533a8e1175bSopenharmony_ci REGEX.""") 534a8e1175bSopenharmony_ci parser_unset_all.add_argument('regexs', metavar='REGEX', nargs='*') 535a8e1175bSopenharmony_ci 536a8e1175bSopenharmony_ci def add_adapter(name, function, description): 537a8e1175bSopenharmony_ci subparser = subparsers.add_parser(name, help=description) 538a8e1175bSopenharmony_ci subparser.set_defaults(adapter=function) 539a8e1175bSopenharmony_ci add_adapter('baremetal', baremetal_adapter, 540a8e1175bSopenharmony_ci """Like full, but exclude features that require platform 541a8e1175bSopenharmony_ci features such as file input-output.""") 542a8e1175bSopenharmony_ci add_adapter('baremetal_size', baremetal_size_adapter, 543a8e1175bSopenharmony_ci """Like baremetal, but exclude debugging features. 544a8e1175bSopenharmony_ci Useful for code size measurements.""") 545a8e1175bSopenharmony_ci add_adapter('full', full_adapter, 546a8e1175bSopenharmony_ci """Uncomment most features. 547a8e1175bSopenharmony_ci Exclude alternative implementations and platform support 548a8e1175bSopenharmony_ci options, as well as some options that are awkward to test. 549a8e1175bSopenharmony_ci """) 550a8e1175bSopenharmony_ci add_adapter('full_no_deprecated', no_deprecated_adapter(full_adapter), 551a8e1175bSopenharmony_ci """Uncomment most non-deprecated features. 552a8e1175bSopenharmony_ci Like "full", but without deprecated features. 553a8e1175bSopenharmony_ci """) 554a8e1175bSopenharmony_ci add_adapter('full_no_platform', no_platform_adapter(full_adapter), 555a8e1175bSopenharmony_ci """Uncomment most non-platform features. 556a8e1175bSopenharmony_ci Like "full", but without platform features. 557a8e1175bSopenharmony_ci """) 558a8e1175bSopenharmony_ci add_adapter('realfull', realfull_adapter, 559a8e1175bSopenharmony_ci """Uncomment all boolean #defines. 560a8e1175bSopenharmony_ci Suitable for generating documentation, but not for building.""") 561a8e1175bSopenharmony_ci add_adapter('crypto', crypto_adapter(None), 562a8e1175bSopenharmony_ci """Only include crypto features. Exclude X.509 and TLS.""") 563a8e1175bSopenharmony_ci add_adapter('crypto_baremetal', crypto_adapter(baremetal_adapter), 564a8e1175bSopenharmony_ci """Like baremetal, but with only crypto features, 565a8e1175bSopenharmony_ci excluding X.509 and TLS.""") 566a8e1175bSopenharmony_ci add_adapter('crypto_full', crypto_adapter(full_adapter), 567a8e1175bSopenharmony_ci """Like full, but with only crypto features, 568a8e1175bSopenharmony_ci excluding X.509 and TLS.""") 569a8e1175bSopenharmony_ci 570a8e1175bSopenharmony_ci args = parser.parse_args() 571a8e1175bSopenharmony_ci config = ConfigFile(args.file) 572a8e1175bSopenharmony_ci if args.command is None: 573a8e1175bSopenharmony_ci parser.print_help() 574a8e1175bSopenharmony_ci return 1 575a8e1175bSopenharmony_ci elif args.command == 'get': 576a8e1175bSopenharmony_ci if args.symbol in config: 577a8e1175bSopenharmony_ci value = config[args.symbol] 578a8e1175bSopenharmony_ci if value: 579a8e1175bSopenharmony_ci sys.stdout.write(value + '\n') 580a8e1175bSopenharmony_ci return 0 if args.symbol in config else 1 581a8e1175bSopenharmony_ci elif args.command == 'set': 582a8e1175bSopenharmony_ci if not args.force and args.symbol not in config.settings: 583a8e1175bSopenharmony_ci sys.stderr.write("A #define for the symbol {} " 584a8e1175bSopenharmony_ci "was not found in {}\n" 585a8e1175bSopenharmony_ci .format(args.symbol, config.filename)) 586a8e1175bSopenharmony_ci return 1 587a8e1175bSopenharmony_ci config.set(args.symbol, value=args.value) 588a8e1175bSopenharmony_ci elif args.command == 'set-all': 589a8e1175bSopenharmony_ci config.change_matching(args.regexs, True) 590a8e1175bSopenharmony_ci elif args.command == 'unset': 591a8e1175bSopenharmony_ci config.unset(args.symbol) 592a8e1175bSopenharmony_ci elif args.command == 'unset-all': 593a8e1175bSopenharmony_ci config.change_matching(args.regexs, False) 594a8e1175bSopenharmony_ci else: 595a8e1175bSopenharmony_ci config.adapt(args.adapter) 596a8e1175bSopenharmony_ci config.write(args.write) 597a8e1175bSopenharmony_ci return 0 598a8e1175bSopenharmony_ci 599a8e1175bSopenharmony_ci # Import modules only used by main only if main is defined and called. 600a8e1175bSopenharmony_ci # pylint: disable=wrong-import-position 601a8e1175bSopenharmony_ci import argparse 602a8e1175bSopenharmony_ci import sys 603a8e1175bSopenharmony_ci sys.exit(main()) 604