From 0555f71d91cb9d69ef2b14fb47de4799cc9e7043 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 21 Nov 2024 16:47:37 +0100 Subject: [PATCH 01/17] Create a framework for PSA test cases with automatic dependencies The new class `psa_test_case.TestCase` will automatically infer dependencies from the test data. The dependency inference is not done yet, this will be implemented in subsequent commits. No change to any generated file since this new module is not used yet. Signed-off-by: Gilles Peskine --- scripts/mbedtls_framework/psa_test_case.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 scripts/mbedtls_framework/psa_test_case.py diff --git a/scripts/mbedtls_framework/psa_test_case.py b/scripts/mbedtls_framework/psa_test_case.py new file mode 100644 index 000000000..198bbf148 --- /dev/null +++ b/scripts/mbedtls_framework/psa_test_case.py @@ -0,0 +1,20 @@ +"""Generate test cases for PSA API calls, with automatic dependencies. +""" + +# Copyright The Mbed TLS Contributors +# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later +# + +from . import test_case + + +class TestCase(test_case.TestCase): + """A PSA test case with automatically inferred dependencies. + + For mechanisms like ECC curves where the support status includes + the key bit-size, this class assumes that only one bit-size is + involved in a given test case. + """ + + def __init__(self) -> None: + super().__init__() From 24901cdbf965e01c44a5aa17c3dff547cca1e66a Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 11 Apr 2024 11:17:58 +0200 Subject: [PATCH 02/17] TestCase: add mechanism to skip a test case Allow "skipping" a test case, meaning that the test case is generated commented out. This is useful when systematically generating test cases according to certain rules, where some generated tests cannot be executed but we still want them to be visible when auditing the generation output. Signed-off-by: Gilles Peskine --- scripts/mbedtls_framework/test_case.py | 31 +++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/scripts/mbedtls_framework/test_case.py b/scripts/mbedtls_framework/test_case.py index 47a5e4f7d..f1b94242a 100644 --- a/scripts/mbedtls_framework/test_case.py +++ b/scripts/mbedtls_framework/test_case.py @@ -57,6 +57,7 @@ def __init__(self, description: Optional[str] = None): self.dependencies = [] #type: List[str] self.function = None #type: Optional[str] self.arguments = [] #type: List[str] + self.skip_reasons = [] #type: List[str] def add_comment(self, *lines: str) -> None: self.comments += lines @@ -73,6 +74,23 @@ def set_function(self, function: str) -> None: def set_arguments(self, arguments: List[str]) -> None: self.arguments = arguments + def skip_because(self, reason: str) -> None: + """Skip this test case. + + It will be included in the output, but commented out. + + This is intended for test cases that are obtained from a + systematic enumeration, but that have dependencies that cannot + be fulfilled. Since we don't want to have test cases that are + never executed, we arrange not to have actual test cases. But + we do include comments to make it easier to understand the output + of test case generation. + + reason must be a non-empty string explaining to humans why this + test case is skipped. + """ + self.skip_reasons.append(reason) + def check_completeness(self) -> None: if self.description is None: raise MissingDescription @@ -93,10 +111,17 @@ def write(self, out: typing_util.Writable) -> None: out.write('\n') for line in self.comments: out.write('# ' + line + '\n') - out.write(self.description + '\n') + prefix = '' + if self.skip_reasons: + prefix = '## ' + for reason in self.skip_reasons: + out.write('## # skipped because: ' + reason + '\n') + out.write(prefix + self.description + '\n') if self.dependencies: - out.write('depends_on:' + ':'.join(self.dependencies) + '\n') - out.write(self.function + ':' + ':'.join(self.arguments) + '\n') + out.write(prefix + 'depends_on:' + + ':'.join(self.dependencies) + '\n') + out.write(prefix + self.function + ':' + + ':'.join(self.arguments) + '\n') def write_data_file(filename: str, test_cases: Iterable[TestCase], From 5f0d6551b44c5a0671bbbc8807423391401d162a Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 21 Nov 2024 17:24:15 +0100 Subject: [PATCH 03/17] Use psa_test_case.TestCase throughout generate_psa_tests.py But for now, fully override its automatic dependency inference. We will switch to using the automatic dependencies in future commits. No change to the generated files. Signed-off-by: Gilles Peskine --- scripts/generate_psa_tests.py | 17 +++++++++-------- scripts/mbedtls_framework/crypto_data_tests.py | 2 +- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/scripts/generate_psa_tests.py b/scripts/generate_psa_tests.py index 1618e793d..7c90c81f5 100755 --- a/scripts/generate_psa_tests.py +++ b/scripts/generate_psa_tests.py @@ -18,6 +18,7 @@ from mbedtls_framework import macro_collector #pylint: disable=unused-import from mbedtls_framework import psa_information from mbedtls_framework import psa_storage +from mbedtls_framework import psa_test_case from mbedtls_framework import test_case from mbedtls_framework import test_data_generation @@ -33,16 +34,16 @@ def test_case_for_key_type_not_supported( for an unsupported key type or size. """ psa_information.hack_dependencies_not_implemented(dependencies) - tc = test_case.TestCase() + tc = psa_test_case.TestCase() short_key_type = crypto_knowledge.short_expression(key_type) adverb = 'not' if dependencies else 'never' if param_descr: adverb = param_descr + ' ' + adverb tc.set_description('PSA {} {} {}-bit {} supported' .format(verb, short_key_type, bits, adverb)) - tc.set_dependencies(dependencies) tc.set_function(verb + '_not_supported') tc.set_arguments([key_type] + list(args)) + tc.set_dependencies(dependencies) return tc class KeyTypeNotSupported: @@ -148,13 +149,13 @@ def test_case_for_key_generation( """Return one test case exercising a key generation. """ psa_information.hack_dependencies_not_implemented(dependencies) - tc = test_case.TestCase() + tc = psa_test_case.TestCase() short_key_type = crypto_knowledge.short_expression(key_type) tc.set_description('PSA {} {}-bit' .format(short_key_type, bits)) - tc.set_dependencies(dependencies) tc.set_function('generate_key') tc.set_arguments([key_type] + list(args) + [result]) + tc.set_dependencies(dependencies) return tc @@ -252,7 +253,7 @@ def make_test_case( ) -> test_case.TestCase: """Construct a failure test case for a one-key or keyless operation.""" #pylint: disable=too-many-arguments,too-many-locals - tc = test_case.TestCase() + tc = psa_test_case.TestCase() pretty_alg = alg.short_expression() if reason == self.Reason.NOT_SUPPORTED: short_deps = [re.sub(r'PSA_WANT_ALG_', r'', dep) @@ -276,7 +277,6 @@ def make_test_case( for i, dep in enumerate(dependencies): if dep in not_deps: dependencies[i] = '!' + dep - tc.set_dependencies(dependencies) tc.set_function(category.name.lower() + '_fail') arguments = [] # type: List[str] if kt: @@ -289,6 +289,7 @@ def make_test_case( 'INVALID_ARGUMENT') arguments.append('PSA_ERROR_' + error) tc.set_arguments(arguments) + tc.set_dependencies(dependencies) return tc def no_key_test_cases( @@ -488,7 +489,7 @@ def make_test_case(self, key: StorageTestData) -> test_case.TestCase: correctly. """ verb = 'save' if self.forward else 'read' - tc = test_case.TestCase() + tc = psa_test_case.TestCase() tc.set_description(verb + ' ' + key.description) dependencies = psa_information.automatic_dependencies( key.lifetime.string, key.type.string, @@ -497,7 +498,6 @@ def make_test_case(self, key: StorageTestData) -> test_case.TestCase: dependencies = psa_information.finish_family_dependencies(dependencies, key.bits) dependencies += psa_information.generate_deps_from_description(key.description) dependencies = psa_information.fix_key_pair_dependencies(dependencies, 'BASIC') - tc.set_dependencies(dependencies) tc.set_function('key_storage_' + verb) if self.forward: extra_arguments = [] @@ -515,6 +515,7 @@ def make_test_case(self, key: StorageTestData) -> test_case.TestCase: '"' + key.material.hex() + '"', '"' + key.hex() + '"', *extra_arguments]) + tc.set_dependencies(dependencies) return tc def key_for_lifetime( diff --git a/scripts/mbedtls_framework/crypto_data_tests.py b/scripts/mbedtls_framework/crypto_data_tests.py index a36de692e..e7a0864f7 100644 --- a/scripts/mbedtls_framework/crypto_data_tests.py +++ b/scripts/mbedtls_framework/crypto_data_tests.py @@ -74,10 +74,10 @@ def one_test_case(alg: crypto_knowledge.Algorithm, .format(function, ' ' + note if note else '', alg.short_expression())) - tc.set_dependencies(psa_low_level_dependencies(alg.expression)) tc.set_function(function) tc.set_arguments([alg.expression] + ['"{}"'.format(arg) for arg in arguments]) + tc.set_dependencies(psa_low_level_dependencies(alg.expression)) return tc def test_cases_for_hash(self, From a3d2eaffcc130a33a711aa533cc47e544cc513fa Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 21 Nov 2024 15:51:03 +0100 Subject: [PATCH 04/17] Prepare psa_test_cases to mix manual and automatic dependencies No change to the generated files. Signed-off-by: Gilles Peskine --- .../mbedtls_framework/crypto_data_tests.py | 3 ++- scripts/mbedtls_framework/psa_test_case.py | 27 +++++++++++++++++++ scripts/mbedtls_framework/test_case.py | 8 ++++-- 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/scripts/mbedtls_framework/crypto_data_tests.py b/scripts/mbedtls_framework/crypto_data_tests.py index e7a0864f7..40bd5672c 100644 --- a/scripts/mbedtls_framework/crypto_data_tests.py +++ b/scripts/mbedtls_framework/crypto_data_tests.py @@ -12,6 +12,7 @@ from . import crypto_knowledge from . import psa_information +from . import psa_test_case from . import test_case @@ -69,7 +70,7 @@ def one_test_case(alg: crypto_knowledge.Algorithm, function: str, note: str, arguments: List[str]) -> test_case.TestCase: """Construct one test case involving a hash.""" - tc = test_case.TestCase() + tc = psa_test_case.TestCase() tc.set_description('{}{} {}' .format(function, ' ' + note if note else '', diff --git a/scripts/mbedtls_framework/psa_test_case.py b/scripts/mbedtls_framework/psa_test_case.py index 198bbf148..341db68a0 100644 --- a/scripts/mbedtls_framework/psa_test_case.py +++ b/scripts/mbedtls_framework/psa_test_case.py @@ -5,6 +5,8 @@ # SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later # +from typing import List, Set + from . import test_case @@ -18,3 +20,28 @@ class TestCase(test_case.TestCase): def __init__(self) -> None: super().__init__() + del self.dependencies + self.manual_dependencies = [] #type: List[str] + self.automatic_dependencies = set() #type: Set[str] + + @staticmethod + def infer_dependencies(_arguments: List[str]) -> List[str]: + """Infer dependencies based on the test case arguments.""" + return [] # not implemented yet + + def set_arguments(self, arguments: List[str]) -> None: + """Set test case arguments and automatically infer dependencies.""" + super().set_arguments(arguments) + self.automatic_dependencies.update(self.infer_dependencies(arguments)) + + def set_dependencies(self, dependencies: List[str]) -> None: + """Override any previously added automatic or manual dependencies.""" + self.manual_dependencies = dependencies + self.automatic_dependencies.clear() + + def add_dependencies(self, dependencies: List[str]) -> None: + """Add manual dependencies.""" + self.manual_dependencies += dependencies + + def get_dependencies(self) -> List[str]: + return sorted(self.automatic_dependencies) + self.manual_dependencies diff --git a/scripts/mbedtls_framework/test_case.py b/scripts/mbedtls_framework/test_case.py index f1b94242a..58e75a1ca 100644 --- a/scripts/mbedtls_framework/test_case.py +++ b/scripts/mbedtls_framework/test_case.py @@ -65,6 +65,9 @@ def add_comment(self, *lines: str) -> None: def set_description(self, description: str) -> None: self.description = description + def get_dependencies(self) -> List[str]: + return self.dependencies + def set_dependencies(self, dependencies: List[str]) -> None: self.dependencies = dependencies @@ -117,9 +120,10 @@ def write(self, out: typing_util.Writable) -> None: for reason in self.skip_reasons: out.write('## # skipped because: ' + reason + '\n') out.write(prefix + self.description + '\n') - if self.dependencies: + dependencies = self.get_dependencies() + if dependencies: out.write(prefix + 'depends_on:' + - ':'.join(self.dependencies) + '\n') + ':'.join(dependencies) + '\n') out.write(prefix + self.function + ':' + ':'.join(self.arguments) + '\n') From e3615b9ba0d7208231291567337448ba26e2ccc3 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 21 Nov 2024 19:48:27 +0100 Subject: [PATCH 05/17] Go through psa_test_case.TestCase for skipping never-implemented mechanisms No change to the generated output. Signed-off-by: Gilles Peskine --- scripts/generate_psa_tests.py | 5 ++--- scripts/mbedtls_framework/psa_information.py | 17 +++++++---------- scripts/mbedtls_framework/psa_test_case.py | 7 +++++++ 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/scripts/generate_psa_tests.py b/scripts/generate_psa_tests.py index 7c90c81f5..417fc08f1 100755 --- a/scripts/generate_psa_tests.py +++ b/scripts/generate_psa_tests.py @@ -33,7 +33,6 @@ def test_case_for_key_type_not_supported( """Return one test case exercising a key creation method for an unsupported key type or size. """ - psa_information.hack_dependencies_not_implemented(dependencies) tc = psa_test_case.TestCase() short_key_type = crypto_knowledge.short_expression(key_type) adverb = 'not' if dependencies else 'never' @@ -44,6 +43,7 @@ def test_case_for_key_type_not_supported( tc.set_function(verb + '_not_supported') tc.set_arguments([key_type] + list(args)) tc.set_dependencies(dependencies) + tc.skip_if_any_not_implemented(dependencies) return tc class KeyTypeNotSupported: @@ -148,7 +148,6 @@ def test_case_for_key_generation( ) -> test_case.TestCase: """Return one test case exercising a key generation. """ - psa_information.hack_dependencies_not_implemented(dependencies) tc = psa_test_case.TestCase() short_key_type = crypto_knowledge.short_expression(key_type) tc.set_description('PSA {} {}-bit' @@ -156,7 +155,7 @@ def test_case_for_key_generation( tc.set_function('generate_key') tc.set_arguments([key_type] + list(args) + [result]) tc.set_dependencies(dependencies) - + tc.skip_if_any_not_implemented(dependencies) return tc class KeyGenerate: diff --git a/scripts/mbedtls_framework/psa_information.py b/scripts/mbedtls_framework/psa_information.py index 0d4ea9edd..243e3d0f6 100644 --- a/scripts/mbedtls_framework/psa_information.py +++ b/scripts/mbedtls_framework/psa_information.py @@ -133,11 +133,9 @@ def read_implemented_dependencies(filename: str) -> FrozenSet[str]: for line in open(filename) for symbol in re.findall(r'\bPSA_WANT_\w+\b', line)) _implemented_dependencies = None #type: Optional[FrozenSet[str]] #pylint: disable=invalid-name -def hack_dependencies_not_implemented(dependencies: List[str]) -> None: - """ - Hack dependencies to skip test cases for which at least one dependency - symbol is not available yet. - """ + +def find_dependencies_not_implemented(dependencies: List[str]) -> List[str]: + """List the dependencies that are not implemented.""" global _implemented_dependencies #pylint: disable=global-statement,invalid-name if _implemented_dependencies is None: # Temporary, while Mbed TLS does not just rely on the TF-PSA-Crypto @@ -149,11 +147,10 @@ def hack_dependencies_not_implemented(dependencies: List[str]) -> None: else: _implemented_dependencies = \ read_implemented_dependencies('include/psa/crypto_config.h') - - if not all((dep.lstrip('!') in _implemented_dependencies or - not dep.lstrip('!').startswith('PSA_WANT')) - for dep in dependencies): - dependencies.append('DEPENDENCY_NOT_IMPLEMENTED_YET') + return [dep + for dep in dependencies + if (dep.lstrip('!') not in _implemented_dependencies and + dep.lstrip('!').startswith('PSA_WANT'))] def tweak_key_pair_dependency(dep: str, usage: str): """ diff --git a/scripts/mbedtls_framework/psa_test_case.py b/scripts/mbedtls_framework/psa_test_case.py index 341db68a0..c0738d00a 100644 --- a/scripts/mbedtls_framework/psa_test_case.py +++ b/scripts/mbedtls_framework/psa_test_case.py @@ -7,6 +7,7 @@ from typing import List, Set +from . import psa_information from . import test_case @@ -45,3 +46,9 @@ def add_dependencies(self, dependencies: List[str]) -> None: def get_dependencies(self) -> List[str]: return sorted(self.automatic_dependencies) + self.manual_dependencies + + def skip_if_any_not_implemented(self, dependencies: List[str]) -> None: + """Skip the test case if any of the given dependencies is not implemented.""" + not_implemented = psa_information.find_dependencies_not_implemented(dependencies) + if not_implemented: + self.add_dependencies(['DEPENDENCY_NOT_IMPLEMENTED_YET']) From a489671c9848da24451b09aa5ce4751443ad6b52 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 21 Nov 2024 21:34:19 +0100 Subject: [PATCH 06/17] Move find_dependencies_not_implemented to psa_test_case.py Signed-off-by: Gilles Peskine --- scripts/mbedtls_framework/psa_information.py | 31 +--------------- scripts/mbedtls_framework/psa_test_case.py | 37 ++++++++++++++++++-- 2 files changed, 35 insertions(+), 33 deletions(-) diff --git a/scripts/mbedtls_framework/psa_information.py b/scripts/mbedtls_framework/psa_information.py index 243e3d0f6..f09b64217 100644 --- a/scripts/mbedtls_framework/psa_information.py +++ b/scripts/mbedtls_framework/psa_information.py @@ -8,7 +8,7 @@ import os import re from collections import OrderedDict -from typing import FrozenSet, List, Optional +from typing import List, Optional from . import macro_collector @@ -123,35 +123,6 @@ def generate_deps_from_description( return dep_list -# A temporary hack: at the time of writing, not all dependency symbols -# are implemented yet. Skip test cases for which the dependency symbols are -# not available. Once all dependency symbols are available, this hack must -# be removed so that a bug in the dependency symbols properly leads to a test -# failure. -def read_implemented_dependencies(filename: str) -> FrozenSet[str]: - return frozenset(symbol - for line in open(filename) - for symbol in re.findall(r'\bPSA_WANT_\w+\b', line)) -_implemented_dependencies = None #type: Optional[FrozenSet[str]] #pylint: disable=invalid-name - -def find_dependencies_not_implemented(dependencies: List[str]) -> List[str]: - """List the dependencies that are not implemented.""" - global _implemented_dependencies #pylint: disable=global-statement,invalid-name - if _implemented_dependencies is None: - # Temporary, while Mbed TLS does not just rely on the TF-PSA-Crypto - # build system to build its crypto library. When it does, the first - # case can just be removed. - if os.path.isdir('tf-psa-crypto'): - _implemented_dependencies = \ - read_implemented_dependencies('tf-psa-crypto/include/psa/crypto_config.h') - else: - _implemented_dependencies = \ - read_implemented_dependencies('include/psa/crypto_config.h') - return [dep - for dep in dependencies - if (dep.lstrip('!') not in _implemented_dependencies and - dep.lstrip('!').startswith('PSA_WANT'))] - def tweak_key_pair_dependency(dep: str, usage: str): """ This helper function add the proper suffix to PSA_WANT_KEY_TYPE_xxx_KEY_PAIR diff --git a/scripts/mbedtls_framework/psa_test_case.py b/scripts/mbedtls_framework/psa_test_case.py index c0738d00a..11273f0bf 100644 --- a/scripts/mbedtls_framework/psa_test_case.py +++ b/scripts/mbedtls_framework/psa_test_case.py @@ -5,12 +5,43 @@ # SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later # -from typing import List, Set +import os +import re +from typing import FrozenSet, List, Optional, Set -from . import psa_information from . import test_case +# A temporary hack: at the time of writing, not all dependency symbols +# are implemented yet. Skip test cases for which the dependency symbols are +# not available. Once all dependency symbols are available, this hack must +# be removed so that a bug in the dependency symbols properly leads to a test +# failure. +def read_implemented_dependencies(filename: str) -> FrozenSet[str]: + return frozenset(symbol + for line in open(filename) + for symbol in re.findall(r'\bPSA_WANT_\w+\b', line)) +_implemented_dependencies = None #type: Optional[FrozenSet[str]] #pylint: disable=invalid-name + +def find_dependencies_not_implemented(dependencies: List[str]) -> List[str]: + """List the dependencies that are not implemented.""" + global _implemented_dependencies #pylint: disable=global-statement,invalid-name + if _implemented_dependencies is None: + # Temporary, while Mbed TLS does not just rely on the TF-PSA-Crypto + # build system to build its crypto library. When it does, the first + # case can just be removed. + if os.path.isdir('tf-psa-crypto'): + _implemented_dependencies = \ + read_implemented_dependencies('tf-psa-crypto/include/psa/crypto_config.h') + else: + _implemented_dependencies = \ + read_implemented_dependencies('include/psa/crypto_config.h') + return [dep + for dep in dependencies + if (dep.lstrip('!') not in _implemented_dependencies and + dep.lstrip('!').startswith('PSA_WANT'))] + + class TestCase(test_case.TestCase): """A PSA test case with automatically inferred dependencies. @@ -49,6 +80,6 @@ def get_dependencies(self) -> List[str]: def skip_if_any_not_implemented(self, dependencies: List[str]) -> None: """Skip the test case if any of the given dependencies is not implemented.""" - not_implemented = psa_information.find_dependencies_not_implemented(dependencies) + not_implemented = find_dependencies_not_implemented(dependencies) if not_implemented: self.add_dependencies(['DEPENDENCY_NOT_IMPLEMENTED_YET']) From 8d72e5e7f88be28e7c13c1b2a5d56cc0e043a20c Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 21 Nov 2024 21:44:01 +0100 Subject: [PATCH 07/17] find_dependencies_not_implemented: Also read inferred PSA_WANT symbols To determine PSA mechanisms that are not implemented, also read `PSA_WANT_` symbols that cannot (or are not intended to) be configured independently, and thus are not listed in `psa/crypto_config.h`. Find those symbols in the config adjustment header `psa/crypto_adjust_config_synonyms.h`. No impact on generated files yet, because `find_dependencies_not_implemented` is currently only used on key types that have explicit dependencies. This will allow using hack_dependencies_not_implemented in other places, for example to handle algorithm variants like `PSA_WANT_ALG_ECDSA_ANY` which is inferred from `PSA_WANT_ALG_ECDSA`. Signed-off-by: Gilles Peskine --- scripts/mbedtls_framework/psa_test_case.py | 23 ++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/scripts/mbedtls_framework/psa_test_case.py b/scripts/mbedtls_framework/psa_test_case.py index 11273f0bf..e723365d0 100644 --- a/scripts/mbedtls_framework/psa_test_case.py +++ b/scripts/mbedtls_framework/psa_test_case.py @@ -17,10 +17,12 @@ # not available. Once all dependency symbols are available, this hack must # be removed so that a bug in the dependency symbols properly leads to a test # failure. -def read_implemented_dependencies(filename: str) -> FrozenSet[str]: - return frozenset(symbol - for line in open(filename) - for symbol in re.findall(r'\bPSA_WANT_\w+\b', line)) +def read_implemented_dependencies(acc: Set[str], filename: str) -> None: + with open(filename) as input_stream: + for line in input_stream: + for symbol in re.findall(r'\bPSA_WANT_\w+\b', line): + acc.add(symbol) + _implemented_dependencies = None #type: Optional[FrozenSet[str]] #pylint: disable=invalid-name def find_dependencies_not_implemented(dependencies: List[str]) -> List[str]: @@ -31,11 +33,16 @@ def find_dependencies_not_implemented(dependencies: List[str]) -> List[str]: # build system to build its crypto library. When it does, the first # case can just be removed. if os.path.isdir('tf-psa-crypto'): - _implemented_dependencies = \ - read_implemented_dependencies('tf-psa-crypto/include/psa/crypto_config.h') + include_dir = 'tf-psa-crypto/include' else: - _implemented_dependencies = \ - read_implemented_dependencies('include/psa/crypto_config.h') + include_dir = 'include' + acc = set() #type: Set[str] + for filename in [ + os.path.join(include_dir, 'psa/crypto_config.h'), + os.path.join(include_dir, 'psa/crypto_adjust_config_synonyms.h'), + ]: + read_implemented_dependencies(acc, filename) + _implemented_dependencies = frozenset(acc) return [dep for dep in dependencies if (dep.lstrip('!') not in _implemented_dependencies and From ea6951ace099c14cbaea70ecb4dcca1d71563669 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 21 Nov 2024 22:21:35 +0100 Subject: [PATCH 08/17] Explain why DSA is explicitly excluded We can't even attempt to generate DSA test cases because `asymmetric_key_data.py` doesn't have test data for DSA. Signed-off-by: Gilles Peskine --- scripts/mbedtls_framework/psa_information.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scripts/mbedtls_framework/psa_information.py b/scripts/mbedtls_framework/psa_information.py index f09b64217..4f0633cd2 100644 --- a/scripts/mbedtls_framework/psa_information.py +++ b/scripts/mbedtls_framework/psa_information.py @@ -23,8 +23,13 @@ def __init__(self) -> None: def remove_unwanted_macros( constructors: macro_collector.PSAMacroEnumerator ) -> None: - # Mbed TLS does not support finite-field DSA. + """Remove constructors that should be exckuded from systematic testing.""" + # Mbed TLS does not support finite-field DSA, but 3.6 defines DSA + # identifiers for historical reasons. # Don't attempt to generate any related test case. + # The corresponding test cases would be commented out anyway, + # but for DSA, we don't have enough support in the test scripts + # to generate these test cases. constructors.key_types.discard('PSA_KEY_TYPE_DSA_KEY_PAIR') constructors.key_types.discard('PSA_KEY_TYPE_DSA_PUBLIC_KEY') From 119b3b9740b1a64d0ee8ca302a5dcd72b6d36d3d Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 21 Nov 2024 19:53:17 +0100 Subject: [PATCH 09/17] generate_psa_tests: comment out always-skipped test cases When we generate a test case for a mechanism that is not implemented, comment out the test case rather than giving it a never-fulfilled dependency. That way we don't create test cases that cannot be executed. This changes the generated output in the following ways: * No longer emit test cases with a dependency on `DEPENDENCY_NOT_IMPLEMENTED_YET`. All removed lines that start with `depends_on:` contain `DEPENDENCY_NOT_IMPLEMENTED_YET. * Emit commented-out test cases instead: all the new lines are comment lines. There is no change in which test cases actually get executed. This removes many test cases from the list of available test cases, which causes some of the exceptions in `analyze_outcomes.py` to no longer be useful. Signed-off-by: Gilles Peskine --- scripts/mbedtls_framework/psa_test_case.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/mbedtls_framework/psa_test_case.py b/scripts/mbedtls_framework/psa_test_case.py index e723365d0..a4ce6a027 100644 --- a/scripts/mbedtls_framework/psa_test_case.py +++ b/scripts/mbedtls_framework/psa_test_case.py @@ -89,4 +89,5 @@ def skip_if_any_not_implemented(self, dependencies: List[str]) -> None: """Skip the test case if any of the given dependencies is not implemented.""" not_implemented = find_dependencies_not_implemented(dependencies) if not_implemented: - self.add_dependencies(['DEPENDENCY_NOT_IMPLEMENTED_YET']) + self.skip_because('not implemented: ' + + ' '.join(not_implemented)) From e3f9189103de9f14702fa8f8581457d179d8f0e4 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 21 Nov 2024 16:07:21 +0100 Subject: [PATCH 10/17] generate_psa_tests.py: Sort and uniquify dependencies No semantic change. In the generated files, `depends_on:` lines have entries that are reordered. Signed-off-by: Gilles Peskine --- scripts/mbedtls_framework/psa_test_case.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scripts/mbedtls_framework/psa_test_case.py b/scripts/mbedtls_framework/psa_test_case.py index a4ce6a027..c6839b84d 100644 --- a/scripts/mbedtls_framework/psa_test_case.py +++ b/scripts/mbedtls_framework/psa_test_case.py @@ -83,7 +83,12 @@ def add_dependencies(self, dependencies: List[str]) -> None: self.manual_dependencies += dependencies def get_dependencies(self) -> List[str]: - return sorted(self.automatic_dependencies) + self.manual_dependencies + # Make the output independent of the order in which the dependencies + # are calculated by the script. Also avoid duplicates. This makes + # the output robust with respect to refactoring of the scripts. + dependencies = set(self.manual_dependencies) + dependencies.update(self.automatic_dependencies) + return sorted(dependencies) def skip_if_any_not_implemented(self, dependencies: List[str]) -> None: """Skip the test case if any of the given dependencies is not implemented.""" From adb23c30f933a6210ec41016460ed1cd82a30220 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 21 Nov 2024 17:35:20 +0100 Subject: [PATCH 11/17] Support an alternative prefix in psa_information.automatic_dependencies No change to the generated files (the new code isn't used yet). Signed-off-by: Gilles Peskine --- .../mbedtls_framework/crypto_data_tests.py | 6 ++---- scripts/mbedtls_framework/psa_information.py | 21 ++++++++++++++----- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/scripts/mbedtls_framework/crypto_data_tests.py b/scripts/mbedtls_framework/crypto_data_tests.py index 40bd5672c..5aef5c574 100644 --- a/scripts/mbedtls_framework/crypto_data_tests.py +++ b/scripts/mbedtls_framework/crypto_data_tests.py @@ -21,10 +21,8 @@ def psa_low_level_dependencies(*expressions: str) -> List[str]: This function generates MBEDTLS_PSA_BUILTIN_xxx symbols. """ - high_level = psa_information.automatic_dependencies(*expressions) - for dep in high_level: - assert dep.startswith('PSA_WANT_') - return ['MBEDTLS_PSA_BUILTIN_' + dep[9:] for dep in high_level] + return psa_information.automatic_dependencies(*expressions, + prefix='MBEDTLS_PSA_BUILTIN_') class HashPSALowLevel: diff --git a/scripts/mbedtls_framework/psa_information.py b/scripts/mbedtls_framework/psa_information.py index 4f0633cd2..1ff02da61 100644 --- a/scripts/mbedtls_framework/psa_information.py +++ b/scripts/mbedtls_framework/psa_information.py @@ -57,10 +57,16 @@ def read_psa_interface(self) -> macro_collector.PSAMacroEnumerator: return constructors -def psa_want_symbol(name: str) -> str: - """Return the PSA_WANT_xxx symbol associated with a PSA crypto feature.""" +def psa_want_symbol(name: str, prefix: Optional[str] = None) -> str: + """Return the PSA_WANT_xxx symbol associated with a PSA crypto feature. + + You can use an altenative `prefix`, e.g. 'MBEDTLS_PSA_BUILTIN_' + when specifically testing builtin implementations. + """ + if prefix is None: + prefix = 'PSA_WANT_' if name.startswith('PSA_'): - return name[:4] + 'WANT_' + name[4:] + return prefix + name[4:] else: raise ValueError('Unable to determine the PSA_WANT_ symbol for ' + name) @@ -88,18 +94,23 @@ def finish_family_dependencies(dependencies: List[str], bits: int) -> List[str]: 'PSA_ALG_KEY_AGREEMENT', # chaining 'PSA_ALG_TRUNCATED_MAC', # modifier ]) -def automatic_dependencies(*expressions: str) -> List[str]: +def automatic_dependencies(*expressions: str, + prefix: Optional[str] = None) -> List[str]: """Infer dependencies of a test case by looking for PSA_xxx symbols. The arguments are strings which should be C expressions. Do not use string literals or comments as this function is not smart enough to skip them. + + `prefix`: prefix to use in dependencies. Defaults to ``'PSA_WANT_'``. + Use ``'MBEDTLS_PSA_BUILTIN_'`` when specifically testing + builtin implementations. """ used = set() for expr in expressions: used.update(re.findall(r'PSA_(?:ALG|ECC_FAMILY|DH_FAMILY|KEY_TYPE)_\w+', expr)) used.difference_update(SYMBOLS_WITHOUT_DEPENDENCY) - return sorted(psa_want_symbol(name) for name in used) + return sorted(psa_want_symbol(name, prefix=prefix) for name in used) # Define set of regular expressions and dependencies to optionally append # extra dependencies for test case based on key description. From 16229d872644289856c9ce5df0bb65aa8733053c Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 21 Nov 2024 17:26:00 +0100 Subject: [PATCH 12/17] Switch crypto_data_tests.py to automatic dependencies In `psa_test_cases.TestCase`: * Implement basic support for automatic dependencies, by calling `psa_information.automatic_dependencies`. * Support an alternative dependency prefix. No changes to the generated file. Signed-off-by: Gilles Peskine --- scripts/mbedtls_framework/crypto_data_tests.py | 12 +----------- scripts/mbedtls_framework/psa_test_case.py | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/scripts/mbedtls_framework/crypto_data_tests.py b/scripts/mbedtls_framework/crypto_data_tests.py index 5aef5c574..1d46e3f2c 100644 --- a/scripts/mbedtls_framework/crypto_data_tests.py +++ b/scripts/mbedtls_framework/crypto_data_tests.py @@ -16,15 +16,6 @@ from . import test_case -def psa_low_level_dependencies(*expressions: str) -> List[str]: - """Infer dependencies of a PSA low-level test case by looking for PSA_xxx symbols. - - This function generates MBEDTLS_PSA_BUILTIN_xxx symbols. - """ - return psa_information.automatic_dependencies(*expressions, - prefix='MBEDTLS_PSA_BUILTIN_') - - class HashPSALowLevel: """Generate test cases for the PSA low-level hash interface.""" @@ -68,7 +59,7 @@ def one_test_case(alg: crypto_knowledge.Algorithm, function: str, note: str, arguments: List[str]) -> test_case.TestCase: """Construct one test case involving a hash.""" - tc = psa_test_case.TestCase() + tc = psa_test_case.TestCase(dependency_prefix='MBEDTLS_PSA_BUILTIN_') tc.set_description('{}{} {}' .format(function, ' ' + note if note else '', @@ -76,7 +67,6 @@ def one_test_case(alg: crypto_knowledge.Algorithm, tc.set_function(function) tc.set_arguments([alg.expression] + ['"{}"'.format(arg) for arg in arguments]) - tc.set_dependencies(psa_low_level_dependencies(alg.expression)) return tc def test_cases_for_hash(self, diff --git a/scripts/mbedtls_framework/psa_test_case.py b/scripts/mbedtls_framework/psa_test_case.py index c6839b84d..57172053e 100644 --- a/scripts/mbedtls_framework/psa_test_case.py +++ b/scripts/mbedtls_framework/psa_test_case.py @@ -9,6 +9,7 @@ import re from typing import FrozenSet, List, Optional, Set +from . import psa_information from . import test_case @@ -57,16 +58,24 @@ class TestCase(test_case.TestCase): involved in a given test case. """ - def __init__(self) -> None: + def __init__(self, dependency_prefix: Optional[str] = None) -> None: + """Construct a test case for a PSA Crypto API call. + + `dependency_prefix`: prefix to use in dependencies. Defaults to + ``'PSA_WANT_'``. Use ``'MBEDTLS_PSA_BUILTIN_'`` + when specifically testing builtin implementations. + """ super().__init__() del self.dependencies self.manual_dependencies = [] #type: List[str] self.automatic_dependencies = set() #type: Set[str] + self.dependency_prefix = dependency_prefix #type: Optional[str] - @staticmethod - def infer_dependencies(_arguments: List[str]) -> List[str]: + def infer_dependencies(self, arguments: List[str]) -> List[str]: """Infer dependencies based on the test case arguments.""" - return [] # not implemented yet + dependencies = psa_information.automatic_dependencies(*arguments, + prefix=self.dependency_prefix) + return dependencies def set_arguments(self, arguments: List[str]) -> None: """Set test case arguments and automatically infer dependencies.""" From 05e4ae8bd736ccc0f78fc1bbebabca7fc3edb963 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 21 Nov 2024 18:29:29 +0100 Subject: [PATCH 13/17] psa_test_case automatic dependencies: take the key size into account This fixes the dependencies for DH group and elliptic curve families. No changes to the generated output (the new functionality isn't used yet). Signed-off-by: Gilles Peskine --- scripts/generate_psa_tests.py | 7 ++++++- scripts/mbedtls_framework/psa_test_case.py | 14 ++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/scripts/generate_psa_tests.py b/scripts/generate_psa_tests.py index 417fc08f1..d18411662 100755 --- a/scripts/generate_psa_tests.py +++ b/scripts/generate_psa_tests.py @@ -41,6 +41,7 @@ def test_case_for_key_type_not_supported( tc.set_description('PSA {} {} {}-bit {} supported' .format(verb, short_key_type, bits, adverb)) tc.set_function(verb + '_not_supported') + tc.set_key_bits(bits) tc.set_arguments([key_type] + list(args)) tc.set_dependencies(dependencies) tc.skip_if_any_not_implemented(dependencies) @@ -153,6 +154,7 @@ def test_case_for_key_generation( tc.set_description('PSA {} {}-bit' .format(short_key_type, bits)) tc.set_function('generate_key') + tc.set_key_bits(bits) tc.set_arguments([key_type] + list(args) + [result]) tc.set_dependencies(dependencies) tc.skip_if_any_not_implemented(dependencies) @@ -279,7 +281,9 @@ def make_test_case( tc.set_function(category.name.lower() + '_fail') arguments = [] # type: List[str] if kt: - key_material = kt.key_material(kt.sizes_to_test()[0]) + bits = kt.sizes_to_test()[0] + tc.set_key_bits(bits) + key_material = kt.key_material(bits) arguments += [key_type, test_case.hex_string(key_material)] arguments.append(alg.expression) if category.is_asymmetric(): @@ -498,6 +502,7 @@ def make_test_case(self, key: StorageTestData) -> test_case.TestCase: dependencies += psa_information.generate_deps_from_description(key.description) dependencies = psa_information.fix_key_pair_dependencies(dependencies, 'BASIC') tc.set_function('key_storage_' + verb) + tc.set_key_bits(key.bits) if self.forward: extra_arguments = [] else: diff --git a/scripts/mbedtls_framework/psa_test_case.py b/scripts/mbedtls_framework/psa_test_case.py index 57172053e..d478d0ac3 100644 --- a/scripts/mbedtls_framework/psa_test_case.py +++ b/scripts/mbedtls_framework/psa_test_case.py @@ -70,11 +70,25 @@ def __init__(self, dependency_prefix: Optional[str] = None) -> None: self.manual_dependencies = [] #type: List[str] self.automatic_dependencies = set() #type: Set[str] self.dependency_prefix = dependency_prefix #type: Optional[str] + self.key_bits = None #type: Optional[int] + + def set_key_bits(self, key_bits: Optional[int]) -> None: + """Use the given key size for automatic dependency generation. + + Call this function before set_arguments() if relevant. + + This is only relevant for ECC and DH keys. For other key types, + this information is ignored. + """ + self.key_bits = key_bits def infer_dependencies(self, arguments: List[str]) -> List[str]: """Infer dependencies based on the test case arguments.""" dependencies = psa_information.automatic_dependencies(*arguments, prefix=self.dependency_prefix) + if self.key_bits is not None: + dependencies = psa_information.finish_family_dependencies(dependencies, + self.key_bits) return dependencies def set_arguments(self, arguments: List[str]) -> None: From 4a134ee89f37f07caaef2bc39234b5441654a161 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 21 Nov 2024 18:33:23 +0100 Subject: [PATCH 14/17] psa_test_case automatic dependencies: key pair type operations This fixes the dependencies for key pair types, which have finer-grained dependencies for different operations (BASIC, GENERATE, EXPORT, ...). No changes to the generated output (the new functionality isn't used yet). Signed-off-by: Gilles Peskine --- scripts/generate_psa_tests.py | 3 +++ scripts/mbedtls_framework/psa_test_case.py | 14 ++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/scripts/generate_psa_tests.py b/scripts/generate_psa_tests.py index d18411662..0038693a5 100755 --- a/scripts/generate_psa_tests.py +++ b/scripts/generate_psa_tests.py @@ -42,6 +42,7 @@ def test_case_for_key_type_not_supported( .format(verb, short_key_type, bits, adverb)) tc.set_function(verb + '_not_supported') tc.set_key_bits(bits) + tc.set_key_pair_usage(verb.upper()) tc.set_arguments([key_type] + list(args)) tc.set_dependencies(dependencies) tc.skip_if_any_not_implemented(dependencies) @@ -155,6 +156,7 @@ def test_case_for_key_generation( .format(short_key_type, bits)) tc.set_function('generate_key') tc.set_key_bits(bits) + tc.set_key_pair_usage('GENERATE') tc.set_arguments([key_type] + list(args) + [result]) tc.set_dependencies(dependencies) tc.skip_if_any_not_implemented(dependencies) @@ -503,6 +505,7 @@ def make_test_case(self, key: StorageTestData) -> test_case.TestCase: dependencies = psa_information.fix_key_pair_dependencies(dependencies, 'BASIC') tc.set_function('key_storage_' + verb) tc.set_key_bits(key.bits) + tc.set_key_pair_usage('BASIC') if self.forward: extra_arguments = [] else: diff --git a/scripts/mbedtls_framework/psa_test_case.py b/scripts/mbedtls_framework/psa_test_case.py index d478d0ac3..9c52acf6d 100644 --- a/scripts/mbedtls_framework/psa_test_case.py +++ b/scripts/mbedtls_framework/psa_test_case.py @@ -71,6 +71,7 @@ def __init__(self, dependency_prefix: Optional[str] = None) -> None: self.automatic_dependencies = set() #type: Set[str] self.dependency_prefix = dependency_prefix #type: Optional[str] self.key_bits = None #type: Optional[int] + self.key_pair_usage = None #type: Optional[str] def set_key_bits(self, key_bits: Optional[int]) -> None: """Use the given key size for automatic dependency generation. @@ -82,6 +83,16 @@ def set_key_bits(self, key_bits: Optional[int]) -> None: """ self.key_bits = key_bits + def set_key_pair_usage(self, key_pair_usage: Optional[str]) -> None: + """Use the given suffix for key pair dependencies. + + Call this function before set_arguments() if relevant. + + This is only relevant for key pair types. For other key types, + this information is ignored. + """ + self.key_pair_usage = key_pair_usage + def infer_dependencies(self, arguments: List[str]) -> List[str]: """Infer dependencies based on the test case arguments.""" dependencies = psa_information.automatic_dependencies(*arguments, @@ -89,6 +100,9 @@ def infer_dependencies(self, arguments: List[str]) -> List[str]: if self.key_bits is not None: dependencies = psa_information.finish_family_dependencies(dependencies, self.key_bits) + if self.key_pair_usage is not None: + dependencies = psa_information.fix_key_pair_dependencies(dependencies, + self.key_pair_usage) return dependencies def set_arguments(self, arguments: List[str]) -> None: From f324e9e3b5f4d0bb6c6cb5cc79d6d4ed7bc8a90e Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 21 Nov 2024 18:37:03 +0100 Subject: [PATCH 15/17] psa_test_case automatic dependencies: RSA_GENERATE_MIN_KEY_BITS No changes to the generated output (the new functionality isn't used yet). Signed-off-by: Gilles Peskine --- scripts/mbedtls_framework/psa_test_case.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/mbedtls_framework/psa_test_case.py b/scripts/mbedtls_framework/psa_test_case.py index 9c52acf6d..aa9a49d85 100644 --- a/scripts/mbedtls_framework/psa_test_case.py +++ b/scripts/mbedtls_framework/psa_test_case.py @@ -103,6 +103,11 @@ def infer_dependencies(self, arguments: List[str]) -> List[str]: if self.key_pair_usage is not None: dependencies = psa_information.fix_key_pair_dependencies(dependencies, self.key_pair_usage) + if 'PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_GENERATE' in dependencies and \ + self.key_bits is not None: + size_dependency = ('PSA_VENDOR_RSA_GENERATE_MIN_KEY_BITS <= ' + + str(self.key_bits)) + dependencies.append(size_dependency) return dependencies def set_arguments(self, arguments: List[str]) -> None: From a43fe58f8bafcaca019a33d578a5ebeab2e6da0f Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 21 Nov 2024 20:07:53 +0100 Subject: [PATCH 16/17] psa_test_cases: automatically skip test cases Automatically skip test cases with not-implemented automatic dependencies. No changes to the generated output. Signed-off-by: Gilles Peskine --- scripts/mbedtls_framework/psa_test_case.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/scripts/mbedtls_framework/psa_test_case.py b/scripts/mbedtls_framework/psa_test_case.py index aa9a49d85..eea969f7a 100644 --- a/scripts/mbedtls_framework/psa_test_case.py +++ b/scripts/mbedtls_framework/psa_test_case.py @@ -113,12 +113,18 @@ def infer_dependencies(self, arguments: List[str]) -> List[str]: def set_arguments(self, arguments: List[str]) -> None: """Set test case arguments and automatically infer dependencies.""" super().set_arguments(arguments) - self.automatic_dependencies.update(self.infer_dependencies(arguments)) + dependencies = self.infer_dependencies(arguments) + self.skip_if_any_not_implemented(dependencies) + self.automatic_dependencies.update(dependencies) def set_dependencies(self, dependencies: List[str]) -> None: - """Override any previously added automatic or manual dependencies.""" + """Override any previously added automatic or manual dependencies. + + Also override any previous instruction to skip the test case. + """ self.manual_dependencies = dependencies self.automatic_dependencies.clear() + self.skip_reasons = [] def add_dependencies(self, dependencies: List[str]) -> None: """Add manual dependencies.""" @@ -135,6 +141,5 @@ def get_dependencies(self) -> List[str]: def skip_if_any_not_implemented(self, dependencies: List[str]) -> None: """Skip the test case if any of the given dependencies is not implemented.""" not_implemented = find_dependencies_not_implemented(dependencies) - if not_implemented: - self.skip_because('not implemented: ' + - ' '.join(not_implemented)) + for dep in not_implemented: + self.skip_because('not implemented: ' + dep) From e1f38eb599fffb6b3ac14b087a5f38306d89279f Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 21 Nov 2024 20:09:11 +0100 Subject: [PATCH 17/17] generate_psa_tests: use automatic dependencies for positive test cases This causes more test cases to be commented out due to mechanisms that are not implemented, because the code `generate_psa_tests.StorageFormat` was not trying to skip never-supported dependencies. To review for correctness, filter the diff of the generated files as follows to find new skip reasons: ``` grep -E '^\+## # skipped because' | sort -u ``` And check that none of the appearing mechanisms are implemented. Signed-off-by: Gilles Peskine --- scripts/generate_psa_tests.py | 43 ++++++++--------------------------- 1 file changed, 9 insertions(+), 34 deletions(-) diff --git a/scripts/generate_psa_tests.py b/scripts/generate_psa_tests.py index 0038693a5..40c701217 100755 --- a/scripts/generate_psa_tests.py +++ b/scripts/generate_psa_tests.py @@ -144,7 +144,6 @@ def test_cases_for_not_supported(self) -> Iterator[test_case.TestCase]: def test_case_for_key_generation( key_type: str, bits: int, - dependencies: List[str], *args: str, result: str = '' ) -> test_case.TestCase: @@ -158,8 +157,6 @@ def test_case_for_key_generation( tc.set_key_bits(bits) tc.set_key_pair_usage('GENERATE') tc.set_arguments([key_type] + list(args) + [result]) - tc.set_dependencies(dependencies) - tc.skip_if_any_not_implemented(dependencies) return tc class KeyGenerate: @@ -182,33 +179,18 @@ def test_cases_for_key_type_key_generation( All key types can be generated except for public keys. For public key PSA_ERROR_INVALID_ARGUMENT status is expected. """ - result = 'PSA_SUCCESS' - - import_dependencies = [psa_information.psa_want_symbol(kt.name)] - if kt.params is not None: - import_dependencies += [psa_information.psa_want_symbol(sym) - for i, sym in enumerate(kt.params)] - if kt.name.endswith('_PUBLIC_KEY'): - # The library checks whether the key type is a public key generically, - # before it reaches a point where it needs support for the specific key - # type, so it returns INVALID_ARGUMENT for unsupported public key types. - generate_dependencies = [] - result = 'PSA_ERROR_INVALID_ARGUMENT' - else: - generate_dependencies = \ - psa_information.fix_key_pair_dependencies(import_dependencies, 'GENERATE') for bits in kt.sizes_to_test(): - if kt.name == 'PSA_KEY_TYPE_RSA_KEY_PAIR': - size_dependency = "PSA_VENDOR_RSA_GENERATE_MIN_KEY_BITS <= " + str(bits) - test_dependencies = generate_dependencies + [size_dependency] - else: - test_dependencies = generate_dependencies - yield test_case_for_key_generation( + tc = test_case_for_key_generation( kt.expression, bits, - psa_information.finish_family_dependencies(test_dependencies, bits), str(bits), - result + 'PSA_ERROR_INVALID_ARGUMENT' if kt.is_public() else 'PSA_SUCCESS' ) + if kt.is_public(): + # The library checks whether the key type is a public key generically, + # before it reaches a point where it needs support for the specific key + # type, so it returns INVALID_ARGUMENT for unsupported public key types. + tc.set_dependencies([]) + yield tc def test_cases_for_key_generation(self) -> Iterator[test_case.TestCase]: """Generate test cases that exercise the generation of keys.""" @@ -496,13 +478,7 @@ def make_test_case(self, key: StorageTestData) -> test_case.TestCase: verb = 'save' if self.forward else 'read' tc = psa_test_case.TestCase() tc.set_description(verb + ' ' + key.description) - dependencies = psa_information.automatic_dependencies( - key.lifetime.string, key.type.string, - key.alg.string, key.alg2.string, - ) - dependencies = psa_information.finish_family_dependencies(dependencies, key.bits) - dependencies += psa_information.generate_deps_from_description(key.description) - dependencies = psa_information.fix_key_pair_dependencies(dependencies, 'BASIC') + tc.add_dependencies(psa_information.generate_deps_from_description(key.description)) tc.set_function('key_storage_' + verb) tc.set_key_bits(key.bits) tc.set_key_pair_usage('BASIC') @@ -522,7 +498,6 @@ def make_test_case(self, key: StorageTestData) -> test_case.TestCase: '"' + key.material.hex() + '"', '"' + key.hex() + '"', *extra_arguments]) - tc.set_dependencies(dependencies) return tc def key_for_lifetime(