]> git.dujemihanovic.xyz Git - u-boot.git/commitdiff
binman: implement signing FIT images during image build
authorAlexander Kochetkov <al.kochet@gmail.com>
Mon, 16 Sep 2024 08:24:46 +0000 (11:24 +0300)
committerSimon Glass <sjg@chromium.org>
Fri, 18 Oct 2024 20:10:22 +0000 (14:10 -0600)
The patch implement new property 'fit,sign' that can be declared
at the top-level 'fit' node. If that option is declared, fit tryies
to detect private keys directory among binman include directories.
That directory than passed to mkimage using '-k' flag and that enable
signing of FIT.

Signed-off-by: Alexander Kochetkov <al.kochet@gmail.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
Renumbered files, moved new tests to end:
Signed-off-by: Simon Glass <sjg@chromium.org>
tools/binman/btool/mkimage.py
tools/binman/entries.rst
tools/binman/etype/fit.py
tools/binman/ftest.py
tools/binman/test/340_fit_signature.dts [new file with mode: 0644]
tools/binman/test/340_rsa2048.key [new file with mode: 0644]
tools/binman/test/341_fit_signature.dts [new file with mode: 0644]
tools/binman/test/342_fit_signature.dts [new file with mode: 0644]

index 39a4c8c1432ffc3f95a555cd27a4c53691fdbb00..78d3301bc10e88891536813525095679c4bfd9c5 100644 (file)
@@ -22,7 +22,7 @@ class Bintoolmkimage(bintool.Bintool):
 
     # pylint: disable=R0913
     def run(self, reset_timestamp=False, output_fname=None, external=False,
-            pad=None, align=None):
+            pad=None, align=None, priv_keys_dir=None):
         """Run mkimage
 
         Args:
@@ -34,6 +34,7 @@ class Bintoolmkimage(bintool.Bintool):
                 other things to be easily added later, if required, such as
                 signatures
             align: Bytes to use for alignment of the FIT and its external data
+            priv_keys_dir: Path to directory containing private keys
             version: True to get the mkimage version
         """
         args = []
@@ -45,6 +46,8 @@ class Bintoolmkimage(bintool.Bintool):
             args += ['-B', f'{align:x}']
         if reset_timestamp:
             args.append('-t')
+        if priv_keys_dir:
+            args += ['-k', f'{priv_keys_dir}']
         if output_fname:
             args += ['-F', output_fname]
         return self.run_cmd(*args)
index 3006c5914d67e7d2a8424e76ada0dbe83c2ef0fa..e918162fb48ecc55efd042b6361186166a820572 100644 (file)
@@ -864,6 +864,13 @@ The top-level 'fit' node supports the following special properties:
 
             fit,fdt-list-dir = "arch/arm/dts
 
+    fit,sign
+        Enable signing FIT images via mkimage as described in
+        verified-boot.rst. If the property is found, the private keys path is
+        detected among binman include directories and passed to mkimage via
+        -k flag. All the keys required for signing FIT must be available at
+        time of signing and must be located in single include directory.
+
 Substitutions
 ~~~~~~~~~~~~~
 
index 732510e2a0531ec8bf554d4ebb63bfa738f24c08..b5afbda41b58a28b4c9000de25a71fb27d90a49b 100644 (file)
@@ -9,6 +9,7 @@ import glob
 import os
 
 import libfdt
+import os
 
 from binman.entry import Entry, EntryArg
 from binman.etype.section import Entry_section
@@ -101,6 +102,14 @@ class Entry_fit(Entry_section):
             In this case the input directories are ignored and all devicetree
             files must be in that directory.
 
+        fit,sign
+            Enable signing FIT images via mkimage as described in
+            verified-boot.rst. If the property is found, the private keys path
+            is detected among binman include directories and passed to mkimage
+            via  -k flag. All the keys required for signing FIT must be
+            available at time of signing and must be located in single include
+            directory.
+
     Substitutions
     ~~~~~~~~~~~~~
 
@@ -426,6 +435,7 @@ class Entry_fit(Entry_section):
             self._remove_props = props.split()
         self.mkimage = None
         self.fdtgrep = None
+        self._fit_sign = None
 
     def ReadNode(self):
         super().ReadNode()
