blob: 5780f25b18b5ad7b10ba441fd29c85d6a0ea6e79 [file] [log] [blame]
Gilles Peskine24827022018-09-25 18:49:23 +02001#!/usr/bin/env python3
Gilles Peskinea3b93ff2019-06-03 11:23:56 +02002"""Test the program psa_constant_names.
Gilles Peskine24827022018-09-25 18:49:23 +02003Gather constant names from header files and test cases. Compile a C program
4to print out their numerical values, feed these numerical values to
5psa_constant_names, and check that the output is the original name.
6Return 0 if all test cases pass, 1 if the output was not always as expected,
Gilles Peskinea3b93ff2019-06-03 11:23:56 +02007or 1 (with a Python backtrace) if there was an operational error.
8"""
Gilles Peskine24827022018-09-25 18:49:23 +02009
10import argparse
Gilles Peskinea5000f12019-11-21 17:51:11 +010011from collections import namedtuple
Gilles Peskine24827022018-09-25 18:49:23 +020012import itertools
13import os
14import platform
15import re
16import subprocess
17import sys
18import tempfile
19
Gilles Peskinea0a315c2018-10-19 11:27:10 +020020class ReadFileLineException(Exception):
21 def __init__(self, filename, line_number):
22 message = 'in {} at {}'.format(filename, line_number)
23 super(ReadFileLineException, self).__init__(message)
24 self.filename = filename
25 self.line_number = line_number
26
27class read_file_lines:
Gilles Peskine54f54452019-05-27 18:31:59 +020028 # Dear Pylint, conventionally, a context manager class name is lowercase.
29 # pylint: disable=invalid-name,too-few-public-methods
Gilles Peskinea3b93ff2019-06-03 11:23:56 +020030 """Context manager to read a text file line by line.
31
32 ```
33 with read_file_lines(filename) as lines:
34 for line in lines:
35 process(line)
36 ```
37 is equivalent to
38 ```
39 with open(filename, 'r') as input_file:
40 for line in input_file:
41 process(line)
42 ```
43 except that if process(line) raises an exception, then the read_file_lines
44 snippet annotates the exception with the file name and line number.
45 """
Gilles Peskinea0a315c2018-10-19 11:27:10 +020046 def __init__(self, filename):
47 self.filename = filename
48 self.line_number = 'entry'
Gilles Peskine54f54452019-05-27 18:31:59 +020049 self.generator = None
Gilles Peskinea0a315c2018-10-19 11:27:10 +020050 def __enter__(self):
51 self.generator = enumerate(open(self.filename, 'r'))
52 return self
53 def __iter__(self):
54 for line_number, content in self.generator:
55 self.line_number = line_number
56 yield content
57 self.line_number = 'exit'
Gilles Peskine42a0a0a2019-05-27 18:29:47 +020058 def __exit__(self, exc_type, exc_value, exc_traceback):
59 if exc_type is not None:
Gilles Peskinea0a315c2018-10-19 11:27:10 +020060 raise ReadFileLineException(self.filename, self.line_number) \
Gilles Peskine42a0a0a2019-05-27 18:29:47 +020061 from exc_value
Gilles Peskinea0a315c2018-10-19 11:27:10 +020062
Gilles Peskine24827022018-09-25 18:49:23 +020063class Inputs:
Gilles Peskinea3b93ff2019-06-03 11:23:56 +020064 """Accumulate information about macros to test.
Gilles Peskine4408dfd2019-11-21 17:16:21 +010065
Gilles Peskinea3b93ff2019-06-03 11:23:56 +020066 This includes macro names as well as information about their arguments
67 when applicable.
68 """
69
Gilles Peskine24827022018-09-25 18:49:23 +020070 def __init__(self):
71 # Sets of names per type
72 self.statuses = set(['PSA_SUCCESS'])
73 self.algorithms = set(['0xffffffff'])
74 self.ecc_curves = set(['0xffff'])
Gilles Peskinedcaefae2019-05-16 12:55:35 +020075 self.dh_groups = set(['0xffff'])
Gilles Peskine24827022018-09-25 18:49:23 +020076 self.key_types = set(['0xffffffff'])
77 self.key_usage_flags = set(['0x80000000'])
Gilles Peskine434899f2018-10-19 11:30:26 +020078 # Hard-coded value for unknown algorithms
Darryl Green61b7f612019-02-04 16:00:21 +000079 self.hash_algorithms = set(['0x010000fe'])
Gilles Peskine434899f2018-10-19 11:30:26 +020080 self.mac_algorithms = set(['0x02ff00ff'])
Gilles Peskine882e57e2019-04-12 00:12:07 +020081 self.ka_algorithms = set(['0x30fc0000'])
82 self.kdf_algorithms = set(['0x200000ff'])
Gilles Peskine434899f2018-10-19 11:30:26 +020083 # For AEAD algorithms, the only variability is over the tag length,
84 # and this only applies to known algorithms, so don't test an
85 # unknown algorithm.
86 self.aead_algorithms = set()
Gilles Peskine24827022018-09-25 18:49:23 +020087 # Identifier prefixes
88 self.table_by_prefix = {
89 'ERROR': self.statuses,
90 'ALG': self.algorithms,
91 'CURVE': self.ecc_curves,
Gilles Peskinedcaefae2019-05-16 12:55:35 +020092 'GROUP': self.dh_groups,
Gilles Peskine24827022018-09-25 18:49:23 +020093 'KEY_TYPE': self.key_types,
94 'KEY_USAGE': self.key_usage_flags,
95 }
96 # macro name -> list of argument names
97 self.argspecs = {}
98 # argument name -> list of values
Gilles Peskine434899f2018-10-19 11:30:26 +020099 self.arguments_for = {
100 'mac_length': ['1', '63'],
101 'tag_length': ['1', '63'],
102 }
Gilles Peskine24827022018-09-25 18:49:23 +0200103
Gilles Peskineffe2d6e2019-11-21 17:17:01 +0100104 def get_names(self, type_word):
105 """Return the set of known names of values of the given type."""
106 return {
107 'status': self.statuses,
108 'algorithm': self.algorithms,
109 'ecc_curve': self.ecc_curves,
110 'dh_group': self.dh_groups,
111 'key_type': self.key_types,
112 'key_usage': self.key_usage_flags,
113 }[type_word]
114
Gilles Peskine24827022018-09-25 18:49:23 +0200115 def gather_arguments(self):
Gilles Peskinea3b93ff2019-06-03 11:23:56 +0200116 """Populate the list of values for macro arguments.
Gilles Peskine4408dfd2019-11-21 17:16:21 +0100117
Gilles Peskinea3b93ff2019-06-03 11:23:56 +0200118 Call this after parsing all the inputs.
119 """
Gilles Peskine24827022018-09-25 18:49:23 +0200120 self.arguments_for['hash_alg'] = sorted(self.hash_algorithms)
Gilles Peskine434899f2018-10-19 11:30:26 +0200121 self.arguments_for['mac_alg'] = sorted(self.mac_algorithms)
Gilles Peskine882e57e2019-04-12 00:12:07 +0200122 self.arguments_for['ka_alg'] = sorted(self.ka_algorithms)
Gilles Peskine17542082019-01-04 19:46:31 +0100123 self.arguments_for['kdf_alg'] = sorted(self.kdf_algorithms)
Gilles Peskine434899f2018-10-19 11:30:26 +0200124 self.arguments_for['aead_alg'] = sorted(self.aead_algorithms)
Gilles Peskine24827022018-09-25 18:49:23 +0200125 self.arguments_for['curve'] = sorted(self.ecc_curves)
Gilles Peskinedcaefae2019-05-16 12:55:35 +0200126 self.arguments_for['group'] = sorted(self.dh_groups)
Gilles Peskine24827022018-09-25 18:49:23 +0200127
Gilles Peskine42a0a0a2019-05-27 18:29:47 +0200128 @staticmethod
129 def _format_arguments(name, arguments):
Gilles Peskinea3b93ff2019-06-03 11:23:56 +0200130 """Format a macro call with arguments.."""
Gilles Peskine24827022018-09-25 18:49:23 +0200131 return name + '(' + ', '.join(arguments) + ')'
132
133 def distribute_arguments(self, name):
Gilles Peskinea3b93ff2019-06-03 11:23:56 +0200134 """Generate macro calls with each tested argument set.
Gilles Peskine4408dfd2019-11-21 17:16:21 +0100135
Gilles Peskinea3b93ff2019-06-03 11:23:56 +0200136 If name is a macro without arguments, just yield "name".
137 If name is a macro with arguments, yield a series of
138 "name(arg1,...,argN)" where each argument takes each possible
139 value at least once.
140 """
Gilles Peskinea0a315c2018-10-19 11:27:10 +0200141 try:
142 if name not in self.argspecs:
143 yield name
144 return
145 argspec = self.argspecs[name]
146 if argspec == []:
147 yield name + '()'
148 return
149 argument_lists = [self.arguments_for[arg] for arg in argspec]
150 arguments = [values[0] for values in argument_lists]
Gilles Peskine42a0a0a2019-05-27 18:29:47 +0200151 yield self._format_arguments(name, arguments)
Gilles Peskine54f54452019-05-27 18:31:59 +0200152 # Dear Pylint, enumerate won't work here since we're modifying
153 # the array.
154 # pylint: disable=consider-using-enumerate
Gilles Peskinea0a315c2018-10-19 11:27:10 +0200155 for i in range(len(arguments)):
156 for value in argument_lists[i][1:]:
157 arguments[i] = value
Gilles Peskine42a0a0a2019-05-27 18:29:47 +0200158 yield self._format_arguments(name, arguments)
Gilles Peskinef96ed662018-10-19 11:29:56 +0200159 arguments[i] = argument_lists[0][0]
Gilles Peskinea0a315c2018-10-19 11:27:10 +0200160 except BaseException as e:
161 raise Exception('distribute_arguments({})'.format(name)) from e
Gilles Peskine24827022018-09-25 18:49:23 +0200162
Gilles Peskine5a994c12019-11-21 16:46:51 +0100163 def generate_expressions(self, names):
164 return itertools.chain(*map(self.distribute_arguments, names))
165
Gilles Peskine42a0a0a2019-05-27 18:29:47 +0200166 _argument_split_re = re.compile(r' *, *')
167 @classmethod
168 def _argument_split(cls, arguments):
169 return re.split(cls._argument_split_re, arguments)
170
Gilles Peskine24827022018-09-25 18:49:23 +0200171 # Regex for interesting header lines.
172 # Groups: 1=macro name, 2=type, 3=argument list (optional).
Gilles Peskine42a0a0a2019-05-27 18:29:47 +0200173 _header_line_re = \
Gilles Peskine24827022018-09-25 18:49:23 +0200174 re.compile(r'#define +' +
175 r'(PSA_((?:KEY_)?[A-Z]+)_\w+)' +
176 r'(?:\(([^\n()]*)\))?')
177 # Regex of macro names to exclude.
Gilles Peskine42a0a0a2019-05-27 18:29:47 +0200178 _excluded_name_re = re.compile(r'_(?:GET|IS|OF)_|_(?:BASE|FLAG|MASK)\Z')
Gilles Peskinec68ce962018-10-19 11:31:52 +0200179 # Additional excluded macros.
Gilles Peskine5c196fb2019-05-17 12:04:41 +0200180 _excluded_names = set([
181 # Macros that provide an alternative way to build the same
182 # algorithm as another macro.
183 'PSA_ALG_AEAD_WITH_DEFAULT_TAG_LENGTH',
184 'PSA_ALG_FULL_LENGTH_MAC',
185 # Auxiliary macro whose name doesn't fit the usual patterns for
186 # auxiliary macros.
187 'PSA_ALG_AEAD_WITH_DEFAULT_TAG_LENGTH_CASE',
188 # PSA_ALG_ECDH and PSA_ALG_FFDH are excluded for now as the script
189 # currently doesn't support them.
190 'PSA_ALG_ECDH',
191 'PSA_ALG_FFDH',
192 # Deprecated aliases.
193 'PSA_ERROR_UNKNOWN_ERROR',
194 'PSA_ERROR_OCCUPIED_SLOT',
195 'PSA_ERROR_EMPTY_SLOT',
196 'PSA_ERROR_INSUFFICIENT_CAPACITY',
Gilles Peskine19835122019-05-17 12:06:55 +0200197 'PSA_ERROR_TAMPERING_DETECTED',
Gilles Peskine5c196fb2019-05-17 12:04:41 +0200198 ])
Gilles Peskine24827022018-09-25 18:49:23 +0200199 def parse_header_line(self, line):
Gilles Peskinea3b93ff2019-06-03 11:23:56 +0200200 """Parse a C header line, looking for "#define PSA_xxx"."""
Gilles Peskine42a0a0a2019-05-27 18:29:47 +0200201 m = re.match(self._header_line_re, line)
Gilles Peskine24827022018-09-25 18:49:23 +0200202 if not m:
203 return
204 name = m.group(1)
Gilles Peskine42a0a0a2019-05-27 18:29:47 +0200205 if re.search(self._excluded_name_re, name) or \
206 name in self._excluded_names:
Gilles Peskine24827022018-09-25 18:49:23 +0200207 return
208 dest = self.table_by_prefix.get(m.group(2))
209 if dest is None:
210 return
211 dest.add(name)
212 if m.group(3):
Gilles Peskine42a0a0a2019-05-27 18:29:47 +0200213 self.argspecs[name] = self._argument_split(m.group(3))
Gilles Peskine24827022018-09-25 18:49:23 +0200214
215 def parse_header(self, filename):
Gilles Peskinea3b93ff2019-06-03 11:23:56 +0200216 """Parse a C header file, looking for "#define PSA_xxx"."""
Gilles Peskinea0a315c2018-10-19 11:27:10 +0200217 with read_file_lines(filename) as lines:
218 for line in lines:
Gilles Peskine24827022018-09-25 18:49:23 +0200219 self.parse_header_line(line)
220
221 def add_test_case_line(self, function, argument):
Gilles Peskinea3b93ff2019-06-03 11:23:56 +0200222 """Parse a test case data line, looking for algorithm metadata tests."""
Gilles Peskine24827022018-09-25 18:49:23 +0200223 if function.endswith('_algorithm'):
Darryl Greenb8fe0682019-02-06 13:21:31 +0000224 # As above, ECDH and FFDH algorithms are excluded for now.
225 # Support for them will be added in the future.
Darryl Greenec079502019-01-29 15:48:00 +0000226 if 'ECDH' in argument or 'FFDH' in argument:
227 return
Gilles Peskine24827022018-09-25 18:49:23 +0200228 self.algorithms.add(argument)
229 if function == 'hash_algorithm':
230 self.hash_algorithms.add(argument)
Gilles Peskine434899f2018-10-19 11:30:26 +0200231 elif function in ['mac_algorithm', 'hmac_algorithm']:
232 self.mac_algorithms.add(argument)
233 elif function == 'aead_algorithm':
234 self.aead_algorithms.add(argument)
Gilles Peskine24827022018-09-25 18:49:23 +0200235 elif function == 'key_type':
236 self.key_types.add(argument)
237 elif function == 'ecc_key_types':
238 self.ecc_curves.add(argument)
Gilles Peskinedcaefae2019-05-16 12:55:35 +0200239 elif function == 'dh_key_types':
240 self.dh_groups.add(argument)
Gilles Peskine24827022018-09-25 18:49:23 +0200241
242 # Regex matching a *.data line containing a test function call and
243 # its arguments. The actual definition is partly positional, but this
244 # regex is good enough in practice.
Gilles Peskine42a0a0a2019-05-27 18:29:47 +0200245 _test_case_line_re = re.compile(r'(?!depends_on:)(\w+):([^\n :][^:\n]*)')
Gilles Peskine24827022018-09-25 18:49:23 +0200246 def parse_test_cases(self, filename):
Gilles Peskinea3b93ff2019-06-03 11:23:56 +0200247 """Parse a test case file (*.data), looking for algorithm metadata tests."""
Gilles Peskinea0a315c2018-10-19 11:27:10 +0200248 with read_file_lines(filename) as lines:
249 for line in lines:
Gilles Peskine42a0a0a2019-05-27 18:29:47 +0200250 m = re.match(self._test_case_line_re, line)
Gilles Peskine24827022018-09-25 18:49:23 +0200251 if m:
252 self.add_test_case_line(m.group(1), m.group(2))
253
254def gather_inputs(headers, test_suites):
Gilles Peskinea3b93ff2019-06-03 11:23:56 +0200255 """Read the list of inputs to test psa_constant_names with."""
Gilles Peskine24827022018-09-25 18:49:23 +0200256 inputs = Inputs()
257 for header in headers:
258 inputs.parse_header(header)
259 for test_cases in test_suites:
260 inputs.parse_test_cases(test_cases)
261 inputs.gather_arguments()
262 return inputs
263
264def remove_file_if_exists(filename):
Gilles Peskinea3b93ff2019-06-03 11:23:56 +0200265 """Remove the specified file, ignoring errors."""
Gilles Peskine24827022018-09-25 18:49:23 +0200266 if not filename:
267 return
268 try:
269 os.remove(filename)
Gilles Peskine54f54452019-05-27 18:31:59 +0200270 except OSError:
Gilles Peskine24827022018-09-25 18:49:23 +0200271 pass
272
Gilles Peskineb86b6d32019-11-21 17:26:10 +0100273def run_c(type_word, expressions, include_path=None, keep_c=False):
Gilles Peskine5a994c12019-11-21 16:46:51 +0100274 """Generate and run a program to print out numerical values for expressions."""
Gilles Peskineb86b6d32019-11-21 17:26:10 +0100275 if include_path is None:
276 include_path = []
Gilles Peskine42a0a0a2019-05-27 18:29:47 +0200277 if type_word == 'status':
Gilles Peskinec4cd2ad2019-02-13 18:42:53 +0100278 cast_to = 'long'
279 printf_format = '%ld'
280 else:
281 cast_to = 'unsigned long'
282 printf_format = '0x%08lx'
Gilles Peskine24827022018-09-25 18:49:23 +0200283 c_name = None
284 exe_name = None
285 try:
Gilles Peskine42a0a0a2019-05-27 18:29:47 +0200286 c_fd, c_name = tempfile.mkstemp(prefix='tmp-{}-'.format(type_word),
Gilles Peskine95ab71a2019-01-04 19:46:59 +0100287 suffix='.c',
Gilles Peskine24827022018-09-25 18:49:23 +0200288 dir='programs/psa')
289 exe_suffix = '.exe' if platform.system() == 'Windows' else ''
290 exe_name = c_name[:-2] + exe_suffix
291 remove_file_if_exists(exe_name)
292 c_file = os.fdopen(c_fd, 'w', encoding='ascii')
Gilles Peskine95ab71a2019-01-04 19:46:59 +0100293 c_file.write('/* Generated by test_psa_constant_names.py for {} values */'
Gilles Peskine42a0a0a2019-05-27 18:29:47 +0200294 .format(type_word))
Gilles Peskine95ab71a2019-01-04 19:46:59 +0100295 c_file.write('''
Gilles Peskine24827022018-09-25 18:49:23 +0200296#include <stdio.h>
297#include <psa/crypto.h>
298int main(void)
299{
300''')
Gilles Peskine5a994c12019-11-21 16:46:51 +0100301 for expr in expressions:
Gilles Peskinec4cd2ad2019-02-13 18:42:53 +0100302 c_file.write(' printf("{}\\n", ({}) {});\n'
Gilles Peskine5a994c12019-11-21 16:46:51 +0100303 .format(printf_format, cast_to, expr))
Gilles Peskine24827022018-09-25 18:49:23 +0200304 c_file.write(''' return 0;
305}
306''')
307 c_file.close()
308 cc = os.getenv('CC', 'cc')
309 subprocess.check_call([cc] +
Gilles Peskineb86b6d32019-11-21 17:26:10 +0100310 ['-I' + dir for dir in include_path] +
Gilles Peskine24827022018-09-25 18:49:23 +0200311 ['-o', exe_name, c_name])
Gilles Peskineb86b6d32019-11-21 17:26:10 +0100312 if keep_c:
Gilles Peskinecf9c18e2018-10-19 11:28:42 +0200313 sys.stderr.write('List of {} tests kept at {}\n'
Gilles Peskine42a0a0a2019-05-27 18:29:47 +0200314 .format(type_word, c_name))
Gilles Peskinecf9c18e2018-10-19 11:28:42 +0200315 else:
316 os.remove(c_name)
Gilles Peskine24827022018-09-25 18:49:23 +0200317 output = subprocess.check_output([exe_name])
318 return output.decode('ascii').strip().split('\n')
319 finally:
320 remove_file_if_exists(exe_name)
321
Gilles Peskine42a0a0a2019-05-27 18:29:47 +0200322NORMALIZE_STRIP_RE = re.compile(r'\s+')
Gilles Peskine24827022018-09-25 18:49:23 +0200323def normalize(expr):
Gilles Peskinea3b93ff2019-06-03 11:23:56 +0200324 """Normalize the C expression so as not to care about trivial differences.
Gilles Peskine4408dfd2019-11-21 17:16:21 +0100325
Gilles Peskinea3b93ff2019-06-03 11:23:56 +0200326 Currently "trivial differences" means whitespace.
327 """
Gilles Peskine5a6dc892019-11-21 16:48:07 +0100328 return re.sub(NORMALIZE_STRIP_RE, '', expr)
Gilles Peskine24827022018-09-25 18:49:23 +0200329
Gilles Peskineb86b6d32019-11-21 17:26:10 +0100330def collect_values(inputs, type_word, include_path=None, keep_c=False):
Gilles Peskinec2317112019-11-21 17:17:39 +0100331 """Generate expressions using known macro names and calculate their values.
332
333 Return a list of pairs of (expr, value) where expr is an expression and
334 value is a string representation of its integer value.
335 """
336 names = inputs.get_names(type_word)
337 expressions = sorted(inputs.generate_expressions(names))
Gilles Peskineb86b6d32019-11-21 17:26:10 +0100338 values = run_c(type_word, expressions,
339 include_path=include_path, keep_c=keep_c)
Gilles Peskinec2317112019-11-21 17:17:39 +0100340 return expressions, values
341
Gilles Peskine24609332019-11-21 17:44:21 +0100342class Tests:
343 """An object representing tests and their results."""
Gilles Peskine4408dfd2019-11-21 17:16:21 +0100344
Gilles Peskinea5000f12019-11-21 17:51:11 +0100345 Error = namedtuple('Error',
346 ['type', 'expression', 'value', 'output'])
347
Gilles Peskine24609332019-11-21 17:44:21 +0100348 def __init__(self, options):
349 self.options = options
350 self.count = 0
351 self.errors = []
Gilles Peskine4408dfd2019-11-21 17:16:21 +0100352
Gilles Peskine24609332019-11-21 17:44:21 +0100353 def run_one(self, inputs, type_word):
354 """Test psa_constant_names for the specified type.
Gilles Peskine24827022018-09-25 18:49:23 +0200355
Gilles Peskine24609332019-11-21 17:44:21 +0100356 Run the program on the names for this type.
357 Use the inputs to figure out what arguments to pass to macros that
358 take arguments.
359 """
360 expressions, values = collect_values(inputs, type_word,
361 include_path=self.options.include,
362 keep_c=self.options.keep_c)
363 output = subprocess.check_output([self.options.program, type_word] +
364 values)
365 outputs = output.decode('ascii').strip().split('\n')
366 self.count += len(expressions)
367 for expr, value, output in zip(expressions, values, outputs):
368 if normalize(expr) != normalize(output):
Gilles Peskinea5000f12019-11-21 17:51:11 +0100369 self.errors.append(self.Error(type=type_word,
370 expression=expr,
371 value=value,
372 output=output))
Gilles Peskine24827022018-09-25 18:49:23 +0200373
Gilles Peskine24609332019-11-21 17:44:21 +0100374 def run_all(self, inputs):
375 """Run psa_constant_names on all the gathered inputs."""
376 for type_word in ['status', 'algorithm', 'ecc_curve', 'dh_group',
377 'key_type', 'key_usage']:
378 self.run_one(inputs, type_word)
Gilles Peskine4408dfd2019-11-21 17:16:21 +0100379
Gilles Peskine24609332019-11-21 17:44:21 +0100380 def report(self, out):
381 """Describe each case where the output is not as expected.
382
383 Write the errors to ``out``.
384 Also write a total.
385 """
Gilles Peskinea5000f12019-11-21 17:51:11 +0100386 for error in self.errors:
Gilles Peskine24609332019-11-21 17:44:21 +0100387 out.write('For {} "{}", got "{}" (value: {})\n'
Gilles Peskinea5000f12019-11-21 17:51:11 +0100388 .format(error.type, error.expression,
389 error.output, error.value))
Gilles Peskine24609332019-11-21 17:44:21 +0100390 out.write('{} test cases'.format(self.count))
391 if self.errors:
392 out.write(', {} FAIL\n'.format(len(self.errors)))
393 else:
394 out.write(' PASS\n')
Gilles Peskine24827022018-09-25 18:49:23 +0200395
Gilles Peskine69f93b52019-11-21 16:49:50 +0100396HEADERS = ['psa/crypto.h', 'psa/crypto_extra.h', 'psa/crypto_values.h']
397TEST_SUITES = ['tests/suites/test_suite_psa_crypto_metadata.data']
398
Gilles Peskine54f54452019-05-27 18:31:59 +0200399def main():
Gilles Peskine24827022018-09-25 18:49:23 +0200400 parser = argparse.ArgumentParser(description=globals()['__doc__'])
401 parser.add_argument('--include', '-I',
402 action='append', default=['include'],
403 help='Directory for header files')
Gilles Peskinecf9c18e2018-10-19 11:28:42 +0200404 parser.add_argument('--keep-c',
405 action='store_true', dest='keep_c', default=False,
406 help='Keep the intermediate C file')
407 parser.add_argument('--no-keep-c',
408 action='store_false', dest='keep_c',
409 help='Don\'t keep the intermediate C file (default)')
Gilles Peskine8f5a5012019-11-21 16:49:10 +0100410 parser.add_argument('--program',
411 default='programs/psa/psa_constant_names',
412 help='Program to test')
Gilles Peskine24827022018-09-25 18:49:23 +0200413 options = parser.parse_args()
Gilles Peskine69f93b52019-11-21 16:49:50 +0100414 headers = [os.path.join(options.include[0], h) for h in HEADERS]
415 inputs = gather_inputs(headers, TEST_SUITES)
Gilles Peskine24609332019-11-21 17:44:21 +0100416 tests = Tests(options)
417 tests.run_all(inputs)
418 tests.report(sys.stdout)
419 if tests.errors:
Gilles Peskine24827022018-09-25 18:49:23 +0200420 exit(1)
Gilles Peskine54f54452019-05-27 18:31:59 +0200421
422if __name__ == '__main__':
423 main()