blob: 6f3d72697d598982d007ed622d2a0c73d2006879 [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
25import re
Gilles Peskine09940492021-01-26 22:16:30 +010026import sys
Gilles Peskine3d778392021-02-17 15:11:05 +010027from typing import Callable, Dict, FrozenSet, Iterable, Iterator, List, Optional, TypeVar
Gilles Peskine09940492021-01-26 22:16:30 +010028
29import scripts_path # pylint: disable=unused-import
Gilles Peskine14e428f2021-01-26 22:19:21 +010030from mbedtls_dev import crypto_knowledge
Gilles Peskine09940492021-01-26 22:16:30 +010031from mbedtls_dev import macro_collector
Gilles Peskine897dff92021-03-10 15:03:44 +010032from mbedtls_dev import psa_storage
Gilles Peskine14e428f2021-01-26 22:19:21 +010033from mbedtls_dev import test_case
Gilles Peskine09940492021-01-26 22:16:30 +010034
35T = TypeVar('T') #pylint: disable=invalid-name
36
Gilles Peskine14e428f2021-01-26 22:19:21 +010037
Gilles Peskine7f756872021-02-16 12:13:12 +010038def psa_want_symbol(name: str) -> str:
Gilles Peskineaf172842021-01-27 18:24:48 +010039 """Return the PSA_WANT_xxx symbol associated with a PSA crypto feature."""
40 if name.startswith('PSA_'):
41 return name[:4] + 'WANT_' + name[4:]
42 else:
43 raise ValueError('Unable to determine the PSA_WANT_ symbol for ' + name)
44
Gilles Peskine7f756872021-02-16 12:13:12 +010045def finish_family_dependency(dep: str, bits: int) -> str:
46 """Finish dep if it's a family dependency symbol prefix.
47
48 A family dependency symbol prefix is a PSA_WANT_ symbol that needs to be
49 qualified by the key size. If dep is such a symbol, finish it by adjusting
50 the prefix and appending the key size. Other symbols are left unchanged.
51 """
52 return re.sub(r'_FAMILY_(.*)', r'_\1_' + str(bits), dep)
53
54def finish_family_dependencies(dependencies: List[str], bits: int) -> List[str]:
55 """Finish any family dependency symbol prefixes.
56
57 Apply `finish_family_dependency` to each element of `dependencies`.
58 """
59 return [finish_family_dependency(dep, bits) for dep in dependencies]
Gilles Peskineaf172842021-01-27 18:24:48 +010060
Gilles Peskinef8223ab2021-03-10 15:07:16 +010061def automatic_dependencies(*expressions: str) -> List[str]:
62 """Infer dependencies of a test case by looking for PSA_xxx symbols.
63
64 The arguments are strings which should be C expressions. Do not use
65 string literals or comments as this function is not smart enough to
66 skip them.
67 """
68 used = set()
69 for expr in expressions:
70 used.update(re.findall(r'PSA_(?:ALG|ECC_FAMILY|KEY_TYPE)_\w+', expr))
71 return sorted(psa_want_symbol(name) for name in used)
72
Gilles Peskined169d602021-02-16 14:16:25 +010073# A temporary hack: at the time of writing, not all dependency symbols
74# are implemented yet. Skip test cases for which the dependency symbols are
75# not available. Once all dependency symbols are available, this hack must
76# be removed so that a bug in the dependency symbols proprely leads to a test
77# failure.
78def read_implemented_dependencies(filename: str) -> FrozenSet[str]:
79 return frozenset(symbol
80 for line in open(filename)
81 for symbol in re.findall(r'\bPSA_WANT_\w+\b', line))
82IMPLEMENTED_DEPENDENCIES = read_implemented_dependencies('include/psa/crypto_config.h')
83def hack_dependencies_not_implemented(dependencies: List[str]) -> None:
84 if not all(dep.lstrip('!') in IMPLEMENTED_DEPENDENCIES
85 for dep in dependencies):
86 dependencies.append('DEPENDENCY_NOT_IMPLEMENTED_YET')
87
Gilles Peskine14e428f2021-01-26 22:19:21 +010088
Gilles Peskineb94ea512021-03-10 02:12:08 +010089class Information:
90 """Gather information about PSA constructors."""
Gilles Peskine09940492021-01-26 22:16:30 +010091
Gilles Peskineb94ea512021-03-10 02:12:08 +010092 def __init__(self) -> None:
Gilles Peskine09940492021-01-26 22:16:30 +010093 self.constructors = self.read_psa_interface()
94
95 @staticmethod
Gilles Peskine09940492021-01-26 22:16:30 +010096 def remove_unwanted_macros(
Gilles Peskineb93f8542021-04-19 13:50:25 +020097 constructors: macro_collector.PSAMacroEnumerator
Gilles Peskine09940492021-01-26 22:16:30 +010098 ) -> None:
Gilles Peskineb93f8542021-04-19 13:50:25 +020099 # Mbed TLS doesn't support finite-field DH yet and will not support
100 # finite-field DSA. Don't attempt to generate any related test case.
101 constructors.key_types.discard('PSA_KEY_TYPE_DH_KEY_PAIR')
102 constructors.key_types.discard('PSA_KEY_TYPE_DH_PUBLIC_KEY')
Gilles Peskine09940492021-01-26 22:16:30 +0100103 constructors.key_types.discard('PSA_KEY_TYPE_DSA_KEY_PAIR')
104 constructors.key_types.discard('PSA_KEY_TYPE_DSA_PUBLIC_KEY')
Gilles Peskine09940492021-01-26 22:16:30 +0100105
Gilles Peskineb93f8542021-04-19 13:50:25 +0200106 def read_psa_interface(self) -> macro_collector.PSAMacroEnumerator:
Gilles Peskine09940492021-01-26 22:16:30 +0100107 """Return the list of known key types, algorithms, etc."""
Gilles Peskined6d2d6a2021-03-30 21:46:35 +0200108 constructors = macro_collector.InputsForTest()
Gilles Peskine09940492021-01-26 22:16:30 +0100109 header_file_names = ['include/psa/crypto_values.h',
110 'include/psa/crypto_extra.h']
Gilles Peskineb93f8542021-04-19 13:50:25 +0200111 test_suites = ['tests/suites/test_suite_psa_crypto_metadata.data']
Gilles Peskine09940492021-01-26 22:16:30 +0100112 for header_file_name in header_file_names:
Gilles Peskineb93f8542021-04-19 13:50:25 +0200113 constructors.parse_header(header_file_name)
114 for test_cases in test_suites:
115 constructors.parse_test_cases(test_cases)
Gilles Peskine09940492021-01-26 22:16:30 +0100116 self.remove_unwanted_macros(constructors)
Gilles Peskined6d2d6a2021-03-30 21:46:35 +0200117 constructors.gather_arguments()
Gilles Peskine09940492021-01-26 22:16:30 +0100118 return constructors
119
Gilles Peskine14e428f2021-01-26 22:19:21 +0100120
Gilles Peskineb94ea512021-03-10 02:12:08 +0100121def test_case_for_key_type_not_supported(
122 verb: str, key_type: str, bits: int,
123 dependencies: List[str],
124 *args: str,
125 param_descr: str = ''
126) -> test_case.TestCase:
127 """Return one test case exercising a key creation method
128 for an unsupported key type or size.
129 """
130 hack_dependencies_not_implemented(dependencies)
131 tc = test_case.TestCase()
132 short_key_type = re.sub(r'PSA_(KEY_TYPE|ECC_FAMILY)_', r'', key_type)
133 adverb = 'not' if dependencies else 'never'
134 if param_descr:
135 adverb = param_descr + ' ' + adverb
136 tc.set_description('PSA {} {} {}-bit {} supported'
137 .format(verb, short_key_type, bits, adverb))
138 tc.set_dependencies(dependencies)
139 tc.set_function(verb + '_not_supported')
140 tc.set_arguments([key_type] + list(args))
141 return tc
142
143class NotSupported:
144 """Generate test cases for when something is not supported."""
145
146 def __init__(self, info: Information) -> None:
147 self.constructors = info.constructors
Gilles Peskine14e428f2021-01-26 22:19:21 +0100148
Gilles Peskine60b29fe2021-02-16 14:06:50 +0100149 ALWAYS_SUPPORTED = frozenset([
150 'PSA_KEY_TYPE_DERIVE',
151 'PSA_KEY_TYPE_RAW_DATA',
152 ])
Gilles Peskine14e428f2021-01-26 22:19:21 +0100153 def test_cases_for_key_type_not_supported(
Gilles Peskine60b29fe2021-02-16 14:06:50 +0100154 self,
Gilles Peskineaf172842021-01-27 18:24:48 +0100155 kt: crypto_knowledge.KeyType,
156 param: Optional[int] = None,
157 param_descr: str = '',
Gilles Peskine3d778392021-02-17 15:11:05 +0100158 ) -> Iterator[test_case.TestCase]:
Gilles Peskineaf172842021-01-27 18:24:48 +0100159 """Return test cases exercising key creation when the given type is unsupported.
160
161 If param is present and not None, emit test cases conditioned on this
162 parameter not being supported. If it is absent or None, emit test cases
163 conditioned on the base type not being supported.
164 """
Gilles Peskine60b29fe2021-02-16 14:06:50 +0100165 if kt.name in self.ALWAYS_SUPPORTED:
166 # Don't generate test cases for key types that are always supported.
167 # They would be skipped in all configurations, which is noise.
Gilles Peskine3d778392021-02-17 15:11:05 +0100168 return
Gilles Peskineaf172842021-01-27 18:24:48 +0100169 import_dependencies = [('!' if param is None else '') +
170 psa_want_symbol(kt.name)]
171 if kt.params is not None:
172 import_dependencies += [('!' if param == i else '') +
173 psa_want_symbol(sym)
174 for i, sym in enumerate(kt.params)]
Gilles Peskine14e428f2021-01-26 22:19:21 +0100175 if kt.name.endswith('_PUBLIC_KEY'):
176 generate_dependencies = []
177 else:
178 generate_dependencies = import_dependencies
Gilles Peskine14e428f2021-01-26 22:19:21 +0100179 for bits in kt.sizes_to_test():
Gilles Peskine3d778392021-02-17 15:11:05 +0100180 yield test_case_for_key_type_not_supported(
Gilles Peskine7f756872021-02-16 12:13:12 +0100181 'import', kt.expression, bits,
182 finish_family_dependencies(import_dependencies, bits),
Gilles Peskineaf172842021-01-27 18:24:48 +0100183 test_case.hex_string(kt.key_material(bits)),
184 param_descr=param_descr,
Gilles Peskine3d778392021-02-17 15:11:05 +0100185 )
Gilles Peskineaf172842021-01-27 18:24:48 +0100186 if not generate_dependencies and param is not None:
187 # If generation is impossible for this key type, rather than
188 # supported or not depending on implementation capabilities,
189 # only generate the test case once.
190 continue
Gilles Peskine3d778392021-02-17 15:11:05 +0100191 yield test_case_for_key_type_not_supported(
Gilles Peskine7f756872021-02-16 12:13:12 +0100192 'generate', kt.expression, bits,
193 finish_family_dependencies(generate_dependencies, bits),
Gilles Peskineaf172842021-01-27 18:24:48 +0100194 str(bits),
195 param_descr=param_descr,
Gilles Peskine3d778392021-02-17 15:11:05 +0100196 )
Gilles Peskine14e428f2021-01-26 22:19:21 +0100197 # To be added: derive
Gilles Peskine14e428f2021-01-26 22:19:21 +0100198
Gilles Peskineb93f8542021-04-19 13:50:25 +0200199 ECC_KEY_TYPES = ('PSA_KEY_TYPE_ECC_KEY_PAIR',
200 'PSA_KEY_TYPE_ECC_PUBLIC_KEY')
201
Gilles Peskine3d778392021-02-17 15:11:05 +0100202 def test_cases_for_not_supported(self) -> Iterator[test_case.TestCase]:
Gilles Peskine14e428f2021-01-26 22:19:21 +0100203 """Generate test cases that exercise the creation of keys of unsupported types."""
Gilles Peskine14e428f2021-01-26 22:19:21 +0100204 for key_type in sorted(self.constructors.key_types):
Gilles Peskineb93f8542021-04-19 13:50:25 +0200205 if key_type in self.ECC_KEY_TYPES:
206 continue
Gilles Peskine14e428f2021-01-26 22:19:21 +0100207 kt = crypto_knowledge.KeyType(key_type)
Gilles Peskine3d778392021-02-17 15:11:05 +0100208 yield from self.test_cases_for_key_type_not_supported(kt)
Gilles Peskineaf172842021-01-27 18:24:48 +0100209 for curve_family in sorted(self.constructors.ecc_curves):
Gilles Peskineb93f8542021-04-19 13:50:25 +0200210 for constr in self.ECC_KEY_TYPES:
Gilles Peskineaf172842021-01-27 18:24:48 +0100211 kt = crypto_knowledge.KeyType(constr, [curve_family])
Gilles Peskine3d778392021-02-17 15:11:05 +0100212 yield from self.test_cases_for_key_type_not_supported(
Gilles Peskineaf172842021-01-27 18:24:48 +0100213 kt, param_descr='type')
Gilles Peskine3d778392021-02-17 15:11:05 +0100214 yield from self.test_cases_for_key_type_not_supported(
Gilles Peskineaf172842021-01-27 18:24:48 +0100215 kt, 0, param_descr='curve')
Gilles Peskineb94ea512021-03-10 02:12:08 +0100216
217
Gilles Peskine897dff92021-03-10 15:03:44 +0100218class StorageKey(psa_storage.Key):
219 """Representation of a key for storage format testing."""
220
221 def __init__(self, *, description: str, **kwargs) -> None:
222 super().__init__(**kwargs)
223 self.description = description #type: str
224
225class StorageFormat:
226 """Storage format stability test cases."""
227
228 def __init__(self, info: Information, version: int, forward: bool) -> None:
229 """Prepare to generate test cases for storage format stability.
230
231 * `info`: information about the API. See the `Information` class.
232 * `version`: the storage format version to generate test cases for.
233 * `forward`: if true, generate forward compatibility test cases which
234 save a key and check that its representation is as intended. Otherwise
235 generate backward compatibility test cases which inject a key
236 representation and check that it can be read and used.
237 """
238 self.constructors = info.constructors
239 self.version = version
240 self.forward = forward
241
242 def make_test_case(self, key: StorageKey) -> test_case.TestCase:
243 """Construct a storage format test case for the given key.
244
245 If ``forward`` is true, generate a forward compatibility test case:
246 create a key and validate that it has the expected representation.
247 Otherwise generate a backward compatibility test case: inject the
248 key representation into storage and validate that it can be read
249 correctly.
250 """
251 verb = 'save' if self.forward else 'read'
252 tc = test_case.TestCase()
253 tc.set_description('PSA storage {}: {}'.format(verb, key.description))
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100254 dependencies = automatic_dependencies(
255 key.lifetime.string, key.type.string,
256 key.usage.string, key.alg.string, key.alg2.string,
257 )
258 dependencies = finish_family_dependencies(dependencies, key.bits)
259 tc.set_dependencies(dependencies)
Gilles Peskine897dff92021-03-10 15:03:44 +0100260 tc.set_function('key_storage_' + verb)
261 if self.forward:
262 extra_arguments = []
263 else:
264 # Some test keys have the RAW_DATA type and attributes that don't
265 # necessarily make sense. We do this to validate numerical
266 # encodings of the attributes.
267 # Raw data keys have no useful exercise anyway so there is no
268 # loss of test coverage.
269 exercise = key.type.string != 'PSA_KEY_TYPE_RAW_DATA'
270 extra_arguments = ['1' if exercise else '0']
271 tc.set_arguments([key.lifetime.string,
272 key.type.string, str(key.bits),
273 key.usage.string, key.alg.string, key.alg2.string,
274 '"' + key.material.hex() + '"',
275 '"' + key.hex() + '"',
276 *extra_arguments])
277 return tc
278
279 def key_for_usage_flags(
280 self,
281 usage_flags: List[str],
282 short: Optional[str] = None
283 ) -> StorageKey:
284 """Construct a test key for the given key usage."""
285 usage = ' | '.join(usage_flags) if usage_flags else '0'
286 if short is None:
287 short = re.sub(r'\bPSA_KEY_USAGE_', r'', usage)
288 description = 'usage: ' + short
289 key = StorageKey(version=self.version,
290 id=1, lifetime=0x00000001,
291 type='PSA_KEY_TYPE_RAW_DATA', bits=8,
292 usage=usage, alg=0, alg2=0,
293 material=b'K',
294 description=description)
295 return key
296
297 def all_keys_for_usage_flags(self) -> Iterator[StorageKey]:
298 """Generate test keys covering usage flags."""
299 known_flags = sorted(self.constructors.key_usage_flags)
300 yield self.key_for_usage_flags(['0'])
301 for usage_flag in known_flags:
302 yield self.key_for_usage_flags([usage_flag])
303 for flag1, flag2 in zip(known_flags,
304 known_flags[1:] + [known_flags[0]]):
305 yield self.key_for_usage_flags([flag1, flag2])
306 yield self.key_for_usage_flags(known_flags, short='all known')
307
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100308 def keys_for_type(
309 self,
310 key_type: str,
311 params: Optional[Iterable[str]] = None
312 ) -> Iterator[StorageKey]:
313 """Generate test keys for the given key type.
314
315 For key types that depend on a parameter (e.g. elliptic curve family),
316 `param` is the parameter to pass to the constructor. Only a single
317 parameter is supported.
318 """
319 kt = crypto_knowledge.KeyType(key_type, params)
320 for bits in kt.sizes_to_test():
321 usage_flags = 'PSA_KEY_USAGE_EXPORT'
322 alg = 0
323 alg2 = 0
324 key_material = kt.key_material(bits)
325 short_expression = re.sub(r'\bPSA_(?:KEY_TYPE|ECC_FAMILY)_',
326 r'',
327 kt.expression)
328 description = 'type: {} {}-bit'.format(short_expression, bits)
329 key = StorageKey(version=self.version,
330 id=1, lifetime=0x00000001,
331 type=kt.expression, bits=bits,
332 usage=usage_flags, alg=alg, alg2=alg2,
333 material=key_material,
334 description=description)
335 yield key
336
337 def all_keys_for_types(self) -> Iterator[StorageKey]:
338 """Generate test keys covering key types and their representations."""
Gilles Peskineb93f8542021-04-19 13:50:25 +0200339 key_types = sorted(self.constructors.key_types)
340 for key_type in self.constructors.generate_expressions(key_types):
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100341 yield from self.keys_for_type(key_type)
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100342
Gilles Peskined86bc522021-03-10 15:08:57 +0100343 def keys_for_algorithm(self, alg: str) -> Iterator[StorageKey]:
344 """Generate test keys for the specified algorithm."""
345 # For now, we don't have information on the compatibility of key
346 # types and algorithms. So we just test the encoding of algorithms,
347 # and not that operations can be performed with them.
348 descr = alg
349 usage = 'PSA_KEY_USAGE_EXPORT'
350 key1 = StorageKey(version=self.version,
351 id=1, lifetime=0x00000001,
352 type='PSA_KEY_TYPE_RAW_DATA', bits=8,
353 usage=usage, alg=alg, alg2=0,
354 material=b'K',
355 description='alg: ' + descr)
356 yield key1
357 key2 = StorageKey(version=self.version,
358 id=1, lifetime=0x00000001,
359 type='PSA_KEY_TYPE_RAW_DATA', bits=8,
360 usage=usage, alg=0, alg2=alg,
361 material=b'L',
362 description='alg2: ' + descr)
363 yield key2
364
365 def all_keys_for_algorithms(self) -> Iterator[StorageKey]:
366 """Generate test keys covering algorithm encodings."""
Gilles Peskineb93f8542021-04-19 13:50:25 +0200367 algorithms = sorted(self.constructors.algorithms)
368 for alg in self.constructors.generate_expressions(algorithms):
Gilles Peskined86bc522021-03-10 15:08:57 +0100369 yield from self.keys_for_algorithm(alg)
Gilles Peskined86bc522021-03-10 15:08:57 +0100370
Gilles Peskine897dff92021-03-10 15:03:44 +0100371 def all_test_cases(self) -> Iterator[test_case.TestCase]:
372 """Generate all storage format test cases."""
Gilles Peskineae9f14b2021-04-12 14:43:05 +0200373 # First build a list of all keys, then construct all the corresponding
374 # test cases. This allows all required information to be obtained in
375 # one go, which is a significant performance gain as the information
376 # includes numerical values obtained by compiling a C program.
377 keys = [] #type: List[StorageKey]
378 keys += self.all_keys_for_usage_flags()
379 keys += self.all_keys_for_types()
380 keys += self.all_keys_for_algorithms()
381 for key in keys:
Gilles Peskined86bc522021-03-10 15:08:57 +0100382 yield self.make_test_case(key)
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100383 # To do: vary id, lifetime
Gilles Peskine897dff92021-03-10 15:03:44 +0100384
385
Gilles Peskineb94ea512021-03-10 02:12:08 +0100386class TestGenerator:
387 """Generate test data."""
388
389 def __init__(self, options) -> None:
390 self.test_suite_directory = self.get_option(options, 'directory',
391 'tests/suites')
392 self.info = Information()
393
394 @staticmethod
395 def get_option(options, name: str, default: T) -> T:
396 value = getattr(options, name, None)
397 return default if value is None else value
398
Gilles Peskine0298bda2021-03-10 02:34:37 +0100399 def filename_for(self, basename: str) -> str:
400 """The location of the data file with the specified base name."""
401 return os.path.join(self.test_suite_directory, basename + '.data')
402
Gilles Peskineb94ea512021-03-10 02:12:08 +0100403 def write_test_data_file(self, basename: str,
404 test_cases: Iterable[test_case.TestCase]) -> None:
405 """Write the test cases to a .data file.
406
407 The output file is ``basename + '.data'`` in the test suite directory.
408 """
Gilles Peskine0298bda2021-03-10 02:34:37 +0100409 filename = self.filename_for(basename)
Gilles Peskineb94ea512021-03-10 02:12:08 +0100410 test_case.write_data_file(filename, test_cases)
411
Gilles Peskine0298bda2021-03-10 02:34:37 +0100412 TARGETS = {
413 'test_suite_psa_crypto_not_supported.generated':
Gilles Peskine3d778392021-02-17 15:11:05 +0100414 lambda info: NotSupported(info).test_cases_for_not_supported(),
Gilles Peskine897dff92021-03-10 15:03:44 +0100415 'test_suite_psa_crypto_storage_format.current':
416 lambda info: StorageFormat(info, 0, True).all_test_cases(),
417 'test_suite_psa_crypto_storage_format.v0':
418 lambda info: StorageFormat(info, 0, False).all_test_cases(),
Gilles Peskine0298bda2021-03-10 02:34:37 +0100419 } #type: Dict[str, Callable[[Information], Iterable[test_case.TestCase]]]
420
421 def generate_target(self, name: str) -> None:
422 test_cases = self.TARGETS[name](self.info)
423 self.write_test_data_file(name, test_cases)
Gilles Peskine14e428f2021-01-26 22:19:21 +0100424
Gilles Peskine09940492021-01-26 22:16:30 +0100425def main(args):
426 """Command line entry point."""
427 parser = argparse.ArgumentParser(description=__doc__)
Gilles Peskine0298bda2021-03-10 02:34:37 +0100428 parser.add_argument('--list', action='store_true',
429 help='List available targets and exit')
430 parser.add_argument('targets', nargs='*', metavar='TARGET',
431 help='Target file to generate (default: all; "-": none)')
Gilles Peskine09940492021-01-26 22:16:30 +0100432 options = parser.parse_args(args)
433 generator = TestGenerator(options)
Gilles Peskine0298bda2021-03-10 02:34:37 +0100434 if options.list:
435 for name in sorted(generator.TARGETS):
436 print(generator.filename_for(name))
437 return
438 if options.targets:
439 # Allow "-" as a special case so you can run
440 # ``generate_psa_tests.py - $targets`` and it works uniformly whether
441 # ``$targets`` is empty or not.
442 options.targets = [os.path.basename(re.sub(r'\.data\Z', r'', target))
443 for target in options.targets
444 if target != '-']
445 else:
446 options.targets = sorted(generator.TARGETS)
447 for target in options.targets:
448 generator.generate_target(target)
Gilles Peskine09940492021-01-26 22:16:30 +0100449
450if __name__ == '__main__':
451 main(sys.argv[1:])