@@ -508,6 +518,45 @@ class Entry_fit(Entry_section):
         # are removed from self._entries later.
         self._priv_entries = dict(self._entries)
 
+    def _get_priv_keys_dir(self, data):
+        """Detect private keys path among binman include directories
+
+        Args:
+            data: FIT image in binary format
+
+        Returns:
+            str: Single path containing all private keys found or None
+
+        Raises:
+            ValueError: Filename 'rsa2048.key' not found in input path
+            ValueError: Multiple key paths found
+        """
+        def _find_keys_dir(node):
+            for subnode in node.subnodes:
+                if subnode.name.startswith('signature'):
+                    if subnode.props.get('key-name-hint') is None:
+                        continue
+                    hint = subnode.props['key-name-hint'].value
+                    name = tools.get_input_filename(f"{hint}.key")
+                    path = os.path.dirname(name)
+                    if path not in paths:
+                        paths.append(path)
+                else:
+                    _find_keys_dir(subnode)
+            return None
+
+        fdt = Fdt.FromData(data)
+        fdt.Scan()
+
+        paths = []
+
+        _find_keys_dir(fdt.GetRoot())
+
+        if len(paths) > 1:
+            self.Raise("multiple key paths found (%s)" % ",".join(paths))
+
+        return paths[0] if len(paths) else None
+
     def BuildSectionData(self, required):
         """Build FIT entry contents
 
@@ -538,6 +587,8 @@ class Entry_fit(Entry_section):
         align = self._fit_props.get('fit,align')
         if align is not None:
             args.update({'align': fdt_util.fdt32_to_cpu(align.value)})
+        if self._fit_props.get('fit,sign') is not None:
+            args.update({'priv_keys_dir': self._get_priv_keys_dir(data)})
         if self.mkimage.run(reset_timestamp=True, output_fname=output_fname,
                             **args) is None:
             if not self.GetAllowMissing():
index e3f231e4bccbc3caaec8d67294654cbdc191419a..156567ace778245464d9266ccfd2d73ddcf17b21 100644 (file)
@@ -7804,6 +7804,101 @@ fdt         fdtmap                Extract the devicetree blob from the fdtmap
         """Test that binman can produce an iMX8 image"""
         self._DoTestFile('339_nxp_imx8.dts')
 
+    def testFitSignSimple(self):
+        """Test that image with FIT and signature nodes can be signed"""
+        if not elf.ELF_TOOLS:
+            self.skipTest('Python elftools not available')
+        entry_args = {
+            'of-list': 'test-fdt1',
+            'default-dt': 'test-fdt1',
+            'atf-bl31-path': 'bl31.elf',
+        }
+        data = tools.read_file(self.TestFile("340_rsa2048.key"))
+        self._MakeInputFile("keys/rsa2048.key", data)
+
+        test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
+        keys_subdir = os.path.join(self._indir, "keys")
+        data = self._DoReadFileDtb(
+            '340_fit_signature.dts',
+            entry_args=entry_args,
+            extra_indirs=[test_subdir, keys_subdir])[0]
+
+        dtb = fdt.Fdt.FromData(data)
+        dtb.Scan()
+
+        conf = dtb.GetNode('/configurations/conf-uboot-1')
+        self.assertIsNotNone(conf)
+        signature = conf.FindNode('signature')
+        self.assertIsNotNone(signature)
+        self.assertIsNotNone(signature.props.get('value'))
+
+        images = dtb.GetNode('/images')
+        self.assertIsNotNone(images)
+        for subnode in images.subnodes:
+            signature = subnode.FindNode('signature')
+            self.assertIsNotNone(signature)
+            self.assertIsNotNone(signature.props.get('value'))
+
+    def testFitSignKeyNotFound(self):
+        """Test that missing keys raise an error"""
+        if not elf.ELF_TOOLS:
+            self.skipTest('Python elftools not available')
+        entry_args = {
+            'of-list': 'test-fdt1',
+            'default-dt': 'test-fdt1',
+            'atf-bl31-path': 'bl31.elf',
+        }
+        test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
+        with self.assertRaises(ValueError) as e:
+            self._DoReadFileDtb(
+                '340_fit_signature.dts',
+                entry_args=entry_args,
+                extra_indirs=[test_subdir])[0]
+        self.assertIn(
+            'Filename \'rsa2048.key\' not found in input path',
+            str(e.exception))
+
+    def testFitSignMultipleKeyPaths(self):
+        """Test that keys found in multiple paths raise an error"""
+        if not elf.ELF_TOOLS:
+            self.skipTest('Python elftools not available')
+        entry_args = {
+            'of-list': 'test-fdt1',
+            'default-dt': 'test-fdt1',
+            'atf-bl31-path': 'bl31.elf',
+        }
+        data = tools.read_file(self.TestFile("340_rsa2048.key"))
+        self._MakeInputFile("keys1/rsa2048.key", data)
+        data = tools.read_file(self.TestFile("340_rsa2048.key"))
+        self._MakeInputFile("keys2/conf-rsa2048.key", data)
+
+        test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
+        keys_subdir1 = os.path.join(self._indir, "keys1")
+        keys_subdir2 = os.path.join(self._indir, "keys2")
+        with self.assertRaises(ValueError) as e:
+            self._DoReadFileDtb(
+                '341_fit_signature.dts',
+                entry_args=entry_args,
+                extra_indirs=[test_subdir, keys_subdir1, keys_subdir2])[0]
+        self.assertIn(
+            'Node \'/binman/fit\': multiple key paths found',
+            str(e.exception))
+
+    def testFitSignNoSingatureNodes(self):
+        """Test that fit,sign doens't raise error if no signature nodes found"""
+        if not elf.ELF_TOOLS:
+            self.skipTest('Python elftools not available')
+        entry_args = {
+            'of-list': 'test-fdt1',
+            'default-dt': 'test-fdt1',
+            'atf-bl31-path': 'bl31.elf',
+        }
+        test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
+        self._DoReadFileDtb(
+            '342_fit_signature.dts',
+            entry_args=entry_args,
+            extra_indirs=[test_subdir])[0]
+
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/tools/binman/test/340_fit_signature.dts b/tools/binman/test/340_fit_signature.dts
new file mode 100644 (file)
index 0000000..9dce62e
--- /dev/null
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               fit {
+                       description = "test desc";
+                       #address-cells = <1>;
+                       fit,fdt-list = "of-list";
+                       fit,sign;
+
+                       images {
+                               u-boot {
+                                       description = "test u-boot";
+                                       type = "standalone";
+                                       arch = "arm64";
+                                       os = "u-boot";
+                                       compression = "none";
+                                       load = <0x00000000>;
+                                       entry = <0x00000000>;
+
+                                       u-boot-nodtb {
+                                       };
+
+                                       hash {
+                                               algo = "sha256";
+                                       };
+
+                                       signature {
+                                               algo = "sha256,rsa2048";
+                                               key-name-hint = "rsa2048";
+                                       };
+                               };
+                               @atf-SEQ {
+                                       fit,operation = "split-elf";
+                                       description = "test tf-a";
+                                       type = "firmware";
+                                       arch = "arm64";
+                                       os = "arm-trusted-firmware";
+                                       compression = "none";
+                                       fit,load;
+                                       fit,entry;
+                                       fit,data;
+
+                                       atf-bl31 {
+                                       };
+
+                                       hash {
+                                               algo = "sha256";
+                                       };
+
+                                       signature {
+                                               algo = "sha256,rsa2048";
+                                               key-name-hint = "rsa2048";
+                                       };
+                               };
+                               @fdt-SEQ {
+                                       description = "test fdt";
+                                       type = "flat_dt";
+                                       compression = "none";
+
+                                       hash {
+                                               algo = "sha256";
+                                       };
+
+                                       signature {
+                                               algo = "sha256,rsa2048";
+                                               key-name-hint = "rsa2048";
+                                       };
+                               };
+                       };
+
+                       configurations {
+                               default = "@conf-uboot-DEFAULT-SEQ";
+                               @conf-uboot-SEQ {
+                                       description = "uboot config";
+                                       fdt = "fdt-SEQ";
+                                       fit,firmware = "u-boot";
+                                       fit,loadables;
+
+                                       hash {
+                                               algo = "sha256";
+                                       };
+
+                                       signature {
+                                               algo = "sha256,rsa2048";
+                                               key-name-hint = "rsa2048";
+                                               sign-images = "firmware", "loadables", "fdt";
+                                       };
+                               };
+                       };
+               };
+       };
+};
diff --git a/tools/binman/test/340_rsa2048.key b/tools/binman/test/340_rsa2048.key
new file mode 100644 (file)
index 0000000..e74b20c
--- /dev/null
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDVUiT2JAF8Ajcx
+3XTB5qdGxuPMVFcXKJH+4L66oSt4YUBGi1bClo80U2azu08BTzk2Jzv6hez/mvzL
+hBvL3WnPwMl5vdOxb1kvUQyKLSw2bkM8VB0X1jGsKsKjzArg/aI8RknfiaSc5jua
+2lqwUFwv2RMF8jvIMN/1GnTLdECeMFVgVFSFkzIocISAHGPoGUOxTf8xK7o0x4RX
+NzB+95RtIqTQ5Az/KPVCOcQR5ETrUBXHF1I0rYjJjHHO4dUxxfDqFabt60EzQ/R2
+oZu58C4y0TrRI98g4hVPBYapildWjaNQm1Exa4ZaSDVl01OXsFW9Dm80PqfW4tTH
+Cm4nuCq5AgMBAAECggEBAIoG5b2SHJfFwzrzpQmVmeTU6i6a3+MvMBAwEZkmkb8J
+hhJfNFsiGjTsRgbDiuI5BbbBejCmmWvmN+3jZCzr7fwsLPEl36TufFF+atO5WOM7
+Qyv07QIwaOGSpXBgpSVhV6kSfdgy8p1G54hSAt4UkSGwnnt5ei8VWMP6Q1oltW3k
+f9DQ/ar4UEVa4jlJU3xqchcUTiKBKSH6pMC/Fqlq8x5JTLmk1Yb6C2UNcgJYez1u
+sHkdCA0FG3rFPrpFoQ1LUjMj1uEYNAxM3jOxE7Uvmk4yo9WpQDY7cRb2+Th9YY8a
+IKQ2s81Yg2TmkGzr8f5nrZz3WbAmQhQgsKbwlo6snjUCgYEA7kBOt0JlU7bJTfOr
+9s51g2VUfIH9lDS2Eh8MY+Bt6Y0Kdw/UK4HR8ZlN/nn0bHuHkc12K8lXEsQpgIEW
+DaqHytZJHqFs2egzKu/IvQYZ2WXEMj47LZQxEDHO9gtjE+5qCW9yJGqxW9BJKPVD
+F4spus4NqC+yD5OHM+6ESUtL/wMCgYEA5TZj6OHmECeh3efrwHqjDcjrqQbOTozU
+KPCNCY3Pv4Cg4xas/L93TE2CY6HJTr6mwEMUM+M4Ujjj15VCmSDQ/YYcGau1jo+f
+XdphOEENrPwoe9ATWIyBpT/wDrEz3L6JbE9dWMYY8vKYESt3qhVqDlbpmnYl8Jm+
+O3r5Cy2NlJMCgYEAyqzsCZuy5QcesnByvm8dqpxdxdkzJYu9wyakfKZj+gUgfO57
+OFOkjFk07yFB27MuPctCFredmfpDr+ygHRoPkG7AHw2Fss2EEaeP5bU18ilPQMqN
+vxVMs5EblVVUgJUVoVcsC2yz2f4S7oPOAk5BPoehOIzydauznWrvIAas7I8CgYBr
+CFHxLoNq6cbZQ3JACERZrIf2/vmZjoOHtoR1gKYRK7R1NmKDB7lihRMtCSBix/4/
+61Lkw+bJ5kzmn4lgzgUpTdWTWy5FquVlQxOA3EfRjlItNsXB5KKpksi7Y53vJ34u
+eIUDbkW6NPQzmFOhtaw3k/gzq5Yd2v0M82iWAqiJRwKBgQCl2+e2cjISK31QhKTC
+puhwQ0/YuC3zlwMXQgB3nPw8b9RlaDTMrRBCIUFIrrX11tHswGWpyVsxW2AvZ3Zm
+jsWpwGkUdpRdXJBhSaisV/PA+x3kYhpibzEI8FrzhU69zNROCb8CTkN4WcdBdq6J
+PUh/jRtKoE79qrlnIlNvFoz2gQ==
+-----END PRIVATE KEY-----
diff --git a/tools/binman/test/341_fit_signature.dts b/tools/binman/test/341_fit_signature.dts
new file mode 100644 (file)
index 0000000..77bec8d
--- /dev/null
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               fit {
+                       description = "test desc";
+                       #address-cells = <1>;
+                       fit,fdt-list = "of-list";
+                       fit,sign;
+
+                       images {
+                               u-boot {
+                                       description = "test u-boot";
+                                       type = "standalone";
+                                       arch = "arm64";
+                                       os = "u-boot";
+                                       compression = "none";
+                                       load = <0x00000000>;
+                                       entry = <0x00000000>;
+
+                                       u-boot-nodtb {
+                                       };
+
+                                       hash {
+                                               algo = "sha256";
+                                       };
+
+                                       signature {
+                                               algo = "sha256,rsa2048";
+                                               key-name-hint = "rsa2048";
+                                       };
+                               };
+                               @atf-SEQ {
+                                       fit,operation = "split-elf";
+                                       description = "test tf-a";
+                                       type = "firmware";
+                                       arch = "arm64";
+                                       os = "arm-trusted-firmware";
+                                       compression = "none";
+                                       fit,load;
+                                       fit,entry;
+                                       fit,data;
+
+                                       atf-bl31 {
+                                       };
+
+                                       hash {
+                                               algo = "sha256";
+                                       };
+
+                                       signature {
+                                               algo = "sha256,rsa2048";
+                                               key-name-hint = "rsa2048";
+                                       };
+                               };
+                               @fdt-SEQ {
+                                       description = "test fdt";
+                                       type = "flat_dt";
+                                       compression = "none";
+
+                                       hash {
+                                               algo = "sha256";
+                                       };
+
+                                       signature {
+                                               algo = "sha256,rsa2048";
+                                               key-name-hint = "rsa2048";
+                                       };
+                               };
+                       };
+
+                       configurations {
+                               default = "@conf-uboot-DEFAULT-SEQ";
+                               @conf-uboot-SEQ {
+                                       description = "uboot config";
+                                       fdt = "fdt-SEQ";
+                                       fit,firmware = "u-boot";
+                                       fit,loadables;
+
+                                       hash {
+                                               algo = "sha256";
+                                       };
+
+                                       signature {
+                                               algo = "sha256,rsa2048";
+                                               key-name-hint = "conf-rsa2048";
+                                               sign-images = "firmware", "loadables", "fdt";
+                                       };
+                               };
+                       };
+               };
+       };
+};
diff --git a/tools/binman/test/342_fit_signature.dts b/tools/binman/test/342_fit_signature.dts
new file mode 100644 (file)
index 0000000..267105d
--- /dev/null
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               fit {
+                       description = "test desc";
+                       #address-cells = <1>;
+                       fit,fdt-list = "of-list";
+                       fit,sign;
+
+                       images {
+                               u-boot {
+                                       description = "test u-boot";
+                                       type = "standalone";
+                                       arch = "arm64";
+                                       os = "u-boot";
+                                       compression = "none";
+                                       load = <0x00000000>;
+                                       entry = <0x00000000>;
+
+                                       u-boot-nodtb {
+                                       };
+                               };
+                               @atf-SEQ {
+                                       fit,operation = "split-elf";
+                                       description = "test tf-a";
+                                       type = "firmware";
+                                       arch = "arm64";
+                                       os = "arm-trusted-firmware";
+                                       compression = "none";
+                                       fit,load;
+                                       fit,entry;
+                                       fit,data;
+
+                                       atf-bl31 {
+                                       };
+                               };
+                               @fdt-SEQ {
+                                       description = "test fdt";
+                                       type = "flat_dt";
+                                       compression = "none";
+                               };
+                       };
+
+                       configurations {
+                               default = "@conf-uboot-DEFAULT-SEQ";
+                               @conf-uboot-SEQ {
+                                       description = "uboot config";
+                                       fdt = "fdt-SEQ";
+                                       fit,firmware = "u-boot";
+                                       fit,loadables;
+                               };
+                       };
+               };
+       };
+};