1# SPDX-License-Identifier: GPL-2.0+
2# Copyright (c) 2022-2023 Texas Instruments Incorporated - https://www.ti.com/
3# Written by Neha Malcom Francis <n-francis@ti.com>
4#
5
6# Support for generation of TI secured bootloaders booted by ROM
7
8from binman.entry import EntryArg
9from binman.etype.x509_cert import Entry_x509_cert
10
11import hashlib
12
13from dtoc import fdt_util
14from u_boot_pylib  import tools
15
16VALID_SHAS = [256, 384, 512, 224]
17SHA_OIDS = {256:'2.16.840.1.101.3.4.2.1',
18            384:'2.16.840.1.101.3.4.2.2',
19            512:'2.16.840.1.101.3.4.2.3',
20            224:'2.16.840.1.101.3.4.2.4'}
21
22class Entry_ti_secure_rom(Entry_x509_cert):
23    """Entry containing a TI x509 certificate binary for images booted by ROM
24
25    Properties / Entry arguments:
26        - keyfile: Filename of file containing key to sign binary with
27        - combined: boolean if device follows combined boot flow
28        - countersign: boolean if device contains countersigned system firmware
29        - load: load address of SPL
30        - sw-rev: software revision
31        - sha: Hash function to be used for signing
32        - core: core on which bootloader runs, valid cores are 'secure' and 'public'
33        - content: phandle of SPL in case of legacy bootflow or phandles of component binaries
34          in case of combined bootflow
35        - core-opts (optional): lockstep (0) or split (2) mode set to 0 by default
36
37    The following properties are only for generating a combined bootflow binary:
38        - sysfw-inner-cert: boolean if binary contains sysfw inner certificate
39        - dm-data: boolean if binary contains dm-data binary
40        - content-sbl: phandle of SPL binary
41        - content-sysfw: phandle of sysfw binary
42        - content-sysfw-data: phandle of sysfw-data or tifs-data binary
43        - content-sysfw-inner-cert (optional): phandle of sysfw inner certificate binary
44        - content-dm-data (optional): phandle of dm-data binary
45        - load-sysfw: load address of sysfw binary
46        - load-sysfw-data: load address of sysfw-data or tifs-data binary
47        - load-sysfw-inner-cert (optional): load address of sysfw inner certificate binary
48        - load-dm-data (optional): load address of dm-data binary
49
50    Output files:
51        - input.<unique_name> - input file passed to openssl
52        - config.<unique_name> - input file generated for openssl (which is
53          used as the config file)
54        - cert.<unique_name> - output file generated by openssl (which is
55          used as the entry contents)
56
57    openssl signs the provided data, using the TI templated config file and
58    writes the signature in this entry. This allows verification that the
59    data is genuine.
60    """
61    def __init__(self, section, etype, node):
62        super().__init__(section, etype, node)
63        self.openssl = None
64
65    def ReadNode(self):
66        super().ReadNode()
67        self.combined = fdt_util.GetBool(self._node, 'combined', False)
68        self.countersign = fdt_util.GetBool(self._node, 'countersign', False)
69        self.load_addr = fdt_util.GetInt(self._node, 'load', 0x00000000)
70        self.sw_rev = fdt_util.GetInt(self._node, 'sw-rev', 1)
71        self.sha = fdt_util.GetInt(self._node, 'sha', 512)
72        self.core = fdt_util.GetString(self._node, 'core', 'secure')
73        self.bootcore_opts = fdt_util.GetInt(self._node, 'core-opts')
74        self.key_fname = self.GetEntryArgsOrProps([
75            EntryArg('keyfile', str)], required=True)[0]
76        if self.combined:
77            self.sysfw_inner_cert = fdt_util.GetBool(self._node, 'sysfw-inner-cert', False)
78            self.load_addr_sysfw = fdt_util.GetInt(self._node, 'load-sysfw', 0x00000000)
79            self.load_addr_sysfw_data = fdt_util.GetInt(self._node, 'load-sysfw-data', 0x00000000)
80            self.dm_data = fdt_util.GetBool(self._node, 'dm-data', False)
81            if self.dm_data:
82                self.load_addr_dm_data = fdt_util.GetInt(self._node, 'load-dm-data', 0x00000000)
83        self.req_dist_name = {'C': 'US',
84                    'ST': 'TX',
85                    'L': 'Dallas',
86                    'O': 'Texas Instruments Incorporated',
87                    'OU': 'Processors',
88                    'CN': 'TI Support',
89                    'emailAddress': 'support@ti.com'}
90        self.debug = fdt_util.GetBool(self._node, 'debug', False)
91
92    def NonCombinedGetCertificate(self, required):
93        """Generate certificate for legacy boot flow
94
95        Args:
96            required: True if the data must be present, False if it is OK to
97                return None
98
99        Returns:
100            bytes content of the entry, which is the certificate binary for the
101                provided data
102        """
103        if self.bootcore_opts is None:
104            self.bootcore_opts = 0
105
106        if self.core == 'secure':
107            if self.countersign:
108                self.cert_type = 3
109            else:
110                self.cert_type = 2
111            self.bootcore = 0
112        else:
113            self.cert_type = 1
114            self.bootcore = 16
115
116        return super().GetCertificate(required=required, type='rom')
117
118    def CombinedGetCertificate(self, required):
119        """Generate certificate for combined boot flow
120
121        Args:
122            required: True if the data must be present, False if it is OK to
123                return None
124
125        Returns:
126            bytes content of the entry, which is the certificate binary for the
127                provided data
128        """
129        uniq = self.GetUniqueName()
130
131        self.num_comps = 3
132        self.sha_type = SHA_OIDS[self.sha]
133
134        if self.bootcore_opts is None:
135            self.bootcore_opts = 0
136
137        # sbl
138        self.content = fdt_util.GetPhandleList(self._node, 'content-sbl')
139        input_data_sbl = self.GetContents(required)
140        if input_data_sbl is None:
141            return None
142
143        input_fname_sbl = tools.get_output_filename('input.%s' % uniq)
144        tools.write_file(input_fname_sbl, input_data_sbl)
145
146        indata_sbl = tools.read_file(input_fname_sbl)
147        self.hashval_sbl = hashlib.sha512(indata_sbl).hexdigest()
148        self.imagesize_sbl = len(indata_sbl)
149
150        # sysfw
151        self.content = fdt_util.GetPhandleList(self._node, 'content-sysfw')
152        input_data_sysfw = self.GetContents(required)
153
154        input_fname_sysfw = tools.get_output_filename('input.%s' % uniq)
155        tools.write_file(input_fname_sysfw, input_data_sysfw)
156
157        indata_sysfw = tools.read_file(input_fname_sysfw)
158        self.hashval_sysfw = hashlib.sha512(indata_sysfw).hexdigest()
159        self.imagesize_sysfw = len(indata_sysfw)
160
161        # sysfw data
162        self.content = fdt_util.GetPhandleList(self._node, 'content-sysfw-data')
163        input_data_sysfw_data = self.GetContents(required)
164
165        input_fname_sysfw_data = tools.get_output_filename('input.%s' % uniq)
166        tools.write_file(input_fname_sysfw_data, input_data_sysfw_data)
167
168        indata_sysfw_data = tools.read_file(input_fname_sysfw_data)
169        self.hashval_sysfw_data = hashlib.sha512(indata_sysfw_data).hexdigest()
170        self.imagesize_sysfw_data = len(indata_sysfw_data)
171
172        # sysfw inner cert
173        self.sysfw_inner_cert_ext_boot_block = ""
174        self.sysfw_inner_cert_ext_boot_sequence_string = ""
175        imagesize_sysfw_inner_cert = 0
176        if self.sysfw_inner_cert:
177            self.content = fdt_util.GetPhandleList(self._node, 'content-sysfw-inner-cert')
178            input_data_sysfw_inner_cert = self.GetContents(required)
179
180            input_fname_sysfw_inner_cert = tools.get_output_filename('input.%s' % uniq)
181            tools.write_file(input_fname_sysfw_inner_cert, input_data_sysfw_inner_cert)
182
183            indata_sysfw_inner_cert = tools.read_file(input_fname_sysfw_inner_cert)
184            hashval_sysfw_inner_cert = hashlib.sha512(indata_sysfw_inner_cert).hexdigest()
185            imagesize_sysfw_inner_cert = len(indata_sysfw_inner_cert)
186            self.num_comps += 1
187            self.sysfw_inner_cert_ext_boot_sequence_string = "sysfw_inner_cert=SEQUENCE:sysfw_inner_cert"
188            self.sysfw_inner_cert_ext_boot_block = f"""[sysfw_inner_cert]
189compType = INTEGER:3
190bootCore = INTEGER:0
191compOpts = INTEGER:0
192destAddr = FORMAT:HEX,OCT:00000000
193compSize = INTEGER:{imagesize_sysfw_inner_cert}
194shaType  = OID:{self.sha_type}
195shaValue = FORMAT:HEX,OCT:{hashval_sysfw_inner_cert}"""
196
197        # dm data
198        self.dm_data_ext_boot_sequence_string = ""
199        self.dm_data_ext_boot_block = ""
200        imagesize_dm_data = 0
201        if self.dm_data:
202            self.content = fdt_util.GetPhandleList(self._node, 'content-dm-data')
203            input_data_dm_data = self.GetContents(required)
204
205            input_fname_dm_data = tools.get_output_filename('input.%s' % uniq)
206            tools.write_file(input_fname_dm_data, input_data_dm_data)
207
208            indata_dm_data = tools.read_file(input_fname_dm_data)
209            hashval_dm_data = hashlib.sha512(indata_dm_data).hexdigest()
210            imagesize_dm_data = len(indata_dm_data)
211            self.num_comps += 1
212            self.dm_data_ext_boot_sequence_string = "dm_data=SEQUENCE:dm_data"
213            self.dm_data_ext_boot_block = f"""[dm_data]
214compType = INTEGER:17
215bootCore = INTEGER:16
216compOpts = INTEGER:0
217destAddr = FORMAT:HEX,OCT:{self.load_addr_dm_data:08x}
218compSize = INTEGER:{imagesize_dm_data}
219shaType  = OID:{self.sha_type}
220shaValue = FORMAT:HEX,OCT:{hashval_dm_data}"""
221
222        self.total_size = self.imagesize_sbl +  self.imagesize_sysfw + self.imagesize_sysfw_data + imagesize_sysfw_inner_cert + imagesize_dm_data
223        return super().GetCertificate(required=required, type='rom-combined')
224
225    def GetCertificate(self, required):
226        """Get the contents of this entry
227
228        Args:
229            required: True if the data must be present, False if it is OK to
230                return None
231
232        Returns:
233            bytes content of the entry, which is the certificate binary for the
234                provided data
235        """
236        if self.combined:
237            return self.CombinedGetCertificate(required)
238        else:
239            return self.NonCombinedGetCertificate(required)
240
241    def ObtainContents(self):
242        data = self.data
243        if data is None:
244            data = self.GetCertificate(False)
245        if data is None:
246            return False
247        self.SetContents(data)
248        return True
249
250    def ProcessContents(self):
251        # The blob may have changed due to WriteSymbols()
252        data = self.data
253        return self.ProcessContentsUpdate(data)
254
255    def AddBintools(self, btools):
256        super().AddBintools(btools)
257        self.openssl = self.AddBintool(btools, 'openssl')
258