From 5b34efe865887060e626fe4e78859dab591fc24a Mon Sep 17 00:00:00 2001 From: Ivan Mikhaylov Date: Wed, 8 Mar 2023 01:13:40 +0000 Subject: [PATCH] binman: add tests for sign option Add the test which provides sequence of actions: 1. create the image from binman dts 2. create public and private keys 3. add public key into dtb with fdt_add_pubkey 4. 1. sign FIT container with new sign option with extracting from image 2. sign exact FIT container with replacing of it in image 5. check with fit_check_sign Signed-off-by: Ivan Mikhaylov Renumber test file from 277 to 280; Move UpdateSignatures() to Entry base class; Don't allow missing mkimage as it doesn't make sense; Propagate --toolpath for CI; Call mark_build_done() to avoid regenerating FIT: Signed-off-by: Simon Glass --- tools/binman/control.py | 2 + tools/binman/entry.py | 3 + tools/binman/etype/fit.py | 4 +- tools/binman/etype/section.py | 3 - tools/binman/ftest.py | 93 ++++++++++++++++++++++++++ tools/binman/test/280_fit_sign.dts | 63 +++++++++++++++++ tools/binman/test/281_sign_non_fit.dts | 65 ++++++++++++++++++ 7 files changed, 227 insertions(+), 6 deletions(-) create mode 100644 tools/binman/test/280_fit_sign.dts create mode 100644 tools/binman/test/281_sign_non_fit.dts diff --git a/tools/binman/control.py b/tools/binman/control.py index cf2c91f622..0febcb79a6 100644 --- a/tools/binman/control.py +++ b/tools/binman/control.py @@ -464,6 +464,8 @@ def SignEntries(image_fname, input_fname, privatekey_fname, algo, entry_paths, image_fname = os.path.abspath(image_fname) image = Image.FromFile(image_fname) + image.mark_build_done() + BeforeReplace(image, allow_resize=True) for entry_path in entry_paths: diff --git a/tools/binman/entry.py b/tools/binman/entry.py index b10a43333e..39456906a4 100644 --- a/tools/binman/entry.py +++ b/tools/binman/entry.py @@ -1378,3 +1378,6 @@ features to produce new behaviours. if entries: for entry in entries.values(): entry.mark_build_done() + + def UpdateSignatures(self, privatekey_fname, algo, input_fname): + self.Raise('Updating signatures is not supported with this entry type') diff --git a/tools/binman/etype/fit.py b/tools/binman/etype/fit.py index 3aea9865bf..c395706ece 100644 --- a/tools/binman/etype/fit.py +++ b/tools/binman/etype/fit.py @@ -847,9 +847,7 @@ class Entry_fit(Entry_section): args.append(fname) if self.mkimage.run_cmd(*args) is None: - # Bintool is missing; just use empty data as the output - self.record_missing_bintool(self.mkimage) - return + self.Raise("Missing tool: 'mkimage'") data = tools.read_file(fname) self.WriteData(data) diff --git a/tools/binman/etype/section.py b/tools/binman/etype/section.py index e87009d6ce..c36edd1350 100644 --- a/tools/binman/etype/section.py +++ b/tools/binman/etype/section.py @@ -1014,6 +1014,3 @@ class Entry_section(Entry): for entry in entries.values(): return entry.read_elf_segments() return None - - def UpdateSignatures(self, privatekey_fname, algo, input_fname): - self.Raise('Updating signatures is not supported with this entry type') diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index f1e14c6b3d..9862e23438 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -707,6 +707,14 @@ class TestFunctional(unittest.TestCase): AddNode(dtb.GetRoot(), '') return tree + def _CheckSign(self, fit, key): + try: + tools.run('fit_check_sign', '-k', key, '-f', fit) + except: + self.fail('Expected signed FIT container') + return False + return True + def testRun(self): """Test a basic run with valid args""" result = self._RunBinman('-h') @@ -6565,6 +6573,91 @@ fdt fdtmap Extract the devicetree blob from the fdtmap err = stderr.getvalue() self.assertRegex(err, "Image 'image'.*missing bintools.*: openssl") + def _PrepareSignEnv(self, dts='280_fit_sign.dts'): + """Prepare sign environment + + Create private and public keys, add pubkey into dtb. + + Returns: + Tuple: + FIT container + Image name + Private key + DTB + """ + + data = self._DoReadFileRealDtb(dts) + updated_fname = tools.get_output_filename('image-updated.bin') + tools.write_file(updated_fname, data) + dtb = tools.get_output_filename('source.dtb') + private_key = tools.get_output_filename('test_key.key') + public_key = tools.get_output_filename('test_key.crt') + fit = tools.get_output_filename('fit.fit') + key_dir = tools.get_output_dir() + + tools.run('openssl', 'req', '-batch' , '-newkey', 'rsa:4096', + '-sha256', '-new', '-nodes', '-x509', '-keyout', + private_key, '-out', public_key) + tools.run('fdt_add_pubkey', '-a', 'sha256,rsa4096', '-k', key_dir, + '-n', 'test_key', '-r', 'conf', dtb) + + return fit, updated_fname, private_key, dtb + + def testSignSimple(self): + """Test that a FIT container can be signed in image""" + is_signed = False + fit, fname, private_key, dtb = self._PrepareSignEnv() + + # do sign with private key + control.SignEntries(fname, None, private_key, 'sha256,rsa4096', + ['fit']) + is_signed = self._CheckSign(fit, dtb) + + self.assertEqual(is_signed, True) + + def testSignExactFIT(self): + """Test that a FIT container can be signed and replaced in image""" + is_signed = False + fit, fname, private_key, dtb = self._PrepareSignEnv() + + # Make sure we propagate the toolpath, since mkimage may not be on PATH + args = [] + if self.toolpath: + for path in self.toolpath: + args += ['--toolpath', path] + + # do sign with private key + self._DoBinman(*args, 'sign', '-i', fname, '-k', private_key, '-a', + 'sha256,rsa4096', '-f', fit, 'fit') + is_signed = self._CheckSign(fit, dtb) + + self.assertEqual(is_signed, True) + + def testSignNonFit(self): + """Test a non-FIT entry cannot be signed""" + is_signed = False + fit, fname, private_key, _ = self._PrepareSignEnv( + '281_sign_non_fit.dts') + + # do sign with private key + with self.assertRaises(ValueError) as e: + self._DoBinman('sign', '-i', fname, '-k', private_key, '-a', + 'sha256,rsa4096', '-f', fit, 'u-boot') + self.assertIn( + "Node '/u-boot': Updating signatures is not supported with this entry type", + str(e.exception)) + + def testSignMissingMkimage(self): + """Test that FIT signing handles a missing mkimage tool""" + fit, fname, private_key, _ = self._PrepareSignEnv() + + # try to sign with a missing mkimage tool + bintool.Bintool.set_missing_list(['mkimage']) + with self.assertRaises(ValueError) as e: + control.SignEntries(fname, None, private_key, 'sha256,rsa4096', + ['fit']) + self.assertIn("Node '/fit': Missing tool: 'mkimage'", str(e.exception)) + if __name__ == "__main__": unittest.main() diff --git a/tools/binman/test/280_fit_sign.dts b/tools/binman/test/280_fit_sign.dts new file mode 100644 index 0000000000..b9f17dc5c0 --- /dev/null +++ b/tools/binman/test/280_fit_sign.dts @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <0x100000>; + allow-repack; + + fit { + description = "U-Boot"; + offset = <0x10000>; + images { + u-boot-1 { + description = "U-Boot"; + type = "standalone"; + arch = "arm64"; + os = "u-boot"; + compression = "none"; + hash-1 { + algo = "sha256"; + }; + u-boot { + }; + }; + + fdt-1 { + description = "test.dtb"; + type = "flat_dt"; + arch = "arm64"; + compression = "none"; + hash-1 { + algo = "sha256"; + }; + u-boot-spl-dtb { + }; + }; + + }; + + configurations { + default = "conf-1"; + conf-1 { + description = "u-boot with fdt"; + firmware = "u-boot-1"; + fdt = "fdt-1"; + signature-1 { + algo = "sha256,rsa4096"; + key-name-hint = "test_key"; + sign-images = "firmware", "fdt"; + }; + + }; + }; + }; + + fdtmap { + }; + }; +}; diff --git a/tools/binman/test/281_sign_non_fit.dts b/tools/binman/test/281_sign_non_fit.dts new file mode 100644 index 0000000000..e16c954246 --- /dev/null +++ b/tools/binman/test/281_sign_non_fit.dts @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <0x100000>; + allow-repack; + + u-boot { + }; + fit { + description = "U-Boot"; + offset = <0x10000>; + images { + u-boot-1 { + description = "U-Boot"; + type = "standalone"; + arch = "arm64"; + os = "u-boot"; + compression = "none"; + hash-1 { + algo = "sha256"; + }; + u-boot { + }; + }; + + fdt-1 { + description = "test.dtb"; + type = "flat_dt"; + arch = "arm64"; + compression = "none"; + hash-1 { + algo = "sha256"; + }; + u-boot-spl-dtb { + }; + }; + + }; + + configurations { + default = "conf-1"; + conf-1 { + description = "u-boot with fdt"; + firmware = "u-boot-1"; + fdt = "fdt-1"; + signature-1 { + algo = "sha256,rsa4096"; + key-name-hint = "test_key"; + sign-images = "firmware", "fdt"; + }; + + }; + }; + }; + + fdtmap { + }; + }; +}; -- 2.39.5