@@ -179,4 +179,241 @@ over the image's ``data`` property value only (i.e. the raw image content,
179179not any FDT metadata). The algorithm is given by the hash node's ``algo ``
180180property and the resulting digest is stored in its ``value `` property.
181181
182+ Worked example
183+ ~~~~~~~~~~~~~~
184+
185+ This section walks through a concrete FIT to show exactly which bytes are
186+ included in a configuration signature hash.
187+
188+ Source
189+ ^^^^^^
190+
191+ Consider the following minimal FIT source::
192+
193+ / {
194+ description = "Example FIT";
195+ #address-cells = <1>;
196+
197+ images {
198+ kernel {
199+ data = /incbin/("vmlinuz");
200+ type = "kernel";
201+ arch = "arm64";
202+ os = "linux";
203+ compression = "none";
204+ load = <0x40000000>;
205+ entry = <0x40000000>;
206+ hash-1 {
207+ algo = "sha256";
208+ };
209+ };
210+ fdt-1 {
211+ data = /incbin/("board.dtb");
212+ type = "flat_dt";
213+ arch = "arm64";
214+ compression = "none";
215+ hash-1 {
216+ algo = "sha256";
217+ };
218+ };
219+ };
220+ configurations {
221+ default = "conf-1";
222+ conf-1 {
223+ description = "Boot Linux";
224+ compatible = "vendor,board";
225+ kernel = "kernel";
226+ fdt = "fdt-1";
227+ signature-1 {
228+ algo = "sha256,rsa2048";
229+ key-name-hint = "dev";
230+ sign-images = "kernel", "fdt";
231+ };
232+ };
233+ };
234+ };
235+
236+ After signing
237+ ^^^^^^^^^^^^^
238+
239+ During signing, the signer adds a ``value `` property to each hash node
240+ containing the image digest, and adds ``value ``, ``hashed-nodes ``,
241+ ``hashed-strings `` and other properties to the signature node. The
242+ resulting FIT looks like this::
243+
244+ / {
245+ description = "Example FIT";
246+ timestamp = <0x67d96bac>;
247+ #address-cells = <1>;
248+
249+ images {
250+ kernel {
251+ data = <...kernel data...>;
252+ type = "kernel";
253+ arch = "arm64";
254+ os = "linux";
255+ compression = "none";
256+ load = <0x40000000>;
257+ entry = <0x40000000>;
258+ hash-1 {
259+ algo = "sha256";
260+ value = <...32-byte SHA-256 digest of kernel data...>;
261+ };
262+ };
263+ fdt-1 {
264+ data = <...devicetree data...>;
265+ type = "flat_dt";
266+ arch = "arm64";
267+ compression = "none";
268+ hash-1 {
269+ algo = "sha256";
270+ value = <...32-byte SHA-256 digest of devicetree data...>;
271+ };
272+ };
273+ };
274+ configurations {
275+ default = "conf-1";
276+ conf-1 {
277+ description = "Boot Linux";
278+ compatible = "vendor,board";
279+ kernel = "kernel";
280+ fdt = "fdt-1";
281+ signature-1 {
282+ algo = "sha256,rsa2048";
283+ key-name-hint = "dev";
284+ sign-images = "kernel", "fdt";
285+ value = <...256-byte RSA-2048 signature...>;
286+ hashed-nodes = "/", "/configurations/conf-1",
287+ "/images/kernel", "/images/kernel/hash-1",
288+ "/images/fdt-1", "/images/fdt-1/hash-1";
289+ hashed-strings = <0x00000000 0x000000d4>;
290+ timestamp = <0x67d96bac>;
291+ signer-name = "mkimage";
292+ signer-version = "2025.04-rc3";
293+ };
294+ };
295+ };
296+ };
297+
298+ Node list
299+ ^^^^^^^^^
300+
301+ For the configuration signature ``/configurations/conf-1/signature-1 ``, the
302+ node list is:
303+
304+ - ``/ ``
305+ - ``/configurations/conf-1 ``
306+ - ``/images/kernel ``
307+ - ``/images/kernel/hash-1 ``
308+ - ``/images/fdt-1 ``
309+ - ``/images/fdt-1/hash-1 ``
310+
311+ What is hashed
312+ ^^^^^^^^^^^^^^
313+
314+ The following shows the signed FIT with **bold ** indicating the parts that are
315+ included in the configuration signature hash. Lines in normal weight are not
316+ hashed. Note that node braces (``{ `` and ``} ``) represent ``FDT_BEGIN_NODE ``
317+ and ``FDT_END_NODE `` tokens respectively; these are included whenever the node
318+ or its parent is in the node list.
319+
320+ .. parsed-literal ::
321+
322+ **/ { **
323+ **description = "Example FIT"; **
324+ **timestamp = <0x67d96bac>; **
325+ **#address-cells = <1>; **
326+
327+ **images { **
328+ **kernel { **
329+ data = <...kernel data...>;
330+ **type = "kernel"; **
331+ **arch = "arm64"; **
332+ **os = "linux"; **
333+ **compression = "none"; **
334+ **load = <0x40000000>; **
335+ **entry = <0x40000000>; **
336+ **hash-1 { **
337+ **algo = "sha256"; **
338+ **value = <...32-byte SHA-256 digest...>; **
339+ **}; **
340+ **}; **
341+ **fdt-1 { **
342+ data = <...devicetree data...>;
343+ **type = "flat_dt"; **
344+ **arch = "arm64"; **
345+ **compression = "none"; **
346+ **hash-1 { **
347+ **algo = "sha256"; **
348+ **value = <...32-byte SHA-256 digest...>; **
349+ **}; **
350+ **}; **
351+ **}; **
352+ **configurations { **
353+ default = "conf-1";
354+ **conf-1 { **
355+ **description = "Boot Linux"; **
356+ **compatible = "vendor,board"; **
357+ **kernel = "kernel"; **
358+ **fdt = "fdt-1"; **
359+ **signature-1 { **
360+ algo = "sha256,rsa2048";
361+ key-name-hint = "dev";
362+ sign-images = "kernel", "fdt";
363+ value = <...256-byte RSA-2048 signature...>;
364+ hashed-nodes = "/", "/configurations/conf-1", ...;
365+ hashed-strings = <0x00000000 0x000000d4>;
366+ timestamp = <0x67d96bac>;
367+ signer-name = "mkimage";
368+ signer-version = "2025.04-rc3";
369+ **}; **
370+ **}; **
371+ **}; **
372+ **}; **
373+
374+ Strings block:
375+ **description\\ 0 **
376+ **timestamp\\ 0 **
377+ **#address-cells\\ 0 **
378+ **type\\ 0 **
379+ **arch\\ 0 **
380+ **os\\ 0 **
381+ **compression\\ 0 **
382+ **load\\ 0 **
383+ **entry\\ 0 **
384+ **algo\\ 0 **
385+ **value\\ 0 **
386+ **compatible\\ 0 **
387+ **kernel\\ 0 **
388+ **fdt\\ 0 **
389+ default\\ 0
390+ padding\\ 0
391+
392+ Key points to note:
393+
394+ - The ``data `` properties of both image nodes are excluded since image-data
395+ integrity is verified separately through the hash nodes.
396+ - The ``default `` property of the ``configurations `` node is not hashed because
397+ that node is not in the node list (only its parent ``/ `` is). This is safe
398+ because the bootloader selects a configuration by its own logic, not by
399+ trusting the default.
400+ - All properties of ``signature-1 `` are excluded because that node is not in the
401+ node list. Its braces are included because its parent ``conf-1 `` is. This is
402+ safe because the signature itself is verified against a trusted public key,
403+ not by hashing.
404+ - The ``images `` and ``configurations `` nodes have no properties of their own,
405+ but their braces are included because their parent ``/ `` is in the node list.
406+ This serves as a structural sanity check, ensuring that an attacker cannot
407+ inject unexpected nodes into the tree without detection.
408+ - The strings-block region contains the property name strings referenced by the
409+ hashed nodes. Although the string-table offset in each ``FDT_PROP `` token is
410+ hashed, the string at that offset must also be protected; otherwise an
411+ attacker could rename a property (e.g. changing ``algo `` to something
412+ unrecognised) to trick the bootloader into skipping verification. The hashed
413+ region should therefore always start at offset 0.
414+
415+ The complete byte sequence (structure-block regions plus strings-block region)
416+ is hashed with SHA-256. The resulting digest is then signed with the RSA-2048
417+ private key to produce the signature ``value ``.
418+
182419.. sectionauthor :: Simon Glass <sjg@chromium.org>
0 commit comments