image: Support signing of images

Add support for signing images using a new signature node. The process
is handled by fdt_add_verification_data() which now takes parameters to
provide the keys and related information.

Signed-off-by: Simon Glass <sjg@chromium.org>
diff --git a/common/image-fit.c b/common/image-fit.c
index f40f160..b75e119 100644
--- a/common/image-fit.c
+++ b/common/image-fit.c
@@ -234,42 +234,45 @@
  * @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
  *
+ * This function avoid using puts() since it prints a newline on the host
+ * but does not in U-Boot.
+ *
  * 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;
+	char *algo;
+	int required;
+	int ret, i;
 
-	/*
-	 * 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,
+	debug("%s  %s node:    '%s'\n", p, type,
 	      fit_get_name(fit, noffset, NULL));
-
-	printf("%s  Hash algo:    ", p);
+	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 {
@@ -278,7 +281,18 @@
 		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);
+	}
 }
 
 /**
@@ -303,8 +317,12 @@
 	 * 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");
+	}
 }
 
 /**
@@ -944,13 +962,23 @@
 {
 	const void	*data;
 	size_t		size;
-	int		noffset;
+	int		noffset = 0;
 	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)) {
 		err_msg = "Can't get image data/size";
-		return 0;
+		goto error;
+	}
+
+	/* 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 */
@@ -970,6 +998,15 @@
 						 &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..9b222da 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,145 @@
 
 	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;
+	char *err_msg = "";
+	int verified = 0;
+	int ret;
+
+	/* Process all hash subnodes of the component image node */
+	for (noffset = fdt_first_subnode(fit, image_noffset);
+	     noffset >= 0;
+	     noffset = fdt_next_subnode(fit, noffset)) {
+		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 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 (noffset = fdt_first_subnode(sig_blob, sig_node);
+	     noffset >= 0;
+	     noffset = fdt_next_subnode(sig_blob, noffset)) {
+		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;
+}