From 4023dc9c95ccb5bcb3719c1c10e3d4dce967e0a2 Mon Sep 17 00:00:00 2001 From: Ivan Mikhaylov Date: Wed, 8 Mar 2023 01:13:39 +0000 Subject: [PATCH] binman: add sign option for binman Introduce proof of concept for binman's new option which provides sign and replace FIT containers in binary images. Usage as example: from: mkimage -G privateky -r -o sha256,rsa4096 -F fit binman replace -i flash.bin -f fit.fit fit to: binman sign -i flash.bin -k privatekey -a sha256,rsa4096 -f fit.fit fit and to this one if it's need to be extracted, signed with key and put it back in image: binman sign -i flash.bin -k privatekey -a sha256,rsa4096 fit Signed-off-by: Ivan Mikhaylov --- common/main.c | 1 + tools/binman/cmdline.py | 13 +++++++++++++ tools/binman/control.py | 28 +++++++++++++++++++++++++++- tools/binman/etype/fit.py | 18 ++++++++++++++++++ tools/binman/etype/section.py | 3 +++ 5 files changed, 62 insertions(+), 1 deletion(-) diff --git a/common/main.c b/common/main.c index 682f3359ea..7c70de2e59 100644 --- a/common/main.c +++ b/common/main.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include 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..cf2c91f622 100644 --- a/tools/binman/control.py +++ b/tools/binman/control.py @@ -448,6 +448,29 @@ 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) + + 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 +683,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 +702,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/etype/fit.py b/tools/binman/etype/fit.py index 03fe88e7a6..3aea9865bf 100644 --- a/tools/binman/etype/fit.py +++ b/tools/binman/etype/fit.py @@ -835,3 +835,21 @@ 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: + # Bintool is missing; just use empty data as the output + self.record_missing_bintool(self.mkimage) + return + + data = tools.read_file(fname) + self.WriteData(data) diff --git a/tools/binman/etype/section.py b/tools/binman/etype/section.py index c36edd1350..e87009d6ce 100644 --- a/tools/binman/etype/section.py +++ b/tools/binman/etype/section.py @@ -1014,3 +1014,6 @@ 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') -- 2.39.5