[U-Boot] [RFC PATCH 42/44] image: Add support for signing of FIT configurations

Simon Glass
2013-01-05T01:52:11Z

While signing images is useful, it does not provide complete protection
against several types of attack. For example, it it possible to create a
FIT with the same signed images, but with the configuration changed such
that a different one is selected (mix and match attack). It is also possible
to substitute a signed image from an older FIT version into a newer FIT
(roll-back attack).

Add support for signing of FIT configurations using the libfdt's region
support.

Please see doc/uImage.FIT/signature.txt for more information.

Signed-off-by: Simon Glass <sjg@chromium.org>
---
common/cmd_bootm.c | 12 ++
common/image-sig.c | 212 +++++++++++++++++++++++-
doc/uImage.FIT/sign-configs.its | 45 +++++
doc/uImage.FIT/signature.txt | 167 ++++++++++++++++++-
include/image.h | 15 ++
tools/image-host.c | 350 ++++++++++++++++++++++++++++++++++++++-
6 files changed, 798 insertions(+), 3 deletions(-)
create mode 100644 doc/uImage.FIT/sign-configs.its

diff --git a/common/cmd_bootm.c b/common/cmd_bootm.c
index cac9e53..d71ffb5 100644
--- a/common/cmd_bootm.c
+++ b/common/cmd_bootm.c
@@ -972,6 +972,18 @@ static const void *boot_get_kernel(cmd_tbl_t *cmdtp, int flag, int argc,
NULL);
printf(" Using '%s' configuration\n",
images->fit_uname_cfg);
+ if (IMAGE_ENABLE_VERIFY && images->verify) {
+ puts(" Verifying Hash Integrity ... ");
+ if (!fit_config_verify(fit_hdr,
+ cfg_noffset)) {
+ puts("Bad Data Hash\n");
+ bootstage_error(
+ BOOTSTAGE_ID_FIT_CHECK_HASH);
+ return NULL;
+ }
+ puts("OK\n");
+ }
+
bootstage_mark(BOOTSTAGE_ID_FIT_CONFIG);

os_noffset = fit_conf_get_kernel_node(fit_hdr,
diff --git a/common/image-sig.c b/common/image-sig.c
index c74d2f4..cfbb6ed 100644
--- a/common/image-sig.c
+++ b/common/image-sig.c
@@ -25,7 +25,6 @@
#include <malloc.h>
DECLARE_GLOBAL_DATA_PTR;
#endif /* !USE_HOSTCC*/
-#include <errno.h>
#include <image.h>
#include <rsa.h>

@@ -50,6 +49,27 @@ struct image_sig_algo *image_get_sig_algo(const char *name)
return NULL;
}

