[U-Boot] [RFC PATCH 33/44] image: Verify signatures in FIT images

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

After checking hashes, also check signatures of FIT images.

Signed-off-by: Simon Glass <sjg@chromium.org>
---
common/image-fit.c | 80 ++++++++++++++++++++--------
common/image-sig.c | 148 ++++++++++++++++++++++++++++++++++++++++++++++++++++
include/image.h | 37 +++++++++++++
3 files changed, 243 insertions(+), 22 deletions(-)

diff --git a/common/image-fit.c b/common/image-fit.c
index ed98460..d255595 100644
--- a/common/image-fit.c
+++ b/common/image-fit.c
@@ -31,6 +31,8 @@
#include <time.h>
#else
#include <common.h>
+#include <errno.h>
+DECLARE_GLOBAL_DATA_PTR;
#endif /* !USE_HOSTCC*/

#include <bootstage.h>
@@ -235,42 +237,42 @@ void fit_print_contents(const void *fit)
* @fit: pointer to the FIT format image header
* @noffset: offset of the hash node
* @p: pointer to prefix string
+ * @type: Type of information to print ("hash" or "sign")
*
* fit_image_print_data() lists properies for the processed hash node
*
* returns:
* no returned results
*/
-static void fit_image_print_data(const void *fit, int noffset, const char *p)
+static void fit_image_print_data(const void *fit, int noffset, const char *p,
+ const char *type)
{
- char *algo;
+ const char *keyname;
uint8_t *value;
int value_len;
- int i, ret;
-
- /*
- * Check subnode name, must be equal to "hash".
- * Multiple hash nodes require unique unit node
- * names, e.g. hash at 1, hash at 2, etc.
- */
- if (strncmp(fit_get_name(fit, noffset, NULL),
- FIT_HASH_NODENAME,
- strlen(FIT_HASH_NODENAME)) != 0)
- return;
-
- debug("%s Hash node: '%s'\n", p,
- fit_get_name(fit, noffset, NULL));
+ char *algo;
+ int required;
+ int ret, i;

- printf("%s Hash algo: ", p);
+ debug("%s %s node: '%s'\n", p, type,
+ fit_get_name(fit, noffset, NULL));
+ printf("%s %s algo: ", p, type);
if (fit_image_hash_get_algo(fit, noffset, &algo)) {
printf("invalid/unsupported\n");
return;
}
- printf("%s\n", algo);
+ printf("%s", algo);
+ keyname = fdt_getprop(fit, noffset, "key-name-hint", NULL);
+ required = fdt_getprop(fit, noffset, "required", NULL) != NULL;
+ if (keyname)
+ printf(":%s", keyname);
+ if (required)
+ printf(" (required)");
+ printf("\n");

ret = fit_image_hash_get_value(fit, noffset, &value,
&value_len);
- printf("%s Hash value: ", p);
+ printf("%s %s value: ", p, type);
if (ret) {
printf("unavailable\n");
} else {
@@ -279,7 +281,18 @@ static void fit_image_print_data(const void *fit, int noffset, const char *p)
printf("\n");
}

- debug("%s Hash len: %d\n", p, value_len);
+ debug("%s %s len: %d\n", p, type, value_len);
+
+ /* Signatures have a time stamp */
+ if (IMAGE_ENABLE_TIMESTAMP && keyname) {
+ time_t timestamp;
+
+ printf("%s Timestamp: ", p);
+ if (fit_get_timestamp(fit, noffset, &timestamp))
+ printf("unavailable\n");
+ else
+ genimg_print_time(timestamp);
+ }
}

/**
@@ -304,8 +317,12 @@ static void fit_image_print_verification_data(const void *fit, int noffset,
* names, e.g. hash at 1, hash at 2, signature at 1, signature at 2, etc.
*/
name = fit_get_name(fit, noffset, NULL);
- if (!strncmp(name, FIT_HASH_NODENAME, strlen(FIT_HASH_NODENAME)))
- fit_image_print_data(fit, noffset, p);
+ if (!strncmp(name, FIT_HASH_NODENAME, strlen(FIT_HASH_NODENAME))) {
+ fit_image_print_data(fit, noffset, p, "Hash");
+ } else if (!strncmp(name, FIT_SIG_NODENAME,
+ strlen(FIT_SIG_NODENAME))) {
+ fit_image_print_data(fit, noffset, p, "Sign");
+ }
}

/**
@@ -952,6 +969,8 @@ int fit_image_verify(const void *fit, int image_noffset)
int noffset;
int ndepth;
char *err_msg = "";
+ int verify_all = 1;
+ int ret;

/* Get image data and data length */
if (fit_image_get_data(fit, image_noffset, &data, &size)) {
@@ -959,6 +978,14 @@ int fit_image_verify(const void *fit, int image_noffset)
return 0;
}

+ /* Verify all required signatures */
+ if (IMAGE_ENABLE_VERIFY &&
+ fit_image_verify_required_sigs(fit, image_noffset,
+ data, size, gd_fdt_blob(), &verify_all)) {
+ err_msg = "Unable to verify required signature";
+ goto error;
+ }
+
/* Process all hash subnodes of the component image node */
for (ndepth = 0,
noffset = fdt_next_subnode(fit, image_noffset, &ndepth);
@@ -977,6 +1004,15 @@ int fit_image_verify(const void *fit, int image_noffset)
size, &err_msg))
goto error;
puts("+ ");
+ } else if (IMAGE_ENABLE_VERIFY && verify_all &&
+ !strncmp(name, FIT_SIG_NODENAME,
+ strlen(FIT_SIG_NODENAME))) {
+ ret = fit_image_check_sig(fit, noffset, data,
+ size, -1, &err_msg);
+ if (ret)
+ puts("- ");
+ else
+ puts("+ ");
}
}

