diff options
author | Tom Rini <trini@konsulko.com> | 2023-03-16 12:16:14 -0400 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2023-03-16 12:16:14 -0400 |
commit | cb90ddb2a64f30e6b0411f9385ddd84c5612314e (patch) | |
tree | e0f834058c62cc2d4a852fc83b9f92cd85121857 /tools/binman | |
parent | a5faa4a9eb45f2cc0e858622db8fabafd644085b (diff) | |
parent | c3cea95fd21937ce82be3dbd1062dde8fb0e6114 (diff) | |
download | u-boot-cb90ddb2a64f30e6b0411f9385ddd84c5612314e.tar.gz u-boot-cb90ddb2a64f30e6b0411f9385ddd84c5612314e.tar.bz2 u-boot-cb90ddb2a64f30e6b0411f9385ddd84c5612314e.zip |
Merge tag 'dm-next-12mar23a' of git://git.denx.de/u-boot-dm into next
More tests and fixes for fdt command
binman signing feature
fix buildman -A bug introduced recently
Signed-off-by: Tom Rini <trini@konsulko.com>
Diffstat (limited to 'tools/binman')
-rw-r--r-- | tools/binman/binman.rst | 47 | ||||
-rw-r--r-- | tools/binman/cmdline.py | 13 | ||||
-rw-r--r-- | tools/binman/control.py | 30 | ||||
-rw-r--r-- | tools/binman/entry.py | 3 | ||||
-rw-r--r-- | tools/binman/etype/fit.py | 16 | ||||
-rw-r--r-- | tools/binman/ftest.py | 93 | ||||
-rw-r--r-- | tools/binman/test/280_fit_sign.dts | 63 | ||||
-rw-r--r-- | tools/binman/test/281_sign_non_fit.dts | 65 |
8 files changed, 329 insertions, 1 deletions
diff --git a/tools/binman/binman.rst b/tools/binman/binman.rst index 7fc0c7fe03..23cbb99b4b 100644 --- a/tools/binman/binman.rst +++ b/tools/binman/binman.rst @@ -1366,6 +1366,24 @@ when it was created. .. _`BinmanLogging`: +Signing FIT container with private key in an image +-------------------------------------------------- + +You can sign FIT container with private key in your image. +For example:: + + $ binman sign -i image.bin -k privatekey -a sha256,rsa4096 fit + +binman will extract FIT container, sign and replace it immediately. + +If you want to sign and replace FIT container in place:: + + $ binman sign -i image.bin -k privatekey -a sha256,rsa4096 -f fit.fit fit + +which will sign FIT container with private key and replace it immediately +inside your image. + + Logging ------- @@ -1751,6 +1769,35 @@ Options: output directory if a single test is run (pass test name at the end of the command line +binman sign +----------- + +Usage:: + + binman sign [-h] -a ALGO [-f FILE] -i IMAGE -k KEY [paths ...] + +positional arguments: + +paths + Paths within file to sign (wildcard) + +options: + +-h, --help + show this help message and exit + +-a ALGO, --algo ALGO + Hash algorithm e.g. sha256,rsa4096 + +-f FILE, --file FILE + Input filename to sign + +-i IMAGE, --image IMAGE + Image filename to update + +-k KEY, --key KEY + Private key file for signing + binman tool ----------- diff --git a/tools/binman/cmdline.py b/tools/binman/cmdline.py index 1b7bbe80cd..4b875a9dcd 100644 --- a/tools/binman/cmdline.py +++ b/tools/binman/cmdline.py @@ -176,6 +176,19 @@ controlled by a description in the board device tree.''' replace_parser.add_argument('paths', type=str, nargs='*', help='Paths within file to replace (wildcard)') + sign_parser = subparsers.add_parser('sign', + help='Sign entries in image') + sign_parser.add_argument('-a', '--algo', type=str, required=True, + help='Hash algorithm e.g. sha256,rsa4096') + sign_parser.add_argument('-f', '--file', type=str, required=False, + help='Input filename to sign') + sign_parser.add_argument('-i', '--image', type=str, required=True, + help='Image filename to update') + sign_parser.add_argument('-k', '--key', type=str, required=True, + help='Private key file for signing') + sign_parser.add_argument('paths', type=str, nargs='*', + help='Paths within file to sign (wildcard)') + if HAS_TESTS: test_parser = subparsers.add_parser('test', help='Run tests') test_parser.add_argument('-P', '--processes', type=int, diff --git a/tools/binman/control.py b/tools/binman/control.py index 2f2b4893b7..0febcb79a6 100644 --- a/tools/binman/control.py +++ b/tools/binman/control.py @@ -448,6 +448,31 @@ def ReplaceEntries(image_fname, input_fname, indir, entry_paths, AfterReplace(image, allow_resize=allow_resize, write_map=write_map) return image +def SignEntries(image_fname, input_fname, privatekey_fname, algo, entry_paths, + write_map=False): + """Sign and replace the data from one or more entries from input files + + Args: + image_fname: Image filename to process + input_fname: Single input filename to use if replacing one file, None + otherwise + algo: Hashing algorithm + entry_paths: List of entry paths to sign + privatekey_fname: Private key filename + write_map (bool): True to write the map file + """ + 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: + entry = image.FindEntryPath(entry_path) + entry.UpdateSignatures(privatekey_fname, algo, input_fname) + + AfterReplace(image, allow_resize=True, write_map=write_map) def PrepareImagesAndDtbs(dtb_fname, select_images, update_fdt, use_expanded): """Prepare the images to be processed and select the device tree @@ -660,7 +685,7 @@ def Binman(args): tools.set_tool_paths(tool_paths or None) bintool.Bintool.set_tool_dir(args.tooldir) - if args.cmd in ['ls', 'extract', 'replace', 'tool']: + if args.cmd in ['ls', 'extract', 'replace', 'tool', 'sign']: try: tout.init(args.verbosity) if args.cmd == 'replace': @@ -679,6 +704,9 @@ def Binman(args): do_compress=not args.compressed, allow_resize=not args.fix_size, write_map=args.map) + if args.cmd == 'sign': + SignEntries(args.image, args.file, args.key, args.algo, args.paths) + if args.cmd == 'tool': if args.list: bintool.Bintool.list_all() 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 03fe88e7a6..c395706ece 100644 --- a/tools/binman/etype/fit.py +++ b/tools/binman/etype/fit.py @@ -835,3 +835,19 @@ class Entry_fit(Entry_section): def CheckEntries(self): pass + + def UpdateSignatures(self, privatekey_fname, algo, input_fname): + uniq = self.GetUniqueName() + args = [ '-G', privatekey_fname, '-r', '-o', algo, '-F' ] + if input_fname: + fname = input_fname + else: + fname = tools.get_output_filename('%s.fit' % uniq) + tools.write_file(fname, self.GetData()) + args.append(fname) + + if self.mkimage.run_cmd(*args) is None: + self.Raise("Missing tool: 'mkimage'") + + data = tools.read_file(fname) + self.WriteData(data) diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index d455ea02ea..43b4f850a6 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -709,6 +709,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') @@ -6583,6 +6591,91 @@ fdt fdtmap Extract the devicetree blob from the fdtmap self._DoTestFile('278_mkimage_missing_multiple.dts', allow_missing=False) self.assertIn("not found in input path", str(e.exception)) + 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 { + }; + }; +}; |