+struct image_region *fit_region_make_list(const void *fit,
+ struct fdt_region *fdt_regions, int count)
+{
+ struct image_region *region;
+ int i;
+
+ debug("Hash regions:\n");
+ debug("%10s %10s\n", "Offset", "Size");
+ region = calloc(sizeof(*region), count);
+ if (!region)
+ return NULL;
+ for (i = 0; i < count; i++) {
+ debug("%10x %10x\n", fdt_regions[i].offset,
+ fdt_regions[i].size);
+ region[i].data = fit + fdt_regions[i].offset;
+ region[i].size = fdt_regions[i].size;
+ }
+
+ return region;
+}
+
static int fit_image_setup_verify(struct image_sign_info *info,
const void *fit, int noffset, int required_keynode,
char **err_msgp)
@@ -195,3 +215,193 @@ int fit_image_verify_required_sigs(const void *fit, int image_noffset,

return 0;
}
+
+int fit_config_check_sig(const void *fit, int noffset, int required_keynode,
+ char **err_msgp)
+{
+ char * const exc_prop[] = {"data"};
+ struct fdt_region *fdt_regions;
+ const char *prop, *end, *name;
+ struct image_sign_info info;
+ struct image_region *region;
+ const uint32_t *strings;
+ uint8_t *fit_value;
+ int fit_value_len;
+ int max_regions;
+ char **node_inc;
+ int i, prop_len;
+ char path[200];
+ int count;
+
+ debug("%s: fdt=%p, conf='%s', sig='%s'\n", __func__, gd_fdt_blob(),
+ fit_get_name(fit, noffset, NULL),
+ fit_get_name(gd_fdt_blob(), required_keynode, NULL));
+ *err_msgp = NULL;
+ if (fit_image_setup_verify(&info, fit, noffset, required_keynode,
+ err_msgp))
+ return -1;
+
+ if (fit_image_hash_get_value(fit, noffset, &fit_value,
+ &fit_value_len)) {
+ *err_msgp = "Can't get hash value property";
+ return -1;
+ }
+
+ /* Count the number of strings in the property */
+ prop = fdt_getprop(fit, noffset, "hashed-nodes", &prop_len);
+ end = prop ? prop + prop_len : prop;
+ for (name = prop, count = 0; name < end; name++)
+ if (!*name)
+ count++;
+ if (!count) {
+ *err_msgp = "Can't get hashed-nodes property";
+ return -1;
+ }
+
+ /* Create a list of node names from those strings */
+ node_inc = calloc(sizeof(char *), count);
+ if (!node_inc) {
+ *err_msgp = "Out of memory for include node list";
+ return -1;
+ }
+ debug("Hash nodes (%d):\n", count);
+ for (name = prop, i = 0; name < end; name += strlen(name) + 1, i++) {
+ debug(" '%s'\n", name);
+ node_inc[i] = (char *)name;
+ }
+
+ /*
+ * Each node can generate one region for each sub-node. Allow for
+ * 7 sub-nodes (hash at 1, signature@1, etc.) and some extra.
+ */
+ max_regions = 20 + count * 7;
+ fdt_regions = calloc(sizeof(*fdt_regions), max_regions);
+ if (!fdt_regions) {
+ *err_msgp = "Out of memory for fdt hash regions";
+ return -1;
+ }
+
+ /* Get a list of regions to hash */
+ count = fdt_find_regions(fit, node_inc, count,
+ exc_prop, ARRAY_SIZE(exc_prop),
+ fdt_regions, max_regions - 1,
+ path, sizeof(path), 0);
+ if (count < 0) {
+ *err_msgp = "Failed to hash configuration";
+ return -1;
+ }
+ if (count == 0) {
+ *err_msgp = "No data to hash";
+ return -1;
+ }
+ if (count == max_regions - 1) {
+ *err_msgp = "Too many hash regions";
+ return -1;
+ }
+
+ /* Add the strings */
+ strings = fdt_getprop(fit, noffset, "hashed-strings", NULL);
+ if (strings) {
+ fdt_regions[count].offset = fdt_off_dt_strings(fit) +
+ fdt32_to_cpu(strings[0]);
+ fdt_regions[count].size = fdt32_to_cpu(strings[1]);
+ count++;
+ }
+
+ region = fit_region_make_list(fit, fdt_regions, count);
+ free(fdt_regions);
+
+ if (info.algo->verify(&info, region, count, fit_value, fit_value_len)) {
+ *err_msgp = "Verification failed";
+ return -1;
+ }
+
+ return 0;
+}
+
+static int fit_config_verify_sig(const void *fit, int conf_noffset,
+ const void *sig_blob, int sig_offset)
+{
+ int noffset;
+ int ndepth;
+ char *err_msg = "";
+ int verified = 0;
+ int ret;
+
+ /* Process all hash subnodes of the component conf node */
+ for (ndepth = 0,
+ noffset = fdt_next_subnode(fit, conf_noffset, &ndepth);
+ noffset >= 0;
+ noffset = fdt_next_subnode(fit, noffset, &ndepth)) {
+ const char *name = fit_get_name(fit, noffset, NULL);
+
+ if (!strncmp(name, FIT_SIG_NODENAME,
+ strlen(FIT_SIG_NODENAME))) {
+ ret = fit_config_check_sig(fit, noffset, sig_offset,
+ &err_msg);
+ if (ret) {
+ puts("- ");
+ } else {
+ puts("+ ");
+ verified = 1;
+ break;
+ }
+ }
+ }
+
+ if (noffset == -FDT_ERR_TRUNCATED || noffset == -FDT_ERR_BADSTRUCTURE) {
+ err_msg = "Corrupted or truncated tree";
+ goto error;
+ }
+
+ return verified ? 0 : -EPERM;
+
+error:
+ printf(" error!\n%s for '%s' hash node in '%s' config node\n",
+ err_msg, fit_get_name(fit, noffset, NULL),
+ fit_get_name(fit, conf_noffset, NULL));
+ return -1;
+}
+
+int fit_config_verify_required_sigs(const void *fit, int conf_noffset,
+ const void *sig_blob)
+{
+ int ndepth, noffset;
+ int sig_node;
+
+ /* Work out what we need to verify */
+ sig_node = fdt_subnode_offset(sig_blob, 0, FIT_SIG_NODENAME);
+ if (sig_node < 0) {
+ debug("%s: No signature node found: %s\n", __func__,
+ fdt_strerror(sig_node));
+ return 0;
+ }
+
+ for (ndepth = 0, noffset = fdt_next_subnode(sig_blob, sig_node,
+ &ndepth);
+ noffset >= 0;
+ noffset = fdt_next_subnode(sig_blob, noffset,
+ &ndepth)) {
+ const char *required;
+ int ret;
+
+ required = fdt_getprop(sig_blob, noffset, "required", NULL);
+ if (!required || strcmp(required, "conf"))
+ continue;
+ ret = fit_config_verify_sig(fit, conf_noffset, sig_blob,
+ noffset);
+ if (ret) {
+ printf("Failed to verify required signature '%s'\n",
+ fit_get_name(sig_blob, noffset, NULL));
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+int fit_config_verify(const void *fit, int conf_noffset)
+{
+ return !fit_config_verify_required_sigs(fit, conf_noffset,
+ gd_fdt_blob());
+}
diff --git a/doc/uImage.FIT/sign-configs.its b/doc/uImage.FIT/sign-configs.its
new file mode 100644
index 0000000..3c17f04
--- /dev/null
+++ b/doc/uImage.FIT/sign-configs.its
@@ -0,0 +1,45 @@
+/dts-v1/;
+
+/ {
+ description = "Chrome OS kernel image with one or more FDT blobs";
+ #address-cells = <1>;
+
+ images {
+ kernel at 1 {
+ data = /incbin/("test-kernel.bin");
+ type = "kernel_noload";
+ arch = "sandbox";
+ os = "linux";
+ compression = "lzo";
+ load = <0x4>;
+ entry = <0x8>;
+ kernel-version = <1>;
+ hash at 1 {
+ algo = "sha1";
+ };
+ };
+ fdt at 1 {
+ description = "snow";
+ data = /incbin/("sandbox-kernel.dtb");
+ type = "flat_dt";
+ arch = "sandbox";
+ compression = "none";
+ fdt-version = <1>;
+ hash at 1 {
+ algo = "sha1";
+ };
+ };
+ };
+ configurations {
+ default = "conf at 1";
+ conf at 1 {
+ kernel = "kernel at 1";
+ fdt = "fdt at 1";
+ signature at 1 {
+ algo = "sha1,rsa2048";
+ key-name-hint = "dev";
+ sign-images = "fdt", "kernel";
+ };
+ };
+ };
+};
diff --git a/doc/uImage.FIT/signature.txt b/doc/uImage.FIT/signature.txt
index 48aa616..484d76d 100644
--- a/doc/uImage.FIT/signature.txt
+++ b/doc/uImage.FIT/signature.txt
@@ -105,8 +105,27 @@ When the image is signed, the following properties are optional:

- comment: Additional information about the signer or image

+For config bindings (see Signed Configurations below), the following
+additional properties are optional:

-Example: See sign-images.its for an example image tree source file.
+- sign-images: A list of images to sign, each being a property of the conf
+node that contains then. The default is "kernel,fdt" which means that these
+two images will be looked up in the config and signed if present.
+
+For config bindings, these properties are added by the signer:
+
+- hashed-nodes: A list of nodes which were hashed by the signer. Each is
+ a string - the full path to node. A typical value might be:
+
+ hashed-nodes = "/", "/configurations/conf at 1", "/images/kernel at 1",
+ "/images/kernel at 1/hash at 1", "/images/fdt at 1",
+ "/images/fdt at 1/hash at 1";
+
+- hashed-strings: The start and size of the string region of the FIT that
+ was hashed
+
+Example: See sign-images.its for an example image tree source file and
+sign-configs.its for config signing.

Public Key Storage
@@ -144,6 +163,152 @@ For RSA the following are mandatory:
- rsa,n0-inverse: -1 / modulus[0] mod 2^32

+Signed Configurations
+---------------------
+While signing images is useful, it does not provide complete protection
+against several types of attack. For example, it it possible to create a
+FIT with the same signed images, but with the configuration changed such
+that a different one is selected (mix and match attack). It is also possible
+to substitute a signed image from an older FIT version into a newer FIT
+(roll-back attack).
+
+As an example, consider this FIT:
+
+/ {
+ images {
+ kernel at 1 {
+ data =
+ signature at 1 {
+ algo = "sha1,rsa2048";
+ value = <kernel signature 1>
+ };
+ };
+ kernel at 2 {
+ data =
+ signature at 1 {
+ algo = "sha1,rsa2048";
+ value = <kernel signature 2>
+ };
+ };
+ fdt at 1 {
+ data = ;
+ signature at 1 {
+ algo = "sha1,rsa2048";
+ vaue = <fdt signature 1>
+ };
+ };
+ fdt at 2 {
+ data = ;
+ signature at 1 {
+ algo = "sha1,rsa2048";
+ vaue = <fdt signature 2>
+ };
+ };
+ };
+ configurations {
+ default = "conf at 1";
+ conf at 1 {
+ kernel = "kernel at 1";
+ fdt = "fdt at 1";
+ };
+ conf at 1 {
+ kernel = "kernel at 2";
+ fdt = "fdt at 2";
+ };
+ };
+};
+
+Since both kernels are signed it is easy for an attacker to add a new
+configuration 3 with kernel 1 and fdt 2:
+
+ configurations {
+ default = "conf at 1";
+ conf at 1 {
+ kernel = "kernel at 1";
+ fdt = "fdt at 1";
+ };
+ conf at 1 {
+ kernel = "kernel at 2";
+ fdt = "fdt at 2";
+ };
+ conf at 3 {
+ kernel = "kernel at 1";
+ fdt = "fdt at 2";
+ };
+ };
+
+With signed images, nothing protects against this. Whether it gains an
+advantage for the attacker is debatable, but it is not secure.
+
+To solved this problem, we support signed configurations. In this case it
+is the configurations that are signed, not the image. Each image has its
+own hash, and we include the hash in the configuration signature.
+
+So the above example is adjusted to look like this:
+
+/ {
+ images {
+ kernel at 1 {
+ data =
+ hash at 1 {
+ algo = "sha1";
+ value = <kernel hash 1>
+ };
+ };
+ kernel at 2 {
+ data =
+ hash at 1 {
+ algo = "sha1";
+ value = <kernel hash 2>
+ };
+ };
+ fdt at 1 {
+ data = ;
+ hash at 1 {
+ algo = "sha1";
+ value = <fdt hash 1>
+ };
+ };
+ fdt at 2 {
+ data = ;
+ hash at 1 {
+ algo = "sha1";
+ value = <fdt hash 2>
+ };
+ };
+ };
+ configurations {
+ default = "conf at 1";
+ conf at 1 {
+ kernel = "kernel at 1";
+ fdt = "fdt at 1";
+ signature at 1 {
+ algo = "sha1,rsa2048";
+ value = <conf 1 signature>;
+ };
+ };
+ conf at 2 {
+ kernel = "kernel at 2";
+ fdt = "fdt at 2";
+ signature at 1 {
+ algo = "sha1,rsa2048";
+ value = <conf 1 signature>;
+ };
+ };
+ };
+};
+
+
+You can see that we have added hashes for all images (since they are no
+longer signed), and a signature to each configuration. In the above example,
+mkimage will sign configurations/conf at 1, the kernel and fdt that are
+pointed to by the configuration (/images/kernel at 1, /images/kernel at 1/hash at 1,
+/images/fdt at 1, /images/fdt at 1/hash at 1) and the root structure of the image
+(so that it isn't possible to add or remove root nodes). The signature is
+written into /configurations/conf at 1/signature at 1/value. It can easily be
+verified later even if the FIT has signed with other keys in the meantime.
+
+
Verification
------------
FITs are verified when loaded. After the configuration is selected a list
diff --git a/include/image.h b/include/image.h
index b8aea83..ad3e4b8 100644
--- a/include/image.h
+++ b/include/image.h
@@ -633,6 +633,7 @@ int fit_add_verification_data(const char *keydir, void *keydest, void *fit,
const char *comment, int require_keys);

int fit_image_verify(const void *fit, int noffset);
+int fit_config_verify(const void *fit, int conf_noffset);
int fit_all_image_verify(const void *fit);
int fit_image_check_os(const void *fit, int noffset, uint8_t os);
int fit_image_check_arch(const void *fit, int noffset, uint8_t arch);
@@ -801,6 +802,20 @@ int fit_image_verify_required_sigs(const void *fit, int image_noffset,
int fit_image_check_sig(const void *fit, int noffset, const void *data,
size_t size, int required_keynode, char **err_msgp);

+/**
+ * fit_region_make_list() - Make a list of regions to hash
+ *
+ * Given a list of FIT regions (offset, size) provided by libfdt, create
+ * a list of regions (void *, size) for use by the signature creationg
+ * and verification code.
+ *
+ * @fit: FIT image to process
+ * @fdt_regions: Regions as returned by libfdt
+ * @count: Number of regions returned by libfdt
+ * @return pointer to list of regions, or NULL if out of memory
+ */
+struct image_region *fit_region_make_list(const void *fit,
+ struct fdt_region *fdt_regions, int count);

#ifndef USE_HOSTCC
static inline int fit_image_check_target_arch(const void *fdt, int node)
diff --git a/tools/image-host.c b/tools/image-host.c
index 4196190..8304253 100644
--- a/tools/image-host.c
+++ b/tools/image-host.c
@@ -343,10 +343,335 @@ int fit_image_add_verification_data(const char *keydir, void *keydest,
return 0;
}

+struct strlist {
+ int count;
+ char **strings;
+};
+
+static void strlist_init(struct strlist *list)
+{
+ memset(list, '\0', sizeof(*list));
+}
+
+static void strlist_free(struct strlist *list)
+{
+ int i;
+
+ for (i = 0; i < list->count; i++)
+ free(list->strings[i]);
+ free(list->strings);
+}
+
+static int strlist_add(struct strlist *list, const char *str)
+{
+ char *dup;
+
+ dup = strdup(str);
+ list->strings = realloc(list->strings,
+ (list->count + 1) * sizeof(char *));
+ if (!list || !str)
+ return -1;
+ list->strings[list->count++] = dup;
+
+ return 0;
+}
+
+static const char *fit_config_get_image_list(void *fit, int noffset,
+ int *lenp, int *allow_missingp)
+{
+ static const char default_list[] = FIT_KERNEL_PROP "\0"
+ FIT_FDT_PROP;
+ const char *prop;
+
+ /* If there is an "image" property, use that */
+ prop = fdt_getprop(fit, noffset, "sign-images", lenp);
+ if (prop) {
+ *allow_missingp = 0;
+ return *lenp ? prop : NULL;
+ }
+
+ /* Default image list */
+ *allow_missingp = 1;
+ *lenp = sizeof(default_list);
+
+ return default_list;
+}
+
+static int fit_config_get_hash_list(void *fit, int conf_noffset,
+ int sig_offset, struct strlist *node_inc)
+{
+ int allow_missing;
+ const char *prop, *iname, *end;
+ const char *conf_name, *sig_name;
+ char name[200], path[200];
+ int image_count;
+ int ret, len;
+
+ conf_name = fit_get_name(fit, conf_noffset, NULL);
+ sig_name = fit_get_name(fit, sig_offset, NULL);
+
+ /*
+ * Build a list of nodes we need to hash. We always need the root
+ * node and the configuration.
+ */
+ strlist_init(node_inc);
+ snprintf(name, sizeof(name), "%s/%s", FIT_CONFS_PATH, conf_name);
+ if (strlist_add(node_inc, "/") ||
+ strlist_add(node_inc, name))
+ goto err_mem;
+
+ /* Get a list of images that we intend to sign */
+ prop = fit_config_get_image_list(fit, conf_noffset, &len,
+ &allow_missing);
+ if (!prop)
+ return 0;
+
+ /* Locate the images */
+ end = prop + len;
+ image_count = 0;
+ for (iname = prop; iname < end; iname += strlen(iname) + 1) {
+ int ndepth, noffset;
+ int image_noffset;
+ int hash_count;
+
+ image_noffset = fit_conf_get_prop_node(fit, conf_noffset,
+ iname);
+ if (image_noffset < 0) {
+ printf("Failed to find image '%s' in"
+ " configuration '%s/%s'\n", iname, conf_name,
+ sig_name);
+ if (allow_missing)
+ continue;
+
+ return -ENOENT;
+ }
+
+ ret = fdt_get_path(fit, image_noffset, path, sizeof(path));
+ if (ret < 0)
+ goto err_path;
+ if (strlist_add(node_inc, path))
+ goto err_mem;
+
+ snprintf(name, sizeof(name), "%s/%s", FIT_CONFS_PATH,
+ conf_name);
+
+ /* Add all this image's hashes */
+ hash_count = 0;
+ for (ndepth = 0,
+ noffset = fdt_next_subnode(fit, image_noffset, &ndepth);
+ noffset >= 0;
+ noffset = fdt_next_subnode(fit, noffset, &ndepth)) {
+ const char *name = fit_get_name(fit, noffset, NULL);
+
+ if (strncmp(name, FIT_HASH_NODENAME,
+ strlen(FIT_HASH_NODENAME)))
+ continue;
+ ret = fdt_get_path(fit, noffset, path, sizeof(path));
+ if (ret < 0)
+ goto err_path;
+ if (strlist_add(node_inc, path))
+ goto err_mem;
+ hash_count++;
+ }
+
+ if (!hash_count) {
+ printf("Failed to find any hash nodes in configuration"
+ " '%s/%s' image '%s' - without these it is"
+ " not possible to verify this image\n",
+ conf_name, sig_name, iname);
+ return -ENOMSG;
+ }
+
+ image_count++;
+ }
+
+ if (!image_count) {
+ printf("Failed to find any images for configuration '%s/%s'\n",
+ conf_name, sig_name);
+ return -ENOMSG;
+ }
+
+ return 0;
+
+err_mem:
+ printf("Out of memory processing configuration '%s/%s'\n", conf_name,
+ sig_name);
+ return -ENOMEM;
+
+err_path:
+ printf("Failed to get path for image '%s' in"
+ " configuration '%s/%s': %s\n", iname, conf_name, sig_name,
+ fdt_strerror(ret));
+ return -ENOENT;
+}
+
+static int fit_config_get_data(void *fit, int conf_noffset, int noffset,
+ struct image_region **regionp, int *region_countp,
+ char **region_propp, int *region_proplen)
+{
+ char * const exc_prop[] = {"data"};
+ struct strlist node_inc;
+ struct image_region *region;
+ struct fdt_region fdt_regions[100];
+ const char *conf_name, *sig_name;
+ char path[200];
+ int count, i;
+ char *region_prop;
+ int ret, len;
+
+ conf_name = fit_get_name(fit, conf_noffset, NULL);
+ sig_name = fit_get_name(fit, conf_noffset, NULL);
+ debug("%s: conf='%s', sig='%s'\n", __func__, conf_name, sig_name);
+
+ /* Get a list of nodes we want to hash */
+ ret = fit_config_get_hash_list(fit, conf_noffset, noffset, &node_inc);
+ if (ret)
+ return ret;
+
+ /* Get a list of regions to hash */
+ count = fdt_find_regions(fit, node_inc.strings, node_inc.count,
+ exc_prop, ARRAY_SIZE(exc_prop),
+ fdt_regions, ARRAY_SIZE(fdt_regions),
+ path, sizeof(path), 1);
+ if (count < 0) {
+ printf("Failed to hash configuration '%s/%s': %s\n", conf_name,
+ sig_name, fdt_strerror(ret));
+ return -EIO;
+ }
+ if (count == 0) {
+ printf("No data to hash for configuration '%s/%s': %s\n",
+ conf_name, sig_name, fdt_strerror(ret));
+ return -EINVAL;
+ }
+
+ /* Build our list of data blocks */
+ region = fit_region_make_list(fit, fdt_regions, count);
+ if (!region) {
+ printf("Out of memory hashing configuration '%s/%s'\n",
+ conf_name, sig_name);
+ return -ENOMEM;
+ }
+
+ /* Create a list of all hashed properties */
+ debug("Hash nodes:\n");
+ for (i = len = 0; i < node_inc.count; i++) {
+ debug(" %s\n", node_inc.strings[i]);
+ len += strlen(node_inc.strings[i]) + 1;
+ }
+ region_prop = malloc(len);
+ if (!region_prop) {
+ printf("Out of memory setting up regions for"
+ " configuration '%s/%s'\n", conf_name, sig_name);
+ return -ENOMEM;
+ }
+ for (i = len = 0; i < node_inc.count;
+ len += strlen(node_inc.strings[i]) + 1, i++)
+ strcpy(region_prop + len, node_inc.strings[i]);
+ strlist_free(&node_inc);
+
+ *region_countp = count;
+ *regionp = region;
+ *region_propp = region_prop;
+ *region_proplen = len;
+
+ return 0;
+}
+
+static int fit_config_process_sig(const char *keydir, void *keydest,
+ void *fit, const char *conf_name, int conf_noffset,
+ int noffset, const char *comment, int require_keys)
+{
+ struct image_sign_info info;
+ const char *node_name;
+ struct image_region *region;
+ char *region_prop;
+ int region_proplen;
+ int region_count;
+ uint8_t *value;
+ uint value_len;
+ int ret;
+
+ node_name = fit_get_name(fit, noffset, NULL);
+ if (fit_config_get_data(fit, conf_noffset, noffset, &region,
+ &region_count, &region_prop, &region_proplen))
+ return -1;
+
+ if (fit_image_setup_sig(&info, keydir, fit, conf_name, noffset,
+ require_keys ? "conf" : NULL))
+ return -1;
+
+ ret = info.algo->sign(&info, region, region_count, &value, &value_len);
+ free(region);
+ if (ret) {
+ printf("Failed to sign '%s' signature node in '%s'"
+ " conf node\n", node_name, conf_name);
+
+ /* We allow keys to be missing */
+ if (ret == -ENOENT)
+ return 0;
+ return -1;
+ }
+
+ if (fit_image_write_sig(fit, noffset, value, value_len, comment,
+ region_prop, region_proplen)) {
+ printf("Can't write signature for "
+ "'%s' signature node in '%s' conf node\n",
+ node_name, conf_name);
+ return -1;
+ }
+ free(value);
+ free(region_prop);
+
+ /* Get keyname again, as FDT has changed and invalidated our pointer */
+ info.keyname = fdt_getprop(fit, noffset, "key-name-hint", NULL);
+
+ /* Write the public key into the supplied FDT file */
+ if (keydest && info.algo->add_verify_data(&info, keydest)) {
+ printf("Failed to add verification data for "
+ "'%s' signature node in '%s' image node\n",
+ node_name, conf_name);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int fit_config_add_verification_data(const char *keydir, void *keydest,
+ void *fit, int conf_noffset, const char *comment,
+ int require_keys)
+{
+ const char *conf_name;
+ int noffset;
+ int ndepth;
+
+ conf_name = fit_get_name(fit, conf_noffset, NULL);
+
+ /* Process all hash subnodes of the configuration node */
+ for (ndepth = 0,
+ noffset = fdt_next_subnode(fit, conf_noffset, &ndepth);
+ noffset >= 0;
+ noffset = fdt_next_subnode(fit, noffset, &ndepth)) {
+ const char *node_name;
+ int ret = 0;
+
+ node_name = fit_get_name(fit, noffset, NULL);
+ if (!strncmp(node_name, FIT_SIG_NODENAME,
+ strlen(FIT_SIG_NODENAME))) {
+ ret = fit_config_process_sig(keydir, keydest,
+ fit, conf_name, conf_noffset, noffset, comment,
+ require_keys);
+ }
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
int fit_add_verification_data(const char *keydir, void *keydest, void *fit,
const char *comment, int require_keys)
{
- int images_noffset;
+ int images_noffset, confs_noffset;
int noffset;
int ndepth;
int ret;
@@ -375,5 +700,28 @@ int fit_add_verification_data(const char *keydir, void *keydest, void *fit,
return ret;
}

+ /* If there are no keys, we can't sign configurations */
+ if (!keydir)
+ return 0;
+
+ /* Find configurations parent node offset */
+ confs_noffset = fdt_path_offset(fit, FIT_CONFS_PATH);
+ if (confs_noffset < 0) {
+ printf("Can't find images parent node '%s' (%s)\n",
+ FIT_IMAGES_PATH, fdt_strerror(confs_noffset));
+ return -ENOENT;
+ }
+
+ /* Process its subnodes, print out component images details */
+ for (ndepth = 0,
+ noffset = fdt_next_subnode(fit, confs_noffset, &ndepth);
+ noffset >= 0;
+ noffset = fdt_next_subnode(fit, noffset, &ndepth)) {
+ ret = fit_config_add_verification_data(keydir, keydest,
+ fit, noffset, comment, require_keys);
+ if (ret)
+ return ret;
+ }
+
return 0;
}
--
1.7.7.3