aboutsummaryrefslogtreecommitdiff
path: root/tools/binman/etype/nxp_imx8mcst.py
blob: 8221517b0c48e10bbe147d1c1ec351e7ba6c6fa5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# SPDX-License-Identifier: GPL-2.0+
# Copyright 2023-2024 Marek Vasut <marex@denx.de>
# Written with much help from Simon Glass <sjg@chromium.org>
#
# Entry-type module for generating the i.MX8M code signing tool
# input configuration file and invocation of cst on generated
# input configuration file and input data to be signed.
#

import configparser
import os
import struct

from collections import OrderedDict

from binman.entry import Entry
from binman.etype.mkimage import Entry_mkimage
from binman.etype.section import Entry_section
from binman import elf
from dtoc import fdt_util
from u_boot_pylib import tools

MAGIC_NXP_IMX_IVT = 0x412000d1
MAGIC_FITIMAGE    = 0xedfe0dd0

csf_config_template = """
[Header]
  Version = 4.3
  Hash Algorithm = sha256
  Engine = CAAM
  Engine Configuration = 0
  Certificate Format = X509
  Signature Format = CMS

[Install SRK]
  File = "SRK_1_2_3_4_table.bin"
  Source index = 0

[Install CSFK]
  File = "CSF1_1_sha256_4096_65537_v3_usr_crt.pem"

[Authenticate CSF]

[Unlock]
  Engine = CAAM
  Features = MID

[Install Key]
  Verification index = 0
  Target Index = 2
  File = "IMG1_1_sha256_4096_65537_v3_usr_crt.pem"

[Authenticate Data]
  Verification index = 2
  Blocks = 0x1234 0x78 0xabcd "data.bin"
"""

class Entry_nxp_imx8mcst(Entry_mkimage):
    """NXP i.MX8M CST .cfg file generator and cst invoker

    Properties / Entry arguments:
        - nxp,loader-address - loader address (SPL text base)
    """

    def __init__(self, section, etype, node):
        super().__init__(section, etype, node)
        self.required_props = ['nxp,loader-address']

    def ReadNode(self):
        super().ReadNode()
        self.loader_address = fdt_util.GetInt(self._node, 'nxp,loader-address')
        self.srk_table = os.getenv('SRK_TABLE', fdt_util.GetString(self._node, 'nxp,srk-table', 'SRK_1_2_3_4_table.bin'))
        self.csf_crt = os.getenv('CSF_KEY', fdt_util.GetString(self._node, 'nxp,csf-crt', 'CSF1_1_sha256_4096_65537_v3_usr_crt.pem'))
        self.img_crt = os.getenv('IMG_KEY', fdt_util.GetString(self._node, 'nxp,img-crt', 'IMG1_1_sha256_4096_65537_v3_usr_crt.pem'))
        self.unlock = fdt_util.GetBool(self._node, 'nxp,unlock')
        self.ReadEntries()

    def BuildSectionData(self, required):
        data, input_fname, uniq = self.collect_contents_to_file(
            self._entries.values(), 'input')

        # Parse the input data and figure out what it is that is being signed.
        # - If it is mkimage'd imx8mimage, then extract to be signed data size
        #   from imx8mimage header, and calculate CSF blob offset right past
        #   the SPL from this information.
        # - If it is fitImage, then pad the image to 4k, add generated IVT and
        #   sign the whole payload, then append CSF blob at the end right past
        #   the IVT.
        signtype = struct.unpack('<I', data[:4])[0]
        signbase = self.loader_address
        signsize = 0
        if signtype == MAGIC_NXP_IMX_IVT: # SPL/imx8mimage
            # Sign the payload including imx8mimage header
            # (extra 0x40 bytes before the payload)
            signbase -= 0x40
            signsize = struct.unpack('<I', data[24:28])[0] - signbase
            # Remove mkimage generated padding from the end of data
            data = data[:signsize]
        elif signtype == MAGIC_FITIMAGE: # fitImage
            # Align fitImage to 4k
            signsize = tools.align(len(data), 0x1000)
            data += tools.get_bytes(0, signsize - len(data))
            # Add generated IVT
            data += struct.pack('<I', MAGIC_NXP_IMX_IVT)
            data += struct.pack('<I', signbase + signsize) # IVT base
            data += struct.pack('<I', 0)
            data += struct.pack('<I', 0)
            data += struct.pack('<I', 0)
            data += struct.pack('<I', signbase + signsize) # IVT base
            data += struct.pack('<I', signbase + signsize + 0x20) # CSF base
            data += struct.pack('<I', 0)
        else:
            # Unknown section type, pass input data through.
            return data

        # Write out customized data to be signed
        output_dname = tools.get_output_filename(f'nxp.cst-input-data.{uniq}')
        tools.write_file(output_dname, data)

        # Generate CST configuration file used to sign payload
        cfg_fname = tools.get_output_filename('nxp.csf-config-txt.%s' % uniq)
        config = configparser.ConfigParser()
        # Do not make key names lowercase
        config.optionxform = str
        # Load configuration template and modify keys of interest
        config.read_string(csf_config_template)
        config['Install SRK']['File'] = '"' + self.srk_table + '"'
        config['Install CSFK']['File'] = '"' + self.csf_crt + '"'
        config['Install Key']['File'] = '"' + self.img_crt + '"'
        config['Authenticate Data']['Blocks'] = hex(signbase) + ' 0 ' + hex(len(data)) + ' "' + str(output_dname) + '"'
        if not self.unlock:
            config.remove_section('Unlock')
        with open(cfg_fname, 'w') as cfgf:
            config.write(cfgf)

        output_fname = tools.get_output_filename(f'nxp.csf-output-blob.{uniq}')
        args = ['-i', cfg_fname, '-o', output_fname]
        if self.cst.run_cmd(*args) is not None:
            outdata = tools.read_file(output_fname)
            return data + outdata
        else:
            # Bintool is missing; just use the input data as the output
            self.record_missing_bintool(self.cst)
            return data

    def SetImagePos(self, image_pos):
        # Customized SoC specific SetImagePos which skips the mkimage etype
        # implementation and removes the 0x48 offset introduced there. That
        # offset is only used for uImage/fitImage, which is not the case in
        # here.
        upto = 0x00
        for entry in super().GetEntries().values():
            entry.SetOffsetSize(upto, None)

            # Give up if any entries lack a size
            if entry.size is None:
                return
            upto += entry.size

        Entry_section.SetImagePos(self, image_pos)

    def AddBintools(self, btools):
        super().AddBintools(btools)
        self.cst = self.AddBintool(btools, 'cst')