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

login
register
mail settings
Submitter Simon Glass
Date Jan. 5, 2013, 1:52 a.m.
Message ID <1357350734-13737-34-git-send-email-sjg@chromium.org>
Download mbox | patch
Permalink /patch/209618/
State Superseded, archived
Headers show

Comments

Simon Glass - Jan. 5, 2013, 1:52 a.m.
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(-)
Marek Vasut - Jan. 5, 2013, 8:20 a.m.
Dear Simon Glass,

[...]

> +	/* 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");

puts()

[...]
Best regards,
Marek Vasut
Simon Glass - Jan. 5, 2013, 9:48 p.m.
Hi Marek,

On Sat, Jan 5, 2013 at 12:20 AM, Marek Vasut <marex@denx.de> wrote:
> Dear Simon Glass,
>
> [...]
>
>> +     /* 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");
>
> puts()

I don't think I can do that, since puts() prints a newline on the host
(mkimage) but not in U-Boot. I will add a comment to that effect.

Regards,
Simon

>
> [...]
> Best regards,
> Marek Vasut

Patch

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@1, hash@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@1, hash@2, signature@1, signature@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)
 {