blob: d1307ad11a228da0fc6c6cce183318f64f5e10df [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,
Przemyslaw Stekiel437da192021-10-20 11:59:50 +0200238 result: str = ''
Przemyslaw Stekiel1b0978b2021-10-15 15:21:51 +0200239) -> test_case.TestCase:
240 """Return one test case exercising a key generation.
241 """
242 hack_dependencies_not_implemented(dependencies)
243 tc = test_case.TestCase()
244 short_key_type = re.sub(r'PSA_(KEY_TYPE|ECC_FAMILY)_', r'', key_type)
245 tc.set_description('PSA {} {}-bit'
Przemyslaw Stekiel437da192021-10-20 11:59:50 +0200246 .format(short_key_type, bits))
Przemyslaw Stekiel1b0978b2021-10-15 15:21:51 +0200247 tc.set_dependencies(dependencies)
248 tc.set_function('generate_key')
249 tc.set_arguments([key_type] + list(args))
250 tc.set_result(result)
251
252 return tc
253
254class KeyGenerate:
255 """Generate positive and negative (invalid argument) test cases for key generation."""
256
257 def __init__(self, info: Information) -> None:
258 self.constructors = info.constructors
259
Przemyslaw Stekiel437da192021-10-20 11:59:50 +0200260 ECC_KEY_TYPES = ('PSA_KEY_TYPE_ECC_KEY_PAIR',
261 'PSA_KEY_TYPE_ECC_PUBLIC_KEY')
262
263 @staticmethod
Przemyslaw Stekiel1b0978b2021-10-15 15:21:51 +0200264 def test_cases_for_key_type_key_generation(
Przemyslaw Stekiel437da192021-10-20 11:59:50 +0200265 kt: crypto_knowledge.KeyType
Przemyslaw Stekiel1b0978b2021-10-15 15:21:51 +0200266 ) -> 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),
Przemyslaw Stekiel437da192021-10-20 11:59:50 +0200288 result
Przemyslaw Stekiel1b0978b2021-10-15 15:21:51 +0200289 )
290
Przemyslaw Stekiel1b0978b2021-10-15 15:21:51 +0200291 def test_cases_for_key_generation(self) -> Iterator[test_case.TestCase]:
292 """Generate test cases that exercise the generation of keys."""
293 for key_type in sorted(self.constructors.key_types):
294 if key_type in self.ECC_KEY_TYPES:
295 continue
296 kt = crypto_knowledge.KeyType(key_type)
297 yield from self.test_cases_for_key_type_key_generation(kt)
298 for curve_family in sorted(self.constructors.ecc_curves):
299 for constr in self.ECC_KEY_TYPES:
300 kt = crypto_knowledge.KeyType(constr, [curve_family])
Przemyslaw Stekiel437da192021-10-20 11:59:50 +0200301 yield from self.test_cases_for_key_type_key_generation(kt)
Przemyslaw Stekiel1b0978b2021-10-15 15:21:51 +0200302
Gilles Peskine897dff92021-03-10 15:03:44 +0100303class StorageKey(psa_storage.Key):
304 """Representation of a key for storage format testing."""
305
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200306 IMPLICIT_USAGE_FLAGS = {
307 'PSA_KEY_USAGE_SIGN_HASH': 'PSA_KEY_USAGE_SIGN_MESSAGE',
308 'PSA_KEY_USAGE_VERIFY_HASH': 'PSA_KEY_USAGE_VERIFY_MESSAGE'
309 } #type: Dict[str, str]
310 """Mapping of usage flags to the flags that they imply."""
311
312 def __init__(
313 self,
314 usage: str,
315 without_implicit_usage: Optional[bool] = False,
316 **kwargs
317 ) -> None:
318 """Prepare to generate a key.
319
320 * `usage` : The usage flags used for the key.
321 * `without_implicit_usage`: Flag to defide to apply the usage extension
322 """
gabor-mezei-arm2c9e54a2021-06-29 17:21:21 +0200323 super().__init__(usage=usage, **kwargs)
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200324
325 if not without_implicit_usage:
326 for flag, implicit in self.IMPLICIT_USAGE_FLAGS.items():
327 if self.usage.value() & psa_storage.Expr(flag).value() and \
328 self.usage.value() & psa_storage.Expr(implicit).value() == 0:
329 self.usage = psa_storage.Expr(self.usage.string + ' | ' + implicit)
330
331class StorageTestData(StorageKey):
332 """Representation of test case data for storage format testing."""
333
gabor-mezei-arm672e3762021-06-24 10:16:44 +0200334 def __init__(
335 self,
336 description: str,
337 expected_usage: Optional[str] = None,
338 **kwargs
339 ) -> None:
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200340 """Prepare to generate test data
gabor-mezei-arm672e3762021-06-24 10:16:44 +0200341
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200342 * `description` : used for the the test case names
343 * `expected_usage`: the usage flags generated as the expected usage flags
344 in the test cases. CAn differ from the usage flags
345 stored in the keys because of the usage flags extension.
gabor-mezei-arm672e3762021-06-24 10:16:44 +0200346 """
Gilles Peskine897dff92021-03-10 15:03:44 +0100347 super().__init__(**kwargs)
348 self.description = description #type: str
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200349 self.expected_usage = expected_usage if expected_usage else self.usage.string #type: str
gabor-mezei-arm7748b6f2021-06-24 10:04:38 +0200350
Gilles Peskine897dff92021-03-10 15:03:44 +0100351class StorageFormat:
352 """Storage format stability test cases."""
353
354 def __init__(self, info: Information, version: int, forward: bool) -> None:
355 """Prepare to generate test cases for storage format stability.
356
357 * `info`: information about the API. See the `Information` class.
358 * `version`: the storage format version to generate test cases for.
359 * `forward`: if true, generate forward compatibility test cases which
360 save a key and check that its representation is as intended. Otherwise
361 generate backward compatibility test cases which inject a key
362 representation and check that it can be read and used.
363 """
gabor-mezei-arm7b5c4e22021-06-23 17:01:44 +0200364 self.constructors = info.constructors #type: macro_collector.PSAMacroEnumerator
365 self.version = version #type: int
366 self.forward = forward #type: bool
Gilles Peskine897dff92021-03-10 15:03:44 +0100367
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200368 def make_test_case(self, key: StorageTestData) -> test_case.TestCase:
Gilles Peskine897dff92021-03-10 15:03:44 +0100369 """Construct a storage format test case for the given key.
370
371 If ``forward`` is true, generate a forward compatibility test case:
372 create a key and validate that it has the expected representation.
373 Otherwise generate a backward compatibility test case: inject the
374 key representation into storage and validate that it can be read
375 correctly.
376 """
377 verb = 'save' if self.forward else 'read'
378 tc = test_case.TestCase()
379 tc.set_description('PSA storage {}: {}'.format(verb, key.description))
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100380 dependencies = automatic_dependencies(
381 key.lifetime.string, key.type.string,
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200382 key.expected_usage, key.alg.string, key.alg2.string,
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100383 )
384 dependencies = finish_family_dependencies(dependencies, key.bits)
385 tc.set_dependencies(dependencies)
Gilles Peskine897dff92021-03-10 15:03:44 +0100386 tc.set_function('key_storage_' + verb)
387 if self.forward:
388 extra_arguments = []
389 else:
Gilles Peskine45f1cd72021-04-21 20:11:33 +0200390 flags = []
Gilles Peskine897dff92021-03-10 15:03:44 +0100391 # Some test keys have the RAW_DATA type and attributes that don't
392 # necessarily make sense. We do this to validate numerical
393 # encodings of the attributes.
394 # Raw data keys have no useful exercise anyway so there is no
395 # loss of test coverage.
Gilles Peskine45f1cd72021-04-21 20:11:33 +0200396 if key.type.string != 'PSA_KEY_TYPE_RAW_DATA':
397 flags.append('TEST_FLAG_EXERCISE')
398 if 'READ_ONLY' in key.lifetime.string:
399 flags.append('TEST_FLAG_READ_ONLY')
400 extra_arguments = [' | '.join(flags) if flags else '0']
Gilles Peskine897dff92021-03-10 15:03:44 +0100401 tc.set_arguments([key.lifetime.string,
402 key.type.string, str(key.bits),
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200403 key.expected_usage, key.alg.string, key.alg2.string,
Gilles Peskine897dff92021-03-10 15:03:44 +0100404 '"' + key.material.hex() + '"',
405 '"' + key.hex() + '"',
406 *extra_arguments])
407 return tc
408
Gilles Peskineeb7bdaa2021-04-21 22:05:34 +0200409 def key_for_lifetime(
410 self,
411 lifetime: str,
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200412 ) -> StorageTestData:
Gilles Peskineeb7bdaa2021-04-21 22:05:34 +0200413 """Construct a test key for the given lifetime."""
414 short = lifetime
415 short = re.sub(r'PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION',
416 r'', short)
417 short = re.sub(r'PSA_KEY_[A-Z]+_', r'', short)
418 description = 'lifetime: ' + short
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200419 key = StorageTestData(version=self.version,
420 id=1, lifetime=lifetime,
421 type='PSA_KEY_TYPE_RAW_DATA', bits=8,
422 usage='PSA_KEY_USAGE_EXPORT', alg=0, alg2=0,
423 material=b'L',
424 description=description)
425 return key
Gilles Peskineeb7bdaa2021-04-21 22:05:34 +0200426
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200427 def all_keys_for_lifetimes(self) -> Iterator[StorageTestData]:
Gilles Peskineeb7bdaa2021-04-21 22:05:34 +0200428 """Generate test keys covering lifetimes."""
429 lifetimes = sorted(self.constructors.lifetimes)
430 expressions = self.constructors.generate_expressions(lifetimes)
431 for lifetime in expressions:
432 # Don't attempt to create or load a volatile key in storage
433 if 'VOLATILE' in lifetime:
434 continue
435 # Don't attempt to create a read-only key in storage,
436 # but do attempt to load one.
437 if 'READ_ONLY' in lifetime and self.forward:
438 continue
gabor-mezei-arm340fbf32021-06-28 19:26:55 +0200439 yield self.key_for_lifetime(lifetime)
Gilles Peskineeb7bdaa2021-04-21 22:05:34 +0200440
gabor-mezei-arm63857802021-06-29 15:39:56 +0200441 def keys_for_usage_flags(
Gilles Peskine897dff92021-03-10 15:03:44 +0100442 self,
443 usage_flags: List[str],
gabor-mezei-arm6ee72532021-06-24 09:42:02 +0200444 short: Optional[str] = None,
gabor-mezei-arm340fbf32021-06-28 19:26:55 +0200445 test_implicit_usage: Optional[bool] = False
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200446 ) -> Iterator[StorageTestData]:
Gilles Peskine897dff92021-03-10 15:03:44 +0100447 """Construct a test key for the given key usage."""
448 usage = ' | '.join(usage_flags) if usage_flags else '0'
449 if short is None:
450 short = re.sub(r'\bPSA_KEY_USAGE_', r'', usage)
gabor-mezei-arm340fbf32021-06-28 19:26:55 +0200451 extra_desc = ' with implication' if test_implicit_usage else ''
gabor-mezei-arm6ee72532021-06-24 09:42:02 +0200452 description = 'usage' + extra_desc + ': ' + short
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200453 key1 = StorageTestData(version=self.version,
454 id=1, lifetime=0x00000001,
455 type='PSA_KEY_TYPE_RAW_DATA', bits=8,
456 expected_usage=usage,
457 usage=usage, alg=0, alg2=0,
458 material=b'K',
459 description=description)
460 yield key1
461
gabor-mezei-arm340fbf32021-06-28 19:26:55 +0200462 if test_implicit_usage:
463 description = 'usage without implication' + ': ' + short
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200464 key2 = StorageTestData(version=self.version,
465 id=1, lifetime=0x00000001,
466 type='PSA_KEY_TYPE_RAW_DATA', bits=8,
467 without_implicit_usage=True,
468 usage=usage, alg=0, alg2=0,
469 material=b'K',
470 description=description)
471 yield key2
Gilles Peskine897dff92021-03-10 15:03:44 +0100472
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200473 def generate_keys_for_usage_flags(self, **kwargs) -> Iterator[StorageTestData]:
Gilles Peskine897dff92021-03-10 15:03:44 +0100474 """Generate test keys covering usage flags."""
475 known_flags = sorted(self.constructors.key_usage_flags)
gabor-mezei-arm63857802021-06-29 15:39:56 +0200476 yield from self.keys_for_usage_flags(['0'], **kwargs)
gabor-mezei-arm340fbf32021-06-28 19:26:55 +0200477 for usage_flag in known_flags:
gabor-mezei-arm63857802021-06-29 15:39:56 +0200478 yield from self.keys_for_usage_flags([usage_flag], **kwargs)
gabor-mezei-arm340fbf32021-06-28 19:26:55 +0200479 for flag1, flag2 in zip(known_flags,
480 known_flags[1:] + [known_flags[0]]):
gabor-mezei-arm63857802021-06-29 15:39:56 +0200481 yield from self.keys_for_usage_flags([flag1, flag2], **kwargs)
gabor-mezei-arm49d6ea92021-06-24 14:38:51 +0200482
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200483 def generate_key_for_all_usage_flags(self) -> Iterator[StorageTestData]:
gabor-mezei-arm49d6ea92021-06-24 14:38:51 +0200484 known_flags = sorted(self.constructors.key_usage_flags)
gabor-mezei-arm63857802021-06-29 15:39:56 +0200485 yield from self.keys_for_usage_flags(known_flags, short='all known')
gabor-mezei-arm49d6ea92021-06-24 14:38:51 +0200486
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200487 def all_keys_for_usage_flags(self) -> Iterator[StorageTestData]:
gabor-mezei-arm340fbf32021-06-28 19:26:55 +0200488 yield from self.generate_keys_for_usage_flags()
489 yield from self.generate_key_for_all_usage_flags()
Gilles Peskine897dff92021-03-10 15:03:44 +0100490
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100491 def keys_for_type(
492 self,
493 key_type: str,
494 params: Optional[Iterable[str]] = None
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200495 ) -> Iterator[StorageTestData]:
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100496 """Generate test keys for the given key type.
497
498 For key types that depend on a parameter (e.g. elliptic curve family),
499 `param` is the parameter to pass to the constructor. Only a single
500 parameter is supported.
501 """
502 kt = crypto_knowledge.KeyType(key_type, params)
503 for bits in kt.sizes_to_test():
504 usage_flags = 'PSA_KEY_USAGE_EXPORT'
505 alg = 0
506 alg2 = 0
507 key_material = kt.key_material(bits)
508 short_expression = re.sub(r'\bPSA_(?:KEY_TYPE|ECC_FAMILY)_',
509 r'',
510 kt.expression)
511 description = 'type: {} {}-bit'.format(short_expression, bits)
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200512 key = StorageTestData(version=self.version,
513 id=1, lifetime=0x00000001,
514 type=kt.expression, bits=bits,
515 usage=usage_flags, alg=alg, alg2=alg2,
516 material=key_material,
517 description=description)
518 yield key
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100519
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200520 def all_keys_for_types(self) -> Iterator[StorageTestData]:
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100521 """Generate test keys covering key types and their representations."""
Gilles Peskine537d5fa2021-04-19 13:50:25 +0200522 key_types = sorted(self.constructors.key_types)
gabor-mezei-arm340fbf32021-06-28 19:26:55 +0200523 for key_type in self.constructors.generate_expressions(key_types):
524 yield from self.keys_for_type(key_type)
Gilles Peskinef8223ab2021-03-10 15:07:16 +0100525
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200526 def keys_for_algorithm(self, alg: str) -> Iterator[StorageTestData]:
Gilles Peskined86bc522021-03-10 15:08:57 +0100527 """Generate test keys for the specified algorithm."""
528 # For now, we don't have information on the compatibility of key
529 # types and algorithms. So we just test the encoding of algorithms,
530 # and not that operations can be performed with them.
Gilles Peskine20f55f62021-04-21 10:18:19 +0200531 descr = re.sub(r'PSA_ALG_', r'', alg)
532 descr = re.sub(r',', r', ', re.sub(r' +', r'', descr))
Gilles Peskined86bc522021-03-10 15:08:57 +0100533 usage = 'PSA_KEY_USAGE_EXPORT'
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200534 key1 = StorageTestData(version=self.version,
535 id=1, lifetime=0x00000001,
536 type='PSA_KEY_TYPE_RAW_DATA', bits=8,
537 usage=usage, alg=alg, alg2=0,
538 material=b'K',
539 description='alg: ' + descr)
540 yield key1
541 key2 = StorageTestData(version=self.version,
542 id=1, lifetime=0x00000001,
543 type='PSA_KEY_TYPE_RAW_DATA', bits=8,
544 usage=usage, alg=0, alg2=alg,
545 material=b'L',
546 description='alg2: ' + descr)
547 yield key2
Gilles Peskined86bc522021-03-10 15:08:57 +0100548
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200549 def all_keys_for_algorithms(self) -> Iterator[StorageTestData]:
Gilles Peskined86bc522021-03-10 15:08:57 +0100550 """Generate test keys covering algorithm encodings."""
Gilles Peskine537d5fa2021-04-19 13:50:25 +0200551 algorithms = sorted(self.constructors.algorithms)
gabor-mezei-arm340fbf32021-06-28 19:26:55 +0200552 for alg in self.constructors.generate_expressions(algorithms):
553 yield from self.keys_for_algorithm(alg)
Gilles Peskined86bc522021-03-10 15:08:57 +0100554
gabor-mezei-arm0c24edd2021-06-29 15:42:57 +0200555 def generate_all_keys(self) -> Iterator[StorageTestData]:
gabor-mezei-arm780cf9d2021-06-24 09:49:50 +0200556 """Generate all keys for the test cases."""
gabor-mezei-arm0c24edd2021-06-29 15:42:57 +0200557 yield from self.all_keys_for_lifetimes()
558 yield from self.all_keys_for_usage_flags()
559 yield from self.all_keys_for_types()
560 yield from self.all_keys_for_algorithms()
gabor-mezei-arm780cf9d2021-06-24 09:49:50 +0200561
gabor-mezei-arm340fbf32021-06-28 19:26:55 +0200562 def all_test_cases(self) -> Iterator[test_case.TestCase]:
Gilles Peskine897dff92021-03-10 15:03:44 +0100563 """Generate all storage format test cases."""
Gilles Peskine3c9d4232021-04-12 14:43:05 +0200564 # First build a list of all keys, then construct all the corresponding
565 # test cases. This allows all required information to be obtained in
566 # one go, which is a significant performance gain as the information
567 # includes numerical values obtained by compiling a C program.
Gilles Peskine45f2a402021-07-06 21:05:52 +0200568 all_keys = list(self.generate_all_keys())
569 for key in all_keys:
gabor-mezei-arm340fbf32021-06-28 19:26:55 +0200570 if key.location_value() != 0:
571 # Skip keys with a non-default location, because they
572 # require a driver and we currently have no mechanism to
573 # determine whether a driver is available.
574 continue
575 yield self.make_test_case(key)
Gilles Peskine897dff92021-03-10 15:03:44 +0100576
gabor-mezei-arma4102cb2021-06-24 09:53:26 +0200577class StorageFormatForward(StorageFormat):
578 """Storage format stability test cases for forward compatibility."""
579
580 def __init__(self, info: Information, version: int) -> None:
581 super().__init__(info, version, True)
582
583class StorageFormatV0(StorageFormat):
584 """Storage format stability test cases for version 0 compatibility."""
585
586 def __init__(self, info: Information) -> None:
587 super().__init__(info, 0, False)
Gilles Peskine897dff92021-03-10 15:03:44 +0100588
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200589 def all_keys_for_usage_flags(self) -> Iterator[StorageTestData]:
gabor-mezei-arm7748b6f2021-06-24 10:04:38 +0200590 """Generate test keys covering usage flags."""
gabor-mezei-arm340fbf32021-06-28 19:26:55 +0200591 yield from self.generate_keys_for_usage_flags(test_implicit_usage=True)
592 yield from self.generate_key_for_all_usage_flags()
gabor-mezei-arm7748b6f2021-06-24 10:04:38 +0200593
gabor-mezei-arm5df1dee2021-06-28 17:40:32 +0200594 def keys_for_implicit_usage(
gabor-mezei-arm672e3762021-06-24 10:16:44 +0200595 self,
gabor-mezei-arm2710bb12021-06-28 16:54:11 +0200596 implyer_usage: str,
gabor-mezei-arm672e3762021-06-24 10:16:44 +0200597 alg: str,
gabor-mezei-arm2784bfe2021-06-28 20:02:11 +0200598 key_type: crypto_knowledge.KeyType
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200599 ) -> StorageTestData:
gabor-mezei-arm0f8136a2021-06-24 14:38:25 +0200600 # pylint: disable=too-many-locals
gabor-mezei-arm8f405102021-06-28 16:27:29 +0200601 """Generate test keys for the specified implicit usage flag,
gabor-mezei-arm672e3762021-06-24 10:16:44 +0200602 algorithm and key type combination.
603 """
gabor-mezei-arm2784bfe2021-06-28 20:02:11 +0200604 bits = key_type.sizes_to_test()[0]
gabor-mezei-arm2710bb12021-06-28 16:54:11 +0200605 implicit_usage = StorageKey.IMPLICIT_USAGE_FLAGS[implyer_usage]
gabor-mezei-armd9050a52021-06-28 16:35:48 +0200606 usage_flags = 'PSA_KEY_USAGE_EXPORT'
gabor-mezei-arm2710bb12021-06-28 16:54:11 +0200607 material_usage_flags = usage_flags + ' | ' + implyer_usage
608 expected_usage_flags = material_usage_flags + ' | ' + implicit_usage
gabor-mezei-armd9050a52021-06-28 16:35:48 +0200609 alg2 = 0
gabor-mezei-arm2784bfe2021-06-28 20:02:11 +0200610 key_material = key_type.key_material(bits)
gabor-mezei-arm2710bb12021-06-28 16:54:11 +0200611 usage_expression = re.sub(r'PSA_KEY_USAGE_', r'', implyer_usage)
gabor-mezei-armd9050a52021-06-28 16:35:48 +0200612 alg_expression = re.sub(r'PSA_ALG_', r'', alg)
613 alg_expression = re.sub(r',', r', ', re.sub(r' +', r'', alg_expression))
614 key_type_expression = re.sub(r'\bPSA_(?:KEY_TYPE|ECC_FAMILY)_',
615 r'',
gabor-mezei-arm2784bfe2021-06-28 20:02:11 +0200616 key_type.expression)
gabor-mezei-arm5df1dee2021-06-28 17:40:32 +0200617 description = 'implied by {}: {} {} {}-bit'.format(
gabor-mezei-armd9050a52021-06-28 16:35:48 +0200618 usage_expression, alg_expression, key_type_expression, bits)
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200619 key = StorageTestData(version=self.version,
620 id=1, lifetime=0x00000001,
621 type=key_type.expression, bits=bits,
622 usage=material_usage_flags,
623 expected_usage=expected_usage_flags,
624 without_implicit_usage=True,
625 alg=alg, alg2=alg2,
626 material=key_material,
627 description=description)
628 return key
gabor-mezei-arm672e3762021-06-24 10:16:44 +0200629
630 def gather_key_types_for_sign_alg(self) -> Dict[str, List[str]]:
gabor-mezei-arm0f8136a2021-06-24 14:38:25 +0200631 # pylint: disable=too-many-locals
gabor-mezei-arm672e3762021-06-24 10:16:44 +0200632 """Match possible key types for sign algorithms."""
633 # To create a valid combinaton both the algorithms and key types
634 # must be filtered. Pair them with keywords created from its names.
635 incompatible_alg_keyword = frozenset(['RAW', 'ANY', 'PURE'])
636 incompatible_key_type_keywords = frozenset(['MONTGOMERY'])
637 keyword_translation = {
638 'ECDSA': 'ECC',
639 'ED[0-9]*.*' : 'EDWARDS'
640 }
641 exclusive_keywords = {
642 'EDWARDS': 'ECC'
643 }
gabor-mezei-arm0f8136a2021-06-24 14:38:25 +0200644 key_types = set(self.constructors.generate_expressions(self.constructors.key_types))
645 algorithms = set(self.constructors.generate_expressions(self.constructors.sign_algorithms))
gabor-mezei-arm672e3762021-06-24 10:16:44 +0200646 alg_with_keys = {} #type: Dict[str, List[str]]
647 translation_table = str.maketrans('(', '_', ')')
648 for alg in algorithms:
649 # Generate keywords from the name of the algorithm
650 alg_keywords = set(alg.partition('(')[0].split(sep='_')[2:])
651 # Translate keywords for better matching with the key types
652 for keyword in alg_keywords.copy():
653 for pattern, replace in keyword_translation.items():
654 if re.match(pattern, keyword):
655 alg_keywords.remove(keyword)
656 alg_keywords.add(replace)
657 # Filter out incompatible algortihms
658 if not alg_keywords.isdisjoint(incompatible_alg_keyword):
659 continue
660
661 for key_type in key_types:
662 # Generate keywords from the of the key type
663 key_type_keywords = set(key_type.translate(translation_table).split(sep='_')[3:])
664
665 # Remove ambigious keywords
666 for keyword1, keyword2 in exclusive_keywords.items():
667 if keyword1 in key_type_keywords:
668 key_type_keywords.remove(keyword2)
669
670 if key_type_keywords.isdisjoint(incompatible_key_type_keywords) and\
671 not key_type_keywords.isdisjoint(alg_keywords):
672 if alg in alg_with_keys:
673 alg_with_keys[alg].append(key_type)
674 else:
675 alg_with_keys[alg] = [key_type]
676 return alg_with_keys
677
gabor-mezei-arm2a499c02021-06-29 15:29:24 +0200678 def all_keys_for_implicit_usage(self) -> Iterator[StorageTestData]:
gabor-mezei-arm672e3762021-06-24 10:16:44 +0200679 """Generate test keys for usage flag extensions."""
680 # Generate a key type and algorithm pair for each extendable usage
681 # flag to generate a valid key for exercising. The key is generated
682 # without usage extension to check the extension compatiblity.
gabor-mezei-arm672e3762021-06-24 10:16:44 +0200683 alg_with_keys = self.gather_key_types_for_sign_alg()
gabor-mezei-arm11e48382021-06-24 16:35:01 +0200684
gabor-mezei-arm340fbf32021-06-28 19:26:55 +0200685 for usage in sorted(StorageKey.IMPLICIT_USAGE_FLAGS, key=str):
686 for alg in sorted(alg_with_keys):
687 for key_type in sorted(alg_with_keys[alg]):
688 # The key types must be filtered to fit the specific usage flag.
gabor-mezei-arm2784bfe2021-06-28 20:02:11 +0200689 kt = crypto_knowledge.KeyType(key_type)
690 if kt.is_valid_for_signature(usage):
691 yield self.keys_for_implicit_usage(usage, alg, kt)
gabor-mezei-arm672e3762021-06-24 10:16:44 +0200692
gabor-mezei-arm0c24edd2021-06-29 15:42:57 +0200693 def generate_all_keys(self) -> Iterator[StorageTestData]:
694 yield from super().generate_all_keys()
695 yield from self.all_keys_for_implicit_usage()
gabor-mezei-arm7748b6f2021-06-24 10:04:38 +0200696
Gilles Peskineb94ea512021-03-10 02:12:08 +0100697class TestGenerator:
698 """Generate test data."""
699
700 def __init__(self, options) -> None:
701 self.test_suite_directory = self.get_option(options, 'directory',
702 'tests/suites')
703 self.info = Information()
704
705 @staticmethod
706 def get_option(options, name: str, default: T) -> T:
707 value = getattr(options, name, None)
708 return default if value is None else value
709
Gilles Peskine0298bda2021-03-10 02:34:37 +0100710 def filename_for(self, basename: str) -> str:
711 """The location of the data file with the specified base name."""
Bence Szépkúti9e84ec72021-05-07 11:49:17 +0200712 return posixpath.join(self.test_suite_directory, basename + '.data')
Gilles Peskine0298bda2021-03-10 02:34:37 +0100713
Gilles Peskineb94ea512021-03-10 02:12:08 +0100714 def write_test_data_file(self, basename: str,
715 test_cases: Iterable[test_case.TestCase]) -> None:
716 """Write the test cases to a .data file.
717
718 The output file is ``basename + '.data'`` in the test suite directory.
719 """
Gilles Peskine0298bda2021-03-10 02:34:37 +0100720 filename = self.filename_for(basename)
Gilles Peskineb94ea512021-03-10 02:12:08 +0100721 test_case.write_data_file(filename, test_cases)
722
Gilles Peskine0298bda2021-03-10 02:34:37 +0100723 TARGETS = {
Przemyslaw Stekiel1b0978b2021-10-15 15:21:51 +0200724 'test_suite_psa_crypto_generate_key.generated':
725 lambda info: KeyGenerate(info).test_cases_for_key_generation(),
Gilles Peskine0298bda2021-03-10 02:34:37 +0100726 'test_suite_psa_crypto_not_supported.generated':
Gilles Peskine3d778392021-02-17 15:11:05 +0100727 lambda info: NotSupported(info).test_cases_for_not_supported(),
Gilles Peskine897dff92021-03-10 15:03:44 +0100728 'test_suite_psa_crypto_storage_format.current':
gabor-mezei-arma4102cb2021-06-24 09:53:26 +0200729 lambda info: StorageFormatForward(info, 0).all_test_cases(),
Gilles Peskine897dff92021-03-10 15:03:44 +0100730 'test_suite_psa_crypto_storage_format.v0':
gabor-mezei-arma4102cb2021-06-24 09:53:26 +0200731 lambda info: StorageFormatV0(info).all_test_cases(),
Gilles Peskine0298bda2021-03-10 02:34:37 +0100732 } #type: Dict[str, Callable[[Information], Iterable[test_case.TestCase]]]
733
734 def generate_target(self, name: str) -> None:
735 test_cases = self.TARGETS[name](self.info)
736 self.write_test_data_file(name, test_cases)
Gilles Peskine14e428f2021-01-26 22:19:21 +0100737
Gilles Peskine09940492021-01-26 22:16:30 +0100738def main(args):
739 """Command line entry point."""
740 parser = argparse.ArgumentParser(description=__doc__)
Gilles Peskine0298bda2021-03-10 02:34:37 +0100741 parser.add_argument('--list', action='store_true',
742 help='List available targets and exit')
743 parser.add_argument('targets', nargs='*', metavar='TARGET',
744 help='Target file to generate (default: all; "-": none)')
Gilles Peskine09940492021-01-26 22:16:30 +0100745 options = parser.parse_args(args)
Gilles Peskinec86f20a2021-04-22 00:20:47 +0200746 build_tree.chdir_to_root()
Gilles Peskine09940492021-01-26 22:16:30 +0100747 generator = TestGenerator(options)
Gilles Peskine0298bda2021-03-10 02:34:37 +0100748 if options.list:
749 for name in sorted(generator.TARGETS):
750 print(generator.filename_for(name))
751 return
752 if options.targets:
753 # Allow "-" as a special case so you can run
754 # ``generate_psa_tests.py - $targets`` and it works uniformly whether
755 # ``$targets`` is empty or not.
756 options.targets = [os.path.basename(re.sub(r'\.data\Z', r'', target))
757 for target in options.targets
758 if target != '-']
759 else:
760 options.targets = sorted(generator.TARGETS)
761 for target in options.targets:
762 generator.generate_target(target)
Gilles Peskine09940492021-01-26 22:16:30 +0100763
764if __name__ == '__main__':
765 main(sys.argv[1:])