psa_information.py (6426B)
1 """Collect information about PSA cryptographic mechanisms. 2 """ 3 4 # Copyright The Mbed TLS Contributors 5 # SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later 6 # 7 8 import re 9 from collections import OrderedDict 10 from typing import List, Optional 11 12 from . import build_tree 13 from . import macro_collector 14 15 16 class Information: 17 """Gather information about PSA constructors.""" 18 19 def __init__(self) -> None: 20 self.constructors = self.read_psa_interface() 21 22 @staticmethod 23 def remove_unwanted_macros( 24 constructors: macro_collector.PSAMacroEnumerator 25 ) -> None: 26 """Remove constructors that should be exckuded from systematic testing.""" 27 # Mbed TLS does not support finite-field DSA, but 3.6 defines DSA 28 # identifiers for historical reasons. 29 # Don't attempt to generate any related test case. 30 # The corresponding test cases would be commented out anyway, 31 # but for DSA, we don't have enough support in the test scripts 32 # to generate these test cases. 33 constructors.key_types.discard('PSA_KEY_TYPE_DSA_KEY_PAIR') 34 constructors.key_types.discard('PSA_KEY_TYPE_DSA_PUBLIC_KEY') 35 36 def read_psa_interface(self) -> macro_collector.PSAMacroEnumerator: 37 """Return the list of known key types, algorithms, etc.""" 38 constructors = macro_collector.InputsForTest() 39 40 if build_tree.looks_like_root('.'): 41 if build_tree.looks_like_mbedtls_root('.') and \ 42 (not build_tree.is_mbedtls_3_6()): 43 header_file_names = ['tf-psa-crypto/include/psa/crypto_values.h', 44 'tf-psa-crypto/include/psa/crypto_extra.h'] 45 test_suites = ['tf-psa-crypto/tests/suites/test_suite_psa_crypto_metadata.data'] 46 else: 47 header_file_names = ['include/psa/crypto_values.h', 48 'include/psa/crypto_extra.h'] 49 test_suites = ['tests/suites/test_suite_psa_crypto_metadata.data'] 50 51 for header_file_name in header_file_names: 52 constructors.parse_header(header_file_name) 53 for test_cases in test_suites: 54 constructors.parse_test_cases(test_cases) 55 self.remove_unwanted_macros(constructors) 56 constructors.gather_arguments() 57 return constructors 58 59 60 def psa_want_symbol(name: str, prefix: Optional[str] = None) -> str: 61 """Return the PSA_WANT_xxx symbol associated with a PSA crypto feature. 62 63 You can use an altenative `prefix`, e.g. 'MBEDTLS_PSA_BUILTIN_' 64 when specifically testing builtin implementations. 65 """ 66 if prefix is None: 67 prefix = 'PSA_WANT_' 68 if name.startswith('PSA_'): 69 return prefix + name[4:] 70 else: 71 raise ValueError('Unable to determine the PSA_WANT_ symbol for ' + name) 72 73 def finish_family_dependency(dep: str, bits: int) -> str: 74 """Finish dep if it's a family dependency symbol prefix. 75 76 A family dependency symbol prefix is a PSA_WANT_ symbol that needs to be 77 qualified by the key size. If dep is such a symbol, finish it by adjusting 78 the prefix and appending the key size. Other symbols are left unchanged. 79 """ 80 return re.sub(r'_FAMILY_(.*)', r'_\1_' + str(bits), dep) 81 82 def finish_family_dependencies(dependencies: List[str], bits: int) -> List[str]: 83 """Finish any family dependency symbol prefixes. 84 85 Apply `finish_family_dependency` to each element of `dependencies`. 86 """ 87 return [finish_family_dependency(dep, bits) for dep in dependencies] 88 89 SYMBOLS_WITHOUT_DEPENDENCY = frozenset([ 90 'PSA_ALG_AEAD_WITH_AT_LEAST_THIS_LENGTH_TAG', # modifier, only in policies 91 'PSA_ALG_AEAD_WITH_SHORTENED_TAG', # modifier 92 'PSA_ALG_ANY_HASH', # only in policies 93 'PSA_ALG_AT_LEAST_THIS_LENGTH_MAC', # modifier, only in policies 94 'PSA_ALG_KEY_AGREEMENT', # chaining 95 'PSA_ALG_TRUNCATED_MAC', # modifier 96 ]) 97 def automatic_dependencies(*expressions: str, 98 prefix: Optional[str] = None) -> List[str]: 99 """Infer dependencies of a test case by looking for PSA_xxx symbols. 100 101 The arguments are strings which should be C expressions. Do not use 102 string literals or comments as this function is not smart enough to 103 skip them. 104 105 `prefix`: prefix to use in dependencies. Defaults to ``'PSA_WANT_'``. 106 Use ``'MBEDTLS_PSA_BUILTIN_'`` when specifically testing 107 builtin implementations. 108 """ 109 used = set() 110 for expr in expressions: 111 used.update(re.findall(r'PSA_(?:ALG|ECC_FAMILY|DH_FAMILY|KEY_TYPE)_\w+', expr)) 112 used.difference_update(SYMBOLS_WITHOUT_DEPENDENCY) 113 return sorted(psa_want_symbol(name, prefix=prefix) for name in used) 114 115 # Define set of regular expressions and dependencies to optionally append 116 # extra dependencies for test case based on key description. 117 118 # Skip AES test cases which require 192- or 256-bit key 119 # if MBEDTLS_AES_ONLY_128_BIT_KEY_LENGTH defined 120 AES_128BIT_ONLY_DEP_REGEX = re.compile(r'AES\s(192|256)') 121 AES_128BIT_ONLY_DEP = ['!MBEDTLS_AES_ONLY_128_BIT_KEY_LENGTH'] 122 # Skip AES/ARIA/CAMELLIA test cases which require decrypt operation in ECB mode 123 # if MBEDTLS_BLOCK_CIPHER_NO_DECRYPT enabled. 124 ECB_NO_PADDING_DEP_REGEX = re.compile(r'(AES|ARIA|CAMELLIA).*ECB_NO_PADDING') 125 ECB_NO_PADDING_DEP = ['!MBEDTLS_BLOCK_CIPHER_NO_DECRYPT'] 126 127 DEPENDENCY_FROM_DESCRIPTION = OrderedDict() 128 DEPENDENCY_FROM_DESCRIPTION[AES_128BIT_ONLY_DEP_REGEX] = AES_128BIT_ONLY_DEP 129 DEPENDENCY_FROM_DESCRIPTION[ECB_NO_PADDING_DEP_REGEX] = ECB_NO_PADDING_DEP 130 def generate_deps_from_description( 131 description: str 132 ) -> List[str]: 133 """Return additional dependencies based on test case description and REGEX. 134 """ 135 dep_list = [] 136 for regex, deps in DEPENDENCY_FROM_DESCRIPTION.items(): 137 if re.search(regex, description): 138 dep_list += deps 139 140 return dep_list 141 142 def tweak_key_pair_dependency(dep: str, usages: List[str]) -> List[str]: 143 """ 144 This helper function add the proper suffix to PSA_WANT_KEY_TYPE_xxx_KEY_PAIR 145 symbols according to the required usage. 146 """ 147 if dep.endswith('KEY_PAIR'): 148 return [dep + '_' + usage for usage in usages] 149 return [dep] 150 151 def fix_key_pair_dependencies(dep_list: List[str], usages: List[str]) -> List[str]: 152 new_list = [new_deps 153 for dep in dep_list 154 for new_deps in tweak_key_pair_dependency(dep, usages)] 155 156 return new_list