All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 00/11] efi_loader: capsule: improve capsule authentication support
@ 2021-10-07  6:23 AKASHI Takahiro
  2021-10-07  6:23 ` [PATCH v4 01/11] Revert "Revert "mkeficapsule: Remove dtb related options"" AKASHI Takahiro
                   ` (10 more replies)
  0 siblings, 11 replies; 40+ messages in thread
From: AKASHI Takahiro @ 2021-10-07  6:23 UTC (permalink / raw)
  To: xypron.glpk, agraf
  Cc: ilias.apalodimas, sughosh.ganu, masami.hiramatsu, u-boot,
	AKASHI Takahiro

As I proposed and discussed in [1] and [2], I have made a couple of
improvements on the current implementation of capsule update in this
patch set.

* add signing feature to mkeficapsule
* add "--guid" option to mkeficapsule
* add man page of mkeficapsule

[1] https://lists.denx.de/pipermail/u-boot/2021-April/447918.html
[2] https://lists.denx.de/pipermail/u-boot/2021-July/455292.html

Prerequisite patches
====================
None

Test
====
* locally passed the pytest which is included in this patch series
  on sandbox built.
  (CONFIG_EFI_CAPSULE_AUTHENTICATE is required for authentication test.)

Changes
=======
v4 (Oct 7, 2021)
* rebased on v2021.10
* align with "Revert "efi_capsule: Move signature from DTB to .rodata""
* add more missing *revert* commits (patch#1,#2,#3)
* add fdtsig.sh, replacing dtb support in mkeficapsule (patch#4)
* update/revise the man/uefi doc (patch#6,#7)
* fix a bug in parsing guid string (patch#8)
* add a test for "--guid" option (patch#10)
* use dtb-based authentication test as done in v1 (patch#11)

v3 (Aug 31, 2021)
* rebased on v2021.10-rc3
* remove pytest-related patches
* add function descriptions in mkeficapsule.c
* correct format specifiers in printf()
* let main() return 0 or -1 only
* update doc/develop/uefi/uefi.rst for syntax change of mkeficapsule

v2 (July 28, 2021)
* rebased on v2021.10-rc*
* removed dependency on target's configuration
* removed fdtsig.sh and others
* add man page
* update the UEFI document
* add dedicate defconfig for testing on sandbox
* add gitlab CI support
* add "--guid" option to mkeficapsule
  (yet rather RFC)

Initial release (May 12, 2021)
* based on v2021.07-rc2

AKASHI Takahiro (11):
  Revert "Revert "mkeficapsule: Remove dtb related options""
  Revert "Revert "doc: Update CapsuleUpdate READMEs""
  efi_loader: capsule: add back efi_get_public_key_data()
  tools: add fdtsig.sh
  tools: mkeficapsule: add firmwware image signing
  tools: mkeficapsule: add man page
  doc: update UEFI document for usage of mkeficapsule
  tools: mkeficapsule: allow for specifying GUID explicitly
  test/py: efi_capsule: align with the syntax change of mkeficapsule
  test/py: efi_capsule: add a test for "--guid" option
  test/py: efi_capsule: add image authentication test

 MAINTAINERS                                   |   2 +
 doc/develop/uefi/uefi.rst                     |  94 +++
 doc/mkeficapsule.1                            | 107 +++
 lib/efi_loader/efi_capsule.c                  |  36 +
 .../py/tests/test_efi_capsule/capsule_defs.py |   5 +
 test/py/tests/test_efi_capsule/conftest.py    |  42 +-
 test/py/tests/test_efi_capsule/signature.dts  |  10 +
 .../test_efi_capsule/test_capsule_firmware.py |  67 ++
 .../test_capsule_firmware_signed.py           | 233 +++++++
 tools/Kconfig                                 |   7 +
 tools/Makefile                                |   8 +-
 tools/fdtsig.sh                               |  40 ++
 tools/mkeficapsule.c                          | 638 ++++++++++++------
 13 files changed, 1062 insertions(+), 227 deletions(-)
 create mode 100644 doc/mkeficapsule.1
 create mode 100644 test/py/tests/test_efi_capsule/signature.dts
 create mode 100644 test/py/tests/test_efi_capsule/test_capsule_firmware_signed.py
 create mode 100755 tools/fdtsig.sh

-- 
2.33.0


^ permalink raw reply	[flat|nested] 40+ messages in thread

* [PATCH v4 01/11] Revert "Revert "mkeficapsule: Remove dtb related options""
  2021-10-07  6:23 [PATCH v4 00/11] efi_loader: capsule: improve capsule authentication support AKASHI Takahiro
@ 2021-10-07  6:23 ` AKASHI Takahiro
  2021-10-08 15:38   ` Simon Glass
  2021-10-07  6:23 ` [PATCH v4 02/11] Revert "Revert "doc: Update CapsuleUpdate READMEs"" AKASHI Takahiro
                   ` (9 subsequent siblings)
  10 siblings, 1 reply; 40+ messages in thread
From: AKASHI Takahiro @ 2021-10-07  6:23 UTC (permalink / raw)
  To: xypron.glpk, agraf
  Cc: ilias.apalodimas, sughosh.ganu, masami.hiramatsu, u-boot,
	AKASHI Takahiro

This reverts commit d428e81266a59974ade74c1ba019af39f23304ab.
We have agreed with removing dtb-related stuff from mkeficapsule
command even if the commit 47a25e81d35c ("Revert "efi_capsule: Move
signature from DTB to .rodata"") was applied.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 tools/mkeficapsule.c | 229 ++-----------------------------------------
 1 file changed, 7 insertions(+), 222 deletions(-)

diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
index de0a62898886..4995ba4e0c2a 100644
--- a/tools/mkeficapsule.c
+++ b/tools/mkeficapsule.c
@@ -4,22 +4,17 @@
  *		Author: AKASHI Takahiro
  */
 
-#include <errno.h>
 #include <getopt.h>
 #include <malloc.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <unistd.h>
 #include <linux/types.h>
 
-#include <sys/mman.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 
-#include "fdt_host.h"
-
 typedef __u8 u8;
 typedef __u16 u16;
 typedef __u32 u32;
@@ -29,9 +24,6 @@ typedef __s32 s32;
 
 #define aligned_u64 __aligned_u64
 
-#define SIGNATURE_NODENAME	"signature"
-#define OVERLAY_NODENAME	"__overlay__"
-
 #ifndef __packed
 #define __packed __attribute__((packed))
 #endif
@@ -52,9 +44,6 @@ static struct option options[] = {
 	{"raw", required_argument, NULL, 'r'},
 	{"index", required_argument, NULL, 'i'},
 	{"instance", required_argument, NULL, 'I'},
-	{"dtb", required_argument, NULL, 'D'},
-	{"public key", required_argument, NULL, 'K'},
-	{"overlay", no_argument, NULL, 'O'},
 	{"help", no_argument, NULL, 'h'},
 	{NULL, 0, NULL, 0},
 };
@@ -68,187 +57,10 @@ static void print_usage(void)
 	       "\t-r, --raw <raw image>       new raw image file\n"
 	       "\t-i, --index <index>         update image index\n"
 	       "\t-I, --instance <instance>   update hardware instance\n"
-	       "\t-K, --public-key <key file> public key esl file\n"
-	       "\t-D, --dtb <dtb file>        dtb file\n"
-	       "\t-O, --overlay               the dtb file is an overlay\n"
 	       "\t-h, --help                  print a help message\n",
 	       tool_name);
 }
 
-static int fdt_add_pub_key_data(void *sptr, void *dptr, size_t key_size,
-				bool overlay)
-{
-	int parent;
-	int ov_node;
-	int frag_node;
-	int ret = 0;
-
-	if (overlay) {
-		/*
-		 * The signature would be stored in the
-		 * first fragment node of the overlay
-		 */
-		frag_node = fdt_first_subnode(dptr, 0);
-		if (frag_node == -FDT_ERR_NOTFOUND) {
-			fprintf(stderr,
-				"Couldn't find the fragment node: %s\n",
-				fdt_strerror(frag_node));
-			goto done;
-		}
-
-		ov_node = fdt_subnode_offset(dptr, frag_node, OVERLAY_NODENAME);
-		if (ov_node == -FDT_ERR_NOTFOUND) {
-			fprintf(stderr,
-				"Couldn't find the __overlay__ node: %s\n",
-				fdt_strerror(ov_node));
-			goto done;
-		}
-	} else {
-		ov_node = 0;
-	}
-
-	parent = fdt_subnode_offset(dptr, ov_node, SIGNATURE_NODENAME);
-	if (parent == -FDT_ERR_NOTFOUND) {
-		parent = fdt_add_subnode(dptr, ov_node, SIGNATURE_NODENAME);
-		if (parent < 0) {
-			ret = parent;
-			if (ret != -FDT_ERR_NOSPACE) {
-				fprintf(stderr,
-					"Couldn't create signature node: %s\n",
-					fdt_strerror(parent));
-			}
-		}
-	}
-	if (ret)
-		goto done;
-
-	/* Write the key to the FDT node */
-	ret = fdt_setprop(dptr, parent, "capsule-key",
-			  sptr, key_size);
-
-done:
-	if (ret)
-		ret = ret == -FDT_ERR_NOSPACE ? -ENOSPC : -EIO;
-
-	return ret;
-}
-
-static int add_public_key(const char *pkey_file, const char *dtb_file,
-			  bool overlay)
-{
-	int ret;
-	int srcfd = -1;
-	int destfd = -1;
-	void *sptr = NULL;
-	void *dptr = NULL;
-	off_t src_size;
-	struct stat pub_key;
-	struct stat dtb;
-
-	/* Find out the size of the public key */
-	srcfd = open(pkey_file, O_RDONLY);
-	if (srcfd == -1) {
-		fprintf(stderr, "%s: Can't open %s: %s\n",
-			__func__, pkey_file, strerror(errno));
-		ret = -1;
-		goto err;
-	}
-
-	ret = fstat(srcfd, &pub_key);
-	if (ret == -1) {
-		fprintf(stderr, "%s: Can't stat %s: %s\n",
-			__func__, pkey_file, strerror(errno));
-		ret = -1;
-		goto err;
-	}
-
-	src_size = pub_key.st_size;
-
-	/* mmap the public key esl file */
-	sptr = mmap(0, src_size, PROT_READ, MAP_SHARED, srcfd, 0);
-	if (sptr == MAP_FAILED) {
-		fprintf(stderr, "%s: Failed to mmap %s:%s\n",
-			__func__, pkey_file, strerror(errno));
-		ret = -1;
-		goto err;
-	}
-
-	/* Open the dest FDT */
-	destfd = open(dtb_file, O_RDWR);
-	if (destfd == -1) {
-		fprintf(stderr, "%s: Can't open %s: %s\n",
-			__func__, dtb_file, strerror(errno));
-		ret = -1;
-		goto err;
-	}
-
-	ret = fstat(destfd, &dtb);
-	if (ret == -1) {
-		fprintf(stderr, "%s: Can't stat %s: %s\n",
-			__func__, dtb_file, strerror(errno));
-		goto err;
-	}
-
-	dtb.st_size += src_size + 0x30;
-	if (ftruncate(destfd, dtb.st_size)) {
-		fprintf(stderr, "%s: Can't expand %s: %s\n",
-			__func__, dtb_file, strerror(errno));
-		ret = -1;
-		goto err;
-	}
-
-	errno = 0;
-	/* mmap the dtb file */
-	dptr = mmap(0, dtb.st_size, PROT_READ | PROT_WRITE, MAP_SHARED,
-		    destfd, 0);
-	if (dptr == MAP_FAILED) {
-		fprintf(stderr, "%s: Failed to mmap %s:%s\n",
-			__func__, dtb_file, strerror(errno));
-		ret = -1;
-		goto err;
-	}
-
-	if (fdt_check_header(dptr)) {
-		fprintf(stderr, "%s: Invalid FDT header\n", __func__);
-		ret = -1;
-		goto err;
-	}
-
-	ret = fdt_open_into(dptr, dptr, dtb.st_size);
-	if (ret) {
-		fprintf(stderr, "%s: Cannot expand FDT: %s\n",
-			__func__, fdt_strerror(ret));
-		ret = -1;
-		goto err;
-	}
-
-	/* Copy the esl file to the expanded FDT */
-	ret = fdt_add_pub_key_data(sptr, dptr, src_size, overlay);
-	if (ret < 0) {
-		fprintf(stderr, "%s: Unable to add public key to the FDT\n",
-			__func__);
-		ret = -1;
-		goto err;
-	}
-
-	ret = 0;
-
-err:
-	if (sptr)
-		munmap(sptr, src_size);
-
-	if (dptr)
-		munmap(dptr, dtb.st_size);
-
-	if (srcfd != -1)
-		close(srcfd);
-
-	if (destfd != -1)
-		close(destfd);
-
-	return ret;
-}
-
 static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
 			unsigned long index, unsigned long instance)
 {
@@ -366,22 +178,16 @@ err_1:
 int main(int argc, char **argv)
 {
 	char *file;
-	char *pkey_file;
-	char *dtb_file;
 	efi_guid_t *guid;
 	unsigned long index, instance;
 	int c, idx;
-	int ret;
-	bool overlay = false;
 
 	file = NULL;
-	pkey_file = NULL;
-	dtb_file = NULL;
 	guid = NULL;
 	index = 0;
 	instance = 0;
 	for (;;) {
-		c = getopt_long(argc, argv, "f:r:i:I:v:D:K:Oh", options, &idx);
+		c = getopt_long(argc, argv, "f:r:i:I:v:h", options, &idx);
 		if (c == -1)
 			break;
 
@@ -408,43 +214,22 @@ int main(int argc, char **argv)
 		case 'I':
 			instance = strtoul(optarg, NULL, 0);
 			break;
-		case 'K':
-			if (pkey_file) {
-				printf("Public Key already specified\n");
-				return -1;
-			}
-			pkey_file = optarg;
-			break;
-		case 'D':
-			if (dtb_file) {
-				printf("DTB file already specified\n");
-				return -1;
-			}
-			dtb_file = optarg;
-			break;
-		case 'O':
-			overlay = true;
-			break;
 		case 'h':
 			print_usage();
 			return 0;
 		}
 	}
 
-	/* need a fit image file or raw image file */
-	if (!file && !pkey_file && !dtb_file) {
+	/* need an output file */
+	if (argc != optind + 1) {
 		print_usage();
 		exit(EXIT_FAILURE);
 	}
 
-	if (pkey_file && dtb_file) {
-		ret = add_public_key(pkey_file, dtb_file, overlay);
-		if (ret == -1) {
-			printf("Adding public key to the dtb failed\n");
-			exit(EXIT_FAILURE);
-		} else {
-			exit(EXIT_SUCCESS);
-		}
+	/* need a fit image file or raw image file */
+	if (!file) {
+		print_usage();
+		exit(EXIT_SUCCESS);
 	}
 
 	if (create_fwbin(argv[optind], file, guid, index, instance)
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v4 02/11] Revert "Revert "doc: Update CapsuleUpdate READMEs""
  2021-10-07  6:23 [PATCH v4 00/11] efi_loader: capsule: improve capsule authentication support AKASHI Takahiro
  2021-10-07  6:23 ` [PATCH v4 01/11] Revert "Revert "mkeficapsule: Remove dtb related options"" AKASHI Takahiro
@ 2021-10-07  6:23 ` AKASHI Takahiro
  2021-10-07  6:23 ` [PATCH v4 03/11] efi_loader: capsule: add back efi_get_public_key_data() AKASHI Takahiro
                   ` (8 subsequent siblings)
  10 siblings, 0 replies; 40+ messages in thread
From: AKASHI Takahiro @ 2021-10-07  6:23 UTC (permalink / raw)
  To: xypron.glpk, agraf
  Cc: ilias.apalodimas, sughosh.ganu, masami.hiramatsu, u-boot,
	AKASHI Takahiro

This reverts commit a7e4f905d206d5895dab4bd38a8316e4f2fe15fe.
The description originally written by Sughosh is still valid
even after the commit 47a25e81d35c ("Revert "efi_capsule: Move signature
from DTB to .rodata"") was applied.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 doc/develop/uefi/uefi.rst | 124 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 124 insertions(+)

diff --git a/doc/develop/uefi/uefi.rst b/doc/develop/uefi/uefi.rst
index 4f2b8b036db8..f17138f5c765 100644
--- a/doc/develop/uefi/uefi.rst
+++ b/doc/develop/uefi/uefi.rst
@@ -277,6 +277,130 @@ Enable ``CONFIG_OPTEE``, ``CONFIG_CMD_OPTEE_RPMB`` and ``CONFIG_EFI_MM_COMM_TEE`
 
 [1] https://optee.readthedocs.io/en/latest/building/efi_vars/stmm.html
 
+Enabling UEFI Capsule Update feature
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Support has been added for the UEFI capsule update feature which
+enables updating the U-Boot image using the UEFI firmware management
+protocol (FMP). The capsules are not passed to the firmware through
+the UpdateCapsule runtime service. Instead, capsule-on-disk
+functionality is used for fetching the capsule from the EFI System
+Partition (ESP) by placing the capsule file under the
+\EFI\UpdateCapsule directory.
+
+The directory \EFI\UpdateCapsule is checked for capsules only within the
+EFI system partition on the device specified in the active boot option
+determined by reference to BootNext variable or BootOrder variable processing.
+The active Boot Variable is the variable with highest priority BootNext or
+within BootOrder that refers to a device found to be present. Boot variables
+in BootOrder but referring to devices not present are ignored when determining
+active boot variable.
+Before starting a capsule update make sure your capsules are installed in the
+correct ESP partition or set BootNext.
+
+Performing the update
+*********************
+
+Since U-boot doesn't currently support SetVariable at runtime there's a Kconfig
+option (CONFIG_EFI_IGNORE_OSINDICATIONS) to disable the OsIndications variable
+check. If that option is enabled just copy your capsule to \EFI\UpdateCapsule.
+
+If that option is disabled, you'll need to set the OsIndications variable with::
+
+    => setenv -e -nv -bs -rt -v OsIndications =0x04
+
+Finally, the capsule update can be initiated either by rebooting the board,
+which is the preferred method, or by issuing the following command::
+
+    => efidebug capsule disk-update
+
+**The efidebug command is should only be used during debugging/development.**
+
+Enabling Capsule Authentication
+*******************************
+
+The UEFI specification defines a way of authenticating the capsule to
+be updated by verifying the capsule signature. The capsule signature
+is computed and prepended to the capsule payload at the time of
+capsule generation. This signature is then verified by using the
+public key stored as part of the X509 certificate. This certificate is
+in the form of an efi signature list (esl) file, which is embedded as
+part of U-Boot.
+
+The capsule authentication feature can be enabled through the
+following config, in addition to the configs listed above for capsule
+update::
+
+    CONFIG_EFI_CAPSULE_AUTHENTICATE=y
+    CONFIG_EFI_CAPSULE_KEY_PATH=<path to .esl cert>
+
+The public and private keys used for the signing process are generated
+and used by the steps highlighted below::
+
+    1. Install utility commands on your host
+       * OPENSSL
+       * efitools
+
+    2. Create signing keys and certificate files on your host
+
+        $ openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=CRT/ \
+            -keyout CRT.key -out CRT.crt -nodes -days 365
+        $ cert-to-efi-sig-list CRT.crt CRT.esl
+
+        $ openssl x509 -in CRT.crt -out CRT.cer -outform DER
+        $ openssl x509 -inform DER -in CRT.cer -outform PEM -out CRT.pub.pem
+
+        $ openssl pkcs12 -export -out CRT.pfx -inkey CRT.key -in CRT.crt
+        $ openssl pkcs12 -in CRT.pfx -nodes -out CRT.pem
+
+The capsule file can be generated by using the GenerateCapsule.py
+script in EDKII::
+
+    $ ./BaseTools/BinWrappers/PosixLike/GenerateCapsule -e -o \
+      <capsule_file_name> --monotonic-count <val> --fw-version \
+      <val> --lsv <val> --guid \
+      e2bb9c06-70e9-4b14-97a3-5a7913176e3f --verbose \
+      --update-image-index <val> --signer-private-cert \
+      /path/to/CRT.pem --trusted-public-cert \
+      /path/to/CRT.pub.pem --other-public-cert /path/to/CRT.pub.pem \
+      <u-boot.bin>
+
+Place the capsule generated in the above step on the EFI System
+Partition under the EFI/UpdateCapsule directory
+
+Testing on QEMU
+***************
+
+Currently, support has been added on the QEMU ARM64 virt platform for
+updating the U-Boot binary as a raw image when the platform is booted
+in non-secure mode, i.e. with CONFIG_TFABOOT disabled. For this
+configuration, the QEMU platform needs to be booted with
+'secure=off'. The U-Boot binary placed on the first bank of the NOR
+flash at offset 0x0. The U-Boot environment is placed on the second
+NOR flash bank at offset 0x4000000.
+
+The capsule update feature is enabled with the following configuration
+settings::
+
+    CONFIG_MTD=y
+    CONFIG_FLASH_CFI_MTD=y
+    CONFIG_CMD_MTDPARTS=y
+    CONFIG_CMD_DFU=y
+    CONFIG_DFU_MTD=y
+    CONFIG_PCI_INIT_R=y
+    CONFIG_EFI_CAPSULE_ON_DISK=y
+    CONFIG_EFI_CAPSULE_FIRMWARE_MANAGEMENT=y
+    CONFIG_EFI_CAPSULE_FIRMWARE=y
+    CONFIG_EFI_CAPSULE_FIRMWARE_RAW=y
+
+In addition, the following config needs to be disabled(QEMU ARM specific)::
+
+    CONFIG_TFABOOT
+
+The capsule file can be generated by using the tools/mkeficapsule::
+
+    $ mkeficapsule --raw <u-boot.bin> --index 1 <capsule_file_name>
+
 Executing the boot manager
 ~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v4 03/11] efi_loader: capsule: add back efi_get_public_key_data()
  2021-10-07  6:23 [PATCH v4 00/11] efi_loader: capsule: improve capsule authentication support AKASHI Takahiro
  2021-10-07  6:23 ` [PATCH v4 01/11] Revert "Revert "mkeficapsule: Remove dtb related options"" AKASHI Takahiro
  2021-10-07  6:23 ` [PATCH v4 02/11] Revert "Revert "doc: Update CapsuleUpdate READMEs"" AKASHI Takahiro
@ 2021-10-07  6:23 ` AKASHI Takahiro
  2021-10-08 19:25   ` Ilias Apalodimas
  2021-10-15  0:40   ` Simon Glass
  2021-10-07  6:23 ` [PATCH v4 04/11] tools: add fdtsig.sh AKASHI Takahiro
                   ` (7 subsequent siblings)
  10 siblings, 2 replies; 40+ messages in thread
From: AKASHI Takahiro @ 2021-10-07  6:23 UTC (permalink / raw)
  To: xypron.glpk, agraf
  Cc: ilias.apalodimas, sughosh.ganu, masami.hiramatsu, u-boot,
	AKASHI Takahiro

The commit 47a25e81d35c ("Revert "efi_capsule: Move signature from DTB to
.rodata"") failed to revert the removal of efi_get_public_key_data().

Add back this function and move it under lib/efi_loader so that other
platforms can utilize it. It is now declared as a weak function so that
it can be replaced with a platform-specific implementation.

Fixes: 47a25e81d35c ("Revert "efi_capsule: Move signature from DTB to
	.rodata"")
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 lib/efi_loader/efi_capsule.c | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
index b75e4bcba1a9..44f5da61a9be 100644
--- a/lib/efi_loader/efi_capsule.c
+++ b/lib/efi_loader/efi_capsule.c
@@ -11,15 +11,20 @@
 #include <common.h>
 #include <efi_loader.h>
 #include <efi_variable.h>
+#include <env.h>
+#include <fdtdec.h>
 #include <fs.h>
 #include <malloc.h>
 #include <mapmem.h>
 #include <sort.h>
+#include <asm/global_data.h>
 
 #include <crypto/pkcs7.h>
 #include <crypto/pkcs7_parser.h>
 #include <linux/err.h>
 
+DECLARE_GLOBAL_DATA_PTR;
+
 const efi_guid_t efi_guid_capsule_report = EFI_CAPSULE_REPORT_GUID;
 static const efi_guid_t efi_guid_firmware_management_capsule_id =
 		EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID;
@@ -251,6 +256,37 @@ out:
 }
 
 #if defined(CONFIG_EFI_CAPSULE_AUTHENTICATE)
+int __weak efi_get_public_key_data(void **pkey, efi_uintn_t *pkey_len)
+{
+	const void *fdt_blob = gd->fdt_blob;
+	const void *blob;
+	const char *cnode_name = "capsule-key";
+	const char *snode_name = "signature";
+	int sig_node;
+	int len;
+
+	sig_node = fdt_subnode_offset(fdt_blob, 0, snode_name);
+	if (sig_node < 0) {
+		log_err("Unable to get signature node offset\n");
+
+		return -FDT_ERR_NOTFOUND;
+	}
+
+	blob = fdt_getprop(fdt_blob, sig_node, cnode_name, &len);
+
+	if (!blob || len < 0) {
+		log_err("Unable to get capsule-key value\n");
+		*pkey = NULL;
+		*pkey_len = 0;
+
+		return -FDT_ERR_NOTFOUND;
+	}
+
+	*pkey = (void *)blob;
+	*pkey_len = len;
+
+	return 0;
+}
 
 efi_status_t efi_capsule_authenticate(const void *capsule, efi_uintn_t capsule_size,
 				      void **image, efi_uintn_t *image_size)
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v4 04/11] tools: add fdtsig.sh
  2021-10-07  6:23 [PATCH v4 00/11] efi_loader: capsule: improve capsule authentication support AKASHI Takahiro
                   ` (2 preceding siblings ...)
  2021-10-07  6:23 ` [PATCH v4 03/11] efi_loader: capsule: add back efi_get_public_key_data() AKASHI Takahiro
@ 2021-10-07  6:23 ` AKASHI Takahiro
  2021-10-11 14:54   ` Simon Glass
  2021-10-07  6:23 ` [PATCH v4 05/11] tools: mkeficapsule: add firmwware image signing AKASHI Takahiro
                   ` (6 subsequent siblings)
  10 siblings, 1 reply; 40+ messages in thread
From: AKASHI Takahiro @ 2021-10-07  6:23 UTC (permalink / raw)
  To: xypron.glpk, agraf
  Cc: ilias.apalodimas, sughosh.ganu, masami.hiramatsu, u-boot,
	AKASHI Takahiro

With this script, a public key is added to a device tree blob
as the default efi_get_public_key_data() expects.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 MAINTAINERS     |  1 +
 tools/fdtsig.sh | 40 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 41 insertions(+)
 create mode 100755 tools/fdtsig.sh

diff --git a/MAINTAINERS b/MAINTAINERS
index 5370b550648e..650e428b6cb4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -713,6 +713,7 @@ F:	cmd/bootefi.c
 F:	cmd/efidebug.c
 F:	cmd/nvedit_efi.c
 F:	tools/efivar.py
+F:	tools/fdtsig.sh
 F:	tools/file2include.c
 F:	tools/mkeficapsule.c
 
diff --git a/tools/fdtsig.sh b/tools/fdtsig.sh
new file mode 100755
index 000000000000..5ce7357614d7
--- /dev/null
+++ b/tools/fdtsig.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0+
+#
+# script to add a certificate (efi-signature-list) to dtb blob
+
+usage() {
+	if [ -n "$*" ]; then
+		echo "ERROR: $*"
+	fi
+	echo "Usage: "$(basename $0) " <esl file> <dtb file>"
+}
+
+if [ "$#" -ne 2 ]; then
+	usage "Arguments missing"
+	exit 1
+fi
+
+ESL=$1
+DTB=$2
+NEW_DTB=$(basename $DTB)_tmp
+SIG=signature
+
+cat << 'EOF' > $SIG.dts
+/dts-v1/;
+/plugin/;
+
+&{/} {
+    signature {
+	    capsule-key = /incbin/("ESL");
+    };
+};
+EOF
+
+sed -in "s/ESL/$ESL/" $SIG.dts
+
+dtc -@ -I dts -O dtb -o $SIG.dtbo $SIG.dts
+fdtoverlay -i $DTB -o $NEW_DTB -v $SIG.dtbo
+mv $NEW_DTB $DTB
+
+#rm $SIG.dts $SIG.dtbo $NEW_DTB
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v4 05/11] tools: mkeficapsule: add firmwware image signing
  2021-10-07  6:23 [PATCH v4 00/11] efi_loader: capsule: improve capsule authentication support AKASHI Takahiro
                   ` (3 preceding siblings ...)
  2021-10-07  6:23 ` [PATCH v4 04/11] tools: add fdtsig.sh AKASHI Takahiro
@ 2021-10-07  6:23 ` AKASHI Takahiro
  2021-10-20  8:17   ` Masami Hiramatsu
  2021-10-07  6:23 ` [PATCH v4 06/11] tools: mkeficapsule: add man page AKASHI Takahiro
                   ` (5 subsequent siblings)
  10 siblings, 1 reply; 40+ messages in thread
From: AKASHI Takahiro @ 2021-10-07  6:23 UTC (permalink / raw)
  To: xypron.glpk, agraf
  Cc: ilias.apalodimas, sughosh.ganu, masami.hiramatsu, u-boot,
	AKASHI Takahiro

With this enhancement, mkeficapsule will be able to sign a capsule
file when it is created. A signature added will be used later
in the verification at FMP's SetImage() call.

To do that, We need specify additional command parameters:
  -monotonic-cout <count> : monotonic count
  -private-key <private key file> : private key file
  -certificate <certificate file> : certificate file
Only when all of those parameters are given, a signature will be added
to a capsule file.

Users are expected to maintain and increment the monotonic count at
every time of the update for each firmware image.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 tools/Kconfig        |   7 +
 tools/Makefile       |   8 +-
 tools/mkeficapsule.c | 435 +++++++++++++++++++++++++++++++++++++++----
 3 files changed, 416 insertions(+), 34 deletions(-)

diff --git a/tools/Kconfig b/tools/Kconfig
index d6f82cd949b5..9a37ed035311 100644
--- a/tools/Kconfig
+++ b/tools/Kconfig
@@ -20,4 +20,11 @@ config TOOLS_LIBCRYPTO
 	  This selection does not affect target features, such as runtime FIT
 	  signature verification.
 
+config TOOLS_MKEFICAPSULE
+	bool "Build efimkcapsule command"
+	default y if EFI_CAPSULE_ON_DISK
+	help
+	  This command allows users to create a UEFI capsule file and,
+	  optionally sign that file. If you want to enable UEFI capsule
+	  update feature on your target, you certainly need this.
 endmenu
diff --git a/tools/Makefile b/tools/Makefile
index 4a86321f6467..6ea3033dbfb8 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -237,8 +237,12 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs
 hostprogs-$(CONFIG_ASN1_COMPILER)	+= asn1_compiler
 HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
 
-mkeficapsule-objs	:= mkeficapsule.o $(LIBFDT_OBJS)
-hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
+HOSTLDLIBS_mkeficapsule += -luuid
+ifeq ($(CONFIG_TOOLS_LIBCRYPTO),y)
+HOSTLDLIBS_mkeficapsule += \
+	$(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto")
+endif
+hostprogs-$(CONFIG_TOOLS_MKEFICAPSULE) += mkeficapsule
 
 # We build some files with extra pedantic flags to try to minimize things
 # that won't build on some weird host compiler -- though there are lots of
diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
index 4995ba4e0c2a..5541e4bda894 100644
--- a/tools/mkeficapsule.c
+++ b/tools/mkeficapsule.c
@@ -15,6 +15,16 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 
+#include <linux/kconfig.h>
+#ifdef CONFIG_TOOLS_LIBCRYPTO
+#include <openssl/asn1.h>
+#include <openssl/bio.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+#include <openssl/pkcs7.h>
+#endif
+
 typedef __u8 u8;
 typedef __u16 u16;
 typedef __u32 u32;
@@ -38,12 +48,25 @@ efi_guid_t efi_guid_image_type_uboot_fit =
 		EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID;
 efi_guid_t efi_guid_image_type_uboot_raw =
 		EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID;
+efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
+
+#ifdef CONFIG_TOOLS_LIBCRYPTO
+static const char *opts_short = "f:r:i:I:v:p:c:m:dh";
+#else
+static const char *opts_short = "f:r:i:I:v:h";
+#endif
 
 static struct option options[] = {
 	{"fit", required_argument, NULL, 'f'},
 	{"raw", required_argument, NULL, 'r'},
 	{"index", required_argument, NULL, 'i'},
 	{"instance", required_argument, NULL, 'I'},
+#ifdef CONFIG_TOOLS_LIBCRYPTO
+	{"private-key", required_argument, NULL, 'p'},
+	{"certificate", required_argument, NULL, 'c'},
+	{"monotonic-count", required_argument, NULL, 'm'},
+	{"dump-sig", no_argument, NULL, 'd'},
+#endif
 	{"help", no_argument, NULL, 'h'},
 	{NULL, 0, NULL, 0},
 };
@@ -57,16 +80,280 @@ static void print_usage(void)
 	       "\t-r, --raw <raw image>       new raw image file\n"
 	       "\t-i, --index <index>         update image index\n"
 	       "\t-I, --instance <instance>   update hardware instance\n"
+#ifdef CONFIG_TOOLS_LIBCRYPTO
+	       "\t-p, --private-key <privkey file>  private key file\n"
+	       "\t-c, --certificate <cert file>     signer's certificate file\n"
+	       "\t-m, --monotonic-count <count>     monotonic count\n"
+	       "\t-d, --dump_sig              dump signature (*.p7)\n"
+#endif
 	       "\t-h, --help                  print a help message\n",
 	       tool_name);
 }
 
+/**
+ * auth_context - authentication context
+ * @key_file:	Path to a private key file
+ * @cert_file:	Path to a certificate file
+ * @image_data:	Pointer to firmware data
+ * @image_size:	Size of firmware data
+ * @auth:	Authentication header
+ * @sig_data:	Signature data
+ * @sig_size:	Size of signature data
+ *
+ * Data structure used in create_auth_data(). @key_file through
+ * @image_size are input parameters. @auth, @sig_data and @sig_size
+ * are filled in by create_auth_data().
+ */
+struct auth_context {
+	char *key_file;
+	char *cert_file;
+	u8 *image_data;
+	size_t image_size;
+	struct efi_firmware_image_authentication auth;
+	u8 *sig_data;
+	size_t sig_size;
+};
+
+static int dump_sig;
+
+#ifdef CONFIG_TOOLS_LIBCRYPTO
+/**
+ * fileio-read_pkey - read out a private key
+ * @filename:	Path to a private key file
+ *
+ * Read out a private key file and parse it into "EVP_PKEY" structure.
+ *
+ * Return:
+ * * Pointer to private key structure  - on success
+ * * NULL - on failure
+ */
+static EVP_PKEY *fileio_read_pkey(const char *filename)
+{
+	EVP_PKEY *key = NULL;
+	BIO *bio;
+
+	bio = BIO_new_file(filename, "r");
+	if (!bio)
+		goto out;
+
+	key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
+
+out:
+	BIO_free_all(bio);
+	if (!key) {
+		printf("Can't load key from file '%s'\n", filename);
+		ERR_print_errors_fp(stderr);
+	}
+
+	return key;
+}
+
+/**
+ * fileio-read_cert - read out a certificate
+ * @filename:	Path to a certificate file
+ *
+ * Read out a certificate file and parse it into "X509" structure.
+ *
+ * Return:
+ * * Pointer to certificate structure  - on success
+ * * NULL - on failure
+ */
+static X509 *fileio_read_cert(const char *filename)
+{
+	X509 *cert = NULL;
+	BIO *bio;
+
+	bio = BIO_new_file(filename, "r");
+	if (!bio)
+		goto out;
+
+	cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+
+out:
+	BIO_free_all(bio);
+	if (!cert) {
+		printf("Can't load certificate from file '%s'\n", filename);
+		ERR_print_errors_fp(stderr);
+	}
+
+	return cert;
+}
+
+/**
+ * create_auth_data - compose authentication data in capsule
+ * @auth_context:	Pointer to authentication context
+ *
+ * Fill up an authentication header (.auth) and signature data (.sig_data)
+ * in @auth_context, using library functions from openssl.
+ * All the parameters in @auth_context must be filled in by a caller.
+ *
+ * Return:
+ * * 0  - on success
+ * * -1 - on failure
+ */
+static int create_auth_data(struct auth_context *ctx)
+{
+	EVP_PKEY *key = NULL;
+	X509 *cert = NULL;
+	BIO *data_bio = NULL;
+	const EVP_MD *md;
+	PKCS7 *p7;
+	int flags, ret = -1;
+
+	OpenSSL_add_all_digests();
+	OpenSSL_add_all_ciphers();
+	ERR_load_crypto_strings();
+
+	key = fileio_read_pkey(ctx->key_file);
+	if (!key)
+		goto err;
+	cert = fileio_read_cert(ctx->cert_file);
+	if (!cert)
+		goto err;
+
+	/*
+	 * create a BIO, containing:
+	 *  * firmware image
+	 *  * monotonic count
+	 * in this order!
+	 * See EDK2's FmpAuthenticatedHandlerRsa2048Sha256()
+	 */
+	data_bio = BIO_new(BIO_s_mem());
+	BIO_write(data_bio, ctx->image_data, ctx->image_size);
+	BIO_write(data_bio, &ctx->auth.monotonic_count,
+		  sizeof(ctx->auth.monotonic_count));
+
+	md = EVP_get_digestbyname("SHA256");
+	if (!md)
+		goto err;
+
+	/* create signature */
+	/* TODO: maybe add PKCS7_NOATTR and PKCS7_NOSMIMECAP */
+	flags = PKCS7_BINARY | PKCS7_DETACHED;
+	p7 = PKCS7_sign(NULL, NULL, NULL, data_bio, flags | PKCS7_PARTIAL);
+	if (!p7)
+		goto err;
+	if (!PKCS7_sign_add_signer(p7, cert, key, md, flags))
+		goto err;
+	if (!PKCS7_final(p7, data_bio, flags))
+		goto err;
+
+	/* convert pkcs7 into DER */
+	ctx->sig_data = NULL;
+	ctx->sig_size = ASN1_item_i2d((ASN1_VALUE *)p7, &ctx->sig_data,
+				      ASN1_ITEM_rptr(PKCS7));
+	if (!ctx->sig_size)
+		goto err;
+
+	/* fill auth_info */
+	ctx->auth.auth_info.hdr.dwLength = sizeof(ctx->auth.auth_info)
+						+ ctx->sig_size;
+	ctx->auth.auth_info.hdr.wRevision = WIN_CERT_REVISION_2_0;
+	ctx->auth.auth_info.hdr.wCertificateType = WIN_CERT_TYPE_EFI_GUID;
+	memcpy(&ctx->auth.auth_info.cert_type, &efi_guid_cert_type_pkcs7,
+	       sizeof(efi_guid_cert_type_pkcs7));
+
+	ret = 0;
+err:
+	BIO_free_all(data_bio);
+	EVP_PKEY_free(key);
+	X509_free(cert);
+
+	return ret;
+}
+
+/**
+ * dump_signature - dump out a signature
+ * @path:	Path to a capsule file
+ * @signature:	Signature data
+ * @sig_size:	Size of signature data
+ *
+ * Signature data pointed to by @signature will be saved into
+ * a file whose file name is @path with ".p7" suffix.
+ *
+ * Return:
+ * * 0  - on success
+ * * -1 - on failure
+ */
+static int dump_signature(const char *path, u8 *signature, size_t sig_size)
+{
+	char *sig_path;
+	FILE *f;
+	size_t size;
+	int ret = -1;
+
+	sig_path = malloc(strlen(path) + 3 + 1);
+	if (!sig_path)
+		return ret;
+
+	sprintf(sig_path, "%s.p7", path);
+	f = fopen(sig_path, "w");
+	if (!f)
+		goto err;
+
+	size = fwrite(signature, 1, sig_size, f);
+	if (size == sig_size)
+		ret = 0;
+
+	fclose(f);
+err:
+	free(sig_path);
+	return ret;
+}
+
+/**
+ * free_sig_data - free out signature data
+ * @ctx:	Pointer to authentication context
+ *
+ * Free signature data allocated in create_auth_data().
+ */
+static void free_sig_data(struct auth_context *ctx)
+{
+	if (ctx->sig_size)
+		OPENSSL_free(ctx->sig_data);
+}
+#else
+static int create_auth_data(struct auth_context *ctx)
+{
+	return 0;
+}
+
+static int dump_signature(const char *path, u8 *signature, size_t sig_size)
+{
+	return 0;
+}
+
+static void free_sig_data(struct auth_context *ctx) {}
+#endif
+
+/**
+ * create_fwbin - create an uefi capsule file
+ * @path:	Path to a created capsule file
+ * @bin:	Path to a firmware binary to encapsulate
+ * @guid:	GUID of related FMP driver
+ * @index:	Index number in capsule
+ * @instance:	Instance number in capsule
+ * @mcount:	Monotonic count in authentication information
+ * @private_file:	Path to a private key file
+ * @cert_file:	Path to a certificate file
+ *
+ * This function actually does the job of creating an uefi capsule file.
+ * All the arguments must be supplied.
+ * If either @private_file ror @cert_file is NULL, the capsule file
+ * won't be signed.
+ *
+ * Return:
+ * * 0  - on success
+ * * -1 - on failure
+ */
 static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
-			unsigned long index, unsigned long instance)
+			unsigned long index, unsigned long instance,
+			uint64_t mcount, char *privkey_file, char *cert_file)
 {
 	struct efi_capsule_header header;
 	struct efi_firmware_management_capsule_header capsule;
 	struct efi_firmware_management_capsule_image_header image;
+	struct auth_context auth_context;
 	FILE *f, *g;
 	struct stat bin_stat;
 	u8 *data;
@@ -76,8 +363,9 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
 #ifdef DEBUG
 	printf("For output: %s\n", path);
 	printf("\tbin: %s\n\ttype: %pUl\n", bin, guid);
-	printf("\tindex: %ld\n\tinstance: %ld\n", index, instance);
+	printf("\tindex: %lu\n\tinstance: %lu\n", index, instance);
 #endif
+	auth_context.sig_size = 0;
 
 	g = fopen(bin, "r");
 	if (!g) {
@@ -93,11 +381,34 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
 		printf("cannot allocate memory: %zx\n", (size_t)bin_stat.st_size);
 		goto err_1;
 	}
-	f = fopen(path, "w");
-	if (!f) {
-		printf("cannot open %s\n", path);
+
+	size = fread(data, 1, bin_stat.st_size, g);
+	if (size < bin_stat.st_size) {
+		printf("read failed (%zx)\n", size);
 		goto err_2;
 	}
+
+	/* first, calculate signature to determine its size */
+	if (privkey_file && cert_file) {
+		auth_context.key_file = privkey_file;
+		auth_context.cert_file = cert_file;
+		auth_context.auth.monotonic_count = mcount;
+		auth_context.image_data = data;
+		auth_context.image_size = bin_stat.st_size;
+
+		if (create_auth_data(&auth_context)) {
+			printf("Signing firmware image failed\n");
+			goto err_3;
+		}
+
+		if (dump_sig &&
+		    dump_signature(path, auth_context.sig_data,
+				   auth_context.sig_size)) {
+			printf("Creating signature file failed\n");
+			goto err_3;
+		}
+	}
+
 	header.capsule_guid = efi_guid_fm_capsule;
 	header.header_size = sizeof(header);
 	/* TODO: The current implementation ignores flags */
@@ -106,11 +417,20 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
 					+ sizeof(capsule) + sizeof(u64)
 					+ sizeof(image)
 					+ bin_stat.st_size;
+	if (auth_context.sig_size)
+		header.capsule_image_size += sizeof(auth_context.auth)
+				+ auth_context.sig_size;
+
+	f = fopen(path, "w");
+	if (!f) {
+		printf("cannot open %s\n", path);
+		goto err_3;
+	}
 
 	size = fwrite(&header, 1, sizeof(header), f);
 	if (size < sizeof(header)) {
 		printf("write failed (%zx)\n", size);
-		goto err_3;
+		goto err_4;
 	}
 
 	capsule.version = 0x00000001;
@@ -119,13 +439,13 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
 	size = fwrite(&capsule, 1, sizeof(capsule), f);
 	if (size < (sizeof(capsule))) {
 		printf("write failed (%zx)\n", size);
-		goto err_3;
+		goto err_4;
 	}
 	offset = sizeof(capsule) + sizeof(u64);
 	size = fwrite(&offset, 1, sizeof(offset), f);
 	if (size < sizeof(offset)) {
 		printf("write failed (%zx)\n", size);
-		goto err_3;
+		goto err_4;
 	}
 
 	image.version = 0x00000003;
@@ -135,34 +455,53 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
 	image.reserved[1] = 0;
 	image.reserved[2] = 0;
 	image.update_image_size = bin_stat.st_size;
+	if (auth_context.sig_size)
+		image.update_image_size += sizeof(auth_context.auth)
+				+ auth_context.sig_size;
 	image.update_vendor_code_size = 0; /* none */
 	image.update_hardware_instance = instance;
 	image.image_capsule_support = 0;
+	if (auth_context.sig_size)
+		image.image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION;
 
 	size = fwrite(&image, 1, sizeof(image), f);
 	if (size < sizeof(image)) {
 		printf("write failed (%zx)\n", size);
-		goto err_3;
+		goto err_4;
 	}
-	size = fread(data, 1, bin_stat.st_size, g);
-	if (size < bin_stat.st_size) {
-		printf("read failed (%zx)\n", size);
-		goto err_3;
+
+	if (auth_context.sig_size) {
+		size = fwrite(&auth_context.auth, 1,
+			      sizeof(auth_context.auth), f);
+		if (size < sizeof(auth_context.auth)) {
+			printf("write failed (%zx)\n", size);
+			goto err_4;
+		}
+		size = fwrite(auth_context.sig_data, 1,
+			      auth_context.sig_size, f);
+		if (size < auth_context.sig_size) {
+			printf("write failed (%zx)\n", size);
+			goto err_4;
+		}
 	}
+
 	size = fwrite(data, 1, bin_stat.st_size, f);
 	if (size < bin_stat.st_size) {
 		printf("write failed (%zx)\n", size);
-		goto err_3;
+		goto err_4;
 	}
 
 	fclose(f);
 	fclose(g);
 	free(data);
+	free_sig_data(&auth_context);
 
 	return 0;
 
-err_3:
+err_4:
 	fclose(f);
+err_3:
+	free_sig_data(&auth_context);
 err_2:
 	free(data);
 err_1:
@@ -171,23 +510,37 @@ err_1:
 	return -1;
 }
 
-/*
- * Usage:
- *   $ mkeficapsule -f <firmware binary> <output file>
+/**
+ * main - main entry function of mkeficapsule
+ * @argc:	Number of arguments
+ * @argv:	Array of pointers to arguments
+ *
+ * Create an uefi capsule file, optionally signing it.
+ * Parse all the arguments and pass them on to create_fwbin().
+ *
+ * Return:
+ * * 0  - on success
+ * * -1 - on failure
  */
 int main(int argc, char **argv)
 {
 	char *file;
 	efi_guid_t *guid;
 	unsigned long index, instance;
+	uint64_t mcount;
+	char *privkey_file, *cert_file;
 	int c, idx;
 
 	file = NULL;
 	guid = NULL;
 	index = 0;
 	instance = 0;
+	mcount = 0;
+	privkey_file = NULL;
+	cert_file = NULL;
+	dump_sig = 0;
 	for (;;) {
-		c = getopt_long(argc, argv, "f:r:i:I:v:h", options, &idx);
+		c = getopt_long(argc, argv, opts_short, options, &idx);
 		if (c == -1)
 			break;
 
@@ -214,29 +567,47 @@ int main(int argc, char **argv)
 		case 'I':
 			instance = strtoul(optarg, NULL, 0);
 			break;
+#ifdef CONFIG_TOOLS_LIBCRYPTO
+		case 'p':
+			if (privkey_file) {
+				printf("Private Key already specified\n");
+				return -1;
+			}
+			privkey_file = optarg;
+			break;
+		case 'c':
+			if (cert_file) {
+				printf("Certificate file already specified\n");
+				return -1;
+			}
+			cert_file = optarg;
+			break;
+		case 'm':
+			mcount = strtoul(optarg, NULL, 0);
+			break;
+		case 'd':
+			dump_sig = 1;
+			break;
+#endif /* CONFIG_TOOLS_LIBCRYPTO */
 		case 'h':
 			print_usage();
 			return 0;
 		}
 	}
 
-	/* need an output file */
-	if (argc != optind + 1) {
-		print_usage();
-		exit(EXIT_FAILURE);
-	}
-
-	/* need a fit image file or raw image file */
-	if (!file) {
+	/* check necessary parameters */
+	if ((argc != optind + 1) || !file ||
+	    ((privkey_file && !cert_file) ||
+	     (!privkey_file && cert_file))) {
 		print_usage();
-		exit(EXIT_SUCCESS);
+		return -1;
 	}
 
-	if (create_fwbin(argv[optind], file, guid, index, instance)
-			< 0) {
+	if (create_fwbin(argv[optind], file, guid, index, instance,
+			 mcount, privkey_file, cert_file) < 0) {
 		printf("Creating firmware capsule failed\n");
-		exit(EXIT_FAILURE);
+		return -1;
 	}
 
-	exit(EXIT_SUCCESS);
+	return 0;
 }
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v4 06/11] tools: mkeficapsule: add man page
  2021-10-07  6:23 [PATCH v4 00/11] efi_loader: capsule: improve capsule authentication support AKASHI Takahiro
                   ` (4 preceding siblings ...)
  2021-10-07  6:23 ` [PATCH v4 05/11] tools: mkeficapsule: add firmwware image signing AKASHI Takahiro
@ 2021-10-07  6:23 ` AKASHI Takahiro
  2021-10-07  6:23 ` [PATCH v4 07/11] doc: update UEFI document for usage of mkeficapsule AKASHI Takahiro
                   ` (4 subsequent siblings)
  10 siblings, 0 replies; 40+ messages in thread
From: AKASHI Takahiro @ 2021-10-07  6:23 UTC (permalink / raw)
  To: xypron.glpk, agraf
  Cc: ilias.apalodimas, sughosh.ganu, masami.hiramatsu, u-boot,
	AKASHI Takahiro

Add a man page for mkeficapsule command.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 MAINTAINERS        |  1 +
 doc/mkeficapsule.1 | 95 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 96 insertions(+)
 create mode 100644 doc/mkeficapsule.1

diff --git a/MAINTAINERS b/MAINTAINERS
index 650e428b6cb4..1ef3aae41328 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -694,6 +694,7 @@ S:	Maintained
 T:	git https://source.denx.de/u-boot/custodians/u-boot-efi.git
 F:	doc/api/efi.rst
 F:	doc/develop/uefi/*
+F:	doc/mkeficapsule.1
 F:	doc/usage/bootefi.rst
 F:	drivers/rtc/emul_rtc.c
 F:	include/capitalization.h
diff --git a/doc/mkeficapsule.1 b/doc/mkeficapsule.1
new file mode 100644
index 000000000000..837e09ab451e
--- /dev/null
+++ b/doc/mkeficapsule.1
@@ -0,0 +1,95 @@
+.TH MAEFICAPSULE 1 "May 2021"
+
+.SH NAME
+mkeficapsule \- Generate EFI capsule file for U-Boot
+
+.SH SYNOPSIS
+.B mkeficapsule
+.RB [\fIoptions\fP] " \fIcapsule-file\fP"
+
+.SH "DESCRIPTION"
+The
+\fBmkeficapsule\fP
+command is used to create an EFI capsule file for use with the U-Boot
+EFI capsule update.
+A capsule file may contain various type of firmware blobs which
+are to be applied to the system and must be placed in the specific
+directory on the UEFI system partition. An update will be automatically
+executed at next reboot.
+
+Optionally, a capsule file can be signed with a given private key.
+In this case, the update will be authenticated by verifying the signature
+before applying.
+
+\fBmkeficapsule\fP supports two different format of image files:
+.TP
+.I raw image
+format is a single binary blob of any type of firmware.
+
+.TP
+.I FIT (Flattened Image Tree) image
+format
+is the same as used in the new \fIuImage\fP format and allows for
+multiple binary blobs in a single capsule file.
+This type of image file can be generated by \fBmkimage\fP.
+
+.SH "OPTIONS"
+One of \fB--fit\fP or \fB--raw\fP option must be specified.
+
+.TP
+.BI "-f, --fit \fIfit-image-file\fP"
+Specify a FIT image file
+
+.TP
+.BI "-r, --raw \fIraw-image-file\fP"
+Specify a raw image file
+
+.TP
+.BI "-i, --index \fIindex\fP"
+Specify an image index
+
+.TP
+.BI "-I, --instance \fIinstance\fP"
+Specify a hardware instance
+
+.TP
+.BI "-h, --help"
+Print a help message
+
+.TP 0
+.B With signing:
+
+\fB--private-key\fP, \fB--certificate\fP and \fB--monotonic-count\fP are
+all mandatory.
+
+.TP
+.BI "-p, --private-key \fIprivate-key-file\fP"
+Specify signer's private key file in PEM
+
+.TP
+.BI "-c, --certificate \fIcertificate-file\fP"
+Specify signer's certificate file in EFI certificate list format
+
+.TP
+.BI "-m, --monotonic-count \fIcount\fP"
+Specify a monotonic count which is set to be monotonically incremented
+at every firmware update.
+
+.TP
+.BI "-d, --dump_sig"
+Dump signature data into *.p7 file
+
+.PP
+.SH FILES
+.TP
+.BI "\fI/EFI/UpdateCapsule\fP"
+The directory in which all capsule files be placed
+
+.SH SEE ALSO
+.B mkimage
+
+.SH AUTHORS
+Written by AKASHI Takahiro <takahiro.akashi@linaro.org>
+
+.SH HOMEPAGE
+http://www.denx.de/wiki/U-Boot/WebHome
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v4 07/11] doc: update UEFI document for usage of mkeficapsule
  2021-10-07  6:23 [PATCH v4 00/11] efi_loader: capsule: improve capsule authentication support AKASHI Takahiro
                   ` (5 preceding siblings ...)
  2021-10-07  6:23 ` [PATCH v4 06/11] tools: mkeficapsule: add man page AKASHI Takahiro
@ 2021-10-07  6:23 ` AKASHI Takahiro
  2021-10-07  6:23 ` [PATCH v4 08/11] tools: mkeficapsule: allow for specifying GUID explicitly AKASHI Takahiro
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 40+ messages in thread
From: AKASHI Takahiro @ 2021-10-07  6:23 UTC (permalink / raw)
  To: xypron.glpk, agraf
  Cc: ilias.apalodimas, sughosh.ganu, masami.hiramatsu, u-boot,
	AKASHI Takahiro

Now we can use mkeficapsule command instead of EDK-II's script
to create a signed capsule file. So update the instruction for
capsule authentication.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 doc/develop/uefi/uefi.rst | 122 ++++++++++++++------------------------
 1 file changed, 46 insertions(+), 76 deletions(-)

diff --git a/doc/develop/uefi/uefi.rst b/doc/develop/uefi/uefi.rst
index f17138f5c765..6ae517e92c44 100644
--- a/doc/develop/uefi/uefi.rst
+++ b/doc/develop/uefi/uefi.rst
@@ -284,37 +284,52 @@ Support has been added for the UEFI capsule update feature which
 enables updating the U-Boot image using the UEFI firmware management
 protocol (FMP). The capsules are not passed to the firmware through
 the UpdateCapsule runtime service. Instead, capsule-on-disk
-functionality is used for fetching the capsule from the EFI System
-Partition (ESP) by placing the capsule file under the
-\EFI\UpdateCapsule directory.
-
-The directory \EFI\UpdateCapsule is checked for capsules only within the
-EFI system partition on the device specified in the active boot option
-determined by reference to BootNext variable or BootOrder variable processing.
-The active Boot Variable is the variable with highest priority BootNext or
-within BootOrder that refers to a device found to be present. Boot variables
-in BootOrder but referring to devices not present are ignored when determining
-active boot variable.
-Before starting a capsule update make sure your capsules are installed in the
-correct ESP partition or set BootNext.
+functionality is used for fetching capsules from the EFI System
+Partition (ESP) by placing capsule files under the directory::
+
+    \EFI\UpdateCapsule
+
+The directory is checked for capsules only within the
+EFI system partition on the device specified in the active boot option,
+which is determined by BootXXXX variable in BootNext, or if not, the highest
+priority one within BootOrder. Any BootXXXX variables referring to devices
+not present are ignored when determining the active boot option.
+
+Please note that capsules will be applied in the alphabetic order of
+capsule file names.
+
+Creating a capsule file
+***********************
+
+A capsule file can be created by using tools/mkeficapsule.
+To build this tool, enable::
+
+    CONFIG_TOOLS_MKEFICAPSULE=y
+    CONFIG_TOOLS_LIBCRYPTO=y
+
+Run the following command::
+
+    $ mkeficapsule \
+      --index 1 --instance 0 \
+      [--fit <FIT image> | --raw <raw image>] \
+      <capsule_file_name>
 
 Performing the update
 *********************
 
-Since U-boot doesn't currently support SetVariable at runtime there's a Kconfig
-option (CONFIG_EFI_IGNORE_OSINDICATIONS) to disable the OsIndications variable
-check. If that option is enabled just copy your capsule to \EFI\UpdateCapsule.
-
-If that option is disabled, you'll need to set the OsIndications variable with::
+Put capsule files under the directory mentioned above.
+Then, following the UEFI specification, you'll need to set
+the EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED
+bit in OsIndications variable with::
 
     => setenv -e -nv -bs -rt -v OsIndications =0x04
 
-Finally, the capsule update can be initiated either by rebooting the board,
-which is the preferred method, or by issuing the following command::
-
-    => efidebug capsule disk-update
+Since U-boot doesn't currently support SetVariable at runtime, its value
+won't be taken over across the reboot. If this is the case, you can skip
+this feature check with the Kconfig option (CONFIG_EFI_IGNORE_OSINDICATIONS)
+set.
 
-**The efidebug command is should only be used during debugging/development.**
+Finally, the capsule update can be initiated by rebooting the board.
 
 Enabling Capsule Authentication
 *******************************
@@ -338,7 +353,7 @@ The public and private keys used for the signing process are generated
 and used by the steps highlighted below::
 
     1. Install utility commands on your host
-       * OPENSSL
+       * openssl
        * efitools
 
     2. Create signing keys and certificate files on your host
@@ -347,59 +362,14 @@ and used by the steps highlighted below::
             -keyout CRT.key -out CRT.crt -nodes -days 365
         $ cert-to-efi-sig-list CRT.crt CRT.esl
 
-        $ openssl x509 -in CRT.crt -out CRT.cer -outform DER
-        $ openssl x509 -inform DER -in CRT.cer -outform PEM -out CRT.pub.pem
-
-        $ openssl pkcs12 -export -out CRT.pfx -inkey CRT.key -in CRT.crt
-        $ openssl pkcs12 -in CRT.pfx -nodes -out CRT.pem
-
-The capsule file can be generated by using the GenerateCapsule.py
-script in EDKII::
-
-    $ ./BaseTools/BinWrappers/PosixLike/GenerateCapsule -e -o \
-      <capsule_file_name> --monotonic-count <val> --fw-version \
-      <val> --lsv <val> --guid \
-      e2bb9c06-70e9-4b14-97a3-5a7913176e3f --verbose \
-      --update-image-index <val> --signer-private-cert \
-      /path/to/CRT.pem --trusted-public-cert \
-      /path/to/CRT.pub.pem --other-public-cert /path/to/CRT.pub.pem \
-      <u-boot.bin>
-
-Place the capsule generated in the above step on the EFI System
-Partition under the EFI/UpdateCapsule directory
-
-Testing on QEMU
-***************
-
-Currently, support has been added on the QEMU ARM64 virt platform for
-updating the U-Boot binary as a raw image when the platform is booted
-in non-secure mode, i.e. with CONFIG_TFABOOT disabled. For this
-configuration, the QEMU platform needs to be booted with
-'secure=off'. The U-Boot binary placed on the first bank of the NOR
-flash at offset 0x0. The U-Boot environment is placed on the second
-NOR flash bank at offset 0x4000000.
-
-The capsule update feature is enabled with the following configuration
-settings::
-
-    CONFIG_MTD=y
-    CONFIG_FLASH_CFI_MTD=y
-    CONFIG_CMD_MTDPARTS=y
-    CONFIG_CMD_DFU=y
-    CONFIG_DFU_MTD=y
-    CONFIG_PCI_INIT_R=y
-    CONFIG_EFI_CAPSULE_ON_DISK=y
-    CONFIG_EFI_CAPSULE_FIRMWARE_MANAGEMENT=y
-    CONFIG_EFI_CAPSULE_FIRMWARE=y
-    CONFIG_EFI_CAPSULE_FIRMWARE_RAW=y
-
-In addition, the following config needs to be disabled(QEMU ARM specific)::
-
-    CONFIG_TFABOOT
-
-The capsule file can be generated by using the tools/mkeficapsule::
+Run the following command to create and sign the capsule file::
 
-    $ mkeficapsule --raw <u-boot.bin> --index 1 <capsule_file_name>
+    $ mkeficapsule --monotonic-count 1 \
+      --private-key CRT.key \
+      --certificate CRT.crt \
+      --index 1 --instance 0 \
+      [--fit <FIT image> | --raw <raw image>] \
+      <capsule_file_name>
 
 Executing the boot manager
 ~~~~~~~~~~~~~~~~~~~~~~~~~~
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v4 08/11] tools: mkeficapsule: allow for specifying GUID explicitly
  2021-10-07  6:23 [PATCH v4 00/11] efi_loader: capsule: improve capsule authentication support AKASHI Takahiro
                   ` (6 preceding siblings ...)
  2021-10-07  6:23 ` [PATCH v4 07/11] doc: update UEFI document for usage of mkeficapsule AKASHI Takahiro
@ 2021-10-07  6:23 ` AKASHI Takahiro
  2021-10-07  6:23 ` [PATCH v4 09/11] test/py: efi_capsule: align with the syntax change of mkeficapsule AKASHI Takahiro
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 40+ messages in thread
From: AKASHI Takahiro @ 2021-10-07  6:23 UTC (permalink / raw)
  To: xypron.glpk, agraf
  Cc: ilias.apalodimas, sughosh.ganu, masami.hiramatsu, u-boot,
	AKASHI Takahiro

The existing options, "--fit" and "--raw," are only used to put a proper
GUID in a capsule header, where GUID identifies a particular FMP (Firmware
Management Protocol) driver which then would handle the firmware binary in
a capsule. In fact, mkeficapsule does the exact same job in creating
a capsule file whatever the firmware binary type is.

To prepare for the future extension, the command syntax will be a bit
modified to allow users to specify arbitrary GUID for their own FMP driver.
OLD:
   [--fit <image> | --raw <image>] <capsule file>
NEW:
   [--fit | --raw | --guid <guid-string>] <image> <capsule file>

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 doc/develop/uefi/uefi.rst |  4 +-
 doc/mkeficapsule.1        | 26 +++++++++----
 tools/mkeficapsule.c      | 78 ++++++++++++++++++++++++++++++---------
 3 files changed, 81 insertions(+), 27 deletions(-)

diff --git a/doc/develop/uefi/uefi.rst b/doc/develop/uefi/uefi.rst
index 6ae517e92c44..7319f52d27be 100644
--- a/doc/develop/uefi/uefi.rst
+++ b/doc/develop/uefi/uefi.rst
@@ -368,8 +368,8 @@ Run the following command to create and sign the capsule file::
       --private-key CRT.key \
       --certificate CRT.crt \
       --index 1 --instance 0 \
-      [--fit <FIT image> | --raw <raw image>] \
-      <capsule_file_name>
+      [--fit | --raw | --guid <guid-string] \
+      <image_blob> <capsule_file_name>
 
 Executing the boot manager
 ~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/doc/mkeficapsule.1 b/doc/mkeficapsule.1
index 837e09ab451e..312e8a8b3188 100644
--- a/doc/mkeficapsule.1
+++ b/doc/mkeficapsule.1
@@ -5,7 +5,7 @@ mkeficapsule \- Generate EFI capsule file for U-Boot
 
 .SH SYNOPSIS
 .B mkeficapsule
-.RB [\fIoptions\fP] " \fIcapsule-file\fP"
+.RB [\fIoptions\fP] " \fIimage-blob\fP \fIcapsule-file\fP"
 
 .SH "DESCRIPTION"
 The
@@ -21,7 +21,7 @@ Optionally, a capsule file can be signed with a given private key.
 In this case, the update will be authenticated by verifying the signature
 before applying.
 
-\fBmkeficapsule\fP supports two different format of image files:
+\fBmkeficapsule\fP takes any type of image files, including:
 .TP
 .I raw image
 format is a single binary blob of any type of firmware.
@@ -33,16 +33,28 @@ is the same as used in the new \fIuImage\fP format and allows for
 multiple binary blobs in a single capsule file.
 This type of image file can be generated by \fBmkimage\fP.
 
+.PP
+If you want to use other types than above two, you should explicitly
+specify a guid for the FMP driver.
+
 .SH "OPTIONS"
-One of \fB--fit\fP or \fB--raw\fP option must be specified.
+One of \fB--fit\fP, \fB--raw\fP or \fB--guid\fP option must be specified.
 
 .TP
-.BI "-f, --fit \fIfit-image-file\fP"
-Specify a FIT image file
+.BI "-f, --fit
+Indicate that the blob is a FIT image file
 
 .TP
-.BI "-r, --raw \fIraw-image-file\fP"
-Specify a raw image file
+.BI "-r, --raw
+Indicate that the blob is a raw image file
+
+.TP
+.BI "-g, --guid \fIguid-string\fP"
+Specify guid for image blob type. The format is:
+    xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+
+The first three elements are in little endian, while the rest
+is in big endian.
 
 .TP
 .BI "-i, --index \fIindex\fP"
diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
index 5541e4bda894..2e61ee196caf 100644
--- a/tools/mkeficapsule.c
+++ b/tools/mkeficapsule.c
@@ -14,7 +14,7 @@
 
 #include <sys/stat.h>
 #include <sys/types.h>
-
+#include <uuid/uuid.h>
 #include <linux/kconfig.h>
 #ifdef CONFIG_TOOLS_LIBCRYPTO
 #include <openssl/asn1.h>
@@ -51,14 +51,15 @@ efi_guid_t efi_guid_image_type_uboot_raw =
 efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
 
 #ifdef CONFIG_TOOLS_LIBCRYPTO
-static const char *opts_short = "f:r:i:I:v:p:c:m:dh";
+static const char *opts_short = "frg:i:I:v:p:c:m:dh";
 #else
-static const char *opts_short = "f:r:i:I:v:h";
+static const char *opts_short = "frg:i:I:v:h";
 #endif
 
 static struct option options[] = {
-	{"fit", required_argument, NULL, 'f'},
-	{"raw", required_argument, NULL, 'r'},
+	{"fit", no_argument, NULL, 'f'},
+	{"raw", no_argument, NULL, 'r'},
+	{"guid", required_argument, NULL, 'g'},
 	{"index", required_argument, NULL, 'i'},
 	{"instance", required_argument, NULL, 'I'},
 #ifdef CONFIG_TOOLS_LIBCRYPTO
@@ -73,11 +74,12 @@ static struct option options[] = {
 
 static void print_usage(void)
 {
-	printf("Usage: %s [options] <output file>\n"
+	printf("Usage: %s [options] <image blob> <output file>\n"
 	       "Options:\n"
 
-	       "\t-f, --fit <fit image>       new FIT image file\n"
-	       "\t-r, --raw <raw image>       new raw image file\n"
+	       "\t-f, --fit                   FIT image type\n"
+	       "\t-r, --raw                   raw image type\n"
+	       "\t-g, --guid <guid string>    guid for image blob type\n"
 	       "\t-i, --index <index>         update image index\n"
 	       "\t-I, --instance <instance>   update hardware instance\n"
 #ifdef CONFIG_TOOLS_LIBCRYPTO
@@ -510,6 +512,37 @@ err_1:
 	return -1;
 }
 
+/**
+ * convert_uuid_to_guid() - convert uuid string to guid string
+ * @buf:	String for UUID
+ *
+ * UUID and GUID have the same data structure, but their string
+ * formats are different due to the endianness. See lib/uuid.c.
+ * Since uuid_parse() can handle only UUID, this function must
+ * be called to get correct data for GUID when parsing a string.
+ *
+ * The correct data will be returned in @buf.
+ */
+void convert_uuid_to_guid(unsigned char *buf)
+{
+	unsigned char c;
+
+	c = buf[0];
+	buf[0] = buf[3];
+	buf[3] = c;
+	c = buf[1];
+	buf[1] = buf[2];
+	buf[2] = c;
+
+	c = buf[4];
+	buf[4] = buf[5];
+	buf[5] = c;
+
+	c = buf[6];
+	buf[6] = buf[7];
+	buf[7] = c;
+}
+
 /**
  * main - main entry function of mkeficapsule
  * @argc:	Number of arguments
@@ -524,14 +557,13 @@ err_1:
  */
 int main(int argc, char **argv)
 {
-	char *file;
 	efi_guid_t *guid;
+	unsigned char uuid_buf[16];
 	unsigned long index, instance;
 	uint64_t mcount;
 	char *privkey_file, *cert_file;
 	int c, idx;
 
-	file = NULL;
 	guid = NULL;
 	index = 0;
 	instance = 0;
@@ -546,21 +578,31 @@ int main(int argc, char **argv)
 
 		switch (c) {
 		case 'f':
-			if (file) {
-				printf("Image already specified\n");
+			if (guid) {
+				printf("Image type already specified\n");
 				return -1;
 			}
-			file = optarg;
 			guid = &efi_guid_image_type_uboot_fit;
 			break;
 		case 'r':
-			if (file) {
-				printf("Image already specified\n");
+			if (guid) {
+				printf("Image type already specified\n");
 				return -1;
 			}
-			file = optarg;
 			guid = &efi_guid_image_type_uboot_raw;
 			break;
+		case 'g':
+			if (guid) {
+				printf("Image type already specified\n");
+				return -1;
+			}
+			if (uuid_parse(optarg, uuid_buf)) {
+				printf("Wrong guid format\n");
+				return -1;
+			}
+			convert_uuid_to_guid(uuid_buf);
+			guid = (efi_guid_t *)uuid_buf;
+			break;
 		case 'i':
 			index = strtoul(optarg, NULL, 0);
 			break;
@@ -596,14 +638,14 @@ int main(int argc, char **argv)
 	}
 
 	/* check necessary parameters */
-	if ((argc != optind + 1) || !file ||
+	if ((argc != optind + 2) || !guid ||
 	    ((privkey_file && !cert_file) ||
 	     (!privkey_file && cert_file))) {
 		print_usage();
 		return -1;
 	}
 
-	if (create_fwbin(argv[optind], file, guid, index, instance,
+	if (create_fwbin(argv[argc - 1], argv[argc - 2], guid, index, instance,
 			 mcount, privkey_file, cert_file) < 0) {
 		printf("Creating firmware capsule failed\n");
 		return -1;
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v4 09/11] test/py: efi_capsule: align with the syntax change of mkeficapsule
  2021-10-07  6:23 [PATCH v4 00/11] efi_loader: capsule: improve capsule authentication support AKASHI Takahiro
                   ` (7 preceding siblings ...)
  2021-10-07  6:23 ` [PATCH v4 08/11] tools: mkeficapsule: allow for specifying GUID explicitly AKASHI Takahiro
@ 2021-10-07  6:23 ` AKASHI Takahiro
  2021-10-07  6:23 ` [PATCH v4 10/11] test/py: efi_capsule: add a test for "--guid" option AKASHI Takahiro
  2021-10-07  6:23 ` [PATCH v4 11/11] test/py: efi_capsule: add image authentication test AKASHI Takahiro
  10 siblings, 0 replies; 40+ messages in thread
From: AKASHI Takahiro @ 2021-10-07  6:23 UTC (permalink / raw)
  To: xypron.glpk, agraf
  Cc: ilias.apalodimas, sughosh.ganu, masami.hiramatsu, u-boot,
	AKASHI Takahiro

Since the syntax of mkeficapsule was changed in the previous commit,
we need to modify command line arguments in a pytest script and added
one more test case, Case 4, for verifying the new "--guid" option.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 test/py/tests/test_efi_capsule/conftest.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/test/py/tests/test_efi_capsule/conftest.py b/test/py/tests/test_efi_capsule/conftest.py
index 6ad5608cd71c..8b5368c11abc 100644
--- a/test/py/tests/test_efi_capsule/conftest.py
+++ b/test/py/tests/test_efi_capsule/conftest.py
@@ -50,10 +50,10 @@ def efi_capsule_data(request, u_boot_config):
         check_call('cd %s; %s/tools/mkimage -f uboot_bin_env.its uboot_bin_env.itb' %
                    (data_dir, u_boot_config.build_dir),
                    shell=True)
-        check_call('cd %s; %s/tools/mkeficapsule --fit uboot_bin_env.itb --index 1 Test01' %
+        check_call('cd %s; %s/tools/mkeficapsule --index 1 --fit uboot_bin_env.itb Test01' %
                    (data_dir, u_boot_config.build_dir),
                    shell=True)
-        check_call('cd %s; %s/tools/mkeficapsule --raw u-boot.bin.new --index 1 Test02' %
+        check_call('cd %s; %s/tools/mkeficapsule --index 1 --raw u-boot.bin.new Test02' %
                    (data_dir, u_boot_config.build_dir),
                    shell=True)
 
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v4 10/11] test/py: efi_capsule: add a test for "--guid" option
  2021-10-07  6:23 [PATCH v4 00/11] efi_loader: capsule: improve capsule authentication support AKASHI Takahiro
                   ` (8 preceding siblings ...)
  2021-10-07  6:23 ` [PATCH v4 09/11] test/py: efi_capsule: align with the syntax change of mkeficapsule AKASHI Takahiro
@ 2021-10-07  6:23 ` AKASHI Takahiro
  2021-10-07  6:23 ` [PATCH v4 11/11] test/py: efi_capsule: add image authentication test AKASHI Takahiro
  10 siblings, 0 replies; 40+ messages in thread
From: AKASHI Takahiro @ 2021-10-07  6:23 UTC (permalink / raw)
  To: xypron.glpk, agraf
  Cc: ilias.apalodimas, sughosh.ganu, masami.hiramatsu, u-boot,
	AKASHI Takahiro

This test scenario tests a new feature of mkeficapsule, "--guid" option,
which allows us to specify FMP driver's guid explicitly at the command
line.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 test/py/tests/test_efi_capsule/conftest.py    |  3 +
 .../test_efi_capsule/test_capsule_firmware.py | 67 +++++++++++++++++++
 2 files changed, 70 insertions(+)

diff --git a/test/py/tests/test_efi_capsule/conftest.py b/test/py/tests/test_efi_capsule/conftest.py
index 8b5368c11abc..cd750347879e 100644
--- a/test/py/tests/test_efi_capsule/conftest.py
+++ b/test/py/tests/test_efi_capsule/conftest.py
@@ -56,6 +56,9 @@ def efi_capsule_data(request, u_boot_config):
         check_call('cd %s; %s/tools/mkeficapsule --index 1 --raw u-boot.bin.new Test02' %
                    (data_dir, u_boot_config.build_dir),
                    shell=True)
+        check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid E2BB9C06-70E9-4B14-97A3-5A7913176E3F u-boot.bin.new Test03' %
+                   (data_dir, u_boot_config.build_dir),
+                   shell=True)
 
         # Create a disk image with EFI system partition
         check_call('virt-make-fs --partition=gpt --size=+1M --type=vfat %s %s' %
diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware.py b/test/py/tests/test_efi_capsule/test_capsule_firmware.py
index 9eeaae27d626..9cc973560fa1 100644
--- a/test/py/tests/test_efi_capsule/test_capsule_firmware.py
+++ b/test/py/tests/test_efi_capsule/test_capsule_firmware.py
@@ -247,3 +247,70 @@ class TestEfiCapsuleFirmwareFit(object):
                 'sf read 4000000 100000 10',
                 'md.b 4000000 10'])
             assert 'u-boot:New' in ''.join(output)
+
+    def test_efi_capsule_fw4(
+            self, u_boot_config, u_boot_console, efi_capsule_data):
+        """
+        Test Case 4 - Test "--guid" option of mkeficapsule
+                      The test scenario is the same as Case 3.
+        """
+        disk_img = efi_capsule_data
+        with u_boot_console.log.section('Test Case 4-a, before reboot'):
+            output = u_boot_console.run_command_list([
+                'host bind 0 %s' % disk_img,
+                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi -s ""',
+                'efidebug boot order 1',
+                'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
+                'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
+                'env save'])
+
+            # initialize content
+            output = u_boot_console.run_command_list([
+                'sf probe 0:0',
+                'fatload host 0:1 4000000 %s/u-boot.bin.old' % CAPSULE_DATA_DIR,
+                'sf write 4000000 100000 10',
+                'sf read 5000000 100000 10',
+                'md.b 5000000 10'])
+            assert 'Old' in ''.join(output)
+
+            # place a capsule file
+            output = u_boot_console.run_command_list([
+                'fatload host 0:1 4000000 %s/Test03' % CAPSULE_DATA_DIR,
+                'fatwrite host 0:1 4000000 %s/Test03 $filesize' % CAPSULE_INSTALL_DIR,
+                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+            assert 'Test03' in ''.join(output)
+
+        # reboot
+        u_boot_console.restart_uboot()
+
+        capsule_early = u_boot_config.buildconfig.get(
+            'config_efi_capsule_on_disk_early')
+        with u_boot_console.log.section('Test Case 4-b, after reboot'):
+            if not capsule_early:
+                # make sure that dfu_alt_info exists even persistent variables
+                # are not available.
+                output = u_boot_console.run_command_list([
+                    'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
+                    'host bind 0 %s' % disk_img,
+                    'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+                assert 'Test03' in ''.join(output)
+
+                # need to run uefi command to initiate capsule handling
+                output = u_boot_console.run_command(
+                    'env print -e Capsule0000')
+
+            output = u_boot_console.run_command_list(['efidebug capsule esrt'])
+
+            # ensure that  EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID is in the ESRT.
+            assert 'E2BB9C06-70E9-4B14-97A3-5A7913176E3F' in ''.join(output)
+
+            output = u_boot_console.run_command_list([
+                'host bind 0 %s' % disk_img,
+                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+            assert 'Test03' not in ''.join(output)
+
+            output = u_boot_console.run_command_list([
+                'sf probe 0:0',
+                'sf read 4000000 100000 10',
+                'md.b 4000000 10'])
+            assert 'u-boot:New' in ''.join(output)
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v4 11/11] test/py: efi_capsule: add image authentication test
  2021-10-07  6:23 [PATCH v4 00/11] efi_loader: capsule: improve capsule authentication support AKASHI Takahiro
                   ` (9 preceding siblings ...)
  2021-10-07  6:23 ` [PATCH v4 10/11] test/py: efi_capsule: add a test for "--guid" option AKASHI Takahiro
@ 2021-10-07  6:23 ` AKASHI Takahiro
  10 siblings, 0 replies; 40+ messages in thread
From: AKASHI Takahiro @ 2021-10-07  6:23 UTC (permalink / raw)
  To: xypron.glpk, agraf
  Cc: ilias.apalodimas, sughosh.ganu, masami.hiramatsu, u-boot,
	AKASHI Takahiro

Add a couple of test cases against capsule image authentication
for capsule-on-disk, where only a signed capsule file with the verified
signature will be applied to the system.

Due to the difficulty of embedding a public key (esl file) in U-Boot
binary during pytest setup time, all the keys/certificates are pre-created.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 .../py/tests/test_efi_capsule/capsule_defs.py |   5 +
 test/py/tests/test_efi_capsule/conftest.py    |  35 ++-
 test/py/tests/test_efi_capsule/signature.dts  |  10 +
 .../test_capsule_firmware_signed.py           | 233 ++++++++++++++++++
 4 files changed, 280 insertions(+), 3 deletions(-)
 create mode 100644 test/py/tests/test_efi_capsule/signature.dts
 create mode 100644 test/py/tests/test_efi_capsule/test_capsule_firmware_signed.py

diff --git a/test/py/tests/test_efi_capsule/capsule_defs.py b/test/py/tests/test_efi_capsule/capsule_defs.py
index 4fd6353c2040..aa9bf5eee3aa 100644
--- a/test/py/tests/test_efi_capsule/capsule_defs.py
+++ b/test/py/tests/test_efi_capsule/capsule_defs.py
@@ -3,3 +3,8 @@
 # Directories
 CAPSULE_DATA_DIR = '/EFI/CapsuleTestData'
 CAPSULE_INSTALL_DIR = '/EFI/UpdateCapsule'
+
+# v1.5.1 or earlier of efitools has a bug in sha256 calculation, and
+# you need build a newer version on your own.
+# The path must terminate with '/'.
+EFITOOLS_PATH = ''
diff --git a/test/py/tests/test_efi_capsule/conftest.py b/test/py/tests/test_efi_capsule/conftest.py
index cd750347879e..ab4787c4d26c 100644
--- a/test/py/tests/test_efi_capsule/conftest.py
+++ b/test/py/tests/test_efi_capsule/conftest.py
@@ -10,13 +10,13 @@ import pytest
 from capsule_defs import *
 
 #
-# Fixture for UEFI secure boot test
+# Fixture for UEFI capsule test
 #
 
-
 @pytest.fixture(scope='session')
 def efi_capsule_data(request, u_boot_config):
-    """Set up a file system to be used in UEFI capsule test.
+    """Set up a file system to be used in UEFI capsule and
+       authentication test.
 
     Args:
         request: Pytest request object.
@@ -40,6 +40,26 @@ def efi_capsule_data(request, u_boot_config):
         check_call('mkdir -p %s' % data_dir, shell=True)
         check_call('mkdir -p %s' % install_dir, shell=True)
 
+        capsule_auth_enabled = u_boot_config.buildconfig.get(
+                    'config_efi_capsule_authenticate')
+        if capsule_auth_enabled:
+            # Create private key (SIGNER.key) and certificate (SIGNER.crt)
+            check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_SIGNER/ -keyout SIGNER.key -out SIGNER.crt -nodes -days 365'
+                       % data_dir, shell=True)
+            check_call('cd %s; %scert-to-efi-sig-list SIGNER.crt SIGNER.esl'
+                       % (data_dir, EFITOOLS_PATH), shell=True)
+
+            # Update dtb adding capsule certificate
+            check_call('cd %s; cp %s/test/py/tests/test_efi_capsule/signature.dts .'
+                       % (data_dir, u_boot_config.source_dir), shell=True)
+            check_call('cd %s; dtc -@ -I dts -O dtb -o signature.dtbo signature.dts; fdtoverlay -i %s/arch/sandbox/dts/test.dtb -o test_sig.dtb signature.dtbo'
+                       % (data_dir, u_boot_config.build_dir), shell=True)
+
+            # Create *malicious* private key (SIGNER2.key) and certificate
+            # (SIGNER2.crt)
+            check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_SIGNER/ -keyout SIGNER2.key -out SIGNER2.crt -nodes -days 365'
+                       % data_dir, shell=True)
+
         # Create capsule files
         # two regions: one for u-boot.bin and the other for u-boot.env
         check_call('cd %s; echo -n u-boot:Old > u-boot.bin.old; echo -n u-boot:New > u-boot.bin.new; echo -n u-boot-env:Old -> u-boot.env.old; echo -n u-boot-env:New > u-boot.env.new' % data_dir,
@@ -59,6 +79,15 @@ def efi_capsule_data(request, u_boot_config):
         check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid E2BB9C06-70E9-4B14-97A3-5A7913176E3F u-boot.bin.new Test03' %
                    (data_dir, u_boot_config.build_dir),
                    shell=True)
+        if capsule_auth_enabled:
+            # firmware signed with proper key
+            check_call('cd %s; %s/tools/mkeficapsule --index 1 --monotonic-count 1 --private-key SIGNER.key --certificate SIGNER.crt --raw u-boot.bin.new Test04' %
+                       (data_dir, u_boot_config.build_dir),
+                       shell=True)
+            # firmware signed with *mal* key
+            check_call('cd %s; %s/tools/mkeficapsule --index 1 --monotonic-count 1 --private-key SIGNER2.key --certificate SIGNER2.crt --raw u-boot.bin.new Test05' %
+                       (data_dir, u_boot_config.build_dir),
+                       shell=True)
 
         # Create a disk image with EFI system partition
         check_call('virt-make-fs --partition=gpt --size=+1M --type=vfat %s %s' %
diff --git a/test/py/tests/test_efi_capsule/signature.dts b/test/py/tests/test_efi_capsule/signature.dts
new file mode 100644
index 000000000000..078cfc76c93c
--- /dev/null
+++ b/test/py/tests/test_efi_capsule/signature.dts
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+/plugin/;
+
+&{/} {
+	signature {
+		capsule-key = /incbin/("SIGNER.esl");
+	};
+};
diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware_signed.py b/test/py/tests/test_efi_capsule/test_capsule_firmware_signed.py
new file mode 100644
index 000000000000..9230d14a1871
--- /dev/null
+++ b/test/py/tests/test_efi_capsule/test_capsule_firmware_signed.py
@@ -0,0 +1,233 @@
+# SPDX-License-Identifier:      GPL-2.0+
+# Copyright (c) 2021, Linaro Limited
+# Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
+#
+# U-Boot UEFI: Firmware Update (Signed capsule) Test
+
+"""
+This test verifies capsule-on-disk firmware update
+with signed capsule files
+"""
+
+import pytest
+from capsule_defs import CAPSULE_DATA_DIR, CAPSULE_INSTALL_DIR
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('efi_capsule_firmware_raw')
+@pytest.mark.buildconfigspec('efi_capsule_authenticate')
+@pytest.mark.buildconfigspec('dfu')
+@pytest.mark.buildconfigspec('dfu_sf')
+@pytest.mark.buildconfigspec('cmd_efidebug')
+@pytest.mark.buildconfigspec('cmd_fat')
+@pytest.mark.buildconfigspec('cmd_memory')
+@pytest.mark.buildconfigspec('cmd_nvedit_efi')
+@pytest.mark.buildconfigspec('cmd_sf')
+@pytest.mark.slow
+class TestEfiCapsuleFirmwareSigned(object):
+    def test_efi_capsule_auth1(
+            self, u_boot_config, u_boot_console, efi_capsule_data):
+        """
+        Test Case 1 - Update U-Boot on SPI Flash, raw image format
+                      0x100000-0x150000: U-Boot binary (but dummy)
+
+                      If the capsule is properly signed, the authentication
+                      should pass and the firmware be updated.
+        """
+        disk_img = efi_capsule_data
+        with u_boot_console.log.section('Test Case 1-a, before reboot'):
+            output = u_boot_console.run_command_list([
+                'host bind 0 %s' % disk_img,
+                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi',
+                'efidebug boot order 1',
+                'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
+                'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
+                'env save'])
+
+            # initialize content
+            output = u_boot_console.run_command_list([
+                'sf probe 0:0',
+                'fatload host 0:1 4000000 %s/u-boot.bin.old' % CAPSULE_DATA_DIR,
+                'sf write 4000000 100000 10',
+                'sf read 5000000 100000 10',
+                'md.b 5000000 10'])
+            assert 'Old' in ''.join(output)
+
+            # place a capsule file
+            output = u_boot_console.run_command_list([
+                'fatload host 0:1 4000000 %s/Test04' % CAPSULE_DATA_DIR,
+                'fatwrite host 0:1 4000000 %s/Test04 $filesize' % CAPSULE_INSTALL_DIR,
+                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+            assert 'Test04' in ''.join(output)
+
+        # reboot
+        mnt_point = u_boot_config.persistent_data_dir + '/test_efi_capsule'
+        u_boot_console.config.dtb = mnt_point + CAPSULE_DATA_DIR + '/test_sig.dtb'
+        u_boot_console.restart_uboot()
+
+        capsule_early = u_boot_config.buildconfig.get(
+            'config_efi_capsule_on_disk_early')
+        with u_boot_console.log.section('Test Case 1-b, after reboot'):
+            if not capsule_early:
+                # make sure that dfu_alt_info exists even persistent variables
+                # are not available.
+                output = u_boot_console.run_command_list([
+                    'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
+                    'host bind 0 %s' % disk_img,
+                    'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+                assert 'Test04' in ''.join(output)
+
+                # need to run uefi command to initiate capsule handling
+                output = u_boot_console.run_command(
+                    'env print -e Capsule0000')
+
+            output = u_boot_console.run_command_list([
+                'host bind 0 %s' % disk_img,
+                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+            assert 'Test04' not in ''.join(output)
+
+            output = u_boot_console.run_command_list([
+                'sf probe 0:0',
+                'sf read 4000000 100000 10',
+                'md.b 4000000 10'])
+            assert 'u-boot:New' in ''.join(output)
+
+    def test_efi_capsule_auth2(
+            self, u_boot_config, u_boot_console, efi_capsule_data):
+        """
+        Test Case 2 - Update U-Boot on SPI Flash, raw image format
+                      0x100000-0x150000: U-Boot binary (but dummy)
+
+                      If the capsule is signed but with an invalid key,
+                      the authentication should fail and the firmware
+                      not be updated.
+        """
+        disk_img = efi_capsule_data
+        with u_boot_console.log.section('Test Case 2-a, before reboot'):
+            output = u_boot_console.run_command_list([
+                'host bind 0 %s' % disk_img,
+                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi',
+                'efidebug boot order 1',
+                'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
+                'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
+                'env save'])
+
+            # initialize content
+            output = u_boot_console.run_command_list([
+                'sf probe 0:0',
+                'fatload host 0:1 4000000 %s/u-boot.bin.old' % CAPSULE_DATA_DIR,
+                'sf write 4000000 100000 10',
+                'sf read 5000000 100000 10',
+                'md.b 5000000 10'])
+            assert 'Old' in ''.join(output)
+
+            # place a capsule file
+            output = u_boot_console.run_command_list([
+                'fatload host 0:1 4000000 %s/Test05' % CAPSULE_DATA_DIR,
+                'fatwrite host 0:1 4000000 %s/Test05 $filesize' % CAPSULE_INSTALL_DIR,
+                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+            assert 'Test05' in ''.join(output)
+
+        # reboot
+        mnt_point = u_boot_config.persistent_data_dir + '/test_efi_capsule'
+        u_boot_console.config.dtb = mnt_point + CAPSULE_DATA_DIR + '/test_sig.dtb'
+        u_boot_console.restart_uboot()
+
+        capsule_early = u_boot_config.buildconfig.get(
+            'config_efi_capsule_on_disk_early')
+        with u_boot_console.log.section('Test Case 2-b, after reboot'):
+            if not capsule_early:
+                # make sure that dfu_alt_info exists even persistent variables
+                # are not available.
+                output = u_boot_console.run_command_list([
+                    'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
+                    'host bind 0 %s' % disk_img,
+                    'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+                assert 'Test05' in ''.join(output)
+
+                # need to run uefi command to initiate capsule handling
+                output = u_boot_console.run_command(
+                    'env print -e Capsule0000')
+
+            # deleted any way
+            output = u_boot_console.run_command_list([
+                'host bind 0 %s' % disk_img,
+                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+            assert 'Test05' not in ''.join(output)
+
+            # TODO: check CapsuleStatus in CapsuleXXXX
+
+            output = u_boot_console.run_command_list([
+                'sf probe 0:0',
+                'sf read 4000000 100000 10',
+                'md.b 4000000 10'])
+            assert 'u-boot:Old' in ''.join(output)
+
+    def test_efi_capsule_auth3(
+            self, u_boot_config, u_boot_console, efi_capsule_data):
+        """
+        Test Case 3 - Update U-Boot on SPI Flash, raw image format
+                      0x100000-0x150000: U-Boot binary (but dummy)
+
+                      If the capsule is not signed, the authentication
+                      should fail and the firmware not be updated.
+        """
+        disk_img = efi_capsule_data
+        with u_boot_console.log.section('Test Case 3-a, before reboot'):
+            output = u_boot_console.run_command_list([
+                'host bind 0 %s' % disk_img,
+                'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi',
+                'efidebug boot order 1',
+                'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
+                'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
+                'env save'])
+
+            # initialize content
+            output = u_boot_console.run_command_list([
+                'sf probe 0:0',
+                'fatload host 0:1 4000000 %s/u-boot.bin.old' % CAPSULE_DATA_DIR,
+                'sf write 4000000 100000 10',
+                'sf read 5000000 100000 10',
+                'md.b 5000000 10'])
+            assert 'Old' in ''.join(output)
+
+            # place a capsule file
+            output = u_boot_console.run_command_list([
+                'fatload host 0:1 4000000 %s/Test02' % CAPSULE_DATA_DIR,
+                'fatwrite host 0:1 4000000 %s/Test02 $filesize' % CAPSULE_INSTALL_DIR,
+                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+            assert 'Test02' in ''.join(output)
+
+        # reboot
+        mnt_point = u_boot_config.persistent_data_dir + '/test_efi_capsule'
+        u_boot_console.config.dtb = mnt_point + CAPSULE_DATA_DIR + '/test_sig.dtb'
+        u_boot_console.restart_uboot()
+
+        capsule_early = u_boot_config.buildconfig.get(
+            'config_efi_capsule_on_disk_early')
+        with u_boot_console.log.section('Test Case 3-b, after reboot'):
+            if not capsule_early:
+                # make sure that dfu_alt_info exists even persistent variables
+                # are not available.
+                output = u_boot_console.run_command_list([
+                    'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
+                    'host bind 0 %s' % disk_img,
+                    'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+                assert 'Test02' in ''.join(output)
+
+                # need to run uefi command to initiate capsule handling
+                output = u_boot_console.run_command(
+                    'env print -e Capsule0000')
+
+            # deleted any way
+            output = u_boot_console.run_command_list([
+                'host bind 0 %s' % disk_img,
+                'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+            assert 'Test02' not in ''.join(output)
+
+            # TODO: check CapsuleStatus in CapsuleXXXX
+
+            output = u_boot_console.run_command_list([
+                'sf probe 0:0',
+                'sf read 4000000 100000 10',
+                'md.b 4000000 10'])
+            assert 'u-boot:Old' in ''.join(output)
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* Re: [PATCH v4 01/11] Revert "Revert "mkeficapsule: Remove dtb related options""
  2021-10-07  6:23 ` [PATCH v4 01/11] Revert "Revert "mkeficapsule: Remove dtb related options"" AKASHI Takahiro
@ 2021-10-08 15:38   ` Simon Glass
  2021-10-08 19:11     ` Ilias Apalodimas
  0 siblings, 1 reply; 40+ messages in thread
From: Simon Glass @ 2021-10-08 15:38 UTC (permalink / raw)
  To: AKASHI Takahiro
  Cc: Heinrich Schuchardt, Alex Graf, Ilias Apalodimas, Sughosh Ganu,
	Masami Hiramatsu, U-Boot Mailing List

Hi Takahiro,

On Thu, 7 Oct 2021 at 00:24, AKASHI Takahiro <takahiro.akashi@linaro.org> wrote:
>
> This reverts commit d428e81266a59974ade74c1ba019af39f23304ab.
> We have agreed with removing dtb-related stuff from mkeficapsule
> command even if the commit 47a25e81d35c ("Revert "efi_capsule: Move
> signature from DTB to .rodata"") was applied.

Can you please explain why this is being removed? How is the public
key to be communicated?

Please can you also copy me on future related patches? (thank for you
for the private email alerting me)

What does "We have agreed" mean?

>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>  tools/mkeficapsule.c | 229 ++-----------------------------------------
>  1 file changed, 7 insertions(+), 222 deletions(-)
>

Regards,
Simon

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v4 01/11] Revert "Revert "mkeficapsule: Remove dtb related options""
  2021-10-08 15:38   ` Simon Glass
@ 2021-10-08 19:11     ` Ilias Apalodimas
  2021-10-11  0:29       ` AKASHI Takahiro
  0 siblings, 1 reply; 40+ messages in thread
From: Ilias Apalodimas @ 2021-10-08 19:11 UTC (permalink / raw)
  To: Simon Glass
  Cc: AKASHI Takahiro, Heinrich Schuchardt, Alex Graf, Sughosh Ganu,
	Masami Hiramatsu, U-Boot Mailing List

Hi Simon, 

On Fri, Oct 08, 2021 at 09:38:11AM -0600, Simon Glass wrote:
> Hi Takahiro,
> 
> On Thu, 7 Oct 2021 at 00:24, AKASHI Takahiro <takahiro.akashi@linaro.org> wrote:
> >
> > This reverts commit d428e81266a59974ade74c1ba019af39f23304ab.
> > We have agreed with removing dtb-related stuff from mkeficapsule
> > command even if the commit 47a25e81d35c ("Revert "efi_capsule: Move
> > signature from DTB to .rodata"") was applied.
> 
> Can you please explain why this is being removed? How is the public
> key to be communicated?

Via the script that Akashi-san is adding in this patch series.

> 
> Please can you also copy me on future related patches? (thank for you
> for the private email alerting me)
> 
> What does "We have agreed" mean?

This has nothing to do with the public key.  This is only changing the tool
we used to include the key in the DTB.  It is the same patch I mentioned you 
should never have reverted in the first place on your pull request,  but since
that happened very late in the release cycle we said we would re-apply it after
the release.

> 
> >
> > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > ---
> >  tools/mkeficapsule.c | 229 ++-----------------------------------------
> >  1 file changed, 7 insertions(+), 222 deletions(-)
> >
> 
> Regards,
> Simon

It was my patch to begin with so I am not really sure my reviewed tag has
any value, but FWIW

Reviewed-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v4 03/11] efi_loader: capsule: add back efi_get_public_key_data()
  2021-10-07  6:23 ` [PATCH v4 03/11] efi_loader: capsule: add back efi_get_public_key_data() AKASHI Takahiro
@ 2021-10-08 19:25   ` Ilias Apalodimas
  2021-10-15  0:40   ` Simon Glass
  1 sibling, 0 replies; 40+ messages in thread
From: Ilias Apalodimas @ 2021-10-08 19:25 UTC (permalink / raw)
  To: AKASHI Takahiro
  Cc: xypron.glpk, agraf, sughosh.ganu, masami.hiramatsu, u-boot

Akashi-san,

On Thu, Oct 07, 2021 at 03:23:32PM +0900, AKASHI Takahiro wrote:
> The commit 47a25e81d35c ("Revert "efi_capsule: Move signature from DTB to
> .rodata"") failed to revert the removal of efi_get_public_key_data().
> 
> Add back this function and move it under lib/efi_loader so that other
> platforms can utilize it. It is now declared as a weak function so that
> it can be replaced with a platform-specific implementation.
> 
> Fixes: 47a25e81d35c ("Revert "efi_capsule: Move signature from DTB to
> 	.rodata"")

Yep I noticed the same thing a few days ago and told Heinrich, I'd fix
that. Thanks!

> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>  lib/efi_loader/efi_capsule.c | 36 ++++++++++++++++++++++++++++++++++++
>  1 file changed, 36 insertions(+)
> 
> diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
> index b75e4bcba1a9..44f5da61a9be 100644
> --- a/lib/efi_loader/efi_capsule.c
> +++ b/lib/efi_loader/efi_capsule.c
> @@ -11,15 +11,20 @@
>  #include <common.h>
>  #include <efi_loader.h>
>  #include <efi_variable.h>
> +#include <env.h>
> +#include <fdtdec.h>
>  #include <fs.h>
>  #include <malloc.h>
>  #include <mapmem.h>
>  #include <sort.h>
> +#include <asm/global_data.h>
>  
>  #include <crypto/pkcs7.h>
>  #include <crypto/pkcs7_parser.h>
>  #include <linux/err.h>
>  
> +DECLARE_GLOBAL_DATA_PTR;
> +
>  const efi_guid_t efi_guid_capsule_report = EFI_CAPSULE_REPORT_GUID;
>  static const efi_guid_t efi_guid_firmware_management_capsule_id =
>  		EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID;
> @@ -251,6 +256,37 @@ out:
>  }
>  
>  #if defined(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> +int __weak efi_get_public_key_data(void **pkey, efi_uintn_t *pkey_len)
> +{
> +	const void *fdt_blob = gd->fdt_blob;
> +	const void *blob;
> +	const char *cnode_name = "capsule-key";
> +	const char *snode_name = "signature";
> +	int sig_node;
> +	int len;
> +
> +	sig_node = fdt_subnode_offset(fdt_blob, 0, snode_name);
> +	if (sig_node < 0) {
> +		log_err("Unable to get signature node offset\n");
> +
> +		return -FDT_ERR_NOTFOUND;
> +	}
> +
> +	blob = fdt_getprop(fdt_blob, sig_node, cnode_name, &len);
> +
> +	if (!blob || len < 0) {
> +		log_err("Unable to get capsule-key value\n");
> +		*pkey = NULL;
> +		*pkey_len = 0;
> +
> +		return -FDT_ERR_NOTFOUND;
> +	}
> +
> +	*pkey = (void *)blob;
> +	*pkey_len = len;
> +
> +	return 0;
> +}
>  
>  efi_status_t efi_capsule_authenticate(const void *capsule, efi_uintn_t capsule_size,
>  				      void **image, efi_uintn_t *image_size)
> -- 
> 2.33.0
> 

Reviewed-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v4 01/11] Revert "Revert "mkeficapsule: Remove dtb related options""
  2021-10-08 19:11     ` Ilias Apalodimas
@ 2021-10-11  0:29       ` AKASHI Takahiro
  2021-10-11 14:54         ` Simon Glass
  0 siblings, 1 reply; 40+ messages in thread
From: AKASHI Takahiro @ 2021-10-11  0:29 UTC (permalink / raw)
  To: Ilias Apalodimas
  Cc: Simon Glass, Heinrich Schuchardt, Alex Graf, Sughosh Ganu,
	Masami Hiramatsu, U-Boot Mailing List

On Fri, Oct 08, 2021 at 10:11:59PM +0300, Ilias Apalodimas wrote:
> Hi Simon, 
> 
> On Fri, Oct 08, 2021 at 09:38:11AM -0600, Simon Glass wrote:
> > Hi Takahiro,
> > 
> > On Thu, 7 Oct 2021 at 00:24, AKASHI Takahiro <takahiro.akashi@linaro.org> wrote:
> > >
> > > This reverts commit d428e81266a59974ade74c1ba019af39f23304ab.
> > > We have agreed with removing dtb-related stuff from mkeficapsule
> > > command even if the commit 47a25e81d35c ("Revert "efi_capsule: Move
> > > signature from DTB to .rodata"") was applied.
> > 
> > Can you please explain why this is being removed? How is the public
> > key to be communicated?
> 
> Via the script that Akashi-san is adding in this patch series.

Yeah, please see my patch#4 for fdtsig.sh.
The reason why I think we should remove the feature from mkeficapsule
command is partly because we can do the same task by using *existing*
fdt tools and partly because having two totally-independent functionality
(one for a capsule binary and one for fdt) in a single tool seems confusing.

> > 
> > Please can you also copy me on future related patches? (thank for you
> > for the private email alerting me)
> > 
> > What does "We have agreed" mean?
> 
> This has nothing to do with the public key.  This is only changing the tool
> we used to include the key in the DTB.  It is the same patch I mentioned you 
> should never have reverted in the first place on your pull request,  but since
> that happened very late in the release cycle we said we would re-apply it after
> the release.

Indeed.

-Takahiro Akashi


> > 
> > >
> > > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > > ---
> > >  tools/mkeficapsule.c | 229 ++-----------------------------------------
> > >  1 file changed, 7 insertions(+), 222 deletions(-)
> > >
> > 
> > Regards,
> > Simon
> 
> It was my patch to begin with so I am not really sure my reviewed tag has
> any value, but FWIW
> 
> Reviewed-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v4 04/11] tools: add fdtsig.sh
  2021-10-07  6:23 ` [PATCH v4 04/11] tools: add fdtsig.sh AKASHI Takahiro
@ 2021-10-11 14:54   ` Simon Glass
  2021-10-12  1:42     ` AKASHI Takahiro
  0 siblings, 1 reply; 40+ messages in thread
From: Simon Glass @ 2021-10-11 14:54 UTC (permalink / raw)
  To: AKASHI Takahiro
  Cc: Heinrich Schuchardt, Alex Graf, Ilias Apalodimas, Sughosh Ganu,
	Masami Hiramatsu, U-Boot Mailing List

Hi Takahiro,

On Thu, 7 Oct 2021 at 00:25, AKASHI Takahiro <takahiro.akashi@linaro.org> wrote:
>
> With this script, a public key is added to a device tree blob
> as the default efi_get_public_key_data() expects.
>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>  MAINTAINERS     |  1 +
>  tools/fdtsig.sh | 40 ++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 41 insertions(+)
>  create mode 100755 tools/fdtsig.sh

Instead of an ad-hoc script with no tests, could we use binman for
putting the image together and inserting it?

Regards,
Simon

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v4 01/11] Revert "Revert "mkeficapsule: Remove dtb related options""
  2021-10-11  0:29       ` AKASHI Takahiro
@ 2021-10-11 14:54         ` Simon Glass
  2021-10-12  1:15           ` AKASHI Takahiro
  0 siblings, 1 reply; 40+ messages in thread
From: Simon Glass @ 2021-10-11 14:54 UTC (permalink / raw)
  To: AKASHI Takahiro, Ilias Apalodimas, Simon Glass,
	Heinrich Schuchardt, Alex Graf, Sughosh Ganu, Masami Hiramatsu,
	U-Boot Mailing List

Hi Takahiro, Ilias,

On Sun, 10 Oct 2021 at 18:29, AKASHI Takahiro
<takahiro.akashi@linaro.org> wrote:
>
> On Fri, Oct 08, 2021 at 10:11:59PM +0300, Ilias Apalodimas wrote:
> > Hi Simon,
> >
> > On Fri, Oct 08, 2021 at 09:38:11AM -0600, Simon Glass wrote:
> > > Hi Takahiro,
> > >
> > > On Thu, 7 Oct 2021 at 00:24, AKASHI Takahiro <takahiro.akashi@linaro.org> wrote:
> > > >
> > > > This reverts commit d428e81266a59974ade74c1ba019af39f23304ab.
> > > > We have agreed with removing dtb-related stuff from mkeficapsule
> > > > command even if the commit 47a25e81d35c ("Revert "efi_capsule: Move
> > > > signature from DTB to .rodata"") was applied.
> > >
> > > Can you please explain why this is being removed? How is the public
> > > key to be communicated?
> >
> > Via the script that Akashi-san is adding in this patch series.
>
> Yeah, please see my patch#4 for fdtsig.sh.

OK I see. I don't think I was copied on that. I replied on the patch.

Is there a tree somewhere with all the patches and docs so I can take a look?

Regards,
Simon



> The reason why I think we should remove the feature from mkeficapsule
> command is partly because we can do the same task by using *existing*
> fdt tools and partly because having two totally-independent functionality
> (one for a capsule binary and one for fdt) in a single tool seems confusing.
>
> > >
> > > Please can you also copy me on future related patches? (thank for you
> > > for the private email alerting me)
> > >
> > > What does "We have agreed" mean?
> >
> > This has nothing to do with the public key.  This is only changing the tool
> > we used to include the key in the DTB.  It is the same patch I mentioned you
> > should never have reverted in the first place on your pull request,  but since
> > that happened very late in the release cycle we said we would re-apply it after
> > the release.
>
> Indeed.
>
> -Takahiro Akashi
>
>
> > >
> > > >
> > > > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > > > ---
> > > >  tools/mkeficapsule.c | 229 ++-----------------------------------------
> > > >  1 file changed, 7 insertions(+), 222 deletions(-)
> > > >
> > >
> > > Regards,
> > > Simon
> >
> > It was my patch to begin with so I am not really sure my reviewed tag has
> > any value, but FWIW
> >
> > Reviewed-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v4 01/11] Revert "Revert "mkeficapsule: Remove dtb related options""
  2021-10-11 14:54         ` Simon Glass
@ 2021-10-12  1:15           ` AKASHI Takahiro
  0 siblings, 0 replies; 40+ messages in thread
From: AKASHI Takahiro @ 2021-10-12  1:15 UTC (permalink / raw)
  To: Simon Glass
  Cc: Ilias Apalodimas, Heinrich Schuchardt, Alex Graf, Sughosh Ganu,
	Masami Hiramatsu, U-Boot Mailing List

Simon,

On Mon, Oct 11, 2021 at 08:54:10AM -0600, Simon Glass wrote:
> Hi Takahiro, Ilias,
> 
> On Sun, 10 Oct 2021 at 18:29, AKASHI Takahiro
> <takahiro.akashi@linaro.org> wrote:
> >
> > On Fri, Oct 08, 2021 at 10:11:59PM +0300, Ilias Apalodimas wrote:
> > > Hi Simon,
> > >
> > > On Fri, Oct 08, 2021 at 09:38:11AM -0600, Simon Glass wrote:
> > > > Hi Takahiro,
> > > >
> > > > On Thu, 7 Oct 2021 at 00:24, AKASHI Takahiro <takahiro.akashi@linaro.org> wrote:
> > > > >
> > > > > This reverts commit d428e81266a59974ade74c1ba019af39f23304ab.
> > > > > We have agreed with removing dtb-related stuff from mkeficapsule
> > > > > command even if the commit 47a25e81d35c ("Revert "efi_capsule: Move
> > > > > signature from DTB to .rodata"") was applied.
> > > >
> > > > Can you please explain why this is being removed? How is the public
> > > > key to be communicated?
> > >
> > > Via the script that Akashi-san is adding in this patch series.
> >
> > Yeah, please see my patch#4 for fdtsig.sh.
> 
> OK I see. I don't think I was copied on that. I replied on the patch.

In my initial submission, there were some accidentally-duplicated patches
that would have caused some confusion.

> Is there a tree somewhere with all the patches and docs so I can take a look?

Please take a look at:
https://git.linaro.org/people/takahiro.akashi/u-boot.git efi/dm_disk

-Takahiro Akashi


> Regards,
> Simon
> 
> 
> 
> > The reason why I think we should remove the feature from mkeficapsule
> > command is partly because we can do the same task by using *existing*
> > fdt tools and partly because having two totally-independent functionality
> > (one for a capsule binary and one for fdt) in a single tool seems confusing.
> >
> > > >
> > > > Please can you also copy me on future related patches? (thank for you
> > > > for the private email alerting me)
> > > >
> > > > What does "We have agreed" mean?
> > >
> > > This has nothing to do with the public key.  This is only changing the tool
> > > we used to include the key in the DTB.  It is the same patch I mentioned you
> > > should never have reverted in the first place on your pull request,  but since
> > > that happened very late in the release cycle we said we would re-apply it after
> > > the release.
> >
> > Indeed.
> >
> > -Takahiro Akashi
> >
> >
> > > >
> > > > >
> > > > > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > > > > ---
> > > > >  tools/mkeficapsule.c | 229 ++-----------------------------------------
> > > > >  1 file changed, 7 insertions(+), 222 deletions(-)
> > > > >
> > > >
> > > > Regards,
> > > > Simon
> > >
> > > It was my patch to begin with so I am not really sure my reviewed tag has
> > > any value, but FWIW
> > >
> > > Reviewed-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v4 04/11] tools: add fdtsig.sh
  2021-10-11 14:54   ` Simon Glass
@ 2021-10-12  1:42     ` AKASHI Takahiro
  2021-10-15  0:40       ` Simon Glass
  0 siblings, 1 reply; 40+ messages in thread
From: AKASHI Takahiro @ 2021-10-12  1:42 UTC (permalink / raw)
  To: Simon Glass
  Cc: Heinrich Schuchardt, Alex Graf, Ilias Apalodimas, Sughosh Ganu,
	Masami Hiramatsu, U-Boot Mailing List

Simon,

On Mon, Oct 11, 2021 at 08:54:09AM -0600, Simon Glass wrote:
> Hi Takahiro,
> 
> On Thu, 7 Oct 2021 at 00:25, AKASHI Takahiro <takahiro.akashi@linaro.org> wrote:
> >
> > With this script, a public key is added to a device tree blob
> > as the default efi_get_public_key_data() expects.
> >
> > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > ---
> >  MAINTAINERS     |  1 +
> >  tools/fdtsig.sh | 40 ++++++++++++++++++++++++++++++++++++++++
> >  2 files changed, 41 insertions(+)
> >  create mode 100755 tools/fdtsig.sh
> 
> Instead of an ad-hoc script with no tests,

Basically I intended to provide fdtsig.sh as a *sample* script so that
people may want to integrate the logic into their own build rule/systems.
But I could use this script in my 'capsule authentication' test
that is also added in patch#22.

> could we use binman for
> putting the image together and inserting it?

First, as you can see, the script is quite simple and secondly,
the purpose of binman, IIUC, is to help handle/manipulate U-Boot
image binaries.
So I'm not sure whether it is really useful to add such a feature to binman.

-Takahiro Akashi


> Regards,
> Simon

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v4 04/11] tools: add fdtsig.sh
  2021-10-12  1:42     ` AKASHI Takahiro
@ 2021-10-15  0:40       ` Simon Glass
  2021-10-25  3:06         ` AKASHI Takahiro
  0 siblings, 1 reply; 40+ messages in thread
From: Simon Glass @ 2021-10-15  0:40 UTC (permalink / raw)
  To: AKASHI Takahiro, Simon Glass, Heinrich Schuchardt, Alex Graf,
	Ilias Apalodimas, Sughosh Ganu, Masami Hiramatsu,
	U-Boot Mailing List

Hi Takahiro,

On Mon, 11 Oct 2021 at 19:42, AKASHI Takahiro
<takahiro.akashi@linaro.org> wrote:
>
> Simon,
>
> On Mon, Oct 11, 2021 at 08:54:09AM -0600, Simon Glass wrote:
> > Hi Takahiro,
> >
> > On Thu, 7 Oct 2021 at 00:25, AKASHI Takahiro <takahiro.akashi@linaro.org> wrote:
> > >
> > > With this script, a public key is added to a device tree blob
> > > as the default efi_get_public_key_data() expects.
> > >
> > > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > > ---
> > >  MAINTAINERS     |  1 +
> > >  tools/fdtsig.sh | 40 ++++++++++++++++++++++++++++++++++++++++
> > >  2 files changed, 41 insertions(+)
> > >  create mode 100755 tools/fdtsig.sh
> >
> > Instead of an ad-hoc script with no tests,
>
> Basically I intended to provide fdtsig.sh as a *sample* script so that
> people may want to integrate the logic into their own build rule/systems.
> But I could use this script in my 'capsule authentication' test
> that is also added in patch#22.
>
> > could we use binman for
> > putting the image together and inserting it?
>
> First, as you can see, the script is quite simple and secondly,
> the purpose of binman, IIUC, is to help handle/manipulate U-Boot
> image binaries.
> So I'm not sure whether it is really useful to add such a feature to binman.

I'm not sure. The script seems very ad-hoc to me, for a feature that
Linaro is pushing so hard.

I don't see where the script is used in the tests or even mentioned in
the documentation. Am I missing something?

Regards,
Simon

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v4 03/11] efi_loader: capsule: add back efi_get_public_key_data()
  2021-10-07  6:23 ` [PATCH v4 03/11] efi_loader: capsule: add back efi_get_public_key_data() AKASHI Takahiro
  2021-10-08 19:25   ` Ilias Apalodimas
@ 2021-10-15  0:40   ` Simon Glass
  2021-10-20  8:18     ` Masami Hiramatsu
  1 sibling, 1 reply; 40+ messages in thread
From: Simon Glass @ 2021-10-15  0:40 UTC (permalink / raw)
  To: AKASHI Takahiro
  Cc: Heinrich Schuchardt, Alex Graf, Ilias Apalodimas, Sughosh Ganu,
	Masami Hiramatsu, U-Boot Mailing List

Hi Takahiro,

On Thu, 7 Oct 2021 at 00:25, AKASHI Takahiro <takahiro.akashi@linaro.org> wrote:
>
> The commit 47a25e81d35c ("Revert "efi_capsule: Move signature from DTB to
> .rodata"") failed to revert the removal of efi_get_public_key_data().
>
> Add back this function and move it under lib/efi_loader so that other
> platforms can utilize it. It is now declared as a weak function so that
> it can be replaced with a platform-specific implementation.
>
> Fixes: 47a25e81d35c ("Revert "efi_capsule: Move signature from DTB to
>         .rodata"")
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>  lib/efi_loader/efi_capsule.c | 36 ++++++++++++++++++++++++++++++++++++
>  1 file changed, 36 insertions(+)
>
> diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
> index b75e4bcba1a9..44f5da61a9be 100644
> --- a/lib/efi_loader/efi_capsule.c
> +++ b/lib/efi_loader/efi_capsule.c
> @@ -11,15 +11,20 @@
>  #include <common.h>
>  #include <efi_loader.h>
>  #include <efi_variable.h>
> +#include <env.h>
> +#include <fdtdec.h>
>  #include <fs.h>
>  #include <malloc.h>
>  #include <mapmem.h>
>  #include <sort.h>
> +#include <asm/global_data.h>
>
>  #include <crypto/pkcs7.h>
>  #include <crypto/pkcs7_parser.h>
>  #include <linux/err.h>
>
> +DECLARE_GLOBAL_DATA_PTR;
> +
>  const efi_guid_t efi_guid_capsule_report = EFI_CAPSULE_REPORT_GUID;
>  static const efi_guid_t efi_guid_firmware_management_capsule_id =
>                 EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID;
> @@ -251,6 +256,37 @@ out:
>  }
>
>  #if defined(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> +int __weak efi_get_public_key_data(void **pkey, efi_uintn_t *pkey_len)

I don't think this should be weak. What other way is there of handling
this and why would it be platform-specific?

> +{
> +       const void *fdt_blob = gd->fdt_blob;
> +       const void *blob;
> +       const char *cnode_name = "capsule-key";
> +       const char *snode_name = "signature";
> +       int sig_node;
> +       int len;
> +
> +       sig_node = fdt_subnode_offset(fdt_blob, 0, snode_name);
> +       if (sig_node < 0) {
> +               log_err("Unable to get signature node offset\n");
> +
> +               return -FDT_ERR_NOTFOUND;
> +       }
> +
> +       blob = fdt_getprop(fdt_blob, sig_node, cnode_name, &len);
> +
> +       if (!blob || len < 0) {
> +               log_err("Unable to get capsule-key value\n");
> +               *pkey = NULL;
> +               *pkey_len = 0;
> +
> +               return -FDT_ERR_NOTFOUND;
> +       }
> +
> +       *pkey = (void *)blob;
> +       *pkey_len = len;
> +
> +       return 0;
> +}
>
>  efi_status_t efi_capsule_authenticate(const void *capsule, efi_uintn_t capsule_size,
>                                       void **image, efi_uintn_t *image_size)
> --
> 2.33.0
>

Regards,
Simon

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v4 05/11] tools: mkeficapsule: add firmwware image signing
  2021-10-07  6:23 ` [PATCH v4 05/11] tools: mkeficapsule: add firmwware image signing AKASHI Takahiro
@ 2021-10-20  8:17   ` Masami Hiramatsu
  2021-10-25  3:12     ` AKASHI Takahiro
  0 siblings, 1 reply; 40+ messages in thread
From: Masami Hiramatsu @ 2021-10-20  8:17 UTC (permalink / raw)
  To: AKASHI Takahiro
  Cc: Heinrich Schuchardt, Alexander Graf, Ilias Apalodimas,
	Sughosh Ganu, U-Boot Mailing List

Hello Akashi-san,

Can you split this patch out from this series?
It seems that the series is doing several different things. This
capsule signing has no alternatives, but others are modifying or
replacing the current existing feature. In other words, this capsule
signing feature has no alternatives, but others we can continue to
use.

Thank you,

2021年10月7日(木) 15:25 AKASHI Takahiro <takahiro.akashi@linaro.org>:
>
> With this enhancement, mkeficapsule will be able to sign a capsule
> file when it is created. A signature added will be used later
> in the verification at FMP's SetImage() call.
>
> To do that, We need specify additional command parameters:
>   -monotonic-cout <count> : monotonic count
>   -private-key <private key file> : private key file
>   -certificate <certificate file> : certificate file
> Only when all of those parameters are given, a signature will be added
> to a capsule file.
>
> Users are expected to maintain and increment the monotonic count at
> every time of the update for each firmware image.
>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>  tools/Kconfig        |   7 +
>  tools/Makefile       |   8 +-
>  tools/mkeficapsule.c | 435 +++++++++++++++++++++++++++++++++++++++----
>  3 files changed, 416 insertions(+), 34 deletions(-)
>
> diff --git a/tools/Kconfig b/tools/Kconfig
> index d6f82cd949b5..9a37ed035311 100644
> --- a/tools/Kconfig
> +++ b/tools/Kconfig
> @@ -20,4 +20,11 @@ config TOOLS_LIBCRYPTO
>           This selection does not affect target features, such as runtime FIT
>           signature verification.
>
> +config TOOLS_MKEFICAPSULE
> +       bool "Build efimkcapsule command"
> +       default y if EFI_CAPSULE_ON_DISK
> +       help
> +         This command allows users to create a UEFI capsule file and,
> +         optionally sign that file. If you want to enable UEFI capsule
> +         update feature on your target, you certainly need this.
>  endmenu
> diff --git a/tools/Makefile b/tools/Makefile
> index 4a86321f6467..6ea3033dbfb8 100644
> --- a/tools/Makefile
> +++ b/tools/Makefile
> @@ -237,8 +237,12 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs
>  hostprogs-$(CONFIG_ASN1_COMPILER)      += asn1_compiler
>  HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
>
> -mkeficapsule-objs      := mkeficapsule.o $(LIBFDT_OBJS)
> -hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
> +HOSTLDLIBS_mkeficapsule += -luuid
> +ifeq ($(CONFIG_TOOLS_LIBCRYPTO),y)
> +HOSTLDLIBS_mkeficapsule += \
> +       $(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto")
> +endif
> +hostprogs-$(CONFIG_TOOLS_MKEFICAPSULE) += mkeficapsule
>
>  # We build some files with extra pedantic flags to try to minimize things
>  # that won't build on some weird host compiler -- though there are lots of
> diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
> index 4995ba4e0c2a..5541e4bda894 100644
> --- a/tools/mkeficapsule.c
> +++ b/tools/mkeficapsule.c
> @@ -15,6 +15,16 @@
>  #include <sys/stat.h>
>  #include <sys/types.h>
>
> +#include <linux/kconfig.h>
> +#ifdef CONFIG_TOOLS_LIBCRYPTO
> +#include <openssl/asn1.h>
> +#include <openssl/bio.h>
> +#include <openssl/evp.h>
> +#include <openssl/err.h>
> +#include <openssl/pem.h>
> +#include <openssl/pkcs7.h>
> +#endif
> +
>  typedef __u8 u8;
>  typedef __u16 u16;
>  typedef __u32 u32;
> @@ -38,12 +48,25 @@ efi_guid_t efi_guid_image_type_uboot_fit =
>                 EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID;
>  efi_guid_t efi_guid_image_type_uboot_raw =
>                 EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID;
> +efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
> +
> +#ifdef CONFIG_TOOLS_LIBCRYPTO
> +static const char *opts_short = "f:r:i:I:v:p:c:m:dh";
> +#else
> +static const char *opts_short = "f:r:i:I:v:h";
> +#endif
>
>  static struct option options[] = {
>         {"fit", required_argument, NULL, 'f'},
>         {"raw", required_argument, NULL, 'r'},
>         {"index", required_argument, NULL, 'i'},
>         {"instance", required_argument, NULL, 'I'},
> +#ifdef CONFIG_TOOLS_LIBCRYPTO
> +       {"private-key", required_argument, NULL, 'p'},
> +       {"certificate", required_argument, NULL, 'c'},
> +       {"monotonic-count", required_argument, NULL, 'm'},
> +       {"dump-sig", no_argument, NULL, 'd'},
> +#endif
>         {"help", no_argument, NULL, 'h'},
>         {NULL, 0, NULL, 0},
>  };
> @@ -57,16 +80,280 @@ static void print_usage(void)
>                "\t-r, --raw <raw image>       new raw image file\n"
>                "\t-i, --index <index>         update image index\n"
>                "\t-I, --instance <instance>   update hardware instance\n"
> +#ifdef CONFIG_TOOLS_LIBCRYPTO
> +              "\t-p, --private-key <privkey file>  private key file\n"
> +              "\t-c, --certificate <cert file>     signer's certificate file\n"
> +              "\t-m, --monotonic-count <count>     monotonic count\n"
> +              "\t-d, --dump_sig              dump signature (*.p7)\n"
> +#endif
>                "\t-h, --help                  print a help message\n",
>                tool_name);
>  }
>
> +/**
> + * auth_context - authentication context
> + * @key_file:  Path to a private key file
> + * @cert_file: Path to a certificate file
> + * @image_data:        Pointer to firmware data
> + * @image_size:        Size of firmware data
> + * @auth:      Authentication header
> + * @sig_data:  Signature data
> + * @sig_size:  Size of signature data
> + *
> + * Data structure used in create_auth_data(). @key_file through
> + * @image_size are input parameters. @auth, @sig_data and @sig_size
> + * are filled in by create_auth_data().
> + */
> +struct auth_context {
> +       char *key_file;
> +       char *cert_file;
> +       u8 *image_data;
> +       size_t image_size;
> +       struct efi_firmware_image_authentication auth;
> +       u8 *sig_data;
> +       size_t sig_size;
> +};
> +
> +static int dump_sig;
> +
> +#ifdef CONFIG_TOOLS_LIBCRYPTO
> +/**
> + * fileio-read_pkey - read out a private key
> + * @filename:  Path to a private key file
> + *
> + * Read out a private key file and parse it into "EVP_PKEY" structure.
> + *
> + * Return:
> + * * Pointer to private key structure  - on success
> + * * NULL - on failure
> + */
> +static EVP_PKEY *fileio_read_pkey(const char *filename)
> +{
> +       EVP_PKEY *key = NULL;
> +       BIO *bio;
> +
> +       bio = BIO_new_file(filename, "r");
> +       if (!bio)
> +               goto out;
> +
> +       key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
> +
> +out:
> +       BIO_free_all(bio);
> +       if (!key) {
> +               printf("Can't load key from file '%s'\n", filename);
> +               ERR_print_errors_fp(stderr);
> +       }
> +
> +       return key;
> +}
> +
> +/**
> + * fileio-read_cert - read out a certificate
> + * @filename:  Path to a certificate file
> + *
> + * Read out a certificate file and parse it into "X509" structure.
> + *
> + * Return:
> + * * Pointer to certificate structure  - on success
> + * * NULL - on failure
> + */
> +static X509 *fileio_read_cert(const char *filename)
> +{
> +       X509 *cert = NULL;
> +       BIO *bio;
> +
> +       bio = BIO_new_file(filename, "r");
> +       if (!bio)
> +               goto out;
> +
> +       cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
> +
> +out:
> +       BIO_free_all(bio);
> +       if (!cert) {
> +               printf("Can't load certificate from file '%s'\n", filename);
> +               ERR_print_errors_fp(stderr);
> +       }
> +
> +       return cert;
> +}
> +
> +/**
> + * create_auth_data - compose authentication data in capsule
> + * @auth_context:      Pointer to authentication context
> + *
> + * Fill up an authentication header (.auth) and signature data (.sig_data)
> + * in @auth_context, using library functions from openssl.
> + * All the parameters in @auth_context must be filled in by a caller.
> + *
> + * Return:
> + * * 0  - on success
> + * * -1 - on failure
> + */
> +static int create_auth_data(struct auth_context *ctx)
> +{
> +       EVP_PKEY *key = NULL;
> +       X509 *cert = NULL;
> +       BIO *data_bio = NULL;
> +       const EVP_MD *md;
> +       PKCS7 *p7;
> +       int flags, ret = -1;
> +
> +       OpenSSL_add_all_digests();
> +       OpenSSL_add_all_ciphers();
> +       ERR_load_crypto_strings();
> +
> +       key = fileio_read_pkey(ctx->key_file);
> +       if (!key)
> +               goto err;
> +       cert = fileio_read_cert(ctx->cert_file);
> +       if (!cert)
> +               goto err;
> +
> +       /*
> +        * create a BIO, containing:
> +        *  * firmware image
> +        *  * monotonic count
> +        * in this order!
> +        * See EDK2's FmpAuthenticatedHandlerRsa2048Sha256()
> +        */
> +       data_bio = BIO_new(BIO_s_mem());
> +       BIO_write(data_bio, ctx->image_data, ctx->image_size);
> +       BIO_write(data_bio, &ctx->auth.monotonic_count,
> +                 sizeof(ctx->auth.monotonic_count));
> +
> +       md = EVP_get_digestbyname("SHA256");
> +       if (!md)
> +               goto err;
> +
> +       /* create signature */
> +       /* TODO: maybe add PKCS7_NOATTR and PKCS7_NOSMIMECAP */
> +       flags = PKCS7_BINARY | PKCS7_DETACHED;
> +       p7 = PKCS7_sign(NULL, NULL, NULL, data_bio, flags | PKCS7_PARTIAL);
> +       if (!p7)
> +               goto err;
> +       if (!PKCS7_sign_add_signer(p7, cert, key, md, flags))
> +               goto err;
> +       if (!PKCS7_final(p7, data_bio, flags))
> +               goto err;
> +
> +       /* convert pkcs7 into DER */
> +       ctx->sig_data = NULL;
> +       ctx->sig_size = ASN1_item_i2d((ASN1_VALUE *)p7, &ctx->sig_data,
> +                                     ASN1_ITEM_rptr(PKCS7));
> +       if (!ctx->sig_size)
> +               goto err;
> +
> +       /* fill auth_info */
> +       ctx->auth.auth_info.hdr.dwLength = sizeof(ctx->auth.auth_info)
> +                                               + ctx->sig_size;
> +       ctx->auth.auth_info.hdr.wRevision = WIN_CERT_REVISION_2_0;
> +       ctx->auth.auth_info.hdr.wCertificateType = WIN_CERT_TYPE_EFI_GUID;
> +       memcpy(&ctx->auth.auth_info.cert_type, &efi_guid_cert_type_pkcs7,
> +              sizeof(efi_guid_cert_type_pkcs7));
> +
> +       ret = 0;
> +err:
> +       BIO_free_all(data_bio);
> +       EVP_PKEY_free(key);
> +       X509_free(cert);
> +
> +       return ret;
> +}
> +
> +/**
> + * dump_signature - dump out a signature
> + * @path:      Path to a capsule file
> + * @signature: Signature data
> + * @sig_size:  Size of signature data
> + *
> + * Signature data pointed to by @signature will be saved into
> + * a file whose file name is @path with ".p7" suffix.
> + *
> + * Return:
> + * * 0  - on success
> + * * -1 - on failure
> + */
> +static int dump_signature(const char *path, u8 *signature, size_t sig_size)
> +{
> +       char *sig_path;
> +       FILE *f;
> +       size_t size;
> +       int ret = -1;
> +
> +       sig_path = malloc(strlen(path) + 3 + 1);
> +       if (!sig_path)
> +               return ret;
> +
> +       sprintf(sig_path, "%s.p7", path);
> +       f = fopen(sig_path, "w");
> +       if (!f)
> +               goto err;
> +
> +       size = fwrite(signature, 1, sig_size, f);
> +       if (size == sig_size)
> +               ret = 0;
> +
> +       fclose(f);
> +err:
> +       free(sig_path);
> +       return ret;
> +}
> +
> +/**
> + * free_sig_data - free out signature data
> + * @ctx:       Pointer to authentication context
> + *
> + * Free signature data allocated in create_auth_data().
> + */
> +static void free_sig_data(struct auth_context *ctx)
> +{
> +       if (ctx->sig_size)
> +               OPENSSL_free(ctx->sig_data);
> +}
> +#else
> +static int create_auth_data(struct auth_context *ctx)
> +{
> +       return 0;
> +}
> +
> +static int dump_signature(const char *path, u8 *signature, size_t sig_size)
> +{
> +       return 0;
> +}
> +
> +static void free_sig_data(struct auth_context *ctx) {}
> +#endif
> +
> +/**
> + * create_fwbin - create an uefi capsule file
> + * @path:      Path to a created capsule file
> + * @bin:       Path to a firmware binary to encapsulate
> + * @guid:      GUID of related FMP driver
> + * @index:     Index number in capsule
> + * @instance:  Instance number in capsule
> + * @mcount:    Monotonic count in authentication information
> + * @private_file:      Path to a private key file
> + * @cert_file: Path to a certificate file
> + *
> + * This function actually does the job of creating an uefi capsule file.
> + * All the arguments must be supplied.
> + * If either @private_file ror @cert_file is NULL, the capsule file
> + * won't be signed.
> + *
> + * Return:
> + * * 0  - on success
> + * * -1 - on failure
> + */
>  static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> -                       unsigned long index, unsigned long instance)
> +                       unsigned long index, unsigned long instance,
> +                       uint64_t mcount, char *privkey_file, char *cert_file)
>  {
>         struct efi_capsule_header header;
>         struct efi_firmware_management_capsule_header capsule;
>         struct efi_firmware_management_capsule_image_header image;
> +       struct auth_context auth_context;
>         FILE *f, *g;
>         struct stat bin_stat;
>         u8 *data;
> @@ -76,8 +363,9 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
>  #ifdef DEBUG
>         printf("For output: %s\n", path);
>         printf("\tbin: %s\n\ttype: %pUl\n", bin, guid);
> -       printf("\tindex: %ld\n\tinstance: %ld\n", index, instance);
> +       printf("\tindex: %lu\n\tinstance: %lu\n", index, instance);
>  #endif
> +       auth_context.sig_size = 0;
>
>         g = fopen(bin, "r");
>         if (!g) {
> @@ -93,11 +381,34 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
>                 printf("cannot allocate memory: %zx\n", (size_t)bin_stat.st_size);
>                 goto err_1;
>         }
> -       f = fopen(path, "w");
> -       if (!f) {
> -               printf("cannot open %s\n", path);
> +
> +       size = fread(data, 1, bin_stat.st_size, g);
> +       if (size < bin_stat.st_size) {
> +               printf("read failed (%zx)\n", size);
>                 goto err_2;
>         }
> +
> +       /* first, calculate signature to determine its size */
> +       if (privkey_file && cert_file) {
> +               auth_context.key_file = privkey_file;
> +               auth_context.cert_file = cert_file;
> +               auth_context.auth.monotonic_count = mcount;
> +               auth_context.image_data = data;
> +               auth_context.image_size = bin_stat.st_size;
> +
> +               if (create_auth_data(&auth_context)) {
> +                       printf("Signing firmware image failed\n");
> +                       goto err_3;
> +               }
> +
> +               if (dump_sig &&
> +                   dump_signature(path, auth_context.sig_data,
> +                                  auth_context.sig_size)) {
> +                       printf("Creating signature file failed\n");
> +                       goto err_3;
> +               }
> +       }
> +
>         header.capsule_guid = efi_guid_fm_capsule;
>         header.header_size = sizeof(header);
>         /* TODO: The current implementation ignores flags */
> @@ -106,11 +417,20 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
>                                         + sizeof(capsule) + sizeof(u64)
>                                         + sizeof(image)
>                                         + bin_stat.st_size;
> +       if (auth_context.sig_size)
> +               header.capsule_image_size += sizeof(auth_context.auth)
> +                               + auth_context.sig_size;
> +
> +       f = fopen(path, "w");
> +       if (!f) {
> +               printf("cannot open %s\n", path);
> +               goto err_3;
> +       }
>
>         size = fwrite(&header, 1, sizeof(header), f);
>         if (size < sizeof(header)) {
>                 printf("write failed (%zx)\n", size);
> -               goto err_3;
> +               goto err_4;
>         }
>
>         capsule.version = 0x00000001;
> @@ -119,13 +439,13 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
>         size = fwrite(&capsule, 1, sizeof(capsule), f);
>         if (size < (sizeof(capsule))) {
>                 printf("write failed (%zx)\n", size);
> -               goto err_3;
> +               goto err_4;
>         }
>         offset = sizeof(capsule) + sizeof(u64);
>         size = fwrite(&offset, 1, sizeof(offset), f);
>         if (size < sizeof(offset)) {
>                 printf("write failed (%zx)\n", size);
> -               goto err_3;
> +               goto err_4;
>         }
>
>         image.version = 0x00000003;
> @@ -135,34 +455,53 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
>         image.reserved[1] = 0;
>         image.reserved[2] = 0;
>         image.update_image_size = bin_stat.st_size;
> +       if (auth_context.sig_size)
> +               image.update_image_size += sizeof(auth_context.auth)
> +                               + auth_context.sig_size;
>         image.update_vendor_code_size = 0; /* none */
>         image.update_hardware_instance = instance;
>         image.image_capsule_support = 0;
> +       if (auth_context.sig_size)
> +               image.image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION;
>
>         size = fwrite(&image, 1, sizeof(image), f);
>         if (size < sizeof(image)) {
>                 printf("write failed (%zx)\n", size);
> -               goto err_3;
> +               goto err_4;
>         }
> -       size = fread(data, 1, bin_stat.st_size, g);
> -       if (size < bin_stat.st_size) {
> -               printf("read failed (%zx)\n", size);
> -               goto err_3;
> +
> +       if (auth_context.sig_size) {
> +               size = fwrite(&auth_context.auth, 1,
> +                             sizeof(auth_context.auth), f);
> +               if (size < sizeof(auth_context.auth)) {
> +                       printf("write failed (%zx)\n", size);
> +                       goto err_4;
> +               }
> +               size = fwrite(auth_context.sig_data, 1,
> +                             auth_context.sig_size, f);
> +               if (size < auth_context.sig_size) {
> +                       printf("write failed (%zx)\n", size);
> +                       goto err_4;
> +               }
>         }
> +
>         size = fwrite(data, 1, bin_stat.st_size, f);
>         if (size < bin_stat.st_size) {
>                 printf("write failed (%zx)\n", size);
> -               goto err_3;
> +               goto err_4;
>         }
>
>         fclose(f);
>         fclose(g);
>         free(data);
> +       free_sig_data(&auth_context);
>
>         return 0;
>
> -err_3:
> +err_4:
>         fclose(f);
> +err_3:
> +       free_sig_data(&auth_context);
>  err_2:
>         free(data);
>  err_1:
> @@ -171,23 +510,37 @@ err_1:
>         return -1;
>  }
>
> -/*
> - * Usage:
> - *   $ mkeficapsule -f <firmware binary> <output file>
> +/**
> + * main - main entry function of mkeficapsule
> + * @argc:      Number of arguments
> + * @argv:      Array of pointers to arguments
> + *
> + * Create an uefi capsule file, optionally signing it.
> + * Parse all the arguments and pass them on to create_fwbin().
> + *
> + * Return:
> + * * 0  - on success
> + * * -1 - on failure
>   */
>  int main(int argc, char **argv)
>  {
>         char *file;
>         efi_guid_t *guid;
>         unsigned long index, instance;
> +       uint64_t mcount;
> +       char *privkey_file, *cert_file;
>         int c, idx;
>
>         file = NULL;
>         guid = NULL;
>         index = 0;
>         instance = 0;
> +       mcount = 0;
> +       privkey_file = NULL;
> +       cert_file = NULL;
> +       dump_sig = 0;
>         for (;;) {
> -               c = getopt_long(argc, argv, "f:r:i:I:v:h", options, &idx);
> +               c = getopt_long(argc, argv, opts_short, options, &idx);
>                 if (c == -1)
>                         break;
>
> @@ -214,29 +567,47 @@ int main(int argc, char **argv)
>                 case 'I':
>                         instance = strtoul(optarg, NULL, 0);
>                         break;
> +#ifdef CONFIG_TOOLS_LIBCRYPTO
> +               case 'p':
> +                       if (privkey_file) {
> +                               printf("Private Key already specified\n");
> +                               return -1;
> +                       }
> +                       privkey_file = optarg;
> +                       break;
> +               case 'c':
> +                       if (cert_file) {
> +                               printf("Certificate file already specified\n");
> +                               return -1;
> +                       }
> +                       cert_file = optarg;
> +                       break;
> +               case 'm':
> +                       mcount = strtoul(optarg, NULL, 0);
> +                       break;
> +               case 'd':
> +                       dump_sig = 1;
> +                       break;
> +#endif /* CONFIG_TOOLS_LIBCRYPTO */
>                 case 'h':
>                         print_usage();
>                         return 0;
>                 }
>         }
>
> -       /* need an output file */
> -       if (argc != optind + 1) {
> -               print_usage();
> -               exit(EXIT_FAILURE);
> -       }
> -
> -       /* need a fit image file or raw image file */
> -       if (!file) {
> +       /* check necessary parameters */
> +       if ((argc != optind + 1) || !file ||
> +           ((privkey_file && !cert_file) ||
> +            (!privkey_file && cert_file))) {
>                 print_usage();
> -               exit(EXIT_SUCCESS);
> +               return -1;
>         }
>
> -       if (create_fwbin(argv[optind], file, guid, index, instance)
> -                       < 0) {
> +       if (create_fwbin(argv[optind], file, guid, index, instance,
> +                        mcount, privkey_file, cert_file) < 0) {
>                 printf("Creating firmware capsule failed\n");
> -               exit(EXIT_FAILURE);
> +               return -1;
>         }
>
> -       exit(EXIT_SUCCESS);
> +       return 0;
>  }
> --
> 2.33.0
>


-- 
Masami Hiramatsu

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v4 03/11] efi_loader: capsule: add back efi_get_public_key_data()
  2021-10-15  0:40   ` Simon Glass
@ 2021-10-20  8:18     ` Masami Hiramatsu
  2021-10-20  9:08       ` François Ozog
  2021-10-20 13:39       ` Simon Glass
  0 siblings, 2 replies; 40+ messages in thread
From: Masami Hiramatsu @ 2021-10-20  8:18 UTC (permalink / raw)
  To: Simon Glass
  Cc: AKASHI Takahiro, Heinrich Schuchardt, Alex Graf,
	Ilias Apalodimas, Sughosh Ganu, U-Boot Mailing List

Hi Simon,

2021年10月15日(金) 9:40 Simon Glass <sjg@chromium.org>:
>
> Hi Takahiro,
>
> On Thu, 7 Oct 2021 at 00:25, AKASHI Takahiro <takahiro.akashi@linaro.org> wrote:
> >
> > The commit 47a25e81d35c ("Revert "efi_capsule: Move signature from DTB to
> > .rodata"") failed to revert the removal of efi_get_public_key_data().
> >
> > Add back this function and move it under lib/efi_loader so that other
> > platforms can utilize it. It is now declared as a weak function so that
> > it can be replaced with a platform-specific implementation.
> >
> > Fixes: 47a25e81d35c ("Revert "efi_capsule: Move signature from DTB to
> >         .rodata"")
> > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > ---
> >  lib/efi_loader/efi_capsule.c | 36 ++++++++++++++++++++++++++++++++++++
> >  1 file changed, 36 insertions(+)
> >
> > diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
> > index b75e4bcba1a9..44f5da61a9be 100644
> > --- a/lib/efi_loader/efi_capsule.c
> > +++ b/lib/efi_loader/efi_capsule.c
> > @@ -11,15 +11,20 @@
> >  #include <common.h>
> >  #include <efi_loader.h>
> >  #include <efi_variable.h>
> > +#include <env.h>
> > +#include <fdtdec.h>
> >  #include <fs.h>
> >  #include <malloc.h>
> >  #include <mapmem.h>
> >  #include <sort.h>
> > +#include <asm/global_data.h>
> >
> >  #include <crypto/pkcs7.h>
> >  #include <crypto/pkcs7_parser.h>
> >  #include <linux/err.h>
> >
> > +DECLARE_GLOBAL_DATA_PTR;
> > +
> >  const efi_guid_t efi_guid_capsule_report = EFI_CAPSULE_REPORT_GUID;
> >  static const efi_guid_t efi_guid_firmware_management_capsule_id =
> >                 EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID;
> > @@ -251,6 +256,37 @@ out:
> >  }
> >
> >  #if defined(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> > +int __weak efi_get_public_key_data(void **pkey, efi_uintn_t *pkey_len)
>
> I don't think this should be weak. What other way is there of handling
> this and why would it be platform-specific?

I have a question about the current design of the capsule auth key.
If the platform has its own key-storage, how can the platform use the
platform specific storage? Does such platform load the key from the storage
and generate the dtb node in the platform initialization code? (or
device driver?)

Thank you,


>
> > +{
> > +       const void *fdt_blob = gd->fdt_blob;
> > +       const void *blob;
> > +       const char *cnode_name = "capsule-key";
> > +       const char *snode_name = "signature";
> > +       int sig_node;
> > +       int len;
> > +
> > +       sig_node = fdt_subnode_offset(fdt_blob, 0, snode_name);
> > +       if (sig_node < 0) {
> > +               log_err("Unable to get signature node offset\n");
> > +
> > +               return -FDT_ERR_NOTFOUND;
> > +       }
> > +
> > +       blob = fdt_getprop(fdt_blob, sig_node, cnode_name, &len);
> > +
> > +       if (!blob || len < 0) {
> > +               log_err("Unable to get capsule-key value\n");
> > +               *pkey = NULL;
> > +               *pkey_len = 0;
> > +
> > +               return -FDT_ERR_NOTFOUND;
> > +       }
> > +
> > +       *pkey = (void *)blob;
> > +       *pkey_len = len;
> > +
> > +       return 0;
> > +}
> >
> >  efi_status_t efi_capsule_authenticate(const void *capsule, efi_uintn_t capsule_size,
> >                                       void **image, efi_uintn_t *image_size)
> > --
> > 2.33.0
> >
>
> Regards,
> Simon



--
Masami Hiramatsu

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v4 03/11] efi_loader: capsule: add back efi_get_public_key_data()
  2021-10-20  8:18     ` Masami Hiramatsu
@ 2021-10-20  9:08       ` François Ozog
  2021-10-20 13:39       ` Simon Glass
  1 sibling, 0 replies; 40+ messages in thread
From: François Ozog @ 2021-10-20  9:08 UTC (permalink / raw)
  To: Masami Hiramatsu
  Cc: AKASHI Takahiro, Alex Graf, Heinrich Schuchardt,
	Ilias Apalodimas, Simon Glass, Sughosh Ganu, U-Boot Mailing List

Le mer. 20 oct. 2021 à 10:18, Masami Hiramatsu <masami.hiramatsu@linaro.org>
a écrit :

> Hi Simon,
>
> 2021年10月15日(金) 9:40 Simon Glass <sjg@chromium.org>:
> >
> > Hi Takahiro,
> >
> > On Thu, 7 Oct 2021 at 00:25, AKASHI Takahiro <takahiro.akashi@linaro.org>
> wrote:
> > >
> > > The commit 47a25e81d35c ("Revert "efi_capsule: Move signature from DTB
> to
> > > .rodata"") failed to revert the removal of efi_get_public_key_data().
> > >
> > > Add back this function and move it under lib/efi_loader so that other
> > > platforms can utilize it. It is now declared as a weak function so that
> > > it can be replaced with a platform-specific implementation.
> > >
> > > Fixes: 47a25e81d35c ("Revert "efi_capsule: Move signature from DTB to
> > >         .rodata"")
> > > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > > ---
> > >  lib/efi_loader/efi_capsule.c | 36 ++++++++++++++++++++++++++++++++++++
> > >  1 file changed, 36 insertions(+)
> > >
> > > diff --git a/lib/efi_loader/efi_capsule.c
> b/lib/efi_loader/efi_capsule.c
> > > index b75e4bcba1a9..44f5da61a9be 100644
> > > --- a/lib/efi_loader/efi_capsule.c
> > > +++ b/lib/efi_loader/efi_capsule.c
> > > @@ -11,15 +11,20 @@
> > >  #include <common.h>
> > >  #include <efi_loader.h>
> > >  #include <efi_variable.h>
> > > +#include <env.h>
> > > +#include <fdtdec.h>
> > >  #include <fs.h>
> > >  #include <malloc.h>
> > >  #include <mapmem.h>
> > >  #include <sort.h>
> > > +#include <asm/global_data.h>
> > >
> > >  #include <crypto/pkcs7.h>
> > >  #include <crypto/pkcs7_parser.h>
> > >  #include <linux/err.h>
> > >
> > > +DECLARE_GLOBAL_DATA_PTR;
> > > +
> > >  const efi_guid_t efi_guid_capsule_report = EFI_CAPSULE_REPORT_GUID;
> > >  static const efi_guid_t efi_guid_firmware_management_capsule_id =
> > >                 EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID;
> > > @@ -251,6 +256,37 @@ out:
> > >  }
> > >
> > >  #if defined(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> > > +int __weak efi_get_public_key_data(void **pkey, efi_uintn_t *pkey_len)
> >
> > I don't think this should be weak. What other way is there of handling
> > this and why would it be platform-specific?
>
> I have a question about the current design of the capsule auth key.
> If the platform has its own key-storage, how can the platform use the
> platform specific storage? Does such platform load the key from the storage
> and generate the dtb node in the platform initialization code? (or
> device driver?)

it depends on what the capsule contains.

If the capsule contains SCP firmware or secure firmware or TAs, U-Boot may
not be even allowed to see the key.
If the capsule contains U-Boot itself it may be again outside scope of
U-Boot because that may be secure firmware that verifies the signature. We
may allow U-Boot to update itself but the final say is the secure firmware
that may prevent the boot.
If the capsule contains device firmware then it may depend on the device:
secure device U-Boot can do anything, otherwise then it is to be decided by
U-Boot.


>
> Thank you,
>
>
> >
> > > +{
> > > +       const void *fdt_blob = gd->fdt_blob;
> > > +       const void *blob;
> > > +       const char *cnode_name = "capsule-key";
> > > +       const char *snode_name = "signature";
> > > +       int sig_node;
> > > +       int len;
> > > +
> > > +       sig_node = fdt_subnode_offset(fdt_blob, 0, snode_name);
> > > +       if (sig_node < 0) {
> > > +               log_err("Unable to get signature node offset\n");
> > > +
> > > +               return -FDT_ERR_NOTFOUND;
> > > +       }
> > > +
> > > +       blob = fdt_getprop(fdt_blob, sig_node, cnode_name, &len);
> > > +
> > > +       if (!blob || len < 0) {
> > > +               log_err("Unable to get capsule-key value\n");
> > > +               *pkey = NULL;
> > > +               *pkey_len = 0;
> > > +
> > > +               return -FDT_ERR_NOTFOUND;
> > > +       }
> > > +
> > > +       *pkey = (void *)blob;
> > > +       *pkey_len = len;
> > > +
> > > +       return 0;
> > > +}
> > >
> > >  efi_status_t efi_capsule_authenticate(const void *capsule,
> efi_uintn_t capsule_size,
> > >                                       void **image, efi_uintn_t
> *image_size)
> > > --
> > > 2.33.0
> > >
> >
> > Regards,
> > Simon
>
>
>
> --
> Masami Hiramatsu
>
-- 
François-Frédéric Ozog | *Director Business Development*
T: +33.67221.6485
francois.ozog@linaro.org | Skype: ffozog

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v4 03/11] efi_loader: capsule: add back efi_get_public_key_data()
  2021-10-20  8:18     ` Masami Hiramatsu
  2021-10-20  9:08       ` François Ozog
@ 2021-10-20 13:39       ` Simon Glass
  2021-10-25  5:14         ` AKASHI Takahiro
  1 sibling, 1 reply; 40+ messages in thread
From: Simon Glass @ 2021-10-20 13:39 UTC (permalink / raw)
  To: Masami Hiramatsu
  Cc: AKASHI Takahiro, Heinrich Schuchardt, Alex Graf,
	Ilias Apalodimas, Sughosh Ganu, U-Boot Mailing List

Hi Masami,

On Wed, 20 Oct 2021 at 02:18, Masami Hiramatsu
<masami.hiramatsu@linaro.org> wrote:
>
> Hi Simon,
>
> 2021年10月15日(金) 9:40 Simon Glass <sjg@chromium.org>:
> >
> > Hi Takahiro,
> >
> > On Thu, 7 Oct 2021 at 00:25, AKASHI Takahiro <takahiro.akashi@linaro.org> wrote:
> > >
> > > The commit 47a25e81d35c ("Revert "efi_capsule: Move signature from DTB to
> > > .rodata"") failed to revert the removal of efi_get_public_key_data().
> > >
> > > Add back this function and move it under lib/efi_loader so that other
> > > platforms can utilize it. It is now declared as a weak function so that
> > > it can be replaced with a platform-specific implementation.
> > >
> > > Fixes: 47a25e81d35c ("Revert "efi_capsule: Move signature from DTB to
> > >         .rodata"")
> > > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > > ---
> > >  lib/efi_loader/efi_capsule.c | 36 ++++++++++++++++++++++++++++++++++++
> > >  1 file changed, 36 insertions(+)
> > >
> > > diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
> > > index b75e4bcba1a9..44f5da61a9be 100644
> > > --- a/lib/efi_loader/efi_capsule.c
> > > +++ b/lib/efi_loader/efi_capsule.c
> > > @@ -11,15 +11,20 @@
> > >  #include <common.h>
> > >  #include <efi_loader.h>
> > >  #include <efi_variable.h>
> > > +#include <env.h>
> > > +#include <fdtdec.h>
> > >  #include <fs.h>
> > >  #include <malloc.h>
> > >  #include <mapmem.h>
> > >  #include <sort.h>
> > > +#include <asm/global_data.h>
> > >
> > >  #include <crypto/pkcs7.h>
> > >  #include <crypto/pkcs7_parser.h>
> > >  #include <linux/err.h>
> > >
> > > +DECLARE_GLOBAL_DATA_PTR;
> > > +
> > >  const efi_guid_t efi_guid_capsule_report = EFI_CAPSULE_REPORT_GUID;
> > >  static const efi_guid_t efi_guid_firmware_management_capsule_id =
> > >                 EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID;
> > > @@ -251,6 +256,37 @@ out:
> > >  }
> > >
> > >  #if defined(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> > > +int __weak efi_get_public_key_data(void **pkey, efi_uintn_t *pkey_len)
> >
> > I don't think this should be weak. What other way is there of handling
> > this and why would it be platform-specific?
>
> I have a question about the current design of the capsule auth key.
> If the platform has its own key-storage, how can the platform use the
> platform specific storage? Does such platform load the key from the storage
> and generate the dtb node in the platform initialization code? (or
> device driver?)

Are we talking about a public key (which I assume from the function
naming) or some secret key. What is an auth key?

As I understand it, the public key should be provided by the platform
in devicetree that U-Boot uses, by whatever prior stage has access to
the key.

Regards,
Simon

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v4 04/11] tools: add fdtsig.sh
  2021-10-15  0:40       ` Simon Glass
@ 2021-10-25  3:06         ` AKASHI Takahiro
  2021-10-26  6:00           ` AKASHI Takahiro
  0 siblings, 1 reply; 40+ messages in thread
From: AKASHI Takahiro @ 2021-10-25  3:06 UTC (permalink / raw)
  To: Simon Glass
  Cc: Heinrich Schuchardt, Alex Graf, Ilias Apalodimas, Sughosh Ganu,
	Masami Hiramatsu, U-Boot Mailing List

Simon,

On Thu, Oct 14, 2021 at 06:40:24PM -0600, Simon Glass wrote:
> Hi Takahiro,
> 
> On Mon, 11 Oct 2021 at 19:42, AKASHI Takahiro
> <takahiro.akashi@linaro.org> wrote:
> >
> > Simon,
> >
> > On Mon, Oct 11, 2021 at 08:54:09AM -0600, Simon Glass wrote:
> > > Hi Takahiro,
> > >
> > > On Thu, 7 Oct 2021 at 00:25, AKASHI Takahiro <takahiro.akashi@linaro.org> wrote:
> > > >
> > > > With this script, a public key is added to a device tree blob
> > > > as the default efi_get_public_key_data() expects.
> > > >
> > > > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > > > ---
> > > >  MAINTAINERS     |  1 +
> > > >  tools/fdtsig.sh | 40 ++++++++++++++++++++++++++++++++++++++++
> > > >  2 files changed, 41 insertions(+)
> > > >  create mode 100755 tools/fdtsig.sh
> > >
> > > Instead of an ad-hoc script with no tests,
> >
> > Basically I intended to provide fdtsig.sh as a *sample* script so that
> > people may want to integrate the logic into their own build rule/systems.
> > But I could use this script in my 'capsule authentication' test
> > that is also added in patch#22.
> >
> > > could we use binman for
> > > putting the image together and inserting it?
> >
> > First, as you can see, the script is quite simple and secondly,
> > the purpose of binman, IIUC, is to help handle/manipulate U-Boot
> > image binaries.
> > So I'm not sure whether it is really useful to add such a feature to binman.
> 
> I'm not sure. The script seems very ad-hoc to me, for a feature that
> Linaro is pushing so hard.

To be honest, I've never used binman :) So I'm not sure whether binman
is the best place to add this feature. For example, README under tools/binman
says, "It seems better to use the mkimage tool to generate binaries and avoid
blurring the boundaries between building input files (mkimage) and packaging
then into a final image (binman)."
Obviously, dtb is not the final image.

> I don't see where the script is used in the tests or even mentioned in
> the documentation. Am I missing something?

Due to the history of submissions of this series, the current pytest
scenario doesn't use the script, but you can see the exact same
sequence of commands at test/py/tests/test_efi_capsule/conftest.py:
---8<---
        # Update dtb adding capsule certificate
        check_call('cd %s; cp %s/test/py/tests/test_efi_capsule/signature.dts .'
                   % (data_dir, u_boot_config.source_dir), shell=True)
        check_call('cd %s; dtc -@ -I dts -O dtb -o signature.dtbo signature.dts; fdtoverlay -i %s/arch/sandbox/dts/test.dtb -o test_sig.dtb signature.dtbo'
                   % (data_dir, u_boot_config.build_dir), shell=True)
--->8---
(Please see my patch#11.)

What I meant is that we can directly use fdtsig.sh here if your concern
is that the script is *not exercised* anywhere.

-Takahiro Akashi

> Regards,
> Simon

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v4 05/11] tools: mkeficapsule: add firmwware image signing
  2021-10-20  8:17   ` Masami Hiramatsu
@ 2021-10-25  3:12     ` AKASHI Takahiro
  2021-10-25  5:40       ` Masami Hiramatsu
  0 siblings, 1 reply; 40+ messages in thread
From: AKASHI Takahiro @ 2021-10-25  3:12 UTC (permalink / raw)
  To: Masami Hiramatsu
  Cc: Heinrich Schuchardt, Alexander Graf, Ilias Apalodimas,
	Sughosh Ganu, U-Boot Mailing List

Hi, Masami,

On Wed, Oct 20, 2021 at 05:17:12PM +0900, Masami Hiramatsu wrote:
> Hello Akashi-san,
> 
> Can you split this patch out from this series?
> It seems that the series is doing several different things. This
> capsule signing has no alternatives, but others are modifying or
> replacing the current existing feature. In other words, this capsule
> signing feature has no alternatives, but others we can continue to
> use.

What do you specifically mean by "existing feature" or "others we can
continue to use"?

All the commits, I think, in this series are closely related with
each other.

-Takahiro Akashi

> Thank you,
> 
> 2021年10月7日(木) 15:25 AKASHI Takahiro <takahiro.akashi@linaro.org>:
> >
> > With this enhancement, mkeficapsule will be able to sign a capsule
> > file when it is created. A signature added will be used later
> > in the verification at FMP's SetImage() call.
> >
> > To do that, We need specify additional command parameters:
> >   -monotonic-cout <count> : monotonic count
> >   -private-key <private key file> : private key file
> >   -certificate <certificate file> : certificate file
> > Only when all of those parameters are given, a signature will be added
> > to a capsule file.
> >
> > Users are expected to maintain and increment the monotonic count at
> > every time of the update for each firmware image.
> >
> > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > ---
> >  tools/Kconfig        |   7 +
> >  tools/Makefile       |   8 +-
> >  tools/mkeficapsule.c | 435 +++++++++++++++++++++++++++++++++++++++----
> >  3 files changed, 416 insertions(+), 34 deletions(-)
> >
> > diff --git a/tools/Kconfig b/tools/Kconfig
> > index d6f82cd949b5..9a37ed035311 100644
> > --- a/tools/Kconfig
> > +++ b/tools/Kconfig
> > @@ -20,4 +20,11 @@ config TOOLS_LIBCRYPTO
> >           This selection does not affect target features, such as runtime FIT
> >           signature verification.
> >
> > +config TOOLS_MKEFICAPSULE
> > +       bool "Build efimkcapsule command"
> > +       default y if EFI_CAPSULE_ON_DISK
> > +       help
> > +         This command allows users to create a UEFI capsule file and,
> > +         optionally sign that file. If you want to enable UEFI capsule
> > +         update feature on your target, you certainly need this.
> >  endmenu
> > diff --git a/tools/Makefile b/tools/Makefile
> > index 4a86321f6467..6ea3033dbfb8 100644
> > --- a/tools/Makefile
> > +++ b/tools/Makefile
> > @@ -237,8 +237,12 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs
> >  hostprogs-$(CONFIG_ASN1_COMPILER)      += asn1_compiler
> >  HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
> >
> > -mkeficapsule-objs      := mkeficapsule.o $(LIBFDT_OBJS)
> > -hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
> > +HOSTLDLIBS_mkeficapsule += -luuid
> > +ifeq ($(CONFIG_TOOLS_LIBCRYPTO),y)
> > +HOSTLDLIBS_mkeficapsule += \
> > +       $(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto")
> > +endif
> > +hostprogs-$(CONFIG_TOOLS_MKEFICAPSULE) += mkeficapsule
> >
> >  # We build some files with extra pedantic flags to try to minimize things
> >  # that won't build on some weird host compiler -- though there are lots of
> > diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
> > index 4995ba4e0c2a..5541e4bda894 100644
> > --- a/tools/mkeficapsule.c
> > +++ b/tools/mkeficapsule.c
> > @@ -15,6 +15,16 @@
> >  #include <sys/stat.h>
> >  #include <sys/types.h>
> >
> > +#include <linux/kconfig.h>
> > +#ifdef CONFIG_TOOLS_LIBCRYPTO
> > +#include <openssl/asn1.h>
> > +#include <openssl/bio.h>
> > +#include <openssl/evp.h>
> > +#include <openssl/err.h>
> > +#include <openssl/pem.h>
> > +#include <openssl/pkcs7.h>
> > +#endif
> > +
> >  typedef __u8 u8;
> >  typedef __u16 u16;
> >  typedef __u32 u32;
> > @@ -38,12 +48,25 @@ efi_guid_t efi_guid_image_type_uboot_fit =
> >                 EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID;
> >  efi_guid_t efi_guid_image_type_uboot_raw =
> >                 EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID;
> > +efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
> > +
> > +#ifdef CONFIG_TOOLS_LIBCRYPTO
> > +static const char *opts_short = "f:r:i:I:v:p:c:m:dh";
> > +#else
> > +static const char *opts_short = "f:r:i:I:v:h";
> > +#endif
> >
> >  static struct option options[] = {
> >         {"fit", required_argument, NULL, 'f'},
> >         {"raw", required_argument, NULL, 'r'},
> >         {"index", required_argument, NULL, 'i'},
> >         {"instance", required_argument, NULL, 'I'},
> > +#ifdef CONFIG_TOOLS_LIBCRYPTO
> > +       {"private-key", required_argument, NULL, 'p'},
> > +       {"certificate", required_argument, NULL, 'c'},
> > +       {"monotonic-count", required_argument, NULL, 'm'},
> > +       {"dump-sig", no_argument, NULL, 'd'},
> > +#endif
> >         {"help", no_argument, NULL, 'h'},
> >         {NULL, 0, NULL, 0},
> >  };
> > @@ -57,16 +80,280 @@ static void print_usage(void)
> >                "\t-r, --raw <raw image>       new raw image file\n"
> >                "\t-i, --index <index>         update image index\n"
> >                "\t-I, --instance <instance>   update hardware instance\n"
> > +#ifdef CONFIG_TOOLS_LIBCRYPTO
> > +              "\t-p, --private-key <privkey file>  private key file\n"
> > +              "\t-c, --certificate <cert file>     signer's certificate file\n"
> > +              "\t-m, --monotonic-count <count>     monotonic count\n"
> > +              "\t-d, --dump_sig              dump signature (*.p7)\n"
> > +#endif
> >                "\t-h, --help                  print a help message\n",
> >                tool_name);
> >  }
> >
> > +/**
> > + * auth_context - authentication context
> > + * @key_file:  Path to a private key file
> > + * @cert_file: Path to a certificate file
> > + * @image_data:        Pointer to firmware data
> > + * @image_size:        Size of firmware data
> > + * @auth:      Authentication header
> > + * @sig_data:  Signature data
> > + * @sig_size:  Size of signature data
> > + *
> > + * Data structure used in create_auth_data(). @key_file through
> > + * @image_size are input parameters. @auth, @sig_data and @sig_size
> > + * are filled in by create_auth_data().
> > + */
> > +struct auth_context {
> > +       char *key_file;
> > +       char *cert_file;
> > +       u8 *image_data;
> > +       size_t image_size;
> > +       struct efi_firmware_image_authentication auth;
> > +       u8 *sig_data;
> > +       size_t sig_size;
> > +};
> > +
> > +static int dump_sig;
> > +
> > +#ifdef CONFIG_TOOLS_LIBCRYPTO
> > +/**
> > + * fileio-read_pkey - read out a private key
> > + * @filename:  Path to a private key file
> > + *
> > + * Read out a private key file and parse it into "EVP_PKEY" structure.
> > + *
> > + * Return:
> > + * * Pointer to private key structure  - on success
> > + * * NULL - on failure
> > + */
> > +static EVP_PKEY *fileio_read_pkey(const char *filename)
> > +{
> > +       EVP_PKEY *key = NULL;
> > +       BIO *bio;
> > +
> > +       bio = BIO_new_file(filename, "r");
> > +       if (!bio)
> > +               goto out;
> > +
> > +       key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
> > +
> > +out:
> > +       BIO_free_all(bio);
> > +       if (!key) {
> > +               printf("Can't load key from file '%s'\n", filename);
> > +               ERR_print_errors_fp(stderr);
> > +       }
> > +
> > +       return key;
> > +}
> > +
> > +/**
> > + * fileio-read_cert - read out a certificate
> > + * @filename:  Path to a certificate file
> > + *
> > + * Read out a certificate file and parse it into "X509" structure.
> > + *
> > + * Return:
> > + * * Pointer to certificate structure  - on success
> > + * * NULL - on failure
> > + */
> > +static X509 *fileio_read_cert(const char *filename)
> > +{
> > +       X509 *cert = NULL;
> > +       BIO *bio;
> > +
> > +       bio = BIO_new_file(filename, "r");
> > +       if (!bio)
> > +               goto out;
> > +
> > +       cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
> > +
> > +out:
> > +       BIO_free_all(bio);
> > +       if (!cert) {
> > +               printf("Can't load certificate from file '%s'\n", filename);
> > +               ERR_print_errors_fp(stderr);
> > +       }
> > +
> > +       return cert;
> > +}
> > +
> > +/**
> > + * create_auth_data - compose authentication data in capsule
> > + * @auth_context:      Pointer to authentication context
> > + *
> > + * Fill up an authentication header (.auth) and signature data (.sig_data)
> > + * in @auth_context, using library functions from openssl.
> > + * All the parameters in @auth_context must be filled in by a caller.
> > + *
> > + * Return:
> > + * * 0  - on success
> > + * * -1 - on failure
> > + */
> > +static int create_auth_data(struct auth_context *ctx)
> > +{
> > +       EVP_PKEY *key = NULL;
> > +       X509 *cert = NULL;
> > +       BIO *data_bio = NULL;
> > +       const EVP_MD *md;
> > +       PKCS7 *p7;
> > +       int flags, ret = -1;
> > +
> > +       OpenSSL_add_all_digests();
> > +       OpenSSL_add_all_ciphers();
> > +       ERR_load_crypto_strings();
> > +
> > +       key = fileio_read_pkey(ctx->key_file);
> > +       if (!key)
> > +               goto err;
> > +       cert = fileio_read_cert(ctx->cert_file);
> > +       if (!cert)
> > +               goto err;
> > +
> > +       /*
> > +        * create a BIO, containing:
> > +        *  * firmware image
> > +        *  * monotonic count
> > +        * in this order!
> > +        * See EDK2's FmpAuthenticatedHandlerRsa2048Sha256()
> > +        */
> > +       data_bio = BIO_new(BIO_s_mem());
> > +       BIO_write(data_bio, ctx->image_data, ctx->image_size);
> > +       BIO_write(data_bio, &ctx->auth.monotonic_count,
> > +                 sizeof(ctx->auth.monotonic_count));
> > +
> > +       md = EVP_get_digestbyname("SHA256");
> > +       if (!md)
> > +               goto err;
> > +
> > +       /* create signature */
> > +       /* TODO: maybe add PKCS7_NOATTR and PKCS7_NOSMIMECAP */
> > +       flags = PKCS7_BINARY | PKCS7_DETACHED;
> > +       p7 = PKCS7_sign(NULL, NULL, NULL, data_bio, flags | PKCS7_PARTIAL);
> > +       if (!p7)
> > +               goto err;
> > +       if (!PKCS7_sign_add_signer(p7, cert, key, md, flags))
> > +               goto err;
> > +       if (!PKCS7_final(p7, data_bio, flags))
> > +               goto err;
> > +
> > +       /* convert pkcs7 into DER */
> > +       ctx->sig_data = NULL;
> > +       ctx->sig_size = ASN1_item_i2d((ASN1_VALUE *)p7, &ctx->sig_data,
> > +                                     ASN1_ITEM_rptr(PKCS7));
> > +       if (!ctx->sig_size)
> > +               goto err;
> > +
> > +       /* fill auth_info */
> > +       ctx->auth.auth_info.hdr.dwLength = sizeof(ctx->auth.auth_info)
> > +                                               + ctx->sig_size;
> > +       ctx->auth.auth_info.hdr.wRevision = WIN_CERT_REVISION_2_0;
> > +       ctx->auth.auth_info.hdr.wCertificateType = WIN_CERT_TYPE_EFI_GUID;
> > +       memcpy(&ctx->auth.auth_info.cert_type, &efi_guid_cert_type_pkcs7,
> > +              sizeof(efi_guid_cert_type_pkcs7));
> > +
> > +       ret = 0;
> > +err:
> > +       BIO_free_all(data_bio);
> > +       EVP_PKEY_free(key);
> > +       X509_free(cert);
> > +
> > +       return ret;
> > +}
> > +
> > +/**
> > + * dump_signature - dump out a signature
> > + * @path:      Path to a capsule file
> > + * @signature: Signature data
> > + * @sig_size:  Size of signature data
> > + *
> > + * Signature data pointed to by @signature will be saved into
> > + * a file whose file name is @path with ".p7" suffix.
> > + *
> > + * Return:
> > + * * 0  - on success
> > + * * -1 - on failure
> > + */
> > +static int dump_signature(const char *path, u8 *signature, size_t sig_size)
> > +{
> > +       char *sig_path;
> > +       FILE *f;
> > +       size_t size;
> > +       int ret = -1;
> > +
> > +       sig_path = malloc(strlen(path) + 3 + 1);
> > +       if (!sig_path)
> > +               return ret;
> > +
> > +       sprintf(sig_path, "%s.p7", path);
> > +       f = fopen(sig_path, "w");
> > +       if (!f)
> > +               goto err;
> > +
> > +       size = fwrite(signature, 1, sig_size, f);
> > +       if (size == sig_size)
> > +               ret = 0;
> > +
> > +       fclose(f);
> > +err:
> > +       free(sig_path);
> > +       return ret;
> > +}
> > +
> > +/**
> > + * free_sig_data - free out signature data
> > + * @ctx:       Pointer to authentication context
> > + *
> > + * Free signature data allocated in create_auth_data().
> > + */
> > +static void free_sig_data(struct auth_context *ctx)
> > +{
> > +       if (ctx->sig_size)
> > +               OPENSSL_free(ctx->sig_data);
> > +}
> > +#else
> > +static int create_auth_data(struct auth_context *ctx)
> > +{
> > +       return 0;
> > +}
> > +
> > +static int dump_signature(const char *path, u8 *signature, size_t sig_size)
> > +{
> > +       return 0;
> > +}
> > +
> > +static void free_sig_data(struct auth_context *ctx) {}
> > +#endif
> > +
> > +/**
> > + * create_fwbin - create an uefi capsule file
> > + * @path:      Path to a created capsule file
> > + * @bin:       Path to a firmware binary to encapsulate
> > + * @guid:      GUID of related FMP driver
> > + * @index:     Index number in capsule
> > + * @instance:  Instance number in capsule
> > + * @mcount:    Monotonic count in authentication information
> > + * @private_file:      Path to a private key file
> > + * @cert_file: Path to a certificate file
> > + *
> > + * This function actually does the job of creating an uefi capsule file.
> > + * All the arguments must be supplied.
> > + * If either @private_file ror @cert_file is NULL, the capsule file
> > + * won't be signed.
> > + *
> > + * Return:
> > + * * 0  - on success
> > + * * -1 - on failure
> > + */
> >  static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > -                       unsigned long index, unsigned long instance)
> > +                       unsigned long index, unsigned long instance,
> > +                       uint64_t mcount, char *privkey_file, char *cert_file)
> >  {
> >         struct efi_capsule_header header;
> >         struct efi_firmware_management_capsule_header capsule;
> >         struct efi_firmware_management_capsule_image_header image;
> > +       struct auth_context auth_context;
> >         FILE *f, *g;
> >         struct stat bin_stat;
> >         u8 *data;
> > @@ -76,8 +363,9 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> >  #ifdef DEBUG
> >         printf("For output: %s\n", path);
> >         printf("\tbin: %s\n\ttype: %pUl\n", bin, guid);
> > -       printf("\tindex: %ld\n\tinstance: %ld\n", index, instance);
> > +       printf("\tindex: %lu\n\tinstance: %lu\n", index, instance);
> >  #endif
> > +       auth_context.sig_size = 0;
> >
> >         g = fopen(bin, "r");
> >         if (!g) {
> > @@ -93,11 +381,34 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> >                 printf("cannot allocate memory: %zx\n", (size_t)bin_stat.st_size);
> >                 goto err_1;
> >         }
> > -       f = fopen(path, "w");
> > -       if (!f) {
> > -               printf("cannot open %s\n", path);
> > +
> > +       size = fread(data, 1, bin_stat.st_size, g);
> > +       if (size < bin_stat.st_size) {
> > +               printf("read failed (%zx)\n", size);
> >                 goto err_2;
> >         }
> > +
> > +       /* first, calculate signature to determine its size */
> > +       if (privkey_file && cert_file) {
> > +               auth_context.key_file = privkey_file;
> > +               auth_context.cert_file = cert_file;
> > +               auth_context.auth.monotonic_count = mcount;
> > +               auth_context.image_data = data;
> > +               auth_context.image_size = bin_stat.st_size;
> > +
> > +               if (create_auth_data(&auth_context)) {
> > +                       printf("Signing firmware image failed\n");
> > +                       goto err_3;
> > +               }
> > +
> > +               if (dump_sig &&
> > +                   dump_signature(path, auth_context.sig_data,
> > +                                  auth_context.sig_size)) {
> > +                       printf("Creating signature file failed\n");
> > +                       goto err_3;
> > +               }
> > +       }
> > +
> >         header.capsule_guid = efi_guid_fm_capsule;
> >         header.header_size = sizeof(header);
> >         /* TODO: The current implementation ignores flags */
> > @@ -106,11 +417,20 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> >                                         + sizeof(capsule) + sizeof(u64)
> >                                         + sizeof(image)
> >                                         + bin_stat.st_size;
> > +       if (auth_context.sig_size)
> > +               header.capsule_image_size += sizeof(auth_context.auth)
> > +                               + auth_context.sig_size;
> > +
> > +       f = fopen(path, "w");
> > +       if (!f) {
> > +               printf("cannot open %s\n", path);
> > +               goto err_3;
> > +       }
> >
> >         size = fwrite(&header, 1, sizeof(header), f);
> >         if (size < sizeof(header)) {
> >                 printf("write failed (%zx)\n", size);
> > -               goto err_3;
> > +               goto err_4;
> >         }
> >
> >         capsule.version = 0x00000001;
> > @@ -119,13 +439,13 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> >         size = fwrite(&capsule, 1, sizeof(capsule), f);
> >         if (size < (sizeof(capsule))) {
> >                 printf("write failed (%zx)\n", size);
> > -               goto err_3;
> > +               goto err_4;
> >         }
> >         offset = sizeof(capsule) + sizeof(u64);
> >         size = fwrite(&offset, 1, sizeof(offset), f);
> >         if (size < sizeof(offset)) {
> >                 printf("write failed (%zx)\n", size);
> > -               goto err_3;
> > +               goto err_4;
> >         }
> >
> >         image.version = 0x00000003;
> > @@ -135,34 +455,53 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> >         image.reserved[1] = 0;
> >         image.reserved[2] = 0;
> >         image.update_image_size = bin_stat.st_size;
> > +       if (auth_context.sig_size)
> > +               image.update_image_size += sizeof(auth_context.auth)
> > +                               + auth_context.sig_size;
> >         image.update_vendor_code_size = 0; /* none */
> >         image.update_hardware_instance = instance;
> >         image.image_capsule_support = 0;
> > +       if (auth_context.sig_size)
> > +               image.image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION;
> >
> >         size = fwrite(&image, 1, sizeof(image), f);
> >         if (size < sizeof(image)) {
> >                 printf("write failed (%zx)\n", size);
> > -               goto err_3;
> > +               goto err_4;
> >         }
> > -       size = fread(data, 1, bin_stat.st_size, g);
> > -       if (size < bin_stat.st_size) {
> > -               printf("read failed (%zx)\n", size);
> > -               goto err_3;
> > +
> > +       if (auth_context.sig_size) {
> > +               size = fwrite(&auth_context.auth, 1,
> > +                             sizeof(auth_context.auth), f);
> > +               if (size < sizeof(auth_context.auth)) {
> > +                       printf("write failed (%zx)\n", size);
> > +                       goto err_4;
> > +               }
> > +               size = fwrite(auth_context.sig_data, 1,
> > +                             auth_context.sig_size, f);
> > +               if (size < auth_context.sig_size) {
> > +                       printf("write failed (%zx)\n", size);
> > +                       goto err_4;
> > +               }
> >         }
> > +
> >         size = fwrite(data, 1, bin_stat.st_size, f);
> >         if (size < bin_stat.st_size) {
> >                 printf("write failed (%zx)\n", size);
> > -               goto err_3;
> > +               goto err_4;
> >         }
> >
> >         fclose(f);
> >         fclose(g);
> >         free(data);
> > +       free_sig_data(&auth_context);
> >
> >         return 0;
> >
> > -err_3:
> > +err_4:
> >         fclose(f);
> > +err_3:
> > +       free_sig_data(&auth_context);
> >  err_2:
> >         free(data);
> >  err_1:
> > @@ -171,23 +510,37 @@ err_1:
> >         return -1;
> >  }
> >
> > -/*
> > - * Usage:
> > - *   $ mkeficapsule -f <firmware binary> <output file>
> > +/**
> > + * main - main entry function of mkeficapsule
> > + * @argc:      Number of arguments
> > + * @argv:      Array of pointers to arguments
> > + *
> > + * Create an uefi capsule file, optionally signing it.
> > + * Parse all the arguments and pass them on to create_fwbin().
> > + *
> > + * Return:
> > + * * 0  - on success
> > + * * -1 - on failure
> >   */
> >  int main(int argc, char **argv)
> >  {
> >         char *file;
> >         efi_guid_t *guid;
> >         unsigned long index, instance;
> > +       uint64_t mcount;
> > +       char *privkey_file, *cert_file;
> >         int c, idx;
> >
> >         file = NULL;
> >         guid = NULL;
> >         index = 0;
> >         instance = 0;
> > +       mcount = 0;
> > +       privkey_file = NULL;
> > +       cert_file = NULL;
> > +       dump_sig = 0;
> >         for (;;) {
> > -               c = getopt_long(argc, argv, "f:r:i:I:v:h", options, &idx);
> > +               c = getopt_long(argc, argv, opts_short, options, &idx);
> >                 if (c == -1)
> >                         break;
> >
> > @@ -214,29 +567,47 @@ int main(int argc, char **argv)
> >                 case 'I':
> >                         instance = strtoul(optarg, NULL, 0);
> >                         break;
> > +#ifdef CONFIG_TOOLS_LIBCRYPTO
> > +               case 'p':
> > +                       if (privkey_file) {
> > +                               printf("Private Key already specified\n");
> > +                               return -1;
> > +                       }
> > +                       privkey_file = optarg;
> > +                       break;
> > +               case 'c':
> > +                       if (cert_file) {
> > +                               printf("Certificate file already specified\n");
> > +                               return -1;
> > +                       }
> > +                       cert_file = optarg;
> > +                       break;
> > +               case 'm':
> > +                       mcount = strtoul(optarg, NULL, 0);
> > +                       break;
> > +               case 'd':
> > +                       dump_sig = 1;
> > +                       break;
> > +#endif /* CONFIG_TOOLS_LIBCRYPTO */
> >                 case 'h':
> >                         print_usage();
> >                         return 0;
> >                 }
> >         }
> >
> > -       /* need an output file */
> > -       if (argc != optind + 1) {
> > -               print_usage();
> > -               exit(EXIT_FAILURE);
> > -       }
> > -
> > -       /* need a fit image file or raw image file */
> > -       if (!file) {
> > +       /* check necessary parameters */
> > +       if ((argc != optind + 1) || !file ||
> > +           ((privkey_file && !cert_file) ||
> > +            (!privkey_file && cert_file))) {
> >                 print_usage();
> > -               exit(EXIT_SUCCESS);
> > +               return -1;
> >         }
> >
> > -       if (create_fwbin(argv[optind], file, guid, index, instance)
> > -                       < 0) {
> > +       if (create_fwbin(argv[optind], file, guid, index, instance,
> > +                        mcount, privkey_file, cert_file) < 0) {
> >                 printf("Creating firmware capsule failed\n");
> > -               exit(EXIT_FAILURE);
> > +               return -1;
> >         }
> >
> > -       exit(EXIT_SUCCESS);
> > +       return 0;
> >  }
> > --
> > 2.33.0
> >
> 
> 
> -- 
> Masami Hiramatsu

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v4 03/11] efi_loader: capsule: add back efi_get_public_key_data()
  2021-10-20 13:39       ` Simon Glass
@ 2021-10-25  5:14         ` AKASHI Takahiro
  2021-10-25  6:28           ` François Ozog
  0 siblings, 1 reply; 40+ messages in thread
From: AKASHI Takahiro @ 2021-10-25  5:14 UTC (permalink / raw)
  To: Simon Glass
  Cc: Masami Hiramatsu, Heinrich Schuchardt, Alex Graf,
	Ilias Apalodimas, Sughosh Ganu, U-Boot Mailing List

On Wed, Oct 20, 2021 at 07:39:37AM -0600, Simon Glass wrote:
> Hi Masami,
> 
> On Wed, 20 Oct 2021 at 02:18, Masami Hiramatsu
> <masami.hiramatsu@linaro.org> wrote:
> >
> > Hi Simon,
> >
> > 2021年10月15日(金) 9:40 Simon Glass <sjg@chromium.org>:
> > >
> > > Hi Takahiro,
> > >
> > > On Thu, 7 Oct 2021 at 00:25, AKASHI Takahiro <takahiro.akashi@linaro.org> wrote:
> > > >
> > > > The commit 47a25e81d35c ("Revert "efi_capsule: Move signature from DTB to
> > > > .rodata"") failed to revert the removal of efi_get_public_key_data().
> > > >
> > > > Add back this function and move it under lib/efi_loader so that other
> > > > platforms can utilize it. It is now declared as a weak function so that
> > > > it can be replaced with a platform-specific implementation.
> > > >
> > > > Fixes: 47a25e81d35c ("Revert "efi_capsule: Move signature from DTB to
> > > >         .rodata"")
> > > > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > > > ---
> > > >  lib/efi_loader/efi_capsule.c | 36 ++++++++++++++++++++++++++++++++++++
> > > >  1 file changed, 36 insertions(+)
> > > >
> > > > diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
> > > > index b75e4bcba1a9..44f5da61a9be 100644
> > > > --- a/lib/efi_loader/efi_capsule.c
> > > > +++ b/lib/efi_loader/efi_capsule.c
> > > > @@ -11,15 +11,20 @@
> > > >  #include <common.h>
> > > >  #include <efi_loader.h>
> > > >  #include <efi_variable.h>
> > > > +#include <env.h>
> > > > +#include <fdtdec.h>
> > > >  #include <fs.h>
> > > >  #include <malloc.h>
> > > >  #include <mapmem.h>
> > > >  #include <sort.h>
> > > > +#include <asm/global_data.h>
> > > >
> > > >  #include <crypto/pkcs7.h>
> > > >  #include <crypto/pkcs7_parser.h>
> > > >  #include <linux/err.h>
> > > >
> > > > +DECLARE_GLOBAL_DATA_PTR;
> > > > +
> > > >  const efi_guid_t efi_guid_capsule_report = EFI_CAPSULE_REPORT_GUID;
> > > >  static const efi_guid_t efi_guid_firmware_management_capsule_id =
> > > >                 EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID;
> > > > @@ -251,6 +256,37 @@ out:
> > > >  }
> > > >
> > > >  #if defined(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> > > > +int __weak efi_get_public_key_data(void **pkey, efi_uintn_t *pkey_len)
> > >
> > > I don't think this should be weak. What other way is there of handling
> > > this and why would it be platform-specific?
> >
> > I have a question about the current design of the capsule auth key.
> > If the platform has its own key-storage, how can the platform use the
> > platform specific storage? Does such platform load the key from the storage
> > and generate the dtb node in the platform initialization code? (or
> > device driver?)
> 
> Are we talking about a public key (which I assume from the function
> naming) or some secret key. What is an auth key?

Surely, a public key (more strictly, x509 certificate under the current
implementation)

> As I understand it, the public key should be provided by the platform
> in devicetree that U-Boot uses, by whatever prior stage has access to
> the key.

I still believe that some platform provider may want to save the key
in a *safer* storage, which should be at least read-only for non-authorized
writers. But if this issue (__weak or not) is the only blocking factor
for my entire patch series, I'm willing to drop __weak for now since
someone with production system may change it in the future when he has
a good reason for that :)

-Takahiro Akashi


> Regards,
> Simon

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v4 05/11] tools: mkeficapsule: add firmwware image signing
  2021-10-25  3:12     ` AKASHI Takahiro
@ 2021-10-25  5:40       ` Masami Hiramatsu
  2021-10-25  6:09         ` AKASHI Takahiro
  2021-10-25  9:58         ` Sughosh Ganu
  0 siblings, 2 replies; 40+ messages in thread
From: Masami Hiramatsu @ 2021-10-25  5:40 UTC (permalink / raw)
  To: AKASHI Takahiro, Masami Hiramatsu, Heinrich Schuchardt,
	Alexander Graf, Ilias Apalodimas, Sughosh Ganu,
	U-Boot Mailing List

Hi Takahiro,

2021年10月25日(月) 12:12 AKASHI Takahiro <takahiro.akashi@linaro.org>:
>
> Hi, Masami,
>
> On Wed, Oct 20, 2021 at 05:17:12PM +0900, Masami Hiramatsu wrote:
> > Hello Akashi-san,
> >
> > Can you split this patch out from this series?
> > It seems that the series is doing several different things. This
> > capsule signing has no alternatives, but others are modifying or
> > replacing the current existing feature. In other words, this capsule
> > signing feature has no alternatives, but others we can continue to
> > use.
>
> What do you specifically mean by "existing feature" or "others we can
> continue to use"?
>
> All the commits, I think, in this series are closely related with
> each other.

I meant I can use the devicetree to embed the authentication key in the
U-Boot itself (currently U-Boot expects that.) Or is there any issue with
putting the key in the devicetree?

Without this patch, I can not sign the capsule file for U-Boot, since
the GenerateCapsule in EDK2 supports old format (is that updated?)

Thank you,


>
> -Takahiro Akashi
>
> > Thank you,
> >
> > 2021年10月7日(木) 15:25 AKASHI Takahiro <takahiro.akashi@linaro.org>:
> > >
> > > With this enhancement, mkeficapsule will be able to sign a capsule
> > > file when it is created. A signature added will be used later
> > > in the verification at FMP's SetImage() call.
> > >
> > > To do that, We need specify additional command parameters:
> > >   -monotonic-cout <count> : monotonic count
> > >   -private-key <private key file> : private key file
> > >   -certificate <certificate file> : certificate file
> > > Only when all of those parameters are given, a signature will be added
> > > to a capsule file.
> > >
> > > Users are expected to maintain and increment the monotonic count at
> > > every time of the update for each firmware image.
> > >
> > > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > > ---
> > >  tools/Kconfig        |   7 +
> > >  tools/Makefile       |   8 +-
> > >  tools/mkeficapsule.c | 435 +++++++++++++++++++++++++++++++++++++++----
> > >  3 files changed, 416 insertions(+), 34 deletions(-)
> > >
> > > diff --git a/tools/Kconfig b/tools/Kconfig
> > > index d6f82cd949b5..9a37ed035311 100644
> > > --- a/tools/Kconfig
> > > +++ b/tools/Kconfig
> > > @@ -20,4 +20,11 @@ config TOOLS_LIBCRYPTO
> > >           This selection does not affect target features, such as runtime FIT
> > >           signature verification.
> > >
> > > +config TOOLS_MKEFICAPSULE
> > > +       bool "Build efimkcapsule command"
> > > +       default y if EFI_CAPSULE_ON_DISK
> > > +       help
> > > +         This command allows users to create a UEFI capsule file and,
> > > +         optionally sign that file. If you want to enable UEFI capsule
> > > +         update feature on your target, you certainly need this.
> > >  endmenu
> > > diff --git a/tools/Makefile b/tools/Makefile
> > > index 4a86321f6467..6ea3033dbfb8 100644
> > > --- a/tools/Makefile
> > > +++ b/tools/Makefile
> > > @@ -237,8 +237,12 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs
> > >  hostprogs-$(CONFIG_ASN1_COMPILER)      += asn1_compiler
> > >  HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
> > >
> > > -mkeficapsule-objs      := mkeficapsule.o $(LIBFDT_OBJS)
> > > -hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
> > > +HOSTLDLIBS_mkeficapsule += -luuid
> > > +ifeq ($(CONFIG_TOOLS_LIBCRYPTO),y)
> > > +HOSTLDLIBS_mkeficapsule += \
> > > +       $(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto")
> > > +endif
> > > +hostprogs-$(CONFIG_TOOLS_MKEFICAPSULE) += mkeficapsule
> > >
> > >  # We build some files with extra pedantic flags to try to minimize things
> > >  # that won't build on some weird host compiler -- though there are lots of
> > > diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
> > > index 4995ba4e0c2a..5541e4bda894 100644
> > > --- a/tools/mkeficapsule.c
> > > +++ b/tools/mkeficapsule.c
> > > @@ -15,6 +15,16 @@
> > >  #include <sys/stat.h>
> > >  #include <sys/types.h>
> > >
> > > +#include <linux/kconfig.h>
> > > +#ifdef CONFIG_TOOLS_LIBCRYPTO
> > > +#include <openssl/asn1.h>
> > > +#include <openssl/bio.h>
> > > +#include <openssl/evp.h>
> > > +#include <openssl/err.h>
> > > +#include <openssl/pem.h>
> > > +#include <openssl/pkcs7.h>
> > > +#endif
> > > +
> > >  typedef __u8 u8;
> > >  typedef __u16 u16;
> > >  typedef __u32 u32;
> > > @@ -38,12 +48,25 @@ efi_guid_t efi_guid_image_type_uboot_fit =
> > >                 EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID;
> > >  efi_guid_t efi_guid_image_type_uboot_raw =
> > >                 EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID;
> > > +efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
> > > +
> > > +#ifdef CONFIG_TOOLS_LIBCRYPTO
> > > +static const char *opts_short = "f:r:i:I:v:p:c:m:dh";
> > > +#else
> > > +static const char *opts_short = "f:r:i:I:v:h";
> > > +#endif
> > >
> > >  static struct option options[] = {
> > >         {"fit", required_argument, NULL, 'f'},
> > >         {"raw", required_argument, NULL, 'r'},
> > >         {"index", required_argument, NULL, 'i'},
> > >         {"instance", required_argument, NULL, 'I'},
> > > +#ifdef CONFIG_TOOLS_LIBCRYPTO
> > > +       {"private-key", required_argument, NULL, 'p'},
> > > +       {"certificate", required_argument, NULL, 'c'},
> > > +       {"monotonic-count", required_argument, NULL, 'm'},
> > > +       {"dump-sig", no_argument, NULL, 'd'},
> > > +#endif
> > >         {"help", no_argument, NULL, 'h'},
> > >         {NULL, 0, NULL, 0},
> > >  };
> > > @@ -57,16 +80,280 @@ static void print_usage(void)
> > >                "\t-r, --raw <raw image>       new raw image file\n"
> > >                "\t-i, --index <index>         update image index\n"
> > >                "\t-I, --instance <instance>   update hardware instance\n"
> > > +#ifdef CONFIG_TOOLS_LIBCRYPTO
> > > +              "\t-p, --private-key <privkey file>  private key file\n"
> > > +              "\t-c, --certificate <cert file>     signer's certificate file\n"
> > > +              "\t-m, --monotonic-count <count>     monotonic count\n"
> > > +              "\t-d, --dump_sig              dump signature (*.p7)\n"
> > > +#endif
> > >                "\t-h, --help                  print a help message\n",
> > >                tool_name);
> > >  }
> > >
> > > +/**
> > > + * auth_context - authentication context
> > > + * @key_file:  Path to a private key file
> > > + * @cert_file: Path to a certificate file
> > > + * @image_data:        Pointer to firmware data
> > > + * @image_size:        Size of firmware data
> > > + * @auth:      Authentication header
> > > + * @sig_data:  Signature data
> > > + * @sig_size:  Size of signature data
> > > + *
> > > + * Data structure used in create_auth_data(). @key_file through
> > > + * @image_size are input parameters. @auth, @sig_data and @sig_size
> > > + * are filled in by create_auth_data().
> > > + */
> > > +struct auth_context {
> > > +       char *key_file;
> > > +       char *cert_file;
> > > +       u8 *image_data;
> > > +       size_t image_size;
> > > +       struct efi_firmware_image_authentication auth;
> > > +       u8 *sig_data;
> > > +       size_t sig_size;
> > > +};
> > > +
> > > +static int dump_sig;
> > > +
> > > +#ifdef CONFIG_TOOLS_LIBCRYPTO
> > > +/**
> > > + * fileio-read_pkey - read out a private key
> > > + * @filename:  Path to a private key file
> > > + *
> > > + * Read out a private key file and parse it into "EVP_PKEY" structure.
> > > + *
> > > + * Return:
> > > + * * Pointer to private key structure  - on success
> > > + * * NULL - on failure
> > > + */
> > > +static EVP_PKEY *fileio_read_pkey(const char *filename)
> > > +{
> > > +       EVP_PKEY *key = NULL;
> > > +       BIO *bio;
> > > +
> > > +       bio = BIO_new_file(filename, "r");
> > > +       if (!bio)
> > > +               goto out;
> > > +
> > > +       key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
> > > +
> > > +out:
> > > +       BIO_free_all(bio);
> > > +       if (!key) {
> > > +               printf("Can't load key from file '%s'\n", filename);
> > > +               ERR_print_errors_fp(stderr);
> > > +       }
> > > +
> > > +       return key;
> > > +}
> > > +
> > > +/**
> > > + * fileio-read_cert - read out a certificate
> > > + * @filename:  Path to a certificate file
> > > + *
> > > + * Read out a certificate file and parse it into "X509" structure.
> > > + *
> > > + * Return:
> > > + * * Pointer to certificate structure  - on success
> > > + * * NULL - on failure
> > > + */
> > > +static X509 *fileio_read_cert(const char *filename)
> > > +{
> > > +       X509 *cert = NULL;
> > > +       BIO *bio;
> > > +
> > > +       bio = BIO_new_file(filename, "r");
> > > +       if (!bio)
> > > +               goto out;
> > > +
> > > +       cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
> > > +
> > > +out:
> > > +       BIO_free_all(bio);
> > > +       if (!cert) {
> > > +               printf("Can't load certificate from file '%s'\n", filename);
> > > +               ERR_print_errors_fp(stderr);
> > > +       }
> > > +
> > > +       return cert;
> > > +}
> > > +
> > > +/**
> > > + * create_auth_data - compose authentication data in capsule
> > > + * @auth_context:      Pointer to authentication context
> > > + *
> > > + * Fill up an authentication header (.auth) and signature data (.sig_data)
> > > + * in @auth_context, using library functions from openssl.
> > > + * All the parameters in @auth_context must be filled in by a caller.
> > > + *
> > > + * Return:
> > > + * * 0  - on success
> > > + * * -1 - on failure
> > > + */
> > > +static int create_auth_data(struct auth_context *ctx)
> > > +{
> > > +       EVP_PKEY *key = NULL;
> > > +       X509 *cert = NULL;
> > > +       BIO *data_bio = NULL;
> > > +       const EVP_MD *md;
> > > +       PKCS7 *p7;
> > > +       int flags, ret = -1;
> > > +
> > > +       OpenSSL_add_all_digests();
> > > +       OpenSSL_add_all_ciphers();
> > > +       ERR_load_crypto_strings();
> > > +
> > > +       key = fileio_read_pkey(ctx->key_file);
> > > +       if (!key)
> > > +               goto err;
> > > +       cert = fileio_read_cert(ctx->cert_file);
> > > +       if (!cert)
> > > +               goto err;
> > > +
> > > +       /*
> > > +        * create a BIO, containing:
> > > +        *  * firmware image
> > > +        *  * monotonic count
> > > +        * in this order!
> > > +        * See EDK2's FmpAuthenticatedHandlerRsa2048Sha256()
> > > +        */
> > > +       data_bio = BIO_new(BIO_s_mem());
> > > +       BIO_write(data_bio, ctx->image_data, ctx->image_size);
> > > +       BIO_write(data_bio, &ctx->auth.monotonic_count,
> > > +                 sizeof(ctx->auth.monotonic_count));
> > > +
> > > +       md = EVP_get_digestbyname("SHA256");
> > > +       if (!md)
> > > +               goto err;
> > > +
> > > +       /* create signature */
> > > +       /* TODO: maybe add PKCS7_NOATTR and PKCS7_NOSMIMECAP */
> > > +       flags = PKCS7_BINARY | PKCS7_DETACHED;
> > > +       p7 = PKCS7_sign(NULL, NULL, NULL, data_bio, flags | PKCS7_PARTIAL);
> > > +       if (!p7)
> > > +               goto err;
> > > +       if (!PKCS7_sign_add_signer(p7, cert, key, md, flags))
> > > +               goto err;
> > > +       if (!PKCS7_final(p7, data_bio, flags))
> > > +               goto err;
> > > +
> > > +       /* convert pkcs7 into DER */
> > > +       ctx->sig_data = NULL;
> > > +       ctx->sig_size = ASN1_item_i2d((ASN1_VALUE *)p7, &ctx->sig_data,
> > > +                                     ASN1_ITEM_rptr(PKCS7));
> > > +       if (!ctx->sig_size)
> > > +               goto err;
> > > +
> > > +       /* fill auth_info */
> > > +       ctx->auth.auth_info.hdr.dwLength = sizeof(ctx->auth.auth_info)
> > > +                                               + ctx->sig_size;
> > > +       ctx->auth.auth_info.hdr.wRevision = WIN_CERT_REVISION_2_0;
> > > +       ctx->auth.auth_info.hdr.wCertificateType = WIN_CERT_TYPE_EFI_GUID;
> > > +       memcpy(&ctx->auth.auth_info.cert_type, &efi_guid_cert_type_pkcs7,
> > > +              sizeof(efi_guid_cert_type_pkcs7));
> > > +
> > > +       ret = 0;
> > > +err:
> > > +       BIO_free_all(data_bio);
> > > +       EVP_PKEY_free(key);
> > > +       X509_free(cert);
> > > +
> > > +       return ret;
> > > +}
> > > +
> > > +/**
> > > + * dump_signature - dump out a signature
> > > + * @path:      Path to a capsule file
> > > + * @signature: Signature data
> > > + * @sig_size:  Size of signature data
> > > + *
> > > + * Signature data pointed to by @signature will be saved into
> > > + * a file whose file name is @path with ".p7" suffix.
> > > + *
> > > + * Return:
> > > + * * 0  - on success
> > > + * * -1 - on failure
> > > + */
> > > +static int dump_signature(const char *path, u8 *signature, size_t sig_size)
> > > +{
> > > +       char *sig_path;
> > > +       FILE *f;
> > > +       size_t size;
> > > +       int ret = -1;
> > > +
> > > +       sig_path = malloc(strlen(path) + 3 + 1);
> > > +       if (!sig_path)
> > > +               return ret;
> > > +
> > > +       sprintf(sig_path, "%s.p7", path);
> > > +       f = fopen(sig_path, "w");
> > > +       if (!f)
> > > +               goto err;
> > > +
> > > +       size = fwrite(signature, 1, sig_size, f);
> > > +       if (size == sig_size)
> > > +               ret = 0;
> > > +
> > > +       fclose(f);
> > > +err:
> > > +       free(sig_path);
> > > +       return ret;
> > > +}
> > > +
> > > +/**
> > > + * free_sig_data - free out signature data
> > > + * @ctx:       Pointer to authentication context
> > > + *
> > > + * Free signature data allocated in create_auth_data().
> > > + */
> > > +static void free_sig_data(struct auth_context *ctx)
> > > +{
> > > +       if (ctx->sig_size)
> > > +               OPENSSL_free(ctx->sig_data);
> > > +}
> > > +#else
> > > +static int create_auth_data(struct auth_context *ctx)
> > > +{
> > > +       return 0;
> > > +}
> > > +
> > > +static int dump_signature(const char *path, u8 *signature, size_t sig_size)
> > > +{
> > > +       return 0;
> > > +}
> > > +
> > > +static void free_sig_data(struct auth_context *ctx) {}
> > > +#endif
> > > +
> > > +/**
> > > + * create_fwbin - create an uefi capsule file
> > > + * @path:      Path to a created capsule file
> > > + * @bin:       Path to a firmware binary to encapsulate
> > > + * @guid:      GUID of related FMP driver
> > > + * @index:     Index number in capsule
> > > + * @instance:  Instance number in capsule
> > > + * @mcount:    Monotonic count in authentication information
> > > + * @private_file:      Path to a private key file
> > > + * @cert_file: Path to a certificate file
> > > + *
> > > + * This function actually does the job of creating an uefi capsule file.
> > > + * All the arguments must be supplied.
> > > + * If either @private_file ror @cert_file is NULL, the capsule file
> > > + * won't be signed.
> > > + *
> > > + * Return:
> > > + * * 0  - on success
> > > + * * -1 - on failure
> > > + */
> > >  static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > -                       unsigned long index, unsigned long instance)
> > > +                       unsigned long index, unsigned long instance,
> > > +                       uint64_t mcount, char *privkey_file, char *cert_file)
> > >  {
> > >         struct efi_capsule_header header;
> > >         struct efi_firmware_management_capsule_header capsule;
> > >         struct efi_firmware_management_capsule_image_header image;
> > > +       struct auth_context auth_context;
> > >         FILE *f, *g;
> > >         struct stat bin_stat;
> > >         u8 *data;
> > > @@ -76,8 +363,9 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > >  #ifdef DEBUG
> > >         printf("For output: %s\n", path);
> > >         printf("\tbin: %s\n\ttype: %pUl\n", bin, guid);
> > > -       printf("\tindex: %ld\n\tinstance: %ld\n", index, instance);
> > > +       printf("\tindex: %lu\n\tinstance: %lu\n", index, instance);
> > >  #endif
> > > +       auth_context.sig_size = 0;
> > >
> > >         g = fopen(bin, "r");
> > >         if (!g) {
> > > @@ -93,11 +381,34 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > >                 printf("cannot allocate memory: %zx\n", (size_t)bin_stat.st_size);
> > >                 goto err_1;
> > >         }
> > > -       f = fopen(path, "w");
> > > -       if (!f) {
> > > -               printf("cannot open %s\n", path);
> > > +
> > > +       size = fread(data, 1, bin_stat.st_size, g);
> > > +       if (size < bin_stat.st_size) {
> > > +               printf("read failed (%zx)\n", size);
> > >                 goto err_2;
> > >         }
> > > +
> > > +       /* first, calculate signature to determine its size */
> > > +       if (privkey_file && cert_file) {
> > > +               auth_context.key_file = privkey_file;
> > > +               auth_context.cert_file = cert_file;
> > > +               auth_context.auth.monotonic_count = mcount;
> > > +               auth_context.image_data = data;
> > > +               auth_context.image_size = bin_stat.st_size;
> > > +
> > > +               if (create_auth_data(&auth_context)) {
> > > +                       printf("Signing firmware image failed\n");
> > > +                       goto err_3;
> > > +               }
> > > +
> > > +               if (dump_sig &&
> > > +                   dump_signature(path, auth_context.sig_data,
> > > +                                  auth_context.sig_size)) {
> > > +                       printf("Creating signature file failed\n");
> > > +                       goto err_3;
> > > +               }
> > > +       }
> > > +
> > >         header.capsule_guid = efi_guid_fm_capsule;
> > >         header.header_size = sizeof(header);
> > >         /* TODO: The current implementation ignores flags */
> > > @@ -106,11 +417,20 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > >                                         + sizeof(capsule) + sizeof(u64)
> > >                                         + sizeof(image)
> > >                                         + bin_stat.st_size;
> > > +       if (auth_context.sig_size)
> > > +               header.capsule_image_size += sizeof(auth_context.auth)
> > > +                               + auth_context.sig_size;
> > > +
> > > +       f = fopen(path, "w");
> > > +       if (!f) {
> > > +               printf("cannot open %s\n", path);
> > > +               goto err_3;
> > > +       }
> > >
> > >         size = fwrite(&header, 1, sizeof(header), f);
> > >         if (size < sizeof(header)) {
> > >                 printf("write failed (%zx)\n", size);
> > > -               goto err_3;
> > > +               goto err_4;
> > >         }
> > >
> > >         capsule.version = 0x00000001;
> > > @@ -119,13 +439,13 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > >         size = fwrite(&capsule, 1, sizeof(capsule), f);
> > >         if (size < (sizeof(capsule))) {
> > >                 printf("write failed (%zx)\n", size);
> > > -               goto err_3;
> > > +               goto err_4;
> > >         }
> > >         offset = sizeof(capsule) + sizeof(u64);
> > >         size = fwrite(&offset, 1, sizeof(offset), f);
> > >         if (size < sizeof(offset)) {
> > >                 printf("write failed (%zx)\n", size);
> > > -               goto err_3;
> > > +               goto err_4;
> > >         }
> > >
> > >         image.version = 0x00000003;
> > > @@ -135,34 +455,53 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > >         image.reserved[1] = 0;
> > >         image.reserved[2] = 0;
> > >         image.update_image_size = bin_stat.st_size;
> > > +       if (auth_context.sig_size)
> > > +               image.update_image_size += sizeof(auth_context.auth)
> > > +                               + auth_context.sig_size;
> > >         image.update_vendor_code_size = 0; /* none */
> > >         image.update_hardware_instance = instance;
> > >         image.image_capsule_support = 0;
> > > +       if (auth_context.sig_size)
> > > +               image.image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION;
> > >
> > >         size = fwrite(&image, 1, sizeof(image), f);
> > >         if (size < sizeof(image)) {
> > >                 printf("write failed (%zx)\n", size);
> > > -               goto err_3;
> > > +               goto err_4;
> > >         }
> > > -       size = fread(data, 1, bin_stat.st_size, g);
> > > -       if (size < bin_stat.st_size) {
> > > -               printf("read failed (%zx)\n", size);
> > > -               goto err_3;
> > > +
> > > +       if (auth_context.sig_size) {
> > > +               size = fwrite(&auth_context.auth, 1,
> > > +                             sizeof(auth_context.auth), f);
> > > +               if (size < sizeof(auth_context.auth)) {
> > > +                       printf("write failed (%zx)\n", size);
> > > +                       goto err_4;
> > > +               }
> > > +               size = fwrite(auth_context.sig_data, 1,
> > > +                             auth_context.sig_size, f);
> > > +               if (size < auth_context.sig_size) {
> > > +                       printf("write failed (%zx)\n", size);
> > > +                       goto err_4;
> > > +               }
> > >         }
> > > +
> > >         size = fwrite(data, 1, bin_stat.st_size, f);
> > >         if (size < bin_stat.st_size) {
> > >                 printf("write failed (%zx)\n", size);
> > > -               goto err_3;
> > > +               goto err_4;
> > >         }
> > >
> > >         fclose(f);
> > >         fclose(g);
> > >         free(data);
> > > +       free_sig_data(&auth_context);
> > >
> > >         return 0;
> > >
> > > -err_3:
> > > +err_4:
> > >         fclose(f);
> > > +err_3:
> > > +       free_sig_data(&auth_context);
> > >  err_2:
> > >         free(data);
> > >  err_1:
> > > @@ -171,23 +510,37 @@ err_1:
> > >         return -1;
> > >  }
> > >
> > > -/*
> > > - * Usage:
> > > - *   $ mkeficapsule -f <firmware binary> <output file>
> > > +/**
> > > + * main - main entry function of mkeficapsule
> > > + * @argc:      Number of arguments
> > > + * @argv:      Array of pointers to arguments
> > > + *
> > > + * Create an uefi capsule file, optionally signing it.
> > > + * Parse all the arguments and pass them on to create_fwbin().
> > > + *
> > > + * Return:
> > > + * * 0  - on success
> > > + * * -1 - on failure
> > >   */
> > >  int main(int argc, char **argv)
> > >  {
> > >         char *file;
> > >         efi_guid_t *guid;
> > >         unsigned long index, instance;
> > > +       uint64_t mcount;
> > > +       char *privkey_file, *cert_file;
> > >         int c, idx;
> > >
> > >         file = NULL;
> > >         guid = NULL;
> > >         index = 0;
> > >         instance = 0;
> > > +       mcount = 0;
> > > +       privkey_file = NULL;
> > > +       cert_file = NULL;
> > > +       dump_sig = 0;
> > >         for (;;) {
> > > -               c = getopt_long(argc, argv, "f:r:i:I:v:h", options, &idx);
> > > +               c = getopt_long(argc, argv, opts_short, options, &idx);
> > >                 if (c == -1)
> > >                         break;
> > >
> > > @@ -214,29 +567,47 @@ int main(int argc, char **argv)
> > >                 case 'I':
> > >                         instance = strtoul(optarg, NULL, 0);
> > >                         break;
> > > +#ifdef CONFIG_TOOLS_LIBCRYPTO
> > > +               case 'p':
> > > +                       if (privkey_file) {
> > > +                               printf("Private Key already specified\n");
> > > +                               return -1;
> > > +                       }
> > > +                       privkey_file = optarg;
> > > +                       break;
> > > +               case 'c':
> > > +                       if (cert_file) {
> > > +                               printf("Certificate file already specified\n");
> > > +                               return -1;
> > > +                       }
> > > +                       cert_file = optarg;
> > > +                       break;
> > > +               case 'm':
> > > +                       mcount = strtoul(optarg, NULL, 0);
> > > +                       break;
> > > +               case 'd':
> > > +                       dump_sig = 1;
> > > +                       break;
> > > +#endif /* CONFIG_TOOLS_LIBCRYPTO */
> > >                 case 'h':
> > >                         print_usage();
> > >                         return 0;
> > >                 }
> > >         }
> > >
> > > -       /* need an output file */
> > > -       if (argc != optind + 1) {
> > > -               print_usage();
> > > -               exit(EXIT_FAILURE);
> > > -       }
> > > -
> > > -       /* need a fit image file or raw image file */
> > > -       if (!file) {
> > > +       /* check necessary parameters */
> > > +       if ((argc != optind + 1) || !file ||
> > > +           ((privkey_file && !cert_file) ||
> > > +            (!privkey_file && cert_file))) {
> > >                 print_usage();
> > > -               exit(EXIT_SUCCESS);
> > > +               return -1;
> > >         }
> > >
> > > -       if (create_fwbin(argv[optind], file, guid, index, instance)
> > > -                       < 0) {
> > > +       if (create_fwbin(argv[optind], file, guid, index, instance,
> > > +                        mcount, privkey_file, cert_file) < 0) {
> > >                 printf("Creating firmware capsule failed\n");
> > > -               exit(EXIT_FAILURE);
> > > +               return -1;
> > >         }
> > >
> > > -       exit(EXIT_SUCCESS);
> > > +       return 0;
> > >  }
> > > --
> > > 2.33.0
> > >
> >
> >
> > --
> > Masami Hiramatsu



-- 
Masami Hiramatsu

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v4 05/11] tools: mkeficapsule: add firmwware image signing
  2021-10-25  5:40       ` Masami Hiramatsu
@ 2021-10-25  6:09         ` AKASHI Takahiro
  2021-10-25  7:04           ` Masami Hiramatsu
  2021-10-25  9:58         ` Sughosh Ganu
  1 sibling, 1 reply; 40+ messages in thread
From: AKASHI Takahiro @ 2021-10-25  6:09 UTC (permalink / raw)
  To: Masami Hiramatsu
  Cc: Heinrich Schuchardt, Alexander Graf, Ilias Apalodimas,
	Sughosh Ganu, U-Boot Mailing List

On Mon, Oct 25, 2021 at 02:40:11PM +0900, Masami Hiramatsu wrote:
> Hi Takahiro,
> 
> 2021年10月25日(月) 12:12 AKASHI Takahiro <takahiro.akashi@linaro.org>:
> >
> > Hi, Masami,
> >
> > On Wed, Oct 20, 2021 at 05:17:12PM +0900, Masami Hiramatsu wrote:
> > > Hello Akashi-san,
> > >
> > > Can you split this patch out from this series?
> > > It seems that the series is doing several different things. This
> > > capsule signing has no alternatives, but others are modifying or
> > > replacing the current existing feature. In other words, this capsule
> > > signing feature has no alternatives, but others we can continue to
> > > use.
> >
> > What do you specifically mean by "existing feature" or "others we can
> > continue to use"?
> >
> > All the commits, I think, in this series are closely related with
> > each other.
> 
> I meant I can use the devicetree to embed the authentication key in the
> U-Boot itself (currently U-Boot expects that.) Or is there any issue with
> putting the key in the devicetree?

I don't get why this particular patch be separated from the others.
Some are prerequisite "revert" commits, some are updates on documents,
and some are test scriptsa All changes can and should be merged at one time.

> Without this patch, I can not sign the capsule file for U-Boot, since
> the GenerateCapsule in EDK2 supports old format (is that updated?)

I don't know, but Sughosh was working on the issue, he might have a patch
for EDK2?

-Takahiro Akashi

> Thank you,
> 
> 
> >
> > -Takahiro Akashi
> >
> > > Thank you,
> > >
> > > 2021年10月7日(木) 15:25 AKASHI Takahiro <takahiro.akashi@linaro.org>:
> > > >
> > > > With this enhancement, mkeficapsule will be able to sign a capsule
> > > > file when it is created. A signature added will be used later
> > > > in the verification at FMP's SetImage() call.
> > > >
> > > > To do that, We need specify additional command parameters:
> > > >   -monotonic-cout <count> : monotonic count
> > > >   -private-key <private key file> : private key file
> > > >   -certificate <certificate file> : certificate file
> > > > Only when all of those parameters are given, a signature will be added
> > > > to a capsule file.
> > > >
> > > > Users are expected to maintain and increment the monotonic count at
> > > > every time of the update for each firmware image.
> > > >
> > > > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > > > ---
> > > >  tools/Kconfig        |   7 +
> > > >  tools/Makefile       |   8 +-
> > > >  tools/mkeficapsule.c | 435 +++++++++++++++++++++++++++++++++++++++----
> > > >  3 files changed, 416 insertions(+), 34 deletions(-)
> > > >
> > > > diff --git a/tools/Kconfig b/tools/Kconfig
> > > > index d6f82cd949b5..9a37ed035311 100644
> > > > --- a/tools/Kconfig
> > > > +++ b/tools/Kconfig
> > > > @@ -20,4 +20,11 @@ config TOOLS_LIBCRYPTO
> > > >           This selection does not affect target features, such as runtime FIT
> > > >           signature verification.
> > > >
> > > > +config TOOLS_MKEFICAPSULE
> > > > +       bool "Build efimkcapsule command"
> > > > +       default y if EFI_CAPSULE_ON_DISK
> > > > +       help
> > > > +         This command allows users to create a UEFI capsule file and,
> > > > +         optionally sign that file. If you want to enable UEFI capsule
> > > > +         update feature on your target, you certainly need this.
> > > >  endmenu
> > > > diff --git a/tools/Makefile b/tools/Makefile
> > > > index 4a86321f6467..6ea3033dbfb8 100644
> > > > --- a/tools/Makefile
> > > > +++ b/tools/Makefile
> > > > @@ -237,8 +237,12 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs
> > > >  hostprogs-$(CONFIG_ASN1_COMPILER)      += asn1_compiler
> > > >  HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
> > > >
> > > > -mkeficapsule-objs      := mkeficapsule.o $(LIBFDT_OBJS)
> > > > -hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
> > > > +HOSTLDLIBS_mkeficapsule += -luuid
> > > > +ifeq ($(CONFIG_TOOLS_LIBCRYPTO),y)
> > > > +HOSTLDLIBS_mkeficapsule += \
> > > > +       $(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto")
> > > > +endif
> > > > +hostprogs-$(CONFIG_TOOLS_MKEFICAPSULE) += mkeficapsule
> > > >
> > > >  # We build some files with extra pedantic flags to try to minimize things
> > > >  # that won't build on some weird host compiler -- though there are lots of
> > > > diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
> > > > index 4995ba4e0c2a..5541e4bda894 100644
> > > > --- a/tools/mkeficapsule.c
> > > > +++ b/tools/mkeficapsule.c
> > > > @@ -15,6 +15,16 @@
> > > >  #include <sys/stat.h>
> > > >  #include <sys/types.h>
> > > >
> > > > +#include <linux/kconfig.h>
> > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO
> > > > +#include <openssl/asn1.h>
> > > > +#include <openssl/bio.h>
> > > > +#include <openssl/evp.h>
> > > > +#include <openssl/err.h>
> > > > +#include <openssl/pem.h>
> > > > +#include <openssl/pkcs7.h>
> > > > +#endif
> > > > +
> > > >  typedef __u8 u8;
> > > >  typedef __u16 u16;
> > > >  typedef __u32 u32;
> > > > @@ -38,12 +48,25 @@ efi_guid_t efi_guid_image_type_uboot_fit =
> > > >                 EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID;
> > > >  efi_guid_t efi_guid_image_type_uboot_raw =
> > > >                 EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID;
> > > > +efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
> > > > +
> > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO
> > > > +static const char *opts_short = "f:r:i:I:v:p:c:m:dh";
> > > > +#else
> > > > +static const char *opts_short = "f:r:i:I:v:h";
> > > > +#endif
> > > >
> > > >  static struct option options[] = {
> > > >         {"fit", required_argument, NULL, 'f'},
> > > >         {"raw", required_argument, NULL, 'r'},
> > > >         {"index", required_argument, NULL, 'i'},
> > > >         {"instance", required_argument, NULL, 'I'},
> > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO
> > > > +       {"private-key", required_argument, NULL, 'p'},
> > > > +       {"certificate", required_argument, NULL, 'c'},
> > > > +       {"monotonic-count", required_argument, NULL, 'm'},
> > > > +       {"dump-sig", no_argument, NULL, 'd'},
> > > > +#endif
> > > >         {"help", no_argument, NULL, 'h'},
> > > >         {NULL, 0, NULL, 0},
> > > >  };
> > > > @@ -57,16 +80,280 @@ static void print_usage(void)
> > > >                "\t-r, --raw <raw image>       new raw image file\n"
> > > >                "\t-i, --index <index>         update image index\n"
> > > >                "\t-I, --instance <instance>   update hardware instance\n"
> > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO
> > > > +              "\t-p, --private-key <privkey file>  private key file\n"
> > > > +              "\t-c, --certificate <cert file>     signer's certificate file\n"
> > > > +              "\t-m, --monotonic-count <count>     monotonic count\n"
> > > > +              "\t-d, --dump_sig              dump signature (*.p7)\n"
> > > > +#endif
> > > >                "\t-h, --help                  print a help message\n",
> > > >                tool_name);
> > > >  }
> > > >
> > > > +/**
> > > > + * auth_context - authentication context
> > > > + * @key_file:  Path to a private key file
> > > > + * @cert_file: Path to a certificate file
> > > > + * @image_data:        Pointer to firmware data
> > > > + * @image_size:        Size of firmware data
> > > > + * @auth:      Authentication header
> > > > + * @sig_data:  Signature data
> > > > + * @sig_size:  Size of signature data
> > > > + *
> > > > + * Data structure used in create_auth_data(). @key_file through
> > > > + * @image_size are input parameters. @auth, @sig_data and @sig_size
> > > > + * are filled in by create_auth_data().
> > > > + */
> > > > +struct auth_context {
> > > > +       char *key_file;
> > > > +       char *cert_file;
> > > > +       u8 *image_data;
> > > > +       size_t image_size;
> > > > +       struct efi_firmware_image_authentication auth;
> > > > +       u8 *sig_data;
> > > > +       size_t sig_size;
> > > > +};
> > > > +
> > > > +static int dump_sig;
> > > > +
> > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO
> > > > +/**
> > > > + * fileio-read_pkey - read out a private key
> > > > + * @filename:  Path to a private key file
> > > > + *
> > > > + * Read out a private key file and parse it into "EVP_PKEY" structure.
> > > > + *
> > > > + * Return:
> > > > + * * Pointer to private key structure  - on success
> > > > + * * NULL - on failure
> > > > + */
> > > > +static EVP_PKEY *fileio_read_pkey(const char *filename)
> > > > +{
> > > > +       EVP_PKEY *key = NULL;
> > > > +       BIO *bio;
> > > > +
> > > > +       bio = BIO_new_file(filename, "r");
> > > > +       if (!bio)
> > > > +               goto out;
> > > > +
> > > > +       key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
> > > > +
> > > > +out:
> > > > +       BIO_free_all(bio);
> > > > +       if (!key) {
> > > > +               printf("Can't load key from file '%s'\n", filename);
> > > > +               ERR_print_errors_fp(stderr);
> > > > +       }
> > > > +
> > > > +       return key;
> > > > +}
> > > > +
> > > > +/**
> > > > + * fileio-read_cert - read out a certificate
> > > > + * @filename:  Path to a certificate file
> > > > + *
> > > > + * Read out a certificate file and parse it into "X509" structure.
> > > > + *
> > > > + * Return:
> > > > + * * Pointer to certificate structure  - on success
> > > > + * * NULL - on failure
> > > > + */
> > > > +static X509 *fileio_read_cert(const char *filename)
> > > > +{
> > > > +       X509 *cert = NULL;
> > > > +       BIO *bio;
> > > > +
> > > > +       bio = BIO_new_file(filename, "r");
> > > > +       if (!bio)
> > > > +               goto out;
> > > > +
> > > > +       cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
> > > > +
> > > > +out:
> > > > +       BIO_free_all(bio);
> > > > +       if (!cert) {
> > > > +               printf("Can't load certificate from file '%s'\n", filename);
> > > > +               ERR_print_errors_fp(stderr);
> > > > +       }
> > > > +
> > > > +       return cert;
> > > > +}
> > > > +
> > > > +/**
> > > > + * create_auth_data - compose authentication data in capsule
> > > > + * @auth_context:      Pointer to authentication context
> > > > + *
> > > > + * Fill up an authentication header (.auth) and signature data (.sig_data)
> > > > + * in @auth_context, using library functions from openssl.
> > > > + * All the parameters in @auth_context must be filled in by a caller.
> > > > + *
> > > > + * Return:
> > > > + * * 0  - on success
> > > > + * * -1 - on failure
> > > > + */
> > > > +static int create_auth_data(struct auth_context *ctx)
> > > > +{
> > > > +       EVP_PKEY *key = NULL;
> > > > +       X509 *cert = NULL;
> > > > +       BIO *data_bio = NULL;
> > > > +       const EVP_MD *md;
> > > > +       PKCS7 *p7;
> > > > +       int flags, ret = -1;
> > > > +
> > > > +       OpenSSL_add_all_digests();
> > > > +       OpenSSL_add_all_ciphers();
> > > > +       ERR_load_crypto_strings();
> > > > +
> > > > +       key = fileio_read_pkey(ctx->key_file);
> > > > +       if (!key)
> > > > +               goto err;
> > > > +       cert = fileio_read_cert(ctx->cert_file);
> > > > +       if (!cert)
> > > > +               goto err;
> > > > +
> > > > +       /*
> > > > +        * create a BIO, containing:
> > > > +        *  * firmware image
> > > > +        *  * monotonic count
> > > > +        * in this order!
> > > > +        * See EDK2's FmpAuthenticatedHandlerRsa2048Sha256()
> > > > +        */
> > > > +       data_bio = BIO_new(BIO_s_mem());
> > > > +       BIO_write(data_bio, ctx->image_data, ctx->image_size);
> > > > +       BIO_write(data_bio, &ctx->auth.monotonic_count,
> > > > +                 sizeof(ctx->auth.monotonic_count));
> > > > +
> > > > +       md = EVP_get_digestbyname("SHA256");
> > > > +       if (!md)
> > > > +               goto err;
> > > > +
> > > > +       /* create signature */
> > > > +       /* TODO: maybe add PKCS7_NOATTR and PKCS7_NOSMIMECAP */
> > > > +       flags = PKCS7_BINARY | PKCS7_DETACHED;
> > > > +       p7 = PKCS7_sign(NULL, NULL, NULL, data_bio, flags | PKCS7_PARTIAL);
> > > > +       if (!p7)
> > > > +               goto err;
> > > > +       if (!PKCS7_sign_add_signer(p7, cert, key, md, flags))
> > > > +               goto err;
> > > > +       if (!PKCS7_final(p7, data_bio, flags))
> > > > +               goto err;
> > > > +
> > > > +       /* convert pkcs7 into DER */
> > > > +       ctx->sig_data = NULL;
> > > > +       ctx->sig_size = ASN1_item_i2d((ASN1_VALUE *)p7, &ctx->sig_data,
> > > > +                                     ASN1_ITEM_rptr(PKCS7));
> > > > +       if (!ctx->sig_size)
> > > > +               goto err;
> > > > +
> > > > +       /* fill auth_info */
> > > > +       ctx->auth.auth_info.hdr.dwLength = sizeof(ctx->auth.auth_info)
> > > > +                                               + ctx->sig_size;
> > > > +       ctx->auth.auth_info.hdr.wRevision = WIN_CERT_REVISION_2_0;
> > > > +       ctx->auth.auth_info.hdr.wCertificateType = WIN_CERT_TYPE_EFI_GUID;
> > > > +       memcpy(&ctx->auth.auth_info.cert_type, &efi_guid_cert_type_pkcs7,
> > > > +              sizeof(efi_guid_cert_type_pkcs7));
> > > > +
> > > > +       ret = 0;
> > > > +err:
> > > > +       BIO_free_all(data_bio);
> > > > +       EVP_PKEY_free(key);
> > > > +       X509_free(cert);
> > > > +
> > > > +       return ret;
> > > > +}
> > > > +
> > > > +/**
> > > > + * dump_signature - dump out a signature
> > > > + * @path:      Path to a capsule file
> > > > + * @signature: Signature data
> > > > + * @sig_size:  Size of signature data
> > > > + *
> > > > + * Signature data pointed to by @signature will be saved into
> > > > + * a file whose file name is @path with ".p7" suffix.
> > > > + *
> > > > + * Return:
> > > > + * * 0  - on success
> > > > + * * -1 - on failure
> > > > + */
> > > > +static int dump_signature(const char *path, u8 *signature, size_t sig_size)
> > > > +{
> > > > +       char *sig_path;
> > > > +       FILE *f;
> > > > +       size_t size;
> > > > +       int ret = -1;
> > > > +
> > > > +       sig_path = malloc(strlen(path) + 3 + 1);
> > > > +       if (!sig_path)
> > > > +               return ret;
> > > > +
> > > > +       sprintf(sig_path, "%s.p7", path);
> > > > +       f = fopen(sig_path, "w");
> > > > +       if (!f)
> > > > +               goto err;
> > > > +
> > > > +       size = fwrite(signature, 1, sig_size, f);
> > > > +       if (size == sig_size)
> > > > +               ret = 0;
> > > > +
> > > > +       fclose(f);
> > > > +err:
> > > > +       free(sig_path);
> > > > +       return ret;
> > > > +}
> > > > +
> > > > +/**
> > > > + * free_sig_data - free out signature data
> > > > + * @ctx:       Pointer to authentication context
> > > > + *
> > > > + * Free signature data allocated in create_auth_data().
> > > > + */
> > > > +static void free_sig_data(struct auth_context *ctx)
> > > > +{
> > > > +       if (ctx->sig_size)
> > > > +               OPENSSL_free(ctx->sig_data);
> > > > +}
> > > > +#else
> > > > +static int create_auth_data(struct auth_context *ctx)
> > > > +{
> > > > +       return 0;
> > > > +}
> > > > +
> > > > +static int dump_signature(const char *path, u8 *signature, size_t sig_size)
> > > > +{
> > > > +       return 0;
> > > > +}
> > > > +
> > > > +static void free_sig_data(struct auth_context *ctx) {}
> > > > +#endif
> > > > +
> > > > +/**
> > > > + * create_fwbin - create an uefi capsule file
> > > > + * @path:      Path to a created capsule file
> > > > + * @bin:       Path to a firmware binary to encapsulate
> > > > + * @guid:      GUID of related FMP driver
> > > > + * @index:     Index number in capsule
> > > > + * @instance:  Instance number in capsule
> > > > + * @mcount:    Monotonic count in authentication information
> > > > + * @private_file:      Path to a private key file
> > > > + * @cert_file: Path to a certificate file
> > > > + *
> > > > + * This function actually does the job of creating an uefi capsule file.
> > > > + * All the arguments must be supplied.
> > > > + * If either @private_file ror @cert_file is NULL, the capsule file
> > > > + * won't be signed.
> > > > + *
> > > > + * Return:
> > > > + * * 0  - on success
> > > > + * * -1 - on failure
> > > > + */
> > > >  static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > > -                       unsigned long index, unsigned long instance)
> > > > +                       unsigned long index, unsigned long instance,
> > > > +                       uint64_t mcount, char *privkey_file, char *cert_file)
> > > >  {
> > > >         struct efi_capsule_header header;
> > > >         struct efi_firmware_management_capsule_header capsule;
> > > >         struct efi_firmware_management_capsule_image_header image;
> > > > +       struct auth_context auth_context;
> > > >         FILE *f, *g;
> > > >         struct stat bin_stat;
> > > >         u8 *data;
> > > > @@ -76,8 +363,9 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > >  #ifdef DEBUG
> > > >         printf("For output: %s\n", path);
> > > >         printf("\tbin: %s\n\ttype: %pUl\n", bin, guid);
> > > > -       printf("\tindex: %ld\n\tinstance: %ld\n", index, instance);
> > > > +       printf("\tindex: %lu\n\tinstance: %lu\n", index, instance);
> > > >  #endif
> > > > +       auth_context.sig_size = 0;
> > > >
> > > >         g = fopen(bin, "r");
> > > >         if (!g) {
> > > > @@ -93,11 +381,34 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > >                 printf("cannot allocate memory: %zx\n", (size_t)bin_stat.st_size);
> > > >                 goto err_1;
> > > >         }
> > > > -       f = fopen(path, "w");
> > > > -       if (!f) {
> > > > -               printf("cannot open %s\n", path);
> > > > +
> > > > +       size = fread(data, 1, bin_stat.st_size, g);
> > > > +       if (size < bin_stat.st_size) {
> > > > +               printf("read failed (%zx)\n", size);
> > > >                 goto err_2;
> > > >         }
> > > > +
> > > > +       /* first, calculate signature to determine its size */
> > > > +       if (privkey_file && cert_file) {
> > > > +               auth_context.key_file = privkey_file;
> > > > +               auth_context.cert_file = cert_file;
> > > > +               auth_context.auth.monotonic_count = mcount;
> > > > +               auth_context.image_data = data;
> > > > +               auth_context.image_size = bin_stat.st_size;
> > > > +
> > > > +               if (create_auth_data(&auth_context)) {
> > > > +                       printf("Signing firmware image failed\n");
> > > > +                       goto err_3;
> > > > +               }
> > > > +
> > > > +               if (dump_sig &&
> > > > +                   dump_signature(path, auth_context.sig_data,
> > > > +                                  auth_context.sig_size)) {
> > > > +                       printf("Creating signature file failed\n");
> > > > +                       goto err_3;
> > > > +               }
> > > > +       }
> > > > +
> > > >         header.capsule_guid = efi_guid_fm_capsule;
> > > >         header.header_size = sizeof(header);
> > > >         /* TODO: The current implementation ignores flags */
> > > > @@ -106,11 +417,20 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > >                                         + sizeof(capsule) + sizeof(u64)
> > > >                                         + sizeof(image)
> > > >                                         + bin_stat.st_size;
> > > > +       if (auth_context.sig_size)
> > > > +               header.capsule_image_size += sizeof(auth_context.auth)
> > > > +                               + auth_context.sig_size;
> > > > +
> > > > +       f = fopen(path, "w");
> > > > +       if (!f) {
> > > > +               printf("cannot open %s\n", path);
> > > > +               goto err_3;
> > > > +       }
> > > >
> > > >         size = fwrite(&header, 1, sizeof(header), f);
> > > >         if (size < sizeof(header)) {
> > > >                 printf("write failed (%zx)\n", size);
> > > > -               goto err_3;
> > > > +               goto err_4;
> > > >         }
> > > >
> > > >         capsule.version = 0x00000001;
> > > > @@ -119,13 +439,13 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > >         size = fwrite(&capsule, 1, sizeof(capsule), f);
> > > >         if (size < (sizeof(capsule))) {
> > > >                 printf("write failed (%zx)\n", size);
> > > > -               goto err_3;
> > > > +               goto err_4;
> > > >         }
> > > >         offset = sizeof(capsule) + sizeof(u64);
> > > >         size = fwrite(&offset, 1, sizeof(offset), f);
> > > >         if (size < sizeof(offset)) {
> > > >                 printf("write failed (%zx)\n", size);
> > > > -               goto err_3;
> > > > +               goto err_4;
> > > >         }
> > > >
> > > >         image.version = 0x00000003;
> > > > @@ -135,34 +455,53 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > >         image.reserved[1] = 0;
> > > >         image.reserved[2] = 0;
> > > >         image.update_image_size = bin_stat.st_size;
> > > > +       if (auth_context.sig_size)
> > > > +               image.update_image_size += sizeof(auth_context.auth)
> > > > +                               + auth_context.sig_size;
> > > >         image.update_vendor_code_size = 0; /* none */
> > > >         image.update_hardware_instance = instance;
> > > >         image.image_capsule_support = 0;
> > > > +       if (auth_context.sig_size)
> > > > +               image.image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION;
> > > >
> > > >         size = fwrite(&image, 1, sizeof(image), f);
> > > >         if (size < sizeof(image)) {
> > > >                 printf("write failed (%zx)\n", size);
> > > > -               goto err_3;
> > > > +               goto err_4;
> > > >         }
> > > > -       size = fread(data, 1, bin_stat.st_size, g);
> > > > -       if (size < bin_stat.st_size) {
> > > > -               printf("read failed (%zx)\n", size);
> > > > -               goto err_3;
> > > > +
> > > > +       if (auth_context.sig_size) {
> > > > +               size = fwrite(&auth_context.auth, 1,
> > > > +                             sizeof(auth_context.auth), f);
> > > > +               if (size < sizeof(auth_context.auth)) {
> > > > +                       printf("write failed (%zx)\n", size);
> > > > +                       goto err_4;
> > > > +               }
> > > > +               size = fwrite(auth_context.sig_data, 1,
> > > > +                             auth_context.sig_size, f);
> > > > +               if (size < auth_context.sig_size) {
> > > > +                       printf("write failed (%zx)\n", size);
> > > > +                       goto err_4;
> > > > +               }
> > > >         }
> > > > +
> > > >         size = fwrite(data, 1, bin_stat.st_size, f);
> > > >         if (size < bin_stat.st_size) {
> > > >                 printf("write failed (%zx)\n", size);
> > > > -               goto err_3;
> > > > +               goto err_4;
> > > >         }
> > > >
> > > >         fclose(f);
> > > >         fclose(g);
> > > >         free(data);
> > > > +       free_sig_data(&auth_context);
> > > >
> > > >         return 0;
> > > >
> > > > -err_3:
> > > > +err_4:
> > > >         fclose(f);
> > > > +err_3:
> > > > +       free_sig_data(&auth_context);
> > > >  err_2:
> > > >         free(data);
> > > >  err_1:
> > > > @@ -171,23 +510,37 @@ err_1:
> > > >         return -1;
> > > >  }
> > > >
> > > > -/*
> > > > - * Usage:
> > > > - *   $ mkeficapsule -f <firmware binary> <output file>
> > > > +/**
> > > > + * main - main entry function of mkeficapsule
> > > > + * @argc:      Number of arguments
> > > > + * @argv:      Array of pointers to arguments
> > > > + *
> > > > + * Create an uefi capsule file, optionally signing it.
> > > > + * Parse all the arguments and pass them on to create_fwbin().
> > > > + *
> > > > + * Return:
> > > > + * * 0  - on success
> > > > + * * -1 - on failure
> > > >   */
> > > >  int main(int argc, char **argv)
> > > >  {
> > > >         char *file;
> > > >         efi_guid_t *guid;
> > > >         unsigned long index, instance;
> > > > +       uint64_t mcount;
> > > > +       char *privkey_file, *cert_file;
> > > >         int c, idx;
> > > >
> > > >         file = NULL;
> > > >         guid = NULL;
> > > >         index = 0;
> > > >         instance = 0;
> > > > +       mcount = 0;
> > > > +       privkey_file = NULL;
> > > > +       cert_file = NULL;
> > > > +       dump_sig = 0;
> > > >         for (;;) {
> > > > -               c = getopt_long(argc, argv, "f:r:i:I:v:h", options, &idx);
> > > > +               c = getopt_long(argc, argv, opts_short, options, &idx);
> > > >                 if (c == -1)
> > > >                         break;
> > > >
> > > > @@ -214,29 +567,47 @@ int main(int argc, char **argv)
> > > >                 case 'I':
> > > >                         instance = strtoul(optarg, NULL, 0);
> > > >                         break;
> > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO
> > > > +               case 'p':
> > > > +                       if (privkey_file) {
> > > > +                               printf("Private Key already specified\n");
> > > > +                               return -1;
> > > > +                       }
> > > > +                       privkey_file = optarg;
> > > > +                       break;
> > > > +               case 'c':
> > > > +                       if (cert_file) {
> > > > +                               printf("Certificate file already specified\n");
> > > > +                               return -1;
> > > > +                       }
> > > > +                       cert_file = optarg;
> > > > +                       break;
> > > > +               case 'm':
> > > > +                       mcount = strtoul(optarg, NULL, 0);
> > > > +                       break;
> > > > +               case 'd':
> > > > +                       dump_sig = 1;
> > > > +                       break;
> > > > +#endif /* CONFIG_TOOLS_LIBCRYPTO */
> > > >                 case 'h':
> > > >                         print_usage();
> > > >                         return 0;
> > > >                 }
> > > >         }
> > > >
> > > > -       /* need an output file */
> > > > -       if (argc != optind + 1) {
> > > > -               print_usage();
> > > > -               exit(EXIT_FAILURE);
> > > > -       }
> > > > -
> > > > -       /* need a fit image file or raw image file */
> > > > -       if (!file) {
> > > > +       /* check necessary parameters */
> > > > +       if ((argc != optind + 1) || !file ||
> > > > +           ((privkey_file && !cert_file) ||
> > > > +            (!privkey_file && cert_file))) {
> > > >                 print_usage();
> > > > -               exit(EXIT_SUCCESS);
> > > > +               return -1;
> > > >         }
> > > >
> > > > -       if (create_fwbin(argv[optind], file, guid, index, instance)
> > > > -                       < 0) {
> > > > +       if (create_fwbin(argv[optind], file, guid, index, instance,
> > > > +                        mcount, privkey_file, cert_file) < 0) {
> > > >                 printf("Creating firmware capsule failed\n");
> > > > -               exit(EXIT_FAILURE);
> > > > +               return -1;
> > > >         }
> > > >
> > > > -       exit(EXIT_SUCCESS);
> > > > +       return 0;
> > > >  }
> > > > --
> > > > 2.33.0
> > > >
> > >
> > >
> > > --
> > > Masami Hiramatsu
> 
> 
> 
> -- 
> Masami Hiramatsu

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v4 03/11] efi_loader: capsule: add back efi_get_public_key_data()
  2021-10-25  5:14         ` AKASHI Takahiro
@ 2021-10-25  6:28           ` François Ozog
  2021-10-25  7:04             ` Masami Hiramatsu
  0 siblings, 1 reply; 40+ messages in thread
From: François Ozog @ 2021-10-25  6:28 UTC (permalink / raw)
  To: AKASHI Takahiro, Alex Graf, Heinrich Schuchardt,
	Ilias Apalodimas, Masami Hiramatsu, Simon Glass, Sughosh Ganu,
	U-Boot Mailing List

Le lun. 25 oct. 2021 à 07:14, AKASHI Takahiro <takahiro.akashi@linaro.org>
a écrit :

> On Wed, Oct 20, 2021 at 07:39:37AM -0600, Simon Glass wrote:
> > Hi Masami,
> >
> > On Wed, 20 Oct 2021 at 02:18, Masami Hiramatsu
> > <masami.hiramatsu@linaro.org> wrote:
> > >
> > > Hi Simon,
> > >
> > > 2021年10月15日(金) 9:40 Simon Glass <sjg@chromium.org>:
> > > >
> > > > Hi Takahiro,
> > > >
> > > > On Thu, 7 Oct 2021 at 00:25, AKASHI Takahiro <
> takahiro.akashi@linaro.org> wrote:
> > > > >
> > > > > The commit 47a25e81d35c ("Revert "efi_capsule: Move signature from
> DTB to
> > > > > .rodata"") failed to revert the removal of
> efi_get_public_key_data().
> > > > >
> > > > > Add back this function and move it under lib/efi_loader so that
> other
> > > > > platforms can utilize it. It is now declared as a weak function so
> that
> > > > > it can be replaced with a platform-specific implementation.
> > > > >
> > > > > Fixes: 47a25e81d35c ("Revert "efi_capsule: Move signature from DTB
> to
> > > > >         .rodata"")
> > > > > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > > > > ---
> > > > >  lib/efi_loader/efi_capsule.c | 36
> ++++++++++++++++++++++++++++++++++++
> > > > >  1 file changed, 36 insertions(+)
> > > > >
> > > > > diff --git a/lib/efi_loader/efi_capsule.c
> b/lib/efi_loader/efi_capsule.c
> > > > > index b75e4bcba1a9..44f5da61a9be 100644
> > > > > --- a/lib/efi_loader/efi_capsule.c
> > > > > +++ b/lib/efi_loader/efi_capsule.c
> > > > > @@ -11,15 +11,20 @@
> > > > >  #include <common.h>
> > > > >  #include <efi_loader.h>
> > > > >  #include <efi_variable.h>
> > > > > +#include <env.h>
> > > > > +#include <fdtdec.h>
> > > > >  #include <fs.h>
> > > > >  #include <malloc.h>
> > > > >  #include <mapmem.h>
> > > > >  #include <sort.h>
> > > > > +#include <asm/global_data.h>
> > > > >
> > > > >  #include <crypto/pkcs7.h>
> > > > >  #include <crypto/pkcs7_parser.h>
> > > > >  #include <linux/err.h>
> > > > >
> > > > > +DECLARE_GLOBAL_DATA_PTR;
> > > > > +
> > > > >  const efi_guid_t efi_guid_capsule_report =
> EFI_CAPSULE_REPORT_GUID;
> > > > >  static const efi_guid_t efi_guid_firmware_management_capsule_id =
> > > > >                 EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID;
> > > > > @@ -251,6 +256,37 @@ out:
> > > > >  }
> > > > >
> > > > >  #if defined(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> > > > > +int __weak efi_get_public_key_data(void **pkey, efi_uintn_t
> *pkey_len)
> > > >
> > > > I don't think this should be weak. What other way is there of
> handling
> > > > this and why would it be platform-specific?
> > >
> > > I have a question about the current design of the capsule auth key.
> > > If the platform has its own key-storage, how can the platform use the
> > > platform specific storage? Does such platform load the key from the
> storage
> > > and generate the dtb node in the platform initialization code? (or
> > > device driver?)
> >
> > Are we talking about a public key (which I assume from the function
> > naming) or some secret key. What is an auth key?
>
> Surely, a public key (more strictly, x509 certificate under the current
> implementation)
>
> > As I understand it, the public key should be provided by the platform
> > in devicetree that U-Boot uses, by whatever prior stage has access to
> > the key.
>
> I still believe that some platform provider may want to save the key
> in a *safer* storage, which should be at least read-only for non-authorized
> writers.


indeed. And U-Boot may not be entitled at all to check the capsule
signature. For example updating SCP firmware with a key that is in secure
world and will never leave this world.


But if this issue (__weak or not) is the only blocking factor
> for my entire patch series, I'm willing to drop __weak for now since
> someone with production system may change it in the future when he has
> a good reason for that :)


If that need be….


>
> -Takahiro Akashi
>
>
> > Regards,
> > Simon
>
-- 
François-Frédéric Ozog | *Director Business Development*
T: +33.67221.6485
francois.ozog@linaro.org | Skype: ffozog

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v4 05/11] tools: mkeficapsule: add firmwware image signing
  2021-10-25  6:09         ` AKASHI Takahiro
@ 2021-10-25  7:04           ` Masami Hiramatsu
  0 siblings, 0 replies; 40+ messages in thread
From: Masami Hiramatsu @ 2021-10-25  7:04 UTC (permalink / raw)
  To: AKASHI Takahiro, Masami Hiramatsu, Heinrich Schuchardt,
	Alexander Graf, Ilias Apalodimas, Sughosh Ganu,
	U-Boot Mailing List

Hi Takahiro,

2021年10月25日(月) 15:09 AKASHI Takahiro <takahiro.akashi@linaro.org>:
>
> On Mon, Oct 25, 2021 at 02:40:11PM +0900, Masami Hiramatsu wrote:
> > Hi Takahiro,
> >
> > 2021年10月25日(月) 12:12 AKASHI Takahiro <takahiro.akashi@linaro.org>:
> > >
> > > Hi, Masami,
> > >
> > > On Wed, Oct 20, 2021 at 05:17:12PM +0900, Masami Hiramatsu wrote:
> > > > Hello Akashi-san,
> > > >
> > > > Can you split this patch out from this series?
> > > > It seems that the series is doing several different things. This
> > > > capsule signing has no alternatives, but others are modifying or
> > > > replacing the current existing feature. In other words, this capsule
> > > > signing feature has no alternatives, but others we can continue to
> > > > use.
> > >
> > > What do you specifically mean by "existing feature" or "others we can
> > > continue to use"?
> > >
> > > All the commits, I think, in this series are closely related with
> > > each other.
> >
> > I meant I can use the devicetree to embed the authentication key in the
> > U-Boot itself (currently U-Boot expects that.) Or is there any issue with
> > putting the key in the devicetree?
>
> I don't get why this particular patch be separated from the others.
> Some are prerequisite "revert" commits, some are updates on documents,
> and some are test scriptsa All changes can and should be merged at one time.

Ah, OK. I got it. I just thought, adding lacked feature and update documents
for that should not be decoupled, but I thought the "revert" part can
be decoupled.

Thank you,

>
> > Without this patch, I can not sign the capsule file for U-Boot, since
> > the GenerateCapsule in EDK2 supports old format (is that updated?)
>
> I don't know, but Sughosh was working on the issue, he might have a patch
> for EDK2?
>
> -Takahiro Akashi
>
> > Thank you,
> >
> >
> > >
> > > -Takahiro Akashi
> > >
> > > > Thank you,
> > > >
> > > > 2021年10月7日(木) 15:25 AKASHI Takahiro <takahiro.akashi@linaro.org>:
> > > > >
> > > > > With this enhancement, mkeficapsule will be able to sign a capsule
> > > > > file when it is created. A signature added will be used later
> > > > > in the verification at FMP's SetImage() call.
> > > > >
> > > > > To do that, We need specify additional command parameters:
> > > > >   -monotonic-cout <count> : monotonic count
> > > > >   -private-key <private key file> : private key file
> > > > >   -certificate <certificate file> : certificate file
> > > > > Only when all of those parameters are given, a signature will be added
> > > > > to a capsule file.
> > > > >
> > > > > Users are expected to maintain and increment the monotonic count at
> > > > > every time of the update for each firmware image.
> > > > >
> > > > > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > > > > ---
> > > > >  tools/Kconfig        |   7 +
> > > > >  tools/Makefile       |   8 +-
> > > > >  tools/mkeficapsule.c | 435 +++++++++++++++++++++++++++++++++++++++----
> > > > >  3 files changed, 416 insertions(+), 34 deletions(-)
> > > > >
> > > > > diff --git a/tools/Kconfig b/tools/Kconfig
> > > > > index d6f82cd949b5..9a37ed035311 100644
> > > > > --- a/tools/Kconfig
> > > > > +++ b/tools/Kconfig
> > > > > @@ -20,4 +20,11 @@ config TOOLS_LIBCRYPTO
> > > > >           This selection does not affect target features, such as runtime FIT
> > > > >           signature verification.
> > > > >
> > > > > +config TOOLS_MKEFICAPSULE
> > > > > +       bool "Build efimkcapsule command"
> > > > > +       default y if EFI_CAPSULE_ON_DISK
> > > > > +       help
> > > > > +         This command allows users to create a UEFI capsule file and,
> > > > > +         optionally sign that file. If you want to enable UEFI capsule
> > > > > +         update feature on your target, you certainly need this.
> > > > >  endmenu
> > > > > diff --git a/tools/Makefile b/tools/Makefile
> > > > > index 4a86321f6467..6ea3033dbfb8 100644
> > > > > --- a/tools/Makefile
> > > > > +++ b/tools/Makefile
> > > > > @@ -237,8 +237,12 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs
> > > > >  hostprogs-$(CONFIG_ASN1_COMPILER)      += asn1_compiler
> > > > >  HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
> > > > >
> > > > > -mkeficapsule-objs      := mkeficapsule.o $(LIBFDT_OBJS)
> > > > > -hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
> > > > > +HOSTLDLIBS_mkeficapsule += -luuid
> > > > > +ifeq ($(CONFIG_TOOLS_LIBCRYPTO),y)
> > > > > +HOSTLDLIBS_mkeficapsule += \
> > > > > +       $(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto")
> > > > > +endif
> > > > > +hostprogs-$(CONFIG_TOOLS_MKEFICAPSULE) += mkeficapsule
> > > > >
> > > > >  # We build some files with extra pedantic flags to try to minimize things
> > > > >  # that won't build on some weird host compiler -- though there are lots of
> > > > > diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
> > > > > index 4995ba4e0c2a..5541e4bda894 100644
> > > > > --- a/tools/mkeficapsule.c
> > > > > +++ b/tools/mkeficapsule.c
> > > > > @@ -15,6 +15,16 @@
> > > > >  #include <sys/stat.h>
> > > > >  #include <sys/types.h>
> > > > >
> > > > > +#include <linux/kconfig.h>
> > > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO
> > > > > +#include <openssl/asn1.h>
> > > > > +#include <openssl/bio.h>
> > > > > +#include <openssl/evp.h>
> > > > > +#include <openssl/err.h>
> > > > > +#include <openssl/pem.h>
> > > > > +#include <openssl/pkcs7.h>
> > > > > +#endif
> > > > > +
> > > > >  typedef __u8 u8;
> > > > >  typedef __u16 u16;
> > > > >  typedef __u32 u32;
> > > > > @@ -38,12 +48,25 @@ efi_guid_t efi_guid_image_type_uboot_fit =
> > > > >                 EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID;
> > > > >  efi_guid_t efi_guid_image_type_uboot_raw =
> > > > >                 EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID;
> > > > > +efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
> > > > > +
> > > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO
> > > > > +static const char *opts_short = "f:r:i:I:v:p:c:m:dh";
> > > > > +#else
> > > > > +static const char *opts_short = "f:r:i:I:v:h";
> > > > > +#endif
> > > > >
> > > > >  static struct option options[] = {
> > > > >         {"fit", required_argument, NULL, 'f'},
> > > > >         {"raw", required_argument, NULL, 'r'},
> > > > >         {"index", required_argument, NULL, 'i'},
> > > > >         {"instance", required_argument, NULL, 'I'},
> > > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO
> > > > > +       {"private-key", required_argument, NULL, 'p'},
> > > > > +       {"certificate", required_argument, NULL, 'c'},
> > > > > +       {"monotonic-count", required_argument, NULL, 'm'},
> > > > > +       {"dump-sig", no_argument, NULL, 'd'},
> > > > > +#endif
> > > > >         {"help", no_argument, NULL, 'h'},
> > > > >         {NULL, 0, NULL, 0},
> > > > >  };
> > > > > @@ -57,16 +80,280 @@ static void print_usage(void)
> > > > >                "\t-r, --raw <raw image>       new raw image file\n"
> > > > >                "\t-i, --index <index>         update image index\n"
> > > > >                "\t-I, --instance <instance>   update hardware instance\n"
> > > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO
> > > > > +              "\t-p, --private-key <privkey file>  private key file\n"
> > > > > +              "\t-c, --certificate <cert file>     signer's certificate file\n"
> > > > > +              "\t-m, --monotonic-count <count>     monotonic count\n"
> > > > > +              "\t-d, --dump_sig              dump signature (*.p7)\n"
> > > > > +#endif
> > > > >                "\t-h, --help                  print a help message\n",
> > > > >                tool_name);
> > > > >  }
> > > > >
> > > > > +/**
> > > > > + * auth_context - authentication context
> > > > > + * @key_file:  Path to a private key file
> > > > > + * @cert_file: Path to a certificate file
> > > > > + * @image_data:        Pointer to firmware data
> > > > > + * @image_size:        Size of firmware data
> > > > > + * @auth:      Authentication header
> > > > > + * @sig_data:  Signature data
> > > > > + * @sig_size:  Size of signature data
> > > > > + *
> > > > > + * Data structure used in create_auth_data(). @key_file through
> > > > > + * @image_size are input parameters. @auth, @sig_data and @sig_size
> > > > > + * are filled in by create_auth_data().
> > > > > + */
> > > > > +struct auth_context {
> > > > > +       char *key_file;
> > > > > +       char *cert_file;
> > > > > +       u8 *image_data;
> > > > > +       size_t image_size;
> > > > > +       struct efi_firmware_image_authentication auth;
> > > > > +       u8 *sig_data;
> > > > > +       size_t sig_size;
> > > > > +};
> > > > > +
> > > > > +static int dump_sig;
> > > > > +
> > > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO
> > > > > +/**
> > > > > + * fileio-read_pkey - read out a private key
> > > > > + * @filename:  Path to a private key file
> > > > > + *
> > > > > + * Read out a private key file and parse it into "EVP_PKEY" structure.
> > > > > + *
> > > > > + * Return:
> > > > > + * * Pointer to private key structure  - on success
> > > > > + * * NULL - on failure
> > > > > + */
> > > > > +static EVP_PKEY *fileio_read_pkey(const char *filename)
> > > > > +{
> > > > > +       EVP_PKEY *key = NULL;
> > > > > +       BIO *bio;
> > > > > +
> > > > > +       bio = BIO_new_file(filename, "r");
> > > > > +       if (!bio)
> > > > > +               goto out;
> > > > > +
> > > > > +       key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
> > > > > +
> > > > > +out:
> > > > > +       BIO_free_all(bio);
> > > > > +       if (!key) {
> > > > > +               printf("Can't load key from file '%s'\n", filename);
> > > > > +               ERR_print_errors_fp(stderr);
> > > > > +       }
> > > > > +
> > > > > +       return key;
> > > > > +}
> > > > > +
> > > > > +/**
> > > > > + * fileio-read_cert - read out a certificate
> > > > > + * @filename:  Path to a certificate file
> > > > > + *
> > > > > + * Read out a certificate file and parse it into "X509" structure.
> > > > > + *
> > > > > + * Return:
> > > > > + * * Pointer to certificate structure  - on success
> > > > > + * * NULL - on failure
> > > > > + */
> > > > > +static X509 *fileio_read_cert(const char *filename)
> > > > > +{
> > > > > +       X509 *cert = NULL;
> > > > > +       BIO *bio;
> > > > > +
> > > > > +       bio = BIO_new_file(filename, "r");
> > > > > +       if (!bio)
> > > > > +               goto out;
> > > > > +
> > > > > +       cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
> > > > > +
> > > > > +out:
> > > > > +       BIO_free_all(bio);
> > > > > +       if (!cert) {
> > > > > +               printf("Can't load certificate from file '%s'\n", filename);
> > > > > +               ERR_print_errors_fp(stderr);
> > > > > +       }
> > > > > +
> > > > > +       return cert;
> > > > > +}
> > > > > +
> > > > > +/**
> > > > > + * create_auth_data - compose authentication data in capsule
> > > > > + * @auth_context:      Pointer to authentication context
> > > > > + *
> > > > > + * Fill up an authentication header (.auth) and signature data (.sig_data)
> > > > > + * in @auth_context, using library functions from openssl.
> > > > > + * All the parameters in @auth_context must be filled in by a caller.
> > > > > + *
> > > > > + * Return:
> > > > > + * * 0  - on success
> > > > > + * * -1 - on failure
> > > > > + */
> > > > > +static int create_auth_data(struct auth_context *ctx)
> > > > > +{
> > > > > +       EVP_PKEY *key = NULL;
> > > > > +       X509 *cert = NULL;
> > > > > +       BIO *data_bio = NULL;
> > > > > +       const EVP_MD *md;
> > > > > +       PKCS7 *p7;
> > > > > +       int flags, ret = -1;
> > > > > +
> > > > > +       OpenSSL_add_all_digests();
> > > > > +       OpenSSL_add_all_ciphers();
> > > > > +       ERR_load_crypto_strings();
> > > > > +
> > > > > +       key = fileio_read_pkey(ctx->key_file);
> > > > > +       if (!key)
> > > > > +               goto err;
> > > > > +       cert = fileio_read_cert(ctx->cert_file);
> > > > > +       if (!cert)
> > > > > +               goto err;
> > > > > +
> > > > > +       /*
> > > > > +        * create a BIO, containing:
> > > > > +        *  * firmware image
> > > > > +        *  * monotonic count
> > > > > +        * in this order!
> > > > > +        * See EDK2's FmpAuthenticatedHandlerRsa2048Sha256()
> > > > > +        */
> > > > > +       data_bio = BIO_new(BIO_s_mem());
> > > > > +       BIO_write(data_bio, ctx->image_data, ctx->image_size);
> > > > > +       BIO_write(data_bio, &ctx->auth.monotonic_count,
> > > > > +                 sizeof(ctx->auth.monotonic_count));
> > > > > +
> > > > > +       md = EVP_get_digestbyname("SHA256");
> > > > > +       if (!md)
> > > > > +               goto err;
> > > > > +
> > > > > +       /* create signature */
> > > > > +       /* TODO: maybe add PKCS7_NOATTR and PKCS7_NOSMIMECAP */
> > > > > +       flags = PKCS7_BINARY | PKCS7_DETACHED;
> > > > > +       p7 = PKCS7_sign(NULL, NULL, NULL, data_bio, flags | PKCS7_PARTIAL);
> > > > > +       if (!p7)
> > > > > +               goto err;
> > > > > +       if (!PKCS7_sign_add_signer(p7, cert, key, md, flags))
> > > > > +               goto err;
> > > > > +       if (!PKCS7_final(p7, data_bio, flags))
> > > > > +               goto err;
> > > > > +
> > > > > +       /* convert pkcs7 into DER */
> > > > > +       ctx->sig_data = NULL;
> > > > > +       ctx->sig_size = ASN1_item_i2d((ASN1_VALUE *)p7, &ctx->sig_data,
> > > > > +                                     ASN1_ITEM_rptr(PKCS7));
> > > > > +       if (!ctx->sig_size)
> > > > > +               goto err;
> > > > > +
> > > > > +       /* fill auth_info */
> > > > > +       ctx->auth.auth_info.hdr.dwLength = sizeof(ctx->auth.auth_info)
> > > > > +                                               + ctx->sig_size;
> > > > > +       ctx->auth.auth_info.hdr.wRevision = WIN_CERT_REVISION_2_0;
> > > > > +       ctx->auth.auth_info.hdr.wCertificateType = WIN_CERT_TYPE_EFI_GUID;
> > > > > +       memcpy(&ctx->auth.auth_info.cert_type, &efi_guid_cert_type_pkcs7,
> > > > > +              sizeof(efi_guid_cert_type_pkcs7));
> > > > > +
> > > > > +       ret = 0;
> > > > > +err:
> > > > > +       BIO_free_all(data_bio);
> > > > > +       EVP_PKEY_free(key);
> > > > > +       X509_free(cert);
> > > > > +
> > > > > +       return ret;
> > > > > +}
> > > > > +
> > > > > +/**
> > > > > + * dump_signature - dump out a signature
> > > > > + * @path:      Path to a capsule file
> > > > > + * @signature: Signature data
> > > > > + * @sig_size:  Size of signature data
> > > > > + *
> > > > > + * Signature data pointed to by @signature will be saved into
> > > > > + * a file whose file name is @path with ".p7" suffix.
> > > > > + *
> > > > > + * Return:
> > > > > + * * 0  - on success
> > > > > + * * -1 - on failure
> > > > > + */
> > > > > +static int dump_signature(const char *path, u8 *signature, size_t sig_size)
> > > > > +{
> > > > > +       char *sig_path;
> > > > > +       FILE *f;
> > > > > +       size_t size;
> > > > > +       int ret = -1;
> > > > > +
> > > > > +       sig_path = malloc(strlen(path) + 3 + 1);
> > > > > +       if (!sig_path)
> > > > > +               return ret;
> > > > > +
> > > > > +       sprintf(sig_path, "%s.p7", path);
> > > > > +       f = fopen(sig_path, "w");
> > > > > +       if (!f)
> > > > > +               goto err;
> > > > > +
> > > > > +       size = fwrite(signature, 1, sig_size, f);
> > > > > +       if (size == sig_size)
> > > > > +               ret = 0;
> > > > > +
> > > > > +       fclose(f);
> > > > > +err:
> > > > > +       free(sig_path);
> > > > > +       return ret;
> > > > > +}
> > > > > +
> > > > > +/**
> > > > > + * free_sig_data - free out signature data
> > > > > + * @ctx:       Pointer to authentication context
> > > > > + *
> > > > > + * Free signature data allocated in create_auth_data().
> > > > > + */
> > > > > +static void free_sig_data(struct auth_context *ctx)
> > > > > +{
> > > > > +       if (ctx->sig_size)
> > > > > +               OPENSSL_free(ctx->sig_data);
> > > > > +}
> > > > > +#else
> > > > > +static int create_auth_data(struct auth_context *ctx)
> > > > > +{
> > > > > +       return 0;
> > > > > +}
> > > > > +
> > > > > +static int dump_signature(const char *path, u8 *signature, size_t sig_size)
> > > > > +{
> > > > > +       return 0;
> > > > > +}
> > > > > +
> > > > > +static void free_sig_data(struct auth_context *ctx) {}
> > > > > +#endif
> > > > > +
> > > > > +/**
> > > > > + * create_fwbin - create an uefi capsule file
> > > > > + * @path:      Path to a created capsule file
> > > > > + * @bin:       Path to a firmware binary to encapsulate
> > > > > + * @guid:      GUID of related FMP driver
> > > > > + * @index:     Index number in capsule
> > > > > + * @instance:  Instance number in capsule
> > > > > + * @mcount:    Monotonic count in authentication information
> > > > > + * @private_file:      Path to a private key file
> > > > > + * @cert_file: Path to a certificate file
> > > > > + *
> > > > > + * This function actually does the job of creating an uefi capsule file.
> > > > > + * All the arguments must be supplied.
> > > > > + * If either @private_file ror @cert_file is NULL, the capsule file
> > > > > + * won't be signed.
> > > > > + *
> > > > > + * Return:
> > > > > + * * 0  - on success
> > > > > + * * -1 - on failure
> > > > > + */
> > > > >  static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > > > -                       unsigned long index, unsigned long instance)
> > > > > +                       unsigned long index, unsigned long instance,
> > > > > +                       uint64_t mcount, char *privkey_file, char *cert_file)
> > > > >  {
> > > > >         struct efi_capsule_header header;
> > > > >         struct efi_firmware_management_capsule_header capsule;
> > > > >         struct efi_firmware_management_capsule_image_header image;
> > > > > +       struct auth_context auth_context;
> > > > >         FILE *f, *g;
> > > > >         struct stat bin_stat;
> > > > >         u8 *data;
> > > > > @@ -76,8 +363,9 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > > >  #ifdef DEBUG
> > > > >         printf("For output: %s\n", path);
> > > > >         printf("\tbin: %s\n\ttype: %pUl\n", bin, guid);
> > > > > -       printf("\tindex: %ld\n\tinstance: %ld\n", index, instance);
> > > > > +       printf("\tindex: %lu\n\tinstance: %lu\n", index, instance);
> > > > >  #endif
> > > > > +       auth_context.sig_size = 0;
> > > > >
> > > > >         g = fopen(bin, "r");
> > > > >         if (!g) {
> > > > > @@ -93,11 +381,34 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > > >                 printf("cannot allocate memory: %zx\n", (size_t)bin_stat.st_size);
> > > > >                 goto err_1;
> > > > >         }
> > > > > -       f = fopen(path, "w");
> > > > > -       if (!f) {
> > > > > -               printf("cannot open %s\n", path);
> > > > > +
> > > > > +       size = fread(data, 1, bin_stat.st_size, g);
> > > > > +       if (size < bin_stat.st_size) {
> > > > > +               printf("read failed (%zx)\n", size);
> > > > >                 goto err_2;
> > > > >         }
> > > > > +
> > > > > +       /* first, calculate signature to determine its size */
> > > > > +       if (privkey_file && cert_file) {
> > > > > +               auth_context.key_file = privkey_file;
> > > > > +               auth_context.cert_file = cert_file;
> > > > > +               auth_context.auth.monotonic_count = mcount;
> > > > > +               auth_context.image_data = data;
> > > > > +               auth_context.image_size = bin_stat.st_size;
> > > > > +
> > > > > +               if (create_auth_data(&auth_context)) {
> > > > > +                       printf("Signing firmware image failed\n");
> > > > > +                       goto err_3;
> > > > > +               }
> > > > > +
> > > > > +               if (dump_sig &&
> > > > > +                   dump_signature(path, auth_context.sig_data,
> > > > > +                                  auth_context.sig_size)) {
> > > > > +                       printf("Creating signature file failed\n");
> > > > > +                       goto err_3;
> > > > > +               }
> > > > > +       }
> > > > > +
> > > > >         header.capsule_guid = efi_guid_fm_capsule;
> > > > >         header.header_size = sizeof(header);
> > > > >         /* TODO: The current implementation ignores flags */
> > > > > @@ -106,11 +417,20 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > > >                                         + sizeof(capsule) + sizeof(u64)
> > > > >                                         + sizeof(image)
> > > > >                                         + bin_stat.st_size;
> > > > > +       if (auth_context.sig_size)
> > > > > +               header.capsule_image_size += sizeof(auth_context.auth)
> > > > > +                               + auth_context.sig_size;
> > > > > +
> > > > > +       f = fopen(path, "w");
> > > > > +       if (!f) {
> > > > > +               printf("cannot open %s\n", path);
> > > > > +               goto err_3;
> > > > > +       }
> > > > >
> > > > >         size = fwrite(&header, 1, sizeof(header), f);
> > > > >         if (size < sizeof(header)) {
> > > > >                 printf("write failed (%zx)\n", size);
> > > > > -               goto err_3;
> > > > > +               goto err_4;
> > > > >         }
> > > > >
> > > > >         capsule.version = 0x00000001;
> > > > > @@ -119,13 +439,13 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > > >         size = fwrite(&capsule, 1, sizeof(capsule), f);
> > > > >         if (size < (sizeof(capsule))) {
> > > > >                 printf("write failed (%zx)\n", size);
> > > > > -               goto err_3;
> > > > > +               goto err_4;
> > > > >         }
> > > > >         offset = sizeof(capsule) + sizeof(u64);
> > > > >         size = fwrite(&offset, 1, sizeof(offset), f);
> > > > >         if (size < sizeof(offset)) {
> > > > >                 printf("write failed (%zx)\n", size);
> > > > > -               goto err_3;
> > > > > +               goto err_4;
> > > > >         }
> > > > >
> > > > >         image.version = 0x00000003;
> > > > > @@ -135,34 +455,53 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > > >         image.reserved[1] = 0;
> > > > >         image.reserved[2] = 0;
> > > > >         image.update_image_size = bin_stat.st_size;
> > > > > +       if (auth_context.sig_size)
> > > > > +               image.update_image_size += sizeof(auth_context.auth)
> > > > > +                               + auth_context.sig_size;
> > > > >         image.update_vendor_code_size = 0; /* none */
> > > > >         image.update_hardware_instance = instance;
> > > > >         image.image_capsule_support = 0;
> > > > > +       if (auth_context.sig_size)
> > > > > +               image.image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION;
> > > > >
> > > > >         size = fwrite(&image, 1, sizeof(image), f);
> > > > >         if (size < sizeof(image)) {
> > > > >                 printf("write failed (%zx)\n", size);
> > > > > -               goto err_3;
> > > > > +               goto err_4;
> > > > >         }
> > > > > -       size = fread(data, 1, bin_stat.st_size, g);
> > > > > -       if (size < bin_stat.st_size) {
> > > > > -               printf("read failed (%zx)\n", size);
> > > > > -               goto err_3;
> > > > > +
> > > > > +       if (auth_context.sig_size) {
> > > > > +               size = fwrite(&auth_context.auth, 1,
> > > > > +                             sizeof(auth_context.auth), f);
> > > > > +               if (size < sizeof(auth_context.auth)) {
> > > > > +                       printf("write failed (%zx)\n", size);
> > > > > +                       goto err_4;
> > > > > +               }
> > > > > +               size = fwrite(auth_context.sig_data, 1,
> > > > > +                             auth_context.sig_size, f);
> > > > > +               if (size < auth_context.sig_size) {
> > > > > +                       printf("write failed (%zx)\n", size);
> > > > > +                       goto err_4;
> > > > > +               }
> > > > >         }
> > > > > +
> > > > >         size = fwrite(data, 1, bin_stat.st_size, f);
> > > > >         if (size < bin_stat.st_size) {
> > > > >                 printf("write failed (%zx)\n", size);
> > > > > -               goto err_3;
> > > > > +               goto err_4;
> > > > >         }
> > > > >
> > > > >         fclose(f);
> > > > >         fclose(g);
> > > > >         free(data);
> > > > > +       free_sig_data(&auth_context);
> > > > >
> > > > >         return 0;
> > > > >
> > > > > -err_3:
> > > > > +err_4:
> > > > >         fclose(f);
> > > > > +err_3:
> > > > > +       free_sig_data(&auth_context);
> > > > >  err_2:
> > > > >         free(data);
> > > > >  err_1:
> > > > > @@ -171,23 +510,37 @@ err_1:
> > > > >         return -1;
> > > > >  }
> > > > >
> > > > > -/*
> > > > > - * Usage:
> > > > > - *   $ mkeficapsule -f <firmware binary> <output file>
> > > > > +/**
> > > > > + * main - main entry function of mkeficapsule
> > > > > + * @argc:      Number of arguments
> > > > > + * @argv:      Array of pointers to arguments
> > > > > + *
> > > > > + * Create an uefi capsule file, optionally signing it.
> > > > > + * Parse all the arguments and pass them on to create_fwbin().
> > > > > + *
> > > > > + * Return:
> > > > > + * * 0  - on success
> > > > > + * * -1 - on failure
> > > > >   */
> > > > >  int main(int argc, char **argv)
> > > > >  {
> > > > >         char *file;
> > > > >         efi_guid_t *guid;
> > > > >         unsigned long index, instance;
> > > > > +       uint64_t mcount;
> > > > > +       char *privkey_file, *cert_file;
> > > > >         int c, idx;
> > > > >
> > > > >         file = NULL;
> > > > >         guid = NULL;
> > > > >         index = 0;
> > > > >         instance = 0;
> > > > > +       mcount = 0;
> > > > > +       privkey_file = NULL;
> > > > > +       cert_file = NULL;
> > > > > +       dump_sig = 0;
> > > > >         for (;;) {
> > > > > -               c = getopt_long(argc, argv, "f:r:i:I:v:h", options, &idx);
> > > > > +               c = getopt_long(argc, argv, opts_short, options, &idx);
> > > > >                 if (c == -1)
> > > > >                         break;
> > > > >
> > > > > @@ -214,29 +567,47 @@ int main(int argc, char **argv)
> > > > >                 case 'I':
> > > > >                         instance = strtoul(optarg, NULL, 0);
> > > > >                         break;
> > > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO
> > > > > +               case 'p':
> > > > > +                       if (privkey_file) {
> > > > > +                               printf("Private Key already specified\n");
> > > > > +                               return -1;
> > > > > +                       }
> > > > > +                       privkey_file = optarg;
> > > > > +                       break;
> > > > > +               case 'c':
> > > > > +                       if (cert_file) {
> > > > > +                               printf("Certificate file already specified\n");
> > > > > +                               return -1;
> > > > > +                       }
> > > > > +                       cert_file = optarg;
> > > > > +                       break;
> > > > > +               case 'm':
> > > > > +                       mcount = strtoul(optarg, NULL, 0);
> > > > > +                       break;
> > > > > +               case 'd':
> > > > > +                       dump_sig = 1;
> > > > > +                       break;
> > > > > +#endif /* CONFIG_TOOLS_LIBCRYPTO */
> > > > >                 case 'h':
> > > > >                         print_usage();
> > > > >                         return 0;
> > > > >                 }
> > > > >         }
> > > > >
> > > > > -       /* need an output file */
> > > > > -       if (argc != optind + 1) {
> > > > > -               print_usage();
> > > > > -               exit(EXIT_FAILURE);
> > > > > -       }
> > > > > -
> > > > > -       /* need a fit image file or raw image file */
> > > > > -       if (!file) {
> > > > > +       /* check necessary parameters */
> > > > > +       if ((argc != optind + 1) || !file ||
> > > > > +           ((privkey_file && !cert_file) ||
> > > > > +            (!privkey_file && cert_file))) {
> > > > >                 print_usage();
> > > > > -               exit(EXIT_SUCCESS);
> > > > > +               return -1;
> > > > >         }
> > > > >
> > > > > -       if (create_fwbin(argv[optind], file, guid, index, instance)
> > > > > -                       < 0) {
> > > > > +       if (create_fwbin(argv[optind], file, guid, index, instance,
> > > > > +                        mcount, privkey_file, cert_file) < 0) {
> > > > >                 printf("Creating firmware capsule failed\n");
> > > > > -               exit(EXIT_FAILURE);
> > > > > +               return -1;
> > > > >         }
> > > > >
> > > > > -       exit(EXIT_SUCCESS);
> > > > > +       return 0;
> > > > >  }
> > > > > --
> > > > > 2.33.0
> > > > >
> > > >
> > > >
> > > > --
> > > > Masami Hiramatsu
> >
> >
> >
> > --
> > Masami Hiramatsu



--
Masami Hiramatsu

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v4 03/11] efi_loader: capsule: add back efi_get_public_key_data()
  2021-10-25  6:28           ` François Ozog
@ 2021-10-25  7:04             ` Masami Hiramatsu
  2021-10-25  7:14               ` François Ozog
  0 siblings, 1 reply; 40+ messages in thread
From: Masami Hiramatsu @ 2021-10-25  7:04 UTC (permalink / raw)
  To: François Ozog
  Cc: AKASHI Takahiro, Alex Graf, Heinrich Schuchardt,
	Ilias Apalodimas, Simon Glass, Sughosh Ganu, U-Boot Mailing List

Hi Francois,

2021年10月25日(月) 15:28 François Ozog <francois.ozog@linaro.org>:
>
>
>
> Le lun. 25 oct. 2021 à 07:14, AKASHI Takahiro <takahiro.akashi@linaro.org> a écrit :
>>
>> On Wed, Oct 20, 2021 at 07:39:37AM -0600, Simon Glass wrote:
>> > Hi Masami,
>> >
>> > On Wed, 20 Oct 2021 at 02:18, Masami Hiramatsu
>> > <masami.hiramatsu@linaro.org> wrote:
>> > >
>> > > Hi Simon,
>> > >
>> > > 2021年10月15日(金) 9:40 Simon Glass <sjg@chromium.org>:
>> > > >
>> > > > Hi Takahiro,
>> > > >
>> > > > On Thu, 7 Oct 2021 at 00:25, AKASHI Takahiro <takahiro.akashi@linaro.org> wrote:
>> > > > >
>> > > > > The commit 47a25e81d35c ("Revert "efi_capsule: Move signature from DTB to
>> > > > > .rodata"") failed to revert the removal of efi_get_public_key_data().
>> > > > >
>> > > > > Add back this function and move it under lib/efi_loader so that other
>> > > > > platforms can utilize it. It is now declared as a weak function so that
>> > > > > it can be replaced with a platform-specific implementation.
>> > > > >
>> > > > > Fixes: 47a25e81d35c ("Revert "efi_capsule: Move signature from DTB to
>> > > > >         .rodata"")
>> > > > > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
>> > > > > ---
>> > > > >  lib/efi_loader/efi_capsule.c | 36 ++++++++++++++++++++++++++++++++++++
>> > > > >  1 file changed, 36 insertions(+)
>> > > > >
>> > > > > diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
>> > > > > index b75e4bcba1a9..44f5da61a9be 100644
>> > > > > --- a/lib/efi_loader/efi_capsule.c
>> > > > > +++ b/lib/efi_loader/efi_capsule.c
>> > > > > @@ -11,15 +11,20 @@
>> > > > >  #include <common.h>
>> > > > >  #include <efi_loader.h>
>> > > > >  #include <efi_variable.h>
>> > > > > +#include <env.h>
>> > > > > +#include <fdtdec.h>
>> > > > >  #include <fs.h>
>> > > > >  #include <malloc.h>
>> > > > >  #include <mapmem.h>
>> > > > >  #include <sort.h>
>> > > > > +#include <asm/global_data.h>
>> > > > >
>> > > > >  #include <crypto/pkcs7.h>
>> > > > >  #include <crypto/pkcs7_parser.h>
>> > > > >  #include <linux/err.h>
>> > > > >
>> > > > > +DECLARE_GLOBAL_DATA_PTR;
>> > > > > +
>> > > > >  const efi_guid_t efi_guid_capsule_report = EFI_CAPSULE_REPORT_GUID;
>> > > > >  static const efi_guid_t efi_guid_firmware_management_capsule_id =
>> > > > >                 EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID;
>> > > > > @@ -251,6 +256,37 @@ out:
>> > > > >  }
>> > > > >
>> > > > >  #if defined(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>> > > > > +int __weak efi_get_public_key_data(void **pkey, efi_uintn_t *pkey_len)
>> > > >
>> > > > I don't think this should be weak. What other way is there of handling
>> > > > this and why would it be platform-specific?
>> > >
>> > > I have a question about the current design of the capsule auth key.
>> > > If the platform has its own key-storage, how can the platform use the
>> > > platform specific storage? Does such platform load the key from the storage
>> > > and generate the dtb node in the platform initialization code? (or
>> > > device driver?)
>> >
>> > Are we talking about a public key (which I assume from the function
>> > naming) or some secret key. What is an auth key?
>>
>> Surely, a public key (more strictly, x509 certificate under the current
>> implementation)
>>
>> > As I understand it, the public key should be provided by the platform
>> > in devicetree that U-Boot uses, by whatever prior stage has access to
>> > the key.
>>
>> I still believe that some platform provider may want to save the key
>> in a *safer* storage, which should be at least read-only for non-authorized
>> writers.
>
>
> indeed. And U-Boot may not be entitled at all to check the capsule signature. For example updating SCP firmware with a key that is in secure world and will never leave this world.

I think secure world firmware updates should be discussed in another
thread, like with FWU. At this point, the capsule signature will be
only authenticated by U-Boot, because we haven't passed the capsule
image to the secure world yet.

>> But if this issue (__weak or not) is the only blocking factor
>> for my entire patch series, I'm willing to drop __weak for now since
>> someone with production system may change it in the future when he has
>> a good reason for that :)
>
>
> If that need be….

Agreed.

Thank you,

>
>>
>>
>> -Takahiro Akashi
>>
>>
>> > Regards,
>> > Simon
>
> --
> François-Frédéric Ozog | Director Business Development
> T: +33.67221.6485
> francois.ozog@linaro.org | Skype: ffozog
>


--
Masami Hiramatsu

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v4 03/11] efi_loader: capsule: add back efi_get_public_key_data()
  2021-10-25  7:04             ` Masami Hiramatsu
@ 2021-10-25  7:14               ` François Ozog
  2021-10-25 15:18                 ` Simon Glass
  0 siblings, 1 reply; 40+ messages in thread
From: François Ozog @ 2021-10-25  7:14 UTC (permalink / raw)
  To: Masami Hiramatsu
  Cc: AKASHI Takahiro, Alex Graf, Heinrich Schuchardt,
	Ilias Apalodimas, Simon Glass, Sughosh Ganu, U-Boot Mailing List

Le lun. 25 oct. 2021 à 09:05, Masami Hiramatsu <masami.hiramatsu@linaro.org>
a écrit :

> Hi Francois,
>
> 2021年10月25日(月) 15:28 François Ozog <francois.ozog@linaro.org>:
> >
> >
> >
> > Le lun. 25 oct. 2021 à 07:14, AKASHI Takahiro <
> takahiro.akashi@linaro.org> a écrit :
> >>
> >> On Wed, Oct 20, 2021 at 07:39:37AM -0600, Simon Glass wrote:
> >> > Hi Masami,
> >> >
> >> > On Wed, 20 Oct 2021 at 02:18, Masami Hiramatsu
> >> > <masami.hiramatsu@linaro.org> wrote:
> >> > >
> >> > > Hi Simon,
> >> > >
> >> > > 2021年10月15日(金) 9:40 Simon Glass <sjg@chromium.org>:
> >> > > >
> >> > > > Hi Takahiro,
> >> > > >
> >> > > > On Thu, 7 Oct 2021 at 00:25, AKASHI Takahiro <
> takahiro.akashi@linaro.org> wrote:
> >> > > > >
> >> > > > > The commit 47a25e81d35c ("Revert "efi_capsule: Move signature
> from DTB to
> >> > > > > .rodata"") failed to revert the removal of
> efi_get_public_key_data().
> >> > > > >
> >> > > > > Add back this function and move it under lib/efi_loader so that
> other
> >> > > > > platforms can utilize it. It is now declared as a weak function
> so that
> >> > > > > it can be replaced with a platform-specific implementation.
> >> > > > >
> >> > > > > Fixes: 47a25e81d35c ("Revert "efi_capsule: Move signature from
> DTB to
> >> > > > >         .rodata"")
> >> > > > > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> >> > > > > ---
> >> > > > >  lib/efi_loader/efi_capsule.c | 36
> ++++++++++++++++++++++++++++++++++++
> >> > > > >  1 file changed, 36 insertions(+)
> >> > > > >
> >> > > > > diff --git a/lib/efi_loader/efi_capsule.c
> b/lib/efi_loader/efi_capsule.c
> >> > > > > index b75e4bcba1a9..44f5da61a9be 100644
> >> > > > > --- a/lib/efi_loader/efi_capsule.c
> >> > > > > +++ b/lib/efi_loader/efi_capsule.c
> >> > > > > @@ -11,15 +11,20 @@
> >> > > > >  #include <common.h>
> >> > > > >  #include <efi_loader.h>
> >> > > > >  #include <efi_variable.h>
> >> > > > > +#include <env.h>
> >> > > > > +#include <fdtdec.h>
> >> > > > >  #include <fs.h>
> >> > > > >  #include <malloc.h>
> >> > > > >  #include <mapmem.h>
> >> > > > >  #include <sort.h>
> >> > > > > +#include <asm/global_data.h>
> >> > > > >
> >> > > > >  #include <crypto/pkcs7.h>
> >> > > > >  #include <crypto/pkcs7_parser.h>
> >> > > > >  #include <linux/err.h>
> >> > > > >
> >> > > > > +DECLARE_GLOBAL_DATA_PTR;
> >> > > > > +
> >> > > > >  const efi_guid_t efi_guid_capsule_report =
> EFI_CAPSULE_REPORT_GUID;
> >> > > > >  static const efi_guid_t
> efi_guid_firmware_management_capsule_id =
> >> > > > >                 EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID;
> >> > > > > @@ -251,6 +256,37 @@ out:
> >> > > > >  }
> >> > > > >
> >> > > > >  #if defined(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> >> > > > > +int __weak efi_get_public_key_data(void **pkey, efi_uintn_t
> *pkey_len)
> >> > > >
> >> > > > I don't think this should be weak. What other way is there of
> handling
> >> > > > this and why would it be platform-specific?
> >> > >
> >> > > I have a question about the current design of the capsule auth key.
> >> > > If the platform has its own key-storage, how can the platform use
> the
> >> > > platform specific storage? Does such platform load the key from the
> storage
> >> > > and generate the dtb node in the platform initialization code? (or
> >> > > device driver?)
> >> >
> >> > Are we talking about a public key (which I assume from the function
> >> > naming) or some secret key. What is an auth key?
> >>
> >> Surely, a public key (more strictly, x509 certificate under the current
> >> implementation)
> >>
> >> > As I understand it, the public key should be provided by the platform
> >> > in devicetree that U-Boot uses, by whatever prior stage has access to
> >> > the key.
> >>
> >> I still believe that some platform provider may want to save the key
> >> in a *safer* storage, which should be at least read-only for
> non-authorized
> >> writers.
> >
> >
> > indeed. And U-Boot may not be entitled at all to check the capsule
> signature. For example updating SCP firmware with a key that is in secure
> world and will never leave this world.
>
> I think secure world firmware updates should be discussed in another
> thread, like with FWU. At this point, the capsule signature will be
> only authenticated by U-Boot, because we haven't passed the capsule
> image to the secure world yet.

i took a wrong example. The choice of authentication is to be done by the
capsule driver designer and is outside scope of U-Boot. And the key may be
in a separate storage or even the driver may invoke secure world for the
authentication (FF-A API or other platform specific). U-Boot may have a
capsule driver to update U-Boot components such as external env file. The
location of the key can be in a u-boot specific device tree.


>
> >> But if this issue (__weak or not) is the only blocking factor
> >> for my entire patch series, I'm willing to drop __weak for now since
> >> someone with production system may change it in the future when he has
> >> a good reason for that :)
> >
> >
> > If that need be….
>
> Agreed.
>
> Thank you,
>
> >
> >>
> >>
> >> -Takahiro Akashi
> >>
> >>
> >> > Regards,
> >> > Simon
> >
> > --
> > François-Frédéric Ozog | Director Business Development
> > T: +33.67221.6485
> > francois.ozog@linaro.org | Skype: ffozog
> >
>
>
> --
> Masami Hiramatsu
>
-- 
François-Frédéric Ozog | *Director Business Development*
T: +33.67221.6485
francois.ozog@linaro.org | Skype: ffozog

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v4 05/11] tools: mkeficapsule: add firmwware image signing
  2021-10-25  5:40       ` Masami Hiramatsu
  2021-10-25  6:09         ` AKASHI Takahiro
@ 2021-10-25  9:58         ` Sughosh Ganu
  2021-10-25 12:46           ` Masami Hiramatsu
  1 sibling, 1 reply; 40+ messages in thread
From: Sughosh Ganu @ 2021-10-25  9:58 UTC (permalink / raw)
  To: Masami Hiramatsu
  Cc: AKASHI Takahiro, Heinrich Schuchardt, Alexander Graf,
	Ilias Apalodimas, U-Boot Mailing List

hi Masami,

On Mon, 25 Oct 2021 at 11:10, Masami Hiramatsu <masami.hiramatsu@linaro.org>
wrote:

> Hi Takahiro,
>
> 2021年10月25日(月) 12:12 AKASHI Takahiro <takahiro.akashi@linaro.org>:
> >
> > Hi, Masami,
> >
> > On Wed, Oct 20, 2021 at 05:17:12PM +0900, Masami Hiramatsu wrote:
> > > Hello Akashi-san,
> > >
> > > Can you split this patch out from this series?
> > > It seems that the series is doing several different things. This
> > > capsule signing has no alternatives, but others are modifying or
> > > replacing the current existing feature. In other words, this capsule
> > > signing feature has no alternatives, but others we can continue to
> > > use.
> >
> > What do you specifically mean by "existing feature" or "others we can
> > continue to use"?
> >
> > All the commits, I think, in this series are closely related with
> > each other.
>
> I meant I can use the devicetree to embed the authentication key in the
> U-Boot itself (currently U-Boot expects that.) Or is there any issue with
> putting the key in the devicetree?
>
> Without this patch, I can not sign the capsule file for U-Boot, since
> the GenerateCapsule in EDK2 supports old format (is that updated?)
>

If you are referring to using Version 3 of the FMP Image Header structure,
yes this has been upstreamed in EDK2. You can rebase and use the current
GenerateCapsule script. Thanks.

-sughosh


>
> Thank you,
>
>
> >
> > -Takahiro Akashi
> >
> > > Thank you,
> > >
> > > 2021年10月7日(木) 15:25 AKASHI Takahiro <takahiro.akashi@linaro.org>:
> > > >
> > > > With this enhancement, mkeficapsule will be able to sign a capsule
> > > > file when it is created. A signature added will be used later
> > > > in the verification at FMP's SetImage() call.
> > > >
> > > > To do that, We need specify additional command parameters:
> > > >   -monotonic-cout <count> : monotonic count
> > > >   -private-key <private key file> : private key file
> > > >   -certificate <certificate file> : certificate file
> > > > Only when all of those parameters are given, a signature will be
> added
> > > > to a capsule file.
> > > >
> > > > Users are expected to maintain and increment the monotonic count at
> > > > every time of the update for each firmware image.
> > > >
> > > > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > > > ---
> > > >  tools/Kconfig        |   7 +
> > > >  tools/Makefile       |   8 +-
> > > >  tools/mkeficapsule.c | 435
> +++++++++++++++++++++++++++++++++++++++----
> > > >  3 files changed, 416 insertions(+), 34 deletions(-)
> > > >
> > > > diff --git a/tools/Kconfig b/tools/Kconfig
> > > > index d6f82cd949b5..9a37ed035311 100644
> > > > --- a/tools/Kconfig
> > > > +++ b/tools/Kconfig
> > > > @@ -20,4 +20,11 @@ config TOOLS_LIBCRYPTO
> > > >           This selection does not affect target features, such as
> runtime FIT
> > > >           signature verification.
> > > >
> > > > +config TOOLS_MKEFICAPSULE
> > > > +       bool "Build efimkcapsule command"
> > > > +       default y if EFI_CAPSULE_ON_DISK
> > > > +       help
> > > > +         This command allows users to create a UEFI capsule file
> and,
> > > > +         optionally sign that file. If you want to enable UEFI
> capsule
> > > > +         update feature on your target, you certainly need this.
> > > >  endmenu
> > > > diff --git a/tools/Makefile b/tools/Makefile
> > > > index 4a86321f6467..6ea3033dbfb8 100644
> > > > --- a/tools/Makefile
> > > > +++ b/tools/Makefile
> > > > @@ -237,8 +237,12 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs
> > > >  hostprogs-$(CONFIG_ASN1_COMPILER)      += asn1_compiler
> > > >  HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
> > > >
> > > > -mkeficapsule-objs      := mkeficapsule.o $(LIBFDT_OBJS)
> > > > -hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
> > > > +HOSTLDLIBS_mkeficapsule += -luuid
> > > > +ifeq ($(CONFIG_TOOLS_LIBCRYPTO),y)
> > > > +HOSTLDLIBS_mkeficapsule += \
> > > > +       $(shell pkg-config --libs libssl libcrypto 2> /dev/null ||
> echo "-lssl -lcrypto")
> > > > +endif
> > > > +hostprogs-$(CONFIG_TOOLS_MKEFICAPSULE) += mkeficapsule
> > > >
> > > >  # We build some files with extra pedantic flags to try to minimize
> things
> > > >  # that won't build on some weird host compiler -- though there are
> lots of
> > > > diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
> > > > index 4995ba4e0c2a..5541e4bda894 100644
> > > > --- a/tools/mkeficapsule.c
> > > > +++ b/tools/mkeficapsule.c
> > > > @@ -15,6 +15,16 @@
> > > >  #include <sys/stat.h>
> > > >  #include <sys/types.h>
> > > >
> > > > +#include <linux/kconfig.h>
> > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO
> > > > +#include <openssl/asn1.h>
> > > > +#include <openssl/bio.h>
> > > > +#include <openssl/evp.h>
> > > > +#include <openssl/err.h>
> > > > +#include <openssl/pem.h>
> > > > +#include <openssl/pkcs7.h>
> > > > +#endif
> > > > +
> > > >  typedef __u8 u8;
> > > >  typedef __u16 u16;
> > > >  typedef __u32 u32;
> > > > @@ -38,12 +48,25 @@ efi_guid_t efi_guid_image_type_uboot_fit =
> > > >                 EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID;
> > > >  efi_guid_t efi_guid_image_type_uboot_raw =
> > > >                 EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID;
> > > > +efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
> > > > +
> > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO
> > > > +static const char *opts_short = "f:r:i:I:v:p:c:m:dh";
> > > > +#else
> > > > +static const char *opts_short = "f:r:i:I:v:h";
> > > > +#endif
> > > >
> > > >  static struct option options[] = {
> > > >         {"fit", required_argument, NULL, 'f'},
> > > >         {"raw", required_argument, NULL, 'r'},
> > > >         {"index", required_argument, NULL, 'i'},
> > > >         {"instance", required_argument, NULL, 'I'},
> > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO
> > > > +       {"private-key", required_argument, NULL, 'p'},
> > > > +       {"certificate", required_argument, NULL, 'c'},
> > > > +       {"monotonic-count", required_argument, NULL, 'm'},
> > > > +       {"dump-sig", no_argument, NULL, 'd'},
> > > > +#endif
> > > >         {"help", no_argument, NULL, 'h'},
> > > >         {NULL, 0, NULL, 0},
> > > >  };
> > > > @@ -57,16 +80,280 @@ static void print_usage(void)
> > > >                "\t-r, --raw <raw image>       new raw image file\n"
> > > >                "\t-i, --index <index>         update image index\n"
> > > >                "\t-I, --instance <instance>   update hardware
> instance\n"
> > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO
> > > > +              "\t-p, --private-key <privkey file>  private key
> file\n"
> > > > +              "\t-c, --certificate <cert file>     signer's
> certificate file\n"
> > > > +              "\t-m, --monotonic-count <count>     monotonic
> count\n"
> > > > +              "\t-d, --dump_sig              dump signature
> (*.p7)\n"
> > > > +#endif
> > > >                "\t-h, --help                  print a help
> message\n",
> > > >                tool_name);
> > > >  }
> > > >
> > > > +/**
> > > > + * auth_context - authentication context
> > > > + * @key_file:  Path to a private key file
> > > > + * @cert_file: Path to a certificate file
> > > > + * @image_data:        Pointer to firmware data
> > > > + * @image_size:        Size of firmware data
> > > > + * @auth:      Authentication header
> > > > + * @sig_data:  Signature data
> > > > + * @sig_size:  Size of signature data
> > > > + *
> > > > + * Data structure used in create_auth_data(). @key_file through
> > > > + * @image_size are input parameters. @auth, @sig_data and @sig_size
> > > > + * are filled in by create_auth_data().
> > > > + */
> > > > +struct auth_context {
> > > > +       char *key_file;
> > > > +       char *cert_file;
> > > > +       u8 *image_data;
> > > > +       size_t image_size;
> > > > +       struct efi_firmware_image_authentication auth;
> > > > +       u8 *sig_data;
> > > > +       size_t sig_size;
> > > > +};
> > > > +
> > > > +static int dump_sig;
> > > > +
> > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO
> > > > +/**
> > > > + * fileio-read_pkey - read out a private key
> > > > + * @filename:  Path to a private key file
> > > > + *
> > > > + * Read out a private key file and parse it into "EVP_PKEY"
> structure.
> > > > + *
> > > > + * Return:
> > > > + * * Pointer to private key structure  - on success
> > > > + * * NULL - on failure
> > > > + */
> > > > +static EVP_PKEY *fileio_read_pkey(const char *filename)
> > > > +{
> > > > +       EVP_PKEY *key = NULL;
> > > > +       BIO *bio;
> > > > +
> > > > +       bio = BIO_new_file(filename, "r");
> > > > +       if (!bio)
> > > > +               goto out;
> > > > +
> > > > +       key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
> > > > +
> > > > +out:
> > > > +       BIO_free_all(bio);
> > > > +       if (!key) {
> > > > +               printf("Can't load key from file '%s'\n", filename);
> > > > +               ERR_print_errors_fp(stderr);
> > > > +       }
> > > > +
> > > > +       return key;
> > > > +}
> > > > +
> > > > +/**
> > > > + * fileio-read_cert - read out a certificate
> > > > + * @filename:  Path to a certificate file
> > > > + *
> > > > + * Read out a certificate file and parse it into "X509" structure.
> > > > + *
> > > > + * Return:
> > > > + * * Pointer to certificate structure  - on success
> > > > + * * NULL - on failure
> > > > + */
> > > > +static X509 *fileio_read_cert(const char *filename)
> > > > +{
> > > > +       X509 *cert = NULL;
> > > > +       BIO *bio;
> > > > +
> > > > +       bio = BIO_new_file(filename, "r");
> > > > +       if (!bio)
> > > > +               goto out;
> > > > +
> > > > +       cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
> > > > +
> > > > +out:
> > > > +       BIO_free_all(bio);
> > > > +       if (!cert) {
> > > > +               printf("Can't load certificate from file '%s'\n",
> filename);
> > > > +               ERR_print_errors_fp(stderr);
> > > > +       }
> > > > +
> > > > +       return cert;
> > > > +}
> > > > +
> > > > +/**
> > > > + * create_auth_data - compose authentication data in capsule
> > > > + * @auth_context:      Pointer to authentication context
> > > > + *
> > > > + * Fill up an authentication header (.auth) and signature data
> (.sig_data)
> > > > + * in @auth_context, using library functions from openssl.
> > > > + * All the parameters in @auth_context must be filled in by a
> caller.
> > > > + *
> > > > + * Return:
> > > > + * * 0  - on success
> > > > + * * -1 - on failure
> > > > + */
> > > > +static int create_auth_data(struct auth_context *ctx)
> > > > +{
> > > > +       EVP_PKEY *key = NULL;
> > > > +       X509 *cert = NULL;
> > > > +       BIO *data_bio = NULL;
> > > > +       const EVP_MD *md;
> > > > +       PKCS7 *p7;
> > > > +       int flags, ret = -1;
> > > > +
> > > > +       OpenSSL_add_all_digests();
> > > > +       OpenSSL_add_all_ciphers();
> > > > +       ERR_load_crypto_strings();
> > > > +
> > > > +       key = fileio_read_pkey(ctx->key_file);
> > > > +       if (!key)
> > > > +               goto err;
> > > > +       cert = fileio_read_cert(ctx->cert_file);
> > > > +       if (!cert)
> > > > +               goto err;
> > > > +
> > > > +       /*
> > > > +        * create a BIO, containing:
> > > > +        *  * firmware image
> > > > +        *  * monotonic count
> > > > +        * in this order!
> > > > +        * See EDK2's FmpAuthenticatedHandlerRsa2048Sha256()
> > > > +        */
> > > > +       data_bio = BIO_new(BIO_s_mem());
> > > > +       BIO_write(data_bio, ctx->image_data, ctx->image_size);
> > > > +       BIO_write(data_bio, &ctx->auth.monotonic_count,
> > > > +                 sizeof(ctx->auth.monotonic_count));
> > > > +
> > > > +       md = EVP_get_digestbyname("SHA256");
> > > > +       if (!md)
> > > > +               goto err;
> > > > +
> > > > +       /* create signature */
> > > > +       /* TODO: maybe add PKCS7_NOATTR and PKCS7_NOSMIMECAP */
> > > > +       flags = PKCS7_BINARY | PKCS7_DETACHED;
> > > > +       p7 = PKCS7_sign(NULL, NULL, NULL, data_bio, flags |
> PKCS7_PARTIAL);
> > > > +       if (!p7)
> > > > +               goto err;
> > > > +       if (!PKCS7_sign_add_signer(p7, cert, key, md, flags))
> > > > +               goto err;
> > > > +       if (!PKCS7_final(p7, data_bio, flags))
> > > > +               goto err;
> > > > +
> > > > +       /* convert pkcs7 into DER */
> > > > +       ctx->sig_data = NULL;
> > > > +       ctx->sig_size = ASN1_item_i2d((ASN1_VALUE *)p7,
> &ctx->sig_data,
> > > > +                                     ASN1_ITEM_rptr(PKCS7));
> > > > +       if (!ctx->sig_size)
> > > > +               goto err;
> > > > +
> > > > +       /* fill auth_info */
> > > > +       ctx->auth.auth_info.hdr.dwLength =
> sizeof(ctx->auth.auth_info)
> > > > +                                               + ctx->sig_size;
> > > > +       ctx->auth.auth_info.hdr.wRevision = WIN_CERT_REVISION_2_0;
> > > > +       ctx->auth.auth_info.hdr.wCertificateType =
> WIN_CERT_TYPE_EFI_GUID;
> > > > +       memcpy(&ctx->auth.auth_info.cert_type,
> &efi_guid_cert_type_pkcs7,
> > > > +              sizeof(efi_guid_cert_type_pkcs7));
> > > > +
> > > > +       ret = 0;
> > > > +err:
> > > > +       BIO_free_all(data_bio);
> > > > +       EVP_PKEY_free(key);
> > > > +       X509_free(cert);
> > > > +
> > > > +       return ret;
> > > > +}
> > > > +
> > > > +/**
> > > > + * dump_signature - dump out a signature
> > > > + * @path:      Path to a capsule file
> > > > + * @signature: Signature data
> > > > + * @sig_size:  Size of signature data
> > > > + *
> > > > + * Signature data pointed to by @signature will be saved into
> > > > + * a file whose file name is @path with ".p7" suffix.
> > > > + *
> > > > + * Return:
> > > > + * * 0  - on success
> > > > + * * -1 - on failure
> > > > + */
> > > > +static int dump_signature(const char *path, u8 *signature, size_t
> sig_size)
> > > > +{
> > > > +       char *sig_path;
> > > > +       FILE *f;
> > > > +       size_t size;
> > > > +       int ret = -1;
> > > > +
> > > > +       sig_path = malloc(strlen(path) + 3 + 1);
> > > > +       if (!sig_path)
> > > > +               return ret;
> > > > +
> > > > +       sprintf(sig_path, "%s.p7", path);
> > > > +       f = fopen(sig_path, "w");
> > > > +       if (!f)
> > > > +               goto err;
> > > > +
> > > > +       size = fwrite(signature, 1, sig_size, f);
> > > > +       if (size == sig_size)
> > > > +               ret = 0;
> > > > +
> > > > +       fclose(f);
> > > > +err:
> > > > +       free(sig_path);
> > > > +       return ret;
> > > > +}
> > > > +
> > > > +/**
> > > > + * free_sig_data - free out signature data
> > > > + * @ctx:       Pointer to authentication context
> > > > + *
> > > > + * Free signature data allocated in create_auth_data().
> > > > + */
> > > > +static void free_sig_data(struct auth_context *ctx)
> > > > +{
> > > > +       if (ctx->sig_size)
> > > > +               OPENSSL_free(ctx->sig_data);
> > > > +}
> > > > +#else
> > > > +static int create_auth_data(struct auth_context *ctx)
> > > > +{
> > > > +       return 0;
> > > > +}
> > > > +
> > > > +static int dump_signature(const char *path, u8 *signature, size_t
> sig_size)
> > > > +{
> > > > +       return 0;
> > > > +}
> > > > +
> > > > +static void free_sig_data(struct auth_context *ctx) {}
> > > > +#endif
> > > > +
> > > > +/**
> > > > + * create_fwbin - create an uefi capsule file
> > > > + * @path:      Path to a created capsule file
> > > > + * @bin:       Path to a firmware binary to encapsulate
> > > > + * @guid:      GUID of related FMP driver
> > > > + * @index:     Index number in capsule
> > > > + * @instance:  Instance number in capsule
> > > > + * @mcount:    Monotonic count in authentication information
> > > > + * @private_file:      Path to a private key file
> > > > + * @cert_file: Path to a certificate file
> > > > + *
> > > > + * This function actually does the job of creating an uefi capsule
> file.
> > > > + * All the arguments must be supplied.
> > > > + * If either @private_file ror @cert_file is NULL, the capsule file
> > > > + * won't be signed.
> > > > + *
> > > > + * Return:
> > > > + * * 0  - on success
> > > > + * * -1 - on failure
> > > > + */
> > > >  static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > > -                       unsigned long index, unsigned long instance)
> > > > +                       unsigned long index, unsigned long instance,
> > > > +                       uint64_t mcount, char *privkey_file, char
> *cert_file)
> > > >  {
> > > >         struct efi_capsule_header header;
> > > >         struct efi_firmware_management_capsule_header capsule;
> > > >         struct efi_firmware_management_capsule_image_header image;
> > > > +       struct auth_context auth_context;
> > > >         FILE *f, *g;
> > > >         struct stat bin_stat;
> > > >         u8 *data;
> > > > @@ -76,8 +363,9 @@ static int create_fwbin(char *path, char *bin,
> efi_guid_t *guid,
> > > >  #ifdef DEBUG
> > > >         printf("For output: %s\n", path);
> > > >         printf("\tbin: %s\n\ttype: %pUl\n", bin, guid);
> > > > -       printf("\tindex: %ld\n\tinstance: %ld\n", index, instance);
> > > > +       printf("\tindex: %lu\n\tinstance: %lu\n", index, instance);
> > > >  #endif
> > > > +       auth_context.sig_size = 0;
> > > >
> > > >         g = fopen(bin, "r");
> > > >         if (!g) {
> > > > @@ -93,11 +381,34 @@ static int create_fwbin(char *path, char *bin,
> efi_guid_t *guid,
> > > >                 printf("cannot allocate memory: %zx\n",
> (size_t)bin_stat.st_size);
> > > >                 goto err_1;
> > > >         }
> > > > -       f = fopen(path, "w");
> > > > -       if (!f) {
> > > > -               printf("cannot open %s\n", path);
> > > > +
> > > > +       size = fread(data, 1, bin_stat.st_size, g);
> > > > +       if (size < bin_stat.st_size) {
> > > > +               printf("read failed (%zx)\n", size);
> > > >                 goto err_2;
> > > >         }
> > > > +
> > > > +       /* first, calculate signature to determine its size */
> > > > +       if (privkey_file && cert_file) {
> > > > +               auth_context.key_file = privkey_file;
> > > > +               auth_context.cert_file = cert_file;
> > > > +               auth_context.auth.monotonic_count = mcount;
> > > > +               auth_context.image_data = data;
> > > > +               auth_context.image_size = bin_stat.st_size;
> > > > +
> > > > +               if (create_auth_data(&auth_context)) {
> > > > +                       printf("Signing firmware image failed\n");
> > > > +                       goto err_3;
> > > > +               }
> > > > +
> > > > +               if (dump_sig &&
> > > > +                   dump_signature(path, auth_context.sig_data,
> > > > +                                  auth_context.sig_size)) {
> > > > +                       printf("Creating signature file failed\n");
> > > > +                       goto err_3;
> > > > +               }
> > > > +       }
> > > > +
> > > >         header.capsule_guid = efi_guid_fm_capsule;
> > > >         header.header_size = sizeof(header);
> > > >         /* TODO: The current implementation ignores flags */
> > > > @@ -106,11 +417,20 @@ static int create_fwbin(char *path, char *bin,
> efi_guid_t *guid,
> > > >                                         + sizeof(capsule) +
> sizeof(u64)
> > > >                                         + sizeof(image)
> > > >                                         + bin_stat.st_size;
> > > > +       if (auth_context.sig_size)
> > > > +               header.capsule_image_size +=
> sizeof(auth_context.auth)
> > > > +                               + auth_context.sig_size;
> > > > +
> > > > +       f = fopen(path, "w");
> > > > +       if (!f) {
> > > > +               printf("cannot open %s\n", path);
> > > > +               goto err_3;
> > > > +       }
> > > >
> > > >         size = fwrite(&header, 1, sizeof(header), f);
> > > >         if (size < sizeof(header)) {
> > > >                 printf("write failed (%zx)\n", size);
> > > > -               goto err_3;
> > > > +               goto err_4;
> > > >         }
> > > >
> > > >         capsule.version = 0x00000001;
> > > > @@ -119,13 +439,13 @@ static int create_fwbin(char *path, char *bin,
> efi_guid_t *guid,
> > > >         size = fwrite(&capsule, 1, sizeof(capsule), f);
> > > >         if (size < (sizeof(capsule))) {
> > > >                 printf("write failed (%zx)\n", size);
> > > > -               goto err_3;
> > > > +               goto err_4;
> > > >         }
> > > >         offset = sizeof(capsule) + sizeof(u64);
> > > >         size = fwrite(&offset, 1, sizeof(offset), f);
> > > >         if (size < sizeof(offset)) {
> > > >                 printf("write failed (%zx)\n", size);
> > > > -               goto err_3;
> > > > +               goto err_4;
> > > >         }
> > > >
> > > >         image.version = 0x00000003;
> > > > @@ -135,34 +455,53 @@ static int create_fwbin(char *path, char *bin,
> efi_guid_t *guid,
> > > >         image.reserved[1] = 0;
> > > >         image.reserved[2] = 0;
> > > >         image.update_image_size = bin_stat.st_size;
> > > > +       if (auth_context.sig_size)
> > > > +               image.update_image_size += sizeof(auth_context.auth)
> > > > +                               + auth_context.sig_size;
> > > >         image.update_vendor_code_size = 0; /* none */
> > > >         image.update_hardware_instance = instance;
> > > >         image.image_capsule_support = 0;
> > > > +       if (auth_context.sig_size)
> > > > +               image.image_capsule_support |=
> CAPSULE_SUPPORT_AUTHENTICATION;
> > > >
> > > >         size = fwrite(&image, 1, sizeof(image), f);
> > > >         if (size < sizeof(image)) {
> > > >                 printf("write failed (%zx)\n", size);
> > > > -               goto err_3;
> > > > +               goto err_4;
> > > >         }
> > > > -       size = fread(data, 1, bin_stat.st_size, g);
> > > > -       if (size < bin_stat.st_size) {
> > > > -               printf("read failed (%zx)\n", size);
> > > > -               goto err_3;
> > > > +
> > > > +       if (auth_context.sig_size) {
> > > > +               size = fwrite(&auth_context.auth, 1,
> > > > +                             sizeof(auth_context.auth), f);
> > > > +               if (size < sizeof(auth_context.auth)) {
> > > > +                       printf("write failed (%zx)\n", size);
> > > > +                       goto err_4;
> > > > +               }
> > > > +               size = fwrite(auth_context.sig_data, 1,
> > > > +                             auth_context.sig_size, f);
> > > > +               if (size < auth_context.sig_size) {
> > > > +                       printf("write failed (%zx)\n", size);
> > > > +                       goto err_4;
> > > > +               }
> > > >         }
> > > > +
> > > >         size = fwrite(data, 1, bin_stat.st_size, f);
> > > >         if (size < bin_stat.st_size) {
> > > >                 printf("write failed (%zx)\n", size);
> > > > -               goto err_3;
> > > > +               goto err_4;
> > > >         }
> > > >
> > > >         fclose(f);
> > > >         fclose(g);
> > > >         free(data);
> > > > +       free_sig_data(&auth_context);
> > > >
> > > >         return 0;
> > > >
> > > > -err_3:
> > > > +err_4:
> > > >         fclose(f);
> > > > +err_3:
> > > > +       free_sig_data(&auth_context);
> > > >  err_2:
> > > >         free(data);
> > > >  err_1:
> > > > @@ -171,23 +510,37 @@ err_1:
> > > >         return -1;
> > > >  }
> > > >
> > > > -/*
> > > > - * Usage:
> > > > - *   $ mkeficapsule -f <firmware binary> <output file>
> > > > +/**
> > > > + * main - main entry function of mkeficapsule
> > > > + * @argc:      Number of arguments
> > > > + * @argv:      Array of pointers to arguments
> > > > + *
> > > > + * Create an uefi capsule file, optionally signing it.
> > > > + * Parse all the arguments and pass them on to create_fwbin().
> > > > + *
> > > > + * Return:
> > > > + * * 0  - on success
> > > > + * * -1 - on failure
> > > >   */
> > > >  int main(int argc, char **argv)
> > > >  {
> > > >         char *file;
> > > >         efi_guid_t *guid;
> > > >         unsigned long index, instance;
> > > > +       uint64_t mcount;
> > > > +       char *privkey_file, *cert_file;
> > > >         int c, idx;
> > > >
> > > >         file = NULL;
> > > >         guid = NULL;
> > > >         index = 0;
> > > >         instance = 0;
> > > > +       mcount = 0;
> > > > +       privkey_file = NULL;
> > > > +       cert_file = NULL;
> > > > +       dump_sig = 0;
> > > >         for (;;) {
> > > > -               c = getopt_long(argc, argv, "f:r:i:I:v:h", options,
> &idx);
> > > > +               c = getopt_long(argc, argv, opts_short, options,
> &idx);
> > > >                 if (c == -1)
> > > >                         break;
> > > >
> > > > @@ -214,29 +567,47 @@ int main(int argc, char **argv)
> > > >                 case 'I':
> > > >                         instance = strtoul(optarg, NULL, 0);
> > > >                         break;
> > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO
> > > > +               case 'p':
> > > > +                       if (privkey_file) {
> > > > +                               printf("Private Key already
> specified\n");
> > > > +                               return -1;
> > > > +                       }
> > > > +                       privkey_file = optarg;
> > > > +                       break;
> > > > +               case 'c':
> > > > +                       if (cert_file) {
> > > > +                               printf("Certificate file already
> specified\n");
> > > > +                               return -1;
> > > > +                       }
> > > > +                       cert_file = optarg;
> > > > +                       break;
> > > > +               case 'm':
> > > > +                       mcount = strtoul(optarg, NULL, 0);
> > > > +                       break;
> > > > +               case 'd':
> > > > +                       dump_sig = 1;
> > > > +                       break;
> > > > +#endif /* CONFIG_TOOLS_LIBCRYPTO */
> > > >                 case 'h':
> > > >                         print_usage();
> > > >                         return 0;
> > > >                 }
> > > >         }
> > > >
> > > > -       /* need an output file */
> > > > -       if (argc != optind + 1) {
> > > > -               print_usage();
> > > > -               exit(EXIT_FAILURE);
> > > > -       }
> > > > -
> > > > -       /* need a fit image file or raw image file */
> > > > -       if (!file) {
> > > > +       /* check necessary parameters */
> > > > +       if ((argc != optind + 1) || !file ||
> > > > +           ((privkey_file && !cert_file) ||
> > > > +            (!privkey_file && cert_file))) {
> > > >                 print_usage();
> > > > -               exit(EXIT_SUCCESS);
> > > > +               return -1;
> > > >         }
> > > >
> > > > -       if (create_fwbin(argv[optind], file, guid, index, instance)
> > > > -                       < 0) {
> > > > +       if (create_fwbin(argv[optind], file, guid, index, instance,
> > > > +                        mcount, privkey_file, cert_file) < 0) {
> > > >                 printf("Creating firmware capsule failed\n");
> > > > -               exit(EXIT_FAILURE);
> > > > +               return -1;
> > > >         }
> > > >
> > > > -       exit(EXIT_SUCCESS);
> > > > +       return 0;
> > > >  }
> > > > --
> > > > 2.33.0
> > > >
> > >
> > >
> > > --
> > > Masami Hiramatsu
>
>
>
> --
> Masami Hiramatsu
>

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v4 05/11] tools: mkeficapsule: add firmwware image signing
  2021-10-25  9:58         ` Sughosh Ganu
@ 2021-10-25 12:46           ` Masami Hiramatsu
  0 siblings, 0 replies; 40+ messages in thread
From: Masami Hiramatsu @ 2021-10-25 12:46 UTC (permalink / raw)
  To: Sughosh Ganu
  Cc: AKASHI Takahiro, Heinrich Schuchardt, Alexander Graf,
	Ilias Apalodimas, U-Boot Mailing List

Hi Sughosh,

2021年10月25日(月) 18:59 Sughosh Ganu <sughosh.ganu@linaro.org>:
>
> hi Masami,
>
> On Mon, 25 Oct 2021 at 11:10, Masami Hiramatsu <masami.hiramatsu@linaro.org> wrote:
>>
>> Hi Takahiro,
>>
>> 2021年10月25日(月) 12:12 AKASHI Takahiro <takahiro.akashi@linaro.org>:
>> >
>> > Hi, Masami,
>> >
>> > On Wed, Oct 20, 2021 at 05:17:12PM +0900, Masami Hiramatsu wrote:
>> > > Hello Akashi-san,
>> > >
>> > > Can you split this patch out from this series?
>> > > It seems that the series is doing several different things. This
>> > > capsule signing has no alternatives, but others are modifying or
>> > > replacing the current existing feature. In other words, this capsule
>> > > signing feature has no alternatives, but others we can continue to
>> > > use.
>> >
>> > What do you specifically mean by "existing feature" or "others we can
>> > continue to use"?
>> >
>> > All the commits, I think, in this series are closely related with
>> > each other.
>>
>> I meant I can use the devicetree to embed the authentication key in the
>> U-Boot itself (currently U-Boot expects that.) Or is there any issue with
>> putting the key in the devicetree?
>>
>> Without this patch, I can not sign the capsule file for U-Boot, since
>> the GenerateCapsule in EDK2 supports old format (is that updated?)
>
>
> If you are referring to using Version 3 of the FMP Image Header structure, yes this has been upstreamed in EDK2. You can rebase and use the current GenerateCapsule script. Thanks.

Good information!

Thank you for update me :)

>
> -sughosh
>
>>
>>
>> Thank you,
>>
>>
>> >
>> > -Takahiro Akashi
>> >
>> > > Thank you,
>> > >
>> > > 2021年10月7日(木) 15:25 AKASHI Takahiro <takahiro.akashi@linaro.org>:
>> > > >
>> > > > With this enhancement, mkeficapsule will be able to sign a capsule
>> > > > file when it is created. A signature added will be used later
>> > > > in the verification at FMP's SetImage() call.
>> > > >
>> > > > To do that, We need specify additional command parameters:
>> > > >   -monotonic-cout <count> : monotonic count
>> > > >   -private-key <private key file> : private key file
>> > > >   -certificate <certificate file> : certificate file
>> > > > Only when all of those parameters are given, a signature will be added
>> > > > to a capsule file.
>> > > >
>> > > > Users are expected to maintain and increment the monotonic count at
>> > > > every time of the update for each firmware image.
>> > > >
>> > > > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
>> > > > ---
>> > > >  tools/Kconfig        |   7 +
>> > > >  tools/Makefile       |   8 +-
>> > > >  tools/mkeficapsule.c | 435 +++++++++++++++++++++++++++++++++++++++----
>> > > >  3 files changed, 416 insertions(+), 34 deletions(-)
>> > > >
>> > > > diff --git a/tools/Kconfig b/tools/Kconfig
>> > > > index d6f82cd949b5..9a37ed035311 100644
>> > > > --- a/tools/Kconfig
>> > > > +++ b/tools/Kconfig
>> > > > @@ -20,4 +20,11 @@ config TOOLS_LIBCRYPTO
>> > > >           This selection does not affect target features, such as runtime FIT
>> > > >           signature verification.
>> > > >
>> > > > +config TOOLS_MKEFICAPSULE
>> > > > +       bool "Build efimkcapsule command"
>> > > > +       default y if EFI_CAPSULE_ON_DISK
>> > > > +       help
>> > > > +         This command allows users to create a UEFI capsule file and,
>> > > > +         optionally sign that file. If you want to enable UEFI capsule
>> > > > +         update feature on your target, you certainly need this.
>> > > >  endmenu
>> > > > diff --git a/tools/Makefile b/tools/Makefile
>> > > > index 4a86321f6467..6ea3033dbfb8 100644
>> > > > --- a/tools/Makefile
>> > > > +++ b/tools/Makefile
>> > > > @@ -237,8 +237,12 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs
>> > > >  hostprogs-$(CONFIG_ASN1_COMPILER)      += asn1_compiler
>> > > >  HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
>> > > >
>> > > > -mkeficapsule-objs      := mkeficapsule.o $(LIBFDT_OBJS)
>> > > > -hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
>> > > > +HOSTLDLIBS_mkeficapsule += -luuid
>> > > > +ifeq ($(CONFIG_TOOLS_LIBCRYPTO),y)
>> > > > +HOSTLDLIBS_mkeficapsule += \
>> > > > +       $(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto")
>> > > > +endif
>> > > > +hostprogs-$(CONFIG_TOOLS_MKEFICAPSULE) += mkeficapsule
>> > > >
>> > > >  # We build some files with extra pedantic flags to try to minimize things
>> > > >  # that won't build on some weird host compiler -- though there are lots of
>> > > > diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
>> > > > index 4995ba4e0c2a..5541e4bda894 100644
>> > > > --- a/tools/mkeficapsule.c
>> > > > +++ b/tools/mkeficapsule.c
>> > > > @@ -15,6 +15,16 @@
>> > > >  #include <sys/stat.h>
>> > > >  #include <sys/types.h>
>> > > >
>> > > > +#include <linux/kconfig.h>
>> > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO
>> > > > +#include <openssl/asn1.h>
>> > > > +#include <openssl/bio.h>
>> > > > +#include <openssl/evp.h>
>> > > > +#include <openssl/err.h>
>> > > > +#include <openssl/pem.h>
>> > > > +#include <openssl/pkcs7.h>
>> > > > +#endif
>> > > > +
>> > > >  typedef __u8 u8;
>> > > >  typedef __u16 u16;
>> > > >  typedef __u32 u32;
>> > > > @@ -38,12 +48,25 @@ efi_guid_t efi_guid_image_type_uboot_fit =
>> > > >                 EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID;
>> > > >  efi_guid_t efi_guid_image_type_uboot_raw =
>> > > >                 EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID;
>> > > > +efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
>> > > > +
>> > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO
>> > > > +static const char *opts_short = "f:r:i:I:v:p:c:m:dh";
>> > > > +#else
>> > > > +static const char *opts_short = "f:r:i:I:v:h";
>> > > > +#endif
>> > > >
>> > > >  static struct option options[] = {
>> > > >         {"fit", required_argument, NULL, 'f'},
>> > > >         {"raw", required_argument, NULL, 'r'},
>> > > >         {"index", required_argument, NULL, 'i'},
>> > > >         {"instance", required_argument, NULL, 'I'},
>> > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO
>> > > > +       {"private-key", required_argument, NULL, 'p'},
>> > > > +       {"certificate", required_argument, NULL, 'c'},
>> > > > +       {"monotonic-count", required_argument, NULL, 'm'},
>> > > > +       {"dump-sig", no_argument, NULL, 'd'},
>> > > > +#endif
>> > > >         {"help", no_argument, NULL, 'h'},
>> > > >         {NULL, 0, NULL, 0},
>> > > >  };
>> > > > @@ -57,16 +80,280 @@ static void print_usage(void)
>> > > >                "\t-r, --raw <raw image>       new raw image file\n"
>> > > >                "\t-i, --index <index>         update image index\n"
>> > > >                "\t-I, --instance <instance>   update hardware instance\n"
>> > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO
>> > > > +              "\t-p, --private-key <privkey file>  private key file\n"
>> > > > +              "\t-c, --certificate <cert file>     signer's certificate file\n"
>> > > > +              "\t-m, --monotonic-count <count>     monotonic count\n"
>> > > > +              "\t-d, --dump_sig              dump signature (*.p7)\n"
>> > > > +#endif
>> > > >                "\t-h, --help                  print a help message\n",
>> > > >                tool_name);
>> > > >  }
>> > > >
>> > > > +/**
>> > > > + * auth_context - authentication context
>> > > > + * @key_file:  Path to a private key file
>> > > > + * @cert_file: Path to a certificate file
>> > > > + * @image_data:        Pointer to firmware data
>> > > > + * @image_size:        Size of firmware data
>> > > > + * @auth:      Authentication header
>> > > > + * @sig_data:  Signature data
>> > > > + * @sig_size:  Size of signature data
>> > > > + *
>> > > > + * Data structure used in create_auth_data(). @key_file through
>> > > > + * @image_size are input parameters. @auth, @sig_data and @sig_size
>> > > > + * are filled in by create_auth_data().
>> > > > + */
>> > > > +struct auth_context {
>> > > > +       char *key_file;
>> > > > +       char *cert_file;
>> > > > +       u8 *image_data;
>> > > > +       size_t image_size;
>> > > > +       struct efi_firmware_image_authentication auth;
>> > > > +       u8 *sig_data;
>> > > > +       size_t sig_size;
>> > > > +};
>> > > > +
>> > > > +static int dump_sig;
>> > > > +
>> > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO
>> > > > +/**
>> > > > + * fileio-read_pkey - read out a private key
>> > > > + * @filename:  Path to a private key file
>> > > > + *
>> > > > + * Read out a private key file and parse it into "EVP_PKEY" structure.
>> > > > + *
>> > > > + * Return:
>> > > > + * * Pointer to private key structure  - on success
>> > > > + * * NULL - on failure
>> > > > + */
>> > > > +static EVP_PKEY *fileio_read_pkey(const char *filename)
>> > > > +{
>> > > > +       EVP_PKEY *key = NULL;
>> > > > +       BIO *bio;
>> > > > +
>> > > > +       bio = BIO_new_file(filename, "r");
>> > > > +       if (!bio)
>> > > > +               goto out;
>> > > > +
>> > > > +       key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
>> > > > +
>> > > > +out:
>> > > > +       BIO_free_all(bio);
>> > > > +       if (!key) {
>> > > > +               printf("Can't load key from file '%s'\n", filename);
>> > > > +               ERR_print_errors_fp(stderr);
>> > > > +       }
>> > > > +
>> > > > +       return key;
>> > > > +}
>> > > > +
>> > > > +/**
>> > > > + * fileio-read_cert - read out a certificate
>> > > > + * @filename:  Path to a certificate file
>> > > > + *
>> > > > + * Read out a certificate file and parse it into "X509" structure.
>> > > > + *
>> > > > + * Return:
>> > > > + * * Pointer to certificate structure  - on success
>> > > > + * * NULL - on failure
>> > > > + */
>> > > > +static X509 *fileio_read_cert(const char *filename)
>> > > > +{
>> > > > +       X509 *cert = NULL;
>> > > > +       BIO *bio;
>> > > > +
>> > > > +       bio = BIO_new_file(filename, "r");
>> > > > +       if (!bio)
>> > > > +               goto out;
>> > > > +
>> > > > +       cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
>> > > > +
>> > > > +out:
>> > > > +       BIO_free_all(bio);
>> > > > +       if (!cert) {
>> > > > +               printf("Can't load certificate from file '%s'\n", filename);
>> > > > +               ERR_print_errors_fp(stderr);
>> > > > +       }
>> > > > +
>> > > > +       return cert;
>> > > > +}
>> > > > +
>> > > > +/**
>> > > > + * create_auth_data - compose authentication data in capsule
>> > > > + * @auth_context:      Pointer to authentication context
>> > > > + *
>> > > > + * Fill up an authentication header (.auth) and signature data (.sig_data)
>> > > > + * in @auth_context, using library functions from openssl.
>> > > > + * All the parameters in @auth_context must be filled in by a caller.
>> > > > + *
>> > > > + * Return:
>> > > > + * * 0  - on success
>> > > > + * * -1 - on failure
>> > > > + */
>> > > > +static int create_auth_data(struct auth_context *ctx)
>> > > > +{
>> > > > +       EVP_PKEY *key = NULL;
>> > > > +       X509 *cert = NULL;
>> > > > +       BIO *data_bio = NULL;
>> > > > +       const EVP_MD *md;
>> > > > +       PKCS7 *p7;
>> > > > +       int flags, ret = -1;
>> > > > +
>> > > > +       OpenSSL_add_all_digests();
>> > > > +       OpenSSL_add_all_ciphers();
>> > > > +       ERR_load_crypto_strings();
>> > > > +
>> > > > +       key = fileio_read_pkey(ctx->key_file);
>> > > > +       if (!key)
>> > > > +               goto err;
>> > > > +       cert = fileio_read_cert(ctx->cert_file);
>> > > > +       if (!cert)
>> > > > +               goto err;
>> > > > +
>> > > > +       /*
>> > > > +        * create a BIO, containing:
>> > > > +        *  * firmware image
>> > > > +        *  * monotonic count
>> > > > +        * in this order!
>> > > > +        * See EDK2's FmpAuthenticatedHandlerRsa2048Sha256()
>> > > > +        */
>> > > > +       data_bio = BIO_new(BIO_s_mem());
>> > > > +       BIO_write(data_bio, ctx->image_data, ctx->image_size);
>> > > > +       BIO_write(data_bio, &ctx->auth.monotonic_count,
>> > > > +                 sizeof(ctx->auth.monotonic_count));
>> > > > +
>> > > > +       md = EVP_get_digestbyname("SHA256");
>> > > > +       if (!md)
>> > > > +               goto err;
>> > > > +
>> > > > +       /* create signature */
>> > > > +       /* TODO: maybe add PKCS7_NOATTR and PKCS7_NOSMIMECAP */
>> > > > +       flags = PKCS7_BINARY | PKCS7_DETACHED;
>> > > > +       p7 = PKCS7_sign(NULL, NULL, NULL, data_bio, flags | PKCS7_PARTIAL);
>> > > > +       if (!p7)
>> > > > +               goto err;
>> > > > +       if (!PKCS7_sign_add_signer(p7, cert, key, md, flags))
>> > > > +               goto err;
>> > > > +       if (!PKCS7_final(p7, data_bio, flags))
>> > > > +               goto err;
>> > > > +
>> > > > +       /* convert pkcs7 into DER */
>> > > > +       ctx->sig_data = NULL;
>> > > > +       ctx->sig_size = ASN1_item_i2d((ASN1_VALUE *)p7, &ctx->sig_data,
>> > > > +                                     ASN1_ITEM_rptr(PKCS7));
>> > > > +       if (!ctx->sig_size)
>> > > > +               goto err;
>> > > > +
>> > > > +       /* fill auth_info */
>> > > > +       ctx->auth.auth_info.hdr.dwLength = sizeof(ctx->auth.auth_info)
>> > > > +                                               + ctx->sig_size;
>> > > > +       ctx->auth.auth_info.hdr.wRevision = WIN_CERT_REVISION_2_0;
>> > > > +       ctx->auth.auth_info.hdr.wCertificateType = WIN_CERT_TYPE_EFI_GUID;
>> > > > +       memcpy(&ctx->auth.auth_info.cert_type, &efi_guid_cert_type_pkcs7,
>> > > > +              sizeof(efi_guid_cert_type_pkcs7));
>> > > > +
>> > > > +       ret = 0;
>> > > > +err:
>> > > > +       BIO_free_all(data_bio);
>> > > > +       EVP_PKEY_free(key);
>> > > > +       X509_free(cert);
>> > > > +
>> > > > +       return ret;
>> > > > +}
>> > > > +
>> > > > +/**
>> > > > + * dump_signature - dump out a signature
>> > > > + * @path:      Path to a capsule file
>> > > > + * @signature: Signature data
>> > > > + * @sig_size:  Size of signature data
>> > > > + *
>> > > > + * Signature data pointed to by @signature will be saved into
>> > > > + * a file whose file name is @path with ".p7" suffix.
>> > > > + *
>> > > > + * Return:
>> > > > + * * 0  - on success
>> > > > + * * -1 - on failure
>> > > > + */
>> > > > +static int dump_signature(const char *path, u8 *signature, size_t sig_size)
>> > > > +{
>> > > > +       char *sig_path;
>> > > > +       FILE *f;
>> > > > +       size_t size;
>> > > > +       int ret = -1;
>> > > > +
>> > > > +       sig_path = malloc(strlen(path) + 3 + 1);
>> > > > +       if (!sig_path)
>> > > > +               return ret;
>> > > > +
>> > > > +       sprintf(sig_path, "%s.p7", path);
>> > > > +       f = fopen(sig_path, "w");
>> > > > +       if (!f)
>> > > > +               goto err;
>> > > > +
>> > > > +       size = fwrite(signature, 1, sig_size, f);
>> > > > +       if (size == sig_size)
>> > > > +               ret = 0;
>> > > > +
>> > > > +       fclose(f);
>> > > > +err:
>> > > > +       free(sig_path);
>> > > > +       return ret;
>> > > > +}
>> > > > +
>> > > > +/**
>> > > > + * free_sig_data - free out signature data
>> > > > + * @ctx:       Pointer to authentication context
>> > > > + *
>> > > > + * Free signature data allocated in create_auth_data().
>> > > > + */
>> > > > +static void free_sig_data(struct auth_context *ctx)
>> > > > +{
>> > > > +       if (ctx->sig_size)
>> > > > +               OPENSSL_free(ctx->sig_data);
>> > > > +}
>> > > > +#else
>> > > > +static int create_auth_data(struct auth_context *ctx)
>> > > > +{
>> > > > +       return 0;
>> > > > +}
>> > > > +
>> > > > +static int dump_signature(const char *path, u8 *signature, size_t sig_size)
>> > > > +{
>> > > > +       return 0;
>> > > > +}
>> > > > +
>> > > > +static void free_sig_data(struct auth_context *ctx) {}
>> > > > +#endif
>> > > > +
>> > > > +/**
>> > > > + * create_fwbin - create an uefi capsule file
>> > > > + * @path:      Path to a created capsule file
>> > > > + * @bin:       Path to a firmware binary to encapsulate
>> > > > + * @guid:      GUID of related FMP driver
>> > > > + * @index:     Index number in capsule
>> > > > + * @instance:  Instance number in capsule
>> > > > + * @mcount:    Monotonic count in authentication information
>> > > > + * @private_file:      Path to a private key file
>> > > > + * @cert_file: Path to a certificate file
>> > > > + *
>> > > > + * This function actually does the job of creating an uefi capsule file.
>> > > > + * All the arguments must be supplied.
>> > > > + * If either @private_file ror @cert_file is NULL, the capsule file
>> > > > + * won't be signed.
>> > > > + *
>> > > > + * Return:
>> > > > + * * 0  - on success
>> > > > + * * -1 - on failure
>> > > > + */
>> > > >  static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
>> > > > -                       unsigned long index, unsigned long instance)
>> > > > +                       unsigned long index, unsigned long instance,
>> > > > +                       uint64_t mcount, char *privkey_file, char *cert_file)
>> > > >  {
>> > > >         struct efi_capsule_header header;
>> > > >         struct efi_firmware_management_capsule_header capsule;
>> > > >         struct efi_firmware_management_capsule_image_header image;
>> > > > +       struct auth_context auth_context;
>> > > >         FILE *f, *g;
>> > > >         struct stat bin_stat;
>> > > >         u8 *data;
>> > > > @@ -76,8 +363,9 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
>> > > >  #ifdef DEBUG
>> > > >         printf("For output: %s\n", path);
>> > > >         printf("\tbin: %s\n\ttype: %pUl\n", bin, guid);
>> > > > -       printf("\tindex: %ld\n\tinstance: %ld\n", index, instance);
>> > > > +       printf("\tindex: %lu\n\tinstance: %lu\n", index, instance);
>> > > >  #endif
>> > > > +       auth_context.sig_size = 0;
>> > > >
>> > > >         g = fopen(bin, "r");
>> > > >         if (!g) {
>> > > > @@ -93,11 +381,34 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
>> > > >                 printf("cannot allocate memory: %zx\n", (size_t)bin_stat.st_size);
>> > > >                 goto err_1;
>> > > >         }
>> > > > -       f = fopen(path, "w");
>> > > > -       if (!f) {
>> > > > -               printf("cannot open %s\n", path);
>> > > > +
>> > > > +       size = fread(data, 1, bin_stat.st_size, g);
>> > > > +       if (size < bin_stat.st_size) {
>> > > > +               printf("read failed (%zx)\n", size);
>> > > >                 goto err_2;
>> > > >         }
>> > > > +
>> > > > +       /* first, calculate signature to determine its size */
>> > > > +       if (privkey_file && cert_file) {
>> > > > +               auth_context.key_file = privkey_file;
>> > > > +               auth_context.cert_file = cert_file;
>> > > > +               auth_context.auth.monotonic_count = mcount;
>> > > > +               auth_context.image_data = data;
>> > > > +               auth_context.image_size = bin_stat.st_size;
>> > > > +
>> > > > +               if (create_auth_data(&auth_context)) {
>> > > > +                       printf("Signing firmware image failed\n");
>> > > > +                       goto err_3;
>> > > > +               }
>> > > > +
>> > > > +               if (dump_sig &&
>> > > > +                   dump_signature(path, auth_context.sig_data,
>> > > > +                                  auth_context.sig_size)) {
>> > > > +                       printf("Creating signature file failed\n");
>> > > > +                       goto err_3;
>> > > > +               }
>> > > > +       }
>> > > > +
>> > > >         header.capsule_guid = efi_guid_fm_capsule;
>> > > >         header.header_size = sizeof(header);
>> > > >         /* TODO: The current implementation ignores flags */
>> > > > @@ -106,11 +417,20 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
>> > > >                                         + sizeof(capsule) + sizeof(u64)
>> > > >                                         + sizeof(image)
>> > > >                                         + bin_stat.st_size;
>> > > > +       if (auth_context.sig_size)
>> > > > +               header.capsule_image_size += sizeof(auth_context.auth)
>> > > > +                               + auth_context.sig_size;
>> > > > +
>> > > > +       f = fopen(path, "w");
>> > > > +       if (!f) {
>> > > > +               printf("cannot open %s\n", path);
>> > > > +               goto err_3;
>> > > > +       }
>> > > >
>> > > >         size = fwrite(&header, 1, sizeof(header), f);
>> > > >         if (size < sizeof(header)) {
>> > > >                 printf("write failed (%zx)\n", size);
>> > > > -               goto err_3;
>> > > > +               goto err_4;
>> > > >         }
>> > > >
>> > > >         capsule.version = 0x00000001;
>> > > > @@ -119,13 +439,13 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
>> > > >         size = fwrite(&capsule, 1, sizeof(capsule), f);
>> > > >         if (size < (sizeof(capsule))) {
>> > > >                 printf("write failed (%zx)\n", size);
>> > > > -               goto err_3;
>> > > > +               goto err_4;
>> > > >         }
>> > > >         offset = sizeof(capsule) + sizeof(u64);
>> > > >         size = fwrite(&offset, 1, sizeof(offset), f);
>> > > >         if (size < sizeof(offset)) {
>> > > >                 printf("write failed (%zx)\n", size);
>> > > > -               goto err_3;
>> > > > +               goto err_4;
>> > > >         }
>> > > >
>> > > >         image.version = 0x00000003;
>> > > > @@ -135,34 +455,53 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
>> > > >         image.reserved[1] = 0;
>> > > >         image.reserved[2] = 0;
>> > > >         image.update_image_size = bin_stat.st_size;
>> > > > +       if (auth_context.sig_size)
>> > > > +               image.update_image_size += sizeof(auth_context.auth)
>> > > > +                               + auth_context.sig_size;
>> > > >         image.update_vendor_code_size = 0; /* none */
>> > > >         image.update_hardware_instance = instance;
>> > > >         image.image_capsule_support = 0;
>> > > > +       if (auth_context.sig_size)
>> > > > +               image.image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION;
>> > > >
>> > > >         size = fwrite(&image, 1, sizeof(image), f);
>> > > >         if (size < sizeof(image)) {
>> > > >                 printf("write failed (%zx)\n", size);
>> > > > -               goto err_3;
>> > > > +               goto err_4;
>> > > >         }
>> > > > -       size = fread(data, 1, bin_stat.st_size, g);
>> > > > -       if (size < bin_stat.st_size) {
>> > > > -               printf("read failed (%zx)\n", size);
>> > > > -               goto err_3;
>> > > > +
>> > > > +       if (auth_context.sig_size) {
>> > > > +               size = fwrite(&auth_context.auth, 1,
>> > > > +                             sizeof(auth_context.auth), f);
>> > > > +               if (size < sizeof(auth_context.auth)) {
>> > > > +                       printf("write failed (%zx)\n", size);
>> > > > +                       goto err_4;
>> > > > +               }
>> > > > +               size = fwrite(auth_context.sig_data, 1,
>> > > > +                             auth_context.sig_size, f);
>> > > > +               if (size < auth_context.sig_size) {
>> > > > +                       printf("write failed (%zx)\n", size);
>> > > > +                       goto err_4;
>> > > > +               }
>> > > >         }
>> > > > +
>> > > >         size = fwrite(data, 1, bin_stat.st_size, f);
>> > > >         if (size < bin_stat.st_size) {
>> > > >                 printf("write failed (%zx)\n", size);
>> > > > -               goto err_3;
>> > > > +               goto err_4;
>> > > >         }
>> > > >
>> > > >         fclose(f);
>> > > >         fclose(g);
>> > > >         free(data);
>> > > > +       free_sig_data(&auth_context);
>> > > >
>> > > >         return 0;
>> > > >
>> > > > -err_3:
>> > > > +err_4:
>> > > >         fclose(f);
>> > > > +err_3:
>> > > > +       free_sig_data(&auth_context);
>> > > >  err_2:
>> > > >         free(data);
>> > > >  err_1:
>> > > > @@ -171,23 +510,37 @@ err_1:
>> > > >         return -1;
>> > > >  }
>> > > >
>> > > > -/*
>> > > > - * Usage:
>> > > > - *   $ mkeficapsule -f <firmware binary> <output file>
>> > > > +/**
>> > > > + * main - main entry function of mkeficapsule
>> > > > + * @argc:      Number of arguments
>> > > > + * @argv:      Array of pointers to arguments
>> > > > + *
>> > > > + * Create an uefi capsule file, optionally signing it.
>> > > > + * Parse all the arguments and pass them on to create_fwbin().
>> > > > + *
>> > > > + * Return:
>> > > > + * * 0  - on success
>> > > > + * * -1 - on failure
>> > > >   */
>> > > >  int main(int argc, char **argv)
>> > > >  {
>> > > >         char *file;
>> > > >         efi_guid_t *guid;
>> > > >         unsigned long index, instance;
>> > > > +       uint64_t mcount;
>> > > > +       char *privkey_file, *cert_file;
>> > > >         int c, idx;
>> > > >
>> > > >         file = NULL;
>> > > >         guid = NULL;
>> > > >         index = 0;
>> > > >         instance = 0;
>> > > > +       mcount = 0;
>> > > > +       privkey_file = NULL;
>> > > > +       cert_file = NULL;
>> > > > +       dump_sig = 0;
>> > > >         for (;;) {
>> > > > -               c = getopt_long(argc, argv, "f:r:i:I:v:h", options, &idx);
>> > > > +               c = getopt_long(argc, argv, opts_short, options, &idx);
>> > > >                 if (c == -1)
>> > > >                         break;
>> > > >
>> > > > @@ -214,29 +567,47 @@ int main(int argc, char **argv)
>> > > >                 case 'I':
>> > > >                         instance = strtoul(optarg, NULL, 0);
>> > > >                         break;
>> > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO
>> > > > +               case 'p':
>> > > > +                       if (privkey_file) {
>> > > > +                               printf("Private Key already specified\n");
>> > > > +                               return -1;
>> > > > +                       }
>> > > > +                       privkey_file = optarg;
>> > > > +                       break;
>> > > > +               case 'c':
>> > > > +                       if (cert_file) {
>> > > > +                               printf("Certificate file already specified\n");
>> > > > +                               return -1;
>> > > > +                       }
>> > > > +                       cert_file = optarg;
>> > > > +                       break;
>> > > > +               case 'm':
>> > > > +                       mcount = strtoul(optarg, NULL, 0);
>> > > > +                       break;
>> > > > +               case 'd':
>> > > > +                       dump_sig = 1;
>> > > > +                       break;
>> > > > +#endif /* CONFIG_TOOLS_LIBCRYPTO */
>> > > >                 case 'h':
>> > > >                         print_usage();
>> > > >                         return 0;
>> > > >                 }
>> > > >         }
>> > > >
>> > > > -       /* need an output file */
>> > > > -       if (argc != optind + 1) {
>> > > > -               print_usage();
>> > > > -               exit(EXIT_FAILURE);
>> > > > -       }
>> > > > -
>> > > > -       /* need a fit image file or raw image file */
>> > > > -       if (!file) {
>> > > > +       /* check necessary parameters */
>> > > > +       if ((argc != optind + 1) || !file ||
>> > > > +           ((privkey_file && !cert_file) ||
>> > > > +            (!privkey_file && cert_file))) {
>> > > >                 print_usage();
>> > > > -               exit(EXIT_SUCCESS);
>> > > > +               return -1;
>> > > >         }
>> > > >
>> > > > -       if (create_fwbin(argv[optind], file, guid, index, instance)
>> > > > -                       < 0) {
>> > > > +       if (create_fwbin(argv[optind], file, guid, index, instance,
>> > > > +                        mcount, privkey_file, cert_file) < 0) {
>> > > >                 printf("Creating firmware capsule failed\n");
>> > > > -               exit(EXIT_FAILURE);
>> > > > +               return -1;
>> > > >         }
>> > > >
>> > > > -       exit(EXIT_SUCCESS);
>> > > > +       return 0;
>> > > >  }
>> > > > --
>> > > > 2.33.0
>> > > >
>> > >
>> > >
>> > > --
>> > > Masami Hiramatsu
>>
>>
>>
>> --
>> Masami Hiramatsu



-- 
Masami Hiramatsu

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v4 03/11] efi_loader: capsule: add back efi_get_public_key_data()
  2021-10-25  7:14               ` François Ozog
@ 2021-10-25 15:18                 ` Simon Glass
  0 siblings, 0 replies; 40+ messages in thread
From: Simon Glass @ 2021-10-25 15:18 UTC (permalink / raw)
  To: François Ozog
  Cc: Masami Hiramatsu, AKASHI Takahiro, Alex Graf,
	Heinrich Schuchardt, Ilias Apalodimas, Sughosh Ganu,
	U-Boot Mailing List

Hi François,

On Mon, 25 Oct 2021 at 01:14, François Ozog <francois.ozog@linaro.org> wrote:
>
>
>
> Le lun. 25 oct. 2021 à 09:05, Masami Hiramatsu <masami.hiramatsu@linaro.org> a écrit :
>>
>> Hi Francois,
>>
>> 2021年10月25日(月) 15:28 François Ozog <francois.ozog@linaro.org>:
>> >
>> >
>> >
>> > Le lun. 25 oct. 2021 à 07:14, AKASHI Takahiro <takahiro.akashi@linaro.org> a écrit :
>> >>
>> >> On Wed, Oct 20, 2021 at 07:39:37AM -0600, Simon Glass wrote:
>> >> > Hi Masami,
>> >> >
>> >> > On Wed, 20 Oct 2021 at 02:18, Masami Hiramatsu
>> >> > <masami.hiramatsu@linaro.org> wrote:
>> >> > >
>> >> > > Hi Simon,
>> >> > >
>> >> > > 2021年10月15日(金) 9:40 Simon Glass <sjg@chromium.org>:
>> >> > > >
>> >> > > > Hi Takahiro,
>> >> > > >
>> >> > > > On Thu, 7 Oct 2021 at 00:25, AKASHI Takahiro <takahiro.akashi@linaro.org> wrote:
>> >> > > > >
>> >> > > > > The commit 47a25e81d35c ("Revert "efi_capsule: Move signature from DTB to
>> >> > > > > .rodata"") failed to revert the removal of efi_get_public_key_data().
>> >> > > > >
>> >> > > > > Add back this function and move it under lib/efi_loader so that other
>> >> > > > > platforms can utilize it. It is now declared as a weak function so that
>> >> > > > > it can be replaced with a platform-specific implementation.
>> >> > > > >
>> >> > > > > Fixes: 47a25e81d35c ("Revert "efi_capsule: Move signature from DTB to
>> >> > > > >         .rodata"")
>> >> > > > > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
>> >> > > > > ---
>> >> > > > >  lib/efi_loader/efi_capsule.c | 36 ++++++++++++++++++++++++++++++++++++
>> >> > > > >  1 file changed, 36 insertions(+)
>> >> > > > >
>> >> > > > > diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
>> >> > > > > index b75e4bcba1a9..44f5da61a9be 100644
>> >> > > > > --- a/lib/efi_loader/efi_capsule.c
>> >> > > > > +++ b/lib/efi_loader/efi_capsule.c
>> >> > > > > @@ -11,15 +11,20 @@
>> >> > > > >  #include <common.h>
>> >> > > > >  #include <efi_loader.h>
>> >> > > > >  #include <efi_variable.h>
>> >> > > > > +#include <env.h>
>> >> > > > > +#include <fdtdec.h>
>> >> > > > >  #include <fs.h>
>> >> > > > >  #include <malloc.h>
>> >> > > > >  #include <mapmem.h>
>> >> > > > >  #include <sort.h>
>> >> > > > > +#include <asm/global_data.h>
>> >> > > > >
>> >> > > > >  #include <crypto/pkcs7.h>
>> >> > > > >  #include <crypto/pkcs7_parser.h>
>> >> > > > >  #include <linux/err.h>
>> >> > > > >
>> >> > > > > +DECLARE_GLOBAL_DATA_PTR;
>> >> > > > > +
>> >> > > > >  const efi_guid_t efi_guid_capsule_report = EFI_CAPSULE_REPORT_GUID;
>> >> > > > >  static const efi_guid_t efi_guid_firmware_management_capsule_id =
>> >> > > > >                 EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID;
>> >> > > > > @@ -251,6 +256,37 @@ out:
>> >> > > > >  }
>> >> > > > >
>> >> > > > >  #if defined(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>> >> > > > > +int __weak efi_get_public_key_data(void **pkey, efi_uintn_t *pkey_len)
>> >> > > >
>> >> > > > I don't think this should be weak. What other way is there of handling
>> >> > > > this and why would it be platform-specific?
>> >> > >
>> >> > > I have a question about the current design of the capsule auth key.
>> >> > > If the platform has its own key-storage, how can the platform use the
>> >> > > platform specific storage? Does such platform load the key from the storage
>> >> > > and generate the dtb node in the platform initialization code? (or
>> >> > > device driver?)
>> >> >
>> >> > Are we talking about a public key (which I assume from the function
>> >> > naming) or some secret key. What is an auth key?
>> >>
>> >> Surely, a public key (more strictly, x509 certificate under the current
>> >> implementation)
>> >>
>> >> > As I understand it, the public key should be provided by the platform
>> >> > in devicetree that U-Boot uses, by whatever prior stage has access to
>> >> > the key.
>> >>
>> >> I still believe that some platform provider may want to save the key
>> >> in a *safer* storage, which should be at least read-only for non-authorized
>> >> writers.
>> >
>> >
>> > indeed. And U-Boot may not be entitled at all to check the capsule signature. For example updating SCP firmware with a key that is in secure world and will never leave this world.
>>
>> I think secure world firmware updates should be discussed in another
>> thread, like with FWU. At this point, the capsule signature will be
>> only authenticated by U-Boot, because we haven't passed the capsule
>> image to the secure world yet.
>
> i took a wrong example. The choice of authentication is to be done by the capsule driver designer and is outside scope of U-Boot. And the key may be in a separate storage or even the driver may invoke secure world for the authentication (FF-A API or other platform specific). U-Boot may have a capsule driver to update U-Boot components such as external env file. The location of the key can be in a u-boot specific device tree.

There is no such thing, in practice. There is just one devicetree. We
need to resolve this as we are still not aligned on this, after many
months. I think in fact there is difference of opinion about the
nature of the firmware image itself. I see it is a combined whole,
where the various blobs are an unfortunate result of firmware
fragmentation and must be stitched together with binman, so they can
each see the full picture to the extent needed.

>
>>
>>
>> >> But if this issue (__weak or not) is the only blocking factor
>> >> for my entire patch series, I'm willing to drop __weak for now since
>> >> someone with production system may change it in the future when he has
>> >> a good reason for that :)
>> >
>> >
>> > If that need be….
>>
>> Agreed.
>>

Yes please


- Simon

>>
>> Thank you,
>>
>> >
>> >>
>> >>
>> >> -Takahiro Akashi
>> >>
>> >>
>> >> > Regards,
>> >> > Simon
>> >
>> > --
>> > François-Frédéric Ozog | Director Business Development
>> > T: +33.67221.6485
>> > francois.ozog@linaro.org | Skype: ffozog
>> >
>>
>>
>> --
>> Masami Hiramatsu
>
> --
> François-Frédéric Ozog | Director Business Development
> T: +33.67221.6485
> francois.ozog@linaro.org | Skype: ffozog
>

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v4 04/11] tools: add fdtsig.sh
  2021-10-25  3:06         ` AKASHI Takahiro
@ 2021-10-26  6:00           ` AKASHI Takahiro
  2021-10-27 14:05             ` Simon Glass
  0 siblings, 1 reply; 40+ messages in thread
From: AKASHI Takahiro @ 2021-10-26  6:00 UTC (permalink / raw)
  To: Simon Glass, Heinrich Schuchardt, Alex Graf, Ilias Apalodimas,
	Sughosh Ganu, Masami Hiramatsu, U-Boot Mailing List

On Mon, Oct 25, 2021 at 12:06:39PM +0900, AKASHI Takahiro wrote:
> Simon,
> 
> On Thu, Oct 14, 2021 at 06:40:24PM -0600, Simon Glass wrote:
> > Hi Takahiro,
> > 
> > On Mon, 11 Oct 2021 at 19:42, AKASHI Takahiro
> > <takahiro.akashi@linaro.org> wrote:
> > >
> > > Simon,
> > >
> > > On Mon, Oct 11, 2021 at 08:54:09AM -0600, Simon Glass wrote:
> > > > Hi Takahiro,
> > > >
> > > > On Thu, 7 Oct 2021 at 00:25, AKASHI Takahiro <takahiro.akashi@linaro.org> wrote:
> > > > >
> > > > > With this script, a public key is added to a device tree blob
> > > > > as the default efi_get_public_key_data() expects.
> > > > >
> > > > > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > > > > ---
> > > > >  MAINTAINERS     |  1 +
> > > > >  tools/fdtsig.sh | 40 ++++++++++++++++++++++++++++++++++++++++
> > > > >  2 files changed, 41 insertions(+)
> > > > >  create mode 100755 tools/fdtsig.sh
> > > >
> > > > Instead of an ad-hoc script with no tests,
> > >
> > > Basically I intended to provide fdtsig.sh as a *sample* script so that
> > > people may want to integrate the logic into their own build rule/systems.
> > > But I could use this script in my 'capsule authentication' test
> > > that is also added in patch#22.
> > >
> > > > could we use binman for
> > > > putting the image together and inserting it?
> > >
> > > First, as you can see, the script is quite simple and secondly,
> > > the purpose of binman, IIUC, is to help handle/manipulate U-Boot
> > > image binaries.
> > > So I'm not sure whether it is really useful to add such a feature to binman.
> > 
> > I'm not sure. The script seems very ad-hoc to me, for a feature that
> > Linaro is pushing so hard.
> 
> To be honest, I've never used binman :) So I'm not sure whether binman
> is the best place to add this feature. For example, README under tools/binman
> says, "It seems better to use the mkimage tool to generate binaries and avoid
> blurring the boundaries between building input files (mkimage) and packaging
> then into a final image (binman)."
> Obviously, dtb is not the final image.
> 
> > I don't see where the script is used in the tests or even mentioned in
> > the documentation. Am I missing something?
> 
> Due to the history of submissions of this series, the current pytest
> scenario doesn't use the script, but you can see the exact same
> sequence of commands at test/py/tests/test_efi_capsule/conftest.py:
> ---8<---
>         # Update dtb adding capsule certificate
>         check_call('cd %s; cp %s/test/py/tests/test_efi_capsule/signature.dts .'
>                    % (data_dir, u_boot_config.source_dir), shell=True)
>         check_call('cd %s; dtc -@ -I dts -O dtb -o signature.dtbo signature.dts; fdtoverlay -i %s/arch/sandbox/dts/test.dtb -o test_sig.dtb signature.dtbo'
>                    % (data_dir, u_boot_config.build_dir), shell=True)
> --->8---
> (Please see my patch#11.)
> 
> What I meant is that we can directly use fdtsig.sh here if your concern
> is that the script is *not exercised* anywhere.

Besides binman or fdtsig.sh, I found that the crucial information was
missing here; the format or how a public key is encoded in a device tree.
With an example of command sequence, we may drop this patch.

So will adding some description to uefi.rst satisfy your needs,
or do you expect an extra rule for embedding a key to be added
in some Makefile?
(I don't think this step is part of build process, though.)

-Takahiro Akashi

> -Takahiro Akashi
> 
> > Regards,
> > Simon

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v4 04/11] tools: add fdtsig.sh
  2021-10-26  6:00           ` AKASHI Takahiro
@ 2021-10-27 14:05             ` Simon Glass
  0 siblings, 0 replies; 40+ messages in thread
From: Simon Glass @ 2021-10-27 14:05 UTC (permalink / raw)
  To: AKASHI Takahiro, Simon Glass, Heinrich Schuchardt, Alex Graf,
	Ilias Apalodimas, Sughosh Ganu, Masami Hiramatsu,
	U-Boot Mailing List

Hi Takahiro,

On Tue, 26 Oct 2021 at 00:00, AKASHI Takahiro
<takahiro.akashi@linaro.org> wrote:
>
> On Mon, Oct 25, 2021 at 12:06:39PM +0900, AKASHI Takahiro wrote:
> > Simon,
> >
> > On Thu, Oct 14, 2021 at 06:40:24PM -0600, Simon Glass wrote:
> > > Hi Takahiro,
> > >
> > > On Mon, 11 Oct 2021 at 19:42, AKASHI Takahiro
> > > <takahiro.akashi@linaro.org> wrote:
> > > >
> > > > Simon,
> > > >
> > > > On Mon, Oct 11, 2021 at 08:54:09AM -0600, Simon Glass wrote:
> > > > > Hi Takahiro,
> > > > >
> > > > > On Thu, 7 Oct 2021 at 00:25, AKASHI Takahiro <takahiro.akashi@linaro.org> wrote:
> > > > > >
> > > > > > With this script, a public key is added to a device tree blob
> > > > > > as the default efi_get_public_key_data() expects.
> > > > > >
> > > > > > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > > > > > ---
> > > > > >  MAINTAINERS     |  1 +
> > > > > >  tools/fdtsig.sh | 40 ++++++++++++++++++++++++++++++++++++++++
> > > > > >  2 files changed, 41 insertions(+)
> > > > > >  create mode 100755 tools/fdtsig.sh
> > > > >
> > > > > Instead of an ad-hoc script with no tests,
> > > >
> > > > Basically I intended to provide fdtsig.sh as a *sample* script so that
> > > > people may want to integrate the logic into their own build rule/systems.
> > > > But I could use this script in my 'capsule authentication' test
> > > > that is also added in patch#22.
> > > >
> > > > > could we use binman for
> > > > > putting the image together and inserting it?
> > > >
> > > > First, as you can see, the script is quite simple and secondly,
> > > > the purpose of binman, IIUC, is to help handle/manipulate U-Boot
> > > > image binaries.
> > > > So I'm not sure whether it is really useful to add such a feature to binman.
> > >
> > > I'm not sure. The script seems very ad-hoc to me, for a feature that
> > > Linaro is pushing so hard.
> >
> > To be honest, I've never used binman :) So I'm not sure whether binman
> > is the best place to add this feature. For example, README under tools/binman
> > says, "It seems better to use the mkimage tool to generate binaries and avoid
> > blurring the boundaries between building input files (mkimage) and packaging
> > then into a final image (binman)."
> > Obviously, dtb is not the final image.
> >
> > > I don't see where the script is used in the tests or even mentioned in
> > > the documentation. Am I missing something?
> >
> > Due to the history of submissions of this series, the current pytest
> > scenario doesn't use the script, but you can see the exact same
> > sequence of commands at test/py/tests/test_efi_capsule/conftest.py:
> > ---8<---
> >         # Update dtb adding capsule certificate
> >         check_call('cd %s; cp %s/test/py/tests/test_efi_capsule/signature.dts .'
> >                    % (data_dir, u_boot_config.source_dir), shell=True)
> >         check_call('cd %s; dtc -@ -I dts -O dtb -o signature.dtbo signature.dts; fdtoverlay -i %s/arch/sandbox/dts/test.dtb -o test_sig.dtb signature.dtbo'
> >                    % (data_dir, u_boot_config.build_dir), shell=True)
> > --->8---
> > (Please see my patch#11.)
> >
> > What I meant is that we can directly use fdtsig.sh here if your concern
> > is that the script is *not exercised* anywhere.
>
> Besides binman or fdtsig.sh, I found that the crucial information was
> missing here; the format or how a public key is encoded in a device tree.
> With an example of command sequence, we may drop this patch.
>
> So will adding some description to uefi.rst satisfy your needs,

Seems OK to me, and some sort of CI test.

> or do you expect an extra rule for embedding a key to be added
> in some Makefile?
> (I don't think this step is part of build process, though.)

I agree, this is best done in the packaging step, after all the bits
have been built.

Binman is designed to run inside the U-Boot build and also separately,
to collect everything and redo whatever happened in the U-Boot build.

REgards,
Simon

^ permalink raw reply	[flat|nested] 40+ messages in thread

end of thread, other threads:[~2021-10-27 14:05 UTC | newest]

Thread overview: 40+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-10-07  6:23 [PATCH v4 00/11] efi_loader: capsule: improve capsule authentication support AKASHI Takahiro
2021-10-07  6:23 ` [PATCH v4 01/11] Revert "Revert "mkeficapsule: Remove dtb related options"" AKASHI Takahiro
2021-10-08 15:38   ` Simon Glass
2021-10-08 19:11     ` Ilias Apalodimas
2021-10-11  0:29       ` AKASHI Takahiro
2021-10-11 14:54         ` Simon Glass
2021-10-12  1:15           ` AKASHI Takahiro
2021-10-07  6:23 ` [PATCH v4 02/11] Revert "Revert "doc: Update CapsuleUpdate READMEs"" AKASHI Takahiro
2021-10-07  6:23 ` [PATCH v4 03/11] efi_loader: capsule: add back efi_get_public_key_data() AKASHI Takahiro
2021-10-08 19:25   ` Ilias Apalodimas
2021-10-15  0:40   ` Simon Glass
2021-10-20  8:18     ` Masami Hiramatsu
2021-10-20  9:08       ` François Ozog
2021-10-20 13:39       ` Simon Glass
2021-10-25  5:14         ` AKASHI Takahiro
2021-10-25  6:28           ` François Ozog
2021-10-25  7:04             ` Masami Hiramatsu
2021-10-25  7:14               ` François Ozog
2021-10-25 15:18                 ` Simon Glass
2021-10-07  6:23 ` [PATCH v4 04/11] tools: add fdtsig.sh AKASHI Takahiro
2021-10-11 14:54   ` Simon Glass
2021-10-12  1:42     ` AKASHI Takahiro
2021-10-15  0:40       ` Simon Glass
2021-10-25  3:06         ` AKASHI Takahiro
2021-10-26  6:00           ` AKASHI Takahiro
2021-10-27 14:05             ` Simon Glass
2021-10-07  6:23 ` [PATCH v4 05/11] tools: mkeficapsule: add firmwware image signing AKASHI Takahiro
2021-10-20  8:17   ` Masami Hiramatsu
2021-10-25  3:12     ` AKASHI Takahiro
2021-10-25  5:40       ` Masami Hiramatsu
2021-10-25  6:09         ` AKASHI Takahiro
2021-10-25  7:04           ` Masami Hiramatsu
2021-10-25  9:58         ` Sughosh Ganu
2021-10-25 12:46           ` Masami Hiramatsu
2021-10-07  6:23 ` [PATCH v4 06/11] tools: mkeficapsule: add man page AKASHI Takahiro
2021-10-07  6:23 ` [PATCH v4 07/11] doc: update UEFI document for usage of mkeficapsule AKASHI Takahiro
2021-10-07  6:23 ` [PATCH v4 08/11] tools: mkeficapsule: allow for specifying GUID explicitly AKASHI Takahiro
2021-10-07  6:23 ` [PATCH v4 09/11] test/py: efi_capsule: align with the syntax change of mkeficapsule AKASHI Takahiro
2021-10-07  6:23 ` [PATCH v4 10/11] test/py: efi_capsule: add a test for "--guid" option AKASHI Takahiro
2021-10-07  6:23 ` [PATCH v4 11/11] test/py: efi_capsule: add image authentication test AKASHI Takahiro

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.