From 567840e3352a17958d43f551b196421cafa3a787 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Tue, 25 Sep 2018 18:27:53 +0200 Subject: [PATCH 01/25] Support multiple values on the command line --- programs/psa/psa_constant_names.c | 81 +++++++++++++++++++++---------- 1 file changed, 56 insertions(+), 25 deletions(-) diff --git a/programs/psa/psa_constant_names.c b/programs/psa/psa_constant_names.c index dd19677c4b..f551e5aead 100644 --- a/programs/psa/psa_constant_names.c +++ b/programs/psa/psa_constant_names.c @@ -138,7 +138,7 @@ static int psa_snprint_ecc_curve(char *buffer, size_t buffer_size, static void usage(const char *program_name) { - printf("Usage: %s TYPE VALUE\n", + printf("Usage: %s TYPE VALUE [VALUE...]\n", program_name == NULL ? "psa_constant_names" : program_name); printf("Print the symbolic name whose numerical value is VALUE in TYPE.\n"); printf("Supported types (with = between aliases):\n"); @@ -149,11 +149,18 @@ static void usage(const char *program_name) printf(" error=status Status code (psa_status_t)\n"); } +typedef enum { + TYPE_STATUS, + TYPE_ALGORITHM, + TYPE_ECC_CURVE, + TYPE_KEY_TYPE, + TYPE_KEY_USAGE, +} value_type; + int main(int argc, char *argv[]) { - char buffer[200]; - unsigned long value; - char *end; + value_type type; + int i; if (argc <= 1 || !strcmp(argv[1], "help") || @@ -162,31 +169,55 @@ int main(int argc, char *argv[]) usage(argv[0]); return EXIT_FAILURE; } - if (argc != 3) { - usage(argv[0]); - return EXIT_FAILURE; - } - value = strtoul(argv[2], &end, 0); - if (*end) { - printf("Non-numeric value: %s\n", argv[2]); - return EXIT_FAILURE; - } - if (!strcmp(argv[1], "error") || !strcmp(argv[1], "status")) - psa_snprint_status(buffer, sizeof(buffer), (psa_status_t) value); - else if (!strcmp(argv[1], "alg") || !strcmp(argv[1], "algorithm")) - psa_snprint_algorithm(buffer, sizeof(buffer), (psa_algorithm_t) value); - else if (!strcmp(argv[1], "curve") || !strcmp(argv[1], "ecc_curve")) - psa_snprint_ecc_curve(buffer, sizeof(buffer), (psa_ecc_curve_t) value); - else if (!strcmp(argv[1], "type") || !strcmp(argv[1], "key_type")) - psa_snprint_key_type(buffer, sizeof(buffer), (psa_key_type_t) value); - else if (!strcmp(argv[1], "usage") || !strcmp(argv[1], "key_usage")) - psa_snprint_key_usage(buffer, sizeof(buffer), (psa_key_usage_t) value); - else { + if (!strcmp(argv[1], "error") || !strcmp(argv[1], "status")) { + type = TYPE_STATUS; + } else if (!strcmp(argv[1], "alg") || !strcmp(argv[1], "algorithm")) { + type = TYPE_ALGORITHM; + } else if (!strcmp(argv[1], "curve") || !strcmp(argv[1], "ecc_curve")) { + type = TYPE_ECC_CURVE; + } else if (!strcmp(argv[1], "type") || !strcmp(argv[1], "key_type")) { + type = TYPE_KEY_TYPE; + } else if (!strcmp(argv[1], "usage") || !strcmp(argv[1], "key_usage")) { + type = TYPE_KEY_USAGE; + } else { printf("Unknown type: %s\n", argv[1]); return EXIT_FAILURE; } - puts(buffer); + for (i = 2; i < argc; i++) { + char buffer[200]; + char *end; + unsigned long value = strtoul(argv[i], &end, 0); + if (*end) { + printf("Non-numeric value: %s\n", argv[i]); + return EXIT_FAILURE; + } + + switch (type) { + case TYPE_STATUS: + psa_snprint_status(buffer, sizeof(buffer), + (psa_status_t) value); + break; + case TYPE_ALGORITHM: + psa_snprint_algorithm(buffer, sizeof(buffer), + (psa_algorithm_t) value); + break; + case TYPE_ECC_CURVE: + psa_snprint_ecc_curve(buffer, sizeof(buffer), + (psa_ecc_curve_t) value); + break; + case TYPE_KEY_TYPE: + psa_snprint_key_type(buffer, sizeof(buffer), + (psa_key_type_t) value); + break; + case TYPE_KEY_USAGE: + psa_snprint_key_usage(buffer, sizeof(buffer), + (psa_key_usage_t) value); + break; + } + puts(buffer); + } + return EXIT_SUCCESS; } From 2482702d15f60982a44f3cea664db78bd4d90c67 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Tue, 25 Sep 2018 18:49:23 +0200 Subject: [PATCH 02/25] Test program for psa_constant_names Test psa_constant_names on many inputs. For each input, find out the numerical value by compiling and running a C program, pass the numerical value to psa_constant_names and compare the output with the original input. Gather inputs by parsing psa/crypto.h and test_suite_psa_crypto_metadata.data. For macros that take an argument, list some possible arguments using the parsed data. --- tests/scripts/test_psa_constant_names.py | 241 +++++++++++++++++++++++ 1 file changed, 241 insertions(+) create mode 100755 tests/scripts/test_psa_constant_names.py diff --git a/tests/scripts/test_psa_constant_names.py b/tests/scripts/test_psa_constant_names.py new file mode 100755 index 0000000000..59292428fa --- /dev/null +++ b/tests/scripts/test_psa_constant_names.py @@ -0,0 +1,241 @@ +#!/usr/bin/env python3 +'''Test the program psa_constant_names. +Gather constant names from header files and test cases. Compile a C program +to print out their numerical values, feed these numerical values to +psa_constant_names, and check that the output is the original name. +Return 0 if all test cases pass, 1 if the output was not always as expected, +or 1 (with a Python backtrace) if there was an operational error.''' + +import argparse +import itertools +import os +import platform +import re +import subprocess +import sys +import tempfile + +class Inputs: + '''Accumulate information about macros to test. +This includes macro names as well as information about their arguments +when applicable.''' + def __init__(self): + # Sets of names per type + self.statuses = set(['PSA_SUCCESS']) + self.algorithms = set(['0xffffffff']) + self.ecc_curves = set(['0xffff']) + self.key_types = set(['0xffffffff']) + self.key_usage_flags = set(['0x80000000']) + # Hard-coded value for an unknown hash algorithm + self.hash_algorithms = set(['0x010000ff']) + # Identifier prefixes + self.table_by_prefix = { + 'ERROR': self.statuses, + 'ALG': self.algorithms, + 'CURVE': self.ecc_curves, + 'KEY_TYPE': self.key_types, + 'KEY_USAGE': self.key_usage_flags, + } + # macro name -> list of argument names + self.argspecs = {} + # argument name -> list of values + self.arguments_for = {} + + def gather_arguments(self): + '''Populate the list of values for macro arguments. +Call this after parsing all the inputs.''' + self.arguments_for['hash_alg'] = sorted(self.hash_algorithms) + self.arguments_for['curve'] = sorted(self.ecc_curves) + + def format_arguments(self, name, arguments): + '''Format a macro call with arguments..''' + return name + '(' + ', '.join(arguments) + ')' + + def distribute_arguments(self, name): + '''Generate macro calls with each tested argument set. +If name is a macro without arguments, just yield "name". +If name is a macro with arguments, yield a series of "name(arg1,...,argN)" +where each argument takes each possible value at least once.''' + if name not in self.argspecs: + yield name + return + argspec = self.argspecs[name] + if argspec == []: + yield name + '()' + return + argument_lists = [self.arguments_for[arg] for arg in argspec] + arguments = [values[0] for values in argument_lists] + yield self.format_arguments(name, arguments) + for i in range(len(arguments)): + for value in argument_lists[i][1:]: + arguments[i] = value + yield self.format_arguments(name, arguments) + arguments[i] = argument_lists[0] + + # Regex for interesting header lines. + # Groups: 1=macro name, 2=type, 3=argument list (optional). + header_line_re = \ + re.compile(r'#define +' + + r'(PSA_((?:KEY_)?[A-Z]+)_\w+)' + + r'(?:\(([^\n()]*)\))?') + # Regex of macro names to exclude. + excluded_name_re = re.compile('_(?:GET|IS|OF)_|_(?:BASE|FLAG|MASK)\Z') + argument_split_re = re.compile(r' *, *') + def parse_header_line(self, line): + '''Parse a C header line, looking for "#define PSA_xxx".''' + m = re.match(self.header_line_re, line) + if not m: + return + name = m.group(1) + if re.search(self.excluded_name_re, name): + return + dest = self.table_by_prefix.get(m.group(2)) + if dest is None: + return + dest.add(name) + if m.group(3): + self.argspecs[name] = re.split(self.argument_split_re, m.group(3)) + + def parse_header(self, filename): + '''Parse a C header file, looking for "#define PSA_xxx".''' + with open(filename, 'r') as input: + for line in input: + self.parse_header_line(line) + + def add_test_case_line(self, function, argument): + '''Parse a test case data line, looking for algorithm metadata tests.''' + if function.endswith('_algorithm'): + self.algorithms.add(argument) + if function == 'hash_algorithm': + self.hash_algorithms.add(argument) + elif function == 'key_type': + self.key_types.add(argument) + elif function == 'ecc_key_types': + self.ecc_curves.add(argument) + + # Regex matching a *.data line containing a test function call and + # its arguments. The actual definition is partly positional, but this + # regex is good enough in practice. + test_case_line_re = re.compile('(?!depends_on:)(\w+):([^\n :][^:\n]*)') + def parse_test_cases(self, filename): + '''Parse a test case file (*.data), looking for algorithm metadata tests.''' + with open(filename, 'r') as input: + for line in input: + m = re.match(self.test_case_line_re, line) + if m: + self.add_test_case_line(m.group(1), m.group(2)) + +def gather_inputs(headers, test_suites): + '''Read the list of inputs to test psa_constant_names with.''' + inputs = Inputs() + for header in headers: + inputs.parse_header(header) + for test_cases in test_suites: + inputs.parse_test_cases(test_cases) + inputs.gather_arguments() + return inputs + +def remove_file_if_exists(filename): + '''Remove the specified file, ignoring errors.''' + if not filename: + return + try: + os.remove(filename) + except: + pass + +def run_c(options, names): + '''Generate and run a program to print out numerical values for names.''' + c_name = None + exe_name = None + try: + c_fd, c_name = tempfile.mkstemp(suffix='.c', + dir='programs/psa') + exe_suffix = '.exe' if platform.system() == 'Windows' else '' + exe_name = c_name[:-2] + exe_suffix + remove_file_if_exists(exe_name) + c_file = os.fdopen(c_fd, 'w', encoding='ascii') + c_file.write('''/* Generated by test_psa_constant_names.py */ +#include +#include +int main(void) +{ +''') + for name in names: + c_file.write(' printf("0x%08x\\n", {});\n'.format(name)) + c_file.write(''' return 0; +} +''') + c_file.close() + cc = os.getenv('CC', 'cc') + subprocess.check_call([cc] + + ['-I' + dir for dir in options.include] + + ['-o', exe_name, c_name]) + os.remove(c_name) + output = subprocess.check_output([exe_name]) + return output.decode('ascii').strip().split('\n') + finally: + remove_file_if_exists(exe_name) + +normalize_strip_re = re.compile(r'\s+') +def normalize(expr): + '''Normalize the C expression so as not to care about trivial differences. +Currently "trivial differences" means whitespace.''' + expr = re.sub(normalize_strip_re, '', expr, len(expr)) + return expr.strip().split('\n') + +def do_test(options, inputs, type, names): + '''Test psa_constant_names for the specified type. +Run program on names. +Use inputs to figure out what arguments to pass to macros that take arguments.''' + names = sorted(itertools.chain(*map(inputs.distribute_arguments, names))) + values = run_c(options, names) + output = subprocess.check_output([options.program, type] + values) + outputs = output.decode('ascii').strip().split('\n') + errors = [(type, name, value, output) + for (name, value, output) in zip(names, values, outputs) + if normalize(name) != normalize(output)] + return len(names), errors + +def report_errors(errors): + '''Describe each case where the output is not as expected.''' + for type, name, value, output in errors: + print('For {} "{}", got "{}" (value: {})' + .format(type, name, output, value)) + +def run_tests(options, inputs): + '''Run psa_constant_names on all the gathered inputs. +Return a tuple (count, errors) where count is the total number of inputs +that were tested and errors is the list of cases where the output was +not as expected.''' + count = 0 + errors = [] + for type, names in [('status', inputs.statuses), + ('algorithm', inputs.algorithms), + ('ecc_curve', inputs.ecc_curves), + ('key_type', inputs.key_types), + ('key_usage', inputs.key_usage_flags)]: + c, e = do_test(options, inputs, type, names) + count += c + errors += e + return count, errors + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=globals()['__doc__']) + parser.add_argument('--include', '-I', + action='append', default=['include'], + help='Directory for header files') + parser.add_argument('--program', + default='programs/psa/psa_constant_names', + help='Program to test') + options = parser.parse_args() + headers = [os.path.join(options.include[0], 'psa/crypto.h')] + test_suites = ['tests/suites/test_suite_psa_crypto_metadata.data'] + inputs = gather_inputs(headers, test_suites) + count, errors = run_tests(options, inputs) + report_errors(errors) + if errors == []: + print('{} test cases PASS'.format(count)) + else: + print('{} test cases, {} FAIL'.format(count, len(errors))) + exit(1) From 377c6832a29178b1cf261a2efc2566e6f490b7a9 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Tue, 25 Sep 2018 19:31:45 +0200 Subject: [PATCH 03/25] Test psa_constant_names in all.sh --- tests/scripts/all.sh | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/scripts/all.sh b/tests/scripts/all.sh index 11d10a3676..84e4490100 100755 --- a/tests/scripts/all.sh +++ b/tests/scripts/all.sh @@ -604,6 +604,15 @@ component_test_default_cmake_gcc_asan () { if_build_succeeded tests/compat.sh } +component_test_psa_constant_names () { + msg "build: cmake, gcc, ASan" # ~ 1 min 50s + CC=gcc cmake -D CMAKE_BUILD_TYPE:String=Asan . + make + + msg "test psa_constant_names" # ~ 1s + record_status tests/scripts/test_psa_constant_names.py +} + component_test_ref_configs () { msg "test/build: ref-configs (ASan build)" # ~ 6 min 20s CC=gcc cmake -D CMAKE_BUILD_TYPE:String=Asan . From a0a315c8157e98e075660a5c82f05cdb9e8f3409 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Fri, 19 Oct 2018 11:27:10 +0200 Subject: [PATCH 04/25] Add location information to input processing exceptions If parsing fails, report the input file name and line number. If distribute_arguments fails, report for what name. --- tests/scripts/test_psa_constant_names.py | 75 ++++++++++++++++++------ 1 file changed, 56 insertions(+), 19 deletions(-) diff --git a/tests/scripts/test_psa_constant_names.py b/tests/scripts/test_psa_constant_names.py index 59292428fa..6891ecc927 100755 --- a/tests/scripts/test_psa_constant_names.py +++ b/tests/scripts/test_psa_constant_names.py @@ -15,6 +15,40 @@ import subprocess import sys import tempfile +class ReadFileLineException(Exception): + def __init__(self, filename, line_number): + message = 'in {} at {}'.format(filename, line_number) + super(ReadFileLineException, self).__init__(message) + self.filename = filename + self.line_number = line_number + +class read_file_lines: + '''Context manager to read a text file line by line. +with read_file_lines(filename) as lines: + for line in lines: + process(line) +is equivalent to +with open(filename, 'r') as input_file: + for line in input_file: + process(line) +except that if process(line) raises an exception, then the read_file_lines +snippet annotates the exception with the file name and line number.''' + def __init__(self, filename): + self.filename = filename + self.line_number = 'entry' + def __enter__(self): + self.generator = enumerate(open(self.filename, 'r')) + return self + def __iter__(self): + for line_number, content in self.generator: + self.line_number = line_number + yield content + self.line_number = 'exit' + def __exit__(self, type, value, traceback): + if type is not None: + raise ReadFileLineException(self.filename, self.line_number) \ + from value + class Inputs: '''Accumulate information about macros to test. This includes macro names as well as information about their arguments @@ -56,21 +90,24 @@ Call this after parsing all the inputs.''' If name is a macro without arguments, just yield "name". If name is a macro with arguments, yield a series of "name(arg1,...,argN)" where each argument takes each possible value at least once.''' - if name not in self.argspecs: - yield name - return - argspec = self.argspecs[name] - if argspec == []: - yield name + '()' - return - argument_lists = [self.arguments_for[arg] for arg in argspec] - arguments = [values[0] for values in argument_lists] - yield self.format_arguments(name, arguments) - for i in range(len(arguments)): - for value in argument_lists[i][1:]: - arguments[i] = value - yield self.format_arguments(name, arguments) - arguments[i] = argument_lists[0] + try: + if name not in self.argspecs: + yield name + return + argspec = self.argspecs[name] + if argspec == []: + yield name + '()' + return + argument_lists = [self.arguments_for[arg] for arg in argspec] + arguments = [values[0] for values in argument_lists] + yield self.format_arguments(name, arguments) + for i in range(len(arguments)): + for value in argument_lists[i][1:]: + arguments[i] = value + yield self.format_arguments(name, arguments) + arguments[i] = argument_lists[0] + except BaseException as e: + raise Exception('distribute_arguments({})'.format(name)) from e # Regex for interesting header lines. # Groups: 1=macro name, 2=type, 3=argument list (optional). @@ -98,8 +135,8 @@ where each argument takes each possible value at least once.''' def parse_header(self, filename): '''Parse a C header file, looking for "#define PSA_xxx".''' - with open(filename, 'r') as input: - for line in input: + with read_file_lines(filename) as lines: + for line in lines: self.parse_header_line(line) def add_test_case_line(self, function, argument): @@ -119,8 +156,8 @@ where each argument takes each possible value at least once.''' test_case_line_re = re.compile('(?!depends_on:)(\w+):([^\n :][^:\n]*)') def parse_test_cases(self, filename): '''Parse a test case file (*.data), looking for algorithm metadata tests.''' - with open(filename, 'r') as input: - for line in input: + with read_file_lines(filename) as lines: + for line in lines: m = re.match(self.test_case_line_re, line) if m: self.add_test_case_line(m.group(1), m.group(2)) From cf9c18e6961ceebd2b5f61a15b705ff0abdb66dc Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Fri, 19 Oct 2018 11:28:42 +0200 Subject: [PATCH 05/25] Add option to keep the temporary C files Useful for debugging and for reviewing what test cases are generated. --- tests/scripts/test_psa_constant_names.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/tests/scripts/test_psa_constant_names.py b/tests/scripts/test_psa_constant_names.py index 6891ecc927..20212936d6 100755 --- a/tests/scripts/test_psa_constant_names.py +++ b/tests/scripts/test_psa_constant_names.py @@ -181,7 +181,7 @@ def remove_file_if_exists(filename): except: pass -def run_c(options, names): +def run_c(options, type, names): '''Generate and run a program to print out numerical values for names.''' c_name = None exe_name = None @@ -208,7 +208,11 @@ int main(void) subprocess.check_call([cc] + ['-I' + dir for dir in options.include] + ['-o', exe_name, c_name]) - os.remove(c_name) + if options.keep_c: + sys.stderr.write('List of {} tests kept at {}\n' + .format(type, c_name)) + else: + os.remove(c_name) output = subprocess.check_output([exe_name]) return output.decode('ascii').strip().split('\n') finally: @@ -226,7 +230,7 @@ def do_test(options, inputs, type, names): Run program on names. Use inputs to figure out what arguments to pass to macros that take arguments.''' names = sorted(itertools.chain(*map(inputs.distribute_arguments, names))) - values = run_c(options, names) + values = run_c(options, type, names) output = subprocess.check_output([options.program, type] + values) outputs = output.decode('ascii').strip().split('\n') errors = [(type, name, value, output) @@ -265,6 +269,12 @@ if __name__ == '__main__': parser.add_argument('--program', default='programs/psa/psa_constant_names', help='Program to test') + parser.add_argument('--keep-c', + action='store_true', dest='keep_c', default=False, + help='Keep the intermediate C file') + parser.add_argument('--no-keep-c', + action='store_false', dest='keep_c', + help='Don\'t keep the intermediate C file (default)') options = parser.parse_args() headers = [os.path.join(options.include[0], 'psa/crypto.h')] test_suites = ['tests/suites/test_suite_psa_crypto_metadata.data'] From f96ed6615c9f51c2b55320ebf02fa145e55b70f2 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Fri, 19 Oct 2018 11:29:56 +0200 Subject: [PATCH 06/25] Fix bug in distribute_arguments for multi-argument macros --- tests/scripts/test_psa_constant_names.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/scripts/test_psa_constant_names.py b/tests/scripts/test_psa_constant_names.py index 20212936d6..9c674e5da9 100755 --- a/tests/scripts/test_psa_constant_names.py +++ b/tests/scripts/test_psa_constant_names.py @@ -105,7 +105,7 @@ where each argument takes each possible value at least once.''' for value in argument_lists[i][1:]: arguments[i] = value yield self.format_arguments(name, arguments) - arguments[i] = argument_lists[0] + arguments[i] = argument_lists[0][0] except BaseException as e: raise Exception('distribute_arguments({})'.format(name)) from e From 434899fccde3f24fad0e17b0c4f0d3c71e6e3fe7 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Fri, 19 Oct 2018 11:30:26 +0200 Subject: [PATCH 07/25] Test truncated MAC and AEAD algorithms For MAC and AEAD algorithms, test the algorithm truncated to certain lengths (1 and 63 bytes). --- include/psa/crypto_values.h | 42 ++++++++++++------------ tests/scripts/test_psa_constant_names.py | 18 ++++++++-- 2 files changed, 37 insertions(+), 23 deletions(-) diff --git a/include/psa/crypto_values.h b/include/psa/crypto_values.h index acf856dd1b..2ae72e0633 100644 --- a/include/psa/crypto_values.h +++ b/include/psa/crypto_values.h @@ -766,7 +766,7 @@ * algorithm is considered identical to the untruncated algorithm * for policy comparison purposes. * - * \param alg A MAC algorithm identifier (value of type + * \param mac_alg A MAC algorithm identifier (value of type * #psa_algorithm_t such that #PSA_ALG_IS_MAC(\p alg) * is true). This may be a truncated or untruncated * MAC algorithm. @@ -782,14 +782,14 @@ * MAC algorithm or if \p mac_length is too small or * too large for the specified MAC algorithm. */ -#define PSA_ALG_TRUNCATED_MAC(alg, mac_length) \ - (((alg) & ~PSA_ALG_MAC_TRUNCATION_MASK) | \ +#define PSA_ALG_TRUNCATED_MAC(mac_alg, mac_length) \ + (((mac_alg) & ~PSA_ALG_MAC_TRUNCATION_MASK) | \ ((mac_length) << PSA_MAC_TRUNCATION_OFFSET & PSA_ALG_MAC_TRUNCATION_MASK)) /** Macro to build the base MAC algorithm corresponding to a truncated * MAC algorithm. * - * \param alg A MAC algorithm identifier (value of type + * \param mac_alg A MAC algorithm identifier (value of type * #psa_algorithm_t such that #PSA_ALG_IS_MAC(\p alg) * is true). This may be a truncated or untruncated * MAC algorithm. @@ -798,12 +798,12 @@ * \return Unspecified if \p alg is not a supported * MAC algorithm. */ -#define PSA_ALG_FULL_LENGTH_MAC(alg) \ - ((alg) & ~PSA_ALG_MAC_TRUNCATION_MASK) +#define PSA_ALG_FULL_LENGTH_MAC(mac_alg) \ + ((mac_alg) & ~PSA_ALG_MAC_TRUNCATION_MASK) /** Length to which a MAC algorithm is truncated. * - * \param alg A MAC algorithm identifier (value of type + * \param mac_alg A MAC algorithm identifier (value of type * #psa_algorithm_t such that #PSA_ALG_IS_MAC(\p alg) * is true). * @@ -812,8 +812,8 @@ * \return Unspecified if \p alg is not a supported * MAC algorithm. */ -#define PSA_MAC_TRUNCATED_LENGTH(alg) \ - (((alg) & PSA_ALG_MAC_TRUNCATION_MASK) >> PSA_MAC_TRUNCATION_OFFSET) +#define PSA_MAC_TRUNCATED_LENGTH(mac_alg) \ + (((mac_alg) & PSA_ALG_MAC_TRUNCATION_MASK) >> PSA_MAC_TRUNCATION_OFFSET) #define PSA_ALG_CIPHER_MAC_BASE ((psa_algorithm_t)0x02c00000) #define PSA_ALG_CBC_MAC ((psa_algorithm_t)0x02c00001) @@ -910,7 +910,7 @@ * Depending on the algorithm, the tag length may affect the calculation * of the ciphertext. * - * \param alg A AEAD algorithm identifier (value of type + * \param aead_alg An AEAD algorithm identifier (value of type * #psa_algorithm_t such that #PSA_ALG_IS_AEAD(\p alg) * is true). * \param tag_length Desired length of the authentication tag in bytes. @@ -921,26 +921,26 @@ * AEAD algorithm or if \p tag_length is not valid * for the specified AEAD algorithm. */ -#define PSA_ALG_AEAD_WITH_TAG_LENGTH(alg, tag_length) \ - (((alg) & ~PSA_ALG_AEAD_TAG_LENGTH_MASK) | \ +#define PSA_ALG_AEAD_WITH_TAG_LENGTH(aead_alg, tag_length) \ + (((aead_alg) & ~PSA_ALG_AEAD_TAG_LENGTH_MASK) | \ ((tag_length) << PSA_AEAD_TAG_LENGTH_OFFSET & \ PSA_ALG_AEAD_TAG_LENGTH_MASK)) /** Calculate the corresponding AEAD algorithm with the default tag length. * - * \param alg An AEAD algorithm (\c PSA_ALG_XXX value such that - * #PSA_ALG_IS_AEAD(\p alg) is true). + * \param aead_alg An AEAD algorithm (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_AEAD(\p alg) is true). * - * \return The corresponding AEAD algorithm with the default tag length - * for that algorithm. + * \return The corresponding AEAD algorithm with the default + * tag length for that algorithm. */ -#define PSA_ALG_AEAD_WITH_DEFAULT_TAG_LENGTH(alg) \ +#define PSA_ALG_AEAD_WITH_DEFAULT_TAG_LENGTH(aead_alg) \ ( \ - PSA__ALG_AEAD_WITH_DEFAULT_TAG_LENGTH__CASE(alg, PSA_ALG_CCM) \ - PSA__ALG_AEAD_WITH_DEFAULT_TAG_LENGTH__CASE(alg, PSA_ALG_GCM) \ + PSA__ALG_AEAD_WITH_DEFAULT_TAG_LENGTH__CASE(aead_alg, PSA_ALG_CCM) \ + PSA__ALG_AEAD_WITH_DEFAULT_TAG_LENGTH__CASE(aead_alg, PSA_ALG_GCM) \ 0) -#define PSA__ALG_AEAD_WITH_DEFAULT_TAG_LENGTH__CASE(alg, ref) \ - PSA_ALG_AEAD_WITH_TAG_LENGTH(alg, 0) == \ +#define PSA__ALG_AEAD_WITH_DEFAULT_TAG_LENGTH__CASE(aead_alg, ref) \ + PSA_ALG_AEAD_WITH_TAG_LENGTH(aead_alg, 0) == \ PSA_ALG_AEAD_WITH_TAG_LENGTH(ref, 0) ? \ ref : diff --git a/tests/scripts/test_psa_constant_names.py b/tests/scripts/test_psa_constant_names.py index 9c674e5da9..15884f6fe2 100755 --- a/tests/scripts/test_psa_constant_names.py +++ b/tests/scripts/test_psa_constant_names.py @@ -60,8 +60,13 @@ when applicable.''' self.ecc_curves = set(['0xffff']) self.key_types = set(['0xffffffff']) self.key_usage_flags = set(['0x80000000']) - # Hard-coded value for an unknown hash algorithm + # Hard-coded value for unknown algorithms self.hash_algorithms = set(['0x010000ff']) + self.mac_algorithms = set(['0x02ff00ff']) + # For AEAD algorithms, the only variability is over the tag length, + # and this only applies to known algorithms, so don't test an + # unknown algorithm. + self.aead_algorithms = set() # Identifier prefixes self.table_by_prefix = { 'ERROR': self.statuses, @@ -73,12 +78,17 @@ when applicable.''' # macro name -> list of argument names self.argspecs = {} # argument name -> list of values - self.arguments_for = {} + self.arguments_for = { + 'mac_length': ['1', '63'], + 'tag_length': ['1', '63'], + } def gather_arguments(self): '''Populate the list of values for macro arguments. Call this after parsing all the inputs.''' self.arguments_for['hash_alg'] = sorted(self.hash_algorithms) + self.arguments_for['mac_alg'] = sorted(self.mac_algorithms) + self.arguments_for['aead_alg'] = sorted(self.aead_algorithms) self.arguments_for['curve'] = sorted(self.ecc_curves) def format_arguments(self, name, arguments): @@ -145,6 +155,10 @@ where each argument takes each possible value at least once.''' self.algorithms.add(argument) if function == 'hash_algorithm': self.hash_algorithms.add(argument) + elif function in ['mac_algorithm', 'hmac_algorithm']: + self.mac_algorithms.add(argument) + elif function == 'aead_algorithm': + self.aead_algorithms.add(argument) elif function == 'key_type': self.key_types.add(argument) elif function == 'ecc_key_types': From c68ce9637a1886a5dc7538c93c36c5d15325892d Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Fri, 19 Oct 2018 11:31:52 +0200 Subject: [PATCH 08/25] Exclude full-length-algorithm macros from testing Calls to PSA_ALG_AEAD_WITH_DEFAULT_TAG_LENGTH and PSA_ALG_FULL_LENGTH_MAC are not in canonical form, so exclude them from the list of constructor macros to test. --- tests/scripts/test_psa_constant_names.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/scripts/test_psa_constant_names.py b/tests/scripts/test_psa_constant_names.py index 15884f6fe2..0201755df3 100755 --- a/tests/scripts/test_psa_constant_names.py +++ b/tests/scripts/test_psa_constant_names.py @@ -127,6 +127,9 @@ where each argument takes each possible value at least once.''' r'(?:\(([^\n()]*)\))?') # Regex of macro names to exclude. excluded_name_re = re.compile('_(?:GET|IS|OF)_|_(?:BASE|FLAG|MASK)\Z') + # Additional excluded macros. + excluded_names = set(['PSA_ALG_AEAD_WITH_DEFAULT_TAG_LENGTH', + 'PSA_ALG_FULL_LENGTH_MAC']) argument_split_re = re.compile(r' *, *') def parse_header_line(self, line): '''Parse a C header line, looking for "#define PSA_xxx".''' @@ -134,7 +137,8 @@ where each argument takes each possible value at least once.''' if not m: return name = m.group(1) - if re.search(self.excluded_name_re, name): + if re.search(self.excluded_name_re, name) or \ + name in self.excluded_names: return dest = self.table_by_prefix.get(m.group(2)) if dest is None: From 182c2e98365c20c5e0e8bca3180ec74d26d1d17f Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Fri, 19 Oct 2018 11:33:51 +0200 Subject: [PATCH 09/25] psa_constant_names: fix display for truncated unknown MAC/AEAD algorithm --- scripts/generate_psa_constants.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/generate_psa_constants.py b/scripts/generate_psa_constants.py index 3e4e88b773..bcda282ce5 100755 --- a/scripts/generate_psa_constants.py +++ b/scripts/generate_psa_constants.py @@ -62,7 +62,10 @@ static int psa_snprint_algorithm(char *buffer, size_t buffer_size, } } else if (PSA_ALG_IS_AEAD(alg)) { core_alg = PSA_ALG_AEAD_WITH_DEFAULT_TAG_LENGTH(alg); - if (core_alg != alg) { + if (core_alg == 0) { + /* For unkonwn AEAD algorithms, there is no "default tag length". */ + core_alg = alg; + } else if (core_alg != alg) { append(&buffer, buffer_size, &required_size, "PSA_ALG_AEAD_WITH_TAG_LENGTH(", 29); length_modifier = PSA_AEAD_TAG_LENGTH(alg); @@ -73,7 +76,7 @@ static int psa_snprint_algorithm(char *buffer, size_t buffer_size, default: %(algorithm_code)s{ append_integer(&buffer, buffer_size, &required_size, - "0x%%08lx", (unsigned long) alg); + "0x%%08lx", (unsigned long) core_alg); } break; } From 265a171c52852686a56571969de82304c127ee7a Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Wed, 31 Oct 2018 14:52:28 +0100 Subject: [PATCH 10/25] Error out if a value is out of range psa_status_t is currently a signed type where only non-negative values are used, which makes things a bit awkward. For now, non-negative values trigger an error. This code will need to be revised if we switch to using negative values as error codes. --- programs/psa/psa_constant_names.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/programs/psa/psa_constant_names.c b/programs/psa/psa_constant_names.c index f551e5aead..cc98a95356 100644 --- a/programs/psa/psa_constant_names.c +++ b/programs/psa/psa_constant_names.c @@ -160,6 +160,7 @@ typedef enum { int main(int argc, char *argv[]) { value_type type; + unsigned long max; int i; if (argc <= 1 || @@ -172,14 +173,19 @@ int main(int argc, char *argv[]) if (!strcmp(argv[1], "error") || !strcmp(argv[1], "status")) { type = TYPE_STATUS; + max = 0x7fffffff; /* hard-coded because psa_status_t is signed */ } else if (!strcmp(argv[1], "alg") || !strcmp(argv[1], "algorithm")) { type = TYPE_ALGORITHM; + max = (psa_algorithm_t)( -1 ); } else if (!strcmp(argv[1], "curve") || !strcmp(argv[1], "ecc_curve")) { type = TYPE_ECC_CURVE; + max = (psa_ecc_curve_t)( -1 ); } else if (!strcmp(argv[1], "type") || !strcmp(argv[1], "key_type")) { type = TYPE_KEY_TYPE; + max = (psa_key_type_t)( -1 ); } else if (!strcmp(argv[1], "usage") || !strcmp(argv[1], "key_usage")) { type = TYPE_KEY_USAGE; + max = (psa_key_usage_t)( -1 ); } else { printf("Unknown type: %s\n", argv[1]); return EXIT_FAILURE; @@ -193,6 +199,10 @@ int main(int argc, char *argv[]) printf("Non-numeric value: %s\n", argv[i]); return EXIT_FAILURE; } + if (value > max) { + printf("Value out of range: %s\n", argv[i]); + return EXIT_FAILURE; + } switch (type) { case TYPE_STATUS: From 451e24c1d8c865999e0cb85306da69103686e959 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Wed, 2 Jan 2019 17:24:41 +0100 Subject: [PATCH 11/25] Fix out-of-tree builds that use the PSA crypto API headers --- include/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index 67c66c8c65..4621271762 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -18,4 +18,5 @@ endif(INSTALL_MBEDTLS_HEADERS) # Make config.h available in an out-of-source build. ssl-opt.sh requires it. if (NOT ${CMAKE_CURRENT_BINARY_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR}) link_to_source(mbedtls) + link_to_source(psa) endif() From 738f017c12b4c86a75e0752064340a7afc7914a9 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Wed, 2 Jan 2019 17:25:16 +0100 Subject: [PATCH 12/25] Fix the build of key_ladder_demo under Clang Clang -Wall -Wincompatible-pointer-types-discards-qualifiers said: thou shalt not put a string literal in a non-const char*. --- programs/psa/key_ladder_demo.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/programs/psa/key_ladder_demo.c b/programs/psa/key_ladder_demo.c index 45a9b6fe3f..26fabb52c8 100644 --- a/programs/psa/key_ladder_demo.c +++ b/programs/psa/key_ladder_demo.c @@ -620,9 +620,9 @@ static void usage( void ) int main( int argc, char *argv[] ) { - char *key_file_name = "master.key"; - char *input_file_name = NULL; - char *output_file_name = NULL; + const char *key_file_name = "master.key"; + const char *input_file_name = NULL; + const char *output_file_name = NULL; const char *ladder[MAX_LADDER_DEPTH]; size_t ladder_depth = 0; int i; From f31dbb7bf1c3bd6f1611d289614732976f3d65f9 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Wed, 2 Jan 2019 17:28:46 +0100 Subject: [PATCH 13/25] CMake: build and install key_ladder_demo --- programs/psa/CMakeLists.txt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/programs/psa/CMakeLists.txt b/programs/psa/CMakeLists.txt index a0fe803d7d..b3eedb63af 100644 --- a/programs/psa/CMakeLists.txt +++ b/programs/psa/CMakeLists.txt @@ -1,7 +1,15 @@ add_executable(crypto_examples crypto_examples.c) target_link_libraries(crypto_examples mbedtls) -install(TARGETS crypto_examples +add_executable(key_ladder_demo key_ladder_demo.c) +target_link_libraries(key_ladder_demo mbedtls) + +install(TARGETS + crypto_examples + key_ladder_demo DESTINATION "bin" PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) +install(PROGRAMS + key_ladder_demo.sh + DESTINATION "bin") From 6a78573088635bc3310f25915ce638bf440dc7e7 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Wed, 2 Jan 2019 17:29:04 +0100 Subject: [PATCH 14/25] CMake: psa_constant_names and test_psa_constant_names Build and install psa_constant_names. Make sure that test_psa_constant_names passes in an out-of-tree build. --- programs/psa/CMakeLists.txt | 4 ++++ tests/CMakeLists.txt | 1 + 2 files changed, 5 insertions(+) diff --git a/programs/psa/CMakeLists.txt b/programs/psa/CMakeLists.txt index b3eedb63af..37038c0de6 100644 --- a/programs/psa/CMakeLists.txt +++ b/programs/psa/CMakeLists.txt @@ -4,9 +4,13 @@ target_link_libraries(crypto_examples mbedtls) add_executable(key_ladder_demo key_ladder_demo.c) target_link_libraries(key_ladder_demo mbedtls) +add_executable(psa_constant_names psa_constant_names.c) +target_link_libraries(psa_constant_names mbedtls) + install(TARGETS crypto_examples key_ladder_demo + psa_constant_names DESTINATION "bin" PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 21cdfaba25..271ae2fb2f 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -134,4 +134,5 @@ if (NOT ${CMAKE_CURRENT_BINARY_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR}) link_to_source(data_files) link_to_source(scripts) link_to_source(ssl-opt.sh) + link_to_source(suites) endif() From 6d194bd92b76e1726ea9cebed78a727fa0689961 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Fri, 4 Jan 2019 19:44:59 +0100 Subject: [PATCH 15/25] Read constant names from crypto_extra.h as well as crypto_values.h test_psa_constant_names.py was originally written before the split of crypto.h into crypto_values.h and more, so it now needs to read crypto_values.h as well. In both generate_psa_constants.py and test_psa_constant_names.py, read crypto_extra.h as well. We don't currently define any value there, but it's plausible that we will one day. --- scripts/generate_psa_constants.py | 10 ++++++---- tests/scripts/test_psa_constant_names.py | 3 ++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/scripts/generate_psa_constants.py b/scripts/generate_psa_constants.py index bcda282ce5..f32339fa5f 100755 --- a/scripts/generate_psa_constants.py +++ b/scripts/generate_psa_constants.py @@ -276,10 +276,11 @@ class MacroCollector: data['key_usage_code'] = self.make_key_usage_code() output_file.write(output_template % data) -def generate_psa_constants(header_file_name, output_file_name): +def generate_psa_constants(header_file_names, output_file_name): collector = MacroCollector() - with open(header_file_name) as header_file: - collector.read_file(header_file) + for header_file_name in header_file_names: + with open(header_file_name) as header_file: + collector.read_file(header_file) temp_file_name = output_file_name + '.tmp' with open(temp_file_name, 'w') as output_file: collector.write_file(output_file) @@ -288,5 +289,6 @@ def generate_psa_constants(header_file_name, output_file_name): if __name__ == '__main__': if not os.path.isdir('programs') and os.path.isdir('../programs'): os.chdir('..') - generate_psa_constants('include/psa/crypto_values.h', + generate_psa_constants(['include/psa/crypto_values.h', + 'include/psa/crypto_extra.h'], 'programs/psa/psa_constant_names_generated.c') diff --git a/tests/scripts/test_psa_constant_names.py b/tests/scripts/test_psa_constant_names.py index 0201755df3..d8f00050fe 100755 --- a/tests/scripts/test_psa_constant_names.py +++ b/tests/scripts/test_psa_constant_names.py @@ -294,7 +294,8 @@ if __name__ == '__main__': action='store_false', dest='keep_c', help='Don\'t keep the intermediate C file (default)') options = parser.parse_args() - headers = [os.path.join(options.include[0], 'psa/crypto.h')] + headers = [os.path.join(options.include[0], 'psa', h) + for h in ['crypto.h', 'crypto_extra.h', 'crypto_values.h']] test_suites = ['tests/suites/test_suite_psa_crypto_metadata.data'] inputs = gather_inputs(headers, test_suites) count, errors = run_tests(options, inputs) From 17542086ab26a4639f82568c1671e427c4c65f53 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Fri, 4 Jan 2019 19:46:31 +0100 Subject: [PATCH 16/25] Recognize kdf_alg as KDF algorithm parameter name --- tests/scripts/test_psa_constant_names.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/scripts/test_psa_constant_names.py b/tests/scripts/test_psa_constant_names.py index d8f00050fe..de0d0146dc 100755 --- a/tests/scripts/test_psa_constant_names.py +++ b/tests/scripts/test_psa_constant_names.py @@ -63,6 +63,7 @@ when applicable.''' # Hard-coded value for unknown algorithms self.hash_algorithms = set(['0x010000ff']) self.mac_algorithms = set(['0x02ff00ff']) + self.kdf_algorithms = set(['0x300000ff', '0x310000ff']) # For AEAD algorithms, the only variability is over the tag length, # and this only applies to known algorithms, so don't test an # unknown algorithm. @@ -88,6 +89,7 @@ when applicable.''' Call this after parsing all the inputs.''' self.arguments_for['hash_alg'] = sorted(self.hash_algorithms) self.arguments_for['mac_alg'] = sorted(self.mac_algorithms) + self.arguments_for['kdf_alg'] = sorted(self.kdf_algorithms) self.arguments_for['aead_alg'] = sorted(self.aead_algorithms) self.arguments_for['curve'] = sorted(self.ecc_curves) From 95ab71a19ad94697db9852ae20c42e5e2cf7afe1 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Fri, 4 Jan 2019 19:46:59 +0100 Subject: [PATCH 17/25] test_psa_constant_names: make tmp files easier to recognize --- tests/scripts/test_psa_constant_names.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/scripts/test_psa_constant_names.py b/tests/scripts/test_psa_constant_names.py index de0d0146dc..7f7076c4cb 100755 --- a/tests/scripts/test_psa_constant_names.py +++ b/tests/scripts/test_psa_constant_names.py @@ -206,13 +206,16 @@ def run_c(options, type, names): c_name = None exe_name = None try: - c_fd, c_name = tempfile.mkstemp(suffix='.c', + c_fd, c_name = tempfile.mkstemp(prefix='tmp-{}-'.format(type), + suffix='.c', dir='programs/psa') exe_suffix = '.exe' if platform.system() == 'Windows' else '' exe_name = c_name[:-2] + exe_suffix remove_file_if_exists(exe_name) c_file = os.fdopen(c_fd, 'w', encoding='ascii') - c_file.write('''/* Generated by test_psa_constant_names.py */ + c_file.write('/* Generated by test_psa_constant_names.py for {} values */' + .format(type)) + c_file.write(''' #include #include int main(void) From ec07950e532d328b36122d2f730d2e0c4efab07f Mon Sep 17 00:00:00 2001 From: Darryl Green Date: Tue, 29 Jan 2019 15:48:00 +0000 Subject: [PATCH 18/25] Exclude ECDH and FFDH key agreement algorithms for now --- tests/scripts/test_psa_constant_names.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/scripts/test_psa_constant_names.py b/tests/scripts/test_psa_constant_names.py index 7f7076c4cb..1c19cd44b0 100755 --- a/tests/scripts/test_psa_constant_names.py +++ b/tests/scripts/test_psa_constant_names.py @@ -131,7 +131,9 @@ where each argument takes each possible value at least once.''' excluded_name_re = re.compile('_(?:GET|IS|OF)_|_(?:BASE|FLAG|MASK)\Z') # Additional excluded macros. excluded_names = set(['PSA_ALG_AEAD_WITH_DEFAULT_TAG_LENGTH', - 'PSA_ALG_FULL_LENGTH_MAC']) + 'PSA_ALG_FULL_LENGTH_MAC', + 'PSA_ALG_ECDH', + 'PSA_ALG_FFDH']) argument_split_re = re.compile(r' *, *') def parse_header_line(self, line): '''Parse a C header line, looking for "#define PSA_xxx".''' @@ -158,6 +160,8 @@ where each argument takes each possible value at least once.''' def add_test_case_line(self, function, argument): '''Parse a test case data line, looking for algorithm metadata tests.''' if function.endswith('_algorithm'): + if 'ECDH' in argument or 'FFDH' in argument: + return self.algorithms.add(argument) if function == 'hash_algorithm': self.hash_algorithms.add(argument) From 61b7f61d5ebd5cb2b81621cbc08303a88945bbac Mon Sep 17 00:00:00 2001 From: Darryl Green Date: Mon, 4 Feb 2019 16:00:21 +0000 Subject: [PATCH 19/25] Change unknown hash algorithm value 0x010000ff corresponds to PSA_ALG_ANY_HASH, so this collides and isn't an unknown algorithm. --- tests/scripts/test_psa_constant_names.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/scripts/test_psa_constant_names.py b/tests/scripts/test_psa_constant_names.py index 1c19cd44b0..2d2e213ff6 100755 --- a/tests/scripts/test_psa_constant_names.py +++ b/tests/scripts/test_psa_constant_names.py @@ -61,7 +61,7 @@ when applicable.''' self.key_types = set(['0xffffffff']) self.key_usage_flags = set(['0x80000000']) # Hard-coded value for unknown algorithms - self.hash_algorithms = set(['0x010000ff']) + self.hash_algorithms = set(['0x010000fe']) self.mac_algorithms = set(['0x02ff00ff']) self.kdf_algorithms = set(['0x300000ff', '0x310000ff']) # For AEAD algorithms, the only variability is over the tag length, From b8fe06820b4a33d19eb21147829dee701ffe60e7 Mon Sep 17 00:00:00 2001 From: Darryl Green Date: Wed, 6 Feb 2019 13:21:31 +0000 Subject: [PATCH 20/25] Document that ECDH and FFDH are excluded only temporarily --- tests/scripts/test_psa_constant_names.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/scripts/test_psa_constant_names.py b/tests/scripts/test_psa_constant_names.py index 2d2e213ff6..000dedc202 100755 --- a/tests/scripts/test_psa_constant_names.py +++ b/tests/scripts/test_psa_constant_names.py @@ -130,6 +130,8 @@ where each argument takes each possible value at least once.''' # Regex of macro names to exclude. excluded_name_re = re.compile('_(?:GET|IS|OF)_|_(?:BASE|FLAG|MASK)\Z') # Additional excluded macros. + # PSA_ALG_ECDH and PSA_ALG_FFDH are excluded for now as the script + # currently doesn't support them. excluded_names = set(['PSA_ALG_AEAD_WITH_DEFAULT_TAG_LENGTH', 'PSA_ALG_FULL_LENGTH_MAC', 'PSA_ALG_ECDH', @@ -160,6 +162,8 @@ where each argument takes each possible value at least once.''' def add_test_case_line(self, function, argument): '''Parse a test case data line, looking for algorithm metadata tests.''' if function.endswith('_algorithm'): + # As above, ECDH and FFDH algorithms are excluded for now. + # Support for them will be added in the future. if 'ECDH' in argument or 'FFDH' in argument: return self.algorithms.add(argument) From d519583ae301eb0ee1b1bb67f5e9a3a38877dab0 Mon Sep 17 00:00:00 2001 From: Darryl Green Date: Wed, 6 Feb 2019 13:41:54 +0000 Subject: [PATCH 21/25] Run generate_psa_constants.py in cmake builds --- programs/psa/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/programs/psa/CMakeLists.txt b/programs/psa/CMakeLists.txt index 37038c0de6..ab11fbc903 100644 --- a/programs/psa/CMakeLists.txt +++ b/programs/psa/CMakeLists.txt @@ -4,6 +4,8 @@ target_link_libraries(crypto_examples mbedtls) add_executable(key_ladder_demo key_ladder_demo.c) target_link_libraries(key_ladder_demo mbedtls) +execute_process(COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/../../scripts/generate_psa_constants.py) + add_executable(psa_constant_names psa_constant_names.c) target_link_libraries(psa_constant_names mbedtls) From 45010a333ed3de7bb073671b975a011b4bf2aaaa Mon Sep 17 00:00:00 2001 From: Darryl Green Date: Wed, 6 Feb 2019 13:43:58 +0000 Subject: [PATCH 22/25] Move test_psa_constant_names to a full config build --- tests/scripts/all.sh | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/tests/scripts/all.sh b/tests/scripts/all.sh index 84e4490100..f7c61b8414 100755 --- a/tests/scripts/all.sh +++ b/tests/scripts/all.sh @@ -604,15 +604,6 @@ component_test_default_cmake_gcc_asan () { if_build_succeeded tests/compat.sh } -component_test_psa_constant_names () { - msg "build: cmake, gcc, ASan" # ~ 1 min 50s - CC=gcc cmake -D CMAKE_BUILD_TYPE:String=Asan . - make - - msg "test psa_constant_names" # ~ 1s - record_status tests/scripts/test_psa_constant_names.py -} - component_test_ref_configs () { msg "test/build: ref-configs (ASan build)" # ~ 6 min 20s CC=gcc cmake -D CMAKE_BUILD_TYPE:String=Asan . @@ -717,6 +708,9 @@ component_test_full_cmake_clang () { msg "test: main suites (full config)" # ~ 5s make test + msg "test: psa_constant_names (full config)" # ~ 1s + record_status tests/scripts/test_psa_constant_names.py + msg "test: ssl-opt.sh default, ECJPAKE, SSL async (full config)" # ~ 1s if_build_succeeded tests/ssl-opt.sh -f 'Default\|ECJPAKE\|SSL async private' From df72306e07281401c8339dc17dff3fb7ff54e53b Mon Sep 17 00:00:00 2001 From: Darryl Green Date: Wed, 6 Feb 2019 15:36:00 +0000 Subject: [PATCH 23/25] Fix typo in generate_psa_constants.py --- scripts/generate_psa_constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/generate_psa_constants.py b/scripts/generate_psa_constants.py index f32339fa5f..32508f286a 100755 --- a/scripts/generate_psa_constants.py +++ b/scripts/generate_psa_constants.py @@ -63,7 +63,7 @@ static int psa_snprint_algorithm(char *buffer, size_t buffer_size, } else if (PSA_ALG_IS_AEAD(alg)) { core_alg = PSA_ALG_AEAD_WITH_DEFAULT_TAG_LENGTH(alg); if (core_alg == 0) { - /* For unkonwn AEAD algorithms, there is no "default tag length". */ + /* For unknown AEAD algorithms, there is no "default tag length". */ core_alg = alg; } else if (core_alg != alg) { append(&buffer, buffer_size, &required_size, From da7c80e3f1c6d305ff270407e067a5d35c7fa20b Mon Sep 17 00:00:00 2001 From: Darryl Green Date: Wed, 6 Feb 2019 16:24:43 +0000 Subject: [PATCH 24/25] Add dependency to Makefile --- programs/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/Makefile b/programs/Makefile index 2792b09131..51548c327f 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -103,7 +103,7 @@ EXTRA_GENERATED += psa/psa_constant_names_generated.c endif psa/psa_constant_names$(EXEXT): psa/psa_constant_names_generated.c -psa/psa_constant_names_generated.c: ../scripts/generate_psa_constants.py ../include/psa/crypto_values.h +psa/psa_constant_names_generated.c: ../scripts/generate_psa_constants.py ../include/psa/crypto_values.h ../include/psa/crypto_extra.h ../scripts/generate_psa_constants.py aes/aescrypt2$(EXEXT): aes/aescrypt2.c $(DEP) From 21b33b07dfd8f30a08fc4654c2debcc0f166a5c8 Mon Sep 17 00:00:00 2001 From: Darryl Green Date: Thu, 7 Feb 2019 10:50:49 +0000 Subject: [PATCH 25/25] Run generate_psa_constants.py before building psa_constant_names with cmake --- programs/psa/CMakeLists.txt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/programs/psa/CMakeLists.txt b/programs/psa/CMakeLists.txt index ab11fbc903..c80043bc45 100644 --- a/programs/psa/CMakeLists.txt +++ b/programs/psa/CMakeLists.txt @@ -4,11 +4,16 @@ target_link_libraries(crypto_examples mbedtls) add_executable(key_ladder_demo key_ladder_demo.c) target_link_libraries(key_ladder_demo mbedtls) -execute_process(COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/../../scripts/generate_psa_constants.py) - add_executable(psa_constant_names psa_constant_names.c) target_link_libraries(psa_constant_names mbedtls) +add_custom_target( + psa_constant_names_generated + COMMAND ${PYTHON_EXECUTABLE} scripts/generate_psa_constants.py + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../../ +) +add_dependencies(psa_constant_names psa_constant_names_generated) + install(TARGETS crypto_examples key_ladder_demo