quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

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())