Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backport 2.28: Report configuration settings in the outcome file #9316

Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions scripts/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,7 @@ def __init__(self, filename=None):
self.default_path)
super().__init__()
self.filename = filename
self.inclusion_guard = None
self.current_section = 'header'
with open(filename, 'r', encoding='utf-8') as file:
self.templates = [self._parse_line(line) for line in file]
Expand All @@ -406,9 +407,11 @@ def set(self, name, value=None):
r'(?P<arguments>(?:\((?:\w|\s|,)*\))?)' +
r'(?P<separator>\s*)' +
r'(?P<value>.*)')
_ifndef_line_regexp = r'#ifndef (?P<inclusion_guard>\w+)'
_section_line_regexp = (r'\s*/?\*+\s*[\\@]name\s+SECTION:\s*' +
r'(?P<section>.*)[ */]*')
_config_line_regexp = re.compile(r'|'.join([_define_line_regexp,
_ifndef_line_regexp,
_section_line_regexp]))
def _parse_line(self, line):
"""Parse a line in config.h and return the corresponding template."""
Expand All @@ -419,10 +422,16 @@ def _parse_line(self, line):
elif m.group('section'):
self.current_section = m.group('section')
return line
elif m.group('inclusion_guard') and self.inclusion_guard is None:
self.inclusion_guard = m.group('inclusion_guard')
return line
else:
active = not m.group('commented_out')
name = m.group('name')
value = m.group('value')
if name == self.inclusion_guard and value == '':
# The file double-inclusion guard is not an option.
return line
template = (name,
m.group('indentation'),
m.group('define') + name +
Expand Down
14 changes: 7 additions & 7 deletions tests/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@

*.log
/test_suite*
data_files/mpi_write
data_files/hmac_drbg_seed
data_files/ctr_drbg_seed
data_files/entropy_seed
/data_files/mpi_write
/data_files/hmac_drbg_seed
/data_files/ctr_drbg_seed
/data_files/entropy_seed

include/test/instrument_record_status.h
/include/test/instrument_record_status.h

src/libmbed*
/src/libmbed*

libtestdriver1/*
/libtestdriver1/*
1 change: 1 addition & 0 deletions tests/scripts/check-generated-files.sh
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,5 @@ check scripts/generate_visualc_files.pl visualc/VS2010
check scripts/generate_psa_constants.py programs/psa/psa_constant_names_generated.c
check tests/scripts/generate_psa_wrappers.py tests/include/test/psa_test_wrappers.h tests/src/psa_test_wrappers.c
check tests/scripts/generate_bignum_tests.py $(tests/scripts/generate_bignum_tests.py --list)
check tests/scripts/generate_config_tests.py $(tests/scripts/generate_config_tests.py --list)
check tests/scripts/generate_psa_tests.py $(tests/scripts/generate_psa_tests.py --list)
163 changes: 163 additions & 0 deletions tests/scripts/generate_config_tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
#!/usr/bin/env python3
"""Generate test data for configuration reporting.
"""

# Copyright The Mbed TLS Contributors
# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later

import re
import sys
from typing import Iterable, Iterator, List, Optional, Tuple

import scripts_path # pylint: disable=unused-import
import config
from mbedtls_dev import test_case
from mbedtls_dev import test_data_generation


def single_setting_case(setting: config.Setting, when_on: bool,
dependencies: List[str],
note: Optional[str]) -> test_case.TestCase:
"""Construct a test case for a boolean setting.

This test case passes if the setting and its dependencies are enabled,
and is skipped otherwise.

* setting: the setting to be tested.
* when_on: True to test with the setting enabled, or False to test
with the setting disabled.
* dependencies: extra dependencies for the test case.
* note: a note to add after the setting name in the test description.
This is generally a summary of dependencies, and is generally empty
if the given setting is only tested once.
"""
base = setting.name if when_on else '!' + setting.name
tc = test_case.TestCase()
tc.set_function('pass')
description_suffix = ' (' + note + ')' if note else ''
tc.set_description('Config: ' + base + description_suffix)
tc.set_dependencies([base] + dependencies)
return tc


# If foo is a setting that is only meaningful when bar is enabled, set
# SIMPLE_DEPENDENCIES[foo]=bar. More generally, bar can be a colon-separated
# list of settings, meaning that all the settings must be enabled. Each setting
# in bar can be prefixed with '!' to negate it. This is the same syntax as a
# depends_on directive in test data.
# See also `dependencies_of_settting`.
SIMPLE_DEPENDENCIES = {
'MBEDTLS_AESNI_C': 'MBEDTLS_AES_C',
'MBEDTLS_ERROR_STRERROR_DUMMY': '!MBEDTLS_ERROR_C',
'MBEDTLS_GENPRIME': 'MBEDTLS_RSA_C',
'MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES': 'MBEDTLS_ENTROPY_C',
'MBEDTLS_NO_PLATFORM_ENTROPY': 'MBEDTLS_ENTROPY_C',
'MBEDTLS_PKCS1_V15': 'MBEDTLS_RSA_C',
'MBEDTLS_PKCS1_V21': 'MBEDTLS_RSA_C',
'MBEDTLS_PSA_CRYPTO_CLIENT': '!MBEDTLS_PSA_CRYPTO_C',
'MBEDTLS_PSA_INJECT_ENTROPY': 'MBEDTLS_PSA_CRYPTO_C',
'MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS': 'MBEDTLS_PSA_CRYPTO_C',
}

def dependencies_of_setting(cfg: config.Config,
setting: config.Setting) -> Optional[str]:
"""Return dependencies without which a setting is not meaningful.

The dependencies of a setting express when a setting can be enabled and
is relevant. For example, if ``check_config.h`` errors out when
``defined(FOO) && !defined(BAR)``, then ``BAR`` is a dependency of ``FOO``.
If ``FOO`` has no effect when ``CORGE`` is disabled, then ``CORGE``
is a dependency of ``FOO``.

The return value can be a colon-separated list of settings, if the setting
is only meaningful when all of these settings are enabled. Each setting can
be negated by prefixing them with '!'. This is the same syntax as a
depends_on directive in test data.
"""
#pylint: disable=too-many-return-statements
name = setting.name
if name in SIMPLE_DEPENDENCIES:
return SIMPLE_DEPENDENCIES[name]
if name.startswith('MBEDTLS_') and not name.endswith('_C'):
if name.startswith('MBEDTLS_CIPHER_PADDING_'):
return 'MBEDTLS_CIPHER_C:MBEDTLS_CIPHER_MODE_CBC'
if name.startswith('MBEDTLS_PK_PARSE_EC_'):
return 'MBEDTLS_PK_C:MBEDTLS_PK_HAVE_ECC_KEYS'
# For TLS settings, insist on having them once off and once on in
# a configuration where both client support and server support are
# enabled. The settings are also meaningful when only one side is
# enabled, but there isn't much point in having separate records
# for client-side and server-side, so we keep things simple.
# Requiring both sides to be enabled also means we know we'll run
# tests that only run Mbed TLS against itself, which only run in
# configurations with both sides enabled.
if name.startswith('MBEDTLS_SSL_TLS1_3_'):
return 'MBEDTLS_SSL_CLI_C:MBEDTLS_SSL_SRV_C:MBEDTLS_SSL_PROTO_TLS1_3'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return 'MBEDTLS_SSL_CLI_C:MBEDTLS_SSL_SRV_C:MBEDTLS_SSL_PROTO_TLS1_3'
return 'MBEDTLS_SSL_CLI_C:MBEDTLS_SSL_SRV_C:MBEDTLS_SSL_PROTO_TLS1_3_EXPERIMENTAL'

TLS 1.3 is actually not supported in 2.28 thus we could probably just remove this case.

if name.startswith('MBEDTLS_SSL_DTLS_'):
return 'MBEDTLS_SSL_CLI_C:MBEDTLS_SSL_SRV_C:MBEDTLS_SSL_PROTO_DTLS'
if name.startswith('MBEDTLS_SSL_'):
return 'MBEDTLS_SSL_CLI_C:MBEDTLS_SSL_SRV_C'
for pos in re.finditer(r'_', name):
super_name = name[:pos.start()] + '_C'
if cfg.known(super_name):
return super_name
return None

def conditions_for_setting(cfg: config.Config,
setting: config.Setting
) -> Iterator[Tuple[List[str], str]]:
"""Enumerate the conditions under which to test the given setting.

* cfg: all configuration settings.
* setting: the setting to be tested.

Generate a stream of conditions, i.e. extra dependencies to test with
together with a human-readable explanation of each dependency. Some
typical cases:

* By default, generate a one-element stream with no extra dependencies.
* If the setting is ignored unless some other setting is enabled, generate
a one-element stream with that other setting as an extra dependency.
* If the setting is known to interact with some other setting, generate
a stream with one element where this setting is on and one where it's off.
* To skip the setting altogether, generate an empty stream.
"""
name = setting.name
if name.endswith('_ALT') and not config.is_seamless_alt(name):
# We don't test alt implementations, except (most) platform alts
return
dependencies = dependencies_of_setting(cfg, setting)
if dependencies:
yield [dependencies], ''
return
yield [], ''


def enumerate_boolean_setting_cases(cfg: config.Config
) -> Iterable[test_case.TestCase]:
"""Emit test cases for all boolean settings."""
for name in sorted(cfg.settings.keys()):
setting = cfg.settings[name]
if not name.startswith('PSA_WANT_') and setting.value:
continue # non-boolean setting
for when_on in True, False:
for deps, note in conditions_for_setting(cfg, setting):
yield single_setting_case(setting, when_on, deps, note)



class ConfigTestGenerator(test_data_generation.TestGenerator):
"""Generate test cases for configuration reporting."""

def __init__(self, settings):
self.mbedtls_config = config.ConfigFile()
self.targets['test_suite_config.mbedtls_boolean'] = \
lambda: enumerate_boolean_setting_cases(self.mbedtls_config)
self.psa_config = config.ConfigFile('include/psa/crypto_config.h')
self.targets['test_suite_config.psa_boolean'] = \
lambda: enumerate_boolean_setting_cases(self.psa_config)
super().__init__(settings)


if __name__ == '__main__':
test_data_generation.main(sys.argv[1:], __doc__, ConfigTestGenerator)
9 changes: 9 additions & 0 deletions tests/suites/test_suite_config.crypto_combinations.data
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Interesting combinations of low-level crypto options

Config: ECC: Weierstrass curves only
depends_on:MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED:!MBEDTLS_ECP_MONTGOMERY_ENABLED
pass:

Config: ECC: Montgomery curves only
depends_on:!MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED:MBEDTLS_ECP_MONTGOMERY_ENABLED
pass:
14 changes: 14 additions & 0 deletions tests/suites/test_suite_config.function
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/* BEGIN_HEADER */

/* END_HEADER */

/* BEGIN_CASE */
/* This test case always passes. It is intended solely for configuration
* reporting in the outcome file. Write test cases using this function
* with dependencies to record in which configurations the dependencies
* are met. */
void pass()
{
goto exit;
}
/* END_CASE */
Loading