[PATCH 0/2] RFC: add fdt_add_pubkey tool

In order to reduce the coupling between building the kernel and U-Boot, I'd like a tool that can add a public key to U-Boot's dtb without simultaneously signing a FIT image. That tool doesn't seem to exist, so I stole the necessary pieces from mkimage et al and put it in a single .c file.
I'm still working on the details of my proposed "require just k out these n required keys" and how it should be implemented, but it will probably involve teaching this tool a bunch of new options. These patches are not necessarily ready for inclusion (unless someone else finds fdt_add_pubkey useful as is), but I thought I might as well send it out for early comments.
Roman Kopytin (2): tools: add fdt_add_pubkey test_vboot.py: include test of fdt_add_pubkey tool
test/py/tests/test_vboot.py | 8 +++ tools/.gitignore | 1 + tools/Makefile | 3 + tools/fdt_add_pubkey.c | 130 ++++++++++++++++++++++++++++++++++++ 4 files changed, 142 insertions(+) create mode 100644 tools/fdt_add_pubkey.c

Having to use the -K option to mkimage to populate U-Boot's .dtb with the public key while signing the kernel FIT image is often a little awkward. In particular, when using a meta-build system such as bitbake/Yocto, having the tasks of the kernel and U-Boot recipes intertwined, modifying deployed artifacts and rebuilding U-Boot with an updated .dtb is quite cumbersome. Also, in some scenarios one may wish to build U-Boot complete with the public key(s) embedded in the .dtb without the corresponding private keys being present on the same build host.
So this adds a simple tool that allows one to disentangle the kernel and U-Boot builds, by simply copy-pasting just enough of the mkimage code to allow one to add a public key to a .dtb. When using mkimage, some of the information is taken from the .its used to build the kernel (algorithm and key name), so that of course needs to be supplied on the command line.
Signed-off-by: Roman Kopytin Roman.Kopytin@kaspersky.com Cc: Rasmus Villemoes rasmus.villemoes@prevas.dk --- tools/.gitignore | 1 + tools/Makefile | 3 + tools/fdt_add_pubkey.c | 130 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+) create mode 100644 tools/fdt_add_pubkey.c
diff --git a/tools/.gitignore b/tools/.gitignore index a88453f64d..f312b760e4 100644 --- a/tools/.gitignore +++ b/tools/.gitignore @@ -6,6 +6,7 @@ /dumpimage /easylogo/easylogo /envcrc +/fdt_add_pubkey /fdtgrep /file2include /fit_check_sign diff --git a/tools/Makefile b/tools/Makefile index b45219e2c3..c142c48e73 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -73,6 +73,7 @@ mkenvimage-objs := mkenvimage.o os_support.o lib/crc32.o
hostprogs-y += dumpimage mkimage hostprogs-$(CONFIG_TOOLS_LIBCRYPTO) += fit_info fit_check_sign +hostprogs-$(CONFIG_TOOLS_LIBCRYPTO) += fdt_add_pubkey
hostprogs-$(CONFIG_CMD_BOOTEFI_SELFTEST) += file2include
@@ -154,6 +155,7 @@ dumpimage-objs := $(dumpimage-mkimage-objs) dumpimage.o mkimage-objs := $(dumpimage-mkimage-objs) mkimage.o fit_info-objs := $(dumpimage-mkimage-objs) fit_info.o fit_check_sign-objs := $(dumpimage-mkimage-objs) fit_check_sign.o +fdt_add_pubkey-objs := $(dumpimage-mkimage-objs) fdt_add_pubkey.o file2include-objs := file2include.o
ifneq ($(CONFIG_MX23)$(CONFIG_MX28)$(CONFIG_TOOLS_LIBCRYPTO),) @@ -191,6 +193,7 @@ HOSTCFLAGS_fit_image.o += -DMKIMAGE_DTC="$(CONFIG_MKIMAGE_DTC_PATH)" HOSTLDLIBS_dumpimage := $(HOSTLDLIBS_mkimage) HOSTLDLIBS_fit_info := $(HOSTLDLIBS_mkimage) HOSTLDLIBS_fit_check_sign := $(HOSTLDLIBS_mkimage) +HOSTLDLIBS_fdt_add_pubkey := $(HOSTLDLIBS_mkimage)
hostprogs-$(CONFIG_EXYNOS5250) += mkexynosspl hostprogs-$(CONFIG_EXYNOS5420) += mkexynosspl diff --git a/tools/fdt_add_pubkey.c b/tools/fdt_add_pubkey.c new file mode 100644 index 0000000000..96099312e4 --- /dev/null +++ b/tools/fdt_add_pubkey.c @@ -0,0 +1,130 @@ +#include <image.h> +#include "fit_common.h" + +static const char *cmdname; + +static const char *algo_name = "sha1,rsa2048"; /* -a <algo> */ +static const char *keydir = "."; /* -k <keydir> */ +static const char *keyname = "key"; /* -n <keyname> */ +static const char *require_keys; /* -r <conf|image> */ +static const char *keydest; /* argv[n] */ + +static void print_usage(const char *msg) +{ + fprintf(stderr, "Error: %s\n", msg); + fprintf(stderr, "Usage: %s [-a <algo>] [-k <keydir>] [-n <keyname>] [-r <conf|image>] <fdt blob>\n", + cmdname); + fprintf(stderr, "Help information: %s [-h]\n", cmdname); + exit(EXIT_FAILURE); +} + +static void print_help() +{ + fprintf(stderr, "Options:\n" + "\t-a <algo> Cryptographic algorithm. Optional parameter, default value: sha1,rsa2048\n" + "\t-k <keydir> Directory with public key. Optional parameter, default value: .\n" + "\t-n <keyname> Public key name. Optional parameter, default value: key\n" + "\t-r <conf|image> Required: If present this indicates that the key must be verified for the image / configuration to be considered valid.\n" + "\t<fdt blob> FDT blob file for adding of the public key. Required parameter.\n"); + exit(EXIT_FAILURE); +} + +static void process_args(int argc, char *argv[]) +{ + int opt; + + while((opt = getopt(argc, argv, "a:k:n:r:h")) != -1) { + switch (opt) { + case 'k': + keydir = optarg; + break; + case 'a': + algo_name = optarg; + break; + case 'n': + keyname = optarg; + break; + case 'r': + require_keys = optarg; + break; + case 'h': + print_help(); + default: + print_usage("Invalid option"); + } + } + /* The last parameter is expected to be the .dtb to add the public key to */ + if (optind < argc) + keydest = argv[optind]; + + if (!keydest) + print_usage("Missing dtb file to update"); +} + +static void reset_info(struct image_sign_info *info) +{ + if (info == NULL) { + fprintf(stderr, "Error: info is NULL in reset_info()"); + } + memset(info, 0, sizeof(struct image_sign_info)); + + info->keydir = keydir; + info->keyname = keyname; + info->name = algo_name; + info->require_keys = require_keys; + info->crypto = image_get_crypto_algo(algo_name); + if (!info->crypto) { + fprintf(stderr, "Unsupported signature algorithm '%s'\n", algo_name); + exit(EXIT_FAILURE); + } +} + +static int add_pubkey(struct image_sign_info *info) +{ + int destfd, ret; + void *dest_blob = NULL; + struct stat dest_sbuf; + size_t size_inc = 0; + + if (info == NULL) { + fprintf(stderr, "Error: info is NULL in add_pubkey()"); + } + + while (true) { + destfd = mmap_fdt(cmdname, keydest, size_inc, &dest_blob, &dest_sbuf, false, false); + if (destfd < 0) + exit(EXIT_FAILURE); + + ret = info->crypto->add_verify_data(info, dest_blob); + + munmap(dest_blob, dest_sbuf.st_size); + close(destfd); + if (!ret || ret != -ENOSPC) + break; + fprintf(stderr, ".dtb too small, increasing size by 1024 bytes\n"); + size_inc = 1024; + } + return ret; +} + +int main(int argc, char *argv[]) +{ + struct image_sign_info info; + int ret; + + cmdname = argv[0]; + + process_args(argc, argv); + + reset_info(&info); + + ret = add_pubkey(&info); + + if (ret) { + fprintf(stderr, "%s: Cannot add public key to FIT blob: %s\n", + cmdname, strerror(-ret)); + exit(EXIT_FAILURE); + } + + exit(EXIT_SUCCESS); +}

