blob: 589820265c6616dd0718186b8fc6d4d658923a8f [file] [log] [blame]
Gilles Peskine09940492021-01-26 22:16:30 +01001#!/usr/bin/env python3
2"""Generate test data for PSA cryptographic mechanisms.
Gilles Peskine0298bda2021-03-10 02:34:37 +01003
4With no arguments, generate all test data. With non-option arguments,
5generate only the specified files.
Gilles Peskine09940492021-01-26 22:16:30 +01006"""
7
8# Copyright The Mbed TLS Contributors
9# SPDX-License-Identifier: Apache-2.0
10#
11# Licensed under the Apache License, Version 2.0 (the "License"); you may
12# not use this file except in compliance with the License.
13# You may obtain a copy of the License at
14#
15# http://www.apache.org/licenses/LICENSE-2.0
16#
17# Unless required by applicable law or agreed to in writing, software
18# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
19# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20# See the License for the specific language governing permissions and
21# limitations under the License.
22
23import argparse
Gilles Peskine14e428f2021-01-26 22:19:21 +010024import os
Bence Szépkúti9e84ec72021-05-07 11:49:17 +020025import posixpath
Gilles Peskine14e428f2021-01-26 22:19:21 +010026import re
Gilles Peskine09940492021-01-26 22:16:30 +010027import sys
Gilles Peskine3d778392021-02-17 15:11:05 +010028from typing import Callable, Dict, FrozenSet, Iterable, Iterator, List, Optional, TypeVar
Gilles Peskine09940492021-01-26 22:16:30 +010029
30import scripts_path # pylint: disable=unused-import
Gilles Peskinec86f20a2021-04-22 00:20:47 +020031from mbedtls_dev import build_tree
Gilles Peskine14e428f2021-01-26 22:19:21 +010032from mbedtls_dev import crypto_knowledge
Gilles Peskine09940492021-01-26 22:16:30 +010033from mbedtls_dev import macro_collector
Gilles Peskine897dff92021-03-10 15:03:44 +010034from mbedtls_dev import psa_storage
Gilles Peskine14e428f2021-01-26 22:19:21 +010035from mbedtls_dev import test_case
Gilles Peskine09940492021-01-26 22:16:30 +010036
37T = TypeVar('T') #pylint: disable=invalid-name
38
Gilles Peskine14e428f2021-01-26 22:19:21 +010039
Gilles Peskine7f756872021-02-16 12:13:12 +010040def psa_want_symbol(name: str) -> str:
Gilles Peskineaf172842021-01-27 18:24:48 +010041 """Return the PSA_WANT_xxx symbol associated with a PSA crypto feature."""
42 if name.startswith('PSA_'):
43 return name[:4] + 'WANT_' + name[4:]
44 else:
45 raise ValueError('Unable to determine the PSA_WANT_ symbol for ' + name)
46
Gilles Peskine7f756872021-02-16 12:13:12 +010047def finish_family_dependency(dep: str, bits: int) -> str:
48 """Finish dep if it's a family dependency symbol prefix.
49
50 A family dependency symbol prefix is a PSA_WANT_ symbol that needs to be
51 qualified by the key size. If dep is such a symbol, finish it by adjusting
52 the prefix and appending the key size. Other symbols are left unchanged.
53 """
54 return re.sub(r'_FAMILY_(.*)', r'_\1_' + str(bits), dep)
55
56def finish_family_dependencies(dependencies: List[str], bits: int) -> List[str]:
57 """Finish any family dependency symbol prefixes.
58
59 Apply `finish_family_dependency` to each element of `dependencies`.
60 """
61 return [finish_family_dependency(dep, bits) for dep in dependencies]
Gilles Peskineaf172842021-01-27 18:24:48 +010062
Gilles Peskinec5d086f2021-04-20 23:23:45 +020063SYMBOLS_WITHOUT_DEPENDENCY = frozenset([
64 'PSA_ALG_AEAD_WITH_AT_LEAST_THIS_LENGTH_TAG', # modifier, only in policies
65 'PSA_ALG_AEAD_WITH_SHORTENED_TAG', # modifier
66 'PSA_ALG_ANY_HASH', # only in policies
67 'PSA_ALG_AT_LEAST_THIS_LENGTH_MAC', # modifier, only in policies
68 'PSA_ALG_KEY_AGREEMENT', # chaining
69 'PSA_ALG_TRUNCATED_MAC', # modifier
70])
Gilles Peskinef8223ab2021-03-10 15:07:16 +010071def automatic_dependencies(*expressions: str) -> List[str]:
72 """Infer dependencies of a test case by looking for PSA_xxx symbols.
73
74 The arguments are strings which should be C expressions. Do not use
75 string literals or comments as this function is not smart enough to
76 skip them.
77 """
78 used = set()
79 for expr in expressions:
80 used.update(re.findall(r'PSA_(?:ALG|ECC_FAMILY|KEY_TYPE)_\w+', expr))
Gilles Peskinec5d086f2021-04-20 23:23:45 +020081 used.difference_update(SYMBOLS_WITHOUT_DEPENDENCY)
Gilles Peskinef8223ab2021-03-10 15:07:16 +010082 return sorted(psa_want_symbol(name) for name in used)
83
Gilles Peskined169d602021-02-16 14:16:25 +010084# A temporary hack: at the time of writing, not all dependency symbols
85# are implemented yet. Skip test cases for which the dependency symbols are
86# not available. Once all dependency symbols are available, this hack must
87# be removed so that a bug in the dependency symbols proprely leads to a test
88# failure.
89def read_implemented_dependencies(filename: str) -> FrozenSet[str]:
90 return frozenset(symbol
91 for line in open(filename)
92 for symbol in re.findall(r'\bPSA_WANT_\w+\b', line))
Gilles Peskinec86f20a2021-04-22 00:20:47 +020093_implemented_dependencies = None #type: Optional[FrozenSet[str]] #pylint: disable=invalid-name
Gilles Peskined169d602021-02-16 14:16:25 +010094def hack_dependencies_not_implemented(dependencies: List[str]) -> None:
Gilles Peskinec86f20a2021-04-22 00:20:47 +020095 global _implemented_dependencies #pylint: disable=global-statement,invalid-name
96 if _implemented_dependencies is None:
97 _implemented_dependencies = \
98 read_implemented_dependencies('include/psa/crypto_config.h')
99 if not all(dep.lstrip('!') in _implemented_dependencies
Gilles Peskined169d602021-02-16 14:16:25 +0100100 for dep in dependencies):
101 dependencies.append('DEPENDENCY_NOT_IMPLEMENTED_YET')
102
Gilles Peskine14e428f2021-01-26 22:19:21 +0100103
Gilles Peskineb94ea512021-03-10 02:12:08 +0100104class Information:
105 """Gather information about PSA constructors."""
Gilles Peskine09940492021-01-26 22:16:30 +0100106
Gilles Peskineb94ea512021-03-10 02:12:08 +0100107 def __init__(self) -> None:
Gilles Peskine09940492021-01-26 22:16:30 +0100108 self.constructors = self.read_psa_interface()
109
110 @staticmethod
Gilles Peskine09940492021-01-26 22:16:30 +0100111 def remove_unwanted_macros(
Gilles Peskine537d5fa2021-04-19 13:50:25 +0200112 constructors: macro_collector.PSAMacroEnumerator
Gilles Peskine09940492021-01-26 22:16:30 +0100113 ) -> None:
Gilles Peskine537d5fa2021-04-19 13:50:25 +0200114 # Mbed TLS doesn't support finite-field DH yet and will not support
115 # finite-field DSA. Don't attempt to generate any related test case.
116 constructors.key_types.discard('PSA_KEY_TYPE_DH_KEY_PAIR')
117 constructors.key_types.discard('PSA_KEY_TYPE_DH_PUBLIC_KEY')
Gilles Peskine09940492021-01-26 22:16:30 +0100118 constructors.key_types.discard('PSA_KEY_TYPE_DSA_KEY_PAIR')
119 constructors.key_types.discard('PSA_KEY_TYPE_DSA_PUBLIC_KEY')
Gilles Peskine09940492021-01-26 22:16:30 +0100120
Gilles Peskine537d5fa2021-04-19 13:50:25 +0200121 def read_psa_interface(self) -> macro_collector.PSAMacroEnumerator:
Gilles Peskine09940492021-01-26 22:16:30 +0100122 """Return the list of known key types, algorithms, etc."""
Gilles Peskine3d404b82021-03-30 21:46:35 +0200123 constructors = macro_collector.InputsForTest()
Gilles Peskine09940492021-01-26 22:16:30 +0100124 header_file_names = ['include/psa/crypto_values.h',
125 'include/psa/crypto_extra.h']
Gilles Peskine537d5fa2021-04-19 13:50:25 +0200126 test_suites = ['tests/suites/test_suite_psa_crypto_metadata.data']
Gilles Peskine09940492021-01-26 22:16:30 +0100127 for header_file_name in header_file_names:
Gilles Peskine537d5fa2021-04-19 13:50:25 +0200128 constructors.parse_header(header_file_name)
129 for test_cases in test_suites:
130 constructors.parse_test_cases(test_cases)
Gilles Peskine09940492021-01-26 22:16:30 +0100131 self.remove_unwanted_macros(constructors)
Gilles Peskine3d404b82021-03-30 21:46:35 +0200132 constructors.gather_arguments()
Gilles Peskine09940492021-01-26 22:16:30 +0100133 return constructors
134
Gilles Peskine14e428f2021-01-26 22:19:21 +0100135
Przemyslaw Stekielb576c7b2021-10-11 10:15:25 +0200136def test_case_for_key_type_not_supported(
Gilles Peskineb94ea512021-03-10 02:12:08 +0100137 verb: str, key_type: str, bits: int,
138 dependencies: List[str],
139 *args: str,
140 param_descr: str = ''
141) -> test_case.TestCase:
142 """Return one test case exercising a key creation method
143 for an unsupported key type or size.
144 """
145 hack_dependencies_not_implemented(dependencies)
146 tc = test_case.TestCase()
147 short_key_type = re.sub(r'PSA_(KEY_TYPE|ECC_FAMILY)_', r'', key_type)
148 adverb = 'not' if dependencies else 'never'
149 if param_descr:
150 adverb = param_descr + ' ' + adverb
Przemyslaw Stekielb576c7b2021-10-11 10:15:25 +0200151 tc.set_description('PSA {} {} {}-bit {} supported'
152 .format(verb, short_key_type, bits, adverb))
153 tc.set_dependencies(dependencies)
154 tc.set_function(verb + '_not_supported')
155 tc.set_arguments([key_type] + list(args))
156 return tc
157
Gilles Peskineb94ea512021-03-10 02:12:08 +0100158class NotSupported:
Przemyslaw Stekiel8d468e42021-10-18 14:58:20 +0200159 """Generate test cases for when something is not supported."""
Gilles Peskineb94ea512021-03-10 02:12:08 +0100160
161 def __init__(self, info: Information) -> None:
162 self.constructors = info.constructors
Gilles Peskine14e428f2021-01-26 22:19:21 +0100163
Gilles Peskine60b29fe2021-02-16 14:06:50 +0100164 ALWAYS_SUPPORTED = frozenset([
165 'PSA_KEY_TYPE_DERIVE',
166 'PSA_KEY_TYPE_RAW_DATA',
167 ])
Gilles Peskine14e428f2021-01-26 22:19:21 +0100168 def test_cases_for_key_type_not_supported(
Gilles Peskine60b29fe2021-02-16 14:06:50 +0100169 self,
Gilles Peskineaf172842021-01-27 18:24:48 +0100170 kt: crypto_knowledge.KeyType,
171 param: Optional[int] = None,
172 param_descr: str = '',
Gilles Peskine3d778392021-02-17 15:11:05 +0100173 ) -> Iterator[test_case.TestCase]:
Przemyslaw Stekiel8d468e42021-10-18 14:58:20 +0200174 """Return test cases exercising key creation when the given type is unsupported.
Gilles Peskineaf172842021-01-27 18:24:48 +0100175
176 If param is present and not None, emit test cases conditioned on this
177 parameter not being supported. If it is absent or None, emit test cases
Przemyslaw Stekiel8d468e42021-10-18 14:58:20 +0200178 conditioned on the base type not being supported.
Gilles Peskineaf172842021-01-27 18:24:48 +0100179 """
Gilles Peskine60b29fe2021-02-16 14:06:50 +0100180 if kt.name in self.ALWAYS_SUPPORTED:
181 # Don't generate test cases for key types that are always supported.
182 # They would be skipped in all configurations, which is noise.
Gilles Peskine3d778392021-02-17 15:11:05 +0100183 return
Gilles Peskineaf172842021-01-27 18:24:48 +0100184 import_dependencies = [('!' if param is None else '') +
185 psa_want_symbol(kt.name)]
186 if kt.params is not None:
187 import_dependencies += [('!' if param == i else '') +
188 psa_want_symbol(sym)
189 for i, sym in enumerate(kt.params)]
Gilles Peskine14e428f2021-01-26 22:19:21 +0100190 if kt.name.endswith('_PUBLIC_KEY'):
191 generate_dependencies = []
192 else:
193 generate_dependencies = import_dependencies
Gilles Peskine14e428f2021-01-26 22:19:21 +0100194 for bits in kt.sizes_to_test():
Przemyslaw Stekielb576c7b2021-10-11 10:15:25 +0200195 yield test_case_for_key_type_not_supported(
Gilles Peskine7f756872021-02-16 12:13:12 +0100196 'import', kt.expression, bits,
197 finish_family_dependencies(import_dependencies, bits),
Gilles Peskineaf172842021-01-27 18:24:48 +0100198 test_case.hex_string(kt.key_material(bits)),
199 param_descr=param_descr,
Gilles Peskine3d778392021-02-17 15:11:05 +0100200 )
Gilles Peskineaf172842021-01-27 18:24:48 +0100201 if not generate_dependencies and param is not None:
202 # If generation is impossible for this key type, rather than
203 # supported or not depending on implementation capabilities,
204 # only generate the test case once.
205 continue
Przemyslaw Stekiel8d468e42021-10-18 14:58:20 +0200206 # Public key cannot be generated
207 if not kt.name.endswith('_PUBLIC_KEY'):
Przemyslaw Stekielb576c7b2021-10-11 10:15:25 +0200208 yield test_case_for_key_type_not_supported(
209 'generate', kt.expression, bits,
210 finish_family_dependencies(generate_dependencies, bits),
211 str(bits),
212 param_descr=param_descr,
213 )
Gilles Peskine14e428f2021-01-26 22:19:21 +0100214 # To be added: derive
Gilles Peskine14e428f2021-01-26 22:19:21 +0100215
Gilles Peskine537d5fa2021-04-19 13:50:25 +0200216 ECC_KEY_TYPES = ('PSA_KEY_TYPE_ECC_KEY_PAIR',
217 'PSA_KEY_TYPE_ECC_PUBLIC_KEY')
218
Gilles Peskine3d778392021-02-17 15:11:05 +0100219 def test_cases_for_not_supported(self) -> Iterator[test_case.TestCase]:
Gilles Peskine14e428f2021-01-26 22:19:21 +0100220 """Generate test cases that exercise the creation of keys of unsupported types."""
Gilles Peskine14e428f2021-01-26 22:19:21 +0100221 for key_type in sorted(self.constructors.key_types):
Gilles Peskine537d5fa2021-04-19 13:50:25 +0200222 if key_type in self.ECC_KEY_TYPES:
223 continue
Gilles Peskine14e428f2021-01-26 22:19:21 +0100224 kt = crypto_knowledge.KeyType(key_type)
Gilles Peskine3d778392021-02-17 15:11:05 +0100225 yield from self.test_cases_for_key_type_not_supported(kt)
Gilles Peskineaf172842021-01-27 18:24:48 +0100226 for curve_family in sorted(self.constructors.ecc_curves):
Gilles Peskine537d5fa2021-04-19 13:50:25 +0200227 for constr in self.ECC_KEY_TYPES:
Gilles Peskineaf172842021-01-27 18:24:48 +0100228 kt = crypto_knowledge.KeyType(constr, [curve_family])
Gilles Peskine3d778392021-02-17 15:11:05 +0100229 yield from self.test_cases_for_key_type_not_supported(
Gilles Peskineaf172842021-01-27 18:24:48 +0100230 kt, param_descr='type')
Gilles Peskine3d778392021-02-17 15:11:05 +0100231 yield from self.test_cases_for_key_type_not_supported(
Gilles Peskineaf172842021-01-27 18:24:48 +0100232 kt, 0, param_descr='curve')
Gilles Peskineb94ea512021-03-10 02:12:08 +0100233
Przemyslaw Stekiel1b0978b2021-10-15 15:21:51 +0200234def test_case_for_key_generation(
235 key_type: str, bits: int,
236 dependencies: List[str],
237 *args: str,
238 result: str = '',
Przemyslaw Stekiel8d468e42021-10-18 14:58:20 +0200239 param_descr: str = ''
Przemyslaw Stekiel1b0978b2021-10-15 15:21:51 +0200240) -> test_case.TestCase:
241 """Return one test case exercising a key generation.
242 """
243 hack_dependencies_not_implemented(dependencies)
244 tc = test_case.TestCase()
245 short_key_type = re.sub(r'PSA_(KEY_TYPE|ECC_FAMILY)_', r'', key_type)
246 tc.set_description('PSA {} {}-bit'
247 .format( short_key_type, bits))
248 tc.set_dependencies(dependencies)
249 tc.set_function('generate_key')
250 tc.set_arguments([key_type] + list(args))
251 tc.set_result(result)
252
253 return tc
254
255class KeyGenerate:
256 """Generate positive and negative (invalid argument) test cases for key generation."""
257
258 def __init__(self, info: Information) -> None:
259 self.constructors = info.constructors
260
261 def test_cases_for_key_type_key_generation(
262 self,
263 kt: crypto_knowledge.KeyType,
264 param: Optional[int] = None,
265 param_descr: str = '',
266 ) -> Iterator[test_case.TestCase]:
267 """Return test cases exercising key generation.
268
269 All key types can be generated except for public keys. For public key
270 PSA_ERROR_INVALID_ARGUMENT status is expected.
271 """
272 result = 'PSA_SUCCESS'
273
274 import_dependencies = [psa_want_symbol(kt.name)]
275 if kt.params is not None:
276 import_dependencies += [psa_want_symbol(sym)
277 for i, sym in enumerate(kt.params)]
278 if kt.name.endswith('_PUBLIC_KEY'):
279 generate_dependencies = []
280 result = 'PSA_ERROR_INVALID_ARGUMENT'
281 else:
282 generate_dependencies = import_dependencies
283 for bits in kt.sizes_to_test():
284 yield test_case_for_key_generation(
285 kt.expression, bits,
286 finish_family_dependencies(generate_dependencies, bits),
287 str(bits),
288 result,
289 param_descr=param_descr
290 )
291
292 ECC_KEY_TYPES = ('PSA_KEY_TYPE_ECC_KEY_PAIR',
293 'PSA_KEY_TYPE_ECC_PUBLIC_KEY')
294
295 def test_cases_for_key_generation(self) -> Iterator[test_case.TestCase]:
296 """Generate test cases that exercise the generation of keys."""
297 for key_type in sorted(self.constructors.key_types):
298 if key_type in self.ECC_KEY_TYPES:
299 continue
300 kt = crypto_knowledge.KeyType(key_type)
301 yield from self.test_cases_for_key_type_key_generation(kt)
302 for curve_family in sorted(self.constructors.ecc_curves):
303 for constr in self.ECC_KEY_TYPES:
304 kt = crypto_knowledge.KeyType(constr, [curve_family])
305 yield from self.test_cases_for_key_type_key_generation(
306 kt, param_descr='type')
307 yield from self.test_cases_for_key_type_key_generation(
308 kt, 0, param_descr='curve')
309
310
Gilles Peskine897dff92021-03-10 15:03:44 +0100311class StorageKey(psa_storage.Key):
312 """Representation of a key for storage format testing."""
313
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200314 IMPLICIT_USAGE_FLAGS = {
315 'PSA_KEY_USAGE_SIGN_HASH': 'PSA_KEY_USAGE_SIGN_MESSAGE',
316 'PSA_KEY_USAGE_VERIFY_HASH': 'PSA_KEY_USAGE_VERIFY_MESSAGE'
317 } #type: Dict[str, str]
318 """Mapping of usage flags to the flags that they imply."""
319
320 def __init__(
321 self,
322 usage: str,
323 without_implicit_usage: Optional[bool] = False,
324 **kwargs
325 ) -> None:
326 """Prepare to generate a key.
327
328 * `usage` : The usage flags used for the key.
329 * `without_implicit_usage`: Flag to defide to apply the usage extension
330 """
gabor-mezei-arm2c9e54a2021-06-29 17:21:21 +0200331 super().__init__(usage=usage, **kwargs)
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200332
333 if not without_implicit_usage:
334 for flag, implicit in self.IMPLICIT_USAGE_FLAGS.items():
335 if self.usage.value() & psa_storage.Expr(flag).value() and \
336 self.usage.value() & psa_storage.Expr(implicit).value() == 0:
337 self.usage = psa_storage.Expr(self.usage.string + ' | ' + implicit)
338
339class StorageTestData(StorageKey):
340 """Representation of test case data for storage format testing."""
341
gabor-mezei-arm672e3762021-06-24 10:16:44 +0200342 def __init__(
343 self,
344 description: str,
345 expected_usage: Optional[str] = None,
346 **kwargs
347 ) -> None:
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200348 """Prepare to generate test data
gabor-mezei-arm672e3762021-06-24 10:16:44 +0200349
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200350 * `description` : used for the the test case names
351 * `expected_usage`: the usage flags generated as the expected usage flags
352 in the test cases. CAn differ from the usage flags
353 stored in the keys because of the usage flags extension.
gabor-mezei-arm672e3762021-06-24 10:16:44 +0200354 """
Gilles Peskine897dff92021-03-10 15:03:44 +0100355 super().__init__(**kwargs)
356 self.description = description #type: str
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200357 self.expected_usage = expected_usage if expected_usage else self.usage.string #type: str
gabor-mezei-arm7748b6f2021-06-24 10:04:38 +0200358
Gilles Peskine897dff92021-03-10 15:03:44 +0100359class StorageFormat:
360 """Storage format stability test cases."""
361
362 def __init__(self, info: Information, version: int, forward: bool) -> None:
363 """Prepare to generate test cases for storage format stability.
364
365 * `info`: information about the API. See the `Information` class.
366 * `version`: the storage format version to generate test cases for.
367 * `forward`: if true, generate forward compatibility test cases which
368 save a key and check that its representation is as intended. Otherwise
369 generate backward compatibility test cases which inject a key
370 representation and check that it can be read and used.
371 """
gabor-mezei-arm7b5c4e22021-06-23 17:01:44 +0200372 self.constructors = info.constructors #type: macro_collector.PSAMacroEnumerator
373 self.version = version #type: int
374 self.forward = forward #type: bool
Gilles Peskine897dff92021-03-10 15:03:44 +0100375
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200376 def make_test_case(self, key: StorageTestData) -> test_case.TestCase:
Gilles Peskine897dff92021-03-10 15:03:44 +0100377 """Construct a storage format test case for the given key.
378
379 If ``forward`` is true, generate a forward compatibility test case:
380 create a key and validate that it has the expected representation.
381 Otherwise generate a backward compatibility test case: inject the
382 key representation into storage and validate that it can be read
383 correctly.
384 """
385 verb = 'save' if self.forward else 'read'
386 tc = test_case.TestCase()
387 tc.set_description('PSA storage {}: {}'.format(verb, key.description))
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100388 dependencies = automatic_dependencies(
389 key.lifetime.string, key.type.string,
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200390 key.expected_usage, key.alg.string, key.alg2.string,
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100391 )
392 dependencies = finish_family_dependencies(dependencies, key.bits)
393 tc.set_dependencies(dependencies)
Gilles Peskine897dff92021-03-10 15:03:44 +0100394 tc.set_function('key_storage_' + verb)
395 if self.forward:
396 extra_arguments = []
397 else:
Gilles Peskine45f1cd72021-04-21 20:11:33 +0200398 flags = []
Gilles Peskine897dff92021-03-10 15:03:44 +0100399 # Some test keys have the RAW_DATA type and attributes that don't
400 # necessarily make sense. We do this to validate numerical
401 # encodings of the attributes.
402 # Raw data keys have no useful exercise anyway so there is no
403 # loss of test coverage.
Gilles Peskine45f1cd72021-04-21 20:11:33 +0200404 if key.type.string != 'PSA_KEY_TYPE_RAW_DATA':
405 flags.append('TEST_FLAG_EXERCISE')
406 if 'READ_ONLY' in key.lifetime.string:
407 flags.append('TEST_FLAG_READ_ONLY')
408 extra_arguments = [' | '.join(flags) if flags else '0']
Gilles Peskine897dff92021-03-10 15:03:44 +0100409 tc.set_arguments([key.lifetime.string,
410 key.type.string, str(key.bits),
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200411 key.expected_usage, key.alg.string, key.alg2.string,
Gilles Peskine897dff92021-03-10 15:03:44 +0100412 '"' + key.material.hex() + '"',
413 '"' + key.hex() + '"',
414 *extra_arguments])
415 return tc
416
Gilles Peskineeb7bdaa2021-04-21 22:05:34 +0200417 def key_for_lifetime(
418 self,
419 lifetime: str,
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200420 ) -> StorageTestData:
Gilles Peskineeb7bdaa2021-04-21 22:05:34 +0200421 """Construct a test key for the given lifetime."""
422 short = lifetime
423 short = re.sub(r'PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION',
424 r'', short)
425 short = re.sub(r'PSA_KEY_[A-Z]+_', r'', short)
426 description = 'lifetime: ' + short
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200427 key = StorageTestData(version=self.version,
428 id=1, lifetime=lifetime,
429 type='PSA_KEY_TYPE_RAW_DATA', bits=8,
430 usage='PSA_KEY_USAGE_EXPORT', alg=0, alg2=0,
431 material=b'L',
432 description=description)
433 return key
Gilles Peskineeb7bdaa2021-04-21 22:05:34 +0200434
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200435 def all_keys_for_lifetimes(self) -> Iterator[StorageTestData]:
Gilles Peskineeb7bdaa2021-04-21 22:05:34 +0200436 """Generate test keys covering lifetimes."""
437 lifetimes = sorted(self.constructors.lifetimes)
438 expressions = self.constructors.generate_expressions(lifetimes)
439 for lifetime in expressions:
440 # Don't attempt to create or load a volatile key in storage
441 if 'VOLATILE' in lifetime:
442 continue
443 # Don't attempt to create a read-only key in storage,
444 # but do attempt to load one.
445 if 'READ_ONLY' in lifetime and self.forward:
446 continue
gabor-mezei-arm340fbf32021-06-28 19:26:55 +0200447 yield self.key_for_lifetime(lifetime)
Gilles Peskineeb7bdaa2021-04-21 22:05:34 +0200448
gabor-mezei-arm63857802021-06-29 15:39:56 +0200449 def keys_for_usage_flags(
Gilles Peskine897dff92021-03-10 15:03:44 +0100450 self,
451 usage_flags: List[str],
gabor-mezei-arm6ee72532021-06-24 09:42:02 +0200452 short: Optional[str] = None,
gabor-mezei-arm340fbf32021-06-28 19:26:55 +0200453 test_implicit_usage: Optional[bool] = False
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200454 ) -> Iterator[StorageTestData]:
Gilles Peskine897dff92021-03-10 15:03:44 +0100455 """Construct a test key for the given key usage."""
456 usage = ' | '.join(usage_flags) if usage_flags else '0'
457 if short is None:
458 short = re.sub(r'\bPSA_KEY_USAGE_', r'', usage)
gabor-mezei-arm340fbf32021-06-28 19:26:55 +0200459 extra_desc = ' with implication' if test_implicit_usage else ''
gabor-mezei-arm6ee72532021-06-24 09:42:02 +0200460 description = 'usage' + extra_desc + ': ' + short
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200461 key1 = StorageTestData(version=self.version,
462 id=1, lifetime=0x00000001,
463 type='PSA_KEY_TYPE_RAW_DATA', bits=8,
464 expected_usage=usage,
465 usage=usage, alg=0, alg2=0,
466 material=b'K',
467 description=description)
468 yield key1
469
gabor-mezei-arm340fbf32021-06-28 19:26:55 +0200470 if test_implicit_usage:
471 description = 'usage without implication' + ': ' + short
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200472 key2 = StorageTestData(version=self.version,
473 id=1, lifetime=0x00000001,
474 type='PSA_KEY_TYPE_RAW_DATA', bits=8,
475 without_implicit_usage=True,
476 usage=usage, alg=0, alg2=0,
477 material=b'K',
478 description=description)
479 yield key2
Gilles Peskine897dff92021-03-10 15:03:44 +0100480
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200481 def generate_keys_for_usage_flags(self, **kwargs) -> Iterator[StorageTestData]:
Gilles Peskine897dff92021-03-10 15:03:44 +0100482 """Generate test keys covering usage flags."""
483 known_flags = sorted(self.constructors.key_usage_flags)
gabor-mezei-arm63857802021-06-29 15:39:56 +0200484 yield from self.keys_for_usage_flags(['0'], **kwargs)
gabor-mezei-arm340fbf32021-06-28 19:26:55 +0200485 for usage_flag in known_flags:
gabor-mezei-arm63857802021-06-29 15:39:56 +0200486 yield from self.keys_for_usage_flags([usage_flag], **kwargs)
gabor-mezei-arm340fbf32021-06-28 19:26:55 +0200487 for flag1, flag2 in zip(known_flags,
488 known_flags[1:] + [known_flags[0]]):
gabor-mezei-arm63857802021-06-29 15:39:56 +0200489 yield from self.keys_for_usage_flags([flag1, flag2], **kwargs)
gabor-mezei-arm49d6ea92021-06-24 14:38:51 +0200490
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200491 def generate_key_for_all_usage_flags(self) -> Iterator[StorageTestData]:
gabor-mezei-arm49d6ea92021-06-24 14:38:51 +0200492 known_flags = sorted(self.constructors.key_usage_flags)
gabor-mezei-arm63857802021-06-29 15:39:56 +0200493 yield from self.keys_for_usage_flags(known_flags, short='all known')
gabor-mezei-arm49d6ea92021-06-24 14:38:51 +0200494
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200495 def all_keys_for_usage_flags(self) -> Iterator[StorageTestData]:
gabor-mezei-arm340fbf32021-06-28 19:26:55 +0200496 yield from self.generate_keys_for_usage_flags()
497 yield from self.generate_key_for_all_usage_flags()
Gilles Peskine897dff92021-03-10 15:03:44 +0100498
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100499 def keys_for_type(
500 self,
501 key_type: str,
502 params: Optional[Iterable[str]] = None
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200503 ) -> Iterator[StorageTestData]:
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100504 """Generate test keys for the given key type.
505
506 For key types that depend on a parameter (e.g. elliptic curve family),
507 `param` is the parameter to pass to the constructor. Only a single
508 parameter is supported.
509 """
510 kt = crypto_knowledge.KeyType(key_type, params)
511 for bits in kt.sizes_to_test():
512 usage_flags = 'PSA_KEY_USAGE_EXPORT'
513 alg = 0
514 alg2 = 0
515 key_material = kt.key_material(bits)
516 short_expression = re.sub(r'\bPSA_(?:KEY_TYPE|ECC_FAMILY)_',
517 r'',
518 kt.expression)
519 description = 'type: {} {}-bit'.format(short_expression, bits)
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200520 key = StorageTestData(version=self.version,
521 id=1, lifetime=0x00000001,
522 type=kt.expression, bits=bits,
523 usage=usage_flags, alg=alg, alg2=alg2,
524 material=key_material,
525 description=description)
526 yield key
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100527
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200528 def all_keys_for_types(self) -> Iterator[StorageTestData]:
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100529 """Generate test keys covering key types and their representations."""
Gilles Peskine537d5fa2021-04-19 13:50:25 +0200530 key_types = sorted(self.constructors.key_types)
gabor-mezei-arm340fbf32021-06-28 19:26:55 +0200531 for key_type in self.constructors.generate_expressions(key_types):
532 yield from self.keys_for_type(key_type)
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100533
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200534 def keys_for_algorithm(self, alg: str) -> Iterator[StorageTestData]:
Gilles Peskined86bc522021-03-10 15:08:57 +0100535 """Generate test keys for the specified algorithm."""
536 # For now, we don't have information on the compatibility of key
537 # types and algorithms. So we just test the encoding of algorithms,
538 # and not that operations can be performed with them.
Gilles Peskine20f55f62021-04-21 10:18:19 +0200539 descr = re.sub(r'PSA_ALG_', r'', alg)
540 descr = re.sub(r',', r', ', re.sub(r' +', r'', descr))
Gilles Peskined86bc522021-03-10 15:08:57 +0100541 usage = 'PSA_KEY_USAGE_EXPORT'
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200542 key1 = StorageTestData(version=self.version,
543 id=1, lifetime=0x00000001,
544 type='PSA_KEY_TYPE_RAW_DATA', bits=8,
545 usage=usage, alg=alg, alg2=0,
546 material=b'K',
547 description='alg: ' + descr)
548 yield key1
549 key2 = StorageTestData(version=self.version,
550 id=1, lifetime=0x00000001,
551 type='PSA_KEY_TYPE_RAW_DATA', bits=8,
552 usage=usage, alg=0, alg2=alg,
553 material=b'L',
554 description='alg2: ' + descr)
555 yield key2
Gilles Peskined86bc522021-03-10 15:08:57 +0100556
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200557 def all_keys_for_algorithms(self) -> Iterator[StorageTestData]:
Gilles Peskined86bc522021-03-10 15:08:57 +0100558 """Generate test keys covering algorithm encodings."""
Gilles Peskine537d5fa2021-04-19 13:50:25 +0200559 algorithms = sorted(self.constructors.algorithms)
gabor-mezei-arm340fbf32021-06-28 19:26:55 +0200560 for alg in self.constructors.generate_expressions(algorithms):
561 yield from self.keys_for_algorithm(alg)
Gilles Peskined86bc522021-03-10 15:08:57 +0100562
gabor-mezei-arm0c24edd2021-06-29 15:42:57 +0200563 def generate_all_keys(self) -> Iterator[StorageTestData]:
gabor-mezei-arm780cf9d2021-06-24 09:49:50 +0200564 """Generate all keys for the test cases."""
gabor-mezei-arm0c24edd2021-06-29 15:42:57 +0200565 yield from self.all_keys_for_lifetimes()
566 yield from self.all_keys_for_usage_flags()
567 yield from self.all_keys_for_types()
568 yield from self.all_keys_for_algorithms()
gabor-mezei-arm780cf9d2021-06-24 09:49:50 +0200569
gabor-mezei-arm340fbf32021-06-28 19:26:55 +0200570 def all_test_cases(self) -> Iterator[test_case.TestCase]:
Gilles Peskine897dff92021-03-10 15:03:44 +0100571 """Generate all storage format test cases."""
Gilles Peskine3c9d4232021-04-12 14:43:05 +0200572 # First build a list of all keys, then construct all the corresponding
573 # test cases. This allows all required information to be obtained in
574 # one go, which is a significant performance gain as the information
575 # includes numerical values obtained by compiling a C program.
Gilles Peskine45f2a402021-07-06 21:05:52 +0200576 all_keys = list(self.generate_all_keys())
577 for key in all_keys:
gabor-mezei-arm340fbf32021-06-28 19:26:55 +0200578 if key.location_value() != 0:
579 # Skip keys with a non-default location, because they
580 # require a driver and we currently have no mechanism to
581 # determine whether a driver is available.
582 continue
583 yield self.make_test_case(key)
Gilles Peskine897dff92021-03-10 15:03:44 +0100584
gabor-mezei-arma4102cb2021-06-24 09:53:26 +0200585class StorageFormatForward(StorageFormat):
586 """Storage format stability test cases for forward compatibility."""
587
588 def __init__(self, info: Information, version: int) -> None:
589 super().__init__(info, version, True)
590
591class StorageFormatV0(StorageFormat):
592 """Storage format stability test cases for version 0 compatibility."""
593
594 def __init__(self, info: Information) -> None:
595 super().__init__(info, 0, False)
Gilles Peskine897dff92021-03-10 15:03:44 +0100596
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200597 def all_keys_for_usage_flags(self) -> Iterator[StorageTestData]:
gabor-mezei-arm7748b6f2021-06-24 10:04:38 +0200598 """Generate test keys covering usage flags."""
gabor-mezei-arm340fbf32021-06-28 19:26:55 +0200599 yield from self.generate_keys_for_usage_flags(test_implicit_usage=True)
600 yield from self.generate_key_for_all_usage_flags()
gabor-mezei-arm7748b6f2021-06-24 10:04:38 +0200601
gabor-mezei-arm5df1dee2021-06-28 17:40:32 +0200602 def keys_for_implicit_usage(
gabor-mezei-arm672e3762021-06-24 10:16:44 +0200603 self,
gabor-mezei-arm2710bb12021-06-28 16:54:11 +0200604 implyer_usage: str,
gabor-mezei-arm672e3762021-06-24 10:16:44 +0200605 alg: str,
gabor-mezei-arm2784bfe2021-06-28 20:02:11 +0200606 key_type: crypto_knowledge.KeyType
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200607 ) -> StorageTestData:
gabor-mezei-arm0f8136a2021-06-24 14:38:25 +0200608 # pylint: disable=too-many-locals
gabor-mezei-arm8f405102021-06-28 16:27:29 +0200609 """Generate test keys for the specified implicit usage flag,
gabor-mezei-arm672e3762021-06-24 10:16:44 +0200610 algorithm and key type combination.
611 """
gabor-mezei-arm2784bfe2021-06-28 20:02:11 +0200612 bits = key_type.sizes_to_test()[0]
gabor-mezei-arm2710bb12021-06-28 16:54:11 +0200613 implicit_usage = StorageKey.IMPLICIT_USAGE_FLAGS[implyer_usage]
gabor-mezei-armd9050a52021-06-28 16:35:48 +0200614 usage_flags = 'PSA_KEY_USAGE_EXPORT'
gabor-mezei-arm2710bb12021-06-28 16:54:11 +0200615 material_usage_flags = usage_flags + ' | ' + implyer_usage
616 expected_usage_flags = material_usage_flags + ' | ' + implicit_usage
gabor-mezei-armd9050a52021-06-28 16:35:48 +0200617 alg2 = 0
gabor-mezei-arm2784bfe2021-06-28 20:02:11 +0200618 key_material = key_type.key_material(bits)
gabor-mezei-arm2710bb12021-06-28 16:54:11 +0200619 usage_expression = re.sub(r'PSA_KEY_USAGE_', r'', implyer_usage)
gabor-mezei-armd9050a52021-06-28 16:35:48 +0200620 alg_expression = re.sub(r'PSA_ALG_', r'', alg)
621 alg_expression = re.sub(r',', r', ', re.sub(r' +', r'', alg_expression))
622 key_type_expression = re.sub(r'\bPSA_(?:KEY_TYPE|ECC_FAMILY)_',
623 r'',
gabor-mezei-arm2784bfe2021-06-28 20:02:11 +0200624 key_type.expression)
gabor-mezei-arm5df1dee2021-06-28 17:40:32 +0200625 description = 'implied by {}: {} {} {}-bit'.format(
gabor-mezei-armd9050a52021-06-28 16:35:48 +0200626 usage_expression, alg_expression, key_type_expression, bits)
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200627 key = StorageTestData(version=self.version,
628 id=1, lifetime=0x00000001,
629 type=key_type.expression, bits=bits,
630 usage=material_usage_flags,
631 expected_usage=expected_usage_flags,
632 without_implicit_usage=True,
633 alg=alg, alg2=alg2,
634 material=key_material,
635 description=description)
636 return key
gabor-mezei-arm672e3762021-06-24 10:16:44 +0200637
638 def gather_key_types_for_sign_alg(self) -> Dict[str, List[str]]:
gabor-mezei-arm0f8136a2021-06-24 14:38:25 +0200639 # pylint: disable=too-many-locals
gabor-mezei-arm672e3762021-06-24 10:16:44 +0200640 """Match possible key types for sign algorithms."""
641 # To create a valid combinaton both the algorithms and key types
642 # must be filtered. Pair them with keywords created from its names.
643 incompatible_alg_keyword = frozenset(['RAW', 'ANY', 'PURE'])
644 incompatible_key_type_keywords = frozenset(['MONTGOMERY'])
645 keyword_translation = {
646 'ECDSA': 'ECC',
647 'ED[0-9]*.*' : 'EDWARDS'
648 }
649 exclusive_keywords = {
650 'EDWARDS': 'ECC'
651 }
gabor-mezei-arm0f8136a2021-06-24 14:38:25 +0200652 key_types = set(self.constructors.generate_expressions(self.constructors.key_types))
653 algorithms = set(self.constructors.generate_expressions(self.constructors.sign_algorithms))
gabor-mezei-arm672e3762021-06-24 10:16:44 +0200654 alg_with_keys = {} #type: Dict[str, List[str]]
655 translation_table = str.maketrans('(', '_', ')')
656 for alg in algorithms:
657 # Generate keywords from the name of the algorithm
658 alg_keywords = set(alg.partition('(')[0].split(sep='_')[2:])
659 # Translate keywords for better matching with the key types
660 for keyword in alg_keywords.copy():
661 for pattern, replace in keyword_translation.items():
662 if re.match(pattern, keyword):
663 alg_keywords.remove(keyword)
664 alg_keywords.add(replace)
665 # Filter out incompatible algortihms
666 if not alg_keywords.isdisjoint(incompatible_alg_keyword):
667 continue
668
669 for key_type in key_types:
670 # Generate keywords from the of the key type
671 key_type_keywords = set(key_type.translate(translation_table).split(sep='_')[3:])
672
673 # Remove ambigious keywords
674 for keyword1, keyword2 in exclusive_keywords.items():
675 if keyword1 in key_type_keywords:
676 key_type_keywords.remove(keyword2)
677
678 if key_type_keywords.isdisjoint(incompatible_key_type_keywords) and\
679 not key_type_keywords.isdisjoint(alg_keywords):
680 if alg in alg_with_keys:
681 alg_with_keys[alg].append(key_type)
682 else:
683 alg_with_keys[alg] = [key_type]
684 return alg_with_keys
685
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200686 def all_keys_for_implicit_usage(self) -> Iterator[StorageTestData]:
gabor-mezei-arm672e3762021-06-24 10:16:44 +0200687 """Generate test keys for usage flag extensions."""
688 # Generate a key type and algorithm pair for each extendable usage
689 # flag to generate a valid key for exercising. The key is generated
690 # without usage extension to check the extension compatiblity.
gabor-mezei-arm672e3762021-06-24 10:16:44 +0200691 alg_with_keys = self.gather_key_types_for_sign_alg()
gabor-mezei-arm11e48382021-06-24 16:35:01 +0200692
gabor-mezei-arm340fbf32021-06-28 19:26:55 +0200693 for usage in sorted(StorageKey.IMPLICIT_USAGE_FLAGS, key=str):
694 for alg in sorted(alg_with_keys):
695 for key_type in sorted(alg_with_keys[alg]):
696 # The key types must be filtered to fit the specific usage flag.
gabor-mezei-arm2784bfe2021-06-28 20:02:11 +0200697 kt = crypto_knowledge.KeyType(key_type)
698 if kt.is_valid_for_signature(usage):
699 yield self.keys_for_implicit_usage(usage, alg, kt)
gabor-mezei-arm672e3762021-06-24 10:16:44 +0200700
gabor-mezei-arm0c24edd2021-06-29 15:42:57 +0200701 def generate_all_keys(self) -> Iterator[StorageTestData]:
702 yield from super().generate_all_keys()
703 yield from self.all_keys_for_implicit_usage()
gabor-mezei-arm7748b6f2021-06-24 10:04:38 +0200704
Gilles Peskineb94ea512021-03-10 02:12:08 +0100705class TestGenerator:
706 """Generate test data."""
707
708 def __init__(self, options) -> None:
709 self.test_suite_directory = self.get_option(options, 'directory',
710 'tests/suites')
711 self.info = Information()
712
713 @staticmethod
714 def get_option(options, name: str, default: T) -> T:
715 value = getattr(options, name, None)
716 return default if value is None else value
717
Gilles Peskine0298bda2021-03-10 02:34:37 +0100718 def filename_for(self, basename: str) -> str:
719 """The location of the data file with the specified base name."""
Bence Szépkúti9e84ec72021-05-07 11:49:17 +0200720 return posixpath.join(self.test_suite_directory, basename + '.data')
Gilles Peskine0298bda2021-03-10 02:34:37 +0100721
Gilles Peskineb94ea512021-03-10 02:12:08 +0100722 def write_test_data_file(self, basename: str,
723 test_cases: Iterable[test_case.TestCase]) -> None:
724 """Write the test cases to a .data file.
725
726 The output file is ``basename + '.data'`` in the test suite directory.
727 """
Gilles Peskine0298bda2021-03-10 02:34:37 +0100728 filename = self.filename_for(basename)
Gilles Peskineb94ea512021-03-10 02:12:08 +0100729 test_case.write_data_file(filename, test_cases)
730
Gilles Peskine0298bda2021-03-10 02:34:37 +0100731 TARGETS = {
Przemyslaw Stekiel1b0978b2021-10-15 15:21:51 +0200732 'test_suite_psa_crypto_generate_key.generated':
733 lambda info: KeyGenerate(info).test_cases_for_key_generation(),
Gilles Peskine0298bda2021-03-10 02:34:37 +0100734 'test_suite_psa_crypto_not_supported.generated':
Gilles Peskine3d778392021-02-17 15:11:05 +0100735 lambda info: NotSupported(info).test_cases_for_not_supported(),
Gilles Peskine897dff92021-03-10 15:03:44 +0100736 'test_suite_psa_crypto_storage_format.current':
gabor-mezei-arma4102cb2021-06-24 09:53:26 +0200737 lambda info: StorageFormatForward(info, 0).all_test_cases(),
Gilles Peskine897dff92021-03-10 15:03:44 +0100738 'test_suite_psa_crypto_storage_format.v0':
gabor-mezei-arma4102cb2021-06-24 09:53:26 +0200739 lambda info: StorageFormatV0(info).all_test_cases(),
Gilles Peskine0298bda2021-03-10 02:34:37 +0100740 } #type: Dict[str, Callable[[Information], Iterable[test_case.TestCase]]]
741
742 def generate_target(self, name: str) -> None:
743 test_cases = self.TARGETS[name](self.info)
744 self.write_test_data_file(name, test_cases)
Gilles Peskine14e428f2021-01-26 22:19:21 +0100745
Gilles Peskine09940492021-01-26 22:16:30 +0100746def main(args):
747 """Command line entry point."""
748 parser = argparse.ArgumentParser(description=__doc__)
Gilles Peskine0298bda2021-03-10 02:34:37 +0100749 parser.add_argument('--list', action='store_true',
750 help='List available targets and exit')
751 parser.add_argument('targets', nargs='*', metavar='TARGET',
752 help='Target file to generate (default: all; "-": none)')
Gilles Peskine09940492021-01-26 22:16:30 +0100753 options = parser.parse_args(args)
Gilles Peskinec86f20a2021-04-22 00:20:47 +0200754 build_tree.chdir_to_root()
Gilles Peskine09940492021-01-26 22:16:30 +0100755 generator = TestGenerator(options)
Gilles Peskine0298bda2021-03-10 02:34:37 +0100756 if options.list:
757 for name in sorted(generator.TARGETS):
758 print(generator.filename_for(name))
759 return
760 if options.targets:
761 # Allow "-" as a special case so you can run
762 # ``generate_psa_tests.py - $targets`` and it works uniformly whether
763 # ``$targets`` is empty or not.
764 options.targets = [os.path.basename(re.sub(r'\.data\Z', r'', target))
765 for target in options.targets
766 if target != '-']
767 else:
768 options.targets = sorted(generator.TARGETS)
769 for target in options.targets:
770 generator.generate_target(target)
Gilles Peskine09940492021-01-26 22:16:30 +0100771
772if __name__ == '__main__':
773 main(sys.argv[1:])