blob: add8fe52eee370e0a5c5ed00cffb32b242f020cb [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 Peskine8a55b432021-04-20 23:23:45 +020061SYMBOLS_WITHOUT_DEPENDENCY = frozenset([
62 'PSA_ALG_AEAD_WITH_AT_LEAST_THIS_LENGTH_TAG', # modifier, only in policies
63 'PSA_ALG_AEAD_WITH_SHORTENED_TAG', # modifier
64 'PSA_ALG_ANY_HASH', # only in policies
65 'PSA_ALG_AT_LEAST_THIS_LENGTH_MAC', # modifier, only in policies
66 'PSA_ALG_KEY_AGREEMENT', # chaining
67 'PSA_ALG_TRUNCATED_MAC', # modifier
68])
Gilles Peskinef8223ab2021-03-10 15:07:16 +010069def automatic_dependencies(*expressions: str) -> List[str]:
70 """Infer dependencies of a test case by looking for PSA_xxx symbols.
71
72 The arguments are strings which should be C expressions. Do not use
73 string literals or comments as this function is not smart enough to
74 skip them.
75 """
76 used = set()
77 for expr in expressions:
78 used.update(re.findall(r'PSA_(?:ALG|ECC_FAMILY|KEY_TYPE)_\w+', expr))
Gilles Peskine8a55b432021-04-20 23:23:45 +020079 used.difference_update(SYMBOLS_WITHOUT_DEPENDENCY)
Gilles Peskinef8223ab2021-03-10 15:07:16 +010080 return sorted(psa_want_symbol(name) for name in used)
81
Gilles Peskined169d602021-02-16 14:16:25 +010082# A temporary hack: at the time of writing, not all dependency symbols
83# are implemented yet. Skip test cases for which the dependency symbols are
84# not available. Once all dependency symbols are available, this hack must
85# be removed so that a bug in the dependency symbols proprely leads to a test
86# failure.
87def read_implemented_dependencies(filename: str) -> FrozenSet[str]:
88 return frozenset(symbol
89 for line in open(filename)
90 for symbol in re.findall(r'\bPSA_WANT_\w+\b', line))
91IMPLEMENTED_DEPENDENCIES = read_implemented_dependencies('include/psa/crypto_config.h')
92def hack_dependencies_not_implemented(dependencies: List[str]) -> None:
93 if not all(dep.lstrip('!') in IMPLEMENTED_DEPENDENCIES
94 for dep in dependencies):
95 dependencies.append('DEPENDENCY_NOT_IMPLEMENTED_YET')
96
Gilles Peskine14e428f2021-01-26 22:19:21 +010097
Gilles Peskineb94ea512021-03-10 02:12:08 +010098class Information:
99 """Gather information about PSA constructors."""
Gilles Peskine09940492021-01-26 22:16:30 +0100100
Gilles Peskineb94ea512021-03-10 02:12:08 +0100101 def __init__(self) -> None:
Gilles Peskine09940492021-01-26 22:16:30 +0100102 self.constructors = self.read_psa_interface()
103
104 @staticmethod
Gilles Peskine09940492021-01-26 22:16:30 +0100105 def remove_unwanted_macros(
Gilles Peskineb93f8542021-04-19 13:50:25 +0200106 constructors: macro_collector.PSAMacroEnumerator
Gilles Peskine09940492021-01-26 22:16:30 +0100107 ) -> None:
Gilles Peskineb93f8542021-04-19 13:50:25 +0200108 # Mbed TLS doesn't support finite-field DH yet and will not support
109 # finite-field DSA. Don't attempt to generate any related test case.
110 constructors.key_types.discard('PSA_KEY_TYPE_DH_KEY_PAIR')
111 constructors.key_types.discard('PSA_KEY_TYPE_DH_PUBLIC_KEY')
Gilles Peskine09940492021-01-26 22:16:30 +0100112 constructors.key_types.discard('PSA_KEY_TYPE_DSA_KEY_PAIR')
113 constructors.key_types.discard('PSA_KEY_TYPE_DSA_PUBLIC_KEY')
Gilles Peskine09940492021-01-26 22:16:30 +0100114
Gilles Peskineb93f8542021-04-19 13:50:25 +0200115 def read_psa_interface(self) -> macro_collector.PSAMacroEnumerator:
Gilles Peskine09940492021-01-26 22:16:30 +0100116 """Return the list of known key types, algorithms, etc."""
Gilles Peskined6d2d6a2021-03-30 21:46:35 +0200117 constructors = macro_collector.InputsForTest()
Gilles Peskine09940492021-01-26 22:16:30 +0100118 header_file_names = ['include/psa/crypto_values.h',
119 'include/psa/crypto_extra.h']
Gilles Peskineb93f8542021-04-19 13:50:25 +0200120 test_suites = ['tests/suites/test_suite_psa_crypto_metadata.data']
Gilles Peskine09940492021-01-26 22:16:30 +0100121 for header_file_name in header_file_names:
Gilles Peskineb93f8542021-04-19 13:50:25 +0200122 constructors.parse_header(header_file_name)
123 for test_cases in test_suites:
124 constructors.parse_test_cases(test_cases)
Gilles Peskine09940492021-01-26 22:16:30 +0100125 self.remove_unwanted_macros(constructors)
Gilles Peskined6d2d6a2021-03-30 21:46:35 +0200126 constructors.gather_arguments()
Gilles Peskine09940492021-01-26 22:16:30 +0100127 return constructors
128
Gilles Peskine14e428f2021-01-26 22:19:21 +0100129
Przemyslaw Stekield6ead7c2021-10-11 10:15:25 +0200130def test_case_for_key_type_not_supported(
Gilles Peskineb94ea512021-03-10 02:12:08 +0100131 verb: str, key_type: str, bits: int,
132 dependencies: List[str],
133 *args: str,
134 param_descr: str = ''
135) -> test_case.TestCase:
136 """Return one test case exercising a key creation method
137 for an unsupported key type or size.
138 """
139 hack_dependencies_not_implemented(dependencies)
140 tc = test_case.TestCase()
141 short_key_type = re.sub(r'PSA_(KEY_TYPE|ECC_FAMILY)_', r'', key_type)
142 adverb = 'not' if dependencies else 'never'
143 if param_descr:
144 adverb = param_descr + ' ' + adverb
Przemyslaw Stekield6ead7c2021-10-11 10:15:25 +0200145 tc.set_description('PSA {} {} {}-bit {} supported'
146 .format(verb, short_key_type, bits, adverb))
147 tc.set_dependencies(dependencies)
148 tc.set_function(verb + '_not_supported')
149 tc.set_arguments([key_type] + list(args))
150 return tc
151
152def test_case_for_key_type_invalid_argument(
153 verb: str, key_type: str, bits: int,
154 dependencies: List[str],
155 *args: str,
156 param_descr: str = ''
157) -> test_case.TestCase:
158 """Return one test case exercising a key creation method
159 for an invalid argument when key is public.
160 """
161 hack_dependencies_not_implemented(dependencies)
162 tc = test_case.TestCase()
163 short_key_type = re.sub(r'PSA_(KEY_TYPE|ECC_FAMILY)_', r'', key_type)
164 adverb = 'not' if dependencies else 'never'
165 if param_descr:
166 adverb = param_descr + ' ' + adverb
167 tc.set_description('PSA {} {} {}-bit invalid argument'
168 .format(verb, short_key_type, bits))
169 tc.set_function(verb + '_invalid_argument')
Gilles Peskineb94ea512021-03-10 02:12:08 +0100170 tc.set_dependencies(dependencies)
Gilles Peskineb94ea512021-03-10 02:12:08 +0100171 tc.set_arguments([key_type] + list(args))
172 return tc
173
174class NotSupported:
Przemyslaw Stekield6ead7c2021-10-11 10:15:25 +0200175 """Generate test cases for when something is not supported or argument is inavlid."""
Gilles Peskineb94ea512021-03-10 02:12:08 +0100176
177 def __init__(self, info: Information) -> None:
178 self.constructors = info.constructors
Gilles Peskine14e428f2021-01-26 22:19:21 +0100179
Gilles Peskine60b29fe2021-02-16 14:06:50 +0100180 ALWAYS_SUPPORTED = frozenset([
181 'PSA_KEY_TYPE_DERIVE',
182 'PSA_KEY_TYPE_RAW_DATA',
183 ])
Gilles Peskine14e428f2021-01-26 22:19:21 +0100184 def test_cases_for_key_type_not_supported(
Gilles Peskine60b29fe2021-02-16 14:06:50 +0100185 self,
Gilles Peskineaf172842021-01-27 18:24:48 +0100186 kt: crypto_knowledge.KeyType,
187 param: Optional[int] = None,
188 param_descr: str = '',
Gilles Peskine3d778392021-02-17 15:11:05 +0100189 ) -> Iterator[test_case.TestCase]:
Przemyslaw Stekield6ead7c2021-10-11 10:15:25 +0200190 """Return test cases exercising key creation when the given type is unsupported
191 or argument is invalid.
Gilles Peskineaf172842021-01-27 18:24:48 +0100192
193 If param is present and not None, emit test cases conditioned on this
194 parameter not being supported. If it is absent or None, emit test cases
Przemyslaw Stekield6ead7c2021-10-11 10:15:25 +0200195 conditioned on the base type not being supported. If key is public emit test
196 case for invalid argument.
Gilles Peskineaf172842021-01-27 18:24:48 +0100197 """
Gilles Peskine60b29fe2021-02-16 14:06:50 +0100198 if kt.name in self.ALWAYS_SUPPORTED:
199 # Don't generate test cases for key types that are always supported.
200 # They would be skipped in all configurations, which is noise.
Gilles Peskine3d778392021-02-17 15:11:05 +0100201 return
Gilles Peskineaf172842021-01-27 18:24:48 +0100202 import_dependencies = [('!' if param is None else '') +
203 psa_want_symbol(kt.name)]
204 if kt.params is not None:
205 import_dependencies += [('!' if param == i else '') +
206 psa_want_symbol(sym)
207 for i, sym in enumerate(kt.params)]
Gilles Peskine14e428f2021-01-26 22:19:21 +0100208 if kt.name.endswith('_PUBLIC_KEY'):
209 generate_dependencies = []
210 else:
211 generate_dependencies = import_dependencies
Gilles Peskine14e428f2021-01-26 22:19:21 +0100212 for bits in kt.sizes_to_test():
Przemyslaw Stekield6ead7c2021-10-11 10:15:25 +0200213 yield test_case_for_key_type_not_supported(
Gilles Peskine7f756872021-02-16 12:13:12 +0100214 'import', kt.expression, bits,
215 finish_family_dependencies(import_dependencies, bits),
Gilles Peskineaf172842021-01-27 18:24:48 +0100216 test_case.hex_string(kt.key_material(bits)),
217 param_descr=param_descr,
Gilles Peskine3d778392021-02-17 15:11:05 +0100218 )
Gilles Peskineaf172842021-01-27 18:24:48 +0100219 if not generate_dependencies and param is not None:
220 # If generation is impossible for this key type, rather than
221 # supported or not depending on implementation capabilities,
222 # only generate the test case once.
223 continue
Przemyslaw Stekield6ead7c2021-10-11 10:15:25 +0200224 if kt.name.endswith('_PUBLIC_KEY'):
225 yield test_case_for_key_type_invalid_argument(
226 'generate', kt.expression, bits,
227 finish_family_dependencies(generate_dependencies, bits),
228 str(bits),
229 param_descr=param_descr,
230 )
231 else:
232 yield test_case_for_key_type_not_supported(
233 'generate', kt.expression, bits,
234 finish_family_dependencies(generate_dependencies, bits),
235 str(bits),
236 param_descr=param_descr,
237 )
Gilles Peskine14e428f2021-01-26 22:19:21 +0100238 # To be added: derive
Gilles Peskine14e428f2021-01-26 22:19:21 +0100239
Gilles Peskineb93f8542021-04-19 13:50:25 +0200240 ECC_KEY_TYPES = ('PSA_KEY_TYPE_ECC_KEY_PAIR',
241 'PSA_KEY_TYPE_ECC_PUBLIC_KEY')
242
Gilles Peskine3d778392021-02-17 15:11:05 +0100243 def test_cases_for_not_supported(self) -> Iterator[test_case.TestCase]:
Gilles Peskine14e428f2021-01-26 22:19:21 +0100244 """Generate test cases that exercise the creation of keys of unsupported types."""
Gilles Peskine14e428f2021-01-26 22:19:21 +0100245 for key_type in sorted(self.constructors.key_types):
Gilles Peskineb93f8542021-04-19 13:50:25 +0200246 if key_type in self.ECC_KEY_TYPES:
247 continue
Gilles Peskine14e428f2021-01-26 22:19:21 +0100248 kt = crypto_knowledge.KeyType(key_type)
Gilles Peskine3d778392021-02-17 15:11:05 +0100249 yield from self.test_cases_for_key_type_not_supported(kt)
Gilles Peskineaf172842021-01-27 18:24:48 +0100250 for curve_family in sorted(self.constructors.ecc_curves):
Gilles Peskineb93f8542021-04-19 13:50:25 +0200251 for constr in self.ECC_KEY_TYPES:
Gilles Peskineaf172842021-01-27 18:24:48 +0100252 kt = crypto_knowledge.KeyType(constr, [curve_family])
Gilles Peskine3d778392021-02-17 15:11:05 +0100253 yield from self.test_cases_for_key_type_not_supported(
Gilles Peskineaf172842021-01-27 18:24:48 +0100254 kt, param_descr='type')
Gilles Peskine3d778392021-02-17 15:11:05 +0100255 yield from self.test_cases_for_key_type_not_supported(
Gilles Peskineaf172842021-01-27 18:24:48 +0100256 kt, 0, param_descr='curve')
Gilles Peskineb94ea512021-03-10 02:12:08 +0100257
Przemyslaw Stekiel997caf82021-10-15 15:21:51 +0200258def test_case_for_key_generation(
259 key_type: str, bits: int,
260 dependencies: List[str],
261 *args: str,
262 result: str = '',
263 param_descr: str = '',
264) -> test_case.TestCase:
265 """Return one test case exercising a key generation.
266 """
267 hack_dependencies_not_implemented(dependencies)
268 tc = test_case.TestCase()
269 short_key_type = re.sub(r'PSA_(KEY_TYPE|ECC_FAMILY)_', r'', key_type)
270 tc.set_description('PSA {} {}-bit'
271 .format( short_key_type, bits))
272 tc.set_dependencies(dependencies)
273 tc.set_function('generate_key')
274 tc.set_arguments([key_type] + list(args))
275 tc.set_result(result)
276
277 return tc
278
279class KeyGenerate:
280 """Generate positive and negative (invalid argument) test cases for key generation."""
281
282 def __init__(self, info: Information) -> None:
283 self.constructors = info.constructors
284
285 def test_cases_for_key_type_key_generation(
286 self,
287 kt: crypto_knowledge.KeyType,
288 param: Optional[int] = None,
289 param_descr: str = '',
290 ) -> Iterator[test_case.TestCase]:
291 """Return test cases exercising key generation.
292
293 All key types can be generated except for public keys. For public key
294 PSA_ERROR_INVALID_ARGUMENT status is expected.
295 """
296 result = 'PSA_SUCCESS'
297
298 import_dependencies = [psa_want_symbol(kt.name)]
299 if kt.params is not None:
300 import_dependencies += [psa_want_symbol(sym)
301 for i, sym in enumerate(kt.params)]
302 if kt.name.endswith('_PUBLIC_KEY'):
303 generate_dependencies = []
304 result = 'PSA_ERROR_INVALID_ARGUMENT'
305 else:
306 generate_dependencies = import_dependencies
307 for bits in kt.sizes_to_test():
308 yield test_case_for_key_generation(
309 kt.expression, bits,
310 finish_family_dependencies(generate_dependencies, bits),
311 str(bits),
312 result,
313 param_descr=param_descr
314 )
315
316 ECC_KEY_TYPES = ('PSA_KEY_TYPE_ECC_KEY_PAIR',
317 'PSA_KEY_TYPE_ECC_PUBLIC_KEY')
318
319 def test_cases_for_key_generation(self) -> Iterator[test_case.TestCase]:
320 """Generate test cases that exercise the generation of keys."""
321 for key_type in sorted(self.constructors.key_types):
322 if key_type in self.ECC_KEY_TYPES:
323 continue
324 kt = crypto_knowledge.KeyType(key_type)
325 yield from self.test_cases_for_key_type_key_generation(kt)
326 for curve_family in sorted(self.constructors.ecc_curves):
327 for constr in self.ECC_KEY_TYPES:
328 kt = crypto_knowledge.KeyType(constr, [curve_family])
329 yield from self.test_cases_for_key_type_key_generation(
330 kt, param_descr='type')
331 yield from self.test_cases_for_key_type_key_generation(
332 kt, 0, param_descr='curve')
333
334
Gilles Peskine897dff92021-03-10 15:03:44 +0100335class StorageKey(psa_storage.Key):
336 """Representation of a key for storage format testing."""
337
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200338 IMPLICIT_USAGE_FLAGS = {
339 'PSA_KEY_USAGE_SIGN_HASH': 'PSA_KEY_USAGE_SIGN_MESSAGE',
340 'PSA_KEY_USAGE_VERIFY_HASH': 'PSA_KEY_USAGE_VERIFY_MESSAGE'
341 } #type: Dict[str, str]
342 """Mapping of usage flags to the flags that they imply."""
343
344 def __init__(
345 self,
346 usage: str,
347 without_implicit_usage: Optional[bool] = False,
348 **kwargs
349 ) -> None:
350 """Prepare to generate a key.
351
352 * `usage` : The usage flags used for the key.
353 * `without_implicit_usage`: Flag to defide to apply the usage extension
354 """
gabor-mezei-arm3ea27322021-06-29 17:21:21 +0200355 super().__init__(usage=usage, **kwargs)
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200356
357 if not without_implicit_usage:
358 for flag, implicit in self.IMPLICIT_USAGE_FLAGS.items():
359 if self.usage.value() & psa_storage.Expr(flag).value() and \
360 self.usage.value() & psa_storage.Expr(implicit).value() == 0:
361 self.usage = psa_storage.Expr(self.usage.string + ' | ' + implicit)
362
363class StorageTestData(StorageKey):
364 """Representation of test case data for storage format testing."""
365
gabor-mezei-arm044fefc2021-06-24 10:16:44 +0200366 def __init__(
367 self,
368 description: str,
369 expected_usage: Optional[str] = None,
370 **kwargs
371 ) -> None:
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200372 """Prepare to generate test data
gabor-mezei-arm044fefc2021-06-24 10:16:44 +0200373
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200374 * `description` : used for the the test case names
375 * `expected_usage`: the usage flags generated as the expected usage flags
376 in the test cases. CAn differ from the usage flags
377 stored in the keys because of the usage flags extension.
gabor-mezei-arm044fefc2021-06-24 10:16:44 +0200378 """
Gilles Peskine897dff92021-03-10 15:03:44 +0100379 super().__init__(**kwargs)
380 self.description = description #type: str
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200381 self.expected_usage = expected_usage if expected_usage else self.usage.string #type: str
gabor-mezei-arm15c1f032021-06-24 10:04:38 +0200382
Gilles Peskine897dff92021-03-10 15:03:44 +0100383class StorageFormat:
384 """Storage format stability test cases."""
385
386 def __init__(self, info: Information, version: int, forward: bool) -> None:
387 """Prepare to generate test cases for storage format stability.
388
389 * `info`: information about the API. See the `Information` class.
390 * `version`: the storage format version to generate test cases for.
391 * `forward`: if true, generate forward compatibility test cases which
392 save a key and check that its representation is as intended. Otherwise
393 generate backward compatibility test cases which inject a key
394 representation and check that it can be read and used.
395 """
gabor-mezei-arm0bdb84e2021-06-23 17:01:44 +0200396 self.constructors = info.constructors #type: macro_collector.PSAMacroEnumerator
397 self.version = version #type: int
398 self.forward = forward #type: bool
Gilles Peskine897dff92021-03-10 15:03:44 +0100399
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200400 def make_test_case(self, key: StorageTestData) -> test_case.TestCase:
Gilles Peskine897dff92021-03-10 15:03:44 +0100401 """Construct a storage format test case for the given key.
402
403 If ``forward`` is true, generate a forward compatibility test case:
404 create a key and validate that it has the expected representation.
405 Otherwise generate a backward compatibility test case: inject the
406 key representation into storage and validate that it can be read
407 correctly.
408 """
409 verb = 'save' if self.forward else 'read'
410 tc = test_case.TestCase()
411 tc.set_description('PSA storage {}: {}'.format(verb, key.description))
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100412 dependencies = automatic_dependencies(
413 key.lifetime.string, key.type.string,
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200414 key.expected_usage, key.alg.string, key.alg2.string,
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100415 )
416 dependencies = finish_family_dependencies(dependencies, key.bits)
417 tc.set_dependencies(dependencies)
Gilles Peskine897dff92021-03-10 15:03:44 +0100418 tc.set_function('key_storage_' + verb)
419 if self.forward:
420 extra_arguments = []
421 else:
Gilles Peskine643eb832021-04-21 20:11:33 +0200422 flags = []
Gilles Peskine897dff92021-03-10 15:03:44 +0100423 # Some test keys have the RAW_DATA type and attributes that don't
424 # necessarily make sense. We do this to validate numerical
425 # encodings of the attributes.
426 # Raw data keys have no useful exercise anyway so there is no
427 # loss of test coverage.
Gilles Peskine643eb832021-04-21 20:11:33 +0200428 if key.type.string != 'PSA_KEY_TYPE_RAW_DATA':
429 flags.append('TEST_FLAG_EXERCISE')
430 if 'READ_ONLY' in key.lifetime.string:
431 flags.append('TEST_FLAG_READ_ONLY')
432 extra_arguments = [' | '.join(flags) if flags else '0']
Gilles Peskine897dff92021-03-10 15:03:44 +0100433 tc.set_arguments([key.lifetime.string,
434 key.type.string, str(key.bits),
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200435 key.expected_usage, key.alg.string, key.alg2.string,
Gilles Peskine897dff92021-03-10 15:03:44 +0100436 '"' + key.material.hex() + '"',
437 '"' + key.hex() + '"',
438 *extra_arguments])
439 return tc
440
Gilles Peskineefb584d2021-04-21 22:05:34 +0200441 def key_for_lifetime(
442 self,
443 lifetime: str,
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200444 ) -> StorageTestData:
Gilles Peskineefb584d2021-04-21 22:05:34 +0200445 """Construct a test key for the given lifetime."""
446 short = lifetime
447 short = re.sub(r'PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION',
448 r'', short)
449 short = re.sub(r'PSA_KEY_[A-Z]+_', r'', short)
450 description = 'lifetime: ' + short
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200451 key = StorageTestData(version=self.version,
452 id=1, lifetime=lifetime,
453 type='PSA_KEY_TYPE_RAW_DATA', bits=8,
454 usage='PSA_KEY_USAGE_EXPORT', alg=0, alg2=0,
455 material=b'L',
456 description=description)
457 return key
Gilles Peskineefb584d2021-04-21 22:05:34 +0200458
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200459 def all_keys_for_lifetimes(self) -> Iterator[StorageTestData]:
Gilles Peskineefb584d2021-04-21 22:05:34 +0200460 """Generate test keys covering lifetimes."""
461 lifetimes = sorted(self.constructors.lifetimes)
462 expressions = self.constructors.generate_expressions(lifetimes)
463 for lifetime in expressions:
464 # Don't attempt to create or load a volatile key in storage
465 if 'VOLATILE' in lifetime:
466 continue
467 # Don't attempt to create a read-only key in storage,
468 # but do attempt to load one.
469 if 'READ_ONLY' in lifetime and self.forward:
470 continue
gabor-mezei-arm5ea30372021-06-28 19:26:55 +0200471 yield self.key_for_lifetime(lifetime)
Gilles Peskineefb584d2021-04-21 22:05:34 +0200472
gabor-mezei-arm912eca32021-06-29 15:39:56 +0200473 def keys_for_usage_flags(
Gilles Peskine897dff92021-03-10 15:03:44 +0100474 self,
475 usage_flags: List[str],
gabor-mezei-armd71659f2021-06-24 09:42:02 +0200476 short: Optional[str] = None,
gabor-mezei-arm5ea30372021-06-28 19:26:55 +0200477 test_implicit_usage: Optional[bool] = False
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200478 ) -> Iterator[StorageTestData]:
Gilles Peskine897dff92021-03-10 15:03:44 +0100479 """Construct a test key for the given key usage."""
480 usage = ' | '.join(usage_flags) if usage_flags else '0'
481 if short is None:
482 short = re.sub(r'\bPSA_KEY_USAGE_', r'', usage)
gabor-mezei-arm5ea30372021-06-28 19:26:55 +0200483 extra_desc = ' with implication' if test_implicit_usage else ''
gabor-mezei-armd71659f2021-06-24 09:42:02 +0200484 description = 'usage' + extra_desc + ': ' + short
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200485 key1 = StorageTestData(version=self.version,
486 id=1, lifetime=0x00000001,
487 type='PSA_KEY_TYPE_RAW_DATA', bits=8,
488 expected_usage=usage,
489 usage=usage, alg=0, alg2=0,
490 material=b'K',
491 description=description)
492 yield key1
493
gabor-mezei-arm5ea30372021-06-28 19:26:55 +0200494 if test_implicit_usage:
495 description = 'usage without implication' + ': ' + short
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200496 key2 = StorageTestData(version=self.version,
497 id=1, lifetime=0x00000001,
498 type='PSA_KEY_TYPE_RAW_DATA', bits=8,
499 without_implicit_usage=True,
500 usage=usage, alg=0, alg2=0,
501 material=b'K',
502 description=description)
503 yield key2
Gilles Peskine897dff92021-03-10 15:03:44 +0100504
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200505 def generate_keys_for_usage_flags(self, **kwargs) -> Iterator[StorageTestData]:
Gilles Peskine897dff92021-03-10 15:03:44 +0100506 """Generate test keys covering usage flags."""
507 known_flags = sorted(self.constructors.key_usage_flags)
gabor-mezei-arm912eca32021-06-29 15:39:56 +0200508 yield from self.keys_for_usage_flags(['0'], **kwargs)
gabor-mezei-arm5ea30372021-06-28 19:26:55 +0200509 for usage_flag in known_flags:
gabor-mezei-arm912eca32021-06-29 15:39:56 +0200510 yield from self.keys_for_usage_flags([usage_flag], **kwargs)
gabor-mezei-arm5ea30372021-06-28 19:26:55 +0200511 for flag1, flag2 in zip(known_flags,
512 known_flags[1:] + [known_flags[0]]):
gabor-mezei-arm912eca32021-06-29 15:39:56 +0200513 yield from self.keys_for_usage_flags([flag1, flag2], **kwargs)
gabor-mezei-armbce85272021-06-24 14:38:51 +0200514
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200515 def generate_key_for_all_usage_flags(self) -> Iterator[StorageTestData]:
gabor-mezei-armbce85272021-06-24 14:38:51 +0200516 known_flags = sorted(self.constructors.key_usage_flags)
gabor-mezei-arm912eca32021-06-29 15:39:56 +0200517 yield from self.keys_for_usage_flags(known_flags, short='all known')
gabor-mezei-armbce85272021-06-24 14:38:51 +0200518
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200519 def all_keys_for_usage_flags(self) -> Iterator[StorageTestData]:
gabor-mezei-arm5ea30372021-06-28 19:26:55 +0200520 yield from self.generate_keys_for_usage_flags()
521 yield from self.generate_key_for_all_usage_flags()
Gilles Peskine897dff92021-03-10 15:03:44 +0100522
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100523 def keys_for_type(
524 self,
525 key_type: str,
526 params: Optional[Iterable[str]] = None
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200527 ) -> Iterator[StorageTestData]:
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100528 """Generate test keys for the given key type.
529
530 For key types that depend on a parameter (e.g. elliptic curve family),
531 `param` is the parameter to pass to the constructor. Only a single
532 parameter is supported.
533 """
534 kt = crypto_knowledge.KeyType(key_type, params)
535 for bits in kt.sizes_to_test():
536 usage_flags = 'PSA_KEY_USAGE_EXPORT'
537 alg = 0
538 alg2 = 0
539 key_material = kt.key_material(bits)
540 short_expression = re.sub(r'\bPSA_(?:KEY_TYPE|ECC_FAMILY)_',
541 r'',
542 kt.expression)
543 description = 'type: {} {}-bit'.format(short_expression, bits)
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200544 key = StorageTestData(version=self.version,
545 id=1, lifetime=0x00000001,
546 type=kt.expression, bits=bits,
547 usage=usage_flags, alg=alg, alg2=alg2,
548 material=key_material,
549 description=description)
550 yield key
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100551
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200552 def all_keys_for_types(self) -> Iterator[StorageTestData]:
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100553 """Generate test keys covering key types and their representations."""
Gilles Peskineb93f8542021-04-19 13:50:25 +0200554 key_types = sorted(self.constructors.key_types)
gabor-mezei-arm5ea30372021-06-28 19:26:55 +0200555 for key_type in self.constructors.generate_expressions(key_types):
556 yield from self.keys_for_type(key_type)
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100557
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200558 def keys_for_algorithm(self, alg: str) -> Iterator[StorageTestData]:
Gilles Peskined86bc522021-03-10 15:08:57 +0100559 """Generate test keys for the specified algorithm."""
560 # For now, we don't have information on the compatibility of key
561 # types and algorithms. So we just test the encoding of algorithms,
562 # and not that operations can be performed with them.
Gilles Peskineff9629f2021-04-21 10:18:19 +0200563 descr = re.sub(r'PSA_ALG_', r'', alg)
564 descr = re.sub(r',', r', ', re.sub(r' +', r'', descr))
Gilles Peskined86bc522021-03-10 15:08:57 +0100565 usage = 'PSA_KEY_USAGE_EXPORT'
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200566 key1 = StorageTestData(version=self.version,
567 id=1, lifetime=0x00000001,
568 type='PSA_KEY_TYPE_RAW_DATA', bits=8,
569 usage=usage, alg=alg, alg2=0,
570 material=b'K',
571 description='alg: ' + descr)
572 yield key1
573 key2 = StorageTestData(version=self.version,
574 id=1, lifetime=0x00000001,
575 type='PSA_KEY_TYPE_RAW_DATA', bits=8,
576 usage=usage, alg=0, alg2=alg,
577 material=b'L',
578 description='alg2: ' + descr)
579 yield key2
Gilles Peskined86bc522021-03-10 15:08:57 +0100580
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200581 def all_keys_for_algorithms(self) -> Iterator[StorageTestData]:
Gilles Peskined86bc522021-03-10 15:08:57 +0100582 """Generate test keys covering algorithm encodings."""
Gilles Peskineb93f8542021-04-19 13:50:25 +0200583 algorithms = sorted(self.constructors.algorithms)
gabor-mezei-arm5ea30372021-06-28 19:26:55 +0200584 for alg in self.constructors.generate_expressions(algorithms):
585 yield from self.keys_for_algorithm(alg)
Gilles Peskined86bc522021-03-10 15:08:57 +0100586
gabor-mezei-armea840de2021-06-29 15:42:57 +0200587 def generate_all_keys(self) -> Iterator[StorageTestData]:
gabor-mezei-arm8b0c91c2021-06-24 09:49:50 +0200588 """Generate all keys for the test cases."""
gabor-mezei-armea840de2021-06-29 15:42:57 +0200589 yield from self.all_keys_for_lifetimes()
590 yield from self.all_keys_for_usage_flags()
591 yield from self.all_keys_for_types()
592 yield from self.all_keys_for_algorithms()
gabor-mezei-arm8b0c91c2021-06-24 09:49:50 +0200593
gabor-mezei-arm5ea30372021-06-28 19:26:55 +0200594 def all_test_cases(self) -> Iterator[test_case.TestCase]:
Gilles Peskine897dff92021-03-10 15:03:44 +0100595 """Generate all storage format test cases."""
Gilles Peskineae9f14b2021-04-12 14:43:05 +0200596 # First build a list of all keys, then construct all the corresponding
597 # test cases. This allows all required information to be obtained in
598 # one go, which is a significant performance gain as the information
599 # includes numerical values obtained by compiling a C program.
Gilles Peskine3008c582021-07-06 21:05:52 +0200600 all_keys = list(self.generate_all_keys())
601 for key in all_keys:
gabor-mezei-arm5ea30372021-06-28 19:26:55 +0200602 if key.location_value() != 0:
603 # Skip keys with a non-default location, because they
604 # require a driver and we currently have no mechanism to
605 # determine whether a driver is available.
606 continue
607 yield self.make_test_case(key)
Gilles Peskine897dff92021-03-10 15:03:44 +0100608
gabor-mezei-arm4d9fb732021-06-24 09:53:26 +0200609class StorageFormatForward(StorageFormat):
610 """Storage format stability test cases for forward compatibility."""
611
612 def __init__(self, info: Information, version: int) -> None:
613 super().__init__(info, version, True)
614
615class StorageFormatV0(StorageFormat):
616 """Storage format stability test cases for version 0 compatibility."""
617
618 def __init__(self, info: Information) -> None:
619 super().__init__(info, 0, False)
Gilles Peskine897dff92021-03-10 15:03:44 +0100620
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200621 def all_keys_for_usage_flags(self) -> Iterator[StorageTestData]:
gabor-mezei-arm15c1f032021-06-24 10:04:38 +0200622 """Generate test keys covering usage flags."""
gabor-mezei-arm5ea30372021-06-28 19:26:55 +0200623 yield from self.generate_keys_for_usage_flags(test_implicit_usage=True)
624 yield from self.generate_key_for_all_usage_flags()
gabor-mezei-arm15c1f032021-06-24 10:04:38 +0200625
gabor-mezei-armacfcc182021-06-28 17:40:32 +0200626 def keys_for_implicit_usage(
gabor-mezei-arm044fefc2021-06-24 10:16:44 +0200627 self,
gabor-mezei-arme84d3212021-06-28 16:54:11 +0200628 implyer_usage: str,
gabor-mezei-arm044fefc2021-06-24 10:16:44 +0200629 alg: str,
gabor-mezei-arm805c7352021-06-28 20:02:11 +0200630 key_type: crypto_knowledge.KeyType
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200631 ) -> StorageTestData:
gabor-mezei-armb92d61b2021-06-24 14:38:25 +0200632 # pylint: disable=too-many-locals
gabor-mezei-arm927742e2021-06-28 16:27:29 +0200633 """Generate test keys for the specified implicit usage flag,
gabor-mezei-arm044fefc2021-06-24 10:16:44 +0200634 algorithm and key type combination.
635 """
gabor-mezei-arm805c7352021-06-28 20:02:11 +0200636 bits = key_type.sizes_to_test()[0]
gabor-mezei-arme84d3212021-06-28 16:54:11 +0200637 implicit_usage = StorageKey.IMPLICIT_USAGE_FLAGS[implyer_usage]
gabor-mezei-arm47812632021-06-28 16:35:48 +0200638 usage_flags = 'PSA_KEY_USAGE_EXPORT'
gabor-mezei-arme84d3212021-06-28 16:54:11 +0200639 material_usage_flags = usage_flags + ' | ' + implyer_usage
640 expected_usage_flags = material_usage_flags + ' | ' + implicit_usage
gabor-mezei-arm47812632021-06-28 16:35:48 +0200641 alg2 = 0
gabor-mezei-arm805c7352021-06-28 20:02:11 +0200642 key_material = key_type.key_material(bits)
gabor-mezei-arme84d3212021-06-28 16:54:11 +0200643 usage_expression = re.sub(r'PSA_KEY_USAGE_', r'', implyer_usage)
gabor-mezei-arm47812632021-06-28 16:35:48 +0200644 alg_expression = re.sub(r'PSA_ALG_', r'', alg)
645 alg_expression = re.sub(r',', r', ', re.sub(r' +', r'', alg_expression))
646 key_type_expression = re.sub(r'\bPSA_(?:KEY_TYPE|ECC_FAMILY)_',
647 r'',
gabor-mezei-arm805c7352021-06-28 20:02:11 +0200648 key_type.expression)
gabor-mezei-armacfcc182021-06-28 17:40:32 +0200649 description = 'implied by {}: {} {} {}-bit'.format(
gabor-mezei-arm47812632021-06-28 16:35:48 +0200650 usage_expression, alg_expression, key_type_expression, bits)
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200651 key = StorageTestData(version=self.version,
652 id=1, lifetime=0x00000001,
653 type=key_type.expression, bits=bits,
654 usage=material_usage_flags,
655 expected_usage=expected_usage_flags,
656 without_implicit_usage=True,
657 alg=alg, alg2=alg2,
658 material=key_material,
659 description=description)
660 return key
gabor-mezei-arm044fefc2021-06-24 10:16:44 +0200661
662 def gather_key_types_for_sign_alg(self) -> Dict[str, List[str]]:
gabor-mezei-armb92d61b2021-06-24 14:38:25 +0200663 # pylint: disable=too-many-locals
gabor-mezei-arm044fefc2021-06-24 10:16:44 +0200664 """Match possible key types for sign algorithms."""
665 # To create a valid combinaton both the algorithms and key types
666 # must be filtered. Pair them with keywords created from its names.
667 incompatible_alg_keyword = frozenset(['RAW', 'ANY', 'PURE'])
668 incompatible_key_type_keywords = frozenset(['MONTGOMERY'])
669 keyword_translation = {
670 'ECDSA': 'ECC',
671 'ED[0-9]*.*' : 'EDWARDS'
672 }
673 exclusive_keywords = {
674 'EDWARDS': 'ECC'
675 }
gabor-mezei-armb92d61b2021-06-24 14:38:25 +0200676 key_types = set(self.constructors.generate_expressions(self.constructors.key_types))
677 algorithms = set(self.constructors.generate_expressions(self.constructors.sign_algorithms))
gabor-mezei-arm044fefc2021-06-24 10:16:44 +0200678 alg_with_keys = {} #type: Dict[str, List[str]]
679 translation_table = str.maketrans('(', '_', ')')
680 for alg in algorithms:
681 # Generate keywords from the name of the algorithm
682 alg_keywords = set(alg.partition('(')[0].split(sep='_')[2:])
683 # Translate keywords for better matching with the key types
684 for keyword in alg_keywords.copy():
685 for pattern, replace in keyword_translation.items():
686 if re.match(pattern, keyword):
687 alg_keywords.remove(keyword)
688 alg_keywords.add(replace)
689 # Filter out incompatible algortihms
690 if not alg_keywords.isdisjoint(incompatible_alg_keyword):
691 continue
692
693 for key_type in key_types:
694 # Generate keywords from the of the key type
695 key_type_keywords = set(key_type.translate(translation_table).split(sep='_')[3:])
696
697 # Remove ambigious keywords
698 for keyword1, keyword2 in exclusive_keywords.items():
699 if keyword1 in key_type_keywords:
700 key_type_keywords.remove(keyword2)
701
702 if key_type_keywords.isdisjoint(incompatible_key_type_keywords) and\
703 not key_type_keywords.isdisjoint(alg_keywords):
704 if alg in alg_with_keys:
705 alg_with_keys[alg].append(key_type)
706 else:
707 alg_with_keys[alg] = [key_type]
708 return alg_with_keys
709
gabor-mezei-arme4b74992021-06-29 15:29:24 +0200710 def all_keys_for_implicit_usage(self) -> Iterator[StorageTestData]:
gabor-mezei-arm044fefc2021-06-24 10:16:44 +0200711 """Generate test keys for usage flag extensions."""
712 # Generate a key type and algorithm pair for each extendable usage
713 # flag to generate a valid key for exercising. The key is generated
714 # without usage extension to check the extension compatiblity.
gabor-mezei-arm044fefc2021-06-24 10:16:44 +0200715 alg_with_keys = self.gather_key_types_for_sign_alg()
gabor-mezei-arm7d2ec9a2021-06-24 16:35:01 +0200716
gabor-mezei-arm5ea30372021-06-28 19:26:55 +0200717 for usage in sorted(StorageKey.IMPLICIT_USAGE_FLAGS, key=str):
718 for alg in sorted(alg_with_keys):
719 for key_type in sorted(alg_with_keys[alg]):
720 # The key types must be filtered to fit the specific usage flag.
gabor-mezei-arm805c7352021-06-28 20:02:11 +0200721 kt = crypto_knowledge.KeyType(key_type)
722 if kt.is_valid_for_signature(usage):
723 yield self.keys_for_implicit_usage(usage, alg, kt)
gabor-mezei-arm044fefc2021-06-24 10:16:44 +0200724
gabor-mezei-armea840de2021-06-29 15:42:57 +0200725 def generate_all_keys(self) -> Iterator[StorageTestData]:
726 yield from super().generate_all_keys()
727 yield from self.all_keys_for_implicit_usage()
gabor-mezei-arm15c1f032021-06-24 10:04:38 +0200728
Gilles Peskineb94ea512021-03-10 02:12:08 +0100729class TestGenerator:
730 """Generate test data."""
731
732 def __init__(self, options) -> None:
733 self.test_suite_directory = self.get_option(options, 'directory',
734 'tests/suites')
735 self.info = Information()
736
737 @staticmethod
738 def get_option(options, name: str, default: T) -> T:
739 value = getattr(options, name, None)
740 return default if value is None else value
741
Gilles Peskine0298bda2021-03-10 02:34:37 +0100742 def filename_for(self, basename: str) -> str:
743 """The location of the data file with the specified base name."""
744 return os.path.join(self.test_suite_directory, basename + '.data')
745
Gilles Peskineb94ea512021-03-10 02:12:08 +0100746 def write_test_data_file(self, basename: str,
747 test_cases: Iterable[test_case.TestCase]) -> None:
748 """Write the test cases to a .data file.
749
750 The output file is ``basename + '.data'`` in the test suite directory.
751 """
Gilles Peskine0298bda2021-03-10 02:34:37 +0100752 filename = self.filename_for(basename)
Gilles Peskineb94ea512021-03-10 02:12:08 +0100753 test_case.write_data_file(filename, test_cases)
754
Gilles Peskine0298bda2021-03-10 02:34:37 +0100755 TARGETS = {
Przemyslaw Stekiel997caf82021-10-15 15:21:51 +0200756 'test_suite_psa_crypto_generate_key.generated':
757 lambda info: KeyGenerate(info).test_cases_for_key_generation(),
Gilles Peskine0298bda2021-03-10 02:34:37 +0100758 'test_suite_psa_crypto_not_supported.generated':
Gilles Peskine3d778392021-02-17 15:11:05 +0100759 lambda info: NotSupported(info).test_cases_for_not_supported(),
Gilles Peskine897dff92021-03-10 15:03:44 +0100760 'test_suite_psa_crypto_storage_format.current':
gabor-mezei-arm4d9fb732021-06-24 09:53:26 +0200761 lambda info: StorageFormatForward(info, 0).all_test_cases(),
Gilles Peskine897dff92021-03-10 15:03:44 +0100762 'test_suite_psa_crypto_storage_format.v0':
gabor-mezei-arm4d9fb732021-06-24 09:53:26 +0200763 lambda info: StorageFormatV0(info).all_test_cases(),
Gilles Peskine0298bda2021-03-10 02:34:37 +0100764 } #type: Dict[str, Callable[[Information], Iterable[test_case.TestCase]]]
765
766 def generate_target(self, name: str) -> None:
767 test_cases = self.TARGETS[name](self.info)
768 self.write_test_data_file(name, test_cases)
Gilles Peskine14e428f2021-01-26 22:19:21 +0100769
Gilles Peskine09940492021-01-26 22:16:30 +0100770def main(args):
771 """Command line entry point."""
772 parser = argparse.ArgumentParser(description=__doc__)
Gilles Peskine0298bda2021-03-10 02:34:37 +0100773 parser.add_argument('--list', action='store_true',
774 help='List available targets and exit')
775 parser.add_argument('targets', nargs='*', metavar='TARGET',
776 help='Target file to generate (default: all; "-": none)')
Gilles Peskine09940492021-01-26 22:16:30 +0100777 options = parser.parse_args(args)
778 generator = TestGenerator(options)
Gilles Peskine0298bda2021-03-10 02:34:37 +0100779 if options.list:
780 for name in sorted(generator.TARGETS):
781 print(generator.filename_for(name))
782 return
783 if options.targets:
784 # Allow "-" as a special case so you can run
785 # ``generate_psa_tests.py - $targets`` and it works uniformly whether
786 # ``$targets`` is empty or not.
787 options.targets = [os.path.basename(re.sub(r'\.data\Z', r'', target))
788 for target in options.targets
789 if target != '-']
790 else:
791 options.targets = sorted(generator.TARGETS)
792 for target in options.targets:
793 generator.generate_target(target)
Gilles Peskine09940492021-01-26 22:16:30 +0100794
795if __name__ == '__main__':
796 main(sys.argv[1:])