blob: eae9376669b55aa8c913bf0cda5b3985d48ea676 [file] [log] [blame]
Gilles Peskine09940492021-01-26 22:16:30 +01001#!/usr/bin/env python3
2"""Generate test data for PSA cryptographic mechanisms.
Gilles Peskine0298bda2021-03-10 02:34:37 +01003
4With no arguments, generate all test data. With non-option arguments,
5generate only the specified files.
Gilles Peskine09940492021-01-26 22:16:30 +01006"""
7
8# Copyright The Mbed TLS Contributors
Tomás González5fae5602023-11-13 11:45:12 +00009# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
Gilles Peskine09940492021-01-26 22:16:30 +010010
Gilles Peskinef8b6b502022-03-15 17:26:33 +010011import enum
Gilles Peskine14e428f2021-01-26 22:19:21 +010012import re
Gilles Peskine09940492021-01-26 22:16:30 +010013import sys
Werner Lewisdcad1e92022-08-24 11:30:03 +010014from typing import Callable, Dict, FrozenSet, Iterable, Iterator, List, Optional
Gilles Peskine09940492021-01-26 22:16:30 +010015
16import scripts_path # pylint: disable=unused-import
Tomás González2bff1bf2023-10-30 15:29:23 +000017from mbedtls_dev import crypto_data_tests
Gilles Peskine14e428f2021-01-26 22:19:21 +010018from mbedtls_dev import crypto_knowledge
Tomás González734d22c2023-10-30 15:15:45 +000019from mbedtls_dev import macro_collector #pylint: disable=unused-import
20from mbedtls_dev import psa_information
Gilles Peskine897dff92021-03-10 15:03:44 +010021from mbedtls_dev import psa_storage
Gilles Peskine14e428f2021-01-26 22:19:21 +010022from mbedtls_dev import test_case
Gilles Peskine69feebd2022-09-16 21:41:47 +020023from mbedtls_dev import test_data_generation
Gilles Peskine09940492021-01-26 22:16:30 +010024
Gilles Peskine14e428f2021-01-26 22:19:21 +010025
Przemyslaw Stekield6ead7c2021-10-11 10:15:25 +020026def test_case_for_key_type_not_supported(
Gilles Peskineb94ea512021-03-10 02:12:08 +010027 verb: str, key_type: str, bits: int,
28 dependencies: List[str],
29 *args: str,
30 param_descr: str = ''
31) -> test_case.TestCase:
32 """Return one test case exercising a key creation method
33 for an unsupported key type or size.
34 """
Tomás González734d22c2023-10-30 15:15:45 +000035 psa_information.hack_dependencies_not_implemented(dependencies)
Gilles Peskineb94ea512021-03-10 02:12:08 +010036 tc = test_case.TestCase()
Gilles Peskined79aef52022-03-17 23:42:25 +010037 short_key_type = crypto_knowledge.short_expression(key_type)
Gilles Peskineb94ea512021-03-10 02:12:08 +010038 adverb = 'not' if dependencies else 'never'
39 if param_descr:
40 adverb = param_descr + ' ' + adverb
Przemyslaw Stekield6ead7c2021-10-11 10:15:25 +020041 tc.set_description('PSA {} {} {}-bit {} supported'
42 .format(verb, short_key_type, bits, adverb))
Gilles Peskinec3b261a2024-04-10 17:19:04 +020043 tc.set_dependencies(sorted(dependencies))
Przemyslaw Stekield6ead7c2021-10-11 10:15:25 +020044 tc.set_function(verb + '_not_supported')
45 tc.set_arguments([key_type] + list(args))
46 return tc
47
Gilles Peskine4fa76bd2022-12-15 22:14:28 +010048class KeyTypeNotSupported:
49 """Generate test cases for when a key type is not supported."""
Gilles Peskineb94ea512021-03-10 02:12:08 +010050
Tomás González734d22c2023-10-30 15:15:45 +000051 def __init__(self, info: psa_information.Information) -> None:
Gilles Peskineb94ea512021-03-10 02:12:08 +010052 self.constructors = info.constructors
Gilles Peskine14e428f2021-01-26 22:19:21 +010053
Gilles Peskine60b29fe2021-02-16 14:06:50 +010054 ALWAYS_SUPPORTED = frozenset([
55 'PSA_KEY_TYPE_DERIVE',
56 'PSA_KEY_TYPE_RAW_DATA',
57 ])
Gilles Peskine14e428f2021-01-26 22:19:21 +010058 def test_cases_for_key_type_not_supported(
Gilles Peskine60b29fe2021-02-16 14:06:50 +010059 self,
Gilles Peskineaf172842021-01-27 18:24:48 +010060 kt: crypto_knowledge.KeyType,
61 param: Optional[int] = None,
62 param_descr: str = '',
Gilles Peskine3d778392021-02-17 15:11:05 +010063 ) -> Iterator[test_case.TestCase]:
Przemyslaw Stekiel32a8b842021-10-18 14:58:20 +020064 """Return test cases exercising key creation when the given type is unsupported.
Gilles Peskineaf172842021-01-27 18:24:48 +010065
66 If param is present and not None, emit test cases conditioned on this
67 parameter not being supported. If it is absent or None, emit test cases
Przemyslaw Stekiel32a8b842021-10-18 14:58:20 +020068 conditioned on the base type not being supported.
Gilles Peskineaf172842021-01-27 18:24:48 +010069 """
Gilles Peskine60b29fe2021-02-16 14:06:50 +010070 if kt.name in self.ALWAYS_SUPPORTED:
71 # Don't generate test cases for key types that are always supported.
72 # They would be skipped in all configurations, which is noise.
Gilles Peskine3d778392021-02-17 15:11:05 +010073 return
Gilles Peskineaf172842021-01-27 18:24:48 +010074 import_dependencies = [('!' if param is None else '') +
Tomás González734d22c2023-10-30 15:15:45 +000075 psa_information.psa_want_symbol(kt.name)]
Gilles Peskineaf172842021-01-27 18:24:48 +010076 if kt.params is not None:
77 import_dependencies += [('!' if param == i else '') +
Tomás González734d22c2023-10-30 15:15:45 +000078 psa_information.psa_want_symbol(sym)
Gilles Peskineaf172842021-01-27 18:24:48 +010079 for i, sym in enumerate(kt.params)]
Gilles Peskine14e428f2021-01-26 22:19:21 +010080 if kt.name.endswith('_PUBLIC_KEY'):
81 generate_dependencies = []
82 else:
83 generate_dependencies = import_dependencies
Gilles Peskine14e428f2021-01-26 22:19:21 +010084 for bits in kt.sizes_to_test():
Przemyslaw Stekield6ead7c2021-10-11 10:15:25 +020085 yield test_case_for_key_type_not_supported(
Gilles Peskine7f756872021-02-16 12:13:12 +010086 'import', kt.expression, bits,
Tomás González734d22c2023-10-30 15:15:45 +000087 psa_information.finish_family_dependencies(import_dependencies, bits),
Gilles Peskineaf172842021-01-27 18:24:48 +010088 test_case.hex_string(kt.key_material(bits)),
89 param_descr=param_descr,
Gilles Peskine3d778392021-02-17 15:11:05 +010090 )
Gilles Peskineaf172842021-01-27 18:24:48 +010091 if not generate_dependencies and param is not None:
92 # If generation is impossible for this key type, rather than
93 # supported or not depending on implementation capabilities,
94 # only generate the test case once.
95 continue
Przemyslaw Stekiel1ab3a5c2021-11-02 10:50:44 +010096 # For public key we expect that key generation fails with
97 # INVALID_ARGUMENT. It is handled by KeyGenerate class.
Gilles Peskine989c13d2022-03-17 12:52:24 +010098 if not kt.is_public():
Przemyslaw Stekield6ead7c2021-10-11 10:15:25 +020099 yield test_case_for_key_type_not_supported(
100 'generate', kt.expression, bits,
Tomás González734d22c2023-10-30 15:15:45 +0000101 psa_information.finish_family_dependencies(generate_dependencies, bits),
Przemyslaw Stekield6ead7c2021-10-11 10:15:25 +0200102 str(bits),
103 param_descr=param_descr,
104 )
Gilles Peskine14e428f2021-01-26 22:19:21 +0100105 # To be added: derive
Gilles Peskine14e428f2021-01-26 22:19:21 +0100106
Gilles Peskineb93f8542021-04-19 13:50:25 +0200107 ECC_KEY_TYPES = ('PSA_KEY_TYPE_ECC_KEY_PAIR',
108 'PSA_KEY_TYPE_ECC_PUBLIC_KEY')
109
Gilles Peskine3d778392021-02-17 15:11:05 +0100110 def test_cases_for_not_supported(self) -> Iterator[test_case.TestCase]:
Gilles Peskine14e428f2021-01-26 22:19:21 +0100111 """Generate test cases that exercise the creation of keys of unsupported types."""
Gilles Peskine14e428f2021-01-26 22:19:21 +0100112 for key_type in sorted(self.constructors.key_types):
Gilles Peskineb93f8542021-04-19 13:50:25 +0200113 if key_type in self.ECC_KEY_TYPES:
114 continue
Gilles Peskine14e428f2021-01-26 22:19:21 +0100115 kt = crypto_knowledge.KeyType(key_type)
Gilles Peskine3d778392021-02-17 15:11:05 +0100116 yield from self.test_cases_for_key_type_not_supported(kt)
Gilles Peskineaf172842021-01-27 18:24:48 +0100117 for curve_family in sorted(self.constructors.ecc_curves):
Gilles Peskineb93f8542021-04-19 13:50:25 +0200118 for constr in self.ECC_KEY_TYPES:
Gilles Peskineaf172842021-01-27 18:24:48 +0100119 kt = crypto_knowledge.KeyType(constr, [curve_family])
Gilles Peskine3d778392021-02-17 15:11:05 +0100120 yield from self.test_cases_for_key_type_not_supported(
Gilles Peskineaf172842021-01-27 18:24:48 +0100121 kt, param_descr='type')
Gilles Peskine3d778392021-02-17 15:11:05 +0100122 yield from self.test_cases_for_key_type_not_supported(
Gilles Peskineaf172842021-01-27 18:24:48 +0100123 kt, 0, param_descr='curve')
Gilles Peskineb94ea512021-03-10 02:12:08 +0100124
Przemyslaw Stekiel997caf82021-10-15 15:21:51 +0200125def test_case_for_key_generation(
126 key_type: str, bits: int,
Przemyslaw Stekiel997caf82021-10-15 15:21:51 +0200127 *args: str,
Przemyslaw Stekielc03b7c52021-10-20 11:59:50 +0200128 result: str = ''
Przemyslaw Stekiel997caf82021-10-15 15:21:51 +0200129) -> test_case.TestCase:
130 """Return one test case exercising a key generation.
131 """
Gilles Peskine6281cf42024-04-10 16:07:29 +0200132 tc = psa_information.TestCase()
Gilles Peskined79aef52022-03-17 23:42:25 +0100133 short_key_type = crypto_knowledge.short_expression(key_type)
Przemyslaw Stekiel997caf82021-10-15 15:21:51 +0200134 tc.set_description('PSA {} {}-bit'
Przemyslaw Stekielc03b7c52021-10-20 11:59:50 +0200135 .format(short_key_type, bits))
Przemyslaw Stekiel997caf82021-10-15 15:21:51 +0200136 tc.set_function('generate_key')
Gilles Peskine6281cf42024-04-10 16:07:29 +0200137 tc.set_key_bits(bits)
Przemyslaw Stekiel1ab3a5c2021-11-02 10:50:44 +0100138 tc.set_arguments([key_type] + list(args) + [result])
Przemyslaw Stekiel997caf82021-10-15 15:21:51 +0200139 return tc
140
141class KeyGenerate:
142 """Generate positive and negative (invalid argument) test cases for key generation."""
143
Tomás González734d22c2023-10-30 15:15:45 +0000144 def __init__(self, info: psa_information.Information) -> None:
Przemyslaw Stekiel997caf82021-10-15 15:21:51 +0200145 self.constructors = info.constructors
146
Przemyslaw Stekielc03b7c52021-10-20 11:59:50 +0200147 ECC_KEY_TYPES = ('PSA_KEY_TYPE_ECC_KEY_PAIR',
148 'PSA_KEY_TYPE_ECC_PUBLIC_KEY')
149
Przemyslaw Stekiel1ab3a5c2021-11-02 10:50:44 +0100150 @staticmethod
Przemyslaw Stekiel997caf82021-10-15 15:21:51 +0200151 def test_cases_for_key_type_key_generation(
Przemyslaw Stekielc03b7c52021-10-20 11:59:50 +0200152 kt: crypto_knowledge.KeyType
Przemyslaw Stekiel997caf82021-10-15 15:21:51 +0200153 ) -> Iterator[test_case.TestCase]:
154 """Return test cases exercising key generation.
155
156 All key types can be generated except for public keys. For public key
157 PSA_ERROR_INVALID_ARGUMENT status is expected.
158 """
159 result = 'PSA_SUCCESS'
Przemyslaw Stekiel997caf82021-10-15 15:21:51 +0200160 if kt.name.endswith('_PUBLIC_KEY'):
Przemyslaw Stekiel997caf82021-10-15 15:21:51 +0200161 result = 'PSA_ERROR_INVALID_ARGUMENT'
Przemyslaw Stekiel997caf82021-10-15 15:21:51 +0200162 for bits in kt.sizes_to_test():
Gilles Peskine6281cf42024-04-10 16:07:29 +0200163 tc = test_case_for_key_generation(
Przemyslaw Stekiel997caf82021-10-15 15:21:51 +0200164 kt.expression, bits,
Przemyslaw Stekiel997caf82021-10-15 15:21:51 +0200165 str(bits),
Przemyslaw Stekielc03b7c52021-10-20 11:59:50 +0200166 result
Przemyslaw Stekiel997caf82021-10-15 15:21:51 +0200167 )
Gilles Peskine6281cf42024-04-10 16:07:29 +0200168 if result == 'PSA_ERROR_INVALID_ARGUMENT':
169 # The library checks whether the key type is a public key generically,
170 # before it reaches a point where it needs support for the specific key
171 # type, so it returns INVALID_ARGUMENT for unsupported public key types.
172 tc.set_dependencies([])
173 elif kt.name == 'PSA_KEY_TYPE_RSA_KEY_PAIR':
174 # A necessary deviation because PSA_WANT symbols don't
175 # distinguish between key generation and usage, but for
176 # RSA key generation has an extra requirement.
177 tc.dependencies.insert(0, 'MBEDTLS_GENPRIME')
178 yield tc
Przemyslaw Stekiel997caf82021-10-15 15:21:51 +0200179
Przemyslaw Stekiel997caf82021-10-15 15:21:51 +0200180 def test_cases_for_key_generation(self) -> Iterator[test_case.TestCase]:
181 """Generate test cases that exercise the generation of keys."""
182 for key_type in sorted(self.constructors.key_types):
183 if key_type in self.ECC_KEY_TYPES:
184 continue
185 kt = crypto_knowledge.KeyType(key_type)
186 yield from self.test_cases_for_key_type_key_generation(kt)
187 for curve_family in sorted(self.constructors.ecc_curves):
188 for constr in self.ECC_KEY_TYPES:
189 kt = crypto_knowledge.KeyType(constr, [curve_family])
Przemyslaw Stekielc03b7c52021-10-20 11:59:50 +0200190 yield from self.test_cases_for_key_type_key_generation(kt)
Przemyslaw Stekiel997caf82021-10-15 15:21:51 +0200191
Gilles Peskinec05158b2021-04-27 20:40:10 +0200192class OpFail:
193 """Generate test cases for operations that must fail."""
194 #pylint: disable=too-few-public-methods
195
Gilles Peskinef8b6b502022-03-15 17:26:33 +0100196 class Reason(enum.Enum):
197 NOT_SUPPORTED = 0
198 INVALID = 1
199 INCOMPATIBLE = 2
Gilles Peskinec2fc2412021-04-29 21:56:59 +0200200 PUBLIC = 3
Gilles Peskinef8b6b502022-03-15 17:26:33 +0100201
Tomás González734d22c2023-10-30 15:15:45 +0000202 def __init__(self, info: psa_information.Information) -> None:
Gilles Peskinec05158b2021-04-27 20:40:10 +0200203 self.constructors = info.constructors
Gilles Peskinef8b6b502022-03-15 17:26:33 +0100204 key_type_expressions = self.constructors.generate_expressions(
205 sorted(self.constructors.key_types)
206 )
207 self.key_types = [crypto_knowledge.KeyType(kt_expr)
208 for kt_expr in key_type_expressions]
Gilles Peskinec05158b2021-04-27 20:40:10 +0200209
Gilles Peskinef8b6b502022-03-15 17:26:33 +0100210 def make_test_case(
211 self,
212 alg: crypto_knowledge.Algorithm,
213 category: crypto_knowledge.AlgorithmCategory,
214 reason: 'Reason',
215 kt: Optional[crypto_knowledge.KeyType] = None,
216 not_deps: FrozenSet[str] = frozenset(),
217 ) -> test_case.TestCase:
218 """Construct a failure test case for a one-key or keyless operation."""
219 #pylint: disable=too-many-arguments,too-many-locals
Gilles Peskinea2180472021-04-27 21:03:43 +0200220 tc = test_case.TestCase()
Gilles Peskined79aef52022-03-17 23:42:25 +0100221 pretty_alg = alg.short_expression()
Gilles Peskined0964452021-04-29 21:35:03 +0200222 if reason == self.Reason.NOT_SUPPORTED:
223 short_deps = [re.sub(r'PSA_WANT_ALG_', r'', dep)
224 for dep in not_deps]
225 pretty_reason = '!' + '&'.join(sorted(short_deps))
226 else:
227 pretty_reason = reason.name.lower()
Gilles Peskinef8b6b502022-03-15 17:26:33 +0100228 if kt:
229 key_type = kt.expression
Gilles Peskined79aef52022-03-17 23:42:25 +0100230 pretty_type = kt.short_expression()
Gilles Peskinea2180472021-04-27 21:03:43 +0200231 else:
Gilles Peskinef8b6b502022-03-15 17:26:33 +0100232 key_type = ''
233 pretty_type = ''
234 tc.set_description('PSA {} {}: {}{}'
235 .format(category.name.lower(),
236 pretty_alg,
237 pretty_reason,
238 ' with ' + pretty_type if pretty_type else ''))
Tomás González734d22c2023-10-30 15:15:45 +0000239 dependencies = psa_information.automatic_dependencies(alg.base_expression, key_type)
Gilles Peskinef8b6b502022-03-15 17:26:33 +0100240 for i, dep in enumerate(dependencies):
241 if dep in not_deps:
242 dependencies[i] = '!' + dep
Gilles Peskinec3b261a2024-04-10 17:19:04 +0200243 tc.set_dependencies(sorted(dependencies))
Gilles Peskinef8b6b502022-03-15 17:26:33 +0100244 tc.set_function(category.name.lower() + '_fail')
David Horstmann4fc7e0e2023-01-24 18:53:15 +0000245 arguments = [] # type: List[str]
Gilles Peskinef8b6b502022-03-15 17:26:33 +0100246 if kt:
247 key_material = kt.key_material(kt.sizes_to_test()[0])
248 arguments += [key_type, test_case.hex_string(key_material)]
249 arguments.append(alg.expression)
Gilles Peskinec2fc2412021-04-29 21:56:59 +0200250 if category.is_asymmetric():
251 arguments.append('1' if reason == self.Reason.PUBLIC else '0')
Gilles Peskinef8b6b502022-03-15 17:26:33 +0100252 error = ('NOT_SUPPORTED' if reason == self.Reason.NOT_SUPPORTED else
253 'INVALID_ARGUMENT')
254 arguments.append('PSA_ERROR_' + error)
255 tc.set_arguments(arguments)
256 return tc
Gilles Peskinea2180472021-04-27 21:03:43 +0200257
Gilles Peskinef8b6b502022-03-15 17:26:33 +0100258 def no_key_test_cases(
259 self,
260 alg: crypto_knowledge.Algorithm,
261 category: crypto_knowledge.AlgorithmCategory,
262 ) -> Iterator[test_case.TestCase]:
263 """Generate failure test cases for keyless operations with the specified algorithm."""
Gilles Peskine23cb12e2021-04-29 20:54:40 +0200264 if alg.can_do(category):
Gilles Peskinef8b6b502022-03-15 17:26:33 +0100265 # Compatible operation, unsupported algorithm
Tomás González734d22c2023-10-30 15:15:45 +0000266 for dep in psa_information.automatic_dependencies(alg.base_expression):
Gilles Peskinef8b6b502022-03-15 17:26:33 +0100267 yield self.make_test_case(alg, category,
268 self.Reason.NOT_SUPPORTED,
269 not_deps=frozenset([dep]))
270 else:
271 # Incompatible operation, supported algorithm
272 yield self.make_test_case(alg, category, self.Reason.INVALID)
273
274 def one_key_test_cases(
275 self,
276 alg: crypto_knowledge.Algorithm,
277 category: crypto_knowledge.AlgorithmCategory,
278 ) -> Iterator[test_case.TestCase]:
279 """Generate failure test cases for one-key operations with the specified algorithm."""
280 for kt in self.key_types:
281 key_is_compatible = kt.can_do(alg)
Gilles Peskine23cb12e2021-04-29 20:54:40 +0200282 if key_is_compatible and alg.can_do(category):
Gilles Peskinef8b6b502022-03-15 17:26:33 +0100283 # Compatible key and operation, unsupported algorithm
Tomás González734d22c2023-10-30 15:15:45 +0000284 for dep in psa_information.automatic_dependencies(alg.base_expression):
Gilles Peskinef8b6b502022-03-15 17:26:33 +0100285 yield self.make_test_case(alg, category,
286 self.Reason.NOT_SUPPORTED,
287 kt=kt, not_deps=frozenset([dep]))
Gilles Peskinec2fc2412021-04-29 21:56:59 +0200288 # Public key for a private-key operation
289 if category.is_asymmetric() and kt.is_public():
290 yield self.make_test_case(alg, category,
291 self.Reason.PUBLIC,
292 kt=kt)
Gilles Peskinef8b6b502022-03-15 17:26:33 +0100293 elif key_is_compatible:
294 # Compatible key, incompatible operation, supported algorithm
295 yield self.make_test_case(alg, category,
296 self.Reason.INVALID,
297 kt=kt)
Gilles Peskine23cb12e2021-04-29 20:54:40 +0200298 elif alg.can_do(category):
Gilles Peskinef8b6b502022-03-15 17:26:33 +0100299 # Incompatible key, compatible operation, supported algorithm
300 yield self.make_test_case(alg, category,
301 self.Reason.INCOMPATIBLE,
302 kt=kt)
303 else:
304 # Incompatible key and operation. Don't test cases where
305 # multiple things are wrong, to keep the number of test
306 # cases reasonable.
307 pass
308
309 def test_cases_for_algorithm(
310 self,
311 alg: crypto_knowledge.Algorithm,
312 ) -> Iterator[test_case.TestCase]:
Gilles Peskinea2180472021-04-27 21:03:43 +0200313 """Generate operation failure test cases for the specified algorithm."""
Gilles Peskinef8b6b502022-03-15 17:26:33 +0100314 for category in crypto_knowledge.AlgorithmCategory:
315 if category == crypto_knowledge.AlgorithmCategory.PAKE:
316 # PAKE operations are not implemented yet
317 pass
318 elif category.requires_key():
319 yield from self.one_key_test_cases(alg, category)
320 else:
321 yield from self.no_key_test_cases(alg, category)
Gilles Peskinea2180472021-04-27 21:03:43 +0200322
Gilles Peskinec05158b2021-04-27 20:40:10 +0200323 def all_test_cases(self) -> Iterator[test_case.TestCase]:
324 """Generate all test cases for operations that must fail."""
Gilles Peskinea2180472021-04-27 21:03:43 +0200325 algorithms = sorted(self.constructors.algorithms)
Gilles Peskinef8b6b502022-03-15 17:26:33 +0100326 for expr in self.constructors.generate_expressions(algorithms):
327 alg = crypto_knowledge.Algorithm(expr)
Gilles Peskinea2180472021-04-27 21:03:43 +0200328 yield from self.test_cases_for_algorithm(alg)
Gilles Peskinec05158b2021-04-27 20:40:10 +0200329
330
Gilles Peskine897dff92021-03-10 15:03:44 +0100331class StorageKey(psa_storage.Key):
332 """Representation of a key for storage format testing."""
333
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200334 IMPLICIT_USAGE_FLAGS = {
335 'PSA_KEY_USAGE_SIGN_HASH': 'PSA_KEY_USAGE_SIGN_MESSAGE',
336 'PSA_KEY_USAGE_VERIFY_HASH': 'PSA_KEY_USAGE_VERIFY_MESSAGE'
337 } #type: Dict[str, str]
338 """Mapping of usage flags to the flags that they imply."""
339
340 def __init__(
341 self,
Gilles Peskined9af9782022-03-17 22:32:59 +0100342 usage: Iterable[str],
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200343 without_implicit_usage: Optional[bool] = False,
344 **kwargs
345 ) -> None:
346 """Prepare to generate a key.
347
348 * `usage` : The usage flags used for the key.
Tom Cosgrove49f99bc2022-12-04 16:44:21 +0000349 * `without_implicit_usage`: Flag to define to apply the usage extension
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200350 """
Gilles Peskined9af9782022-03-17 22:32:59 +0100351 usage_flags = set(usage)
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200352 if not without_implicit_usage:
Gilles Peskined9af9782022-03-17 22:32:59 +0100353 for flag in sorted(usage_flags):
354 if flag in self.IMPLICIT_USAGE_FLAGS:
355 usage_flags.add(self.IMPLICIT_USAGE_FLAGS[flag])
356 if usage_flags:
357 usage_expression = ' | '.join(sorted(usage_flags))
358 else:
359 usage_expression = '0'
360 super().__init__(usage=usage_expression, **kwargs)
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200361
362class StorageTestData(StorageKey):
363 """Representation of test case data for storage format testing."""
364
gabor-mezei-arm044fefc2021-06-24 10:16:44 +0200365 def __init__(
366 self,
367 description: str,
Gilles Peskined9af9782022-03-17 22:32:59 +0100368 expected_usage: Optional[List[str]] = None,
gabor-mezei-arm044fefc2021-06-24 10:16:44 +0200369 **kwargs
370 ) -> None:
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200371 """Prepare to generate test data
gabor-mezei-arm044fefc2021-06-24 10:16:44 +0200372
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200373 * `description` : used for the the test case names
374 * `expected_usage`: the usage flags generated as the expected usage flags
375 in the test cases. CAn differ from the usage flags
376 stored in the keys because of the usage flags extension.
gabor-mezei-arm044fefc2021-06-24 10:16:44 +0200377 """
Gilles Peskine897dff92021-03-10 15:03:44 +0100378 super().__init__(**kwargs)
379 self.description = description #type: str
Gilles Peskined9af9782022-03-17 22:32:59 +0100380 if expected_usage is None:
381 self.expected_usage = self.usage #type: psa_storage.Expr
382 elif expected_usage:
383 self.expected_usage = psa_storage.Expr(' | '.join(expected_usage))
384 else:
385 self.expected_usage = psa_storage.Expr(0)
gabor-mezei-arm15c1f032021-06-24 10:04:38 +0200386
Gilles Peskine897dff92021-03-10 15:03:44 +0100387class StorageFormat:
388 """Storage format stability test cases."""
389
Tomás González734d22c2023-10-30 15:15:45 +0000390 def __init__(self, info: psa_information.Information, version: int, forward: bool) -> None:
Gilles Peskine897dff92021-03-10 15:03:44 +0100391 """Prepare to generate test cases for storage format stability.
392
Tomás González734d22c2023-10-30 15:15:45 +0000393 * `info`: information about the API. See the `psa_information.Information` class.
Gilles Peskine897dff92021-03-10 15:03:44 +0100394 * `version`: the storage format version to generate test cases for.
395 * `forward`: if true, generate forward compatibility test cases which
396 save a key and check that its representation is as intended. Otherwise
397 generate backward compatibility test cases which inject a key
398 representation and check that it can be read and used.
399 """
gabor-mezei-arm0bdb84e2021-06-23 17:01:44 +0200400 self.constructors = info.constructors #type: macro_collector.PSAMacroEnumerator
401 self.version = version #type: int
402 self.forward = forward #type: bool
Gilles Peskine897dff92021-03-10 15:03:44 +0100403
Gilles Peskine32611242022-03-19 12:09:13 +0100404 RSA_OAEP_RE = re.compile(r'PSA_ALG_RSA_OAEP\((.*)\)\Z')
Gilles Peskine8ddced52022-03-19 15:36:09 +0100405 BRAINPOOL_RE = re.compile(r'PSA_KEY_TYPE_\w+\(PSA_ECC_FAMILY_BRAINPOOL_\w+\)\Z')
Gilles Peskine32611242022-03-19 12:09:13 +0100406 @classmethod
Gilles Peskine8ddced52022-03-19 15:36:09 +0100407 def exercise_key_with_algorithm(
Gilles Peskine32611242022-03-19 12:09:13 +0100408 cls,
409 key_type: psa_storage.Expr, bits: int,
410 alg: psa_storage.Expr
411 ) -> bool:
Gilles Peskine1efe7fd2022-12-15 23:03:19 +0100412 """Whether to exercise the given key with the given algorithm.
Gilles Peskine32611242022-03-19 12:09:13 +0100413
414 Normally only the type and algorithm matter for compatibility, and
415 this is handled in crypto_knowledge.KeyType.can_do(). This function
416 exists to detect exceptional cases. Exceptional cases detected here
417 are not tested in OpFail and should therefore have manually written
418 test cases.
419 """
Gilles Peskine8ddced52022-03-19 15:36:09 +0100420 # Some test keys have the RAW_DATA type and attributes that don't
421 # necessarily make sense. We do this to validate numerical
422 # encodings of the attributes.
423 # Raw data keys have no useful exercise anyway so there is no
424 # loss of test coverage.
425 if key_type.string == 'PSA_KEY_TYPE_RAW_DATA':
426 return False
Gilles Peskinec7686002022-04-20 16:31:37 +0200427 # Mbed TLS only supports 128-bit keys for RC4.
428 if key_type.string == 'PSA_KEY_TYPE_ARC4' and bits != 128:
429 return False
Gilles Peskine32611242022-03-19 12:09:13 +0100430 # OAEP requires room for two hashes plus wrapping
431 m = cls.RSA_OAEP_RE.match(alg.string)
432 if m:
433 hash_alg = m.group(1)
434 hash_length = crypto_knowledge.Algorithm.hash_length(hash_alg)
435 key_length = (bits + 7) // 8
436 # Leave enough room for at least one byte of plaintext
437 return key_length > 2 * hash_length + 2
Gilles Peskine8ddced52022-03-19 15:36:09 +0100438 # There's nothing wrong with ECC keys on Brainpool curves,
439 # but operations with them are very slow. So we only exercise them
440 # with a single algorithm, not with all possible hashes. We do
441 # exercise other curves with all algorithms so test coverage is
442 # perfectly adequate like this.
443 m = cls.BRAINPOOL_RE.match(key_type.string)
444 if m and alg.string != 'PSA_ALG_ECDSA_ANY':
445 return False
Gilles Peskine32611242022-03-19 12:09:13 +0100446 return True
447
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200448 def make_test_case(self, key: StorageTestData) -> test_case.TestCase:
Gilles Peskine897dff92021-03-10 15:03:44 +0100449 """Construct a storage format test case for the given key.
450
451 If ``forward`` is true, generate a forward compatibility test case:
452 create a key and validate that it has the expected representation.
453 Otherwise generate a backward compatibility test case: inject the
454 key representation into storage and validate that it can be read
455 correctly.
456 """
457 verb = 'save' if self.forward else 'read'
Gilles Peskinec7b58d52024-04-10 15:55:39 +0200458 tc = psa_information.TestCase()
Gilles Peskine930ccef2022-03-18 00:02:15 +0100459 tc.set_description(verb + ' ' + key.description)
Gilles Peskinec7b58d52024-04-10 15:55:39 +0200460 tc.set_key_bits(key.bits)
Gilles Peskine897dff92021-03-10 15:03:44 +0100461 tc.set_function('key_storage_' + verb)
462 if self.forward:
463 extra_arguments = []
464 else:
Gilles Peskine643eb832021-04-21 20:11:33 +0200465 flags = []
Gilles Peskine8ddced52022-03-19 15:36:09 +0100466 if self.exercise_key_with_algorithm(key.type, key.bits, key.alg):
Gilles Peskine643eb832021-04-21 20:11:33 +0200467 flags.append('TEST_FLAG_EXERCISE')
468 if 'READ_ONLY' in key.lifetime.string:
469 flags.append('TEST_FLAG_READ_ONLY')
470 extra_arguments = [' | '.join(flags) if flags else '0']
Gilles Peskine897dff92021-03-10 15:03:44 +0100471 tc.set_arguments([key.lifetime.string,
472 key.type.string, str(key.bits),
Gilles Peskined9af9782022-03-17 22:32:59 +0100473 key.expected_usage.string,
474 key.alg.string, key.alg2.string,
Gilles Peskine897dff92021-03-10 15:03:44 +0100475 '"' + key.material.hex() + '"',
476 '"' + key.hex() + '"',
477 *extra_arguments])
478 return tc
479
Gilles Peskineefb584d2021-04-21 22:05:34 +0200480 def key_for_lifetime(
481 self,
482 lifetime: str,
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200483 ) -> StorageTestData:
Gilles Peskineefb584d2021-04-21 22:05:34 +0200484 """Construct a test key for the given lifetime."""
485 short = lifetime
486 short = re.sub(r'PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION',
487 r'', short)
Gilles Peskined79aef52022-03-17 23:42:25 +0100488 short = crypto_knowledge.short_expression(short)
Gilles Peskineefb584d2021-04-21 22:05:34 +0200489 description = 'lifetime: ' + short
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200490 key = StorageTestData(version=self.version,
491 id=1, lifetime=lifetime,
492 type='PSA_KEY_TYPE_RAW_DATA', bits=8,
Gilles Peskined9af9782022-03-17 22:32:59 +0100493 usage=['PSA_KEY_USAGE_EXPORT'], alg=0, alg2=0,
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200494 material=b'L',
495 description=description)
496 return key
Gilles Peskineefb584d2021-04-21 22:05:34 +0200497
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200498 def all_keys_for_lifetimes(self) -> Iterator[StorageTestData]:
Gilles Peskineefb584d2021-04-21 22:05:34 +0200499 """Generate test keys covering lifetimes."""
500 lifetimes = sorted(self.constructors.lifetimes)
501 expressions = self.constructors.generate_expressions(lifetimes)
502 for lifetime in expressions:
503 # Don't attempt to create or load a volatile key in storage
504 if 'VOLATILE' in lifetime:
505 continue
506 # Don't attempt to create a read-only key in storage,
507 # but do attempt to load one.
508 if 'READ_ONLY' in lifetime and self.forward:
509 continue
gabor-mezei-arm5ea30372021-06-28 19:26:55 +0200510 yield self.key_for_lifetime(lifetime)
Gilles Peskineefb584d2021-04-21 22:05:34 +0200511
Gilles Peskinea296e482022-02-24 18:58:08 +0100512 def key_for_usage_flags(
Gilles Peskine897dff92021-03-10 15:03:44 +0100513 self,
514 usage_flags: List[str],
gabor-mezei-armd71659f2021-06-24 09:42:02 +0200515 short: Optional[str] = None,
Gilles Peskinea296e482022-02-24 18:58:08 +0100516 test_implicit_usage: Optional[bool] = True
517 ) -> StorageTestData:
Gilles Peskine897dff92021-03-10 15:03:44 +0100518 """Construct a test key for the given key usage."""
Gilles Peskinea296e482022-02-24 18:58:08 +0100519 extra_desc = ' without implication' if test_implicit_usage else ''
Gilles Peskined9af9782022-03-17 22:32:59 +0100520 description = 'usage' + extra_desc + ': '
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200521 key1 = StorageTestData(version=self.version,
522 id=1, lifetime=0x00000001,
523 type='PSA_KEY_TYPE_RAW_DATA', bits=8,
Gilles Peskined9af9782022-03-17 22:32:59 +0100524 expected_usage=usage_flags,
Gilles Peskinea296e482022-02-24 18:58:08 +0100525 without_implicit_usage=not test_implicit_usage,
Gilles Peskined9af9782022-03-17 22:32:59 +0100526 usage=usage_flags, alg=0, alg2=0,
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200527 material=b'K',
528 description=description)
Gilles Peskined9af9782022-03-17 22:32:59 +0100529 if short is None:
Gilles Peskined79aef52022-03-17 23:42:25 +0100530 usage_expr = key1.expected_usage.string
531 key1.description += crypto_knowledge.short_expression(usage_expr)
Gilles Peskined9af9782022-03-17 22:32:59 +0100532 else:
533 key1.description += short
Gilles Peskinea296e482022-02-24 18:58:08 +0100534 return key1
Gilles Peskine897dff92021-03-10 15:03:44 +0100535
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200536 def generate_keys_for_usage_flags(self, **kwargs) -> Iterator[StorageTestData]:
Gilles Peskine897dff92021-03-10 15:03:44 +0100537 """Generate test keys covering usage flags."""
538 known_flags = sorted(self.constructors.key_usage_flags)
Gilles Peskinea296e482022-02-24 18:58:08 +0100539 yield self.key_for_usage_flags(['0'], **kwargs)
gabor-mezei-arm5ea30372021-06-28 19:26:55 +0200540 for usage_flag in known_flags:
Gilles Peskinea296e482022-02-24 18:58:08 +0100541 yield self.key_for_usage_flags([usage_flag], **kwargs)
gabor-mezei-arm5ea30372021-06-28 19:26:55 +0200542 for flag1, flag2 in zip(known_flags,
543 known_flags[1:] + [known_flags[0]]):
Gilles Peskinea296e482022-02-24 18:58:08 +0100544 yield self.key_for_usage_flags([flag1, flag2], **kwargs)
gabor-mezei-armbce85272021-06-24 14:38:51 +0200545
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200546 def generate_key_for_all_usage_flags(self) -> Iterator[StorageTestData]:
gabor-mezei-armbce85272021-06-24 14:38:51 +0200547 known_flags = sorted(self.constructors.key_usage_flags)
Gilles Peskinea296e482022-02-24 18:58:08 +0100548 yield self.key_for_usage_flags(known_flags, short='all known')
gabor-mezei-armbce85272021-06-24 14:38:51 +0200549
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200550 def all_keys_for_usage_flags(self) -> Iterator[StorageTestData]:
gabor-mezei-arm5ea30372021-06-28 19:26:55 +0200551 yield from self.generate_keys_for_usage_flags()
552 yield from self.generate_key_for_all_usage_flags()
Gilles Peskine897dff92021-03-10 15:03:44 +0100553
Gilles Peskine6213a002021-04-29 22:28:07 +0200554 def key_for_type_and_alg(
555 self,
556 kt: crypto_knowledge.KeyType,
557 bits: int,
558 alg: Optional[crypto_knowledge.Algorithm] = None,
559 ) -> StorageTestData:
560 """Construct a test key of the given type.
561
562 If alg is not None, this key allows it.
563 """
Gilles Peskined9af9782022-03-17 22:32:59 +0100564 usage_flags = ['PSA_KEY_USAGE_EXPORT']
Gilles Peskine0de11432022-03-18 09:58:09 +0100565 alg1 = 0 #type: psa_storage.Exprable
Gilles Peskine6213a002021-04-29 22:28:07 +0200566 alg2 = 0
Gilles Peskine0de11432022-03-18 09:58:09 +0100567 if alg is not None:
568 alg1 = alg.expression
569 usage_flags += alg.usage_flags(public=kt.is_public())
Gilles Peskine6213a002021-04-29 22:28:07 +0200570 key_material = kt.key_material(bits)
Gilles Peskine930ccef2022-03-18 00:02:15 +0100571 description = 'type: {} {}-bit'.format(kt.short_expression(1), bits)
Gilles Peskine6213a002021-04-29 22:28:07 +0200572 if alg is not None:
Gilles Peskine930ccef2022-03-18 00:02:15 +0100573 description += ', ' + alg.short_expression(1)
Gilles Peskine6213a002021-04-29 22:28:07 +0200574 key = StorageTestData(version=self.version,
575 id=1, lifetime=0x00000001,
576 type=kt.expression, bits=bits,
577 usage=usage_flags, alg=alg1, alg2=alg2,
578 material=key_material,
579 description=description)
580 return key
581
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100582 def keys_for_type(
583 self,
584 key_type: str,
Gilles Peskine6213a002021-04-29 22:28:07 +0200585 all_algorithms: List[crypto_knowledge.Algorithm],
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200586 ) -> Iterator[StorageTestData]:
Gilles Peskine6213a002021-04-29 22:28:07 +0200587 """Generate test keys for the given key type."""
588 kt = crypto_knowledge.KeyType(key_type)
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100589 for bits in kt.sizes_to_test():
Gilles Peskine6213a002021-04-29 22:28:07 +0200590 # Test a non-exercisable key, as well as exercisable keys for
591 # each compatible algorithm.
592 # To do: test reading a key from storage with an incompatible
593 # or unsupported algorithm.
594 yield self.key_for_type_and_alg(kt, bits)
595 compatible_algorithms = [alg for alg in all_algorithms
596 if kt.can_do(alg)]
597 for alg in compatible_algorithms:
598 yield self.key_for_type_and_alg(kt, bits, alg)
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100599
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200600 def all_keys_for_types(self) -> Iterator[StorageTestData]:
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100601 """Generate test keys covering key types and their representations."""
Gilles Peskineb93f8542021-04-19 13:50:25 +0200602 key_types = sorted(self.constructors.key_types)
Gilles Peskine6213a002021-04-29 22:28:07 +0200603 all_algorithms = [crypto_knowledge.Algorithm(alg)
604 for alg in self.constructors.generate_expressions(
605 sorted(self.constructors.algorithms)
606 )]
gabor-mezei-arm5ea30372021-06-28 19:26:55 +0200607 for key_type in self.constructors.generate_expressions(key_types):
Gilles Peskine6213a002021-04-29 22:28:07 +0200608 yield from self.keys_for_type(key_type, all_algorithms)
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100609
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200610 def keys_for_algorithm(self, alg: str) -> Iterator[StorageTestData]:
Gilles Peskine6213a002021-04-29 22:28:07 +0200611 """Generate test keys for the encoding of the specified algorithm."""
612 # These test cases only validate the encoding of algorithms, not
613 # whether the key read from storage is suitable for an operation.
614 # `keys_for_types` generate read tests with an algorithm and a
615 # compatible key.
Gilles Peskine930ccef2022-03-18 00:02:15 +0100616 descr = crypto_knowledge.short_expression(alg, 1)
Gilles Peskined9af9782022-03-17 22:32:59 +0100617 usage = ['PSA_KEY_USAGE_EXPORT']
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200618 key1 = StorageTestData(version=self.version,
619 id=1, lifetime=0x00000001,
620 type='PSA_KEY_TYPE_RAW_DATA', bits=8,
621 usage=usage, alg=alg, alg2=0,
622 material=b'K',
623 description='alg: ' + descr)
624 yield key1
625 key2 = StorageTestData(version=self.version,
626 id=1, lifetime=0x00000001,
627 type='PSA_KEY_TYPE_RAW_DATA', bits=8,
628 usage=usage, alg=0, alg2=alg,
629 material=b'L',
630 description='alg2: ' + descr)
631 yield key2
Gilles Peskined86bc522021-03-10 15:08:57 +0100632
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200633 def all_keys_for_algorithms(self) -> Iterator[StorageTestData]:
Gilles Peskined86bc522021-03-10 15:08:57 +0100634 """Generate test keys covering algorithm encodings."""
Gilles Peskineb93f8542021-04-19 13:50:25 +0200635 algorithms = sorted(self.constructors.algorithms)
gabor-mezei-arm5ea30372021-06-28 19:26:55 +0200636 for alg in self.constructors.generate_expressions(algorithms):
637 yield from self.keys_for_algorithm(alg)
Gilles Peskined86bc522021-03-10 15:08:57 +0100638
gabor-mezei-armea840de2021-06-29 15:42:57 +0200639 def generate_all_keys(self) -> Iterator[StorageTestData]:
gabor-mezei-arm8b0c91c2021-06-24 09:49:50 +0200640 """Generate all keys for the test cases."""
gabor-mezei-armea840de2021-06-29 15:42:57 +0200641 yield from self.all_keys_for_lifetimes()
642 yield from self.all_keys_for_usage_flags()
643 yield from self.all_keys_for_types()
644 yield from self.all_keys_for_algorithms()
gabor-mezei-arm8b0c91c2021-06-24 09:49:50 +0200645
gabor-mezei-arm5ea30372021-06-28 19:26:55 +0200646 def all_test_cases(self) -> Iterator[test_case.TestCase]:
Gilles Peskine897dff92021-03-10 15:03:44 +0100647 """Generate all storage format test cases."""
Gilles Peskineae9f14b2021-04-12 14:43:05 +0200648 # First build a list of all keys, then construct all the corresponding
649 # test cases. This allows all required information to be obtained in
650 # one go, which is a significant performance gain as the information
651 # includes numerical values obtained by compiling a C program.
Gilles Peskine3008c582021-07-06 21:05:52 +0200652 all_keys = list(self.generate_all_keys())
653 for key in all_keys:
gabor-mezei-arm5ea30372021-06-28 19:26:55 +0200654 if key.location_value() != 0:
655 # Skip keys with a non-default location, because they
656 # require a driver and we currently have no mechanism to
657 # determine whether a driver is available.
658 continue
659 yield self.make_test_case(key)
Gilles Peskine897dff92021-03-10 15:03:44 +0100660
gabor-mezei-arm4d9fb732021-06-24 09:53:26 +0200661class StorageFormatForward(StorageFormat):
662 """Storage format stability test cases for forward compatibility."""
663
Tomás González734d22c2023-10-30 15:15:45 +0000664 def __init__(self, info: psa_information.Information, version: int) -> None:
gabor-mezei-arm4d9fb732021-06-24 09:53:26 +0200665 super().__init__(info, version, True)
666
667class StorageFormatV0(StorageFormat):
668 """Storage format stability test cases for version 0 compatibility."""
669
Tomás González734d22c2023-10-30 15:15:45 +0000670 def __init__(self, info: psa_information.Information) -> None:
gabor-mezei-arm4d9fb732021-06-24 09:53:26 +0200671 super().__init__(info, 0, False)
Gilles Peskine897dff92021-03-10 15:03:44 +0100672
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200673 def all_keys_for_usage_flags(self) -> Iterator[StorageTestData]:
gabor-mezei-arm15c1f032021-06-24 10:04:38 +0200674 """Generate test keys covering usage flags."""
Gilles Peskinea296e482022-02-24 18:58:08 +0100675 yield from super().all_keys_for_usage_flags()
676 yield from self.generate_keys_for_usage_flags(test_implicit_usage=False)
gabor-mezei-arm15c1f032021-06-24 10:04:38 +0200677
gabor-mezei-armacfcc182021-06-28 17:40:32 +0200678 def keys_for_implicit_usage(
gabor-mezei-arm044fefc2021-06-24 10:16:44 +0200679 self,
gabor-mezei-arme84d3212021-06-28 16:54:11 +0200680 implyer_usage: str,
gabor-mezei-arm044fefc2021-06-24 10:16:44 +0200681 alg: str,
gabor-mezei-arm805c7352021-06-28 20:02:11 +0200682 key_type: crypto_knowledge.KeyType
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200683 ) -> StorageTestData:
gabor-mezei-armb92d61b2021-06-24 14:38:25 +0200684 # pylint: disable=too-many-locals
gabor-mezei-arm927742e2021-06-28 16:27:29 +0200685 """Generate test keys for the specified implicit usage flag,
gabor-mezei-arm044fefc2021-06-24 10:16:44 +0200686 algorithm and key type combination.
687 """
gabor-mezei-arm805c7352021-06-28 20:02:11 +0200688 bits = key_type.sizes_to_test()[0]
gabor-mezei-arme84d3212021-06-28 16:54:11 +0200689 implicit_usage = StorageKey.IMPLICIT_USAGE_FLAGS[implyer_usage]
Gilles Peskined9af9782022-03-17 22:32:59 +0100690 usage_flags = ['PSA_KEY_USAGE_EXPORT']
691 material_usage_flags = usage_flags + [implyer_usage]
692 expected_usage_flags = material_usage_flags + [implicit_usage]
gabor-mezei-arm47812632021-06-28 16:35:48 +0200693 alg2 = 0
gabor-mezei-arm805c7352021-06-28 20:02:11 +0200694 key_material = key_type.key_material(bits)
Gilles Peskine930ccef2022-03-18 00:02:15 +0100695 usage_expression = crypto_knowledge.short_expression(implyer_usage, 1)
696 alg_expression = crypto_knowledge.short_expression(alg, 1)
697 key_type_expression = key_type.short_expression(1)
gabor-mezei-armacfcc182021-06-28 17:40:32 +0200698 description = 'implied by {}: {} {} {}-bit'.format(
gabor-mezei-arm47812632021-06-28 16:35:48 +0200699 usage_expression, alg_expression, key_type_expression, bits)
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200700 key = StorageTestData(version=self.version,
701 id=1, lifetime=0x00000001,
702 type=key_type.expression, bits=bits,
703 usage=material_usage_flags,
704 expected_usage=expected_usage_flags,
705 without_implicit_usage=True,
706 alg=alg, alg2=alg2,
707 material=key_material,
708 description=description)
709 return key
gabor-mezei-arm044fefc2021-06-24 10:16:44 +0200710
711 def gather_key_types_for_sign_alg(self) -> Dict[str, List[str]]:
gabor-mezei-armb92d61b2021-06-24 14:38:25 +0200712 # pylint: disable=too-many-locals
gabor-mezei-arm044fefc2021-06-24 10:16:44 +0200713 """Match possible key types for sign algorithms."""
Shaun Case0e7791f2021-12-20 21:14:10 -0800714 # To create a valid combination both the algorithms and key types
gabor-mezei-arm044fefc2021-06-24 10:16:44 +0200715 # must be filtered. Pair them with keywords created from its names.
716 incompatible_alg_keyword = frozenset(['RAW', 'ANY', 'PURE'])
717 incompatible_key_type_keywords = frozenset(['MONTGOMERY'])
718 keyword_translation = {
719 'ECDSA': 'ECC',
720 'ED[0-9]*.*' : 'EDWARDS'
721 }
722 exclusive_keywords = {
723 'EDWARDS': 'ECC'
724 }
gabor-mezei-armb92d61b2021-06-24 14:38:25 +0200725 key_types = set(self.constructors.generate_expressions(self.constructors.key_types))
726 algorithms = set(self.constructors.generate_expressions(self.constructors.sign_algorithms))
gabor-mezei-arm044fefc2021-06-24 10:16:44 +0200727 alg_with_keys = {} #type: Dict[str, List[str]]
728 translation_table = str.maketrans('(', '_', ')')
729 for alg in algorithms:
730 # Generate keywords from the name of the algorithm
731 alg_keywords = set(alg.partition('(')[0].split(sep='_')[2:])
732 # Translate keywords for better matching with the key types
733 for keyword in alg_keywords.copy():
734 for pattern, replace in keyword_translation.items():
735 if re.match(pattern, keyword):
736 alg_keywords.remove(keyword)
737 alg_keywords.add(replace)
Shaun Case0e7791f2021-12-20 21:14:10 -0800738 # Filter out incompatible algorithms
gabor-mezei-arm044fefc2021-06-24 10:16:44 +0200739 if not alg_keywords.isdisjoint(incompatible_alg_keyword):
740 continue
741
742 for key_type in key_types:
743 # Generate keywords from the of the key type
744 key_type_keywords = set(key_type.translate(translation_table).split(sep='_')[3:])
745
Shaun Case0e7791f2021-12-20 21:14:10 -0800746 # Remove ambiguous keywords
gabor-mezei-arm044fefc2021-06-24 10:16:44 +0200747 for keyword1, keyword2 in exclusive_keywords.items():
748 if keyword1 in key_type_keywords:
749 key_type_keywords.remove(keyword2)
750
751 if key_type_keywords.isdisjoint(incompatible_key_type_keywords) and\
752 not key_type_keywords.isdisjoint(alg_keywords):
753 if alg in alg_with_keys:
754 alg_with_keys[alg].append(key_type)
755 else:
756 alg_with_keys[alg] = [key_type]
757 return alg_with_keys
758
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200759 def all_keys_for_implicit_usage(self) -> Iterator[StorageTestData]:
gabor-mezei-arm044fefc2021-06-24 10:16:44 +0200760 """Generate test keys for usage flag extensions."""
761 # Generate a key type and algorithm pair for each extendable usage
762 # flag to generate a valid key for exercising. The key is generated
Shaun Case0e7791f2021-12-20 21:14:10 -0800763 # without usage extension to check the extension compatibility.
gabor-mezei-arm044fefc2021-06-24 10:16:44 +0200764 alg_with_keys = self.gather_key_types_for_sign_alg()
gabor-mezei-arm7d2ec9a2021-06-24 16:35:01 +0200765
gabor-mezei-arm5ea30372021-06-28 19:26:55 +0200766 for usage in sorted(StorageKey.IMPLICIT_USAGE_FLAGS, key=str):
767 for alg in sorted(alg_with_keys):
768 for key_type in sorted(alg_with_keys[alg]):
769 # The key types must be filtered to fit the specific usage flag.
gabor-mezei-arm805c7352021-06-28 20:02:11 +0200770 kt = crypto_knowledge.KeyType(key_type)
Gilles Peskine989c13d2022-03-17 12:52:24 +0100771 if kt.is_public() and '_SIGN_' in usage:
772 # Can't sign with a public key
773 continue
774 yield self.keys_for_implicit_usage(usage, alg, kt)
gabor-mezei-arm044fefc2021-06-24 10:16:44 +0200775
gabor-mezei-armea840de2021-06-29 15:42:57 +0200776 def generate_all_keys(self) -> Iterator[StorageTestData]:
777 yield from super().generate_all_keys()
778 yield from self.all_keys_for_implicit_usage()
gabor-mezei-arm15c1f032021-06-24 10:04:38 +0200779
Tomás González734d22c2023-10-30 15:15:45 +0000780
Gilles Peskine69feebd2022-09-16 21:41:47 +0200781class PSATestGenerator(test_data_generation.TestGenerator):
Werner Lewisdcad1e92022-08-24 11:30:03 +0100782 """Test generator subclass including PSA targets and info."""
Dave Rodgmanbeb5ad72022-04-22 14:52:41 +0100783 # Note that targets whose names contain 'test_format' have their content
Gilles Peskinecfd4fae2021-04-23 16:37:12 +0200784 # validated by `abi_check.py`.
Werner Lewis0d07e862022-09-02 11:56:34 +0100785 targets = {
Przemyslaw Stekiel997caf82021-10-15 15:21:51 +0200786 'test_suite_psa_crypto_generate_key.generated':
787 lambda info: KeyGenerate(info).test_cases_for_key_generation(),
Gilles Peskine0298bda2021-03-10 02:34:37 +0100788 'test_suite_psa_crypto_not_supported.generated':
Gilles Peskine4fa76bd2022-12-15 22:14:28 +0100789 lambda info: KeyTypeNotSupported(info).test_cases_for_not_supported(),
Tomás González2bff1bf2023-10-30 15:29:23 +0000790 'test_suite_psa_crypto_low_hash.generated':
791 lambda info: crypto_data_tests.HashPSALowLevel(info).all_test_cases(),
Gilles Peskinec05158b2021-04-27 20:40:10 +0200792 'test_suite_psa_crypto_op_fail.generated':
793 lambda info: OpFail(info).all_test_cases(),
Gilles Peskine897dff92021-03-10 15:03:44 +0100794 'test_suite_psa_crypto_storage_format.current':
gabor-mezei-arm4d9fb732021-06-24 09:53:26 +0200795 lambda info: StorageFormatForward(info, 0).all_test_cases(),
Gilles Peskine897dff92021-03-10 15:03:44 +0100796 'test_suite_psa_crypto_storage_format.v0':
gabor-mezei-arm4d9fb732021-06-24 09:53:26 +0200797 lambda info: StorageFormatV0(info).all_test_cases(),
Tomás González734d22c2023-10-30 15:15:45 +0000798 } #type: Dict[str, Callable[[psa_information.Information], Iterable[test_case.TestCase]]]
Gilles Peskine0298bda2021-03-10 02:34:37 +0100799
Werner Lewisdcad1e92022-08-24 11:30:03 +0100800 def __init__(self, options):
801 super().__init__(options)
Tomás González734d22c2023-10-30 15:15:45 +0000802 self.info = psa_information.Information()
Gilles Peskine14e428f2021-01-26 22:19:21 +0100803
Werner Lewisdcad1e92022-08-24 11:30:03 +0100804 def generate_target(self, name: str, *target_args) -> None:
805 super().generate_target(name, self.info)
Gilles Peskine09940492021-01-26 22:16:30 +0100806
Tomás González734d22c2023-10-30 15:15:45 +0000807
Gilles Peskine09940492021-01-26 22:16:30 +0100808if __name__ == '__main__':
Gilles Peskine69feebd2022-09-16 21:41:47 +0200809 test_data_generation.main(sys.argv[1:], __doc__, PSATestGenerator)