blob: b4a13bac3d3387d75d3be437a0591517ed471706 [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
9# SPDX-License-Identifier: Apache-2.0
10#
11# Licensed under the Apache License, Version 2.0 (the "License"); you may
12# not use this file except in compliance with the License.
13# You may obtain a copy of the License at
14#
15# http://www.apache.org/licenses/LICENSE-2.0
16#
17# Unless required by applicable law or agreed to in writing, software
18# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
19# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20# See the License for the specific language governing permissions and
21# limitations under the License.
22
23import argparse
Gilles Peskine14e428f2021-01-26 22:19:21 +010024import os
Bence Szépkúti9e84ec72021-05-07 11:49:17 +020025import posixpath
Gilles Peskine14e428f2021-01-26 22:19:21 +010026import re
Gilles Peskine09940492021-01-26 22:16:30 +010027import sys
Gilles Peskine3d778392021-02-17 15:11:05 +010028from typing import Callable, Dict, FrozenSet, Iterable, Iterator, List, Optional, TypeVar
Gilles Peskine09940492021-01-26 22:16:30 +010029
30import scripts_path # pylint: disable=unused-import
Gilles Peskinec86f20a2021-04-22 00:20:47 +020031from mbedtls_dev import build_tree
Gilles Peskine14e428f2021-01-26 22:19:21 +010032from mbedtls_dev import crypto_knowledge
Gilles Peskine09940492021-01-26 22:16:30 +010033from mbedtls_dev import macro_collector
Gilles Peskine897dff92021-03-10 15:03:44 +010034from mbedtls_dev import psa_storage
Gilles Peskine14e428f2021-01-26 22:19:21 +010035from mbedtls_dev import test_case
Gilles Peskine09940492021-01-26 22:16:30 +010036
37T = TypeVar('T') #pylint: disable=invalid-name
38
Gilles Peskine14e428f2021-01-26 22:19:21 +010039
Gilles Peskine7f756872021-02-16 12:13:12 +010040def psa_want_symbol(name: str) -> str:
Gilles Peskineaf172842021-01-27 18:24:48 +010041 """Return the PSA_WANT_xxx symbol associated with a PSA crypto feature."""
42 if name.startswith('PSA_'):
43 return name[:4] + 'WANT_' + name[4:]
44 else:
45 raise ValueError('Unable to determine the PSA_WANT_ symbol for ' + name)
46
Gilles Peskine7f756872021-02-16 12:13:12 +010047def finish_family_dependency(dep: str, bits: int) -> str:
48 """Finish dep if it's a family dependency symbol prefix.
49
50 A family dependency symbol prefix is a PSA_WANT_ symbol that needs to be
51 qualified by the key size. If dep is such a symbol, finish it by adjusting
52 the prefix and appending the key size. Other symbols are left unchanged.
53 """
54 return re.sub(r'_FAMILY_(.*)', r'_\1_' + str(bits), dep)
55
56def finish_family_dependencies(dependencies: List[str], bits: int) -> List[str]:
57 """Finish any family dependency symbol prefixes.
58
59 Apply `finish_family_dependency` to each element of `dependencies`.
60 """
61 return [finish_family_dependency(dep, bits) for dep in dependencies]
Gilles Peskineaf172842021-01-27 18:24:48 +010062
Gilles Peskinec5d086f2021-04-20 23:23:45 +020063SYMBOLS_WITHOUT_DEPENDENCY = frozenset([
64 'PSA_ALG_AEAD_WITH_AT_LEAST_THIS_LENGTH_TAG', # modifier, only in policies
65 'PSA_ALG_AEAD_WITH_SHORTENED_TAG', # modifier
66 'PSA_ALG_ANY_HASH', # only in policies
67 'PSA_ALG_AT_LEAST_THIS_LENGTH_MAC', # modifier, only in policies
68 'PSA_ALG_KEY_AGREEMENT', # chaining
69 'PSA_ALG_TRUNCATED_MAC', # modifier
70])
Gilles Peskinef8223ab2021-03-10 15:07:16 +010071def automatic_dependencies(*expressions: str) -> List[str]:
72 """Infer dependencies of a test case by looking for PSA_xxx symbols.
73
74 The arguments are strings which should be C expressions. Do not use
75 string literals or comments as this function is not smart enough to
76 skip them.
77 """
78 used = set()
79 for expr in expressions:
80 used.update(re.findall(r'PSA_(?:ALG|ECC_FAMILY|KEY_TYPE)_\w+', expr))
Gilles Peskinec5d086f2021-04-20 23:23:45 +020081 used.difference_update(SYMBOLS_WITHOUT_DEPENDENCY)
Gilles Peskinef8223ab2021-03-10 15:07:16 +010082 return sorted(psa_want_symbol(name) for name in used)
83
Gilles Peskined169d602021-02-16 14:16:25 +010084# A temporary hack: at the time of writing, not all dependency symbols
85# are implemented yet. Skip test cases for which the dependency symbols are
86# not available. Once all dependency symbols are available, this hack must
87# be removed so that a bug in the dependency symbols proprely leads to a test
88# failure.
89def read_implemented_dependencies(filename: str) -> FrozenSet[str]:
90 return frozenset(symbol
91 for line in open(filename)
92 for symbol in re.findall(r'\bPSA_WANT_\w+\b', line))
Gilles Peskinec86f20a2021-04-22 00:20:47 +020093_implemented_dependencies = None #type: Optional[FrozenSet[str]] #pylint: disable=invalid-name
Gilles Peskined169d602021-02-16 14:16:25 +010094def hack_dependencies_not_implemented(dependencies: List[str]) -> None:
Gilles Peskinec86f20a2021-04-22 00:20:47 +020095 global _implemented_dependencies #pylint: disable=global-statement,invalid-name
96 if _implemented_dependencies is None:
97 _implemented_dependencies = \
98 read_implemented_dependencies('include/psa/crypto_config.h')
99 if not all(dep.lstrip('!') in _implemented_dependencies
Gilles Peskined169d602021-02-16 14:16:25 +0100100 for dep in dependencies):
101 dependencies.append('DEPENDENCY_NOT_IMPLEMENTED_YET')
102
Gilles Peskine14e428f2021-01-26 22:19:21 +0100103
Gilles Peskineb94ea512021-03-10 02:12:08 +0100104class Information:
105 """Gather information about PSA constructors."""
Gilles Peskine09940492021-01-26 22:16:30 +0100106
Gilles Peskineb94ea512021-03-10 02:12:08 +0100107 def __init__(self) -> None:
Gilles Peskine09940492021-01-26 22:16:30 +0100108 self.constructors = self.read_psa_interface()
109
110 @staticmethod
Gilles Peskine09940492021-01-26 22:16:30 +0100111 def remove_unwanted_macros(
Gilles Peskine537d5fa2021-04-19 13:50:25 +0200112 constructors: macro_collector.PSAMacroEnumerator
Gilles Peskine09940492021-01-26 22:16:30 +0100113 ) -> None:
Gilles Peskine537d5fa2021-04-19 13:50:25 +0200114 # Mbed TLS doesn't support finite-field DH yet and will not support
115 # finite-field DSA. Don't attempt to generate any related test case.
116 constructors.key_types.discard('PSA_KEY_TYPE_DH_KEY_PAIR')
117 constructors.key_types.discard('PSA_KEY_TYPE_DH_PUBLIC_KEY')
Gilles Peskine09940492021-01-26 22:16:30 +0100118 constructors.key_types.discard('PSA_KEY_TYPE_DSA_KEY_PAIR')
119 constructors.key_types.discard('PSA_KEY_TYPE_DSA_PUBLIC_KEY')
Gilles Peskine09940492021-01-26 22:16:30 +0100120
Gilles Peskine537d5fa2021-04-19 13:50:25 +0200121 def read_psa_interface(self) -> macro_collector.PSAMacroEnumerator:
Gilles Peskine09940492021-01-26 22:16:30 +0100122 """Return the list of known key types, algorithms, etc."""
Gilles Peskine3d404b82021-03-30 21:46:35 +0200123 constructors = macro_collector.InputsForTest()
Gilles Peskine09940492021-01-26 22:16:30 +0100124 header_file_names = ['include/psa/crypto_values.h',
125 'include/psa/crypto_extra.h']
Gilles Peskine537d5fa2021-04-19 13:50:25 +0200126 test_suites = ['tests/suites/test_suite_psa_crypto_metadata.data']
Gilles Peskine09940492021-01-26 22:16:30 +0100127 for header_file_name in header_file_names:
Gilles Peskine537d5fa2021-04-19 13:50:25 +0200128 constructors.parse_header(header_file_name)
129 for test_cases in test_suites:
130 constructors.parse_test_cases(test_cases)
Gilles Peskine09940492021-01-26 22:16:30 +0100131 self.remove_unwanted_macros(constructors)
Gilles Peskine3d404b82021-03-30 21:46:35 +0200132 constructors.gather_arguments()
Gilles Peskine09940492021-01-26 22:16:30 +0100133 return constructors
134
Gilles Peskine14e428f2021-01-26 22:19:21 +0100135
Przemyslaw Stekielb576c7b2021-10-11 10:15:25 +0200136def test_case_for_key_type_not_supported(
Gilles Peskineb94ea512021-03-10 02:12:08 +0100137 verb: str, key_type: str, bits: int,
138 dependencies: List[str],
139 *args: str,
140 param_descr: str = ''
141) -> test_case.TestCase:
142 """Return one test case exercising a key creation method
143 for an unsupported key type or size.
144 """
145 hack_dependencies_not_implemented(dependencies)
146 tc = test_case.TestCase()
147 short_key_type = re.sub(r'PSA_(KEY_TYPE|ECC_FAMILY)_', r'', key_type)
148 adverb = 'not' if dependencies else 'never'
149 if param_descr:
150 adverb = param_descr + ' ' + adverb
Przemyslaw Stekielb576c7b2021-10-11 10:15:25 +0200151 tc.set_description('PSA {} {} {}-bit {} supported'
152 .format(verb, short_key_type, bits, adverb))
153 tc.set_dependencies(dependencies)
154 tc.set_function(verb + '_not_supported')
155 tc.set_arguments([key_type] + list(args))
156 return tc
157
158def test_case_for_key_type_invalid_argument(
159 verb: str, key_type: str, bits: int,
160 dependencies: List[str],
161 *args: str,
162 param_descr: str = ''
163) -> test_case.TestCase:
164 """Return one test case exercising a key creation method
165 for an invalid argument when key is public.
166 """
167 hack_dependencies_not_implemented(dependencies)
168 tc = test_case.TestCase()
169 short_key_type = re.sub(r'PSA_(KEY_TYPE|ECC_FAMILY)_', r'', key_type)
170 adverb = 'not' if dependencies else 'never'
171 if param_descr:
172 adverb = param_descr + ' ' + adverb
173 tc.set_description('PSA {} {} {}-bit invalid argument'
174 .format(verb, short_key_type, bits))
175 tc.set_function(verb + '_invalid_argument')
Gilles Peskineb94ea512021-03-10 02:12:08 +0100176 tc.set_dependencies(dependencies)
Gilles Peskineb94ea512021-03-10 02:12:08 +0100177 tc.set_arguments([key_type] + list(args))
178 return tc
179
180class NotSupported:
Przemyslaw Stekielb576c7b2021-10-11 10:15:25 +0200181 """Generate test cases for when something is not supported or argument is inavlid."""
Gilles Peskineb94ea512021-03-10 02:12:08 +0100182
183 def __init__(self, info: Information) -> None:
184 self.constructors = info.constructors
Gilles Peskine14e428f2021-01-26 22:19:21 +0100185
Gilles Peskine60b29fe2021-02-16 14:06:50 +0100186 ALWAYS_SUPPORTED = frozenset([
187 'PSA_KEY_TYPE_DERIVE',
188 'PSA_KEY_TYPE_RAW_DATA',
189 ])
Gilles Peskine14e428f2021-01-26 22:19:21 +0100190 def test_cases_for_key_type_not_supported(
Gilles Peskine60b29fe2021-02-16 14:06:50 +0100191 self,
Gilles Peskineaf172842021-01-27 18:24:48 +0100192 kt: crypto_knowledge.KeyType,
193 param: Optional[int] = None,
194 param_descr: str = '',
Gilles Peskine3d778392021-02-17 15:11:05 +0100195 ) -> Iterator[test_case.TestCase]:
Przemyslaw Stekielb576c7b2021-10-11 10:15:25 +0200196 """Return test cases exercising key creation when the given type is unsupported
197 or argument is invalid.
Gilles Peskineaf172842021-01-27 18:24:48 +0100198
199 If param is present and not None, emit test cases conditioned on this
200 parameter not being supported. If it is absent or None, emit test cases
Przemyslaw Stekielb576c7b2021-10-11 10:15:25 +0200201 conditioned on the base type not being supported. If key is public emit test
202 case for invalid argument.
Gilles Peskineaf172842021-01-27 18:24:48 +0100203 """
Gilles Peskine60b29fe2021-02-16 14:06:50 +0100204 if kt.name in self.ALWAYS_SUPPORTED:
205 # Don't generate test cases for key types that are always supported.
206 # They would be skipped in all configurations, which is noise.
Gilles Peskine3d778392021-02-17 15:11:05 +0100207 return
Gilles Peskineaf172842021-01-27 18:24:48 +0100208 import_dependencies = [('!' if param is None else '') +
209 psa_want_symbol(kt.name)]
210 if kt.params is not None:
211 import_dependencies += [('!' if param == i else '') +
212 psa_want_symbol(sym)
213 for i, sym in enumerate(kt.params)]
Gilles Peskine14e428f2021-01-26 22:19:21 +0100214 if kt.name.endswith('_PUBLIC_KEY'):
215 generate_dependencies = []
216 else:
217 generate_dependencies = import_dependencies
Gilles Peskine14e428f2021-01-26 22:19:21 +0100218 for bits in kt.sizes_to_test():
Przemyslaw Stekielb576c7b2021-10-11 10:15:25 +0200219 yield test_case_for_key_type_not_supported(
Gilles Peskine7f756872021-02-16 12:13:12 +0100220 'import', kt.expression, bits,
221 finish_family_dependencies(import_dependencies, bits),
Gilles Peskineaf172842021-01-27 18:24:48 +0100222 test_case.hex_string(kt.key_material(bits)),
223 param_descr=param_descr,
Gilles Peskine3d778392021-02-17 15:11:05 +0100224 )
Gilles Peskineaf172842021-01-27 18:24:48 +0100225 if not generate_dependencies and param is not None:
226 # If generation is impossible for this key type, rather than
227 # supported or not depending on implementation capabilities,
228 # only generate the test case once.
229 continue
Przemyslaw Stekielb576c7b2021-10-11 10:15:25 +0200230 if kt.name.endswith('_PUBLIC_KEY'):
231 yield test_case_for_key_type_invalid_argument(
232 'generate', kt.expression, bits,
233 finish_family_dependencies(generate_dependencies, bits),
234 str(bits),
235 param_descr=param_descr,
236 )
237 else:
238 yield test_case_for_key_type_not_supported(
239 'generate', kt.expression, bits,
240 finish_family_dependencies(generate_dependencies, bits),
241 str(bits),
242 param_descr=param_descr,
243 )
Gilles Peskine14e428f2021-01-26 22:19:21 +0100244 # To be added: derive
Gilles Peskine14e428f2021-01-26 22:19:21 +0100245
Gilles Peskine537d5fa2021-04-19 13:50:25 +0200246 ECC_KEY_TYPES = ('PSA_KEY_TYPE_ECC_KEY_PAIR',
247 'PSA_KEY_TYPE_ECC_PUBLIC_KEY')
248
Gilles Peskine3d778392021-02-17 15:11:05 +0100249 def test_cases_for_not_supported(self) -> Iterator[test_case.TestCase]:
Gilles Peskine14e428f2021-01-26 22:19:21 +0100250 """Generate test cases that exercise the creation of keys of unsupported types."""
Gilles Peskine14e428f2021-01-26 22:19:21 +0100251 for key_type in sorted(self.constructors.key_types):
Gilles Peskine537d5fa2021-04-19 13:50:25 +0200252 if key_type in self.ECC_KEY_TYPES:
253 continue
Gilles Peskine14e428f2021-01-26 22:19:21 +0100254 kt = crypto_knowledge.KeyType(key_type)
Gilles Peskine3d778392021-02-17 15:11:05 +0100255 yield from self.test_cases_for_key_type_not_supported(kt)
Gilles Peskineaf172842021-01-27 18:24:48 +0100256 for curve_family in sorted(self.constructors.ecc_curves):
Gilles Peskine537d5fa2021-04-19 13:50:25 +0200257 for constr in self.ECC_KEY_TYPES:
Gilles Peskineaf172842021-01-27 18:24:48 +0100258 kt = crypto_knowledge.KeyType(constr, [curve_family])
Gilles Peskine3d778392021-02-17 15:11:05 +0100259 yield from self.test_cases_for_key_type_not_supported(
Gilles Peskineaf172842021-01-27 18:24:48 +0100260 kt, param_descr='type')
Gilles Peskine3d778392021-02-17 15:11:05 +0100261 yield from self.test_cases_for_key_type_not_supported(
Gilles Peskineaf172842021-01-27 18:24:48 +0100262 kt, 0, param_descr='curve')
Gilles Peskineb94ea512021-03-10 02:12:08 +0100263
Przemyslaw Stekiel1b0978b2021-10-15 15:21:51 +0200264def test_case_for_key_generation(
265 key_type: str, bits: int,
266 dependencies: List[str],
267 *args: str,
268 result: str = '',
269 param_descr: str = '',
270) -> test_case.TestCase:
271 """Return one test case exercising a key generation.
272 """
273 hack_dependencies_not_implemented(dependencies)
274 tc = test_case.TestCase()
275 short_key_type = re.sub(r'PSA_(KEY_TYPE|ECC_FAMILY)_', r'', key_type)
276 tc.set_description('PSA {} {}-bit'
277 .format( short_key_type, bits))
278 tc.set_dependencies(dependencies)
279 tc.set_function('generate_key')
280 tc.set_arguments([key_type] + list(args))
281 tc.set_result(result)
282
283 return tc
284
285class KeyGenerate:
286 """Generate positive and negative (invalid argument) test cases for key generation."""
287
288 def __init__(self, info: Information) -> None:
289 self.constructors = info.constructors
290
291 def test_cases_for_key_type_key_generation(
292 self,
293 kt: crypto_knowledge.KeyType,
294 param: Optional[int] = None,
295 param_descr: str = '',
296 ) -> Iterator[test_case.TestCase]:
297 """Return test cases exercising key generation.
298
299 All key types can be generated except for public keys. For public key
300 PSA_ERROR_INVALID_ARGUMENT status is expected.
301 """
302 result = 'PSA_SUCCESS'
303
304 import_dependencies = [psa_want_symbol(kt.name)]
305 if kt.params is not None:
306 import_dependencies += [psa_want_symbol(sym)
307 for i, sym in enumerate(kt.params)]
308 if kt.name.endswith('_PUBLIC_KEY'):
309 generate_dependencies = []
310 result = 'PSA_ERROR_INVALID_ARGUMENT'
311 else:
312 generate_dependencies = import_dependencies
313 for bits in kt.sizes_to_test():
314 yield test_case_for_key_generation(
315 kt.expression, bits,
316 finish_family_dependencies(generate_dependencies, bits),
317 str(bits),
318 result,
319 param_descr=param_descr
320 )
321
322 ECC_KEY_TYPES = ('PSA_KEY_TYPE_ECC_KEY_PAIR',
323 'PSA_KEY_TYPE_ECC_PUBLIC_KEY')
324
325 def test_cases_for_key_generation(self) -> Iterator[test_case.TestCase]:
326 """Generate test cases that exercise the generation of keys."""
327 for key_type in sorted(self.constructors.key_types):
328 if key_type in self.ECC_KEY_TYPES:
329 continue
330 kt = crypto_knowledge.KeyType(key_type)
331 yield from self.test_cases_for_key_type_key_generation(kt)
332 for curve_family in sorted(self.constructors.ecc_curves):
333 for constr in self.ECC_KEY_TYPES:
334 kt = crypto_knowledge.KeyType(constr, [curve_family])
335 yield from self.test_cases_for_key_type_key_generation(
336 kt, param_descr='type')
337 yield from self.test_cases_for_key_type_key_generation(
338 kt, 0, param_descr='curve')
339
340
Gilles Peskine897dff92021-03-10 15:03:44 +0100341class StorageKey(psa_storage.Key):
342 """Representation of a key for storage format testing."""
343
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200344 IMPLICIT_USAGE_FLAGS = {
345 'PSA_KEY_USAGE_SIGN_HASH': 'PSA_KEY_USAGE_SIGN_MESSAGE',
346 'PSA_KEY_USAGE_VERIFY_HASH': 'PSA_KEY_USAGE_VERIFY_MESSAGE'
347 } #type: Dict[str, str]
348 """Mapping of usage flags to the flags that they imply."""
349
350 def __init__(
351 self,
352 usage: str,
353 without_implicit_usage: Optional[bool] = False,
354 **kwargs
355 ) -> None:
356 """Prepare to generate a key.
357
358 * `usage` : The usage flags used for the key.
359 * `without_implicit_usage`: Flag to defide to apply the usage extension
360 """
gabor-mezei-arm2c9e54a2021-06-29 17:21:21 +0200361 super().__init__(usage=usage, **kwargs)
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200362
363 if not without_implicit_usage:
364 for flag, implicit in self.IMPLICIT_USAGE_FLAGS.items():
365 if self.usage.value() & psa_storage.Expr(flag).value() and \
366 self.usage.value() & psa_storage.Expr(implicit).value() == 0:
367 self.usage = psa_storage.Expr(self.usage.string + ' | ' + implicit)
368
369class StorageTestData(StorageKey):
370 """Representation of test case data for storage format testing."""
371
gabor-mezei-arm672e3762021-06-24 10:16:44 +0200372 def __init__(
373 self,
374 description: str,
375 expected_usage: Optional[str] = None,
376 **kwargs
377 ) -> None:
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200378 """Prepare to generate test data
gabor-mezei-arm672e3762021-06-24 10:16:44 +0200379
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200380 * `description` : used for the the test case names
381 * `expected_usage`: the usage flags generated as the expected usage flags
382 in the test cases. CAn differ from the usage flags
383 stored in the keys because of the usage flags extension.
gabor-mezei-arm672e3762021-06-24 10:16:44 +0200384 """
Gilles Peskine897dff92021-03-10 15:03:44 +0100385 super().__init__(**kwargs)
386 self.description = description #type: str
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200387 self.expected_usage = expected_usage if expected_usage else self.usage.string #type: str
gabor-mezei-arm7748b6f2021-06-24 10:04:38 +0200388
Gilles Peskine897dff92021-03-10 15:03:44 +0100389class StorageFormat:
390 """Storage format stability test cases."""
391
392 def __init__(self, info: Information, version: int, forward: bool) -> None:
393 """Prepare to generate test cases for storage format stability.
394
395 * `info`: information about the API. See the `Information` class.
396 * `version`: the storage format version to generate test cases for.
397 * `forward`: if true, generate forward compatibility test cases which
398 save a key and check that its representation is as intended. Otherwise
399 generate backward compatibility test cases which inject a key
400 representation and check that it can be read and used.
401 """
gabor-mezei-arm7b5c4e22021-06-23 17:01:44 +0200402 self.constructors = info.constructors #type: macro_collector.PSAMacroEnumerator
403 self.version = version #type: int
404 self.forward = forward #type: bool
Gilles Peskine897dff92021-03-10 15:03:44 +0100405
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200406 def make_test_case(self, key: StorageTestData) -> test_case.TestCase:
Gilles Peskine897dff92021-03-10 15:03:44 +0100407 """Construct a storage format test case for the given key.
408
409 If ``forward`` is true, generate a forward compatibility test case:
410 create a key and validate that it has the expected representation.
411 Otherwise generate a backward compatibility test case: inject the
412 key representation into storage and validate that it can be read
413 correctly.
414 """
415 verb = 'save' if self.forward else 'read'
416 tc = test_case.TestCase()
417 tc.set_description('PSA storage {}: {}'.format(verb, key.description))
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100418 dependencies = automatic_dependencies(
419 key.lifetime.string, key.type.string,
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200420 key.expected_usage, key.alg.string, key.alg2.string,
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100421 )
422 dependencies = finish_family_dependencies(dependencies, key.bits)
423 tc.set_dependencies(dependencies)
Gilles Peskine897dff92021-03-10 15:03:44 +0100424 tc.set_function('key_storage_' + verb)
425 if self.forward:
426 extra_arguments = []
427 else:
Gilles Peskine45f1cd72021-04-21 20:11:33 +0200428 flags = []
Gilles Peskine897dff92021-03-10 15:03:44 +0100429 # Some test keys have the RAW_DATA type and attributes that don't
430 # necessarily make sense. We do this to validate numerical
431 # encodings of the attributes.
432 # Raw data keys have no useful exercise anyway so there is no
433 # loss of test coverage.
Gilles Peskine45f1cd72021-04-21 20:11:33 +0200434 if key.type.string != 'PSA_KEY_TYPE_RAW_DATA':
435 flags.append('TEST_FLAG_EXERCISE')
436 if 'READ_ONLY' in key.lifetime.string:
437 flags.append('TEST_FLAG_READ_ONLY')
438 extra_arguments = [' | '.join(flags) if flags else '0']
Gilles Peskine897dff92021-03-10 15:03:44 +0100439 tc.set_arguments([key.lifetime.string,
440 key.type.string, str(key.bits),
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200441 key.expected_usage, key.alg.string, key.alg2.string,
Gilles Peskine897dff92021-03-10 15:03:44 +0100442 '"' + key.material.hex() + '"',
443 '"' + key.hex() + '"',
444 *extra_arguments])
445 return tc
446
Gilles Peskineeb7bdaa2021-04-21 22:05:34 +0200447 def key_for_lifetime(
448 self,
449 lifetime: str,
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200450 ) -> StorageTestData:
Gilles Peskineeb7bdaa2021-04-21 22:05:34 +0200451 """Construct a test key for the given lifetime."""
452 short = lifetime
453 short = re.sub(r'PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION',
454 r'', short)
455 short = re.sub(r'PSA_KEY_[A-Z]+_', r'', short)
456 description = 'lifetime: ' + short
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200457 key = StorageTestData(version=self.version,
458 id=1, lifetime=lifetime,
459 type='PSA_KEY_TYPE_RAW_DATA', bits=8,
460 usage='PSA_KEY_USAGE_EXPORT', alg=0, alg2=0,
461 material=b'L',
462 description=description)
463 return key
Gilles Peskineeb7bdaa2021-04-21 22:05:34 +0200464
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200465 def all_keys_for_lifetimes(self) -> Iterator[StorageTestData]:
Gilles Peskineeb7bdaa2021-04-21 22:05:34 +0200466 """Generate test keys covering lifetimes."""
467 lifetimes = sorted(self.constructors.lifetimes)
468 expressions = self.constructors.generate_expressions(lifetimes)
469 for lifetime in expressions:
470 # Don't attempt to create or load a volatile key in storage
471 if 'VOLATILE' in lifetime:
472 continue
473 # Don't attempt to create a read-only key in storage,
474 # but do attempt to load one.
475 if 'READ_ONLY' in lifetime and self.forward:
476 continue
gabor-mezei-arm340fbf32021-06-28 19:26:55 +0200477 yield self.key_for_lifetime(lifetime)
Gilles Peskineeb7bdaa2021-04-21 22:05:34 +0200478
gabor-mezei-arm63857802021-06-29 15:39:56 +0200479 def keys_for_usage_flags(
Gilles Peskine897dff92021-03-10 15:03:44 +0100480 self,
481 usage_flags: List[str],
gabor-mezei-arm6ee72532021-06-24 09:42:02 +0200482 short: Optional[str] = None,
gabor-mezei-arm340fbf32021-06-28 19:26:55 +0200483 test_implicit_usage: Optional[bool] = False
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200484 ) -> Iterator[StorageTestData]:
Gilles Peskine897dff92021-03-10 15:03:44 +0100485 """Construct a test key for the given key usage."""
486 usage = ' | '.join(usage_flags) if usage_flags else '0'
487 if short is None:
488 short = re.sub(r'\bPSA_KEY_USAGE_', r'', usage)
gabor-mezei-arm340fbf32021-06-28 19:26:55 +0200489 extra_desc = ' with implication' if test_implicit_usage else ''
gabor-mezei-arm6ee72532021-06-24 09:42:02 +0200490 description = 'usage' + extra_desc + ': ' + short
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200491 key1 = StorageTestData(version=self.version,
492 id=1, lifetime=0x00000001,
493 type='PSA_KEY_TYPE_RAW_DATA', bits=8,
494 expected_usage=usage,
495 usage=usage, alg=0, alg2=0,
496 material=b'K',
497 description=description)
498 yield key1
499
gabor-mezei-arm340fbf32021-06-28 19:26:55 +0200500 if test_implicit_usage:
501 description = 'usage without implication' + ': ' + short
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200502 key2 = StorageTestData(version=self.version,
503 id=1, lifetime=0x00000001,
504 type='PSA_KEY_TYPE_RAW_DATA', bits=8,
505 without_implicit_usage=True,
506 usage=usage, alg=0, alg2=0,
507 material=b'K',
508 description=description)
509 yield key2
Gilles Peskine897dff92021-03-10 15:03:44 +0100510
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200511 def generate_keys_for_usage_flags(self, **kwargs) -> Iterator[StorageTestData]:
Gilles Peskine897dff92021-03-10 15:03:44 +0100512 """Generate test keys covering usage flags."""
513 known_flags = sorted(self.constructors.key_usage_flags)
gabor-mezei-arm63857802021-06-29 15:39:56 +0200514 yield from self.keys_for_usage_flags(['0'], **kwargs)
gabor-mezei-arm340fbf32021-06-28 19:26:55 +0200515 for usage_flag in known_flags:
gabor-mezei-arm63857802021-06-29 15:39:56 +0200516 yield from self.keys_for_usage_flags([usage_flag], **kwargs)
gabor-mezei-arm340fbf32021-06-28 19:26:55 +0200517 for flag1, flag2 in zip(known_flags,
518 known_flags[1:] + [known_flags[0]]):
gabor-mezei-arm63857802021-06-29 15:39:56 +0200519 yield from self.keys_for_usage_flags([flag1, flag2], **kwargs)
gabor-mezei-arm49d6ea92021-06-24 14:38:51 +0200520
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200521 def generate_key_for_all_usage_flags(self) -> Iterator[StorageTestData]:
gabor-mezei-arm49d6ea92021-06-24 14:38:51 +0200522 known_flags = sorted(self.constructors.key_usage_flags)
gabor-mezei-arm63857802021-06-29 15:39:56 +0200523 yield from self.keys_for_usage_flags(known_flags, short='all known')
gabor-mezei-arm49d6ea92021-06-24 14:38:51 +0200524
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200525 def all_keys_for_usage_flags(self) -> Iterator[StorageTestData]:
gabor-mezei-arm340fbf32021-06-28 19:26:55 +0200526 yield from self.generate_keys_for_usage_flags()
527 yield from self.generate_key_for_all_usage_flags()
Gilles Peskine897dff92021-03-10 15:03:44 +0100528
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100529 def keys_for_type(
530 self,
531 key_type: str,
532 params: Optional[Iterable[str]] = None
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200533 ) -> Iterator[StorageTestData]:
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100534 """Generate test keys for the given key type.
535
536 For key types that depend on a parameter (e.g. elliptic curve family),
537 `param` is the parameter to pass to the constructor. Only a single
538 parameter is supported.
539 """
540 kt = crypto_knowledge.KeyType(key_type, params)
541 for bits in kt.sizes_to_test():
542 usage_flags = 'PSA_KEY_USAGE_EXPORT'
543 alg = 0
544 alg2 = 0
545 key_material = kt.key_material(bits)
546 short_expression = re.sub(r'\bPSA_(?:KEY_TYPE|ECC_FAMILY)_',
547 r'',
548 kt.expression)
549 description = 'type: {} {}-bit'.format(short_expression, bits)
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200550 key = StorageTestData(version=self.version,
551 id=1, lifetime=0x00000001,
552 type=kt.expression, bits=bits,
553 usage=usage_flags, alg=alg, alg2=alg2,
554 material=key_material,
555 description=description)
556 yield key
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100557
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200558 def all_keys_for_types(self) -> Iterator[StorageTestData]:
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100559 """Generate test keys covering key types and their representations."""
Gilles Peskine537d5fa2021-04-19 13:50:25 +0200560 key_types = sorted(self.constructors.key_types)
gabor-mezei-arm340fbf32021-06-28 19:26:55 +0200561 for key_type in self.constructors.generate_expressions(key_types):
562 yield from self.keys_for_type(key_type)
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100563
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200564 def keys_for_algorithm(self, alg: str) -> Iterator[StorageTestData]:
Gilles Peskined86bc522021-03-10 15:08:57 +0100565 """Generate test keys for the specified algorithm."""
566 # For now, we don't have information on the compatibility of key
567 # types and algorithms. So we just test the encoding of algorithms,
568 # and not that operations can be performed with them.
Gilles Peskine20f55f62021-04-21 10:18:19 +0200569 descr = re.sub(r'PSA_ALG_', r'', alg)
570 descr = re.sub(r',', r', ', re.sub(r' +', r'', descr))
Gilles Peskined86bc522021-03-10 15:08:57 +0100571 usage = 'PSA_KEY_USAGE_EXPORT'
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200572 key1 = StorageTestData(version=self.version,
573 id=1, lifetime=0x00000001,
574 type='PSA_KEY_TYPE_RAW_DATA', bits=8,
575 usage=usage, alg=alg, alg2=0,
576 material=b'K',
577 description='alg: ' + descr)
578 yield key1
579 key2 = StorageTestData(version=self.version,
580 id=1, lifetime=0x00000001,
581 type='PSA_KEY_TYPE_RAW_DATA', bits=8,
582 usage=usage, alg=0, alg2=alg,
583 material=b'L',
584 description='alg2: ' + descr)
585 yield key2
Gilles Peskined86bc522021-03-10 15:08:57 +0100586
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200587 def all_keys_for_algorithms(self) -> Iterator[StorageTestData]:
Gilles Peskined86bc522021-03-10 15:08:57 +0100588 """Generate test keys covering algorithm encodings."""
Gilles Peskine537d5fa2021-04-19 13:50:25 +0200589 algorithms = sorted(self.constructors.algorithms)
gabor-mezei-arm340fbf32021-06-28 19:26:55 +0200590 for alg in self.constructors.generate_expressions(algorithms):
591 yield from self.keys_for_algorithm(alg)
Gilles Peskined86bc522021-03-10 15:08:57 +0100592
gabor-mezei-arm0c24edd2021-06-29 15:42:57 +0200593 def generate_all_keys(self) -> Iterator[StorageTestData]:
gabor-mezei-arm780cf9d2021-06-24 09:49:50 +0200594 """Generate all keys for the test cases."""
gabor-mezei-arm0c24edd2021-06-29 15:42:57 +0200595 yield from self.all_keys_for_lifetimes()
596 yield from self.all_keys_for_usage_flags()
597 yield from self.all_keys_for_types()
598 yield from self.all_keys_for_algorithms()
gabor-mezei-arm780cf9d2021-06-24 09:49:50 +0200599
gabor-mezei-arm340fbf32021-06-28 19:26:55 +0200600 def all_test_cases(self) -> Iterator[test_case.TestCase]:
Gilles Peskine897dff92021-03-10 15:03:44 +0100601 """Generate all storage format test cases."""
Gilles Peskine3c9d4232021-04-12 14:43:05 +0200602 # First build a list of all keys, then construct all the corresponding
603 # test cases. This allows all required information to be obtained in
604 # one go, which is a significant performance gain as the information
605 # includes numerical values obtained by compiling a C program.
Gilles Peskine45f2a402021-07-06 21:05:52 +0200606 all_keys = list(self.generate_all_keys())
607 for key in all_keys:
gabor-mezei-arm340fbf32021-06-28 19:26:55 +0200608 if key.location_value() != 0:
609 # Skip keys with a non-default location, because they
610 # require a driver and we currently have no mechanism to
611 # determine whether a driver is available.
612 continue
613 yield self.make_test_case(key)
Gilles Peskine897dff92021-03-10 15:03:44 +0100614
gabor-mezei-arma4102cb2021-06-24 09:53:26 +0200615class StorageFormatForward(StorageFormat):
616 """Storage format stability test cases for forward compatibility."""
617
618 def __init__(self, info: Information, version: int) -> None:
619 super().__init__(info, version, True)
620
621class StorageFormatV0(StorageFormat):
622 """Storage format stability test cases for version 0 compatibility."""
623
624 def __init__(self, info: Information) -> None:
625 super().__init__(info, 0, False)
Gilles Peskine897dff92021-03-10 15:03:44 +0100626
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200627 def all_keys_for_usage_flags(self) -> Iterator[StorageTestData]:
gabor-mezei-arm7748b6f2021-06-24 10:04:38 +0200628 """Generate test keys covering usage flags."""
gabor-mezei-arm340fbf32021-06-28 19:26:55 +0200629 yield from self.generate_keys_for_usage_flags(test_implicit_usage=True)
630 yield from self.generate_key_for_all_usage_flags()
gabor-mezei-arm7748b6f2021-06-24 10:04:38 +0200631
gabor-mezei-arm5df1dee2021-06-28 17:40:32 +0200632 def keys_for_implicit_usage(
gabor-mezei-arm672e3762021-06-24 10:16:44 +0200633 self,
gabor-mezei-arm2710bb12021-06-28 16:54:11 +0200634 implyer_usage: str,
gabor-mezei-arm672e3762021-06-24 10:16:44 +0200635 alg: str,
gabor-mezei-arm2784bfe2021-06-28 20:02:11 +0200636 key_type: crypto_knowledge.KeyType
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200637 ) -> StorageTestData:
gabor-mezei-arm0f8136a2021-06-24 14:38:25 +0200638 # pylint: disable=too-many-locals
gabor-mezei-arm8f405102021-06-28 16:27:29 +0200639 """Generate test keys for the specified implicit usage flag,
gabor-mezei-arm672e3762021-06-24 10:16:44 +0200640 algorithm and key type combination.
641 """
gabor-mezei-arm2784bfe2021-06-28 20:02:11 +0200642 bits = key_type.sizes_to_test()[0]
gabor-mezei-arm2710bb12021-06-28 16:54:11 +0200643 implicit_usage = StorageKey.IMPLICIT_USAGE_FLAGS[implyer_usage]
gabor-mezei-armd9050a52021-06-28 16:35:48 +0200644 usage_flags = 'PSA_KEY_USAGE_EXPORT'
gabor-mezei-arm2710bb12021-06-28 16:54:11 +0200645 material_usage_flags = usage_flags + ' | ' + implyer_usage
646 expected_usage_flags = material_usage_flags + ' | ' + implicit_usage
gabor-mezei-armd9050a52021-06-28 16:35:48 +0200647 alg2 = 0
gabor-mezei-arm2784bfe2021-06-28 20:02:11 +0200648 key_material = key_type.key_material(bits)
gabor-mezei-arm2710bb12021-06-28 16:54:11 +0200649 usage_expression = re.sub(r'PSA_KEY_USAGE_', r'', implyer_usage)
gabor-mezei-armd9050a52021-06-28 16:35:48 +0200650 alg_expression = re.sub(r'PSA_ALG_', r'', alg)
651 alg_expression = re.sub(r',', r', ', re.sub(r' +', r'', alg_expression))
652 key_type_expression = re.sub(r'\bPSA_(?:KEY_TYPE|ECC_FAMILY)_',
653 r'',
gabor-mezei-arm2784bfe2021-06-28 20:02:11 +0200654 key_type.expression)
gabor-mezei-arm5df1dee2021-06-28 17:40:32 +0200655 description = 'implied by {}: {} {} {}-bit'.format(
gabor-mezei-armd9050a52021-06-28 16:35:48 +0200656 usage_expression, alg_expression, key_type_expression, bits)
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200657 key = StorageTestData(version=self.version,
658 id=1, lifetime=0x00000001,
659 type=key_type.expression, bits=bits,
660 usage=material_usage_flags,
661 expected_usage=expected_usage_flags,
662 without_implicit_usage=True,
663 alg=alg, alg2=alg2,
664 material=key_material,
665 description=description)
666 return key
gabor-mezei-arm672e3762021-06-24 10:16:44 +0200667
668 def gather_key_types_for_sign_alg(self) -> Dict[str, List[str]]:
gabor-mezei-arm0f8136a2021-06-24 14:38:25 +0200669 # pylint: disable=too-many-locals
gabor-mezei-arm672e3762021-06-24 10:16:44 +0200670 """Match possible key types for sign algorithms."""
671 # To create a valid combinaton both the algorithms and key types
672 # must be filtered. Pair them with keywords created from its names.
673 incompatible_alg_keyword = frozenset(['RAW', 'ANY', 'PURE'])
674 incompatible_key_type_keywords = frozenset(['MONTGOMERY'])
675 keyword_translation = {
676 'ECDSA': 'ECC',
677 'ED[0-9]*.*' : 'EDWARDS'
678 }
679 exclusive_keywords = {
680 'EDWARDS': 'ECC'
681 }
gabor-mezei-arm0f8136a2021-06-24 14:38:25 +0200682 key_types = set(self.constructors.generate_expressions(self.constructors.key_types))
683 algorithms = set(self.constructors.generate_expressions(self.constructors.sign_algorithms))
gabor-mezei-arm672e3762021-06-24 10:16:44 +0200684 alg_with_keys = {} #type: Dict[str, List[str]]
685 translation_table = str.maketrans('(', '_', ')')
686 for alg in algorithms:
687 # Generate keywords from the name of the algorithm
688 alg_keywords = set(alg.partition('(')[0].split(sep='_')[2:])
689 # Translate keywords for better matching with the key types
690 for keyword in alg_keywords.copy():
691 for pattern, replace in keyword_translation.items():
692 if re.match(pattern, keyword):
693 alg_keywords.remove(keyword)
694 alg_keywords.add(replace)
695 # Filter out incompatible algortihms
696 if not alg_keywords.isdisjoint(incompatible_alg_keyword):
697 continue
698
699 for key_type in key_types:
700 # Generate keywords from the of the key type
701 key_type_keywords = set(key_type.translate(translation_table).split(sep='_')[3:])
702
703 # Remove ambigious keywords
704 for keyword1, keyword2 in exclusive_keywords.items():
705 if keyword1 in key_type_keywords:
706 key_type_keywords.remove(keyword2)
707
708 if key_type_keywords.isdisjoint(incompatible_key_type_keywords) and\
709 not key_type_keywords.isdisjoint(alg_keywords):
710 if alg in alg_with_keys:
711 alg_with_keys[alg].append(key_type)
712 else:
713 alg_with_keys[alg] = [key_type]
714 return alg_with_keys
715
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200716 def all_keys_for_implicit_usage(self) -> Iterator[StorageTestData]:
gabor-mezei-arm672e3762021-06-24 10:16:44 +0200717 """Generate test keys for usage flag extensions."""
718 # Generate a key type and algorithm pair for each extendable usage
719 # flag to generate a valid key for exercising. The key is generated
720 # without usage extension to check the extension compatiblity.
gabor-mezei-arm672e3762021-06-24 10:16:44 +0200721 alg_with_keys = self.gather_key_types_for_sign_alg()
gabor-mezei-arm11e48382021-06-24 16:35:01 +0200722
gabor-mezei-arm340fbf32021-06-28 19:26:55 +0200723 for usage in sorted(StorageKey.IMPLICIT_USAGE_FLAGS, key=str):
724 for alg in sorted(alg_with_keys):
725 for key_type in sorted(alg_with_keys[alg]):
726 # The key types must be filtered to fit the specific usage flag.
gabor-mezei-arm2784bfe2021-06-28 20:02:11 +0200727 kt = crypto_knowledge.KeyType(key_type)
728 if kt.is_valid_for_signature(usage):
729 yield self.keys_for_implicit_usage(usage, alg, kt)
gabor-mezei-arm672e3762021-06-24 10:16:44 +0200730
gabor-mezei-arm0c24edd2021-06-29 15:42:57 +0200731 def generate_all_keys(self) -> Iterator[StorageTestData]:
732 yield from super().generate_all_keys()
733 yield from self.all_keys_for_implicit_usage()
gabor-mezei-arm7748b6f2021-06-24 10:04:38 +0200734
Gilles Peskineb94ea512021-03-10 02:12:08 +0100735class TestGenerator:
736 """Generate test data."""
737
738 def __init__(self, options) -> None:
739 self.test_suite_directory = self.get_option(options, 'directory',
740 'tests/suites')
741 self.info = Information()
742
743 @staticmethod
744 def get_option(options, name: str, default: T) -> T:
745 value = getattr(options, name, None)
746 return default if value is None else value
747
Gilles Peskine0298bda2021-03-10 02:34:37 +0100748 def filename_for(self, basename: str) -> str:
749 """The location of the data file with the specified base name."""
Bence Szépkúti9e84ec72021-05-07 11:49:17 +0200750 return posixpath.join(self.test_suite_directory, basename + '.data')
Gilles Peskine0298bda2021-03-10 02:34:37 +0100751
Gilles Peskineb94ea512021-03-10 02:12:08 +0100752 def write_test_data_file(self, basename: str,
753 test_cases: Iterable[test_case.TestCase]) -> None:
754 """Write the test cases to a .data file.
755
756 The output file is ``basename + '.data'`` in the test suite directory.
757 """
Gilles Peskine0298bda2021-03-10 02:34:37 +0100758 filename = self.filename_for(basename)
Gilles Peskineb94ea512021-03-10 02:12:08 +0100759 test_case.write_data_file(filename, test_cases)
760
Gilles Peskine0298bda2021-03-10 02:34:37 +0100761 TARGETS = {
Przemyslaw Stekiel1b0978b2021-10-15 15:21:51 +0200762 'test_suite_psa_crypto_generate_key.generated':
763 lambda info: KeyGenerate(info).test_cases_for_key_generation(),
Gilles Peskine0298bda2021-03-10 02:34:37 +0100764 'test_suite_psa_crypto_not_supported.generated':
Gilles Peskine3d778392021-02-17 15:11:05 +0100765 lambda info: NotSupported(info).test_cases_for_not_supported(),
Gilles Peskine897dff92021-03-10 15:03:44 +0100766 'test_suite_psa_crypto_storage_format.current':
gabor-mezei-arma4102cb2021-06-24 09:53:26 +0200767 lambda info: StorageFormatForward(info, 0).all_test_cases(),
Gilles Peskine897dff92021-03-10 15:03:44 +0100768 'test_suite_psa_crypto_storage_format.v0':
gabor-mezei-arma4102cb2021-06-24 09:53:26 +0200769 lambda info: StorageFormatV0(info).all_test_cases(),
Gilles Peskine0298bda2021-03-10 02:34:37 +0100770 } #type: Dict[str, Callable[[Information], Iterable[test_case.TestCase]]]
771
772 def generate_target(self, name: str) -> None:
773 test_cases = self.TARGETS[name](self.info)
774 self.write_test_data_file(name, test_cases)
Gilles Peskine14e428f2021-01-26 22:19:21 +0100775
Gilles Peskine09940492021-01-26 22:16:30 +0100776def main(args):
777 """Command line entry point."""
778 parser = argparse.ArgumentParser(description=__doc__)
Gilles Peskine0298bda2021-03-10 02:34:37 +0100779 parser.add_argument('--list', action='store_true',
780 help='List available targets and exit')
781 parser.add_argument('targets', nargs='*', metavar='TARGET',
782 help='Target file to generate (default: all; "-": none)')
Gilles Peskine09940492021-01-26 22:16:30 +0100783 options = parser.parse_args(args)
Gilles Peskinec86f20a2021-04-22 00:20:47 +0200784 build_tree.chdir_to_root()
Gilles Peskine09940492021-01-26 22:16:30 +0100785 generator = TestGenerator(options)
Gilles Peskine0298bda2021-03-10 02:34:37 +0100786 if options.list:
787 for name in sorted(generator.TARGETS):
788 print(generator.filename_for(name))
789 return
790 if options.targets:
791 # Allow "-" as a special case so you can run
792 # ``generate_psa_tests.py - $targets`` and it works uniformly whether
793 # ``$targets`` is empty or not.
794 options.targets = [os.path.basename(re.sub(r'\.data\Z', r'', target))
795 for target in options.targets
796 if target != '-']
797 else:
798 options.targets = sorted(generator.TARGETS)
799 for target in options.targets:
800 generator.generate_target(target)
Gilles Peskine09940492021-01-26 22:16:30 +0100801
802if __name__ == '__main__':
803 main(sys.argv[1:])