diff --git a/scripts/generate_psa_tests.py b/scripts/generate_psa_tests.py index 482c827db..0f22272f9 100755 --- a/scripts/generate_psa_tests.py +++ b/scripts/generate_psa_tests.py @@ -26,7 +26,7 @@ from mbedtls_framework import test_data_generation def test_case_for_key_type_not_supported( verb: str, key_type: str, bits: int, - dependencies: List[str], + not_supported_mechanism: str, *args: str, param_descr: str = '' ) -> test_case.TestCase: @@ -35,17 +35,16 @@ def test_case_for_key_type_not_supported( """ 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_description('PSA {} {} {}-bit{} not supported' + .format(verb, short_key_type, bits, + ' ' + param_descr if param_descr else '')) + # if tc.description == 'PSA import RSA_KEY_PAIR 1024-bit not supported': + # import pdb; pdb.set_trace() tc.set_function(verb + '_not_supported') tc.set_key_bits(bits) tc.set_key_pair_usage([verb.upper()]) + tc.assumes_not_supported(not_supported_mechanism) tc.set_arguments([key_type] + list(args)) - tc.set_dependencies(dependencies) - tc.skip_if_any_not_implemented(dependencies) return tc class KeyTypeNotSupported: @@ -77,39 +76,27 @@ class KeyTypeNotSupported: # Don't generate test cases for key types that are always supported. # They would be skipped in all configurations, which is noise. return - import_dependencies = [('!' if param is None else '') + - psa_information.psa_want_symbol(kt.name)] - if kt.params is not None: - import_dependencies += [('!' if param == i else '') + - psa_information.psa_want_symbol(sym) - for i, sym in enumerate(kt.params)] - if kt.name.endswith('_PUBLIC_KEY'): - generate_dependencies = [] + if param is None: + not_supported_mechanism = kt.name else: - generate_dependencies = \ - psa_information.fix_key_pair_dependencies(import_dependencies, - ['GENERATE']) - import_dependencies = \ - psa_information.fix_key_pair_dependencies(import_dependencies, - ['IMPORT']) + assert kt.params is not None + not_supported_mechanism = kt.params[param] for bits in kt.sizes_to_test(): yield test_case_for_key_type_not_supported( 'import', kt.expression, bits, - psa_information.finish_family_dependencies(import_dependencies, bits), + not_supported_mechanism, test_case.hex_string(kt.key_material(bits)), param_descr=param_descr, ) - if not generate_dependencies and param is not None: - # If generation is impossible for this key type, rather than - # supported or not depending on implementation capabilities, - # only generate the test case once. - continue - # For public key we expect that key generation fails with - # INVALID_ARGUMENT. It is handled by KeyGenerate class. + # Don't generate not-supported test cases for key generation of + # public keys. Our implementation always returns + # PSA_ERROR_INVALID_ARGUMENT when attempting to generate a + # public key, so we cover this together with the positive cases + # in the KeyGenerate class. if not kt.is_public(): yield test_case_for_key_type_not_supported( 'generate', kt.expression, bits, - psa_information.finish_family_dependencies(generate_dependencies, bits), + not_supported_mechanism, str(bits), param_descr=param_descr, ) diff --git a/scripts/mbedtls_framework/psa_test_case.py b/scripts/mbedtls_framework/psa_test_case.py index 2c730a7f8..39b0ab37e 100644 --- a/scripts/mbedtls_framework/psa_test_case.py +++ b/scripts/mbedtls_framework/psa_test_case.py @@ -70,6 +70,7 @@ class TestCase(test_case.TestCase): self.manual_dependencies = [] #type: List[str] self.automatic_dependencies = set() #type: Set[str] self.dependency_prefix = dependency_prefix #type: Optional[str] + self.negated_dependencies = set() #type: Set[str] self.key_bits = None #type: Optional[int] self.key_pair_usage = None #type: Optional[List[str]] @@ -104,16 +105,56 @@ class TestCase(test_case.TestCase): dependencies = psa_information.fix_key_pair_dependencies(dependencies, self.key_pair_usage) if 'PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_GENERATE' in dependencies and \ + 'PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_GENERATE' not in self.negated_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 assumes_not_supported(self, name: str) -> None: + """Negate the given mechanism for automatic dependency generation. + + Call this function before set_arguments() for a test case that should + run if the given mechanism is not supported. + + Call modifiers such as set_key_bits() and set_key_pair_usage() before + calling this method, if applicable. + + A mechanism is a PSA_XXX symbol, e.g. PSA_KEY_TYPE_AES, PSA_ALG_HMAC, + etc. 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. + """ + if name == 'PSA_KEY_TYPE_RSA_KEY_PAIR' and \ + self.key_bits is not None and \ + self.key_pair_usage == ['GENERATE']: + # When RSA key pair generation is not supported, it could be + # due to the specific key size is out of range, or because + # RSA key pair generation itself is not supported. Assume the + # latter. + dep = psa_information.psa_want_symbol(name, prefix=self.dependency_prefix) + + self.negated_dependencies.add(dep + '_GENERATE') + return + dependencies = self.infer_dependencies([name]) + # * If we have more than one dependency to negate, the result would + # say that all of the dependencies are disabled, which is not + # a desirable outcome: the negation of (A and B) is (!A or !B), + # not (!A and !B). + # * If we have no dependency to negate, the result wouldn't be a + # not-supported case. + # Assert that we don't reach either such case. + assert len(dependencies) == 1 + self.negated_dependencies.add(dependencies[0]) + def set_arguments(self, arguments: List[str]) -> None: """Set test case arguments and automatically infer dependencies.""" super().set_arguments(arguments) dependencies = self.infer_dependencies(arguments) + for i in range(len(dependencies)): #pylint: disable=consider-using-enumerate + if dependencies[i] in self.negated_dependencies: + dependencies[i] = '!' + dependencies[i] self.skip_if_any_not_implemented(dependencies) self.automatic_dependencies.update(dependencies)