From 7298e422504ef4455160216b9b7a1baa1169283f Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Wed, 18 Dec 2019 18:25:41 +0100 Subject: mkimage: fit: add support to encrypt image with aes This commit add the support of encrypting image with aes in mkimage. To enable the ciphering, a node cipher with a reference to a key and IV (Initialization Vector) must be added to the its file. Then mkimage add the encrypted image to the FIT and add the key and IV to the u-boot device tree. Signed-off-by: Philippe Reynes --- tools/Makefile | 14 ++- tools/fit_image.c | 83 ++++++++++++++- tools/image-host.c | 292 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 387 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/Makefile b/tools/Makefile index 345bc84..051127a 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -59,6 +59,7 @@ hostprogs-$(CONFIG_CMD_BOOTEFI_SELFTEST) += file2include FIT_OBJS-$(CONFIG_FIT) := fit_common.o fit_image.o image-host.o common/image-fit.o FIT_SIG_OBJS-$(CONFIG_FIT_SIGNATURE) := common/image-sig.o +FIT_CIPHER_OBJS-$(CONFIG_FIT_CIPHER) := common/image-cipher.o # The following files are synced with upstream DTC. # Use synced versions from scripts/dtc/libfdt/. @@ -75,6 +76,9 @@ RSA_OBJS-$(CONFIG_FIT_SIGNATURE) := $(addprefix lib/rsa/, \ rsa-sign.o rsa-verify.o rsa-checksum.o \ rsa-mod-exp.o) +AES_OBJS-$(CONFIG_FIT_CIPHER) := $(addprefix lib/aes/, \ + aes-encrypt.o) + ROCKCHIP_OBS = lib/rc4.o rkcommon.o rkimage.o rksd.o rkspi.o # common objs for dumpimage and mkimage @@ -82,6 +86,7 @@ dumpimage-mkimage-objs := aisimage.o \ atmelimage.o \ $(FIT_OBJS-y) \ $(FIT_SIG_OBJS-y) \ + $(FIT_CIPHER_OBJS-y) \ common/bootm.o \ lib/crc32.o \ default_image.o \ @@ -116,7 +121,8 @@ dumpimage-mkimage-objs := aisimage.o \ gpimage.o \ gpimage-common.o \ mtk_image.o \ - $(RSA_OBJS-y) + $(RSA_OBJS-y) \ + $(AES_OBJS-y) dumpimage-objs := $(dumpimage-mkimage-objs) dumpimage.o mkimage-objs := $(dumpimage-mkimage-objs) mkimage.o @@ -137,6 +143,12 @@ HOST_EXTRACFLAGS += -DCONFIG_FIT_SIGNATURE HOST_EXTRACFLAGS += -DCONFIG_FIT_SIGNATURE_MAX_SIZE=$(CONFIG_FIT_SIGNATURE_MAX_SIZE) endif +ifdef CONFIG_FIT_CIPHER +# This affects include/image.h, but including the board config file +# is tricky, so manually define this options here. +HOST_EXTRACFLAGS += -DCONFIG_FIT_CIPHER +endif + ifdef CONFIG_SYS_U_BOOT_OFFS HOSTCFLAGS_kwbimage.o += -DCONFIG_SYS_U_BOOT_OFFS=$(CONFIG_SYS_U_BOOT_OFFS) endif diff --git a/tools/fit_image.c b/tools/fit_image.c index 6aa4b1c..dd61a81 100644 --- a/tools/fit_image.c +++ b/tools/fit_image.c @@ -59,6 +59,14 @@ static int fit_add_file_data(struct image_tool_params *params, size_t size_inc, } if (!ret) { + ret = fit_cipher_data(params->keydir, dest_blob, ptr, + params->comment, + params->require_keys, + params->engine_id, + params->cmdname); + } + + if (!ret) { ret = fit_add_verification_data(params->keydir, dest_blob, ptr, params->comment, params->require_keys, @@ -74,7 +82,6 @@ static int fit_add_file_data(struct image_tool_params *params, size_t size_inc, err_keydest: munmap(ptr, sbuf.st_size); close(tfd); - return ret; } @@ -621,6 +628,62 @@ err_no_fd: return ret; } +static int copyfile(const char *src, const char *dst) +{ + int fd_src = -1, fd_dst = -1; + void *buf = NULL; + ssize_t size; + size_t count; + int ret = -1; + + fd_src = open(src, O_RDONLY); + if (fd_src < 0) { + printf("Can't open file %s (%s)\n", src, strerror(errno)); + goto out; + } + + fd_dst = open(dst, O_WRONLY | O_CREAT, 0700); + if (fd_dst < 0) { + printf("Can't open file %s (%s)\n", dst, strerror(errno)); + goto out; + } + + buf = malloc(512); + if (!buf) { + printf("Can't allocate buffer to copy file\n"); + goto out; + } + + while (1) { + size = read(fd_src, buf, 512); + if (size < 0) { + printf("Can't read file %s\n", src); + goto out; + } + if (!size) + break; + + count = size; + size = write(fd_dst, buf, count); + if (size < 0) { + printf("Can't write file %s\n", dst); + goto out; + } + } + + ret = 0; + + out: + if (fd_src >= 0) + close(fd_src); + if (fd_dst >= 0) + close(fd_dst); + if (buf) + free(buf); + + return ret; +} + /** * fit_handle_file - main FIT file processing function * @@ -636,6 +699,7 @@ err_no_fd: static int fit_handle_file(struct image_tool_params *params) { char tmpfile[MKIMAGE_MAX_TMPFILE_LEN]; + char bakfile[MKIMAGE_MAX_TMPFILE_LEN + 4] = {0}; char cmd[MKIMAGE_MAX_DTC_CMDLINE_LEN]; size_t size_inc; int ret; @@ -670,6 +734,7 @@ static int fit_handle_file(struct image_tool_params *params) snprintf(cmd, sizeof(cmd), "cp \"%s\" \"%s\"", params->imagefile, tmpfile); } + if (*cmd && system(cmd) == -1) { fprintf (stderr, "%s: system(%s) failed: %s\n", params->cmdname, cmd, strerror(errno)); @@ -682,6 +747,14 @@ static int fit_handle_file(struct image_tool_params *params) goto err_system; /* + * Copy the tmpfile to bakfile, then in the following loop + * we copy bakfile to tmpfile. So we always start from the + * beginning. + */ + sprintf(bakfile, "%s%s", tmpfile, ".bak"); + rename(tmpfile, bakfile); + + /* * Set hashes for images in the blob. Unfortunately we may need more * space in either FDT, so keep trying until we succeed. * @@ -692,6 +765,11 @@ static int fit_handle_file(struct image_tool_params *params) * steps of this loop is enough to sign with several keys. */ for (size_inc = 0; size_inc < 64 * 1024; size_inc += 1024) { + if (copyfile(bakfile, tmpfile) < 0) { + printf("Can't copy %s to %s\n", bakfile, tmpfile); + ret = -EIO; + break; + } ret = fit_add_file_data(params, size_inc, tmpfile); if (!ret || ret != -ENOSPC) break; @@ -715,13 +793,16 @@ static int fit_handle_file(struct image_tool_params *params) params->cmdname, tmpfile, params->imagefile, strerror (errno)); unlink (tmpfile); + unlink(bakfile); unlink (params->imagefile); return EXIT_FAILURE; } + unlink(bakfile); return EXIT_SUCCESS; err_system: unlink(tmpfile); + unlink(bakfile); return -1; } diff --git a/tools/image-host.c b/tools/image-host.c index 88b3295..9483561 100644 --- a/tools/image-host.c +++ b/tools/image-host.c @@ -12,6 +12,7 @@ #include #include #include +#include /** * fit_set_hash_value - set hash value in requested has node @@ -268,6 +269,262 @@ static int fit_image_process_sig(const char *keydir, void *keydest, return 0; } +static int fit_image_read_data(char *filename, unsigned char *data, + int expected_size) +{ + struct stat sbuf; + int fd, ret = -1; + ssize_t n; + + /* Open file */ + fd = open(filename, O_RDONLY | O_BINARY); + if (fd < 0) { + printf("Can't open file %s (err=%d => %s)\n", + filename, errno, strerror(errno)); + return -1; + } + + /* Compute file size */ + if (fstat(fd, &sbuf) < 0) { + printf("Can't fstat file %s (err=%d => %s)\n", + filename, errno, strerror(errno)); + goto err; + } + + /* Check file size */ + if (sbuf.st_size != expected_size) { + printf("File %s don't have the expected size (size=%ld, expected=%d)\n", + filename, sbuf.st_size, expected_size); + goto err; + } + + /* Read data */ + n = read(fd, data, sbuf.st_size); + if (n < 0) { + printf("Can't read file %s (err=%d => %s)\n", + filename, errno, strerror(errno)); + goto err; + } + + /* Check that we have read all the file */ + if (n != sbuf.st_size) { + printf("Can't read all file %s (read %ld bytes, expexted %ld)\n", + filename, n, sbuf.st_size); + goto err; + } + + ret = 0; + +err: + close(fd); + return ret; +} + +static int fit_image_setup_cipher(struct image_cipher_info *info, + const char *keydir, void *fit, + const char *image_name, int image_noffset, + const char *node_name, int noffset) +{ + char *algo_name; + char filename[128]; + int ret = -1; + + if (fit_image_cipher_get_algo(fit, noffset, &algo_name)) { + printf("Can't get algo name for cipher '%s' in image '%s'\n", + node_name, image_name); + goto out; + } + + info->keydir = keydir; + + /* Read the key name */ + info->keyname = fdt_getprop(fit, noffset, "key-name-hint", NULL); + if (!info->keyname) { + printf("Can't get key name for cipher '%s' in image '%s'\n", + node_name, image_name); + goto out; + } + + /* Read the IV name */ + info->ivname = fdt_getprop(fit, noffset, "iv-name-hint", NULL); + if (!info->ivname) { + printf("Can't get iv name for cipher '%s' in image '%s'\n", + node_name, image_name); + goto out; + } + + info->fit = fit; + info->node_noffset = noffset; + info->name = algo_name; + + info->cipher = image_get_cipher_algo(algo_name); + if (!info->cipher) { + printf("Can't get algo for cipher '%s'\n", image_name); + goto out; + } + + /* Read the key in the file */ + snprintf(filename, sizeof(filename), "%s/%s%s", + info->keydir, info->keyname, ".bin"); + info->key = malloc(info->cipher->key_len); + if (!info->key) { + printf("Can't allocate memory for key\n"); + ret = -1; + goto out; + } + ret = fit_image_read_data(filename, (unsigned char *)info->key, + info->cipher->key_len); + if (ret < 0) + goto out; + + /* Read the IV in the file */ + snprintf(filename, sizeof(filename), "%s/%s%s", + info->keydir, info->ivname, ".bin"); + info->iv = malloc(info->cipher->iv_len); + if (!info->iv) { + printf("Can't allocate memory for iv\n"); + ret = -1; + goto out; + } + ret = fit_image_read_data(filename, (unsigned char *)info->iv, + info->cipher->iv_len); + + out: + return ret; +} + +int fit_image_write_cipher(void *fit, int image_noffset, int noffset, + const void *data, size_t size, + unsigned char *data_ciphered, int data_ciphered_len) +{ + int ret = -1; + + /* Remove unciphered data */ + ret = fdt_delprop(fit, image_noffset, FIT_DATA_PROP); + if (ret) { + printf("Can't remove data (err = %d)\n", ret); + goto out; + } + + /* Add ciphered data */ + ret = fdt_setprop(fit, image_noffset, FIT_DATA_PROP, + data_ciphered, data_ciphered_len); + if (ret) { + printf("Can't add ciphered data (err = %d)\n", ret); + goto out; + } + + /* add non ciphered data size */ + ret = fdt_setprop_u32(fit, image_noffset, "data-size-unciphered", size); + if (ret) { + printf("Can't add unciphered data size (err = %d)\n", ret); + goto out; + } + + out: + return ret; +} + +static int +fit_image_process_cipher(const char *keydir, void *keydest, void *fit, + const char *image_name, int image_noffset, + const char *node_name, int node_noffset, + const void *data, size_t size, + const char *cmdname) +{ + struct image_cipher_info info; + unsigned char *data_ciphered = NULL; + int data_ciphered_len; + int ret; + + memset(&info, 0, sizeof(info)); + + ret = fit_image_setup_cipher(&info, keydir, fit, image_name, + image_noffset, node_name, node_noffset); + if (ret) + goto out; + + ret = info.cipher->encrypt(&info, data, size, + &data_ciphered, &data_ciphered_len); + if (ret) + goto out; + + /* + * Write the public key into the supplied FDT file; this might fail + * several times, since we try signing with successively increasing + * size values + */ + if (keydest) { + ret = info.cipher->add_cipher_data(&info, keydest); + if (ret) { + printf("Failed to add verification data for cipher '%s' in image '%s'\n", + info.keyname, image_name); + goto out; + } + } + + ret = fit_image_write_cipher(fit, image_noffset, node_noffset, + data, size, + data_ciphered, data_ciphered_len); + + out: + free(data_ciphered); + free((void *)info.key); + free((void *)info.iv); + return ret; +} + +int fit_image_cipher_data(const char *keydir, void *keydest, + void *fit, int image_noffset, const char *comment, + int require_keys, const char *engine_id, + const char *cmdname) +{ + const char *image_name; + const void *data; + size_t size; + int node_noffset; + + /* Get image name */ + image_name = fit_get_name(fit, image_noffset, NULL); + if (!image_name) { + printf("Can't get image name\n"); + return -1; + } + + /* Get image data and data length */ + if (fit_image_get_data(fit, image_noffset, &data, &size)) { + printf("Can't get image data/size\n"); + return -1; + } + + /* Process all hash subnodes of the component image node */ + for (node_noffset = fdt_first_subnode(fit, image_noffset); + node_noffset >= 0; + node_noffset = fdt_next_subnode(fit, node_noffset)) { + const char *node_name; + int ret = 0; + + node_name = fit_get_name(fit, node_noffset, NULL); + if (!node_name) { + printf("Can't get node name\n"); + return -1; + } + + if (IMAGE_ENABLE_ENCRYPT && keydir && + !strncmp(node_name, FIT_CIPHER_NODENAME, + strlen(FIT_CIPHER_NODENAME))) + ret = fit_image_process_cipher(keydir, keydest, + fit, image_name, + image_noffset, + node_name, node_noffset, + data, size, cmdname); + if (ret) + return ret; + } + + return 0; +} + /** * fit_image_add_verification_data() - calculate/set verig. data for image node * @@ -675,6 +932,41 @@ static int fit_config_add_verification_data(const char *keydir, void *keydest, return 0; } +int fit_cipher_data(const char *keydir, void *keydest, void *fit, + const char *comment, int require_keys, + const char *engine_id, const char *cmdname) +{ + int images_noffset; + int noffset; + int ret; + + /* Find images parent node offset */ + images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH); + if (images_noffset < 0) { + printf("Can't find images parent node '%s' (%s)\n", + FIT_IMAGES_PATH, fdt_strerror(images_noffset)); + return images_noffset; + } + + /* Process its subnodes, print out component images details */ + for (noffset = fdt_first_subnode(fit, images_noffset); + noffset >= 0; + noffset = fdt_next_subnode(fit, noffset)) { + /* + * Direct child node of the images parent node, + * i.e. component image node. + */ + ret = fit_image_cipher_data(keydir, keydest, + fit, noffset, comment, + require_keys, engine_id, + cmdname); + if (ret) + return ret; + } + + return 0; +} + int fit_add_verification_data(const char *keydir, void *keydest, void *fit, const char *comment, int require_keys, const char *engine_id, const char *cmdname) -- cgit v1.1