Signed-off-by: Roman Kopytin Roman.Kopytin@kaspersky.com Cc: Rasmus Villemoes rasmus.villemoes@prevas.dk --- test/py/tests/test_vboot.py | 8 ++++++++ 1 file changed, 8 insertions(+)
diff --git a/test/py/tests/test_vboot.py b/test/py/tests/test_vboot.py index 095e00cce3..0e9b158e00 100644 --- a/test/py/tests/test_vboot.py +++ b/test/py/tests/test_vboot.py @@ -232,6 +232,13 @@ def test_vboot(u_boot_console, name, sha_algo, padding, sign_options, required,
util.run_and_log(cons, [fit_check_sign, '-f', fit, '-k', dtb])
+ # Create a fresh .dtb without the public keys + dtc('sandbox-u-boot.dts') + # Then add the dev key via the fdt_add_pubkey tool + util.run_and_log(cons, [fdt_add_pubkey, '-a', '%s,rsa2048' % sha_algo, + '-k', tmpdir, '-n', 'dev', '-r', 'conf', dtb]) + util.run_and_log(cons, [fit_check_sign, '-f', fit, '-k', dtb]) + if full_test: # Make sure that U-Boot checks that the config is in the list of # hashed nodes. If it isn't, a security bypass is possible. @@ -373,6 +380,7 @@ def test_vboot(u_boot_console, name, sha_algo, padding, sign_options, required, fit = '%stest.fit' % tmpdir mkimage = cons.config.build_dir + '/tools/mkimage' fit_check_sign = cons.config.build_dir + '/tools/fit_check_sign' + fdt_add_pubkey = cons.config.build_dir + '/tools/fdt_add_pubkey' dtc_args = '-I dts -O dtb -i %s' % tmpdir dtb = '%ssandbox-u-boot.dtb' % tmpdir sig_node = '/configurations/conf-1/signature'
participants (1)
-
Roman Kopytin