diff --git a/scripts/mbedtls_framework/c_parsing_helper.py b/scripts/mbedtls_framework/c_parsing_helper.py index 2abb26791..0e428cd04 100644 --- a/scripts/mbedtls_framework/c_parsing_helper.py +++ b/scripts/mbedtls_framework/c_parsing_helper.py @@ -82,7 +82,7 @@ class FunctionInfo: str_text = "{} {} {}({})".format(" ".join(self.qualifiers), self.return_type, self.name, ", ".join(str_args)).strip() - str_text = FunctionInfo._c_wrap_(str_text) + str_text = self._c_wrap_(str_text) return self.doc + "\n" + str_text @staticmethod @@ -153,16 +153,16 @@ def read_function_declarations(functions: Dict[str, FunctionInfo], name, arguments) -_C_TYPEDEF_DECLARATION_RE = r'typedef (?:struct )?(?P\w+) (?P\w+)' +_C_TYPEDEF_DECLARATION_RE = re.compile(r'typedef (?:struct )?(?P\w+) (?P\w+)') -def read_typedefs(filename: str)-> Dict[str, str]: +def read_typedefs(filename: str) -> Dict[str, str]: """ Extract type definitions in a {typedef aliased name: original type} dictionary. Multi-line typedef struct are not captured. """ type_decl = {} - with open(filename, encoding='utf-8') as inp: - content = inp.read() - for m in re.finditer(_C_TYPEDEF_DECLARATION_RE, content): - type_decl[m.group("name")] = m.group("type") + for _, line in read_logical_lines(filename): + m = _C_TYPEDEF_DECLARATION_RE.match(line) + if m: + type_decl[m.group("name")] = m.group("type") return type_decl diff --git a/scripts/mbedtls_framework/c_wrapper_generator.py b/scripts/mbedtls_framework/c_wrapper_generator.py index b58db97f5..fbf3b51e3 100644 --- a/scripts/mbedtls_framework/c_wrapper_generator.py +++ b/scripts/mbedtls_framework/c_wrapper_generator.py @@ -11,8 +11,8 @@ import os import re import sys -import typing -from typing import Dict, List, Optional, Tuple +import textwrap +from typing import Dict, NamedTuple, List, Optional, Tuple from .c_parsing_helper import ArgumentInfo, FunctionInfo from . import typing_util @@ -25,7 +25,7 @@ def c_declare(prefix: str, name: str, suffix: str) -> str: return prefix + name + suffix -WrapperInfo = typing.NamedTuple('WrapperInfo', [ +WrapperInfo = NamedTuple('WrapperInfo', [ ('argument_names', List[str]), ('guard', Optional[str]), ('wrapper_name', str), @@ -67,46 +67,52 @@ class Base: This includes a description comment and some include directives. """ - prologue = ['/* Automatically generated by {}, do not edit! */'.format(self.program_name), - ''] - prologue += ['/* Copyright The Mbed TLS Contributors', - ' * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later', - ' */', - ''] + + prologue = textwrap.dedent(""" + /* Automatically generated by generate_psa_wrappers.py, do not edit! */ + + /* Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later + */ + + """).lstrip() if header: - prologue += ['#ifndef {}'.format(self.header_guard), - '#define {}'.format(self.header_guard), - '', - '#ifdef __cplusplus', - 'extern "C" {', - '#endif', - ''] + prologue += textwrap.dedent(""" + #ifndef {guard} + #define {guard} + + #ifdef __cplusplus + extern "C" {{ + #endif + + """.format(guard=self.header_guard)).lstrip() for include in self._INCLUDES: - prologue.append('#include {}'.format(include)) + prologue += "#include {}\n".format(include) # Make certain there is an empty line at the end of this section. for i in [-1, -2]: - if prologue[i] != '': - prologue.append('') - - out.write('\n'.join(prologue)) + if prologue[i] != '\n': + prologue += ('\n') + out.write(prologue) def _write_epilogue(self, out: typing_util.Writable, header: bool) -> None: """Write the epilogue of a C file. """ - epilogue = [] + epilogue = "" if header: - epilogue += ['#ifdef __cplusplus', - '}', - '#endif', - '', - '#endif /* {} */'.format(self.header_guard), - ''] - epilogue.append('/* End of automatically generated file. */') - epilogue.append('') - out.write("\n".join(epilogue)) + epilogue += textwrap.dedent(""" + #ifdef __cplusplus + }} + #endif + + #endif /* {guard} */ + + """.format(guard=self.header_guard)).lstrip() + + epilogue += ('/* End of automatically generated file. */\n') + out.write(epilogue) def _wrapper_function_name(self, original_name: str) -> str: """The name of the wrapper function. diff --git a/scripts/mbedtls_framework/code_wrapper/psa_buffer.py b/scripts/mbedtls_framework/code_wrapper/psa_buffer.py old mode 100755 new mode 100644 index 1ce7b3d21..ca6296872 --- a/scripts/mbedtls_framework/code_wrapper/psa_buffer.py +++ b/scripts/mbedtls_framework/code_wrapper/psa_buffer.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 """ PSA Buffer utility data-class. """ @@ -6,7 +5,7 @@ # SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later from typing import List -from mbedtls_framework import typing_util +from .. import typing_util class BufferParameter: """Description of an input or output buffer parameter sequence to a PSA function.""" @@ -28,29 +27,3 @@ class BufferParameter: self.size_name = size_name self.is_output = is_output - def poison_write(self, - out: typing_util.Writable, - poison: bool) -> None: - """Write poisoning or unpoisoning code for a buffer parameter. - - Write poisoning code if poison is true, unpoisoning code otherwise. - """ - out.write(' MBEDTLS_TEST_MEMORY_{}({}, {});\n'.format( - 'POISON' if poison else 'UNPOISON', - self.buffer_name, self.size_name - )) - - @staticmethod - def poison_multi_write(out: typing_util.Writable, - buffer_parameters: List['BufferParameter'], - poison: bool) -> None: - """Write poisoning or unpoisoning code for the buffer parameters. - - Write poisoning code if poison is true, unpoisoning code otherwise. - """ - if not buffer_parameters: - return - out.write('#if !defined(MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS)\n') - for param in buffer_parameters: - param.poison_write(out, poison) - out.write('#endif /* !defined(MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS) */\n') diff --git a/scripts/mbedtls_framework/code_wrapper/psa_test_wrapper.py b/scripts/mbedtls_framework/code_wrapper/psa_test_wrapper.py old mode 100755 new mode 100644 index e81706226..61fd1f393 --- a/scripts/mbedtls_framework/code_wrapper/psa_test_wrapper.py +++ b/scripts/mbedtls_framework/code_wrapper/psa_test_wrapper.py @@ -1,26 +1,21 @@ -#!/usr/bin/env python3 """Generate wrapper functions for PSA function calls. """ # Copyright The Mbed TLS Contributors # SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later -### WARNING: the code in this file has not been extensively reviewed yet. -### We do not think it is harmful, but it may be below our normal standards -### for robustness and maintainability. - import argparse import itertools import os from typing import Iterator, List, Collection, Optional, Tuple -from mbedtls_framework import build_tree -from mbedtls_framework import c_parsing_helper -from mbedtls_framework import c_wrapper_generator -from mbedtls_framework import typing_util +from .. import build_tree +from .. import c_parsing_helper +from .. import c_wrapper_generator +from .. import typing_util -from mbedtls_framework.code_wrapper.psa_buffer import BufferParameter -from mbedtls_framework.code_wrapper.psa_wrapper import PSAWrapper, PSALoggingWrapper, DEFAULTS +from .psa_buffer import BufferParameter +from .psa_wrapper import PSAWrapper, PSALoggingWrapper, DEFAULTS class PSATestWrapper(PSAWrapper): """Generate a C source file containing wrapper functions for PSA Crypto API calls.""" @@ -31,7 +26,7 @@ class PSATestWrapper(PSAWrapper): _WRAPPER_NAME_PREFIX = 'mbedtls_test_wrap_' _WRAPPER_NAME_SUFFIX = '' - _PSA_WRAPPER_INCLUDES = ['\n', + _PSA_WRAPPER_INCLUDES = ['', '', '', ''] @@ -39,10 +34,10 @@ class PSATestWrapper(PSAWrapper): class PSALoggingTestWrapper(PSATestWrapper, PSALoggingWrapper): """Generate a C source file containing wrapper functions that log PSA Crypto API calls.""" - def __init__(self, output_h_f: str, - output_c_f: str, + def __init__(self, out_h_f: str, + out_c_f: str, stream: str, in_headers: Collection[str] = DEFAULTS["input_headers"]) -> None: - super().__init__(output_h_f, output_c_f, in_headers)# type: ignore[arg-type] + super().__init__(out_h_f, out_c_f, in_headers)# type: ignore[arg-type] self.set_stream(stream) diff --git a/scripts/mbedtls_framework/code_wrapper/psa_wrapper.py b/scripts/mbedtls_framework/code_wrapper/psa_wrapper.py old mode 100755 new mode 100644 index e97b3f44e..c128016e5 --- a/scripts/mbedtls_framework/code_wrapper/psa_wrapper.py +++ b/scripts/mbedtls_framework/code_wrapper/psa_wrapper.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 """Generate wrapper functions for PSA function calls. """ @@ -10,13 +9,12 @@ import itertools import os from typing import Any, Iterator, List, Dict, Collection, Optional, Tuple -from mbedtls_framework import build_tree -from mbedtls_framework import c_parsing_helper -from mbedtls_framework import c_wrapper_generator -from mbedtls_framework import typing_util +from .. import build_tree +from .. import c_parsing_helper +from .. import c_wrapper_generator +from .. import typing_util -from mbedtls_framework.code_wrapper.psa_buffer import BufferParameter -from textwrap import dedent +from .psa_buffer import BufferParameter DEFAULTS = { "input_headers" : ['crypto.h', 'crypto_extra.h'], @@ -54,7 +52,7 @@ DEFAULTS = { 'psa_pake_set_peer' : 'defined(PSA_WANT_ALG_SOME_PAKE)', 'psa_pake_set_role' : 'defined(PSA_WANT_ALG_SOME_PAKE)', 'psa_pake_set_user' : 'defined(PSA_WANT_ALG_SOME_PAKE)', - 'psa_pake_setup' : 'defined(PSA_WANT_ALG_SOME_PAKE)' + 'psa_pake_setup' : 'defined(PSA_WANT_ALG_SOME_PAKE)', } } @@ -67,34 +65,32 @@ class PSAWrapper(c_wrapper_generator.Base): _PSA_WRAPPER_INCLUDES = [''] def __init__(self, - output_h_f: str, - output_c_f: str, + out_h_f: str, + out_c_f: str, in_headers: Collection[str] = DEFAULTS["input_headers"], - config: Dict[str, Any]= DEFAULTS) -> None: + config: Dict[str, Any] = DEFAULTS) -> None: super().__init__() - self._FUNCTION_GUARDS = super()._FUNCTION_GUARDS.copy() self.in_headers = in_headers - self.out_c_f = output_c_f - self.out_h_f = output_h_f + self.out_c_f = out_c_f + self.out_h_f = out_h_f self.mbedtls_root = build_tree.guess_mbedtls_root() self.read_config(config) - - if in_headers: - self.read_headers(in_headers) + self.read_headers(in_headers) def read_config(self, cfg: Dict[str, Any])-> None: - """Configure instance's parameters based on a module specific default config """ - + """Configure instance's parameters from a user provided config.""" + if not cfg: + return self._CPP_GUARDS = PSAWrapper.parse_def_guards(cfg["define_guards"]) self._SKIP_FUNCTIONS = cfg["skip_list"] self._FUNCTION_GUARDS.update(cfg["function_guards"]) # type: ignore[arg-type] self._NOT_IMPLEMENTED = cfg["not_implemented"] + self.read_headers(cfg["input_headers"]) def read_headers(self, headers: Collection[str]) -> None: - """ Reads functions from source header files into a Dict[str, FunctionInfo] """ - + """Reads functions to be wrapped from source header files into self.functions.""" for header_name in headers: header_path = self.rel_path(header_name) c_parsing_helper.read_function_declarations(self.functions, header_path) @@ -116,16 +112,18 @@ class PSAWrapper(c_wrapper_generator.Base): # Utility Methods @staticmethod def parse_def_guards(def_list: Collection[str])-> str: - """ Parse an input list of format ["HASH_DEFINE", "!HASH_DEFINE2" ] and generate a - c compatible defined(HASH_DEFINE) && !defined(HASH_DEFINE) syntax string""" + """ Parse an input list of format [[[C] preprocessor] macro, ...] and generate a + c compatible defined() && !defined() syntax string""" output = "" - _dl = ["defined({})".format(n) if n[0] != "!" \ - else "!defined({})".format(n[1:]) for n in def_list] + dl = [("defined({})".format(n) if n[0] != "!" else + "!defined({})".format(n[1:])) + for n in def_list] + # Split the list in chunks of 2 and add new lines - for i in range(0, len(_dl), 2): - output += "{} && {} && \\".format(_dl[i], _dl[i+1]) + "\n "\ - if i+2 <= len(_dl) else _dl[i] + for i in range(0, len(dl), 2): + output += "{} && {} && \\".format(dl[i], dl[i+1]) + "\n "\ + if i+2 <= len(dl) else dl[i] return output @staticmethod @@ -157,6 +155,27 @@ class PSAWrapper(c_wrapper_generator.Base): return True + def _poison_wrap(self, param : BufferParameter, poison: bool, ident_lv = 1) -> str: + """Returns a custom string based on the values of param and poison.""" + return "{}MBEDTLS_TEST_MEMORY_{}({}, {});\n".format((ident_lv * 4) * ' ', + 'POISON' if poison else 'UNPOISON', + param.buffer_name, param.size_name) + + def _poison_multi_write(self, + out: typing_util.Writable, + buffer_parameters: List['BufferParameter'], + poison: bool) -> None: + """Write poisoning or unpoisoning code for the buffer parameters. + Write poisoning code if poison is true, unpoisoning code otherwise. + """ + + if not buffer_parameters: + return + out.write('#if !defined(MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS)\n') + for param in buffer_parameters: + out.write(self._poison_wrap(param, poison)) + out.write('#endif /* !defined(MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS) */\n') + # Override parent's methods def _write_function_call(self, out: typing_util.Writable, function: c_wrapper_generator.FunctionInfo, @@ -168,9 +187,9 @@ class PSAWrapper(c_wrapper_generator.Base): if self._parameter_should_be_copied(function.name, function.arguments[param.index].name)) - BufferParameter.poison_multi_write(out, buffer_parameters, True) + self._poison_multi_write(out, buffer_parameters, True) super()._write_function_call(out, function, argument_names) - BufferParameter.poison_multi_write(out, buffer_parameters, False) + self._poison_multi_write(out, buffer_parameters, False) def _skip_function(self, function: c_wrapper_generator.FunctionInfo) -> bool: if function.return_type != 'psa_status_t': @@ -215,12 +234,12 @@ class PSALoggingWrapper(PSAWrapper, c_wrapper_generator.Logging): def __init__(self, stream: str, - output_h_f: str, - output_c_f: str, + out_h_f: str, + out_c_f: str, in_headers: Collection[str] = DEFAULTS["input_headers"], config: Dict[str, Any]= DEFAULTS) -> None: - super().__init__(output_h_f, output_c_f, in_headers, config) + super().__init__(out_h_f, out_c_f, in_headers, config) self.set_stream(stream) _PRINTF_TYPE_CAST = c_wrapper_generator.Logging._PRINTF_TYPE_CAST.copy() @@ -236,7 +255,7 @@ class PSALoggingWrapper(PSAWrapper, c_wrapper_generator.Logging): 'psa_key_usage_flags_t': 'unsigned', 'psa_pake_role_t': 'int', 'psa_pake_step_t': 'int', - 'psa_status_t': 'int' + 'psa_status_t': 'int', }) def _printf_parameters(self, typ: str, var: str) -> Tuple[str, List[str]]: