blob: 78840c7e99f855af1e9cd7424305cde09afe55c3 [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 Peskinef8223ab2021-03-10 15:07:16 +010063def automatic_dependencies(*expressions: str) -> List[str]:
64 """Infer dependencies of a test case by looking for PSA_xxx symbols.
65
66 The arguments are strings which should be C expressions. Do not use
67 string literals or comments as this function is not smart enough to
68 skip them.
69 """
70 used = set()
71 for expr in expressions:
72 used.update(re.findall(r'PSA_(?:ALG|ECC_FAMILY|KEY_TYPE)_\w+', expr))
73 return sorted(psa_want_symbol(name) for name in used)
74
Gilles Peskined169d602021-02-16 14:16:25 +010075# A temporary hack: at the time of writing, not all dependency symbols
76# are implemented yet. Skip test cases for which the dependency symbols are
77# not available. Once all dependency symbols are available, this hack must
78# be removed so that a bug in the dependency symbols proprely leads to a test
79# failure.
80def read_implemented_dependencies(filename: str) -> FrozenSet[str]:
81 return frozenset(symbol
82 for line in open(filename)
83 for symbol in re.findall(r'\bPSA_WANT_\w+\b', line))
Gilles Peskinec86f20a2021-04-22 00:20:47 +020084_implemented_dependencies = None #type: Optional[FrozenSet[str]] #pylint: disable=invalid-name
Gilles Peskined169d602021-02-16 14:16:25 +010085def hack_dependencies_not_implemented(dependencies: List[str]) -> None:
Gilles Peskinec86f20a2021-04-22 00:20:47 +020086 global _implemented_dependencies #pylint: disable=global-statement,invalid-name
87 if _implemented_dependencies is None:
88 _implemented_dependencies = \
89 read_implemented_dependencies('include/psa/crypto_config.h')
90 if not all(dep.lstrip('!') in _implemented_dependencies
Gilles Peskined169d602021-02-16 14:16:25 +010091 for dep in dependencies):
92 dependencies.append('DEPENDENCY_NOT_IMPLEMENTED_YET')
93
Gilles Peskine14e428f2021-01-26 22:19:21 +010094
Gilles Peskineb94ea512021-03-10 02:12:08 +010095class Information:
96 """Gather information about PSA constructors."""
Gilles Peskine09940492021-01-26 22:16:30 +010097
Gilles Peskineb94ea512021-03-10 02:12:08 +010098 def __init__(self) -> None:
Gilles Peskine09940492021-01-26 22:16:30 +010099 self.constructors = self.read_psa_interface()
100
101 @staticmethod
Gilles Peskine09940492021-01-26 22:16:30 +0100102 def remove_unwanted_macros(
Gilles Peskine537d5fa2021-04-19 13:50:25 +0200103 constructors: macro_collector.PSAMacroEnumerator
Gilles Peskine09940492021-01-26 22:16:30 +0100104 ) -> None:
Gilles Peskine537d5fa2021-04-19 13:50:25 +0200105 # Mbed TLS doesn't support finite-field DH yet and will not support
106 # finite-field DSA. Don't attempt to generate any related test case.
107 constructors.key_types.discard('PSA_KEY_TYPE_DH_KEY_PAIR')
108 constructors.key_types.discard('PSA_KEY_TYPE_DH_PUBLIC_KEY')
Gilles Peskine09940492021-01-26 22:16:30 +0100109 constructors.key_types.discard('PSA_KEY_TYPE_DSA_KEY_PAIR')
110 constructors.key_types.discard('PSA_KEY_TYPE_DSA_PUBLIC_KEY')
Gilles Peskine09940492021-01-26 22:16:30 +0100111
Gilles Peskine537d5fa2021-04-19 13:50:25 +0200112 def read_psa_interface(self) -> macro_collector.PSAMacroEnumerator:
Gilles Peskine09940492021-01-26 22:16:30 +0100113 """Return the list of known key types, algorithms, etc."""
Gilles Peskine3d404b82021-03-30 21:46:35 +0200114 constructors = macro_collector.InputsForTest()
Gilles Peskine09940492021-01-26 22:16:30 +0100115 header_file_names = ['include/psa/crypto_values.h',
116 'include/psa/crypto_extra.h']
Gilles Peskine537d5fa2021-04-19 13:50:25 +0200117 test_suites = ['tests/suites/test_suite_psa_crypto_metadata.data']
Gilles Peskine09940492021-01-26 22:16:30 +0100118 for header_file_name in header_file_names:
Gilles Peskine537d5fa2021-04-19 13:50:25 +0200119 constructors.parse_header(header_file_name)
120 for test_cases in test_suites:
121 constructors.parse_test_cases(test_cases)
Gilles Peskine09940492021-01-26 22:16:30 +0100122 self.remove_unwanted_macros(constructors)
Gilles Peskine3d404b82021-03-30 21:46:35 +0200123 constructors.gather_arguments()
Gilles Peskine09940492021-01-26 22:16:30 +0100124 return constructors
125
Gilles Peskine14e428f2021-01-26 22:19:21 +0100126
Gilles Peskineb94ea512021-03-10 02:12:08 +0100127def test_case_for_key_type_not_supported(
128 verb: str, key_type: str, bits: int,
129 dependencies: List[str],
130 *args: str,
131 param_descr: str = ''
132) -> test_case.TestCase:
133 """Return one test case exercising a key creation method
134 for an unsupported key type or size.
135 """
136 hack_dependencies_not_implemented(dependencies)
137 tc = test_case.TestCase()
138 short_key_type = re.sub(r'PSA_(KEY_TYPE|ECC_FAMILY)_', r'', key_type)
139 adverb = 'not' if dependencies else 'never'
140 if param_descr:
141 adverb = param_descr + ' ' + adverb
142 tc.set_description('PSA {} {} {}-bit {} supported'
143 .format(verb, short_key_type, bits, adverb))
144 tc.set_dependencies(dependencies)
145 tc.set_function(verb + '_not_supported')
146 tc.set_arguments([key_type] + list(args))
147 return tc
148
149class NotSupported:
150 """Generate test cases for when something is not supported."""
151
152 def __init__(self, info: Information) -> None:
153 self.constructors = info.constructors
Gilles Peskine14e428f2021-01-26 22:19:21 +0100154
Gilles Peskine60b29fe2021-02-16 14:06:50 +0100155 ALWAYS_SUPPORTED = frozenset([
156 'PSA_KEY_TYPE_DERIVE',
157 'PSA_KEY_TYPE_RAW_DATA',
158 ])
Gilles Peskine14e428f2021-01-26 22:19:21 +0100159 def test_cases_for_key_type_not_supported(
Gilles Peskine60b29fe2021-02-16 14:06:50 +0100160 self,
Gilles Peskineaf172842021-01-27 18:24:48 +0100161 kt: crypto_knowledge.KeyType,
162 param: Optional[int] = None,
163 param_descr: str = '',
Gilles Peskine3d778392021-02-17 15:11:05 +0100164 ) -> Iterator[test_case.TestCase]:
Gilles Peskineaf172842021-01-27 18:24:48 +0100165 """Return test cases exercising key creation when the given type is unsupported.
166
167 If param is present and not None, emit test cases conditioned on this
168 parameter not being supported. If it is absent or None, emit test cases
169 conditioned on the base type not being supported.
170 """
Gilles Peskine60b29fe2021-02-16 14:06:50 +0100171 if kt.name in self.ALWAYS_SUPPORTED:
172 # Don't generate test cases for key types that are always supported.
173 # They would be skipped in all configurations, which is noise.
Gilles Peskine3d778392021-02-17 15:11:05 +0100174 return
Gilles Peskineaf172842021-01-27 18:24:48 +0100175 import_dependencies = [('!' if param is None else '') +
176 psa_want_symbol(kt.name)]
177 if kt.params is not None:
178 import_dependencies += [('!' if param == i else '') +
179 psa_want_symbol(sym)
180 for i, sym in enumerate(kt.params)]
Gilles Peskine14e428f2021-01-26 22:19:21 +0100181 if kt.name.endswith('_PUBLIC_KEY'):
182 generate_dependencies = []
183 else:
184 generate_dependencies = import_dependencies
Gilles Peskine14e428f2021-01-26 22:19:21 +0100185 for bits in kt.sizes_to_test():
Gilles Peskine3d778392021-02-17 15:11:05 +0100186 yield test_case_for_key_type_not_supported(
Gilles Peskine7f756872021-02-16 12:13:12 +0100187 'import', kt.expression, bits,
188 finish_family_dependencies(import_dependencies, bits),
Gilles Peskineaf172842021-01-27 18:24:48 +0100189 test_case.hex_string(kt.key_material(bits)),
190 param_descr=param_descr,
Gilles Peskine3d778392021-02-17 15:11:05 +0100191 )
Gilles Peskineaf172842021-01-27 18:24:48 +0100192 if not generate_dependencies and param is not None:
193 # If generation is impossible for this key type, rather than
194 # supported or not depending on implementation capabilities,
195 # only generate the test case once.
196 continue
Gilles Peskine3d778392021-02-17 15:11:05 +0100197 yield test_case_for_key_type_not_supported(
Gilles Peskine7f756872021-02-16 12:13:12 +0100198 'generate', kt.expression, bits,
199 finish_family_dependencies(generate_dependencies, bits),
Gilles Peskineaf172842021-01-27 18:24:48 +0100200 str(bits),
201 param_descr=param_descr,
Gilles Peskine3d778392021-02-17 15:11:05 +0100202 )
Gilles Peskine14e428f2021-01-26 22:19:21 +0100203 # To be added: derive
Gilles Peskine14e428f2021-01-26 22:19:21 +0100204
Gilles Peskine537d5fa2021-04-19 13:50:25 +0200205 ECC_KEY_TYPES = ('PSA_KEY_TYPE_ECC_KEY_PAIR',
206 'PSA_KEY_TYPE_ECC_PUBLIC_KEY')
207
Gilles Peskine3d778392021-02-17 15:11:05 +0100208 def test_cases_for_not_supported(self) -> Iterator[test_case.TestCase]:
Gilles Peskine14e428f2021-01-26 22:19:21 +0100209 """Generate test cases that exercise the creation of keys of unsupported types."""
Gilles Peskine14e428f2021-01-26 22:19:21 +0100210 for key_type in sorted(self.constructors.key_types):
Gilles Peskine537d5fa2021-04-19 13:50:25 +0200211 if key_type in self.ECC_KEY_TYPES:
212 continue
Gilles Peskine14e428f2021-01-26 22:19:21 +0100213 kt = crypto_knowledge.KeyType(key_type)
Gilles Peskine3d778392021-02-17 15:11:05 +0100214 yield from self.test_cases_for_key_type_not_supported(kt)
Gilles Peskineaf172842021-01-27 18:24:48 +0100215 for curve_family in sorted(self.constructors.ecc_curves):
Gilles Peskine537d5fa2021-04-19 13:50:25 +0200216 for constr in self.ECC_KEY_TYPES:
Gilles Peskineaf172842021-01-27 18:24:48 +0100217 kt = crypto_knowledge.KeyType(constr, [curve_family])
Gilles Peskine3d778392021-02-17 15:11:05 +0100218 yield from self.test_cases_for_key_type_not_supported(
Gilles Peskineaf172842021-01-27 18:24:48 +0100219 kt, param_descr='type')
Gilles Peskine3d778392021-02-17 15:11:05 +0100220 yield from self.test_cases_for_key_type_not_supported(
Gilles Peskineaf172842021-01-27 18:24:48 +0100221 kt, 0, param_descr='curve')
Gilles Peskineb94ea512021-03-10 02:12:08 +0100222
223
Gilles Peskine897dff92021-03-10 15:03:44 +0100224class StorageKey(psa_storage.Key):
225 """Representation of a key for storage format testing."""
226
227 def __init__(self, *, description: str, **kwargs) -> None:
228 super().__init__(**kwargs)
229 self.description = description #type: str
230
231class StorageFormat:
232 """Storage format stability test cases."""
233
234 def __init__(self, info: Information, version: int, forward: bool) -> None:
235 """Prepare to generate test cases for storage format stability.
236
237 * `info`: information about the API. See the `Information` class.
238 * `version`: the storage format version to generate test cases for.
239 * `forward`: if true, generate forward compatibility test cases which
240 save a key and check that its representation is as intended. Otherwise
241 generate backward compatibility test cases which inject a key
242 representation and check that it can be read and used.
243 """
244 self.constructors = info.constructors
245 self.version = version
246 self.forward = forward
247
248 def make_test_case(self, key: StorageKey) -> test_case.TestCase:
249 """Construct a storage format test case for the given key.
250
251 If ``forward`` is true, generate a forward compatibility test case:
252 create a key and validate that it has the expected representation.
253 Otherwise generate a backward compatibility test case: inject the
254 key representation into storage and validate that it can be read
255 correctly.
256 """
257 verb = 'save' if self.forward else 'read'
258 tc = test_case.TestCase()
259 tc.set_description('PSA storage {}: {}'.format(verb, key.description))
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100260 dependencies = automatic_dependencies(
261 key.lifetime.string, key.type.string,
262 key.usage.string, key.alg.string, key.alg2.string,
263 )
264 dependencies = finish_family_dependencies(dependencies, key.bits)
265 tc.set_dependencies(dependencies)
Gilles Peskine897dff92021-03-10 15:03:44 +0100266 tc.set_function('key_storage_' + verb)
267 if self.forward:
268 extra_arguments = []
269 else:
270 # Some test keys have the RAW_DATA type and attributes that don't
271 # necessarily make sense. We do this to validate numerical
272 # encodings of the attributes.
273 # Raw data keys have no useful exercise anyway so there is no
274 # loss of test coverage.
275 exercise = key.type.string != 'PSA_KEY_TYPE_RAW_DATA'
276 extra_arguments = ['1' if exercise else '0']
277 tc.set_arguments([key.lifetime.string,
278 key.type.string, str(key.bits),
279 key.usage.string, key.alg.string, key.alg2.string,
280 '"' + key.material.hex() + '"',
281 '"' + key.hex() + '"',
282 *extra_arguments])
283 return tc
284
285 def key_for_usage_flags(
286 self,
287 usage_flags: List[str],
288 short: Optional[str] = None
289 ) -> StorageKey:
290 """Construct a test key for the given key usage."""
291 usage = ' | '.join(usage_flags) if usage_flags else '0'
292 if short is None:
293 short = re.sub(r'\bPSA_KEY_USAGE_', r'', usage)
294 description = 'usage: ' + short
295 key = StorageKey(version=self.version,
296 id=1, lifetime=0x00000001,
297 type='PSA_KEY_TYPE_RAW_DATA', bits=8,
298 usage=usage, alg=0, alg2=0,
299 material=b'K',
300 description=description)
301 return key
302
303 def all_keys_for_usage_flags(self) -> Iterator[StorageKey]:
304 """Generate test keys covering usage flags."""
305 known_flags = sorted(self.constructors.key_usage_flags)
306 yield self.key_for_usage_flags(['0'])
307 for usage_flag in known_flags:
308 yield self.key_for_usage_flags([usage_flag])
309 for flag1, flag2 in zip(known_flags,
310 known_flags[1:] + [known_flags[0]]):
311 yield self.key_for_usage_flags([flag1, flag2])
312 yield self.key_for_usage_flags(known_flags, short='all known')
313
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100314 def keys_for_type(
315 self,
316 key_type: str,
317 params: Optional[Iterable[str]] = None
318 ) -> Iterator[StorageKey]:
319 """Generate test keys for the given key type.
320
321 For key types that depend on a parameter (e.g. elliptic curve family),
322 `param` is the parameter to pass to the constructor. Only a single
323 parameter is supported.
324 """
325 kt = crypto_knowledge.KeyType(key_type, params)
326 for bits in kt.sizes_to_test():
327 usage_flags = 'PSA_KEY_USAGE_EXPORT'
328 alg = 0
329 alg2 = 0
330 key_material = kt.key_material(bits)
331 short_expression = re.sub(r'\bPSA_(?:KEY_TYPE|ECC_FAMILY)_',
332 r'',
333 kt.expression)
334 description = 'type: {} {}-bit'.format(short_expression, bits)
335 key = StorageKey(version=self.version,
336 id=1, lifetime=0x00000001,
337 type=kt.expression, bits=bits,
338 usage=usage_flags, alg=alg, alg2=alg2,
339 material=key_material,
340 description=description)
341 yield key
342
343 def all_keys_for_types(self) -> Iterator[StorageKey]:
344 """Generate test keys covering key types and their representations."""
Gilles Peskine537d5fa2021-04-19 13:50:25 +0200345 key_types = sorted(self.constructors.key_types)
346 for key_type in self.constructors.generate_expressions(key_types):
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100347 yield from self.keys_for_type(key_type)
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100348
Gilles Peskined86bc522021-03-10 15:08:57 +0100349 def keys_for_algorithm(self, alg: str) -> Iterator[StorageKey]:
350 """Generate test keys for the specified algorithm."""
351 # For now, we don't have information on the compatibility of key
352 # types and algorithms. So we just test the encoding of algorithms,
353 # and not that operations can be performed with them.
354 descr = alg
355 usage = 'PSA_KEY_USAGE_EXPORT'
356 key1 = StorageKey(version=self.version,
357 id=1, lifetime=0x00000001,
358 type='PSA_KEY_TYPE_RAW_DATA', bits=8,
359 usage=usage, alg=alg, alg2=0,
360 material=b'K',
361 description='alg: ' + descr)
362 yield key1
363 key2 = StorageKey(version=self.version,
364 id=1, lifetime=0x00000001,
365 type='PSA_KEY_TYPE_RAW_DATA', bits=8,
366 usage=usage, alg=0, alg2=alg,
367 material=b'L',
368 description='alg2: ' + descr)
369 yield key2
370
371 def all_keys_for_algorithms(self) -> Iterator[StorageKey]:
372 """Generate test keys covering algorithm encodings."""
Gilles Peskine537d5fa2021-04-19 13:50:25 +0200373 algorithms = sorted(self.constructors.algorithms)
374 for alg in self.constructors.generate_expressions(algorithms):
Gilles Peskined86bc522021-03-10 15:08:57 +0100375 yield from self.keys_for_algorithm(alg)
Gilles Peskined86bc522021-03-10 15:08:57 +0100376
Gilles Peskine897dff92021-03-10 15:03:44 +0100377 def all_test_cases(self) -> Iterator[test_case.TestCase]:
378 """Generate all storage format test cases."""
Gilles Peskine3c9d4232021-04-12 14:43:05 +0200379 # First build a list of all keys, then construct all the corresponding
380 # test cases. This allows all required information to be obtained in
381 # one go, which is a significant performance gain as the information
382 # includes numerical values obtained by compiling a C program.
383 keys = [] #type: List[StorageKey]
384 keys += self.all_keys_for_usage_flags()
385 keys += self.all_keys_for_types()
386 keys += self.all_keys_for_algorithms()
387 for key in keys:
Gilles Peskined86bc522021-03-10 15:08:57 +0100388 yield self.make_test_case(key)
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100389 # To do: vary id, lifetime
Gilles Peskine897dff92021-03-10 15:03:44 +0100390
391
Gilles Peskineb94ea512021-03-10 02:12:08 +0100392class TestGenerator:
393 """Generate test data."""
394
395 def __init__(self, options) -> None:
396 self.test_suite_directory = self.get_option(options, 'directory',
397 'tests/suites')
398 self.info = Information()
399
400 @staticmethod
401 def get_option(options, name: str, default: T) -> T:
402 value = getattr(options, name, None)
403 return default if value is None else value
404
Gilles Peskine0298bda2021-03-10 02:34:37 +0100405 def filename_for(self, basename: str) -> str:
406 """The location of the data file with the specified base name."""
Bence Szépkúti9e84ec72021-05-07 11:49:17 +0200407 return posixpath.join(self.test_suite_directory, basename + '.data')
Gilles Peskine0298bda2021-03-10 02:34:37 +0100408
Gilles Peskineb94ea512021-03-10 02:12:08 +0100409 def write_test_data_file(self, basename: str,
410 test_cases: Iterable[test_case.TestCase]) -> None:
411 """Write the test cases to a .data file.
412
413 The output file is ``basename + '.data'`` in the test suite directory.
414 """
Gilles Peskine0298bda2021-03-10 02:34:37 +0100415 filename = self.filename_for(basename)
Gilles Peskineb94ea512021-03-10 02:12:08 +0100416 test_case.write_data_file(filename, test_cases)
417
Gilles Peskine0298bda2021-03-10 02:34:37 +0100418 TARGETS = {
419 'test_suite_psa_crypto_not_supported.generated':
Gilles Peskine3d778392021-02-17 15:11:05 +0100420 lambda info: NotSupported(info).test_cases_for_not_supported(),
Gilles Peskine897dff92021-03-10 15:03:44 +0100421 'test_suite_psa_crypto_storage_format.current':
422 lambda info: StorageFormat(info, 0, True).all_test_cases(),
423 'test_suite_psa_crypto_storage_format.v0':
424 lambda info: StorageFormat(info, 0, False).all_test_cases(),
Gilles Peskine0298bda2021-03-10 02:34:37 +0100425 } #type: Dict[str, Callable[[Information], Iterable[test_case.TestCase]]]
426
427 def generate_target(self, name: str) -> None:
428 test_cases = self.TARGETS[name](self.info)
429 self.write_test_data_file(name, test_cases)
Gilles Peskine14e428f2021-01-26 22:19:21 +0100430
Gilles Peskine09940492021-01-26 22:16:30 +0100431def main(args):
432 """Command line entry point."""
433 parser = argparse.ArgumentParser(description=__doc__)
Gilles Peskine0298bda2021-03-10 02:34:37 +0100434 parser.add_argument('--list', action='store_true',
435 help='List available targets and exit')
436 parser.add_argument('targets', nargs='*', metavar='TARGET',
437 help='Target file to generate (default: all; "-": none)')
Gilles Peskine09940492021-01-26 22:16:30 +0100438 options = parser.parse_args(args)
Gilles Peskinec86f20a2021-04-22 00:20:47 +0200439 build_tree.chdir_to_root()
Gilles Peskine09940492021-01-26 22:16:30 +0100440 generator = TestGenerator(options)
Gilles Peskine0298bda2021-03-10 02:34:37 +0100441 if options.list:
442 for name in sorted(generator.TARGETS):
443 print(generator.filename_for(name))
444 return
445 if options.targets:
446 # Allow "-" as a special case so you can run
447 # ``generate_psa_tests.py - $targets`` and it works uniformly whether
448 # ``$targets`` is empty or not.
449 options.targets = [os.path.basename(re.sub(r'\.data\Z', r'', target))
450 for target in options.targets
451 if target != '-']
452 else:
453 options.targets = sorted(generator.TARGETS)
454 for target in options.targets:
455 generator.generate_target(target)
Gilles Peskine09940492021-01-26 22:16:30 +0100456
457if __name__ == '__main__':
458 main(sys.argv[1:])