1#! /usr/bin/env python3
2#
3# -----------------------------------------------------------------------------
4# Copyright (c) 2020-2024, Arm Limited. All rights reserved.
5#
6# SPDX-License-Identifier: BSD-3-Clause
7#
8# -----------------------------------------------------------------------------
9
10import os
11import sys
12import click
13from jinja2 import Environment, FileSystemLoader
14from cryptography.hazmat.primitives.hashes import Hash, SHA256
15
16# Add the cwd to the path so that if there is a version of imgtool in there then
17# it gets used over the system imgtool. Used so that imgtool from upstream
18# mcuboot is preferred over system imgtool
19cwd = os.getcwd()
20sys.path = [cwd] + sys.path
21import imgtool
22import imgtool.main
23
24parser_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '../'))
25sys.path.append(parser_path)
26
27# This works around Python 2 and Python 3 handling character encodings
28# differently. More information about this issue at
29# https://click.palletsprojects.com/en/5.x/python3
30os.environ['LC_ALL'] = 'C.UTF-8'
31os.environ['LANG'] = 'C.UTF-8'
32
33
34def get_key_hash_c_array(key_file, mcuboot_hw_key):
35    key = imgtool.main.load_key(key_file)
36    key_bytes = []
37    if mcuboot_hw_key == "ON":
38        digest = Hash(SHA256())
39        digest.update(key.get_public_bytes())
40        key_bytes = digest.finalize()
41    else:
42        # If the full key is used then use only the raw key
43        # bit string (subjectPublicKey). The offset of the
44        # bit string is 26, so drop the first 26 bytes.
45        key_bytes = key.get_public_bytes()[26:]
46
47    return hex_to_c_array(key_bytes)
48
49
50@click.argument('outfile')
51@click.option('--bl2_rot_priv_key_0', metavar='filename', required=True)
52@click.option('--bl2_rot_priv_key_1', metavar='filename', required=False)
53@click.option('--bl2_rot_priv_key_2', metavar='filename', required=False)
54@click.option('--bl2_rot_priv_key_3', metavar='filename', required=False)
55@click.option('--bl2_mcuboot_hw_key', metavar='string', required=True)
56@click.option('--template_path', metavar='filename', required=True)
57@click.option('--secure_debug_pk', metavar='key', required=False)
58@click.option('--huk', metavar='key', required=False)
59@click.option('--iak', metavar='key', required=False)
60@click.option('--boot_seed', metavar='seed', required=False)
61@click.option('--implementation_id', metavar='id', required=False)
62@click.option('--certification_reference', metavar='reference', default="",
63              required=False)
64@click.option('--verification_service_url', metavar='url', default="",
65              required=False)
66@click.option('--entropy_seed', metavar='seed', required=False)
67@click.command(help='''Creates a .c file with the given keys, using the\n
68               provisioning_data_template.jinja2 template which is located in
69               "template_path" and outputs it to "outfile"''')
70def generate_provisioning_data_c(outfile, bl2_rot_priv_key_0,
71                                 bl2_rot_priv_key_1, bl2_mcuboot_hw_key,
72                                 template_path, bl2_rot_priv_key_2,
73                                 bl2_rot_priv_key_3,
74                                 secure_debug_pk, huk, iak, boot_seed,
75                                 implementation_id,
76                                 certification_reference,
77                                 verification_service_url,
78                                 entropy_seed):
79
80    environment = Environment(loader=FileSystemLoader(template_path))
81    template = environment.get_template("provisioning_data_template.jinja2")
82
83    bl2_rot_pub_key_0_hash = ""
84    if bool(bl2_rot_priv_key_0) is True:
85        bl2_rot_pub_key_0_hash = get_key_hash_c_array(
86            bl2_rot_priv_key_0, bl2_mcuboot_hw_key)
87
88    bl2_rot_pub_key_1_hash = ""
89    if bool(bl2_rot_priv_key_1) is True:
90        bl2_rot_pub_key_1_hash = get_key_hash_c_array(
91            bl2_rot_priv_key_1, bl2_mcuboot_hw_key)
92
93    bl2_rot_pub_key_2_hash = ""
94    if bool(bl2_rot_priv_key_2) is True:
95        bl2_rot_pub_key_2_hash = get_key_hash_c_array(
96            bl2_rot_priv_key_2, bl2_mcuboot_hw_key)
97
98    bl2_rot_pub_key_3_hash = ""
99    if bool(bl2_rot_priv_key_3) is True:
100        bl2_rot_pub_key_3_hash = get_key_hash_c_array(
101            bl2_rot_priv_key_3, bl2_mcuboot_hw_key)
102
103    if bool(huk) is False:
104        huk = hex_to_c_array(os.urandom(32))
105
106    if bool(iak) is False:
107        iak = hex_to_c_array(os.urandom(32))
108
109    if bool(boot_seed) is False:
110        boot_seed = hex_to_c_array(os.urandom(32))
111
112    if bool(implementation_id) is False:
113        implementation_id = hex_to_c_array(os.urandom(32))
114
115    if bool(entropy_seed) is False:
116        entropy_seed = hex_to_c_array(os.urandom(64))
117
118    key_arrays = {
119        "bl2_rotpk_0": bl2_rot_pub_key_0_hash,
120        "bl2_rotpk_1": bl2_rot_pub_key_1_hash,
121        "bl2_rotpk_2": bl2_rot_pub_key_2_hash,
122        "bl2_rotpk_3": bl2_rot_pub_key_3_hash,
123        "secure_debug_pk": secure_debug_pk,
124        "huk": huk,
125        "iak": iak,
126        "iak_len": "32",
127        "boot_seed": boot_seed,
128        "implementation_id": implementation_id,
129        "certification_reference": certification_reference,
130        "verification_service_url": verification_service_url,
131        "entropy_seed": entropy_seed
132    }
133
134    with open(outfile, "w") as F:
135        F.write(template.render(key_arrays))
136
137
138def hex_to_c_array(hex_val):
139    c_array = ""
140    for count, b in enumerate(hex_val):
141        if count % 8 == 0 and count != 0:
142            c_array = c_array + '\n'
143        c_array = c_array + "0x{:02x}, ".format(b)
144
145    return c_array
146
147
148if __name__ == '__main__':
149    generate_provisioning_data_c()
150