diff --git a/common/image-sig.c b/common/image-sig.c
index 841c662..793e7d6 100644
--- a/common/image-sig.c
+++ b/common/image-sig.c
@@ -22,6 +22,8 @@
#include <time.h>
#else
#include <common.h>
+#include <malloc.h>
+DECLARE_GLOBAL_DATA_PTR;
#endif /* !USE_HOSTCC*/
#include <errno.h>
#include <image.h>
@@ -40,3 +42,149 @@ struct image_sig_algo *image_get_sig_algo(const char *name)

return NULL;
}
+
+static int fit_image_setup_verify(struct image_sign_info *info,
+ const void *fit, int noffset, int required_keynode,
+ char **err_msgp)
+{
+ char *algo_name;
+
+ if (fit_image_hash_get_algo(fit, noffset, &algo_name)) {
+ *err_msgp = "Can't get hash algo property";
+ return -1;
+ }
+ memset(info, '\0', sizeof(*info));
+ info->keyname = fdt_getprop(fit, noffset, "key-name-hint", NULL);
+ info->fit = (void *)fit;
+ info->node_offset = noffset;
+ info->algo = image_get_sig_algo(algo_name);
+ info->fdt_blob = gd_fdt_blob();
+ info->required_keynode = required_keynode;
+ printf("%s:%s", algo_name, info->keyname);
+
+ if (!info->algo) {
+ *err_msgp = "Unknown signature algorithm";
+ return -1;
+ }
+
+ return 0;
+}
+
+int fit_image_check_sig(const void *fit, int noffset, const void *data,
+ size_t size, int required_keynode, char **err_msgp)
+{
+ struct image_sign_info info;
+ struct image_region region;
+ uint8_t *fit_value;
+ int fit_value_len;
+
+ *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;
+ }
+
+ region.data = data;
+ region.size = size;
+
+ if (info.algo->verify(&info, &region, 1, fit_value, fit_value_len)) {
+ *err_msgp = "Verification failed";
+ return -1;
+ }
+
+ return 0;
+}
+
+static int fit_image_verify_sig(const void *fit, int image_noffset,
+ const char *data, size_t size, 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 image node */
+ 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_SIG_NODENAME,
+ strlen(FIT_SIG_NODENAME))) {
+ ret = fit_image_check_sig(fit, noffset, data,
+ size, -1, &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' image node\n",
+ err_msg, fit_get_name(fit, noffset, NULL),
+ fit_get_name(fit, image_noffset, NULL));
+ return -1;
+}
+
+int fit_image_verify_required_sigs(const void *fit, int image_noffset,
+ const char *data, size_t size, const void *sig_blob,
+ int *no_sigsp)
+{
+ int verify_count = 0;
+ int ndepth, noffset;
+ int sig_node;
+
+ /* Work out what we need to verify */
+ *no_sigsp = 1;
+ 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, "image"))
+ continue;
+ ret = fit_image_verify_sig(fit, image_noffset, data, size,
+ sig_blob, noffset);
+ if (ret) {
+ printf("Failed to verify required signature '%s'\n",
+ fit_get_name(sig_blob, noffset, NULL));
+ return ret;
+ }
+ verify_count++;
+ }
+
+ if (verify_count)
+ *no_sigsp = 0;
+
+ return 0;
+}
diff --git a/include/image.h b/include/image.h
index a1072c0..b8aea83 100644
--- a/include/image.h
+++ b/include/image.h
@@ -765,6 +765,43 @@ struct image_sig_algo {
*/
struct image_sig_algo *image_get_sig_algo(const char *name);

+/**
+ * fit_image_verify_required_sigs() - Verify signatures marked as 'required'
+ *
+ * @fit: FIT to check
+ * @image_noffset: Offset of image node to check
+ * @data: Image data to check
+ * @size: Size of image data
+ * @sig_blob: FDT containing public keys
+ * @no_sigsp: Returns 1 if no signatures were required, and
+ * therefore nothing was checked. The caller may wish
+ * to fall back to other mechanisms, or refuse to
+ * boot.
+ * @return 0 if all verified ok, <0 on error
+ */
+int fit_image_verify_required_sigs(const void *fit, int image_noffset,
+ const char *data, size_t size, const void *sig_blob,
+ int *no_sigsp);
+
+/**
+ * fit_image_check_sig() - Check a single image signature node
+ *
+ * @fit: FIT to check
+ * @noffset: Offset of signature node to check
+ * @data: Image data to check
+ * @size: Size of image data
+ * @required_keynode: Offset in the control FDT of the required key node,
+ * if any. If this is given, then the image wil not
+ * pass verification unless that key is used. If this is
+ * -1 then any signature will do.
+ * @err_msgp: In the event of an error, this will be pointed to a
+ * help error string to display to the user.
+ * @return 0 if all verified ok, <0 on error
+ */
+int fit_image_check_sig(const void *fit, int noffset, const void *data,
+ size_t size, int required_keynode, char **err_msgp);
+
+
#ifndef USE_HOSTCC
static inline int fit_image_check_target_arch(const void *fdt, int node)
{
--
1.7.7.3