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