diff mbox series

[v2,2/8] capsule: authenticate: Embed capsule public key in platform's dtb

Message ID 20230624134118.944567-3-sughosh.ganu@linaro.org
State New
Headers show
Series Integrate EFI capsule tasks into u-boot's build flow | expand

Commit Message

Sughosh Ganu June 24, 2023, 1:41 p.m. UTC
The EFI capsule authentication logic in u-boot expects the public key
in the form of an EFI Signature List(ESL) to be provided as part of
the platform's dtb. Currently, the embedding of the ESL file into the
dtb needs to be done manually. Use the fdt_add_pubkey tool from binman
for embedding the ESL.

The capsule update feature is supported for both raw and FIT
images. Embedding of the ESL for raw images needs to be done through a
binman entry, fdt-esl-embed. For the FIT image, a couple of properties
have been added to the fit node which facilitate embedding the ESL
into all the DTB's that are packaged into the FIT image.

The path to the ESL file is specified through the
CONFIG_EFI_CAPSULE_ESL_FILE symbol.

Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
---
Changes since V1:
* Achieve the embedding of the ESL into the DTB through binman
* Add an entry type fdt-esl-embed for embedding the ESL for raw
  images.
* Add logic in binman's fit entry type for embedding the ESL into all
  the DTB's which are part of the FIT image.
* Add corresponding documentation entries in binman for the above
  changes.

 lib/efi_loader/Kconfig               | 11 ++++
 tools/binman/btool/fdt_add_pubkey.py | 73 +++++++++++++++++++++++++
 tools/binman/entries.rst             | 49 +++++++++++++++++
 tools/binman/etype/fdt_esl_embed.py  | 80 ++++++++++++++++++++++++++++
 tools/binman/etype/fit.py            | 31 +++++++++++
 5 files changed, 244 insertions(+)
 create mode 100644 tools/binman/btool/fdt_add_pubkey.py
 create mode 100644 tools/binman/etype/fdt_esl_embed.py
diff mbox series

Patch

diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
index c5835e6ef6..1326a1d109 100644
--- a/lib/efi_loader/Kconfig
+++ b/lib/efi_loader/Kconfig
@@ -234,6 +234,17 @@  config EFI_CAPSULE_MAX
 	  Select the max capsule index value used for capsule report
 	  variables. This value is used to create CapsuleMax variable.
 
+config EFI_CAPSULE_ESL_FILE
+	string "Path to the EFI Signature List File"
+	default ""
+	depends on EFI_CAPSULE_AUTHENTICATE
+	help
+	  Provides the absolute path to the EFI Signature List
+	  file which will be embedded in the platform's device
+	  tree and used for capsule authentication at the time
+	  of capsule update.
+
+
 config EFI_DEVICE_PATH_TO_TEXT
 	bool "Device path to text protocol"
 	default y
diff --git a/tools/binman/btool/fdt_add_pubkey.py b/tools/binman/btool/fdt_add_pubkey.py
new file mode 100644
index 0000000000..071ad166ff
--- /dev/null
+++ b/tools/binman/btool/fdt_add_pubkey.py
@@ -0,0 +1,73 @@ 
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright 2023 Linaro Limited
+#
+"""Bintool implementation for fdt_add_pubkey tool
+
+fdt_add_pubkey is a tool used for embedding a public key
+into the DTB file.
+
+Currently, this is being used for embedding the EFI Signature
+List(ESL) file into the DTB provided. The contents of the ESL
+file get added as a property, capsule-key under the signature
+node.
+
+The following are the command line options to be provided to the tool.
+Options:
+        -a <algo>       Cryptographic algorithm. Optional parameter, default value: sha1,rsa2048
+        -e <esl file>   EFI Signature List(ESL) file to embed into the FDT
+        -k <keydir>     Directory with public key. Optional parameter, default value: .
+        -n <keyname>    Public key name. Optional parameter, default value: key
+        -r <conf|image> Required: If present this indicates that the key must be verified for the image / configuration to be considered valid.
+        <fdt blob>      FDT blob file for adding of the public key. Required parameter.
+
+"""
+
+from binman import bintool
+
+class Bintoolfdt_add_pubkey(bintool.Bintool):
+    """Handles the 'fdt_add_pubkey' tool
+
+    This bintool supports running the fdt_add_pubkey tool for
+    embedding the public key into the dtb file provided.
+
+    Currently, this is being used for embedding the EFI Signature
+    List(ESL) file into the DTB provided.
+    """
+    def __init__(self, name):
+        super().__init__(name, 'Tool for generating adding pubkey to platform dtb')
+
+    def add_esl(self, esl_fname, dtb_fname):
+        """Add an ESL public key into the DTB
+
+        Args:
+            esl_fname: Path to the ESL file
+            dtb_name: Path to the DTB file
+
+        Returns:
+            None
+        """
+        args = [
+            f'-e',
+            esl_fname,
+            dtb_fname
+        ]
+
+        self.run_cmd(*args)
+
+    def fetch(self, method):
+        """Fetch handler for fdt_add_pubkey
+
+        This builds the tool from source
+
+        Returns:
+            tuple:
+                str: Filename of fetched file to copy to a suitable directory
+                str: Name of temp directory to remove, or None
+        """
+        if method != bintool.FETCH_BUILD:
+            return None
+        result = self.build_from_git(
+            'https://source.denx.de/u-boot/u-boot.git',
+            'tools',
+            'tools/fdt_add_pubkey')
+        return result
diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst
index b71af801fd..b89bb2cc1d 100644
--- a/tools/binman/entries.rst
+++ b/tools/binman/entries.rst
@@ -518,6 +518,38 @@  without the fdtmap header, so it can be viewed with `fdtdump`.
 
 
 
