From patchwork Sat Jan 5 01:52:11 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 209637 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id 5D6EE2C008D for ; Sat, 5 Jan 2013 13:03:45 +1100 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 4335B4A03E; Sat, 5 Jan 2013 03:02:21 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 1HCTOYp9ZHaK; Sat, 5 Jan 2013 03:02:20 +0100 (CET) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id B4B444A192; Sat, 5 Jan 2013 02:57:55 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 04C4F4A10D for ; Sat, 5 Jan 2013 02:57:29 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id Xc3BXPHWjSBK for ; Sat, 5 Jan 2013 02:57:24 +0100 (CET) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from mail-ye0-f202.google.com (mail-ye0-f202.google.com [209.85.213.202]) by theia.denx.de (Postfix) with ESMTPS id 0D2204A036 for ; Sat, 5 Jan 2013 02:56:40 +0100 (CET) Received: by mail-ye0-f202.google.com with SMTP id r9so980221yen.5 for ; Fri, 04 Jan 2013 17:56:39 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=x-received:from:to:cc:subject:date:message-id:x-mailer:in-reply-to :references:x-gm-message-state; bh=cncHtt4FA75i2IDaessOuElGRSch/uPcFIxI6wzMF3w=; b=WKdlGWDKY+Z4q2Jb22bhqWP8195DMz3ZCYA0TA99H5DF0f3a+BOB+6LWPDUv809d1T GO/1r6zhidIttfPX2aqOFvFu4YL8EEomC8ysa7+9DgCQmG+0CqEwPxjHWJiCBioV9bVw YxnMGY9R4UYpdwlkCr+h1gr9d3DK/xqdYLXpr3Cc5MFIkKlOY2ntk+cGZW5CZ1QfeiZ+ lL4mHwOUN0zuEmj6Ftza/fKCf5XAwxF9ZF4Cb6+qLIsbegJdf5nnittCbPAVQ7seNNyl lHV6BBmus2LcYDboQv1oXSyxr9k6mz8HyNme3flQI2DGd++QtUFp5Fw5XUrfB/iAK2pw FOGg== X-Received: by 10.100.243.12 with SMTP id q12mr7640639anh.5.1357350999721; Fri, 04 Jan 2013 17:56:39 -0800 (PST) Received: from wpzn4.hot.corp.google.com (216-239-44-65.google.com [216.239.44.65]) by gmr-mx.google.com with ESMTPS id j11si2297509ank.2.2013.01.04.17.56.39 (version=TLSv1/SSLv3 cipher=AES128-SHA); Fri, 04 Jan 2013 17:56:39 -0800 (PST) Received: from kaka.mtv.corp.google.com (kaka.mtv.corp.google.com [172.22.73.79]) by wpzn4.hot.corp.google.com (Postfix) with ESMTP id 8737582004A; Fri, 4 Jan 2013 17:56:39 -0800 (PST) Received: by kaka.mtv.corp.google.com (Postfix, from userid 121222) id 66C4A160F7C; Fri, 4 Jan 2013 17:56:39 -0800 (PST) From: Simon Glass To: U-Boot Mailing List Date: Fri, 4 Jan 2013 17:52:11 -0800 Message-Id: <1357350734-13737-43-git-send-email-sjg@chromium.org> X-Mailer: git-send-email 1.7.7.3 In-Reply-To: <1357350734-13737-1-git-send-email-sjg@chromium.org> References: <1357350734-13737-1-git-send-email-sjg@chromium.org> X-Gm-Message-State: ALoCoQnDJO6HW+e8e57sCXvcZ7NumLFmlAWltWE4+VwsbknVRDYg2FrYmq5wjz2Jil3yKR6MuHMDT2c/JDoQAe1L7EJqDRKUpPp6a5ImQsv+68SMCX18m5ZCJNUks1o+4eJm2Njh9YUD9Vu1QpMMD0MmN+3r6Nho/KvAzG39l1hYxHWNKBey/Vj1n6z5860qHcF7DdT+Fj5z Cc: Joel A Fernandes , Joe Hershberger , Tom Rini , Vadim Bendebury , =?UTF-8?q?Andreas=20B=C3=A4ck?= Subject: [U-Boot] [RFC PATCH 42/44] image: Add support for signing of FIT configurations X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.11 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de 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 --- 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 DECLARE_GLOBAL_DATA_PTR; #endif /* !USE_HOSTCC*/ -#include #include #include @@ -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@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@1 { + data = /incbin/("test-kernel.bin"); + type = "kernel_noload"; + arch = "sandbox"; + os = "linux"; + compression = "lzo"; + load = <0x4>; + entry = <0x8>; + kernel-version = <1>; + hash@1 { + algo = "sha1"; + }; + }; + fdt@1 { + description = "snow"; + data = /incbin/("sandbox-kernel.dtb"); + type = "flat_dt"; + arch = "sandbox"; + compression = "none"; + fdt-version = <1>; + hash@1 { + algo = "sha1"; + }; + }; + }; + configurations { + default = "conf@1"; + conf@1 { + kernel = "kernel@1"; + fdt = "fdt@1"; + signature@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@1", "/images/kernel@1", + "/images/kernel@1/hash@1", "/images/fdt@1", + "/images/fdt@1/hash@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@1 { + data = + signature@1 { + algo = "sha1,rsa2048"; + value = + }; + }; + kernel@2 { + data = + signature@1 { + algo = "sha1,rsa2048"; + value = + }; + }; + fdt@1 { + data = ; + signature@1 { + algo = "sha1,rsa2048"; + vaue = + }; + }; + fdt@2 { + data = ; + signature@1 { + algo = "sha1,rsa2048"; + vaue = + }; + }; + }; + configurations { + default = "conf@1"; + conf@1 { + kernel = "kernel@1"; + fdt = "fdt@1"; + }; + conf@1 { + kernel = "kernel@2"; + fdt = "fdt@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@1"; + conf@1 { + kernel = "kernel@1"; + fdt = "fdt@1"; + }; + conf@1 { + kernel = "kernel@2"; + fdt = "fdt@2"; + }; + conf@3 { + kernel = "kernel@1"; + fdt = "fdt@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@1 { + data = + hash@1 { + algo = "sha1"; + value = + }; + }; + kernel@2 { + data = + hash@1 { + algo = "sha1"; + value = + }; + }; + fdt@1 { + data = ; + hash@1 { + algo = "sha1"; + value = + }; + }; + fdt@2 { + data = ; + hash@1 { + algo = "sha1"; + value = + }; + }; + }; + configurations { + default = "conf@1"; + conf@1 { + kernel = "kernel@1"; + fdt = "fdt@1"; + signature@1 { + algo = "sha1,rsa2048"; + value = ; + }; + }; + conf@2 { + kernel = "kernel@2"; + fdt = "fdt@2"; + signature@1 { + algo = "sha1,rsa2048"; + value = ; + }; + }; + }; +}; + + +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@1, the kernel and fdt that are +pointed to by the configuration (/images/kernel@1, /images/kernel@1/hash@1, +/images/fdt@1, /images/fdt@1/hash@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@1/signature@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, ®ion, + ®ion_count, ®ion_prop, ®ion_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; }