1a8e1175bSopenharmony_ci#!/usr/bin/env python3 2a8e1175bSopenharmony_ci"""Generate library/psa_crypto_driver_wrappers.h 3a8e1175bSopenharmony_ci library/psa_crypto_driver_wrappers_no_static.c 4a8e1175bSopenharmony_ci 5a8e1175bSopenharmony_ci This module is invoked by the build scripts to auto generate the 6a8e1175bSopenharmony_ci psa_crypto_driver_wrappers.h and psa_crypto_driver_wrappers_no_static 7a8e1175bSopenharmony_ci based on template files in script/data_files/driver_templates/. 8a8e1175bSopenharmony_ci""" 9a8e1175bSopenharmony_ci# Copyright The Mbed TLS Contributors 10a8e1175bSopenharmony_ci# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later 11a8e1175bSopenharmony_ci 12a8e1175bSopenharmony_ciimport sys 13a8e1175bSopenharmony_ciimport os 14a8e1175bSopenharmony_ciimport json 15a8e1175bSopenharmony_cifrom typing import NewType, Dict, Any 16a8e1175bSopenharmony_cifrom traceback import format_tb 17a8e1175bSopenharmony_ciimport argparse 18a8e1175bSopenharmony_ciimport jsonschema 19a8e1175bSopenharmony_ciimport jinja2 20a8e1175bSopenharmony_cifrom mbedtls_dev import build_tree 21a8e1175bSopenharmony_ci 22a8e1175bSopenharmony_ciJSONSchema = NewType('JSONSchema', object) 23a8e1175bSopenharmony_ci# The Driver is an Object, but practically it's indexable and can called a dictionary to 24a8e1175bSopenharmony_ci# keep MyPy happy till MyPy comes with a more composite type for JsonObjects. 25a8e1175bSopenharmony_ciDriver = NewType('Driver', dict) 26a8e1175bSopenharmony_ci 27a8e1175bSopenharmony_ci 28a8e1175bSopenharmony_ciclass JsonValidationException(Exception): 29a8e1175bSopenharmony_ci def __init__(self, message="Json Validation Failed"): 30a8e1175bSopenharmony_ci self.message = message 31a8e1175bSopenharmony_ci super().__init__(self.message) 32a8e1175bSopenharmony_ci 33a8e1175bSopenharmony_ci 34a8e1175bSopenharmony_ciclass DriverReaderException(Exception): 35a8e1175bSopenharmony_ci def __init__(self, message="Driver Reader Failed"): 36a8e1175bSopenharmony_ci self.message = message 37a8e1175bSopenharmony_ci super().__init__(self.message) 38a8e1175bSopenharmony_ci 39a8e1175bSopenharmony_ci 40a8e1175bSopenharmony_cidef render(template_path: str, driver_jsoncontext: list) -> str: 41a8e1175bSopenharmony_ci """ 42a8e1175bSopenharmony_ci Render template from the input file and driver JSON. 43a8e1175bSopenharmony_ci """ 44a8e1175bSopenharmony_ci environment = jinja2.Environment( 45a8e1175bSopenharmony_ci loader=jinja2.FileSystemLoader(os.path.dirname(template_path)), 46a8e1175bSopenharmony_ci keep_trailing_newline=True) 47a8e1175bSopenharmony_ci template = environment.get_template(os.path.basename(template_path)) 48a8e1175bSopenharmony_ci 49a8e1175bSopenharmony_ci return template.render(drivers=driver_jsoncontext) 50a8e1175bSopenharmony_ci 51a8e1175bSopenharmony_cidef generate_driver_wrapper_file(template_dir: str, 52a8e1175bSopenharmony_ci output_dir: str, 53a8e1175bSopenharmony_ci template_file_name: str, 54a8e1175bSopenharmony_ci driver_jsoncontext: list) -> None: 55a8e1175bSopenharmony_ci """ 56a8e1175bSopenharmony_ci Generate the file psa_crypto_driver_wrapper.c. 57a8e1175bSopenharmony_ci """ 58a8e1175bSopenharmony_ci driver_wrapper_template_filename = \ 59a8e1175bSopenharmony_ci os.path.join(template_dir, template_file_name) 60a8e1175bSopenharmony_ci 61a8e1175bSopenharmony_ci result = render(driver_wrapper_template_filename, driver_jsoncontext) 62a8e1175bSopenharmony_ci 63a8e1175bSopenharmony_ci with open(file=os.path.join(output_dir, os.path.splitext(template_file_name)[0]), 64a8e1175bSopenharmony_ci mode='w', 65a8e1175bSopenharmony_ci encoding='UTF-8') as out_file: 66a8e1175bSopenharmony_ci out_file.write(result) 67a8e1175bSopenharmony_ci 68a8e1175bSopenharmony_ci 69a8e1175bSopenharmony_cidef validate_json(driverjson_data: Driver, driverschema_list: dict) -> None: 70a8e1175bSopenharmony_ci """ 71a8e1175bSopenharmony_ci Validate the Driver JSON against an appropriate schema 72a8e1175bSopenharmony_ci the schema passed could be that matching an opaque/ transparent driver. 73a8e1175bSopenharmony_ci """ 74a8e1175bSopenharmony_ci driver_type = driverjson_data["type"] 75a8e1175bSopenharmony_ci driver_prefix = driverjson_data["prefix"] 76a8e1175bSopenharmony_ci try: 77a8e1175bSopenharmony_ci _schema = driverschema_list[driver_type] 78a8e1175bSopenharmony_ci jsonschema.validate(instance=driverjson_data, schema=_schema) 79a8e1175bSopenharmony_ci except KeyError as err: 80a8e1175bSopenharmony_ci # This could happen if the driverjson_data.type does not exist in the provided schema list 81a8e1175bSopenharmony_ci # schemas = {'transparent': transparent_driver_schema, 'opaque': opaque_driver_schema} 82a8e1175bSopenharmony_ci # Print onto stdout and stderr. 83a8e1175bSopenharmony_ci print("Unknown Driver type " + driver_type + 84a8e1175bSopenharmony_ci " for driver " + driver_prefix, str(err)) 85a8e1175bSopenharmony_ci print("Unknown Driver type " + driver_type + 86a8e1175bSopenharmony_ci " for driver " + driver_prefix, str(err), file=sys.stderr) 87a8e1175bSopenharmony_ci raise JsonValidationException() from err 88a8e1175bSopenharmony_ci 89a8e1175bSopenharmony_ci except jsonschema.exceptions.ValidationError as err: 90a8e1175bSopenharmony_ci # Print onto stdout and stderr. 91a8e1175bSopenharmony_ci print("Error: Failed to validate data file: {} using schema: {}." 92a8e1175bSopenharmony_ci "\n Exception Message: \"{}\"" 93a8e1175bSopenharmony_ci " ".format(driverjson_data, _schema, str(err))) 94a8e1175bSopenharmony_ci print("Error: Failed to validate data file: {} using schema: {}." 95a8e1175bSopenharmony_ci "\n Exception Message: \"{}\"" 96a8e1175bSopenharmony_ci " ".format(driverjson_data, _schema, str(err)), file=sys.stderr) 97a8e1175bSopenharmony_ci raise JsonValidationException() from err 98a8e1175bSopenharmony_ci 99a8e1175bSopenharmony_ci 100a8e1175bSopenharmony_cidef load_driver(schemas: Dict[str, Any], driver_file: str) -> Any: 101a8e1175bSopenharmony_ci """loads validated json driver""" 102a8e1175bSopenharmony_ci with open(file=driver_file, mode='r', encoding='UTF-8') as f: 103a8e1175bSopenharmony_ci json_data = json.load(f) 104a8e1175bSopenharmony_ci try: 105a8e1175bSopenharmony_ci validate_json(json_data, schemas) 106a8e1175bSopenharmony_ci except JsonValidationException as e: 107a8e1175bSopenharmony_ci raise DriverReaderException from e 108a8e1175bSopenharmony_ci return json_data 109a8e1175bSopenharmony_ci 110a8e1175bSopenharmony_ci 111a8e1175bSopenharmony_cidef load_schemas(project_root: str) -> Dict[str, Any]: 112a8e1175bSopenharmony_ci """ 113a8e1175bSopenharmony_ci Load schemas map 114a8e1175bSopenharmony_ci """ 115a8e1175bSopenharmony_ci schema_file_paths = { 116a8e1175bSopenharmony_ci 'transparent': os.path.join(project_root, 117a8e1175bSopenharmony_ci 'scripts', 118a8e1175bSopenharmony_ci 'data_files', 119a8e1175bSopenharmony_ci 'driver_jsons', 120a8e1175bSopenharmony_ci 'driver_transparent_schema.json'), 121a8e1175bSopenharmony_ci 'opaque': os.path.join(project_root, 122a8e1175bSopenharmony_ci 'scripts', 123a8e1175bSopenharmony_ci 'data_files', 124a8e1175bSopenharmony_ci 'driver_jsons', 125a8e1175bSopenharmony_ci 'driver_opaque_schema.json') 126a8e1175bSopenharmony_ci } 127a8e1175bSopenharmony_ci driver_schema = {} 128a8e1175bSopenharmony_ci for key, file_path in schema_file_paths.items(): 129a8e1175bSopenharmony_ci with open(file=file_path, mode='r', encoding='UTF-8') as file: 130a8e1175bSopenharmony_ci driver_schema[key] = json.load(file) 131a8e1175bSopenharmony_ci return driver_schema 132a8e1175bSopenharmony_ci 133a8e1175bSopenharmony_ci 134a8e1175bSopenharmony_cidef read_driver_descriptions(project_root: str, 135a8e1175bSopenharmony_ci json_directory: str, 136a8e1175bSopenharmony_ci jsondriver_list: str) -> list: 137a8e1175bSopenharmony_ci """ 138a8e1175bSopenharmony_ci Merge driver JSON files into a single ordered JSON after validation. 139a8e1175bSopenharmony_ci """ 140a8e1175bSopenharmony_ci driver_schema = load_schemas(project_root) 141a8e1175bSopenharmony_ci 142a8e1175bSopenharmony_ci with open(file=os.path.join(json_directory, jsondriver_list), 143a8e1175bSopenharmony_ci mode='r', 144a8e1175bSopenharmony_ci encoding='UTF-8') as driver_list_file: 145a8e1175bSopenharmony_ci driver_list = json.load(driver_list_file) 146a8e1175bSopenharmony_ci 147a8e1175bSopenharmony_ci return [load_driver(schemas=driver_schema, 148a8e1175bSopenharmony_ci driver_file=os.path.join(json_directory, driver_file_name)) 149a8e1175bSopenharmony_ci for driver_file_name in driver_list] 150a8e1175bSopenharmony_ci 151a8e1175bSopenharmony_ci 152a8e1175bSopenharmony_cidef trace_exception(e: Exception, file=sys.stderr) -> None: 153a8e1175bSopenharmony_ci """Prints exception trace to the given TextIO handle""" 154a8e1175bSopenharmony_ci print("Exception: type: %s, message: %s, trace: %s" % ( 155a8e1175bSopenharmony_ci e.__class__, str(e), format_tb(e.__traceback__) 156a8e1175bSopenharmony_ci ), file) 157a8e1175bSopenharmony_ci 158a8e1175bSopenharmony_ci 159a8e1175bSopenharmony_ciTEMPLATE_FILENAMES = ["psa_crypto_driver_wrappers.h.jinja", 160a8e1175bSopenharmony_ci "psa_crypto_driver_wrappers_no_static.c.jinja"] 161a8e1175bSopenharmony_ci 162a8e1175bSopenharmony_cidef main() -> int: 163a8e1175bSopenharmony_ci """ 164a8e1175bSopenharmony_ci Main with command line arguments. 165a8e1175bSopenharmony_ci """ 166a8e1175bSopenharmony_ci def_arg_project_root = build_tree.guess_project_root() 167a8e1175bSopenharmony_ci 168a8e1175bSopenharmony_ci parser = argparse.ArgumentParser() 169a8e1175bSopenharmony_ci parser.add_argument('--project-root', default=def_arg_project_root, 170a8e1175bSopenharmony_ci help='root directory of repo source code') 171a8e1175bSopenharmony_ci parser.add_argument('--template-dir', 172a8e1175bSopenharmony_ci help='directory holding the driver templates') 173a8e1175bSopenharmony_ci parser.add_argument('--json-dir', 174a8e1175bSopenharmony_ci help='directory holding the driver JSONs') 175a8e1175bSopenharmony_ci parser.add_argument('output_directory', nargs='?', 176a8e1175bSopenharmony_ci help='output file\'s location') 177a8e1175bSopenharmony_ci args = parser.parse_args() 178a8e1175bSopenharmony_ci 179a8e1175bSopenharmony_ci project_root = os.path.abspath(args.project_root) 180a8e1175bSopenharmony_ci 181a8e1175bSopenharmony_ci crypto_core_directory = build_tree.crypto_core_directory(project_root) 182a8e1175bSopenharmony_ci 183a8e1175bSopenharmony_ci output_directory = args.output_directory if args.output_directory is not None else \ 184a8e1175bSopenharmony_ci crypto_core_directory 185a8e1175bSopenharmony_ci 186a8e1175bSopenharmony_ci template_directory = args.template_dir if args.template_dir is not None else \ 187a8e1175bSopenharmony_ci os.path.join(project_root, 188a8e1175bSopenharmony_ci 'scripts', 189a8e1175bSopenharmony_ci 'data_files', 190a8e1175bSopenharmony_ci 'driver_templates') 191a8e1175bSopenharmony_ci json_directory = args.json_dir if args.json_dir is not None else \ 192a8e1175bSopenharmony_ci os.path.join(project_root, 193a8e1175bSopenharmony_ci 'scripts', 194a8e1175bSopenharmony_ci 'data_files', 195a8e1175bSopenharmony_ci 'driver_jsons') 196a8e1175bSopenharmony_ci 197a8e1175bSopenharmony_ci try: 198a8e1175bSopenharmony_ci # Read and validate list of driver jsons from driverlist.json 199a8e1175bSopenharmony_ci merged_driver_json = read_driver_descriptions(project_root, 200a8e1175bSopenharmony_ci json_directory, 201a8e1175bSopenharmony_ci 'driverlist.json') 202a8e1175bSopenharmony_ci except DriverReaderException as e: 203a8e1175bSopenharmony_ci trace_exception(e) 204a8e1175bSopenharmony_ci return 1 205a8e1175bSopenharmony_ci for template_filename in TEMPLATE_FILENAMES: 206a8e1175bSopenharmony_ci generate_driver_wrapper_file(template_directory, output_directory, 207a8e1175bSopenharmony_ci template_filename, merged_driver_json) 208a8e1175bSopenharmony_ci return 0 209a8e1175bSopenharmony_ci 210a8e1175bSopenharmony_ci 211a8e1175bSopenharmony_ciif __name__ == '__main__': 212a8e1175bSopenharmony_ci sys.exit(main()) 213