+.. _etype_fdt_esl_embed:
+
+Entry: fdt_esl_embed: An entry for embedding ESL contents into the DTB
+----------------------------------------------------------------------
+
+Properties / Entry Arguments:
+    - esl-file: Path to the ESL file. Usually specified
+      through the CONFIG_EFI_CAPSULE_ESL_FILE Kconfig
+      symbol. Mandatory property.
+    - dtb: A list of DTB file names into which the ESL
+      needs to be embedded. Provided as a string list,
+      similar to the compatible property. At least one
+      DTB file needs to be specified. Mandatory property.
+    - concat: An optional property which specifies the
+      files which need to be concatenated, along with
+      the third file into which the result needs to be
+      put into.
+      E.g. concat = "./u-boot-nodtb.bin",  "./u-boot.dtb", "./u-boot.bin";
+      The above concatenates u-boot-nodtb.bin and the
+      u-boot.dtb files, and puts the result into
+      u-boot.bin.
+
+This entry is used for embedding a public key in the form of an
+EFI Signature List(ESL) file into the DTB. The public key is
+used for authenticating capsules prior to capsule update.
+
+Once the dtb file has been updated by embedding the ESL, the concat
+property allows regenerating the u-boot.bin comprising of the
+u-boot-nodtb.bin and the updated dtb.
+
+
+
 .. _etype_files:
 
 Entry: files: A set of files arranged in a section
@@ -615,6 +647,23 @@  The top-level 'fit' node supports the following special properties:
         `of-list` meaning that `-a of-list="dtb1 dtb2..."` should be passed
         to binman.
 
+    In addition to the above properties, a couple of properties exist for embedding
+    the EFI Signature List(ESL) public key file into the DTB's that are put into
+    the FIT image.
+
+    The capsule update functionality is supported for the FIT and raw image types.
+    For the FIT image with capsule updates enabled along with capsule
+    authentication, it requires the the devicetree with the ESL public key.
+    Embedding the ESL into the DTBs is being done through a couple of additional
+    properties
+
+        embed-esl
+            A boolean property which specifies that the ESL needs to be embedded
+            into all the DTB's that are put on the FIT image.
+        esl-file
+            Path to the ESL file that is to be embedded into the DTB's.
+
+
 Substitutions
 ~~~~~~~~~~~~~
 
