blob: c60b922b04ee7b57aabb8dfcb729d632453b09b6 [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(
103 constructors: macro_collector.PSAMacroCollector
104 ) -> None:
105 # Mbed TLS doesn't support DSA. Don't attempt to generate any related
106 # test case.
107 constructors.key_types.discard('PSA_KEY_TYPE_DSA_KEY_PAIR')
108 constructors.key_types.discard('PSA_KEY_TYPE_DSA_PUBLIC_KEY')
109 constructors.algorithms_from_hash.pop('PSA_ALG_DSA', None)
110 constructors.algorithms_from_hash.pop('PSA_ALG_DETERMINISTIC_DSA', None)
111
112 def read_psa_interface(self) -> macro_collector.PSAMacroCollector:
113 """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']
117 for header_file_name in header_file_names:
118 with open(header_file_name, 'rb') as header_file:
119 constructors.read_file(header_file)
120 self.remove_unwanted_macros(constructors)
Gilles Peskine3d404b82021-03-30 21:46:35 +0200121 constructors.gather_arguments()
Gilles Peskine09940492021-01-26 22:16:30 +0100122 return constructors
123
Gilles Peskine14e428f2021-01-26 22:19:21 +0100124
Gilles Peskineb94ea512021-03-10 02:12:08 +0100125def test_case_for_key_type_not_supported(
126 verb: str, key_type: str, bits: int,
127 dependencies: List[str],
128 *args: str,
129 param_descr: str = ''
130) -> test_case.TestCase:
131 """Return one test case exercising a key creation method
132 for an unsupported key type or size.
133 """
134 hack_dependencies_not_implemented(dependencies)
135 tc = test_case.TestCase()
136 short_key_type = re.sub(r'PSA_(KEY_TYPE|ECC_FAMILY)_', r'', key_type)
137 adverb = 'not' if dependencies else 'never'
138 if param_descr:
139 adverb = param_descr + ' ' + adverb
140 tc.set_description('PSA {} {} {}-bit {} supported'
141 .format(verb, short_key_type, bits, adverb))
142 tc.set_dependencies(dependencies)
143 tc.set_function(verb + '_not_supported')
144 tc.set_arguments([key_type] + list(args))
145 return tc
146
147class NotSupported:
148 """Generate test cases for when something is not supported."""
149
150 def __init__(self, info: Information) -> None:
151 self.constructors = info.constructors
Gilles Peskine14e428f2021-01-26 22:19:21 +0100152
Gilles Peskine60b29fe2021-02-16 14:06:50 +0100153 ALWAYS_SUPPORTED = frozenset([
154 'PSA_KEY_TYPE_DERIVE',
155 'PSA_KEY_TYPE_RAW_DATA',
156 ])
Gilles Peskine14e428f2021-01-26 22:19:21 +0100157 def test_cases_for_key_type_not_supported(
Gilles Peskine60b29fe2021-02-16 14:06:50 +0100158 self,
Gilles Peskineaf172842021-01-27 18:24:48 +0100159 kt: crypto_knowledge.KeyType,
160 param: Optional[int] = None,
161 param_descr: str = '',
Gilles Peskine3d778392021-02-17 15:11:05 +0100162 ) -> Iterator[test_case.TestCase]:
Gilles Peskineaf172842021-01-27 18:24:48 +0100163 """Return test cases exercising key creation when the given type is unsupported.
164
165 If param is present and not None, emit test cases conditioned on this
166 parameter not being supported. If it is absent or None, emit test cases
167 conditioned on the base type not being supported.
168 """
Gilles Peskine60b29fe2021-02-16 14:06:50 +0100169 if kt.name in self.ALWAYS_SUPPORTED:
170 # Don't generate test cases for key types that are always supported.
171 # They would be skipped in all configurations, which is noise.
Gilles Peskine3d778392021-02-17 15:11:05 +0100172 return
Gilles Peskineaf172842021-01-27 18:24:48 +0100173 import_dependencies = [('!' if param is None else '') +
174 psa_want_symbol(kt.name)]
175 if kt.params is not None:
176 import_dependencies += [('!' if param == i else '') +
177 psa_want_symbol(sym)
178 for i, sym in enumerate(kt.params)]
Gilles Peskine14e428f2021-01-26 22:19:21 +0100179 if kt.name.endswith('_PUBLIC_KEY'):
180 generate_dependencies = []
181 else:
182 generate_dependencies = import_dependencies
Gilles Peskine14e428f2021-01-26 22:19:21 +0100183 for bits in kt.sizes_to_test():
Gilles Peskine3d778392021-02-17 15:11:05 +0100184 yield test_case_for_key_type_not_supported(
Gilles Peskine7f756872021-02-16 12:13:12 +0100185 'import', kt.expression, bits,
186 finish_family_dependencies(import_dependencies, bits),
Gilles Peskineaf172842021-01-27 18:24:48 +0100187 test_case.hex_string(kt.key_material(bits)),
188 param_descr=param_descr,
Gilles Peskine3d778392021-02-17 15:11:05 +0100189 )
Gilles Peskineaf172842021-01-27 18:24:48 +0100190 if not generate_dependencies and param is not None:
191 # If generation is impossible for this key type, rather than
192 # supported or not depending on implementation capabilities,
193 # only generate the test case once.
194 continue
Gilles Peskine3d778392021-02-17 15:11:05 +0100195 yield test_case_for_key_type_not_supported(
Gilles Peskine7f756872021-02-16 12:13:12 +0100196 'generate', kt.expression, bits,
197 finish_family_dependencies(generate_dependencies, bits),
Gilles Peskineaf172842021-01-27 18:24:48 +0100198 str(bits),
199 param_descr=param_descr,
Gilles Peskine3d778392021-02-17 15:11:05 +0100200 )
Gilles Peskine14e428f2021-01-26 22:19:21 +0100201 # To be added: derive
Gilles Peskine14e428f2021-01-26 22:19:21 +0100202
Gilles Peskine3d778392021-02-17 15:11:05 +0100203 def test_cases_for_not_supported(self) -> Iterator[test_case.TestCase]:
Gilles Peskine14e428f2021-01-26 22:19:21 +0100204 """Generate test cases that exercise the creation of keys of unsupported types."""
Gilles Peskine14e428f2021-01-26 22:19:21 +0100205 for key_type in sorted(self.constructors.key_types):
206 kt = crypto_knowledge.KeyType(key_type)
Gilles Peskine3d778392021-02-17 15:11:05 +0100207 yield from self.test_cases_for_key_type_not_supported(kt)
Gilles Peskineaf172842021-01-27 18:24:48 +0100208 for curve_family in sorted(self.constructors.ecc_curves):
209 for constr in ('PSA_KEY_TYPE_ECC_KEY_PAIR',
210 'PSA_KEY_TYPE_ECC_PUBLIC_KEY'):
211 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."""
339 for key_type in sorted(self.constructors.key_types):
340 yield from self.keys_for_type(key_type)
341 for key_type in sorted(self.constructors.key_types_from_curve):
342 for curve in sorted(self.constructors.ecc_curves):
343 yield from self.keys_for_type(key_type, [curve])
344 ## Diffie-Hellman (FFDH) is not supported yet, either in
345 ## crypto_knowledge.py or in Mbed TLS.
346 # for key_type in sorted(self.constructors.key_types_from_group):
347 # for group in sorted(self.constructors.dh_groups):
348 # yield from self.keys_for_type(key_type, [group])
349
Gilles Peskined86bc522021-03-10 15:08:57 +0100350 def keys_for_algorithm(self, alg: str) -> Iterator[StorageKey]:
351 """Generate test keys for the specified algorithm."""
352 # For now, we don't have information on the compatibility of key
353 # types and algorithms. So we just test the encoding of algorithms,
354 # and not that operations can be performed with them.
355 descr = alg
356 usage = 'PSA_KEY_USAGE_EXPORT'
357 key1 = StorageKey(version=self.version,
358 id=1, lifetime=0x00000001,
359 type='PSA_KEY_TYPE_RAW_DATA', bits=8,
360 usage=usage, alg=alg, alg2=0,
361 material=b'K',
362 description='alg: ' + descr)
363 yield key1
364 key2 = StorageKey(version=self.version,
365 id=1, lifetime=0x00000001,
366 type='PSA_KEY_TYPE_RAW_DATA', bits=8,
367 usage=usage, alg=0, alg2=alg,
368 material=b'L',
369 description='alg2: ' + descr)
370 yield key2
371
372 def all_keys_for_algorithms(self) -> Iterator[StorageKey]:
373 """Generate test keys covering algorithm encodings."""
374 for alg in sorted(self.constructors.algorithms):
375 yield from self.keys_for_algorithm(alg)
376 # To do: algorithm constructors with parameters
377
Gilles Peskine897dff92021-03-10 15:03:44 +0100378 def all_test_cases(self) -> Iterator[test_case.TestCase]:
379 """Generate all storage format test cases."""
Gilles Peskine3c9d4232021-04-12 14:43:05 +0200380 # First build a list of all keys, then construct all the corresponding
381 # test cases. This allows all required information to be obtained in
382 # one go, which is a significant performance gain as the information
383 # includes numerical values obtained by compiling a C program.
384 keys = [] #type: List[StorageKey]
385 keys += self.all_keys_for_usage_flags()
386 keys += self.all_keys_for_types()
387 keys += self.all_keys_for_algorithms()
388 for key in keys:
Gilles Peskined86bc522021-03-10 15:08:57 +0100389 yield self.make_test_case(key)
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100390 # To do: vary id, lifetime
Gilles Peskine897dff92021-03-10 15:03:44 +0100391
392
Gilles Peskineb94ea512021-03-10 02:12:08 +0100393class TestGenerator:
394 """Generate test data."""
395
396 def __init__(self, options) -> None:
397 self.test_suite_directory = self.get_option(options, 'directory',
398 'tests/suites')
399 self.info = Information()
400
401 @staticmethod
402 def get_option(options, name: str, default: T) -> T:
403 value = getattr(options, name, None)
404 return default if value is None else value
405
Gilles Peskine0298bda2021-03-10 02:34:37 +0100406 def filename_for(self, basename: str) -> str:
407 """The location of the data file with the specified base name."""
Bence Szépkúti9e84ec72021-05-07 11:49:17 +0200408 return posixpath.join(self.test_suite_directory, basename + '.data')
Gilles Peskine0298bda2021-03-10 02:34:37 +0100409
Gilles Peskineb94ea512021-03-10 02:12:08 +0100410 def write_test_data_file(self, basename: str,
411 test_cases: Iterable[test_case.TestCase]) -> None:
412 """Write the test cases to a .data file.
413
414 The output file is ``basename + '.data'`` in the test suite directory.
415 """
Gilles Peskine0298bda2021-03-10 02:34:37 +0100416 filename = self.filename_for(basename)
Gilles Peskineb94ea512021-03-10 02:12:08 +0100417 test_case.write_data_file(filename, test_cases)
418
Gilles Peskine0298bda2021-03-10 02:34:37 +0100419 TARGETS = {
420 'test_suite_psa_crypto_not_supported.generated':
Gilles Peskine3d778392021-02-17 15:11:05 +0100421 lambda info: NotSupported(info).test_cases_for_not_supported(),
Gilles Peskine897dff92021-03-10 15:03:44 +0100422 'test_suite_psa_crypto_storage_format.current':
423 lambda info: StorageFormat(info, 0, True).all_test_cases(),
424 'test_suite_psa_crypto_storage_format.v0':
425 lambda info: StorageFormat(info, 0, False).all_test_cases(),
Gilles Peskine0298bda2021-03-10 02:34:37 +0100426 } #type: Dict[str, Callable[[Information], Iterable[test_case.TestCase]]]
427
428 def generate_target(self, name: str) -> None:
429 test_cases = self.TARGETS[name](self.info)
430 self.write_test_data_file(name, test_cases)
Gilles Peskine14e428f2021-01-26 22:19:21 +0100431
Gilles Peskine09940492021-01-26 22:16:30 +0100432def main(args):
433 """Command line entry point."""
434 parser = argparse.ArgumentParser(description=__doc__)
Gilles Peskine0298bda2021-03-10 02:34:37 +0100435 parser.add_argument('--list', action='store_true',
436 help='List available targets and exit')
437 parser.add_argument('targets', nargs='*', metavar='TARGET',
438 help='Target file to generate (default: all; "-": none)')
Gilles Peskine09940492021-01-26 22:16:30 +0100439 options = parser.parse_args(args)
Gilles Peskinec86f20a2021-04-22 00:20:47 +0200440 build_tree.chdir_to_root()
Gilles Peskine09940492021-01-26 22:16:30 +0100441 generator = TestGenerator(options)
Gilles Peskine0298bda2021-03-10 02:34:37 +0100442 if options.list:
443 for name in sorted(generator.TARGETS):
444 print(generator.filename_for(name))
445 return
446 if options.targets:
447 # Allow "-" as a special case so you can run
448 # ``generate_psa_tests.py - $targets`` and it works uniformly whether
449 # ``$targets`` is empty or not.
450 options.targets = [os.path.basename(re.sub(r'\.data\Z', r'', target))
451 for target in options.targets
452 if target != '-']
453 else:
454 options.targets = sorted(generator.TARGETS)
455 for target in options.targets:
456 generator.generate_target(target)
Gilles Peskine09940492021-01-26 22:16:30 +0100457
458if __name__ == '__main__':
459 main(sys.argv[1:])