diff --git a/tools/binman/etype/fdt_esl_embed.py b/tools/binman/etype/fdt_esl_embed.py
new file mode 100644
index 0000000000..708ab138fd
--- /dev/null
+++ b/tools/binman/etype/fdt_esl_embed.py
@@ -0,0 +1,80 @@ 
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2023 Linaro Limited
+#
+# Entry-type module for Embedding ESL into FDTs
+#
+
+from binman.entry import Entry
+from dtoc import fdt_util
+from u_boot_pylib import tools
+from u_boot_pylib import tout
+
+class Entry_fdt_esl_embed(Entry):
+    """Entry for embedding the ESL file into devicetree(s)
+
+    This is an entry for embedding the EFI Signature List(ESL)
+    file into one or multiple devicetrees.
+
+    The ESL is a public key which is inserted as a property
+    under the signature node in the DTB. One or more dtb files
+    can be specified under the fdt-esl-embed node to embed the
+    ESL file.
+
+    Properties / Entry arguments:
+        - esl-file: Path to the ESL file. Usually specified
+          through the CONFIG_EFI_CAPSULE_ESL_FILE Kconfig
+          symbol. Mandatory property.
+        - dtb: A list of DTB file names into which the ESL
+          needs to be embedded. Provided as a string list,
+          similar to the compatible property. At least one
+          DTB file needs to be specified. Mandatory property.
+        - concat: An optional property which specifies the
+          files which need to be concatenated, along with
+          the third file into which the result needs to be
+          put into.
+          E.g. concat = "./u-boot-nodtb.bin",  "./u-boot.dtb", "./u-boot.bin";
+          The above concatenates u-boot-nodtb.bin and the
+          u-boot.dtb files, and puts the result into
+          u-boot.bin.
+    """
+    def __init__(self, section, etype, node):
+        super().__init__(section, etype, node)
+        self.dtbs = []
+
+    def ReadNode(self):
+        super().ReadNode()
+
+        self.esl = fdt_util.GetString(self._node, 'esl-file')
+        self.dtbs = fdt_util.GetStringList(self._node, 'dtb')
+        self.concat = fdt_util.GetStringList(self._node, 'concat')
+
+        if not self.esl:
+            self.Raise('Path to the ESL file needs to be provided')
+        if not self.dtbs:
+            self.Raise('At least one DTB needs to be provided')
+
+    def dtb_embed_esl(self):
+        for dtb in self.dtbs:
+            self.fdt_add_pubkey.add_esl(self.esl,
+                                        tools.get_input_filename(dtb))
+
+    def concat_binaries(self):
+        bins = self.concat
+        bin0_fname = tools.get_input_filename(bins[0])
+        bin1_fname = tools.get_input_filename(bins[1])
+        bin2_fname = tools.get_input_filename(bins[2])
+        with open(bin0_fname, 'rb') as fd1, open(bin1_fname, 'rb') as fd2, open(bin2_fname, 'wb') as fd3:
+            bin1 = fd1.read()
+            bin2 = fd2.read()
+            bin1 += bin2
+
+            fd3.write(bin1)
+
+    def ObtainContents(self):
+        self.dtb_embed_esl()
+
+        if self.concat:
+            self.concat_binaries()
+
+    def AddBintools(self, btools):
+        self.fdt_add_pubkey = self.AddBintool(btools, 'fdt_add_pubkey')
diff --git a/tools/binman/etype/fit.py b/tools/binman/etype/fit.py
index c395706ece..a745d83b6f 100644
--- a/tools/binman/etype/fit.py
+++ b/tools/binman/etype/fit.py
@@ -20,6 +20,7 @@  OPERATIONS = {
     'gen-fdt-nodes': OP_GEN_FDT_NODES,
     'split-elf': OP_SPLIT_ELF,
     }
+esl_embed_done = False
 
 class Entry_fit(Entry_section):
 
@@ -81,6 +82,22 @@  class Entry_fit(Entry_section):
             `of-list` meaning that `-a of-list="dtb1 dtb2..."` should be passed
             to binman.
 
+    In addition to the above properties, a couple of properties exist for embedding
+    the EFI Signature List(ESL) public key file into the DTB's that are put into
+    the FIT image.
+
+    The capsule update functionality is supported for the FIT and raw image types.
+    For the FIT image with capsule updates enabled along with capsule
+    authentication, it requires the the devicetree with the ESL public key.
+    Embedding the ESL into the DTBs is being done through a couple of additional
+    properties
+
+        embed-esl
+            A boolean property which specifies that the ESL needs to be embedded
+            into all the DTB's that are put on the FIT image.
+        esl-file
+            Path to the ESL file that is to be embedded into the DTB's.
+
     Substitutions
     ~~~~~~~~~~~~~
 
@@ -363,6 +380,9 @@  class Entry_fit(Entry_section):
                 self._fdts = fdts.split()
         self._fit_default_dt = self.GetEntryArgsOrProps([EntryArg('default-dt',
                                                                   str)])[0]
+        self.embed_esl = fdt_util.GetBool(self._node, 'embed-esl')
+        if self.embed_esl:
+            self.esl = fdt_util.GetString(self._node, 'esl-file')
 
     def _get_operation(self, base_node, node):
         """Get the operation referenced by a subnode
@@ -434,6 +454,15 @@  class Entry_fit(Entry_section):
         Returns:
             bytes: Contents of the section
         """
+
+        global esl_embed_done
+
+        if self.embed_esl and not esl_embed_done:
+            for dtb in self._fdts:
+                self.fdt_add_pubkey.add_esl(self.esl,
+                                            tools.get_input_filename(dtb + '.dtb'))
+                esl_embed_done = True
+
         data = self._build_input()
         uniq = self.GetUniqueName()
         input_fname = tools.get_output_filename(f'{uniq}.itb')
@@ -825,6 +854,8 @@  class Entry_fit(Entry_section):
     def AddBintools(self, btools):
         super().AddBintools(btools)
         self.mkimage = self.AddBintool(btools, 'mkimage')
+        if self.embed_esl:
+            self.fdt_add_pubkey = self.AddBintool(btools, 'fdt_add_pubkey')
 
     def CheckMissing(self, missing_list):
         # We must use our private entry list for this since generator nodes