All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/4] efi_loader: capsule: improve capsule authentication support
@ 2021-05-12  4:57 AKASHI Takahiro
  2021-05-12  4:57 ` [PATCH 1/4] tools: mkeficapsule: add firmwware image signing AKASHI Takahiro
                   ` (4 more replies)
  0 siblings, 5 replies; 55+ messages in thread
From: AKASHI Takahiro @ 2021-05-12  4:57 UTC (permalink / raw)
  To: u-boot

As I discussed in [1], I have made a couple of improvements on
the current implemenation of capsule update.

Among others, this patch series
1. add signing feature to mkeficapsule
2. remove dtb operation from mkeficapsule
3. add pytest for capsule authentication (on sandbox)

NOTE:
I temporarily include Patch#3 in order to show that it is not worth
implementing in C as we can do the same thing with a very small
shell script.

My intent is *NOT* to merge Patch#3 in upstream.

Prerequisite patches
====================
See Sughosh's [2] and my [3].

Test
====
* passed the pytest which is included in this patch series
  on sandbox built locally.

Todo
====
* review and update the document for capsule update
    doc/board/emulation/qemu_capsule_update.rst
  (but not in this patch series)

[1] https://lists.denx.de/pipermail/u-boot/2021-April/447918.html
[2] https://lists.denx.de/pipermail/u-boot/2021-April/447183.html
[3] https://lists.denx.de/pipermail/u-boot/2021-May/449347.html
    https://lists.denx.de/pipermail/u-boot/2021-May/449348.html
    https://lists.denx.de/pipermail/u-boot/2021-May/449349.html
    https://lists.denx.de/pipermail/u-boot/2021-May/449350.html
    https://lists.denx.de/pipermail/u-boot/2021-May/449351.html

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

AKASHI Takahiro (4):
  tools: mkeficapsule: add firmwware image signing
  tools: mkeficapsule: remove device-tree related operation
  tools: add fdtsig command
  test/py: efi_capsule: add image authentication test

 Makefile                                      |   7 +-
 .../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  |   8 +
 .../test_capsule_firmware_signed.py           | 234 +++++++++
 tools/Makefile                                |   7 +-
 tools/fdtsig.c                                | 274 +++++++++++
 tools/fdtsig.sh                               |  40 ++
 tools/mkeficapsule.c                          | 455 ++++++++++--------
 9 files changed, 856 insertions(+), 209 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
 create mode 100644 tools/fdtsig.c
 create mode 100755 tools/fdtsig.sh

-- 
2.31.0

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

* [PATCH 1/4] tools: mkeficapsule: add firmwware image signing
  2021-05-12  4:57 [PATCH 0/4] efi_loader: capsule: improve capsule authentication support AKASHI Takahiro
@ 2021-05-12  4:57 ` AKASHI Takahiro
  2021-05-12  8:56   ` Heinrich Schuchardt
  2021-05-12  4:57 ` [PATCH 2/4] tools: mkeficapsule: remove device-tree related operation AKASHI Takahiro
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 55+ messages in thread
From: AKASHI Takahiro @ 2021-05-12  4:57 UTC (permalink / raw)
  To: u-boot

With this enhancement, mkeficapsule will be able to create a capsule
file with a signature which will be verified later by FMP's SetImage().

We will have to specify addtional command parameters:
  -monotonic-cout <count> : monotonic count
  -private-key <private key file> : private key file
  -certificate <certificate file> : certificate file
Only when those parameters are given, a signature will be added
to a capsule file.

Users are expected to maintain the monotonic count for each firmware
image.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 tools/Makefile       |   4 +
 tools/mkeficapsule.c | 324 +++++++++++++++++++++++++++++++++++++++----
 2 files changed, 303 insertions(+), 25 deletions(-)

diff --git a/tools/Makefile b/tools/Makefile
index d020c55d6644..02eae0286e20 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -231,6 +231,10 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs
 hostprogs-$(CONFIG_ASN1_COMPILER)	+= asn1_compiler
 HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
 
+ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),)
+HOSTLDLIBS_mkeficapsule += \
+	$(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto")
+endif
 mkeficapsule-objs	:= mkeficapsule.o $(LIBFDT_OBJS)
 hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
 
diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
index de0a62898886..34ff1bdd82eb 100644
--- a/tools/mkeficapsule.c
+++ b/tools/mkeficapsule.c
@@ -18,7 +18,17 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 
-#include "fdt_host.h"
+#include <linux/kconfig.h>
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
+#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
+
+#include <linux/libfdt.h>
 
 typedef __u8 u8;
 typedef __u16 u16;
@@ -46,6 +56,13 @@ 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;
+
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
+static const char *opts_short = "f:r:i:I:v:D:K:P:C:m:dOh";
+#else
+static const char *opts_short = "f:r:i:I:v:D:K:Oh";
+#endif
 
 static struct option options[] = {
 	{"fit", required_argument, NULL, 'f'},
@@ -54,6 +71,12 @@ static struct option options[] = {
 	{"instance", required_argument, NULL, 'I'},
 	{"dtb", required_argument, NULL, 'D'},
 	{"public key", required_argument, NULL, 'K'},
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
+	{"private-key", required_argument, NULL, 'P'},
+	{"certificate", required_argument, NULL, 'C'},
+	{"monotonic-count", required_argument, NULL, 'm'},
+	{"dump-sig", no_argument, NULL, 'd'},
+#endif
 	{"overlay", no_argument, NULL, 'O'},
 	{"help", no_argument, NULL, 'h'},
 	{NULL, 0, NULL, 0},
@@ -70,6 +93,12 @@ static void print_usage(void)
 	       "\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"
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
+	       "\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-O, --overlay               the dtb file is an overlay\n"
 	       "\t-h, --help                  print a help message\n",
 	       tool_name);
@@ -249,12 +278,167 @@ err:
 	return ret;
 }
 
+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;
+
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+#endif
+
 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;
@@ -266,6 +450,7 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
 	printf("\tbin: %s\n\ttype: %pUl\n", bin, guid);
 	printf("\tindex: %ld\n\tinstance: %ld\n", index, instance);
 #endif
+	auth_context.sig_size = 0;
 
 	g = fopen(bin, "r");
 	if (!g) {
@@ -281,11 +466,36 @@ 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 IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
+	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;
+		}
+	}
+#endif
+
 	header.capsule_guid = efi_guid_fm_capsule;
 	header.header_size = sizeof(header);
 	/* TODO: The current implementation ignores flags */
@@ -294,11 +504,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;
@@ -307,13 +526,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;
@@ -323,34 +542,61 @@ 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 IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
+	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;
+		}
 	}
+#endif
+
 	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);
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
+	if (auth_context.sig_size)
+		OPENSSL_free(auth_context.sig_data);
+#endif
 
 	return 0;
 
-err_3:
+err_4:
 	fclose(f);
+err_3:
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
+	if (auth_context.sig_size)
+		OPENSSL_free(auth_context.sig_data);
+#endif
 err_2:
 	free(data);
 err_1:
@@ -359,10 +605,6 @@ err_1:
 	return -1;
 }
 
-/*
- * Usage:
- *   $ mkeficapsule -f <firmware binary> <output file>
- */
 int main(int argc, char **argv)
 {
 	char *file;
@@ -370,6 +612,8 @@ int main(int argc, char **argv)
 	char *dtb_file;
 	efi_guid_t *guid;
 	unsigned long index, instance;
+	uint64_t mcount;
+	char *privkey_file, *cert_file;
 	int c, idx;
 	int ret;
 	bool overlay = false;
@@ -380,8 +624,12 @@ int main(int argc, char **argv)
 	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:D:K:Oh", options, &idx);
+		c = getopt_long(argc, argv, opts_short, options, &idx);
 		if (c == -1)
 			break;
 
@@ -422,6 +670,28 @@ int main(int argc, char **argv)
 			}
 			dtb_file = optarg;
 			break;
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
+		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
 		case 'O':
 			overlay = true;
 			break;
@@ -431,8 +701,12 @@ int main(int argc, char **argv)
 		}
 	}
 
-	/* need a fit image file or raw image file */
-	if (!file && !pkey_file && !dtb_file) {
+	/* check necessary parameters */
+	if ((file && (!(optind < argc) ||
+		      (privkey_file && !cert_file) ||
+		      (!privkey_file && cert_file))) ||
+	    ((pkey_file && !dtb_file) ||
+	     (!pkey_file && dtb_file))) {
 		print_usage();
 		exit(EXIT_FAILURE);
 	}
@@ -442,12 +716,12 @@ int main(int argc, char **argv)
 		if (ret == -1) {
 			printf("Adding public key to the dtb failed\n");
 			exit(EXIT_FAILURE);
-		} else {
-			exit(EXIT_SUCCESS);
 		}
 	}
 
-	if (create_fwbin(argv[optind], file, guid, index, instance)
+	if (optind < argc &&
+	    create_fwbin(argv[optind], file, guid, index, instance,
+			 mcount, privkey_file, cert_file)
 			< 0) {
 		printf("Creating firmware capsule failed\n");
 		exit(EXIT_FAILURE);
-- 
2.31.0

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

* [PATCH 2/4] tools: mkeficapsule: remove device-tree related operation
  2021-05-12  4:57 [PATCH 0/4] efi_loader: capsule: improve capsule authentication support AKASHI Takahiro
  2021-05-12  4:57 ` [PATCH 1/4] tools: mkeficapsule: add firmwware image signing AKASHI Takahiro
@ 2021-05-12  4:57 ` AKASHI Takahiro
  2021-05-12  7:20   ` Ilias Apalodimas
  2021-05-12  4:57 ` [PATCH 3/4] tools: add fdtsig command AKASHI Takahiro
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 55+ messages in thread
From: AKASHI Takahiro @ 2021-05-12  4:57 UTC (permalink / raw)
  To: u-boot

As we discussed, "-K" and "-D" options have nothing to do with
creating a capsule file. The same result can be obtained by
using standard commands like:
  === signature.dts ===
  /dts-v1/;
  /plugin/;

  &{/} {
        signature {
                capsule-key = /incbin/("SIGNER.esl");
        };
  };
  ===
  $ dtc -@ -I dts -O dtb -o signature.dtbo signature.dts
  $ fdtoverlay -i test.dtb -o test_sig.dtb -v signature.dtbo

So just remove this feature.
(Effectively revert the commit 322c813f4bec ("mkeficapsule: Add support
for embedding public key in a dtb").)

The same feature is implemented by a shell script (tools/fdtsig.sh).

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 Makefile             |   7 +-
 tools/Makefile       |   1 -
 tools/fdtsig.sh      |  40 ++++++++
 tools/mkeficapsule.c | 235 ++-----------------------------------------
 4 files changed, 50 insertions(+), 233 deletions(-)
 create mode 100755 tools/fdtsig.sh

diff --git a/Makefile b/Makefile
index 63b1f0143220..9806464357e0 100644
--- a/Makefile
+++ b/Makefile
@@ -1015,9 +1015,8 @@ cmd_pad_cat = $(cmd_objcopy) && $(append) || { rm -f $@; false; }
 quiet_cmd_lzma = LZMA    $@
 cmd_lzma = lzma -c -z -k -9 $< > $@
 
-quiet_cmd_mkeficapsule = MKEFICAPSULE     $@
-cmd_mkeficapsule = $(objtree)/tools/mkeficapsule -K $(CONFIG_EFI_PKEY_FILE) \
-	-D $@
+quiet_cmd_fdtsig = FDTSIG  $@
+cmd_fdtsig = $(srctree)/tools/fdtsig.sh $(CONFIG_EFI_PKEY_FILE) $@
 
 cfg: u-boot.cfg
 
@@ -1114,7 +1113,7 @@ dtbs: dts/dt.dtb
 ifeq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE)$(CONFIG_EFI_PKEY_DTB_EMBED),yy)
 dts/dt.dtb: u-boot tools
 	$(Q)$(MAKE) $(build)=dts dtbs
-	$(call cmd,mkeficapsule)
+	$(call cmd,fdtsig)
 else
 dts/dt.dtb: u-boot
 	$(Q)$(MAKE) $(build)=dts dtbs
diff --git a/tools/Makefile b/tools/Makefile
index 02eae0286e20..71a52719620c 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -235,7 +235,6 @@ ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),)
 HOSTLDLIBS_mkeficapsule += \
 	$(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto")
 endif
-mkeficapsule-objs	:= mkeficapsule.o $(LIBFDT_OBJS)
 hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
 
 # We build some files with extra pedantic flags to try to minimize things
diff --git a/tools/fdtsig.sh b/tools/fdtsig.sh
new file mode 100755
index 000000000000..aaa0a9190845
--- /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
diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
index 34ff1bdd82eb..97ce68ec83ee 100644
--- a/tools/mkeficapsule.c
+++ b/tools/mkeficapsule.c
@@ -4,17 +4,13 @@
  *		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>
 
@@ -28,8 +24,6 @@
 #include <openssl/pkcs7.h>
 #endif
 
-#include <linux/libfdt.h>
-
 typedef __u8 u8;
 typedef __u16 u16;
 typedef __u32 u32;
@@ -39,9 +33,6 @@ typedef __s32 s32;
 
 #define aligned_u64 __aligned_u64
 
-#define SIGNATURE_NODENAME	"signature"
-#define OVERLAY_NODENAME	"__overlay__"
-
 #ifndef __packed
 #define __packed __attribute__((packed))
 #endif
@@ -59,9 +50,9 @@ efi_guid_t efi_guid_image_type_uboot_raw =
 efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
 
 #if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
-static const char *opts_short = "f:r:i:I:v:D:K:P:C:m:dOh";
+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:D:K:Oh";
+static const char *opts_short = "f:r:i:I:v:h";
 #endif
 
 static struct option options[] = {
@@ -69,15 +60,12 @@ 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'},
 #if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
 	{"private-key", required_argument, NULL, 'P'},
 	{"certificate", required_argument, NULL, 'C'},
 	{"monotonic-count", required_argument, NULL, 'm'},
 	{"dump-sig", no_argument, NULL, 'd'},
 #endif
-	{"overlay", no_argument, NULL, 'O'},
 	{"help", no_argument, NULL, 'h'},
 	{NULL, 0, NULL, 0},
 };
@@ -104,180 +92,6 @@ static void print_usage(void)
 	       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;
-}
-
 struct auth_context {
 	char *key_file;
 	char *cert_file;
@@ -608,19 +422,13 @@ err_1:
 int main(int argc, char **argv)
 {
 	char *file;
-	char *pkey_file;
-	char *dtb_file;
 	efi_guid_t *guid;
 	unsigned long index, instance;
 	uint64_t mcount;
 	char *privkey_file, *cert_file;
 	int c, idx;
-	int ret;
-	bool overlay = false;
 
 	file = NULL;
-	pkey_file = NULL;
-	dtb_file = NULL;
 	guid = NULL;
 	index = 0;
 	instance = 0;
@@ -656,20 +464,6 @@ 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;
 #if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
 		case 'P':
 			if (privkey_file) {
@@ -692,9 +486,6 @@ int main(int argc, char **argv)
 			dump_sig = 1;
 			break;
 #endif
-		case 'O':
-			overlay = true;
-			break;
 		case 'h':
 			print_usage();
 			return 0;
@@ -702,27 +493,15 @@ int main(int argc, char **argv)
 	}
 
 	/* check necessary parameters */
-	if ((file && (!(optind < argc) ||
-		      (privkey_file && !cert_file) ||
-		      (!privkey_file && cert_file))) ||
-	    ((pkey_file && !dtb_file) ||
-	     (!pkey_file && dtb_file))) {
+	if (file && (!(optind < argc) ||
+		     (privkey_file && !cert_file) ||
+		     (!privkey_file && cert_file))) {
 		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);
-		}
-	}
-
-	if (optind < argc &&
-	    create_fwbin(argv[optind], file, guid, index, instance,
-			 mcount, privkey_file, cert_file)
-			< 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);
 	}
-- 
2.31.0

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

* [PATCH 3/4] tools: add fdtsig command
  2021-05-12  4:57 [PATCH 0/4] efi_loader: capsule: improve capsule authentication support AKASHI Takahiro
  2021-05-12  4:57 ` [PATCH 1/4] tools: mkeficapsule: add firmwware image signing AKASHI Takahiro
  2021-05-12  4:57 ` [PATCH 2/4] tools: mkeficapsule: remove device-tree related operation AKASHI Takahiro
@ 2021-05-12  4:57 ` AKASHI Takahiro
  2021-05-13  5:23   ` Heinrich Schuchardt
  2021-05-12  4:57 ` [PATCH 4/4] test/py: efi_capsule: add image authentication test AKASHI Takahiro
  2021-05-12  5:04 ` [PATCH 0/4] efi_loader: capsule: improve capsule authentication support Heinrich Schuchardt
  4 siblings, 1 reply; 55+ messages in thread
From: AKASHI Takahiro @ 2021-05-12  4:57 UTC (permalink / raw)
  To: u-boot

This command allows us to add a certificate (or public key) to dtb blob:
{
	signature {
		capsule-key = "...";
	};
}

The value is actually a signature list in terms of UEFI specificaion,
and used in verifying UEFI capsules.

The code was originally developed by Sughosh and derived from
mkeficapsule.c.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 Makefile       |   2 +-
 tools/Makefile |   2 +
 tools/fdtsig.c | 274 +++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 277 insertions(+), 1 deletion(-)
 create mode 100644 tools/fdtsig.c

diff --git a/Makefile b/Makefile
index 9806464357e0..8b40987234a0 100644
--- a/Makefile
+++ b/Makefile
@@ -1016,7 +1016,7 @@ quiet_cmd_lzma = LZMA    $@
 cmd_lzma = lzma -c -z -k -9 $< > $@
 
 quiet_cmd_fdtsig = FDTSIG  $@
-cmd_fdtsig = $(srctree)/tools/fdtsig.sh $(CONFIG_EFI_PKEY_FILE) $@
+cmd_fdtsig = $(objtree)/tools/fdtsig -K $(CONFIG_EFI_PKEY_FILE) -D $@
 
 cfg: u-boot.cfg
 
diff --git a/tools/Makefile b/tools/Makefile
index 71a52719620c..e6fd1dbade19 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -234,6 +234,8 @@ HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
 ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),)
 HOSTLDLIBS_mkeficapsule += \
 	$(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto")
+	fdtsig-objs	:= fdtsig.o $(LIBFDT_OBJS)
+	hostprogs-y += fdtsig
 endif
 hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
 
diff --git a/tools/fdtsig.c b/tools/fdtsig.c
new file mode 100644
index 000000000000..daa1e63c3b33
--- /dev/null
+++ b/tools/fdtsig.c
@@ -0,0 +1,274 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2021 Linaro Limited
+ * The code in this file was extracted from mkeficapsule.c
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <malloc.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <linux/libfdt.h>
+
+#define SIGNATURE_NODENAME	"signature"
+#define OVERLAY_NODENAME	"__overlay__"
+
+static const char *tool_name = "fdtsig";
+
+static const char *opts_short = "D:K:Oh";
+
+static struct option options[] = {
+	{"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},
+};
+
+static void print_usage(void)
+{
+	printf("Usage: %s [options]\n"
+	       "Options:\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;
+}
+
+int main(int argc, char **argv)
+{
+	char *pkey_file;
+	char *dtb_file;
+	bool overlay;
+	int c, idx, ret;
+
+	pkey_file = NULL;
+	dtb_file = NULL;
+	overlay = false;
+
+	for (;;) {
+		c = getopt_long(argc, argv, opts_short, options, &idx);
+		if (c == -1)
+			break;
+
+		switch (c) {
+		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;
+		}
+	}
+
+	/* check necessary parameters */
+	if (!pkey_file || !dtb_file) {
+		print_usage();
+		exit(EXIT_FAILURE);
+	}
+
+	ret = add_public_key(pkey_file, dtb_file, overlay);
+	if (ret == -1) {
+		printf("Adding public key to the dtb failed\n");
+		exit(EXIT_FAILURE);
+	}
+
+	exit(EXIT_SUCCESS);
+}
-- 
2.31.0

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

* [PATCH 4/4] test/py: efi_capsule: add image authentication test
  2021-05-12  4:57 [PATCH 0/4] efi_loader: capsule: improve capsule authentication support AKASHI Takahiro
                   ` (2 preceding siblings ...)
  2021-05-12  4:57 ` [PATCH 3/4] tools: add fdtsig command AKASHI Takahiro
@ 2021-05-12  4:57 ` AKASHI Takahiro
  2021-05-12  5:04 ` [PATCH 0/4] efi_loader: capsule: improve capsule authentication support Heinrich Schuchardt
  4 siblings, 0 replies; 55+ messages in thread
From: AKASHI Takahiro @ 2021-05-12  4:57 UTC (permalink / raw)
  To: u-boot

Add a couple of test cases against dealing with a signed capsule file.

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  |   8 +
 .../test_capsule_firmware_signed.py           | 234 ++++++++++++++++++
 4 files changed, 279 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 6ad5608cd71c..bd9544bc056f 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
+       authenticaion 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,
@@ -56,6 +76,15 @@ def efi_capsule_data(request, u_boot_config):
         check_call('cd %s; %s/tools/mkeficapsule --raw u-boot.bin.new --index 1 Test02' %
                    (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 --raw u-boot.bin.new --index 1 --monotonic-count 1 --private-key SIGNER.key --certificate SIGNER.crt Test03' %
+                       (data_dir, u_boot_config.build_dir),
+                       shell=True)
+            # firmware signed with *mal* key
+            check_call('cd %s; %s/tools/mkeficapsule --raw u-boot.bin.new --index 1 --monotonic-count 1 --private-key SIGNER2.key --certificate SIGNER2.crt Test04' %
+                       (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..55c15432979a
--- /dev/null
+++ b/test/py/tests/test_efi_capsule/signature.dts
@@ -0,0 +1,8 @@
+/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..58a45ddf57a3
--- /dev/null
+++ b/test/py/tests/test_efi_capsule/test_capsule_firmware_signed.py
@@ -0,0 +1,234 @@
+# 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
+"""
+
+from subprocess import check_call, check_output, CalledProcessError
+import pytest
+from capsule_defs import *
+
+ at pytest.mark.boardspec('sandbox')
+ at pytest.mark.buildconfigspec('efi_capsule_firmware_raw')
+ at pytest.mark.buildconfigspec('efi_capsule_authenticate')
+ at pytest.mark.buildconfigspec('dfu')
+ at pytest.mark.buildconfigspec('dfu_sf')
+ at pytest.mark.buildconfigspec('cmd_efidebug')
+ at pytest.mark.buildconfigspec('cmd_fat')
+ at pytest.mark.buildconfigspec('cmd_memory')
+ at pytest.mark.buildconfigspec('cmd_nvedit_efi')
+ at pytest.mark.buildconfigspec('cmd_sf')
+ at 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/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
+        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 '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([
+                '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)
+
+    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/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 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 'Test04' 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 'Test04' 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.31.0

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

* [PATCH 0/4] efi_loader: capsule: improve capsule authentication support
  2021-05-12  4:57 [PATCH 0/4] efi_loader: capsule: improve capsule authentication support AKASHI Takahiro
                   ` (3 preceding siblings ...)
  2021-05-12  4:57 ` [PATCH 4/4] test/py: efi_capsule: add image authentication test AKASHI Takahiro
@ 2021-05-12  5:04 ` Heinrich Schuchardt
  4 siblings, 0 replies; 55+ messages in thread
From: Heinrich Schuchardt @ 2021-05-12  5:04 UTC (permalink / raw)
  To: u-boot

Am 12. Mai 2021 06:57:49 MESZ schrieb AKASHI Takahiro <takahiro.akashi@linaro.org>:
>As I discussed in [1], I have made a couple of improvements on
>the current implemenation of capsule update.
>
>Among others, this patch series
>1. add signing feature to mkeficapsule
>2. remove dtb operation from mkeficapsule
>3. add pytest for capsule authentication (on sandbox)
>
>NOTE:
>I temporarily include Patch#3 in order to show that it is not worth
>implementing in C as we can do the same thing with a very small
>shell script.
>
>My intent is *NOT* to merge Patch#3 in upstream.
>

Should you need to resend the series, please, put "DO NOT MERGE" into the title of patch 3.

Best regards

Heinrich


>Prerequisite patches
>====================
>See Sughosh's [2] and my [3].
>
>Test
>====
>* passed the pytest which is included in this patch series
>  on sandbox built locally.
>
>Todo
>====
>* review and update the document for capsule update
>    doc/board/emulation/qemu_capsule_update.rst
>  (but not in this patch series)
>
>[1] https://lists.denx.de/pipermail/u-boot/2021-April/447918.html
>[2] https://lists.denx.de/pipermail/u-boot/2021-April/447183.html
>[3] https://lists.denx.de/pipermail/u-boot/2021-May/449347.html
>    https://lists.denx.de/pipermail/u-boot/2021-May/449348.html
>    https://lists.denx.de/pipermail/u-boot/2021-May/449349.html
>    https://lists.denx.de/pipermail/u-boot/2021-May/449350.html
>    https://lists.denx.de/pipermail/u-boot/2021-May/449351.html
>
>Changes
>=======
>Initial release (May 12, 2021)
>* based on v2021.07-rc2
>
>AKASHI Takahiro (4):
>  tools: mkeficapsule: add firmwware image signing
>  tools: mkeficapsule: remove device-tree related operation
>  tools: add fdtsig command
>  test/py: efi_capsule: add image authentication test
>
> Makefile                                      |   7 +-
> .../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  |   8 +
> .../test_capsule_firmware_signed.py           | 234 +++++++++
> tools/Makefile                                |   7 +-
> tools/fdtsig.c                                | 274 +++++++++++
> tools/fdtsig.sh                               |  40 ++
> tools/mkeficapsule.c                          | 455 ++++++++++--------
> 9 files changed, 856 insertions(+), 209 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
> create mode 100644 tools/fdtsig.c
> create mode 100755 tools/fdtsig.sh

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

* [PATCH 2/4] tools: mkeficapsule: remove device-tree related operation
  2021-05-12  4:57 ` [PATCH 2/4] tools: mkeficapsule: remove device-tree related operation AKASHI Takahiro
@ 2021-05-12  7:20   ` Ilias Apalodimas
  2021-05-12  7:49     ` Masami Hiramatsu
  0 siblings, 1 reply; 55+ messages in thread
From: Ilias Apalodimas @ 2021-05-12  7:20 UTC (permalink / raw)
  To: u-boot

Akashi-san,

On Wed, May 12, 2021 at 01:57:51PM +0900, AKASHI Takahiro wrote:
> As we discussed, "-K" and "-D" options have nothing to do with
> creating a capsule file. The same result can be obtained by
> using standard commands like:
>   === signature.dts ===
>   /dts-v1/;
>   /plugin/;
> 
>   &{/} {
>         signature {
>                 capsule-key = /incbin/("SIGNER.esl");
>         };
>   };
>   ===
>   $ dtc -@ -I dts -O dtb -o signature.dtbo signature.dts
>   $ fdtoverlay -i test.dtb -o test_sig.dtb -v signature.dtbo
> 
> So just remove this feature.
> (Effectively revert the commit 322c813f4bec ("mkeficapsule: Add support
> for embedding public key in a dtb").)
> 
> The same feature is implemented by a shell script (tools/fdtsig.sh).
 

The only reason I can see to keep this, is if mkeficapsule gets included
intro distro packages in the future.  That would make end users life a bit
easier, since they would need a single binary to create the whole
CapsuleUpdate sequence. 

Regards
/Ilias

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

* [PATCH 2/4] tools: mkeficapsule: remove device-tree related operation
  2021-05-12  7:20   ` Ilias Apalodimas
@ 2021-05-12  7:49     ` Masami Hiramatsu
  2021-05-12  8:01       ` Ilias Apalodimas
  0 siblings, 1 reply; 55+ messages in thread
From: Masami Hiramatsu @ 2021-05-12  7:49 UTC (permalink / raw)
  To: u-boot

Hi Ilias,

2021?5?12?(?) 16:21 Ilias Apalodimas <ilias.apalodimas@linaro.org>:
>
> Akashi-san,
>
> On Wed, May 12, 2021 at 01:57:51PM +0900, AKASHI Takahiro wrote:
> > As we discussed, "-K" and "-D" options have nothing to do with
> > creating a capsule file. The same result can be obtained by
> > using standard commands like:
> >   === signature.dts ===
> >   /dts-v1/;
> >   /plugin/;
> >
> >   &{/} {
> >         signature {
> >                 capsule-key = /incbin/("SIGNER.esl");
> >         };
> >   };
> >   ===
> >   $ dtc -@ -I dts -O dtb -o signature.dtbo signature.dts
> >   $ fdtoverlay -i test.dtb -o test_sig.dtb -v signature.dtbo
> >
> > So just remove this feature.
> > (Effectively revert the commit 322c813f4bec ("mkeficapsule: Add support
> > for embedding public key in a dtb").)
> >
> > The same feature is implemented by a shell script (tools/fdtsig.sh).
>
>
> The only reason I can see to keep this, is if mkeficapsule gets included
> intro distro packages in the future.  That would make end users life a bit
> easier, since they would need a single binary to create the whole
> CapsuleUpdate sequence.

Hmm, I think it is better to write a manpage of mkeficapsule which
also describes
how to embed the key into dtb as in the above example if it is so short.
Or, distros can package the above shell script with mkeficapsule.

Embedding a key and signing a capsule are different operations but
using the same tool may confuse users (at least me).

Thank you,

-- 
Masami Hiramatsu

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

* [PATCH 2/4] tools: mkeficapsule: remove device-tree related operation
  2021-05-12  7:49     ` Masami Hiramatsu
@ 2021-05-12  8:01       ` Ilias Apalodimas
  2021-05-12 10:01         ` Heinrich Schuchardt
  0 siblings, 1 reply; 55+ messages in thread
From: Ilias Apalodimas @ 2021-05-12  8:01 UTC (permalink / raw)
  To: u-boot

On Wed, May 12, 2021 at 04:49:02PM +0900, Masami Hiramatsu wrote:
> Hi Ilias,
> 
> 2021?5?12?(?) 16:21 Ilias Apalodimas <ilias.apalodimas@linaro.org>:
> >
> > Akashi-san,
> >
> > On Wed, May 12, 2021 at 01:57:51PM +0900, AKASHI Takahiro wrote:
> > > As we discussed, "-K" and "-D" options have nothing to do with
> > > creating a capsule file. The same result can be obtained by
> > > using standard commands like:
> > >   === signature.dts ===
> > >   /dts-v1/;
> > >   /plugin/;
> > >
> > >   &{/} {
> > >         signature {
> > >                 capsule-key = /incbin/("SIGNER.esl");
> > >         };
> > >   };
> > >   ===
> > >   $ dtc -@ -I dts -O dtb -o signature.dtbo signature.dts
> > >   $ fdtoverlay -i test.dtb -o test_sig.dtb -v signature.dtbo
> > >
> > > So just remove this feature.
> > > (Effectively revert the commit 322c813f4bec ("mkeficapsule: Add support
> > > for embedding public key in a dtb").)
> > >
> > > The same feature is implemented by a shell script (tools/fdtsig.sh).
> >
> >
> > The only reason I can see to keep this, is if mkeficapsule gets included
> > intro distro packages in the future.  That would make end users life a bit
> > easier, since they would need a single binary to create the whole
> > CapsuleUpdate sequence.
> 
> Hmm, I think it is better to write a manpage of mkeficapsule which
> also describes
> how to embed the key into dtb as in the above example if it is so short.
> Or, distros can package the above shell script with mkeficapsule.
> 
> Embedding a key and signing a capsule are different operations but
> using the same tool may confuse users (at least me).

Sure fair enough.  I am merely pointing out we need a way to explain all of
those to users. 

Thanks!
/Ilias
> 
> Thank you,
> 
> -- 
> Masami Hiramatsu

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

* [PATCH 1/4] tools: mkeficapsule: add firmwware image signing
  2021-05-12  4:57 ` [PATCH 1/4] tools: mkeficapsule: add firmwware image signing AKASHI Takahiro
@ 2021-05-12  8:56   ` Heinrich Schuchardt
  2021-05-13  3:08     ` AKASHI Takahiro
  0 siblings, 1 reply; 55+ messages in thread
From: Heinrich Schuchardt @ 2021-05-12  8:56 UTC (permalink / raw)
  To: u-boot

On 12.05.21 06:57, AKASHI Takahiro wrote:
> With this enhancement, mkeficapsule will be able to create a capsule
> file with a signature which will be verified later by FMP's SetImage().
>
> We will have to specify addtional command parameters:
>   -monotonic-cout <count> : monotonic count
>   -private-key <private key file> : private key file
>   -certificate <certificate file> : certificate file
> Only when those parameters are given, a signature will be added
> to a capsule file.
>
> Users are expected to maintain the monotonic count for each firmware
> image.
>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>  tools/Makefile       |   4 +
>  tools/mkeficapsule.c | 324 +++++++++++++++++++++++++++++++++++++++----
>  2 files changed, 303 insertions(+), 25 deletions(-)
>
> diff --git a/tools/Makefile b/tools/Makefile
> index d020c55d6644..02eae0286e20 100644
> --- a/tools/Makefile
> +++ b/tools/Makefile
> @@ -231,6 +231,10 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs
>  hostprogs-$(CONFIG_ASN1_COMPILER)	+= asn1_compiler
>  HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
>
> +ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),)
> +HOSTLDLIBS_mkeficapsule += \
> +	$(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto")

I don't expect any user wants to install two tool versions in parallel.

The tool should always be able to add a signature.
Adding a signature must be optional.

> +endif
>  mkeficapsule-objs	:= mkeficapsule.o $(LIBFDT_OBJS)
>  hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
>
> diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
> index de0a62898886..34ff1bdd82eb 100644
> --- a/tools/mkeficapsule.c
> +++ b/tools/mkeficapsule.c
> @@ -18,7 +18,17 @@
>  #include <sys/stat.h>
>  #include <sys/types.h>
>
> -#include "fdt_host.h"
> +#include <linux/kconfig.h>
> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)

see above

> +#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
> +
> +#include <linux/libfdt.h>
>
>  typedef __u8 u8;
>  typedef __u16 u16;
> @@ -46,6 +56,13 @@ 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;
> +
> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)

see above

> +static const char *opts_short = "f:r:i:I:v:D:K:P:C:m:dOh";
> +#else
> +static const char *opts_short = "f:r:i:I:v:D:K:Oh";
> +#endif
>
>  static struct option options[] = {
>  	{"fit", required_argument, NULL, 'f'},
> @@ -54,6 +71,12 @@ static struct option options[] = {
>  	{"instance", required_argument, NULL, 'I'},
>  	{"dtb", required_argument, NULL, 'D'},
>  	{"public key", required_argument, NULL, 'K'},
> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> +	{"private-key", required_argument, NULL, 'P'},
> +	{"certificate", required_argument, NULL, 'C'},
> +	{"monotonic-count", required_argument, NULL, 'm'},

These options should not be required.

> +	{"dump-sig", no_argument, NULL, 'd'},
> +#endif
>  	{"overlay", no_argument, NULL, 'O'},
>  	{"help", no_argument, NULL, 'h'},
>  	{NULL, 0, NULL, 0},
> @@ -70,6 +93,12 @@ static void print_usage(void)
>  	       "\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"
> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)

see above

> +	       "\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-O, --overlay               the dtb file is an overlay\n"
>  	       "\t-h, --help                  print a help message\n",
>  	       tool_name);
> @@ -249,12 +278,167 @@ err:
>  	return ret;
>  }
>
> +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;
> +
> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)

see above

> +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);

Please, you use fprintf(stderr,) for error messages.

> +		ERR_print_errors_fp(stderr);
> +	}
> +
> +	return key;
> +}
> +
> +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);

fprintf(stderr,)

> +		ERR_print_errors_fp(stderr);
> +	}
> +
> +	return cert;
> +}
> +
> +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 */

PKCS7_NOATTR is a value without any documentation in the code.

Please, replace variable names by a long text describing what it missing.

> +	flags = PKCS7_BINARY | PKCS7_DETACHED;

Those constants lack documentation in the code.

> +	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;
> +}
> +
> +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;
> +}
> +#endif
> +
>  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;
> @@ -266,6 +450,7 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
>  	printf("\tbin: %s\n\ttype: %pUl\n", bin, guid);
>  	printf("\tindex: %ld\n\tinstance: %ld\n", index, instance);
>  #endif
> +	auth_context.sig_size = 0;
>
>  	g = fopen(bin, "r");
>  	if (!g) {
> @@ -281,11 +466,36 @@ 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 IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)

see above

> +	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;
> +		}
> +	}
> +#endif
> +
>  	header.capsule_guid = efi_guid_fm_capsule;
>  	header.header_size = sizeof(header);
>  	/* TODO: The current implementation ignores flags */
> @@ -294,11 +504,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;
> @@ -307,13 +526,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;
> @@ -323,34 +542,61 @@ 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 IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)

We don't want to use #if if avoidable.

> +	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;
> +		}
>  	}
> +#endif
> +
>  	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);
> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> +	if (auth_context.sig_size)
> +		OPENSSL_free(auth_context.sig_data);
> +#endif
>
>  	return 0;
>
> -err_3:
> +err_4:
>  	fclose(f);
> +err_3:
> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> +	if (auth_context.sig_size)
> +		OPENSSL_free(auth_context.sig_data);
> +#endif
>  err_2:
>  	free(data);
>  err_1:
> @@ -359,10 +605,6 @@ err_1:
>  	return -1;
>  }
>
> -/*
> - * Usage:
> - *   $ mkeficapsule -f <firmware binary> <output file>
> - */
>  int main(int argc, char **argv)
>  {
>  	char *file;
> @@ -370,6 +612,8 @@ int main(int argc, char **argv)
>  	char *dtb_file;
>  	efi_guid_t *guid;
>  	unsigned long index, instance;
> +	uint64_t mcount;
> +	char *privkey_file, *cert_file;
>  	int c, idx;
>  	int ret;
>  	bool overlay = false;
> @@ -380,8 +624,12 @@ int main(int argc, char **argv)
>  	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:D:K:Oh", options, &idx);
> +		c = getopt_long(argc, argv, opts_short, options, &idx);
>  		if (c == -1)
>  			break;
>
> @@ -422,6 +670,28 @@ int main(int argc, char **argv)
>  			}
>  			dtb_file = optarg;
>  			break;
> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)

see above

Best regards

Heinrich

> +		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
>  		case 'O':
>  			overlay = true;
>  			break;
> @@ -431,8 +701,12 @@ int main(int argc, char **argv)
>  		}
>  	}
>
> -	/* need a fit image file or raw image file */
> -	if (!file && !pkey_file && !dtb_file) {
> +	/* check necessary parameters */
> +	if ((file && (!(optind < argc) ||
> +		      (privkey_file && !cert_file) ||
> +		      (!privkey_file && cert_file))) ||
> +	    ((pkey_file && !dtb_file) ||
> +	     (!pkey_file && dtb_file))) {
>  		print_usage();
>  		exit(EXIT_FAILURE);
>  	}
> @@ -442,12 +716,12 @@ int main(int argc, char **argv)
>  		if (ret == -1) {
>  			printf("Adding public key to the dtb failed\n");
>  			exit(EXIT_FAILURE);
> -		} else {
> -			exit(EXIT_SUCCESS);
>  		}
>  	}
>
> -	if (create_fwbin(argv[optind], file, guid, index, instance)
> +	if (optind < argc &&
> +	    create_fwbin(argv[optind], file, guid, index, instance,
> +			 mcount, privkey_file, cert_file)
>  			< 0) {
>  		printf("Creating firmware capsule failed\n");
>  		exit(EXIT_FAILURE);
>

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

* [PATCH 2/4] tools: mkeficapsule: remove device-tree related operation
  2021-05-12  8:01       ` Ilias Apalodimas
@ 2021-05-12 10:01         ` Heinrich Schuchardt
  2021-05-13  2:33           ` AKASHI Takahiro
  0 siblings, 1 reply; 55+ messages in thread
From: Heinrich Schuchardt @ 2021-05-12 10:01 UTC (permalink / raw)
  To: u-boot

On 12.05.21 10:01, Ilias Apalodimas wrote:
> On Wed, May 12, 2021 at 04:49:02PM +0900, Masami Hiramatsu wrote:
>> Hi Ilias,
>>
>> 2021?5?12?(?) 16:21 Ilias Apalodimas <ilias.apalodimas@linaro.org>:
>>>
>>> Akashi-san,
>>>
>>> On Wed, May 12, 2021 at 01:57:51PM +0900, AKASHI Takahiro wrote:
>>>> As we discussed, "-K" and "-D" options have nothing to do with
>>>> creating a capsule file. The same result can be obtained by
>>>> using standard commands like:
>>>>   === signature.dts ===
>>>>   /dts-v1/;
>>>>   /plugin/;
>>>>
>>>>   &{/} {
>>>>         signature {
>>>>                 capsule-key = /incbin/("SIGNER.esl");
>>>>         };
>>>>   };
>>>>   ===
>>>>   $ dtc -@ -I dts -O dtb -o signature.dtbo signature.dts
>>>>   $ fdtoverlay -i test.dtb -o test_sig.dtb -v signature.dtbo
>>>>
>>>> So just remove this feature.
>>>> (Effectively revert the commit 322c813f4bec ("mkeficapsule: Add support
>>>> for embedding public key in a dtb").)
>>>>
>>>> The same feature is implemented by a shell script (tools/fdtsig.sh).
>>>
>>>
>>> The only reason I can see to keep this, is if mkeficapsule gets included
>>> intro distro packages in the future.  That would make end users life a bit
>>> easier, since they would need a single binary to create the whole
>>> CapsuleUpdate sequence.
>>
>> Hmm, I think it is better to write a manpage of mkeficapsule which
>> also describes
>> how to embed the key into dtb as in the above example if it is so short.
>> Or, distros can package the above shell script with mkeficapsule.
>>
>> Embedding a key and signing a capsule are different operations but
>> using the same tool may confuse users (at least me).
>
> Sure fair enough.  I am merely pointing out we need a way to explain all of
> those to users.

This is currently our only documentation:

https://u-boot.readthedocs.io/en/latest/board/emulation/qemu_capsule_update.html?highlight=mkeficapsule

For mkimage we have a man-page ./doc/mkimage.1 that is packaged with
Debians u-boot-tools package. Please, provide a similar man-page as
./doc/mkeficapsule.1.

Best regards

Heinrich

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

* [PATCH 2/4] tools: mkeficapsule: remove device-tree related operation
  2021-05-12 10:01         ` Heinrich Schuchardt
@ 2021-05-13  2:33           ` AKASHI Takahiro
  2021-05-13  5:08             ` Heinrich Schuchardt
  0 siblings, 1 reply; 55+ messages in thread
From: AKASHI Takahiro @ 2021-05-13  2:33 UTC (permalink / raw)
  To: u-boot

On Wed, May 12, 2021 at 12:01:32PM +0200, Heinrich Schuchardt wrote:
> On 12.05.21 10:01, Ilias Apalodimas wrote:
> > On Wed, May 12, 2021 at 04:49:02PM +0900, Masami Hiramatsu wrote:
> >> Hi Ilias,
> >>
> >> 2021?5?12?(?) 16:21 Ilias Apalodimas <ilias.apalodimas@linaro.org>:
> >>>
> >>> Akashi-san,
> >>>
> >>> On Wed, May 12, 2021 at 01:57:51PM +0900, AKASHI Takahiro wrote:
> >>>> As we discussed, "-K" and "-D" options have nothing to do with
> >>>> creating a capsule file. The same result can be obtained by
> >>>> using standard commands like:
> >>>>   === signature.dts ===
> >>>>   /dts-v1/;
> >>>>   /plugin/;
> >>>>
> >>>>   &{/} {
> >>>>         signature {
> >>>>                 capsule-key = /incbin/("SIGNER.esl");
> >>>>         };
> >>>>   };
> >>>>   ===
> >>>>   $ dtc -@ -I dts -O dtb -o signature.dtbo signature.dts
> >>>>   $ fdtoverlay -i test.dtb -o test_sig.dtb -v signature.dtbo
> >>>>
> >>>> So just remove this feature.
> >>>> (Effectively revert the commit 322c813f4bec ("mkeficapsule: Add support
> >>>> for embedding public key in a dtb").)
> >>>>
> >>>> The same feature is implemented by a shell script (tools/fdtsig.sh).
> >>>
> >>>
> >>> The only reason I can see to keep this, is if mkeficapsule gets included
> >>> intro distro packages in the future.  That would make end users life a bit
> >>> easier, since they would need a single binary to create the whole
> >>> CapsuleUpdate sequence.
> >>
> >> Hmm, I think it is better to write a manpage of mkeficapsule which
> >> also describes
> >> how to embed the key into dtb as in the above example if it is so short.
> >> Or, distros can package the above shell script with mkeficapsule.
> >>
> >> Embedding a key and signing a capsule are different operations but
> >> using the same tool may confuse users (at least me).
> >
> > Sure fair enough.  I am merely pointing out we need a way to explain all of
> > those to users.
> 
> This is currently our only documentation:
> 
> https://u-boot.readthedocs.io/en/latest/board/emulation/qemu_capsule_update.html?highlight=mkeficapsule

As I mentioned several times (and TODO in the cover letter),
this text must be reviewed, revised and generalized
as a platform-independent document.
It contains a couple of errors.

> For mkimage we have a man-page ./doc/mkimage.1 that is packaged with
> Debians u-boot-tools package. Please, provide a similar man-page as
> ./doc/mkeficapsule.1.

So after all do you agree to removing "-K/-D"?
Otherwise, I cannot complete the man page.

-Takahiro Akashi

> Best regards
> 
> Heinrich

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

* [PATCH 1/4] tools: mkeficapsule: add firmwware image signing
  2021-05-12  8:56   ` Heinrich Schuchardt
@ 2021-05-13  3:08     ` AKASHI Takahiro
  2021-05-13  4:22       ` Heinrich Schuchardt
  0 siblings, 1 reply; 55+ messages in thread
From: AKASHI Takahiro @ 2021-05-13  3:08 UTC (permalink / raw)
  To: u-boot

On Wed, May 12, 2021 at 10:56:41AM +0200, Heinrich Schuchardt wrote:
> On 12.05.21 06:57, AKASHI Takahiro wrote:
> > With this enhancement, mkeficapsule will be able to create a capsule
> > file with a signature which will be verified later by FMP's SetImage().
> >
> > We will have to specify addtional command parameters:
> >   -monotonic-cout <count> : monotonic count
> >   -private-key <private key file> : private key file
> >   -certificate <certificate file> : certificate file
> > Only when those parameters are given, a signature will be added
> > to a capsule file.
> >
> > Users are expected to maintain the monotonic count for each firmware
> > image.
> >
> > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > ---
> >  tools/Makefile       |   4 +
> >  tools/mkeficapsule.c | 324 +++++++++++++++++++++++++++++++++++++++----
> >  2 files changed, 303 insertions(+), 25 deletions(-)
> >
> > diff --git a/tools/Makefile b/tools/Makefile
> > index d020c55d6644..02eae0286e20 100644
> > --- a/tools/Makefile
> > +++ b/tools/Makefile
> > @@ -231,6 +231,10 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs
> >  hostprogs-$(CONFIG_ASN1_COMPILER)	+= asn1_compiler
> >  HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
> >
> > +ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),)
> > +HOSTLDLIBS_mkeficapsule += \
> > +	$(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto")
> 
> I don't expect any user wants to install two tool versions in parallel.
> 
> The tool should always be able to add a signature.
> Adding a signature must be optional.

It seems to me that those two statements mutually contradict.
Or do you intend to say that we should have a separate kconfig
option to enable/disable signing feature in mkeficapsule?

If so, I can agree.

In either way, we should have an option to turn on/off this functionality
as not all users use signed capsules.

> > +endif
> >  mkeficapsule-objs	:= mkeficapsule.o $(LIBFDT_OBJS)
> >  hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
> >
> > diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
> > index de0a62898886..34ff1bdd82eb 100644
> > --- a/tools/mkeficapsule.c
> > +++ b/tools/mkeficapsule.c
> > @@ -18,7 +18,17 @@
> >  #include <sys/stat.h>
> >  #include <sys/types.h>
> >
> > -#include "fdt_host.h"
> > +#include <linux/kconfig.h>
> > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> 
> see above
> 
> > +#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
> > +
> > +#include <linux/libfdt.h>
> >
> >  typedef __u8 u8;
> >  typedef __u16 u16;
> > @@ -46,6 +56,13 @@ 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;
> > +
> > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> 
> see above
> 
> > +static const char *opts_short = "f:r:i:I:v:D:K:P:C:m:dOh";
> > +#else
> > +static const char *opts_short = "f:r:i:I:v:D:K:Oh";
> > +#endif
> >
> >  static struct option options[] = {
> >  	{"fit", required_argument, NULL, 'f'},
> > @@ -54,6 +71,12 @@ static struct option options[] = {
> >  	{"instance", required_argument, NULL, 'I'},
> >  	{"dtb", required_argument, NULL, 'D'},
> >  	{"public key", required_argument, NULL, 'K'},
> > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> > +	{"private-key", required_argument, NULL, 'P'},
> > +	{"certificate", required_argument, NULL, 'C'},
> > +	{"monotonic-count", required_argument, NULL, 'm'},
> 
> These options should not be required.

I don't get you. What do you mean?

> > +	{"dump-sig", no_argument, NULL, 'd'},
> > +#endif
> >  	{"overlay", no_argument, NULL, 'O'},
> >  	{"help", no_argument, NULL, 'h'},
> >  	{NULL, 0, NULL, 0},
> > @@ -70,6 +93,12 @@ static void print_usage(void)
> >  	       "\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"
> > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> 
> see above
> 
> > +	       "\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-O, --overlay               the dtb file is an overlay\n"
> >  	       "\t-h, --help                  print a help message\n",
> >  	       tool_name);
> > @@ -249,12 +278,167 @@ err:
> >  	return ret;
> >  }
> >
> > +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;
> > +
> > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> 
> see above
> 
> > +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);
> 
> Please, you use fprintf(stderr,) for error messages.
> 
> > +		ERR_print_errors_fp(stderr);
> > +	}
> > +
> > +	return key;
> > +}
> > +
> > +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);
> 
> fprintf(stderr,)
> 
> > +		ERR_print_errors_fp(stderr);
> > +	}
> > +
> > +	return cert;
> > +}
> > +
> > +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 */
> 
> PKCS7_NOATTR is a value without any documentation in the code.

Nak.
Those macros are part of openssl library. See openssl/pkcs7.h.

> Please, replace variable names by a long text describing what it missing.
> 
> > +	flags = PKCS7_BINARY | PKCS7_DETACHED;
> 
> Those constants lack documentation in the code.

Nak again.

> > +	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;
> > +}
> > +
> > +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;
> > +}
> > +#endif
> > +
> >  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;
> > @@ -266,6 +450,7 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> >  	printf("\tbin: %s\n\ttype: %pUl\n", bin, guid);
> >  	printf("\tindex: %ld\n\tinstance: %ld\n", index, instance);
> >  #endif
> > +	auth_context.sig_size = 0;
> >
> >  	g = fopen(bin, "r");
> >  	if (!g) {
> > @@ -281,11 +466,36 @@ 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 IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> 
> see above
> 
> > +	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;
> > +		}
> > +	}
> > +#endif
> > +
> >  	header.capsule_guid = efi_guid_fm_capsule;
> >  	header.header_size = sizeof(header);
> >  	/* TODO: The current implementation ignores flags */
> > @@ -294,11 +504,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;
> > @@ -307,13 +526,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;
> > @@ -323,34 +542,61 @@ 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 IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> 
> We don't want to use #if if avoidable.

For this specific chunk of code, we can remove #ifdef,
but we should not remove #ifdef elsewhere.

-Takahiro Akashi


> > +	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;
> > +		}
> >  	}
> > +#endif
> > +
> >  	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);
> > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> > +	if (auth_context.sig_size)
> > +		OPENSSL_free(auth_context.sig_data);
> > +#endif
> >
> >  	return 0;
> >
> > -err_3:
> > +err_4:
> >  	fclose(f);
> > +err_3:
> > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> > +	if (auth_context.sig_size)
> > +		OPENSSL_free(auth_context.sig_data);
> > +#endif
> >  err_2:
> >  	free(data);
> >  err_1:
> > @@ -359,10 +605,6 @@ err_1:
> >  	return -1;
> >  }
> >
> > -/*
> > - * Usage:
> > - *   $ mkeficapsule -f <firmware binary> <output file>
> > - */
> >  int main(int argc, char **argv)
> >  {
> >  	char *file;
> > @@ -370,6 +612,8 @@ int main(int argc, char **argv)
> >  	char *dtb_file;
> >  	efi_guid_t *guid;
> >  	unsigned long index, instance;
> > +	uint64_t mcount;
> > +	char *privkey_file, *cert_file;
> >  	int c, idx;
> >  	int ret;
> >  	bool overlay = false;
> > @@ -380,8 +624,12 @@ int main(int argc, char **argv)
> >  	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:D:K:Oh", options, &idx);
> > +		c = getopt_long(argc, argv, opts_short, options, &idx);
> >  		if (c == -1)
> >  			break;
> >
> > @@ -422,6 +670,28 @@ int main(int argc, char **argv)
> >  			}
> >  			dtb_file = optarg;
> >  			break;
> > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> 
> see above
> 
> Best regards
> 
> Heinrich
> 
> > +		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
> >  		case 'O':
> >  			overlay = true;
> >  			break;
> > @@ -431,8 +701,12 @@ int main(int argc, char **argv)
> >  		}
> >  	}
> >
> > -	/* need a fit image file or raw image file */
> > -	if (!file && !pkey_file && !dtb_file) {
> > +	/* check necessary parameters */
> > +	if ((file && (!(optind < argc) ||
> > +		      (privkey_file && !cert_file) ||
> > +		      (!privkey_file && cert_file))) ||
> > +	    ((pkey_file && !dtb_file) ||
> > +	     (!pkey_file && dtb_file))) {
> >  		print_usage();
> >  		exit(EXIT_FAILURE);
> >  	}
> > @@ -442,12 +716,12 @@ int main(int argc, char **argv)
> >  		if (ret == -1) {
> >  			printf("Adding public key to the dtb failed\n");
> >  			exit(EXIT_FAILURE);
> > -		} else {
> > -			exit(EXIT_SUCCESS);
> >  		}
> >  	}
> >
> > -	if (create_fwbin(argv[optind], file, guid, index, instance)
> > +	if (optind < argc &&
> > +	    create_fwbin(argv[optind], file, guid, index, instance,
> > +			 mcount, privkey_file, cert_file)
> >  			< 0) {
> >  		printf("Creating firmware capsule failed\n");
> >  		exit(EXIT_FAILURE);
> >
> 

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

* [PATCH 1/4] tools: mkeficapsule: add firmwware image signing
  2021-05-13  3:08     ` AKASHI Takahiro
@ 2021-05-13  4:22       ` Heinrich Schuchardt
  2021-05-13  5:00         ` AKASHI Takahiro
  2021-05-13  5:12         ` Masami Hiramatsu
  0 siblings, 2 replies; 55+ messages in thread
From: Heinrich Schuchardt @ 2021-05-13  4:22 UTC (permalink / raw)
  To: u-boot

On 5/13/21 5:08 AM, AKASHI Takahiro wrote:
> On Wed, May 12, 2021 at 10:56:41AM +0200, Heinrich Schuchardt wrote:
>> On 12.05.21 06:57, AKASHI Takahiro wrote:
>>> With this enhancement, mkeficapsule will be able to create a capsule
>>> file with a signature which will be verified later by FMP's SetImage().
>>>
>>> We will have to specify addtional command parameters:
>>>    -monotonic-cout <count> : monotonic count
>>>    -private-key <private key file> : private key file
>>>    -certificate <certificate file> : certificate file
>>> Only when those parameters are given, a signature will be added
>>> to a capsule file.
>>>
>>> Users are expected to maintain the monotonic count for each firmware
>>> image.
>>>
>>> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
>>> ---
>>>   tools/Makefile       |   4 +
>>>   tools/mkeficapsule.c | 324 +++++++++++++++++++++++++++++++++++++++----
>>>   2 files changed, 303 insertions(+), 25 deletions(-)
>>>
>>> diff --git a/tools/Makefile b/tools/Makefile
>>> index d020c55d6644..02eae0286e20 100644
>>> --- a/tools/Makefile
>>> +++ b/tools/Makefile
>>> @@ -231,6 +231,10 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs
>>>   hostprogs-$(CONFIG_ASN1_COMPILER)	+= asn1_compiler
>>>   HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
>>>
>>> +ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),)
>>> +HOSTLDLIBS_mkeficapsule += \
>>> +	$(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto")
>>
>> I don't expect any user wants to install two tool versions in parallel.
>>
>> The tool should always be able to add a signature.
>> Adding a signature must be optional.
>
> It seems to me that those two statements mutually contradict.
> Or do you intend to say that we should have a separate kconfig
> option to enable/disable signing feature in mkeficapsule?
>
> If so, I can agree.
>
> In either way, we should have an option to turn on/off this functionality
> as not all users use signed capsules.

I want to have a single binary to distribute with Linux distros (e.g.
Debian/Ubuntu package u-boot-tools).

This should allow both

- create signed capsules
- create unsigned capsules

The user shall select signing via command line parameters.

Support for signing via the tool shall not depend on board Kconfig
parameters.

Best regards

Heinrich

>
>>> +endif
>>>   mkeficapsule-objs	:= mkeficapsule.o $(LIBFDT_OBJS)
>>>   hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
>>>
>>> diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
>>> index de0a62898886..34ff1bdd82eb 100644
>>> --- a/tools/mkeficapsule.c
>>> +++ b/tools/mkeficapsule.c
>>> @@ -18,7 +18,17 @@
>>>   #include <sys/stat.h>
>>>   #include <sys/types.h>
>>>
>>> -#include "fdt_host.h"
>>> +#include <linux/kconfig.h>
>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>>
>> see above
>>
>>> +#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
>>> +
>>> +#include <linux/libfdt.h>
>>>
>>>   typedef __u8 u8;
>>>   typedef __u16 u16;
>>> @@ -46,6 +56,13 @@ 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;
>>> +
>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>>
>> see above
>>
>>> +static const char *opts_short = "f:r:i:I:v:D:K:P:C:m:dOh";
>>> +#else
>>> +static const char *opts_short = "f:r:i:I:v:D:K:Oh";
>>> +#endif
>>>
>>>   static struct option options[] = {
>>>   	{"fit", required_argument, NULL, 'f'},
>>> @@ -54,6 +71,12 @@ static struct option options[] = {
>>>   	{"instance", required_argument, NULL, 'I'},
>>>   	{"dtb", required_argument, NULL, 'D'},
>>>   	{"public key", required_argument, NULL, 'K'},
>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>>> +	{"private-key", required_argument, NULL, 'P'},
>>> +	{"certificate", required_argument, NULL, 'C'},
>>> +	{"monotonic-count", required_argument, NULL, 'm'},
>>
>> These options should not be required.
>
> I don't get you. What do you mean?
>
>>> +	{"dump-sig", no_argument, NULL, 'd'},
>>> +#endif
>>>   	{"overlay", no_argument, NULL, 'O'},
>>>   	{"help", no_argument, NULL, 'h'},
>>>   	{NULL, 0, NULL, 0},
>>> @@ -70,6 +93,12 @@ static void print_usage(void)
>>>   	       "\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"
>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>>
>> see above
>>
>>> +	       "\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-O, --overlay               the dtb file is an overlay\n"
>>>   	       "\t-h, --help                  print a help message\n",
>>>   	       tool_name);
>>> @@ -249,12 +278,167 @@ err:
>>>   	return ret;
>>>   }
>>>
>>> +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;
>>> +
>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>>
>> see above
>>
>>> +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);
>>
>> Please, you use fprintf(stderr,) for error messages.
>>
>>> +		ERR_print_errors_fp(stderr);
>>> +	}
>>> +
>>> +	return key;
>>> +}
>>> +
>>> +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);
>>
>> fprintf(stderr,)
>>
>>> +		ERR_print_errors_fp(stderr);
>>> +	}
>>> +
>>> +	return cert;
>>> +}
>>> +
>>> +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 */
>>
>> PKCS7_NOATTR is a value without any documentation in the code.
>
> Nak.
> Those macros are part of openssl library. See openssl/pkcs7.h.
>
>> Please, replace variable names by a long text describing what it missing.
>>
>>> +	flags = PKCS7_BINARY | PKCS7_DETACHED;
>>
>> Those constants lack documentation in the code.
>
> Nak again.
>
>>> +	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;
>>> +}
>>> +
>>> +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;
>>> +}
>>> +#endif
>>> +
>>>   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;
>>> @@ -266,6 +450,7 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
>>>   	printf("\tbin: %s\n\ttype: %pUl\n", bin, guid);
>>>   	printf("\tindex: %ld\n\tinstance: %ld\n", index, instance);
>>>   #endif
>>> +	auth_context.sig_size = 0;
>>>
>>>   	g = fopen(bin, "r");
>>>   	if (!g) {
>>> @@ -281,11 +466,36 @@ 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 IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>>
>> see above
>>
>>> +	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;
>>> +		}
>>> +	}
>>> +#endif
>>> +
>>>   	header.capsule_guid = efi_guid_fm_capsule;
>>>   	header.header_size = sizeof(header);
>>>   	/* TODO: The current implementation ignores flags */
>>> @@ -294,11 +504,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;
>>> @@ -307,13 +526,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;
>>> @@ -323,34 +542,61 @@ 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 IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>>
>> We don't want to use #if if avoidable.
>
> For this specific chunk of code, we can remove #ifdef,
> but we should not remove #ifdef elsewhere.
>
> -Takahiro Akashi
>
>
>>> +	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;
>>> +		}
>>>   	}
>>> +#endif
>>> +
>>>   	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);
>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>>> +	if (auth_context.sig_size)
>>> +		OPENSSL_free(auth_context.sig_data);
>>> +#endif
>>>
>>>   	return 0;
>>>
>>> -err_3:
>>> +err_4:
>>>   	fclose(f);
>>> +err_3:
>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>>> +	if (auth_context.sig_size)
>>> +		OPENSSL_free(auth_context.sig_data);
>>> +#endif
>>>   err_2:
>>>   	free(data);
>>>   err_1:
>>> @@ -359,10 +605,6 @@ err_1:
>>>   	return -1;
>>>   }
>>>
>>> -/*
>>> - * Usage:
>>> - *   $ mkeficapsule -f <firmware binary> <output file>
>>> - */
>>>   int main(int argc, char **argv)
>>>   {
>>>   	char *file;
>>> @@ -370,6 +612,8 @@ int main(int argc, char **argv)
>>>   	char *dtb_file;
>>>   	efi_guid_t *guid;
>>>   	unsigned long index, instance;
>>> +	uint64_t mcount;
>>> +	char *privkey_file, *cert_file;
>>>   	int c, idx;
>>>   	int ret;
>>>   	bool overlay = false;
>>> @@ -380,8 +624,12 @@ int main(int argc, char **argv)
>>>   	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:D:K:Oh", options, &idx);
>>> +		c = getopt_long(argc, argv, opts_short, options, &idx);
>>>   		if (c == -1)
>>>   			break;
>>>
>>> @@ -422,6 +670,28 @@ int main(int argc, char **argv)
>>>   			}
>>>   			dtb_file = optarg;
>>>   			break;
>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>>
>> see above
>>
>> Best regards
>>
>> Heinrich
>>
>>> +		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
>>>   		case 'O':
>>>   			overlay = true;
>>>   			break;
>>> @@ -431,8 +701,12 @@ int main(int argc, char **argv)
>>>   		}
>>>   	}
>>>
>>> -	/* need a fit image file or raw image file */
>>> -	if (!file && !pkey_file && !dtb_file) {
>>> +	/* check necessary parameters */
>>> +	if ((file && (!(optind < argc) ||
>>> +		      (privkey_file && !cert_file) ||
>>> +		      (!privkey_file && cert_file))) ||
>>> +	    ((pkey_file && !dtb_file) ||
>>> +	     (!pkey_file && dtb_file))) {
>>>   		print_usage();
>>>   		exit(EXIT_FAILURE);
>>>   	}
>>> @@ -442,12 +716,12 @@ int main(int argc, char **argv)
>>>   		if (ret == -1) {
>>>   			printf("Adding public key to the dtb failed\n");
>>>   			exit(EXIT_FAILURE);
>>> -		} else {
>>> -			exit(EXIT_SUCCESS);
>>>   		}
>>>   	}
>>>
>>> -	if (create_fwbin(argv[optind], file, guid, index, instance)
>>> +	if (optind < argc &&
>>> +	    create_fwbin(argv[optind], file, guid, index, instance,
>>> +			 mcount, privkey_file, cert_file)
>>>   			< 0) {
>>>   		printf("Creating firmware capsule failed\n");
>>>   		exit(EXIT_FAILURE);
>>>
>>

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

* [PATCH 1/4] tools: mkeficapsule: add firmwware image signing
  2021-05-13  4:22       ` Heinrich Schuchardt
@ 2021-05-13  5:00         ` AKASHI Takahiro
  2021-05-13  5:35           ` Heinrich Schuchardt
  2021-05-13  5:12         ` Masami Hiramatsu
  1 sibling, 1 reply; 55+ messages in thread
From: AKASHI Takahiro @ 2021-05-13  5:00 UTC (permalink / raw)
  To: u-boot

On Thu, May 13, 2021 at 06:22:39AM +0200, Heinrich Schuchardt wrote:
> On 5/13/21 5:08 AM, AKASHI Takahiro wrote:
> > On Wed, May 12, 2021 at 10:56:41AM +0200, Heinrich Schuchardt wrote:
> > > On 12.05.21 06:57, AKASHI Takahiro wrote:
> > > > With this enhancement, mkeficapsule will be able to create a capsule
> > > > file with a signature which will be verified later by FMP's SetImage().
> > > > 
> > > > We will have to specify addtional command parameters:
> > > >    -monotonic-cout <count> : monotonic count
> > > >    -private-key <private key file> : private key file
> > > >    -certificate <certificate file> : certificate file
> > > > Only when those parameters are given, a signature will be added
> > > > to a capsule file.
> > > > 
> > > > Users are expected to maintain the monotonic count for each firmware
> > > > image.
> > > > 
> > > > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > > > ---
> > > >   tools/Makefile       |   4 +
> > > >   tools/mkeficapsule.c | 324 +++++++++++++++++++++++++++++++++++++++----
> > > >   2 files changed, 303 insertions(+), 25 deletions(-)
> > > > 
> > > > diff --git a/tools/Makefile b/tools/Makefile
> > > > index d020c55d6644..02eae0286e20 100644
> > > > --- a/tools/Makefile
> > > > +++ b/tools/Makefile
> > > > @@ -231,6 +231,10 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs
> > > >   hostprogs-$(CONFIG_ASN1_COMPILER)	+= asn1_compiler
> > > >   HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
> > > > 
> > > > +ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),)
> > > > +HOSTLDLIBS_mkeficapsule += \
> > > > +	$(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto")
> > > 
> > > I don't expect any user wants to install two tool versions in parallel.
> > > 
> > > The tool should always be able to add a signature.
> > > Adding a signature must be optional.
> > 
> > It seems to me that those two statements mutually contradict.
> > Or do you intend to say that we should have a separate kconfig
> > option to enable/disable signing feature in mkeficapsule?
> > 
> > If so, I can agree.
> > 
> > In either way, we should have an option to turn on/off this functionality
> > as not all users use signed capsules.
> 
> I want to have a single binary to distribute with Linux distros (e.g.
> Debian/Ubuntu package u-boot-tools).
> 
> This should allow both
> 
> - create signed capsules
> - create unsigned capsules
> 
> The user shall select signing via command line parameters.
> 
> Support for signing via the tool shall not depend on board Kconfig
> parameters.

That is why I proposed that we create a new kconfig option.

Please note that enabling signing feature in mkeficapsule
requires openssl library, and we should not enforce users who don't
need this feature to install an unnecessary package.

-Takahiro Akashi

> Best regards
> 
> Heinrich
> 
> > 
> > > > +endif
> > > >   mkeficapsule-objs	:= mkeficapsule.o $(LIBFDT_OBJS)
> > > >   hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
> > > > 
> > > > diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
> > > > index de0a62898886..34ff1bdd82eb 100644
> > > > --- a/tools/mkeficapsule.c
> > > > +++ b/tools/mkeficapsule.c
> > > > @@ -18,7 +18,17 @@
> > > >   #include <sys/stat.h>
> > > >   #include <sys/types.h>
> > > > 
> > > > -#include "fdt_host.h"
> > > > +#include <linux/kconfig.h>
> > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> > > 
> > > see above
> > > 
> > > > +#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
> > > > +
> > > > +#include <linux/libfdt.h>
> > > > 
> > > >   typedef __u8 u8;
> > > >   typedef __u16 u16;
> > > > @@ -46,6 +56,13 @@ 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;
> > > > +
> > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> > > 
> > > see above
> > > 
> > > > +static const char *opts_short = "f:r:i:I:v:D:K:P:C:m:dOh";
> > > > +#else
> > > > +static const char *opts_short = "f:r:i:I:v:D:K:Oh";
> > > > +#endif
> > > > 
> > > >   static struct option options[] = {
> > > >   	{"fit", required_argument, NULL, 'f'},
> > > > @@ -54,6 +71,12 @@ static struct option options[] = {
> > > >   	{"instance", required_argument, NULL, 'I'},
> > > >   	{"dtb", required_argument, NULL, 'D'},
> > > >   	{"public key", required_argument, NULL, 'K'},
> > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> > > > +	{"private-key", required_argument, NULL, 'P'},
> > > > +	{"certificate", required_argument, NULL, 'C'},
> > > > +	{"monotonic-count", required_argument, NULL, 'm'},
> > > 
> > > These options should not be required.
> > 
> > I don't get you. What do you mean?
> > 
> > > > +	{"dump-sig", no_argument, NULL, 'd'},
> > > > +#endif
> > > >   	{"overlay", no_argument, NULL, 'O'},
> > > >   	{"help", no_argument, NULL, 'h'},
> > > >   	{NULL, 0, NULL, 0},
> > > > @@ -70,6 +93,12 @@ static void print_usage(void)
> > > >   	       "\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"
> > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> > > 
> > > see above
> > > 
> > > > +	       "\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-O, --overlay               the dtb file is an overlay\n"
> > > >   	       "\t-h, --help                  print a help message\n",
> > > >   	       tool_name);
> > > > @@ -249,12 +278,167 @@ err:
> > > >   	return ret;
> > > >   }
> > > > 
> > > > +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;
> > > > +
> > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> > > 
> > > see above
> > > 
> > > > +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);
> > > 
> > > Please, you use fprintf(stderr,) for error messages.
> > > 
> > > > +		ERR_print_errors_fp(stderr);
> > > > +	}
> > > > +
> > > > +	return key;
> > > > +}
> > > > +
> > > > +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);
> > > 
> > > fprintf(stderr,)
> > > 
> > > > +		ERR_print_errors_fp(stderr);
> > > > +	}
> > > > +
> > > > +	return cert;
> > > > +}
> > > > +
> > > > +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 */
> > > 
> > > PKCS7_NOATTR is a value without any documentation in the code.
> > 
> > Nak.
> > Those macros are part of openssl library. See openssl/pkcs7.h.
> > 
> > > Please, replace variable names by a long text describing what it missing.
> > > 
> > > > +	flags = PKCS7_BINARY | PKCS7_DETACHED;
> > > 
> > > Those constants lack documentation in the code.
> > 
> > Nak again.
> > 
> > > > +	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;
> > > > +}
> > > > +
> > > > +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;
> > > > +}
> > > > +#endif
> > > > +
> > > >   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;
> > > > @@ -266,6 +450,7 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > >   	printf("\tbin: %s\n\ttype: %pUl\n", bin, guid);
> > > >   	printf("\tindex: %ld\n\tinstance: %ld\n", index, instance);
> > > >   #endif
> > > > +	auth_context.sig_size = 0;
> > > > 
> > > >   	g = fopen(bin, "r");
> > > >   	if (!g) {
> > > > @@ -281,11 +466,36 @@ 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 IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> > > 
> > > see above
> > > 
> > > > +	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;
> > > > +		}
> > > > +	}
> > > > +#endif
> > > > +
> > > >   	header.capsule_guid = efi_guid_fm_capsule;
> > > >   	header.header_size = sizeof(header);
> > > >   	/* TODO: The current implementation ignores flags */
> > > > @@ -294,11 +504,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;
> > > > @@ -307,13 +526,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;
> > > > @@ -323,34 +542,61 @@ 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 IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> > > 
> > > We don't want to use #if if avoidable.
> > 
> > For this specific chunk of code, we can remove #ifdef,
> > but we should not remove #ifdef elsewhere.
> > 
> > -Takahiro Akashi
> > 
> > 
> > > > +	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;
> > > > +		}
> > > >   	}
> > > > +#endif
> > > > +
> > > >   	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);
> > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> > > > +	if (auth_context.sig_size)
> > > > +		OPENSSL_free(auth_context.sig_data);
> > > > +#endif
> > > > 
> > > >   	return 0;
> > > > 
> > > > -err_3:
> > > > +err_4:
> > > >   	fclose(f);
> > > > +err_3:
> > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> > > > +	if (auth_context.sig_size)
> > > > +		OPENSSL_free(auth_context.sig_data);
> > > > +#endif
> > > >   err_2:
> > > >   	free(data);
> > > >   err_1:
> > > > @@ -359,10 +605,6 @@ err_1:
> > > >   	return -1;
> > > >   }
> > > > 
> > > > -/*
> > > > - * Usage:
> > > > - *   $ mkeficapsule -f <firmware binary> <output file>
> > > > - */
> > > >   int main(int argc, char **argv)
> > > >   {
> > > >   	char *file;
> > > > @@ -370,6 +612,8 @@ int main(int argc, char **argv)
> > > >   	char *dtb_file;
> > > >   	efi_guid_t *guid;
> > > >   	unsigned long index, instance;
> > > > +	uint64_t mcount;
> > > > +	char *privkey_file, *cert_file;
> > > >   	int c, idx;
> > > >   	int ret;
> > > >   	bool overlay = false;
> > > > @@ -380,8 +624,12 @@ int main(int argc, char **argv)
> > > >   	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:D:K:Oh", options, &idx);
> > > > +		c = getopt_long(argc, argv, opts_short, options, &idx);
> > > >   		if (c == -1)
> > > >   			break;
> > > > 
> > > > @@ -422,6 +670,28 @@ int main(int argc, char **argv)
> > > >   			}
> > > >   			dtb_file = optarg;
> > > >   			break;
> > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> > > 
> > > see above
> > > 
> > > Best regards
> > > 
> > > Heinrich
> > > 
> > > > +		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
> > > >   		case 'O':
> > > >   			overlay = true;
> > > >   			break;
> > > > @@ -431,8 +701,12 @@ int main(int argc, char **argv)
> > > >   		}
> > > >   	}
> > > > 
> > > > -	/* need a fit image file or raw image file */
> > > > -	if (!file && !pkey_file && !dtb_file) {
> > > > +	/* check necessary parameters */
> > > > +	if ((file && (!(optind < argc) ||
> > > > +		      (privkey_file && !cert_file) ||
> > > > +		      (!privkey_file && cert_file))) ||
> > > > +	    ((pkey_file && !dtb_file) ||
> > > > +	     (!pkey_file && dtb_file))) {
> > > >   		print_usage();
> > > >   		exit(EXIT_FAILURE);
> > > >   	}
> > > > @@ -442,12 +716,12 @@ int main(int argc, char **argv)
> > > >   		if (ret == -1) {
> > > >   			printf("Adding public key to the dtb failed\n");
> > > >   			exit(EXIT_FAILURE);
> > > > -		} else {
> > > > -			exit(EXIT_SUCCESS);
> > > >   		}
> > > >   	}
> > > > 
> > > > -	if (create_fwbin(argv[optind], file, guid, index, instance)
> > > > +	if (optind < argc &&
> > > > +	    create_fwbin(argv[optind], file, guid, index, instance,
> > > > +			 mcount, privkey_file, cert_file)
> > > >   			< 0) {
> > > >   		printf("Creating firmware capsule failed\n");
> > > >   		exit(EXIT_FAILURE);
> > > > 
> > > 
> 

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

* [PATCH 2/4] tools: mkeficapsule: remove device-tree related operation
  2021-05-13  2:33           ` AKASHI Takahiro
@ 2021-05-13  5:08             ` Heinrich Schuchardt
  2021-05-13  7:13               ` AKASHI Takahiro
  0 siblings, 1 reply; 55+ messages in thread
From: Heinrich Schuchardt @ 2021-05-13  5:08 UTC (permalink / raw)
  To: u-boot

On 5/13/21 4:33 AM, AKASHI Takahiro wrote:
> On Wed, May 12, 2021 at 12:01:32PM +0200, Heinrich Schuchardt wrote:
>> On 12.05.21 10:01, Ilias Apalodimas wrote:
>>> On Wed, May 12, 2021 at 04:49:02PM +0900, Masami Hiramatsu wrote:
>>>> Hi Ilias,
>>>>
>>>> 2021?5?12?(?) 16:21 Ilias Apalodimas <ilias.apalodimas@linaro.org>:
>>>>>
>>>>> Akashi-san,
>>>>>
>>>>> On Wed, May 12, 2021 at 01:57:51PM +0900, AKASHI Takahiro wrote:
>>>>>> As we discussed, "-K" and "-D" options have nothing to do with
>>>>>> creating a capsule file. The same result can be obtained by
>>>>>> using standard commands like:
>>>>>>    === signature.dts ===
>>>>>>    /dts-v1/;
>>>>>>    /plugin/;
>>>>>>
>>>>>>    &{/} {
>>>>>>          signature {
>>>>>>                  capsule-key = /incbin/("SIGNER.esl");
>>>>>>          };
>>>>>>    };
>>>>>>    ===
>>>>>>    $ dtc -@ -I dts -O dtb -o signature.dtbo signature.dts
>>>>>>    $ fdtoverlay -i test.dtb -o test_sig.dtb -v signature.dtbo
>>>>>>
>>>>>> So just remove this feature.
>>>>>> (Effectively revert the commit 322c813f4bec ("mkeficapsule: Add support
>>>>>> for embedding public key in a dtb").)
>>>>>>
>>>>>> The same feature is implemented by a shell script (tools/fdtsig.sh).
>>>>>
>>>>>
>>>>> The only reason I can see to keep this, is if mkeficapsule gets included
>>>>> intro distro packages in the future.  That would make end users life a bit
>>>>> easier, since they would need a single binary to create the whole
>>>>> CapsuleUpdate sequence.
>>>>
>>>> Hmm, I think it is better to write a manpage of mkeficapsule which
>>>> also describes
>>>> how to embed the key into dtb as in the above example if it is so short.
>>>> Or, distros can package the above shell script with mkeficapsule.
>>>>
>>>> Embedding a key and signing a capsule are different operations but
>>>> using the same tool may confuse users (at least me).
>>>
>>> Sure fair enough.  I am merely pointing out we need a way to explain all of
>>> those to users.
>>
>> This is currently our only documentation:
>>
>> https://u-boot.readthedocs.io/en/latest/board/emulation/qemu_capsule_update.html?highlight=mkeficapsule
>
> As I mentioned several times (and TODO in the cover letter),
> this text must be reviewed, revised and generalized
> as a platform-independent document.
> It contains a couple of errors.
>
>> For mkimage we have a man-page ./doc/mkimage.1 that is packaged with
>> Debians u-boot-tools package. Please, provide a similar man-page as
>> ./doc/mkeficapsule.1.
>
> So after all do you agree to removing "-K/-D"?

I see no need to replicate in U-Boot what is already in the device tree
compiler package.

In the current workflow the fdt command is used to load the public key.
This is insecure and not usable for production.

The public key used to verify the capsule must be built into the U-Boot
binary. This will supplant the -K and -D options.

Best regards

Heinrich

> Otherwise, I cannot complete the man page.
>
> -Takahiro Akashi
>
>> Best regards
>>
>> Heinrich

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

* [PATCH 1/4] tools: mkeficapsule: add firmwware image signing
  2021-05-13  4:22       ` Heinrich Schuchardt
  2021-05-13  5:00         ` AKASHI Takahiro
@ 2021-05-13  5:12         ` Masami Hiramatsu
  2021-05-13  5:50           ` Heinrich Schuchardt
  1 sibling, 1 reply; 55+ messages in thread
From: Masami Hiramatsu @ 2021-05-13  5:12 UTC (permalink / raw)
  To: u-boot

Hi Heinrich,

2021?5?13?(?) 13:22 Heinrich Schuchardt <xypron.glpk@gmx.de>:
>
> On 5/13/21 5:08 AM, AKASHI Takahiro wrote:
> > On Wed, May 12, 2021 at 10:56:41AM +0200, Heinrich Schuchardt wrote:
> >> On 12.05.21 06:57, AKASHI Takahiro wrote:
> >>> With this enhancement, mkeficapsule will be able to create a capsule
> >>> file with a signature which will be verified later by FMP's SetImage().
> >>>
> >>> We will have to specify addtional command parameters:
> >>>    -monotonic-cout <count> : monotonic count
> >>>    -private-key <private key file> : private key file
> >>>    -certificate <certificate file> : certificate file
> >>> Only when those parameters are given, a signature will be added
> >>> to a capsule file.
> >>>
> >>> Users are expected to maintain the monotonic count for each firmware
> >>> image.
> >>>
> >>> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> >>> ---
> >>>   tools/Makefile       |   4 +
> >>>   tools/mkeficapsule.c | 324 +++++++++++++++++++++++++++++++++++++++----
> >>>   2 files changed, 303 insertions(+), 25 deletions(-)
> >>>
> >>> diff --git a/tools/Makefile b/tools/Makefile
> >>> index d020c55d6644..02eae0286e20 100644
> >>> --- a/tools/Makefile
> >>> +++ b/tools/Makefile
> >>> @@ -231,6 +231,10 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs
> >>>   hostprogs-$(CONFIG_ASN1_COMPILER) += asn1_compiler
> >>>   HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
> >>>
> >>> +ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),)
> >>> +HOSTLDLIBS_mkeficapsule += \
> >>> +   $(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto")
> >>
> >> I don't expect any user wants to install two tool versions in parallel.
> >>
> >> The tool should always be able to add a signature.
> >> Adding a signature must be optional.
> >
> > It seems to me that those two statements mutually contradict.
> > Or do you intend to say that we should have a separate kconfig
> > option to enable/disable signing feature in mkeficapsule?
> >
> > If so, I can agree.
> >
> > In either way, we should have an option to turn on/off this functionality
> > as not all users use signed capsules.
>
> I want to have a single binary to distribute with Linux distros (e.g.
> Debian/Ubuntu package u-boot-tools).

I couldn't catch your point. If so, the distros can build u-boot with
CONFIG_EFI_CAPSULE_AUTHENTICATE=y...

BTW, IMHO, if u-boot.bin can not find the ESL in the device tree,
it should skip authentication too.

Then, user can choose whether enabling capsule authentication or not
by embedding ESL into their devicetree.

Thank you

>
> This should allow both
>
> - create signed capsules
> - create unsigned capsules
>
> The user shall select signing via command line parameters.
>
> Support for signing via the tool shall not depend on board Kconfig
> parameters.
>
> Best regards
>
> Heinrich
>
> >
> >>> +endif
> >>>   mkeficapsule-objs := mkeficapsule.o $(LIBFDT_OBJS)
> >>>   hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
> >>>
> >>> diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
> >>> index de0a62898886..34ff1bdd82eb 100644
> >>> --- a/tools/mkeficapsule.c
> >>> +++ b/tools/mkeficapsule.c
> >>> @@ -18,7 +18,17 @@
> >>>   #include <sys/stat.h>
> >>>   #include <sys/types.h>
> >>>
> >>> -#include "fdt_host.h"
> >>> +#include <linux/kconfig.h>
> >>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> >>
> >> see above
> >>
> >>> +#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
> >>> +
> >>> +#include <linux/libfdt.h>
> >>>
> >>>   typedef __u8 u8;
> >>>   typedef __u16 u16;
> >>> @@ -46,6 +56,13 @@ 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;
> >>> +
> >>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> >>
> >> see above
> >>
> >>> +static const char *opts_short = "f:r:i:I:v:D:K:P:C:m:dOh";
> >>> +#else
> >>> +static const char *opts_short = "f:r:i:I:v:D:K:Oh";
> >>> +#endif
> >>>
> >>>   static struct option options[] = {
> >>>     {"fit", required_argument, NULL, 'f'},
> >>> @@ -54,6 +71,12 @@ static struct option options[] = {
> >>>     {"instance", required_argument, NULL, 'I'},
> >>>     {"dtb", required_argument, NULL, 'D'},
> >>>     {"public key", required_argument, NULL, 'K'},
> >>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> >>> +   {"private-key", required_argument, NULL, 'P'},
> >>> +   {"certificate", required_argument, NULL, 'C'},
> >>> +   {"monotonic-count", required_argument, NULL, 'm'},
> >>
> >> These options should not be required.
> >
> > I don't get you. What do you mean?
> >
> >>> +   {"dump-sig", no_argument, NULL, 'd'},
> >>> +#endif
> >>>     {"overlay", no_argument, NULL, 'O'},
> >>>     {"help", no_argument, NULL, 'h'},
> >>>     {NULL, 0, NULL, 0},
> >>> @@ -70,6 +93,12 @@ static void print_usage(void)
> >>>            "\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"
> >>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> >>
> >> see above
> >>
> >>> +          "\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-O, --overlay               the dtb file is an overlay\n"
> >>>            "\t-h, --help                  print a help message\n",
> >>>            tool_name);
> >>> @@ -249,12 +278,167 @@ err:
> >>>     return ret;
> >>>   }
> >>>
> >>> +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;
> >>> +
> >>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> >>
> >> see above
> >>
> >>> +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);
> >>
> >> Please, you use fprintf(stderr,) for error messages.
> >>
> >>> +           ERR_print_errors_fp(stderr);
> >>> +   }
> >>> +
> >>> +   return key;
> >>> +}
> >>> +
> >>> +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);
> >>
> >> fprintf(stderr,)
> >>
> >>> +           ERR_print_errors_fp(stderr);
> >>> +   }
> >>> +
> >>> +   return cert;
> >>> +}
> >>> +
> >>> +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 */
> >>
> >> PKCS7_NOATTR is a value without any documentation in the code.
> >
> > Nak.
> > Those macros are part of openssl library. See openssl/pkcs7.h.
> >
> >> Please, replace variable names by a long text describing what it missing.
> >>
> >>> +   flags = PKCS7_BINARY | PKCS7_DETACHED;
> >>
> >> Those constants lack documentation in the code.
> >
> > Nak again.
> >
> >>> +   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;
> >>> +}
> >>> +
> >>> +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;
> >>> +}
> >>> +#endif
> >>> +
> >>>   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;
> >>> @@ -266,6 +450,7 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> >>>     printf("\tbin: %s\n\ttype: %pUl\n", bin, guid);
> >>>     printf("\tindex: %ld\n\tinstance: %ld\n", index, instance);
> >>>   #endif
> >>> +   auth_context.sig_size = 0;
> >>>
> >>>     g = fopen(bin, "r");
> >>>     if (!g) {
> >>> @@ -281,11 +466,36 @@ 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 IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> >>
> >> see above
> >>
> >>> +   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;
> >>> +           }
> >>> +   }
> >>> +#endif
> >>> +
> >>>     header.capsule_guid = efi_guid_fm_capsule;
> >>>     header.header_size = sizeof(header);
> >>>     /* TODO: The current implementation ignores flags */
> >>> @@ -294,11 +504,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;
> >>> @@ -307,13 +526,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;
> >>> @@ -323,34 +542,61 @@ 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 IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> >>
> >> We don't want to use #if if avoidable.
> >
> > For this specific chunk of code, we can remove #ifdef,
> > but we should not remove #ifdef elsewhere.
> >
> > -Takahiro Akashi
> >
> >
> >>> +   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;
> >>> +           }
> >>>     }
> >>> +#endif
> >>> +
> >>>     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);
> >>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> >>> +   if (auth_context.sig_size)
> >>> +           OPENSSL_free(auth_context.sig_data);
> >>> +#endif
> >>>
> >>>     return 0;
> >>>
> >>> -err_3:
> >>> +err_4:
> >>>     fclose(f);
> >>> +err_3:
> >>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> >>> +   if (auth_context.sig_size)
> >>> +           OPENSSL_free(auth_context.sig_data);
> >>> +#endif
> >>>   err_2:
> >>>     free(data);
> >>>   err_1:
> >>> @@ -359,10 +605,6 @@ err_1:
> >>>     return -1;
> >>>   }
> >>>
> >>> -/*
> >>> - * Usage:
> >>> - *   $ mkeficapsule -f <firmware binary> <output file>
> >>> - */
> >>>   int main(int argc, char **argv)
> >>>   {
> >>>     char *file;
> >>> @@ -370,6 +612,8 @@ int main(int argc, char **argv)
> >>>     char *dtb_file;
> >>>     efi_guid_t *guid;
> >>>     unsigned long index, instance;
> >>> +   uint64_t mcount;
> >>> +   char *privkey_file, *cert_file;
> >>>     int c, idx;
> >>>     int ret;
> >>>     bool overlay = false;
> >>> @@ -380,8 +624,12 @@ int main(int argc, char **argv)
> >>>     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:D:K:Oh", options, &idx);
> >>> +           c = getopt_long(argc, argv, opts_short, options, &idx);
> >>>             if (c == -1)
> >>>                     break;
> >>>
> >>> @@ -422,6 +670,28 @@ int main(int argc, char **argv)
> >>>                     }
> >>>                     dtb_file = optarg;
> >>>                     break;
> >>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> >>
> >> see above
> >>
> >> Best regards
> >>
> >> Heinrich
> >>
> >>> +           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
> >>>             case 'O':
> >>>                     overlay = true;
> >>>                     break;
> >>> @@ -431,8 +701,12 @@ int main(int argc, char **argv)
> >>>             }
> >>>     }
> >>>
> >>> -   /* need a fit image file or raw image file */
> >>> -   if (!file && !pkey_file && !dtb_file) {
> >>> +   /* check necessary parameters */
> >>> +   if ((file && (!(optind < argc) ||
> >>> +                 (privkey_file && !cert_file) ||
> >>> +                 (!privkey_file && cert_file))) ||
> >>> +       ((pkey_file && !dtb_file) ||
> >>> +        (!pkey_file && dtb_file))) {
> >>>             print_usage();
> >>>             exit(EXIT_FAILURE);
> >>>     }
> >>> @@ -442,12 +716,12 @@ int main(int argc, char **argv)
> >>>             if (ret == -1) {
> >>>                     printf("Adding public key to the dtb failed\n");
> >>>                     exit(EXIT_FAILURE);
> >>> -           } else {
> >>> -                   exit(EXIT_SUCCESS);
> >>>             }
> >>>     }
> >>>
> >>> -   if (create_fwbin(argv[optind], file, guid, index, instance)
> >>> +   if (optind < argc &&
> >>> +       create_fwbin(argv[optind], file, guid, index, instance,
> >>> +                    mcount, privkey_file, cert_file)
> >>>                     < 0) {
> >>>             printf("Creating firmware capsule failed\n");
> >>>             exit(EXIT_FAILURE);
> >>>
> >>
>


-- 
Masami Hiramatsu

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

* [PATCH 3/4] tools: add fdtsig command
  2021-05-12  4:57 ` [PATCH 3/4] tools: add fdtsig command AKASHI Takahiro
@ 2021-05-13  5:23   ` Heinrich Schuchardt
  2021-05-13  7:03     ` AKASHI Takahiro
  0 siblings, 1 reply; 55+ messages in thread
From: Heinrich Schuchardt @ 2021-05-13  5:23 UTC (permalink / raw)
  To: u-boot

On 5/12/21 6:57 AM, AKASHI Takahiro wrote:
> This command allows us to add a certificate (or public key) to dtb blob:
> {
> 	signature {
> 		capsule-key = "...";
> 	};
> }
>
> The value is actually a signature list in terms of UEFI specificaion,
> and used in verifying UEFI capsules.
>
> The code was originally developed by Sughosh and derived from
> mkeficapsule.c.

I have no clue how you want to use this command. Please, provide a
description of the workflow using this command.

In your comments for patch 2 you wrote that the -K and -D option can be
replaced by tools provided by the device-tree compiler package. Why do
they reappear here?

Please, add a man-page (/doc/fdtsig.1).


>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>   Makefile       |   2 +-
>   tools/Makefile |   2 +
>   tools/fdtsig.c | 274 +++++++++++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 277 insertions(+), 1 deletion(-)
>   create mode 100644 tools/fdtsig.c
>
> diff --git a/Makefile b/Makefile
> index 9806464357e0..8b40987234a0 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -1016,7 +1016,7 @@ quiet_cmd_lzma = LZMA    $@
>   cmd_lzma = lzma -c -z -k -9 $< > $@
>
>   quiet_cmd_fdtsig = FDTSIG  $@
> -cmd_fdtsig = $(srctree)/tools/fdtsig.sh $(CONFIG_EFI_PKEY_FILE) $@
> +cmd_fdtsig = $(objtree)/tools/fdtsig -K $(CONFIG_EFI_PKEY_FILE) -D $@
>
>   cfg: u-boot.cfg
>
> diff --git a/tools/Makefile b/tools/Makefile
> index 71a52719620c..e6fd1dbade19 100644
> --- a/tools/Makefile
> +++ b/tools/Makefile
> @@ -234,6 +234,8 @@ HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
>   ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),)
>   HOSTLDLIBS_mkeficapsule += \
>   	$(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto")
> +	fdtsig-objs	:= fdtsig.o $(LIBFDT_OBJS)
> +	hostprogs-y += fdtsig
>   endif
>   hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
>
> diff --git a/tools/fdtsig.c b/tools/fdtsig.c
> new file mode 100644
> index 000000000000..daa1e63c3b33
> --- /dev/null
> +++ b/tools/fdtsig.c
> @@ -0,0 +1,274 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2021 Linaro Limited
> + * The code in this file was extracted from mkeficapsule.c

That information is better described by the git log.

Please, explain here what this tool is used for.

Best regards

Heinrich

> + */
> +
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <getopt.h>
> +#include <malloc.h>
> +#include <stdbool.h>
> +#include <stdio.h>
> +#include <unistd.h>
> +
> +#include <sys/mman.h>
> +#include <sys/stat.h>
> +
> +#include <linux/libfdt.h>
> +
> +#define SIGNATURE_NODENAME	"signature"
> +#define OVERLAY_NODENAME	"__overlay__"
> +
> +static const char *tool_name = "fdtsig";
> +
> +static const char *opts_short = "D:K:Oh";
> +
> +static struct option options[] = {
> +	{"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},
> +};
> +
> +static void print_usage(void)
> +{
> +	printf("Usage: %s [options]\n"
> +	       "Options:\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;
> +}
> +
> +int main(int argc, char **argv)
> +{
> +	char *pkey_file;
> +	char *dtb_file;
> +	bool overlay;
> +	int c, idx, ret;
> +
> +	pkey_file = NULL;
> +	dtb_file = NULL;
> +	overlay = false;
> +
> +	for (;;) {
> +		c = getopt_long(argc, argv, opts_short, options, &idx);
> +		if (c == -1)
> +			break;
> +
> +		switch (c) {
> +		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;
> +		}
> +	}
> +
> +	/* check necessary parameters */
> +	if (!pkey_file || !dtb_file) {
> +		print_usage();
> +		exit(EXIT_FAILURE);
> +	}
> +
> +	ret = add_public_key(pkey_file, dtb_file, overlay);
> +	if (ret == -1) {
> +		printf("Adding public key to the dtb failed\n");
> +		exit(EXIT_FAILURE);
> +	}
> +
> +	exit(EXIT_SUCCESS);
> +}
>

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

* [PATCH 1/4] tools: mkeficapsule: add firmwware image signing
  2021-05-13  5:00         ` AKASHI Takahiro
@ 2021-05-13  5:35           ` Heinrich Schuchardt
  2021-05-13  6:36             ` AKASHI Takahiro
  0 siblings, 1 reply; 55+ messages in thread
From: Heinrich Schuchardt @ 2021-05-13  5:35 UTC (permalink / raw)
  To: u-boot

On 5/13/21 7:00 AM, AKASHI Takahiro wrote:
> On Thu, May 13, 2021 at 06:22:39AM +0200, Heinrich Schuchardt wrote:
>> On 5/13/21 5:08 AM, AKASHI Takahiro wrote:
>>> On Wed, May 12, 2021 at 10:56:41AM +0200, Heinrich Schuchardt wrote:
>>>> On 12.05.21 06:57, AKASHI Takahiro wrote:
>>>>> With this enhancement, mkeficapsule will be able to create a capsule
>>>>> file with a signature which will be verified later by FMP's SetImage().
>>>>>
>>>>> We will have to specify addtional command parameters:
>>>>>     -monotonic-cout <count> : monotonic count
>>>>>     -private-key <private key file> : private key file
>>>>>     -certificate <certificate file> : certificate file
>>>>> Only when those parameters are given, a signature will be added
>>>>> to a capsule file.
>>>>>
>>>>> Users are expected to maintain the monotonic count for each firmware
>>>>> image.
>>>>>
>>>>> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
>>>>> ---
>>>>>    tools/Makefile       |   4 +
>>>>>    tools/mkeficapsule.c | 324 +++++++++++++++++++++++++++++++++++++++----
>>>>>    2 files changed, 303 insertions(+), 25 deletions(-)
>>>>>
>>>>> diff --git a/tools/Makefile b/tools/Makefile
>>>>> index d020c55d6644..02eae0286e20 100644
>>>>> --- a/tools/Makefile
>>>>> +++ b/tools/Makefile
>>>>> @@ -231,6 +231,10 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs
>>>>>    hostprogs-$(CONFIG_ASN1_COMPILER)	+= asn1_compiler
>>>>>    HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
>>>>>
>>>>> +ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),)
>>>>> +HOSTLDLIBS_mkeficapsule += \
>>>>> +	$(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto")
>>>>
>>>> I don't expect any user wants to install two tool versions in parallel.
>>>>
>>>> The tool should always be able to add a signature.
>>>> Adding a signature must be optional.
>>>
>>> It seems to me that those two statements mutually contradict.
>>> Or do you intend to say that we should have a separate kconfig
>>> option to enable/disable signing feature in mkeficapsule?
>>>
>>> If so, I can agree.
>>>
>>> In either way, we should have an option to turn on/off this functionality
>>> as not all users use signed capsules.
>>
>> I want to have a single binary to distribute with Linux distros (e.g.
>> Debian/Ubuntu package u-boot-tools).
>>
>> This should allow both
>>
>> - create signed capsules
>> - create unsigned capsules
>>
>> The user shall select signing via command line parameters.
>>
>> Support for signing via the tool shall not depend on board Kconfig
>> parameters.
>
> That is why I proposed that we create a new kconfig option.

What do you want to configure? Signing shall always be enabled in
mkeficapsule.

>
> Please note that enabling signing feature in mkeficapsule
> requires openssl library, and we should not enforce users who don't
> need this feature to install an unnecessary package.

Why? There are dozens of other packages depending on OpenSSL on a
developer's machine.

Best regards

Heinrich

>
> -Takahiro Akashi
>
>> Best regards
>>
>> Heinrich
>>
>>>
>>>>> +endif
>>>>>    mkeficapsule-objs	:= mkeficapsule.o $(LIBFDT_OBJS)
>>>>>    hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
>>>>>
>>>>> diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
>>>>> index de0a62898886..34ff1bdd82eb 100644
>>>>> --- a/tools/mkeficapsule.c
>>>>> +++ b/tools/mkeficapsule.c
>>>>> @@ -18,7 +18,17 @@
>>>>>    #include <sys/stat.h>
>>>>>    #include <sys/types.h>
>>>>>
>>>>> -#include "fdt_host.h"
>>>>> +#include <linux/kconfig.h>
>>>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>>>>
>>>> see above
>>>>
>>>>> +#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
>>>>> +
>>>>> +#include <linux/libfdt.h>
>>>>>
>>>>>    typedef __u8 u8;
>>>>>    typedef __u16 u16;
>>>>> @@ -46,6 +56,13 @@ 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;
>>>>> +
>>>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>>>>
>>>> see above
>>>>
>>>>> +static const char *opts_short = "f:r:i:I:v:D:K:P:C:m:dOh";
>>>>> +#else
>>>>> +static const char *opts_short = "f:r:i:I:v:D:K:Oh";
>>>>> +#endif
>>>>>
>>>>>    static struct option options[] = {
>>>>>    	{"fit", required_argument, NULL, 'f'},
>>>>> @@ -54,6 +71,12 @@ static struct option options[] = {
>>>>>    	{"instance", required_argument, NULL, 'I'},
>>>>>    	{"dtb", required_argument, NULL, 'D'},
>>>>>    	{"public key", required_argument, NULL, 'K'},
>>>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>>>>> +	{"private-key", required_argument, NULL, 'P'},
>>>>> +	{"certificate", required_argument, NULL, 'C'},
>>>>> +	{"monotonic-count", required_argument, NULL, 'm'},
>>>>
>>>> These options should not be required.
>>>
>>> I don't get you. What do you mean?
>>>
>>>>> +	{"dump-sig", no_argument, NULL, 'd'},
>>>>> +#endif
>>>>>    	{"overlay", no_argument, NULL, 'O'},
>>>>>    	{"help", no_argument, NULL, 'h'},
>>>>>    	{NULL, 0, NULL, 0},
>>>>> @@ -70,6 +93,12 @@ static void print_usage(void)
>>>>>    	       "\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"
>>>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>>>>
>>>> see above
>>>>
>>>>> +	       "\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-O, --overlay               the dtb file is an overlay\n"
>>>>>    	       "\t-h, --help                  print a help message\n",
>>>>>    	       tool_name);
>>>>> @@ -249,12 +278,167 @@ err:
>>>>>    	return ret;
>>>>>    }
>>>>>
>>>>> +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;
>>>>> +
>>>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>>>>
>>>> see above
>>>>
>>>>> +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);
>>>>
>>>> Please, you use fprintf(stderr,) for error messages.
>>>>
>>>>> +		ERR_print_errors_fp(stderr);
>>>>> +	}
>>>>> +
>>>>> +	return key;
>>>>> +}
>>>>> +
>>>>> +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);
>>>>
>>>> fprintf(stderr,)
>>>>
>>>>> +		ERR_print_errors_fp(stderr);
>>>>> +	}
>>>>> +
>>>>> +	return cert;
>>>>> +}
>>>>> +
>>>>> +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 */
>>>>
>>>> PKCS7_NOATTR is a value without any documentation in the code.
>>>
>>> Nak.
>>> Those macros are part of openssl library. See openssl/pkcs7.h.
>>>
>>>> Please, replace variable names by a long text describing what it missing.
>>>>
>>>>> +	flags = PKCS7_BINARY | PKCS7_DETACHED;
>>>>
>>>> Those constants lack documentation in the code.
>>>
>>> Nak again.
>>>
>>>>> +	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;
>>>>> +}
>>>>> +
>>>>> +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;
>>>>> +}
>>>>> +#endif
>>>>> +
>>>>>    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;
>>>>> @@ -266,6 +450,7 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
>>>>>    	printf("\tbin: %s\n\ttype: %pUl\n", bin, guid);
>>>>>    	printf("\tindex: %ld\n\tinstance: %ld\n", index, instance);
>>>>>    #endif
>>>>> +	auth_context.sig_size = 0;
>>>>>
>>>>>    	g = fopen(bin, "r");
>>>>>    	if (!g) {
>>>>> @@ -281,11 +466,36 @@ 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 IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>>>>
>>>> see above
>>>>
>>>>> +	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;
>>>>> +		}
>>>>> +	}
>>>>> +#endif
>>>>> +
>>>>>    	header.capsule_guid = efi_guid_fm_capsule;
>>>>>    	header.header_size = sizeof(header);
>>>>>    	/* TODO: The current implementation ignores flags */
>>>>> @@ -294,11 +504,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;
>>>>> @@ -307,13 +526,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;
>>>>> @@ -323,34 +542,61 @@ 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 IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>>>>
>>>> We don't want to use #if if avoidable.
>>>
>>> For this specific chunk of code, we can remove #ifdef,
>>> but we should not remove #ifdef elsewhere.
>>>
>>> -Takahiro Akashi
>>>
>>>
>>>>> +	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;
>>>>> +		}
>>>>>    	}
>>>>> +#endif
>>>>> +
>>>>>    	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);
>>>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>>>>> +	if (auth_context.sig_size)
>>>>> +		OPENSSL_free(auth_context.sig_data);
>>>>> +#endif
>>>>>
>>>>>    	return 0;
>>>>>
>>>>> -err_3:
>>>>> +err_4:
>>>>>    	fclose(f);
>>>>> +err_3:
>>>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>>>>> +	if (auth_context.sig_size)
>>>>> +		OPENSSL_free(auth_context.sig_data);
>>>>> +#endif
>>>>>    err_2:
>>>>>    	free(data);
>>>>>    err_1:
>>>>> @@ -359,10 +605,6 @@ err_1:
>>>>>    	return -1;
>>>>>    }
>>>>>
>>>>> -/*
>>>>> - * Usage:
>>>>> - *   $ mkeficapsule -f <firmware binary> <output file>
>>>>> - */
>>>>>    int main(int argc, char **argv)
>>>>>    {
>>>>>    	char *file;
>>>>> @@ -370,6 +612,8 @@ int main(int argc, char **argv)
>>>>>    	char *dtb_file;
>>>>>    	efi_guid_t *guid;
>>>>>    	unsigned long index, instance;
>>>>> +	uint64_t mcount;
>>>>> +	char *privkey_file, *cert_file;
>>>>>    	int c, idx;
>>>>>    	int ret;
>>>>>    	bool overlay = false;
>>>>> @@ -380,8 +624,12 @@ int main(int argc, char **argv)
>>>>>    	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:D:K:Oh", options, &idx);
>>>>> +		c = getopt_long(argc, argv, opts_short, options, &idx);
>>>>>    		if (c == -1)
>>>>>    			break;
>>>>>
>>>>> @@ -422,6 +670,28 @@ int main(int argc, char **argv)
>>>>>    			}
>>>>>    			dtb_file = optarg;
>>>>>    			break;
>>>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>>>>
>>>> see above
>>>>
>>>> Best regards
>>>>
>>>> Heinrich
>>>>
>>>>> +		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
>>>>>    		case 'O':
>>>>>    			overlay = true;
>>>>>    			break;
>>>>> @@ -431,8 +701,12 @@ int main(int argc, char **argv)
>>>>>    		}
>>>>>    	}
>>>>>
>>>>> -	/* need a fit image file or raw image file */
>>>>> -	if (!file && !pkey_file && !dtb_file) {
>>>>> +	/* check necessary parameters */
>>>>> +	if ((file && (!(optind < argc) ||
>>>>> +		      (privkey_file && !cert_file) ||
>>>>> +		      (!privkey_file && cert_file))) ||
>>>>> +	    ((pkey_file && !dtb_file) ||
>>>>> +	     (!pkey_file && dtb_file))) {
>>>>>    		print_usage();
>>>>>    		exit(EXIT_FAILURE);
>>>>>    	}
>>>>> @@ -442,12 +716,12 @@ int main(int argc, char **argv)
>>>>>    		if (ret == -1) {
>>>>>    			printf("Adding public key to the dtb failed\n");
>>>>>    			exit(EXIT_FAILURE);
>>>>> -		} else {
>>>>> -			exit(EXIT_SUCCESS);
>>>>>    		}
>>>>>    	}
>>>>>
>>>>> -	if (create_fwbin(argv[optind], file, guid, index, instance)
>>>>> +	if (optind < argc &&
>>>>> +	    create_fwbin(argv[optind], file, guid, index, instance,
>>>>> +			 mcount, privkey_file, cert_file)
>>>>>    			< 0) {
>>>>>    		printf("Creating firmware capsule failed\n");
>>>>>    		exit(EXIT_FAILURE);
>>>>>
>>>>
>>

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

* [PATCH 1/4] tools: mkeficapsule: add firmwware image signing
  2021-05-13  5:12         ` Masami Hiramatsu
@ 2021-05-13  5:50           ` Heinrich Schuchardt
  2021-05-13  6:44             ` Masami Hiramatsu
  2021-05-13  6:50             ` AKASHI Takahiro
  0 siblings, 2 replies; 55+ messages in thread
From: Heinrich Schuchardt @ 2021-05-13  5:50 UTC (permalink / raw)
  To: u-boot

On 5/13/21 7:12 AM, Masami Hiramatsu wrote:
> Hi Heinrich,
>
> 2021?5?13?(?) 13:22 Heinrich Schuchardt <xypron.glpk@gmx.de>:
>>
>> On 5/13/21 5:08 AM, AKASHI Takahiro wrote:
>>> On Wed, May 12, 2021 at 10:56:41AM +0200, Heinrich Schuchardt wrote:
>>>> On 12.05.21 06:57, AKASHI Takahiro wrote:
>>>>> With this enhancement, mkeficapsule will be able to create a capsule
>>>>> file with a signature which will be verified later by FMP's SetImage().
>>>>>
>>>>> We will have to specify addtional command parameters:
>>>>>     -monotonic-cout <count> : monotonic count
>>>>>     -private-key <private key file> : private key file
>>>>>     -certificate <certificate file> : certificate file
>>>>> Only when those parameters are given, a signature will be added
>>>>> to a capsule file.
>>>>>
>>>>> Users are expected to maintain the monotonic count for each firmware
>>>>> image.
>>>>>
>>>>> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
>>>>> ---
>>>>>    tools/Makefile       |   4 +
>>>>>    tools/mkeficapsule.c | 324 +++++++++++++++++++++++++++++++++++++++----
>>>>>    2 files changed, 303 insertions(+), 25 deletions(-)
>>>>>
>>>>> diff --git a/tools/Makefile b/tools/Makefile
>>>>> index d020c55d6644..02eae0286e20 100644
>>>>> --- a/tools/Makefile
>>>>> +++ b/tools/Makefile
>>>>> @@ -231,6 +231,10 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs
>>>>>    hostprogs-$(CONFIG_ASN1_COMPILER) += asn1_compiler
>>>>>    HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
>>>>>
>>>>> +ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),)
>>>>> +HOSTLDLIBS_mkeficapsule += \
>>>>> +   $(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto")
>>>>
>>>> I don't expect any user wants to install two tool versions in parallel.
>>>>
>>>> The tool should always be able to add a signature.
>>>> Adding a signature must be optional.
>>>
>>> It seems to me that those two statements mutually contradict.
>>> Or do you intend to say that we should have a separate kconfig
>>> option to enable/disable signing feature in mkeficapsule?
>>>
>>> If so, I can agree.
>>>
>>> In either way, we should have an option to turn on/off this functionality
>>> as not all users use signed capsules.
>>
>> I want to have a single binary to distribute with Linux distros (e.g.
>> Debian/Ubuntu package u-boot-tools).
>
> I couldn't catch your point. If so, the distros can build u-boot with
> CONFIG_EFI_CAPSULE_AUTHENTICATE=y...

Why should the tool depend on board configuration?
Who would want capsule updates without authentication?

>
> BTW, IMHO, if u-boot.bin can not find the ESL in the device tree,
> it should skip authentication too.

In this case the capsule should be rejected (if
CONFIG_EFI_CAPSULE_AUTHENTICATE=y).

>
> Then, user can choose whether enabling capsule authentication or not
> by embedding ESL into their devicetree.

The user shall not be able to decide anything that might hamper
security. The U-Boot binary must dictate if a capsule is safe.

Best regards

Heinrich

>
> Thank you
>
>>
>> This should allow both
>>
>> - create signed capsules
>> - create unsigned capsules
>>
>> The user shall select signing via command line parameters.
>>
>> Support for signing via the tool shall not depend on board Kconfig
>> parameters.
>>
>> Best regards
>>
>> Heinrich
>>
>>>
>>>>> +endif
>>>>>    mkeficapsule-objs := mkeficapsule.o $(LIBFDT_OBJS)
>>>>>    hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
>>>>>
>>>>> diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
>>>>> index de0a62898886..34ff1bdd82eb 100644
>>>>> --- a/tools/mkeficapsule.c
>>>>> +++ b/tools/mkeficapsule.c
>>>>> @@ -18,7 +18,17 @@
>>>>>    #include <sys/stat.h>
>>>>>    #include <sys/types.h>
>>>>>
>>>>> -#include "fdt_host.h"
>>>>> +#include <linux/kconfig.h>
>>>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>>>>
>>>> see above
>>>>
>>>>> +#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
>>>>> +
>>>>> +#include <linux/libfdt.h>
>>>>>
>>>>>    typedef __u8 u8;
>>>>>    typedef __u16 u16;
>>>>> @@ -46,6 +56,13 @@ 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;
>>>>> +
>>>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>>>>
>>>> see above
>>>>
>>>>> +static const char *opts_short = "f:r:i:I:v:D:K:P:C:m:dOh";
>>>>> +#else
>>>>> +static const char *opts_short = "f:r:i:I:v:D:K:Oh";
>>>>> +#endif
>>>>>
>>>>>    static struct option options[] = {
>>>>>      {"fit", required_argument, NULL, 'f'},
>>>>> @@ -54,6 +71,12 @@ static struct option options[] = {
>>>>>      {"instance", required_argument, NULL, 'I'},
>>>>>      {"dtb", required_argument, NULL, 'D'},
>>>>>      {"public key", required_argument, NULL, 'K'},
>>>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>>>>> +   {"private-key", required_argument, NULL, 'P'},
>>>>> +   {"certificate", required_argument, NULL, 'C'},
>>>>> +   {"monotonic-count", required_argument, NULL, 'm'},
>>>>
>>>> These options should not be required.
>>>
>>> I don't get you. What do you mean?
>>>
>>>>> +   {"dump-sig", no_argument, NULL, 'd'},
>>>>> +#endif
>>>>>      {"overlay", no_argument, NULL, 'O'},
>>>>>      {"help", no_argument, NULL, 'h'},
>>>>>      {NULL, 0, NULL, 0},
>>>>> @@ -70,6 +93,12 @@ static void print_usage(void)
>>>>>             "\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"
>>>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>>>>
>>>> see above
>>>>
>>>>> +          "\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-O, --overlay               the dtb file is an overlay\n"
>>>>>             "\t-h, --help                  print a help message\n",
>>>>>             tool_name);
>>>>> @@ -249,12 +278,167 @@ err:
>>>>>      return ret;
>>>>>    }
>>>>>
>>>>> +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;
>>>>> +
>>>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>>>>
>>>> see above
>>>>
>>>>> +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);
>>>>
>>>> Please, you use fprintf(stderr,) for error messages.
>>>>
>>>>> +           ERR_print_errors_fp(stderr);
>>>>> +   }
>>>>> +
>>>>> +   return key;
>>>>> +}
>>>>> +
>>>>> +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);
>>>>
>>>> fprintf(stderr,)
>>>>
>>>>> +           ERR_print_errors_fp(stderr);
>>>>> +   }
>>>>> +
>>>>> +   return cert;
>>>>> +}
>>>>> +
>>>>> +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 */
>>>>
>>>> PKCS7_NOATTR is a value without any documentation in the code.
>>>
>>> Nak.
>>> Those macros are part of openssl library. See openssl/pkcs7.h.
>>>
>>>> Please, replace variable names by a long text describing what it missing.
>>>>
>>>>> +   flags = PKCS7_BINARY | PKCS7_DETACHED;
>>>>
>>>> Those constants lack documentation in the code.
>>>
>>> Nak again.
>>>
>>>>> +   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;
>>>>> +}
>>>>> +
>>>>> +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;
>>>>> +}
>>>>> +#endif
>>>>> +
>>>>>    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;
>>>>> @@ -266,6 +450,7 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
>>>>>      printf("\tbin: %s\n\ttype: %pUl\n", bin, guid);
>>>>>      printf("\tindex: %ld\n\tinstance: %ld\n", index, instance);
>>>>>    #endif
>>>>> +   auth_context.sig_size = 0;
>>>>>
>>>>>      g = fopen(bin, "r");
>>>>>      if (!g) {
>>>>> @@ -281,11 +466,36 @@ 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 IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>>>>
>>>> see above
>>>>
>>>>> +   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;
>>>>> +           }
>>>>> +   }
>>>>> +#endif
>>>>> +
>>>>>      header.capsule_guid = efi_guid_fm_capsule;
>>>>>      header.header_size = sizeof(header);
>>>>>      /* TODO: The current implementation ignores flags */
>>>>> @@ -294,11 +504,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;
>>>>> @@ -307,13 +526,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;
>>>>> @@ -323,34 +542,61 @@ 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 IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>>>>
>>>> We don't want to use #if if avoidable.
>>>
>>> For this specific chunk of code, we can remove #ifdef,
>>> but we should not remove #ifdef elsewhere.
>>>
>>> -Takahiro Akashi
>>>
>>>
>>>>> +   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;
>>>>> +           }
>>>>>      }
>>>>> +#endif
>>>>> +
>>>>>      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);
>>>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>>>>> +   if (auth_context.sig_size)
>>>>> +           OPENSSL_free(auth_context.sig_data);
>>>>> +#endif
>>>>>
>>>>>      return 0;
>>>>>
>>>>> -err_3:
>>>>> +err_4:
>>>>>      fclose(f);
>>>>> +err_3:
>>>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>>>>> +   if (auth_context.sig_size)
>>>>> +           OPENSSL_free(auth_context.sig_data);
>>>>> +#endif
>>>>>    err_2:
>>>>>      free(data);
>>>>>    err_1:
>>>>> @@ -359,10 +605,6 @@ err_1:
>>>>>      return -1;
>>>>>    }
>>>>>
>>>>> -/*
>>>>> - * Usage:
>>>>> - *   $ mkeficapsule -f <firmware binary> <output file>
>>>>> - */
>>>>>    int main(int argc, char **argv)
>>>>>    {
>>>>>      char *file;
>>>>> @@ -370,6 +612,8 @@ int main(int argc, char **argv)
>>>>>      char *dtb_file;
>>>>>      efi_guid_t *guid;
>>>>>      unsigned long index, instance;
>>>>> +   uint64_t mcount;
>>>>> +   char *privkey_file, *cert_file;
>>>>>      int c, idx;
>>>>>      int ret;
>>>>>      bool overlay = false;
>>>>> @@ -380,8 +624,12 @@ int main(int argc, char **argv)
>>>>>      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:D:K:Oh", options, &idx);
>>>>> +           c = getopt_long(argc, argv, opts_short, options, &idx);
>>>>>              if (c == -1)
>>>>>                      break;
>>>>>
>>>>> @@ -422,6 +670,28 @@ int main(int argc, char **argv)
>>>>>                      }
>>>>>                      dtb_file = optarg;
>>>>>                      break;
>>>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>>>>
>>>> see above
>>>>
>>>> Best regards
>>>>
>>>> Heinrich
>>>>
>>>>> +           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
>>>>>              case 'O':
>>>>>                      overlay = true;
>>>>>                      break;
>>>>> @@ -431,8 +701,12 @@ int main(int argc, char **argv)
>>>>>              }
>>>>>      }
>>>>>
>>>>> -   /* need a fit image file or raw image file */
>>>>> -   if (!file && !pkey_file && !dtb_file) {
>>>>> +   /* check necessary parameters */
>>>>> +   if ((file && (!(optind < argc) ||
>>>>> +                 (privkey_file && !cert_file) ||
>>>>> +                 (!privkey_file && cert_file))) ||
>>>>> +       ((pkey_file && !dtb_file) ||
>>>>> +        (!pkey_file && dtb_file))) {
>>>>>              print_usage();
>>>>>              exit(EXIT_FAILURE);
>>>>>      }
>>>>> @@ -442,12 +716,12 @@ int main(int argc, char **argv)
>>>>>              if (ret == -1) {
>>>>>                      printf("Adding public key to the dtb failed\n");
>>>>>                      exit(EXIT_FAILURE);
>>>>> -           } else {
>>>>> -                   exit(EXIT_SUCCESS);
>>>>>              }
>>>>>      }
>>>>>
>>>>> -   if (create_fwbin(argv[optind], file, guid, index, instance)
>>>>> +   if (optind < argc &&
>>>>> +       create_fwbin(argv[optind], file, guid, index, instance,
>>>>> +                    mcount, privkey_file, cert_file)
>>>>>                      < 0) {
>>>>>              printf("Creating firmware capsule failed\n");
>>>>>              exit(EXIT_FAILURE);
>>>>>
>>>>
>>
>
>

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

* [PATCH 1/4] tools: mkeficapsule: add firmwware image signing
  2021-05-13  5:35           ` Heinrich Schuchardt
@ 2021-05-13  6:36             ` AKASHI Takahiro
  2021-05-13  6:45               ` Heinrich Schuchardt
  0 siblings, 1 reply; 55+ messages in thread
From: AKASHI Takahiro @ 2021-05-13  6:36 UTC (permalink / raw)
  To: u-boot

On Thu, May 13, 2021 at 07:35:36AM +0200, Heinrich Schuchardt wrote:
> On 5/13/21 7:00 AM, AKASHI Takahiro wrote:
> > On Thu, May 13, 2021 at 06:22:39AM +0200, Heinrich Schuchardt wrote:
> > > On 5/13/21 5:08 AM, AKASHI Takahiro wrote:
> > > > On Wed, May 12, 2021 at 10:56:41AM +0200, Heinrich Schuchardt wrote:
> > > > > On 12.05.21 06:57, AKASHI Takahiro wrote:
> > > > > > With this enhancement, mkeficapsule will be able to create a capsule
> > > > > > file with a signature which will be verified later by FMP's SetImage().
> > > > > > 
> > > > > > We will have to specify addtional command parameters:
> > > > > >     -monotonic-cout <count> : monotonic count
> > > > > >     -private-key <private key file> : private key file
> > > > > >     -certificate <certificate file> : certificate file
> > > > > > Only when those parameters are given, a signature will be added
> > > > > > to a capsule file.
> > > > > > 
> > > > > > Users are expected to maintain the monotonic count for each firmware
> > > > > > image.
> > > > > > 
> > > > > > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > > > > > ---
> > > > > >    tools/Makefile       |   4 +
> > > > > >    tools/mkeficapsule.c | 324 +++++++++++++++++++++++++++++++++++++++----
> > > > > >    2 files changed, 303 insertions(+), 25 deletions(-)
> > > > > > 
> > > > > > diff --git a/tools/Makefile b/tools/Makefile
> > > > > > index d020c55d6644..02eae0286e20 100644
> > > > > > --- a/tools/Makefile
> > > > > > +++ b/tools/Makefile
> > > > > > @@ -231,6 +231,10 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs
> > > > > >    hostprogs-$(CONFIG_ASN1_COMPILER)	+= asn1_compiler
> > > > > >    HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
> > > > > > 
> > > > > > +ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),)
> > > > > > +HOSTLDLIBS_mkeficapsule += \
> > > > > > +	$(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto")
> > > > > 
> > > > > I don't expect any user wants to install two tool versions in parallel.
> > > > > 
> > > > > The tool should always be able to add a signature.
> > > > > Adding a signature must be optional.
> > > > 
> > > > It seems to me that those two statements mutually contradict.
> > > > Or do you intend to say that we should have a separate kconfig
> > > > option to enable/disable signing feature in mkeficapsule?
> > > > 
> > > > If so, I can agree.
> > > > 
> > > > In either way, we should have an option to turn on/off this functionality
> > > > as not all users use signed capsules.
> > > 
> > > I want to have a single binary to distribute with Linux distros (e.g.
> > > Debian/Ubuntu package u-boot-tools).
> > > 
> > > This should allow both
> > > 
> > > - create signed capsules
> > > - create unsigned capsules
> > > 
> > > The user shall select signing via command line parameters.
> > > 
> > > Support for signing via the tool shall not depend on board Kconfig
> > > parameters.
> > 
> > That is why I proposed that we create a new kconfig option.
> 
> What do you want to configure? Signing shall always be enabled in
> mkeficapsule.

I don't think so.

> > 
> > Please note that enabling signing feature in mkeficapsule
> > requires openssl library, and we should not enforce users who don't
> > need this feature to install an unnecessary package.
> 
> Why? There are dozens of other packages depending on OpenSSL on a
> developer's machine.

We don't expect all users have openssl-related packages on their desktop.

-Takahiro Akashi


> Best regards
> 
> Heinrich
> 
> > 
> > -Takahiro Akashi
> > 
> > > Best regards
> > > 
> > > Heinrich
> > > 
> > > > 
> > > > > > +endif
> > > > > >    mkeficapsule-objs	:= mkeficapsule.o $(LIBFDT_OBJS)
> > > > > >    hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
> > > > > > 
> > > > > > diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
> > > > > > index de0a62898886..34ff1bdd82eb 100644
> > > > > > --- a/tools/mkeficapsule.c
> > > > > > +++ b/tools/mkeficapsule.c
> > > > > > @@ -18,7 +18,17 @@
> > > > > >    #include <sys/stat.h>
> > > > > >    #include <sys/types.h>
> > > > > > 
> > > > > > -#include "fdt_host.h"
> > > > > > +#include <linux/kconfig.h>
> > > > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> > > > > 
> > > > > see above
> > > > > 
> > > > > > +#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
> > > > > > +
> > > > > > +#include <linux/libfdt.h>
> > > > > > 
> > > > > >    typedef __u8 u8;
> > > > > >    typedef __u16 u16;
> > > > > > @@ -46,6 +56,13 @@ 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;
> > > > > > +
> > > > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> > > > > 
> > > > > see above
> > > > > 
> > > > > > +static const char *opts_short = "f:r:i:I:v:D:K:P:C:m:dOh";
> > > > > > +#else
> > > > > > +static const char *opts_short = "f:r:i:I:v:D:K:Oh";
> > > > > > +#endif
> > > > > > 
> > > > > >    static struct option options[] = {
> > > > > >    	{"fit", required_argument, NULL, 'f'},
> > > > > > @@ -54,6 +71,12 @@ static struct option options[] = {
> > > > > >    	{"instance", required_argument, NULL, 'I'},
> > > > > >    	{"dtb", required_argument, NULL, 'D'},
> > > > > >    	{"public key", required_argument, NULL, 'K'},
> > > > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> > > > > > +	{"private-key", required_argument, NULL, 'P'},
> > > > > > +	{"certificate", required_argument, NULL, 'C'},
> > > > > > +	{"monotonic-count", required_argument, NULL, 'm'},
> > > > > 
> > > > > These options should not be required.
> > > > 
> > > > I don't get you. What do you mean?
> > > > 
> > > > > > +	{"dump-sig", no_argument, NULL, 'd'},
> > > > > > +#endif
> > > > > >    	{"overlay", no_argument, NULL, 'O'},
> > > > > >    	{"help", no_argument, NULL, 'h'},
> > > > > >    	{NULL, 0, NULL, 0},
> > > > > > @@ -70,6 +93,12 @@ static void print_usage(void)
> > > > > >    	       "\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"
> > > > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> > > > > 
> > > > > see above
> > > > > 
> > > > > > +	       "\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-O, --overlay               the dtb file is an overlay\n"
> > > > > >    	       "\t-h, --help                  print a help message\n",
> > > > > >    	       tool_name);
> > > > > > @@ -249,12 +278,167 @@ err:
> > > > > >    	return ret;
> > > > > >    }
> > > > > > 
> > > > > > +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;
> > > > > > +
> > > > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> > > > > 
> > > > > see above
> > > > > 
> > > > > > +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);
> > > > > 
> > > > > Please, you use fprintf(stderr,) for error messages.
> > > > > 
> > > > > > +		ERR_print_errors_fp(stderr);
> > > > > > +	}
> > > > > > +
> > > > > > +	return key;
> > > > > > +}
> > > > > > +
> > > > > > +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);
> > > > > 
> > > > > fprintf(stderr,)
> > > > > 
> > > > > > +		ERR_print_errors_fp(stderr);
> > > > > > +	}
> > > > > > +
> > > > > > +	return cert;
> > > > > > +}
> > > > > > +
> > > > > > +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 */
> > > > > 
> > > > > PKCS7_NOATTR is a value without any documentation in the code.
> > > > 
> > > > Nak.
> > > > Those macros are part of openssl library. See openssl/pkcs7.h.
> > > > 
> > > > > Please, replace variable names by a long text describing what it missing.
> > > > > 
> > > > > > +	flags = PKCS7_BINARY | PKCS7_DETACHED;
> > > > > 
> > > > > Those constants lack documentation in the code.
> > > > 
> > > > Nak again.
> > > > 
> > > > > > +	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;
> > > > > > +}
> > > > > > +
> > > > > > +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;
> > > > > > +}
> > > > > > +#endif
> > > > > > +
> > > > > >    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;
> > > > > > @@ -266,6 +450,7 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > > > >    	printf("\tbin: %s\n\ttype: %pUl\n", bin, guid);
> > > > > >    	printf("\tindex: %ld\n\tinstance: %ld\n", index, instance);
> > > > > >    #endif
> > > > > > +	auth_context.sig_size = 0;
> > > > > > 
> > > > > >    	g = fopen(bin, "r");
> > > > > >    	if (!g) {
> > > > > > @@ -281,11 +466,36 @@ 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 IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> > > > > 
> > > > > see above
> > > > > 
> > > > > > +	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;
> > > > > > +		}
> > > > > > +	}
> > > > > > +#endif
> > > > > > +
> > > > > >    	header.capsule_guid = efi_guid_fm_capsule;
> > > > > >    	header.header_size = sizeof(header);
> > > > > >    	/* TODO: The current implementation ignores flags */
> > > > > > @@ -294,11 +504,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;
> > > > > > @@ -307,13 +526,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;
> > > > > > @@ -323,34 +542,61 @@ 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 IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> > > > > 
> > > > > We don't want to use #if if avoidable.
> > > > 
> > > > For this specific chunk of code, we can remove #ifdef,
> > > > but we should not remove #ifdef elsewhere.
> > > > 
> > > > -Takahiro Akashi
> > > > 
> > > > 
> > > > > > +	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;
> > > > > > +		}
> > > > > >    	}
> > > > > > +#endif
> > > > > > +
> > > > > >    	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);
> > > > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> > > > > > +	if (auth_context.sig_size)
> > > > > > +		OPENSSL_free(auth_context.sig_data);
> > > > > > +#endif
> > > > > > 
> > > > > >    	return 0;
> > > > > > 
> > > > > > -err_3:
> > > > > > +err_4:
> > > > > >    	fclose(f);
> > > > > > +err_3:
> > > > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> > > > > > +	if (auth_context.sig_size)
> > > > > > +		OPENSSL_free(auth_context.sig_data);
> > > > > > +#endif
> > > > > >    err_2:
> > > > > >    	free(data);
> > > > > >    err_1:
> > > > > > @@ -359,10 +605,6 @@ err_1:
> > > > > >    	return -1;
> > > > > >    }
> > > > > > 
> > > > > > -/*
> > > > > > - * Usage:
> > > > > > - *   $ mkeficapsule -f <firmware binary> <output file>
> > > > > > - */
> > > > > >    int main(int argc, char **argv)
> > > > > >    {
> > > > > >    	char *file;
> > > > > > @@ -370,6 +612,8 @@ int main(int argc, char **argv)
> > > > > >    	char *dtb_file;
> > > > > >    	efi_guid_t *guid;
> > > > > >    	unsigned long index, instance;
> > > > > > +	uint64_t mcount;
> > > > > > +	char *privkey_file, *cert_file;
> > > > > >    	int c, idx;
> > > > > >    	int ret;
> > > > > >    	bool overlay = false;
> > > > > > @@ -380,8 +624,12 @@ int main(int argc, char **argv)
> > > > > >    	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:D:K:Oh", options, &idx);
> > > > > > +		c = getopt_long(argc, argv, opts_short, options, &idx);
> > > > > >    		if (c == -1)
> > > > > >    			break;
> > > > > > 
> > > > > > @@ -422,6 +670,28 @@ int main(int argc, char **argv)
> > > > > >    			}
> > > > > >    			dtb_file = optarg;
> > > > > >    			break;
> > > > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> > > > > 
> > > > > see above
> > > > > 
> > > > > Best regards
> > > > > 
> > > > > Heinrich
> > > > > 
> > > > > > +		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
> > > > > >    		case 'O':
> > > > > >    			overlay = true;
> > > > > >    			break;
> > > > > > @@ -431,8 +701,12 @@ int main(int argc, char **argv)
> > > > > >    		}
> > > > > >    	}
> > > > > > 
> > > > > > -	/* need a fit image file or raw image file */
> > > > > > -	if (!file && !pkey_file && !dtb_file) {
> > > > > > +	/* check necessary parameters */
> > > > > > +	if ((file && (!(optind < argc) ||
> > > > > > +		      (privkey_file && !cert_file) ||
> > > > > > +		      (!privkey_file && cert_file))) ||
> > > > > > +	    ((pkey_file && !dtb_file) ||
> > > > > > +	     (!pkey_file && dtb_file))) {
> > > > > >    		print_usage();
> > > > > >    		exit(EXIT_FAILURE);
> > > > > >    	}
> > > > > > @@ -442,12 +716,12 @@ int main(int argc, char **argv)
> > > > > >    		if (ret == -1) {
> > > > > >    			printf("Adding public key to the dtb failed\n");
> > > > > >    			exit(EXIT_FAILURE);
> > > > > > -		} else {
> > > > > > -			exit(EXIT_SUCCESS);
> > > > > >    		}
> > > > > >    	}
> > > > > > 
> > > > > > -	if (create_fwbin(argv[optind], file, guid, index, instance)
> > > > > > +	if (optind < argc &&
> > > > > > +	    create_fwbin(argv[optind], file, guid, index, instance,
> > > > > > +			 mcount, privkey_file, cert_file)
> > > > > >    			< 0) {
> > > > > >    		printf("Creating firmware capsule failed\n");
> > > > > >    		exit(EXIT_FAILURE);
> > > > > > 
> > > > > 
> > > 
> 

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

* [PATCH 1/4] tools: mkeficapsule: add firmwware image signing
  2021-05-13  5:50           ` Heinrich Schuchardt
@ 2021-05-13  6:44             ` Masami Hiramatsu
  2021-05-13  6:52               ` Heinrich Schuchardt
  2021-05-13  6:50             ` AKASHI Takahiro
  1 sibling, 1 reply; 55+ messages in thread
From: Masami Hiramatsu @ 2021-05-13  6:44 UTC (permalink / raw)
  To: u-boot

Hi Heinrich,

2021?5?13?(?) 14:50 Heinrich Schuchardt <xypron.glpk@gmx.de>:
>
> On 5/13/21 7:12 AM, Masami Hiramatsu wrote:
> > Hi Heinrich,
> >
> > 2021?5?13?(?) 13:22 Heinrich Schuchardt <xypron.glpk@gmx.de>:
> >>
> >> On 5/13/21 5:08 AM, AKASHI Takahiro wrote:
> >>> On Wed, May 12, 2021 at 10:56:41AM +0200, Heinrich Schuchardt wrote:
> >>>> On 12.05.21 06:57, AKASHI Takahiro wrote:
> >>>>> With this enhancement, mkeficapsule will be able to create a capsule
> >>>>> file with a signature which will be verified later by FMP's SetImage().
> >>>>>
> >>>>> We will have to specify addtional command parameters:
> >>>>>     -monotonic-cout <count> : monotonic count
> >>>>>     -private-key <private key file> : private key file
> >>>>>     -certificate <certificate file> : certificate file
> >>>>> Only when those parameters are given, a signature will be added
> >>>>> to a capsule file.
> >>>>>
> >>>>> Users are expected to maintain the monotonic count for each firmware
> >>>>> image.
> >>>>>
> >>>>> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> >>>>> ---
> >>>>>    tools/Makefile       |   4 +
> >>>>>    tools/mkeficapsule.c | 324 +++++++++++++++++++++++++++++++++++++++----
> >>>>>    2 files changed, 303 insertions(+), 25 deletions(-)
> >>>>>
> >>>>> diff --git a/tools/Makefile b/tools/Makefile
> >>>>> index d020c55d6644..02eae0286e20 100644
> >>>>> --- a/tools/Makefile
> >>>>> +++ b/tools/Makefile
> >>>>> @@ -231,6 +231,10 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs
> >>>>>    hostprogs-$(CONFIG_ASN1_COMPILER) += asn1_compiler
> >>>>>    HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
> >>>>>
> >>>>> +ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),)
> >>>>> +HOSTLDLIBS_mkeficapsule += \
> >>>>> +   $(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto")
> >>>>
> >>>> I don't expect any user wants to install two tool versions in parallel.
> >>>>
> >>>> The tool should always be able to add a signature.
> >>>> Adding a signature must be optional.
> >>>
> >>> It seems to me that those two statements mutually contradict.
> >>> Or do you intend to say that we should have a separate kconfig
> >>> option to enable/disable signing feature in mkeficapsule?
> >>>
> >>> If so, I can agree.
> >>>
> >>> In either way, we should have an option to turn on/off this functionality
> >>> as not all users use signed capsules.
> >>
> >> I want to have a single binary to distribute with Linux distros (e.g.
> >> Debian/Ubuntu package u-boot-tools).
> >
> > I couldn't catch your point. If so, the distros can build u-boot with
> > CONFIG_EFI_CAPSULE_AUTHENTICATE=y...
>
> Why should the tool depend on board configuration?

Yeah, at this point I agreed. I think there should be a separated CONFIG
for tools or forcibly link those libraries. (I think most people don't
mind if it requires new libraries to be built, that usually happens.)

> Who would want capsule updates without authentication?

Hm, so you think even CONFIG_EFI_CAPSULE_AUTHENTICATE is
only for development. Capsule must be signed, right?
Then, all distro should build u-boot with
CONFIG_EFI_CAPSULE_AUTHENTICATE=y, isn't it?

> > BTW, IMHO, if u-boot.bin can not find the ESL in the device tree,
> > it should skip authentication too.
>
> In this case the capsule should be rejected (if
> CONFIG_EFI_CAPSULE_AUTHENTICATE=y).

I meant U-Boot has NO key to authenticate the capsule. I think in that
case U-Boot build process must require the key (ESL) and if user doesn't
provide it, the build should fail (if it doesn't skip capsule authentication.)
Or, we have no way to update U-Boot anymore.

> > Then, user can choose whether enabling capsule authentication or not
> > by embedding ESL into their devicetree.
>
> The user shall not be able to decide anything that might hamper
> security. The U-Boot binary must dictate if a capsule is safe.

Hmm, I think the root issue is that the ESL embedding process is not
integrated into the build process yet. For the safe capsule update,
we must enable capsule authentication with keys. (unsafe one is only
for testing/development)
Moreover, the key is stored in the U-Boot itself OR, in the secure storage
outside of U-Boot (Hardware OTP or TPM/HSM are preferable.)

Thus,
CONFIG_EFI_CAPSULE_AUTHENTICATE must depend on (or select)
a new config which points the path for the ESL file (this is embedded while
the build process), OR, the platform driver provides key from its hardware
secure storage.

What would you think about this idea?

Thank you,

>
> Best regards
>
> Heinrich
>
> >
> > Thank you
> >
> >>
> >> This should allow both
> >>
> >> - create signed capsules
> >> - create unsigned capsules
> >>
> >> The user shall select signing via command line parameters.
> >>
> >> Support for signing via the tool shall not depend on board Kconfig
> >> parameters.
> >>
> >> Best regards
> >>
> >> Heinrich
> >>
> >>>
> >>>>> +endif
> >>>>>    mkeficapsule-objs := mkeficapsule.o $(LIBFDT_OBJS)
> >>>>>    hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
> >>>>>
> >>>>> diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
> >>>>> index de0a62898886..34ff1bdd82eb 100644
> >>>>> --- a/tools/mkeficapsule.c
> >>>>> +++ b/tools/mkeficapsule.c
> >>>>> @@ -18,7 +18,17 @@
> >>>>>    #include <sys/stat.h>
> >>>>>    #include <sys/types.h>
> >>>>>
> >>>>> -#include "fdt_host.h"
> >>>>> +#include <linux/kconfig.h>
> >>>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> >>>>
> >>>> see above
> >>>>
> >>>>> +#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
> >>>>> +
> >>>>> +#include <linux/libfdt.h>
> >>>>>
> >>>>>    typedef __u8 u8;
> >>>>>    typedef __u16 u16;
> >>>>> @@ -46,6 +56,13 @@ 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;
> >>>>> +
> >>>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> >>>>
> >>>> see above
> >>>>
> >>>>> +static const char *opts_short = "f:r:i:I:v:D:K:P:C:m:dOh";
> >>>>> +#else
> >>>>> +static const char *opts_short = "f:r:i:I:v:D:K:Oh";
> >>>>> +#endif
> >>>>>
> >>>>>    static struct option options[] = {
> >>>>>      {"fit", required_argument, NULL, 'f'},
> >>>>> @@ -54,6 +71,12 @@ static struct option options[] = {
> >>>>>      {"instance", required_argument, NULL, 'I'},
> >>>>>      {"dtb", required_argument, NULL, 'D'},
> >>>>>      {"public key", required_argument, NULL, 'K'},
> >>>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> >>>>> +   {"private-key", required_argument, NULL, 'P'},
> >>>>> +   {"certificate", required_argument, NULL, 'C'},
> >>>>> +   {"monotonic-count", required_argument, NULL, 'm'},
> >>>>
> >>>> These options should not be required.
> >>>
> >>> I don't get you. What do you mean?
> >>>
> >>>>> +   {"dump-sig", no_argument, NULL, 'd'},
> >>>>> +#endif
> >>>>>      {"overlay", no_argument, NULL, 'O'},
> >>>>>      {"help", no_argument, NULL, 'h'},
> >>>>>      {NULL, 0, NULL, 0},
> >>>>> @@ -70,6 +93,12 @@ static void print_usage(void)
> >>>>>             "\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"
> >>>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> >>>>
> >>>> see above
> >>>>
> >>>>> +          "\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-O, --overlay               the dtb file is an overlay\n"
> >>>>>             "\t-h, --help                  print a help message\n",
> >>>>>             tool_name);
> >>>>> @@ -249,12 +278,167 @@ err:
> >>>>>      return ret;
> >>>>>    }
> >>>>>
> >>>>> +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;
> >>>>> +
> >>>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> >>>>
> >>>> see above
> >>>>
> >>>>> +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);
> >>>>
> >>>> Please, you use fprintf(stderr,) for error messages.
> >>>>
> >>>>> +           ERR_print_errors_fp(stderr);
> >>>>> +   }
> >>>>> +
> >>>>> +   return key;
> >>>>> +}
> >>>>> +
> >>>>> +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);
> >>>>
> >>>> fprintf(stderr,)
> >>>>
> >>>>> +           ERR_print_errors_fp(stderr);
> >>>>> +   }
> >>>>> +
> >>>>> +   return cert;
> >>>>> +}
> >>>>> +
> >>>>> +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 */
> >>>>
> >>>> PKCS7_NOATTR is a value without any documentation in the code.
> >>>
> >>> Nak.
> >>> Those macros are part of openssl library. See openssl/pkcs7.h.
> >>>
> >>>> Please, replace variable names by a long text describing what it missing.
> >>>>
> >>>>> +   flags = PKCS7_BINARY | PKCS7_DETACHED;
> >>>>
> >>>> Those constants lack documentation in the code.
> >>>
> >>> Nak again.
> >>>
> >>>>> +   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;
> >>>>> +}
> >>>>> +
> >>>>> +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;
> >>>>> +}
> >>>>> +#endif
> >>>>> +
> >>>>>    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;
> >>>>> @@ -266,6 +450,7 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> >>>>>      printf("\tbin: %s\n\ttype: %pUl\n", bin, guid);
> >>>>>      printf("\tindex: %ld\n\tinstance: %ld\n", index, instance);
> >>>>>    #endif
> >>>>> +   auth_context.sig_size = 0;
> >>>>>
> >>>>>      g = fopen(bin, "r");
> >>>>>      if (!g) {
> >>>>> @@ -281,11 +466,36 @@ 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 IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> >>>>
> >>>> see above
> >>>>
> >>>>> +   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;
> >>>>> +           }
> >>>>> +   }
> >>>>> +#endif
> >>>>> +
> >>>>>      header.capsule_guid = efi_guid_fm_capsule;
> >>>>>      header.header_size = sizeof(header);
> >>>>>      /* TODO: The current implementation ignores flags */
> >>>>> @@ -294,11 +504,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;
> >>>>> @@ -307,13 +526,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;
> >>>>> @@ -323,34 +542,61 @@ 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 IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> >>>>
> >>>> We don't want to use #if if avoidable.
> >>>
> >>> For this specific chunk of code, we can remove #ifdef,
> >>> but we should not remove #ifdef elsewhere.
> >>>
> >>> -Takahiro Akashi
> >>>
> >>>
> >>>>> +   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;
> >>>>> +           }
> >>>>>      }
> >>>>> +#endif
> >>>>> +
> >>>>>      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);
> >>>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> >>>>> +   if (auth_context.sig_size)
> >>>>> +           OPENSSL_free(auth_context.sig_data);
> >>>>> +#endif
> >>>>>
> >>>>>      return 0;
> >>>>>
> >>>>> -err_3:
> >>>>> +err_4:
> >>>>>      fclose(f);
> >>>>> +err_3:
> >>>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> >>>>> +   if (auth_context.sig_size)
> >>>>> +           OPENSSL_free(auth_context.sig_data);
> >>>>> +#endif
> >>>>>    err_2:
> >>>>>      free(data);
> >>>>>    err_1:
> >>>>> @@ -359,10 +605,6 @@ err_1:
> >>>>>      return -1;
> >>>>>    }
> >>>>>
> >>>>> -/*
> >>>>> - * Usage:
> >>>>> - *   $ mkeficapsule -f <firmware binary> <output file>
> >>>>> - */
> >>>>>    int main(int argc, char **argv)
> >>>>>    {
> >>>>>      char *file;
> >>>>> @@ -370,6 +612,8 @@ int main(int argc, char **argv)
> >>>>>      char *dtb_file;
> >>>>>      efi_guid_t *guid;
> >>>>>      unsigned long index, instance;
> >>>>> +   uint64_t mcount;
> >>>>> +   char *privkey_file, *cert_file;
> >>>>>      int c, idx;
> >>>>>      int ret;
> >>>>>      bool overlay = false;
> >>>>> @@ -380,8 +624,12 @@ int main(int argc, char **argv)
> >>>>>      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:D:K:Oh", options, &idx);
> >>>>> +           c = getopt_long(argc, argv, opts_short, options, &idx);
> >>>>>              if (c == -1)
> >>>>>                      break;
> >>>>>
> >>>>> @@ -422,6 +670,28 @@ int main(int argc, char **argv)
> >>>>>                      }
> >>>>>                      dtb_file = optarg;
> >>>>>                      break;
> >>>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> >>>>
> >>>> see above
> >>>>
> >>>> Best regards
> >>>>
> >>>> Heinrich
> >>>>
> >>>>> +           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
> >>>>>              case 'O':
> >>>>>                      overlay = true;
> >>>>>                      break;
> >>>>> @@ -431,8 +701,12 @@ int main(int argc, char **argv)
> >>>>>              }
> >>>>>      }
> >>>>>
> >>>>> -   /* need a fit image file or raw image file */
> >>>>> -   if (!file && !pkey_file && !dtb_file) {
> >>>>> +   /* check necessary parameters */
> >>>>> +   if ((file && (!(optind < argc) ||
> >>>>> +                 (privkey_file && !cert_file) ||
> >>>>> +                 (!privkey_file && cert_file))) ||
> >>>>> +       ((pkey_file && !dtb_file) ||
> >>>>> +        (!pkey_file && dtb_file))) {
> >>>>>              print_usage();
> >>>>>              exit(EXIT_FAILURE);
> >>>>>      }
> >>>>> @@ -442,12 +716,12 @@ int main(int argc, char **argv)
> >>>>>              if (ret == -1) {
> >>>>>                      printf("Adding public key to the dtb failed\n");
> >>>>>                      exit(EXIT_FAILURE);
> >>>>> -           } else {
> >>>>> -                   exit(EXIT_SUCCESS);
> >>>>>              }
> >>>>>      }
> >>>>>
> >>>>> -   if (create_fwbin(argv[optind], file, guid, index, instance)
> >>>>> +   if (optind < argc &&
> >>>>> +       create_fwbin(argv[optind], file, guid, index, instance,
> >>>>> +                    mcount, privkey_file, cert_file)
> >>>>>                      < 0) {
> >>>>>              printf("Creating firmware capsule failed\n");
> >>>>>              exit(EXIT_FAILURE);
> >>>>>
> >>>>
> >>
> >
> >
>


-- 
Masami Hiramatsu

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

* [PATCH 1/4] tools: mkeficapsule: add firmwware image signing
  2021-05-13  6:36             ` AKASHI Takahiro
@ 2021-05-13  6:45               ` Heinrich Schuchardt
  2021-05-13  7:45                 ` AKASHI Takahiro
  0 siblings, 1 reply; 55+ messages in thread
From: Heinrich Schuchardt @ 2021-05-13  6:45 UTC (permalink / raw)
  To: u-boot

Am 13. Mai 2021 08:36:05 MESZ schrieb AKASHI Takahiro <takahiro.akashi@linaro.org>:
>On Thu, May 13, 2021 at 07:35:36AM +0200, Heinrich Schuchardt wrote:
>> On 5/13/21 7:00 AM, AKASHI Takahiro wrote:
>> > On Thu, May 13, 2021 at 06:22:39AM +0200, Heinrich Schuchardt
>wrote:
>> > > On 5/13/21 5:08 AM, AKASHI Takahiro wrote:
>> > > > On Wed, May 12, 2021 at 10:56:41AM +0200, Heinrich Schuchardt
>wrote:
>> > > > > On 12.05.21 06:57, AKASHI Takahiro wrote:
>> > > > > > With this enhancement, mkeficapsule will be able to create
>a capsule
>> > > > > > file with a signature which will be verified later by FMP's
>SetImage().
>> > > > > > 
>> > > > > > We will have to specify addtional command parameters:
>> > > > > >     -monotonic-cout <count> : monotonic count
>> > > > > >     -private-key <private key file> : private key file
>> > > > > >     -certificate <certificate file> : certificate file
>> > > > > > Only when those parameters are given, a signature will be
>added
>> > > > > > to a capsule file.
>> > > > > > 
>> > > > > > Users are expected to maintain the monotonic count for each
>firmware
>> > > > > > image.
>> > > > > > 
>> > > > > > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
>> > > > > > ---
>> > > > > >    tools/Makefile       |   4 +
>> > > > > >    tools/mkeficapsule.c | 324
>+++++++++++++++++++++++++++++++++++++++----
>> > > > > >    2 files changed, 303 insertions(+), 25 deletions(-)
>> > > > > > 
>> > > > > > diff --git a/tools/Makefile b/tools/Makefile
>> > > > > > index d020c55d6644..02eae0286e20 100644
>> > > > > > --- a/tools/Makefile
>> > > > > > +++ b/tools/Makefile
>> > > > > > @@ -231,6 +231,10 @@ hostprogs-$(CONFIG_MIPS) +=
>mips-relocs
>> > > > > >    hostprogs-$(CONFIG_ASN1_COMPILER)	+= asn1_compiler
>> > > > > >    HOSTCFLAGS_asn1_compiler.o = -idirafter
>$(srctree)/include
>> > > > > > 
>> > > > > > +ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),)
>> > > > > > +HOSTLDLIBS_mkeficapsule += \
>> > > > > > +	$(shell pkg-config --libs libssl libcrypto 2> /dev/null
>|| echo "-lssl -lcrypto")
>> > > > > 
>> > > > > I don't expect any user wants to install two tool versions in
>parallel.
>> > > > > 
>> > > > > The tool should always be able to add a signature.
>> > > > > Adding a signature must be optional.
>> > > > 
>> > > > It seems to me that those two statements mutually contradict.
>> > > > Or do you intend to say that we should have a separate kconfig
>> > > > option to enable/disable signing feature in mkeficapsule?
>> > > > 
>> > > > If so, I can agree.
>> > > > 
>> > > > In either way, we should have an option to turn on/off this
>functionality
>> > > > as not all users use signed capsules.
>> > > 
>> > > I want to have a single binary to distribute with Linux distros
>(e.g.
>> > > Debian/Ubuntu package u-boot-tools).
>> > > 
>> > > This should allow both
>> > > 
>> > > - create signed capsules
>> > > - create unsigned capsules
>> > > 
>> > > The user shall select signing via command line parameters.
>> > > 
>> > > Support for signing via the tool shall not depend on board
>Kconfig
>> > > parameters.
>> > 
>> > That is why I proposed that we create a new kconfig option.
>> 
>> What do you want to configure? Signing shall always be enabled in
>> mkeficapsule.
>
>I don't think so.

Capsule updates without authentication should never be rolled out in production for security reasons.

>
>> > 
>> > Please note that enabling signing feature in mkeficapsule
>> > requires openssl library, and we should not enforce users who don't
>> > need this feature to install an unnecessary package.
>> 
>> Why? There are dozens of other packages depending on OpenSSL on a
>> developer's machine.
>
>We don't expect all users have openssl-related packages on their
>desktop.

We are not talking about users but developers here.

I haven't seen a Linux distro without an OpenSSL package. The package management system will pull it in when u-boot-tools is installed.

Best regards

Heinrich

>
>-Takahiro Akashi
>
>
>> Best regards
>> 
>> Heinrich
>> 
>> > 
>> > -Takahiro Akashi
>> > 
>> > > Best regards
>> > > 
>> > > Heinrich
>> > > 
>> > > > 
>> > > > > > +endif
>> > > > > >    mkeficapsule-objs	:= mkeficapsule.o $(LIBFDT_OBJS)
>> > > > > >    hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) +=
>mkeficapsule
>> > > > > > 
>> > > > > > diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
>> > > > > > index de0a62898886..34ff1bdd82eb 100644
>> > > > > > --- a/tools/mkeficapsule.c
>> > > > > > +++ b/tools/mkeficapsule.c
>> > > > > > @@ -18,7 +18,17 @@
>> > > > > >    #include <sys/stat.h>
>> > > > > >    #include <sys/types.h>
>> > > > > > 
>> > > > > > -#include "fdt_host.h"
>> > > > > > +#include <linux/kconfig.h>
>> > > > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>> > > > > 
>> > > > > see above
>> > > > > 
>> > > > > > +#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
>> > > > > > +
>> > > > > > +#include <linux/libfdt.h>
>> > > > > > 
>> > > > > >    typedef __u8 u8;
>> > > > > >    typedef __u16 u16;
>> > > > > > @@ -46,6 +56,13 @@ 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;
>> > > > > > +
>> > > > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>> > > > > 
>> > > > > see above
>> > > > > 
>> > > > > > +static const char *opts_short = "f:r:i:I:v:D:K:P:C:m:dOh";
>> > > > > > +#else
>> > > > > > +static const char *opts_short = "f:r:i:I:v:D:K:Oh";
>> > > > > > +#endif
>> > > > > > 
>> > > > > >    static struct option options[] = {
>> > > > > >    	{"fit", required_argument, NULL, 'f'},
>> > > > > > @@ -54,6 +71,12 @@ static struct option options[] = {
>> > > > > >    	{"instance", required_argument, NULL, 'I'},
>> > > > > >    	{"dtb", required_argument, NULL, 'D'},
>> > > > > >    	{"public key", required_argument, NULL, 'K'},
>> > > > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>> > > > > > +	{"private-key", required_argument, NULL, 'P'},
>> > > > > > +	{"certificate", required_argument, NULL, 'C'},
>> > > > > > +	{"monotonic-count", required_argument, NULL, 'm'},
>> > > > > 
>> > > > > These options should not be required.
>> > > > 
>> > > > I don't get you. What do you mean?
>> > > > 
>> > > > > > +	{"dump-sig", no_argument, NULL, 'd'},
>> > > > > > +#endif
>> > > > > >    	{"overlay", no_argument, NULL, 'O'},
>> > > > > >    	{"help", no_argument, NULL, 'h'},
>> > > > > >    	{NULL, 0, NULL, 0},
>> > > > > > @@ -70,6 +93,12 @@ static void print_usage(void)
>> > > > > >    	       "\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"
>> > > > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>> > > > > 
>> > > > > see above
>> > > > > 
>> > > > > > +	       "\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-O, --overlay               the dtb file is
>an overlay\n"
>> > > > > >    	       "\t-h, --help                  print a help
>message\n",
>> > > > > >    	       tool_name);
>> > > > > > @@ -249,12 +278,167 @@ err:
>> > > > > >    	return ret;
>> > > > > >    }
>> > > > > > 
>> > > > > > +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;
>> > > > > > +
>> > > > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>> > > > > 
>> > > > > see above
>> > > > > 
>> > > > > > +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);
>> > > > > 
>> > > > > Please, you use fprintf(stderr,) for error messages.
>> > > > > 
>> > > > > > +		ERR_print_errors_fp(stderr);
>> > > > > > +	}
>> > > > > > +
>> > > > > > +	return key;
>> > > > > > +}
>> > > > > > +
>> > > > > > +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);
>> > > > > 
>> > > > > fprintf(stderr,)
>> > > > > 
>> > > > > > +		ERR_print_errors_fp(stderr);
>> > > > > > +	}
>> > > > > > +
>> > > > > > +	return cert;
>> > > > > > +}
>> > > > > > +
>> > > > > > +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 */
>> > > > > 
>> > > > > PKCS7_NOATTR is a value without any documentation in the
>code.
>> > > > 
>> > > > Nak.
>> > > > Those macros are part of openssl library. See openssl/pkcs7.h.
>> > > > 
>> > > > > Please, replace variable names by a long text describing what
>it missing.
>> > > > > 
>> > > > > > +	flags = PKCS7_BINARY | PKCS7_DETACHED;
>> > > > > 
>> > > > > Those constants lack documentation in the code.
>> > > > 
>> > > > Nak again.
>> > > > 
>> > > > > > +	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;
>> > > > > > +}
>> > > > > > +
>> > > > > > +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;
>> > > > > > +}
>> > > > > > +#endif
>> > > > > > +
>> > > > > >    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;
>> > > > > > @@ -266,6 +450,7 @@ static int create_fwbin(char *path,
>char *bin, efi_guid_t *guid,
>> > > > > >    	printf("\tbin: %s\n\ttype: %pUl\n", bin, guid);
>> > > > > >    	printf("\tindex: %ld\n\tinstance: %ld\n", index,
>instance);
>> > > > > >    #endif
>> > > > > > +	auth_context.sig_size = 0;
>> > > > > > 
>> > > > > >    	g = fopen(bin, "r");
>> > > > > >    	if (!g) {
>> > > > > > @@ -281,11 +466,36 @@ 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 IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>> > > > > 
>> > > > > see above
>> > > > > 
>> > > > > > +	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;
>> > > > > > +		}
>> > > > > > +	}
>> > > > > > +#endif
>> > > > > > +
>> > > > > >    	header.capsule_guid = efi_guid_fm_capsule;
>> > > > > >    	header.header_size = sizeof(header);
>> > > > > >    	/* TODO: The current implementation ignores flags */
>> > > > > > @@ -294,11 +504,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;

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

* [PATCH 1/4] tools: mkeficapsule: add firmwware image signing
  2021-05-13  5:50           ` Heinrich Schuchardt
  2021-05-13  6:44             ` Masami Hiramatsu
@ 2021-05-13  6:50             ` AKASHI Takahiro
  2021-05-13  6:55               ` Heinrich Schuchardt
  1 sibling, 1 reply; 55+ messages in thread
From: AKASHI Takahiro @ 2021-05-13  6:50 UTC (permalink / raw)
  To: u-boot

On Thu, May 13, 2021 at 07:50:52AM +0200, Heinrich Schuchardt wrote:
> On 5/13/21 7:12 AM, Masami Hiramatsu wrote:
> > Hi Heinrich,
> > 
> > 2021?5?13?(?) 13:22 Heinrich Schuchardt <xypron.glpk@gmx.de>:
> > > 
> > > On 5/13/21 5:08 AM, AKASHI Takahiro wrote:
> > > > On Wed, May 12, 2021 at 10:56:41AM +0200, Heinrich Schuchardt wrote:
> > > > > On 12.05.21 06:57, AKASHI Takahiro wrote:
> > > > > > With this enhancement, mkeficapsule will be able to create a capsule
> > > > > > file with a signature which will be verified later by FMP's SetImage().
> > > > > > 
> > > > > > We will have to specify addtional command parameters:
> > > > > >     -monotonic-cout <count> : monotonic count
> > > > > >     -private-key <private key file> : private key file
> > > > > >     -certificate <certificate file> : certificate file
> > > > > > Only when those parameters are given, a signature will be added
> > > > > > to a capsule file.
> > > > > > 
> > > > > > Users are expected to maintain the monotonic count for each firmware
> > > > > > image.
> > > > > > 
> > > > > > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > > > > > ---
> > > > > >    tools/Makefile       |   4 +
> > > > > >    tools/mkeficapsule.c | 324 +++++++++++++++++++++++++++++++++++++++----
> > > > > >    2 files changed, 303 insertions(+), 25 deletions(-)
> > > > > > 
> > > > > > diff --git a/tools/Makefile b/tools/Makefile
> > > > > > index d020c55d6644..02eae0286e20 100644
> > > > > > --- a/tools/Makefile
> > > > > > +++ b/tools/Makefile
> > > > > > @@ -231,6 +231,10 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs
> > > > > >    hostprogs-$(CONFIG_ASN1_COMPILER) += asn1_compiler
> > > > > >    HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
> > > > > > 
> > > > > > +ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),)
> > > > > > +HOSTLDLIBS_mkeficapsule += \
> > > > > > +   $(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto")
> > > > > 
> > > > > I don't expect any user wants to install two tool versions in parallel.
> > > > > 
> > > > > The tool should always be able to add a signature.
> > > > > Adding a signature must be optional.
> > > > 
> > > > It seems to me that those two statements mutually contradict.
> > > > Or do you intend to say that we should have a separate kconfig
> > > > option to enable/disable signing feature in mkeficapsule?
> > > > 
> > > > If so, I can agree.
> > > > 
> > > > In either way, we should have an option to turn on/off this functionality
> > > > as not all users use signed capsules.
> > > 
> > > I want to have a single binary to distribute with Linux distros (e.g.
> > > Debian/Ubuntu package u-boot-tools).
> > 
> > I couldn't catch your point. If so, the distros can build u-boot with
> > CONFIG_EFI_CAPSULE_AUTHENTICATE=y...
> 
> Why should the tool depend on board configuration?
> Who would want capsule updates without authentication?

I believe that there are bunch of users who don't need authentication
on their own systems.

> > 
> > BTW, IMHO, if u-boot.bin can not find the ESL in the device tree,
> > it should skip authentication too.
> 
> In this case the capsule should be rejected (if
> CONFIG_EFI_CAPSULE_AUTHENTICATE=y).

That's basically right.
But as I mentioned in my comment against Sughosh's patch,
the authentication process will be enforced only if the capsule has
an attribute, IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED.

I believe that this flag should be able to be specified and managed
outside U-Boot configuration.
So there can be a case where FW update is performed even if
the key/certificate is not found in the device tree.

> > 
> > Then, user can choose whether enabling capsule authentication or not
> > by embedding ESL into their devicetree.

Same comment above.

-Takahiro Akashi

> The user shall not be able to decide anything that might hamper
> security. The U-Boot binary must dictate if a capsule is safe.
> 
> Best regards
> 
> Heinrich
> 
> > 
> > Thank you
> > 
> > > 
> > > This should allow both
> > > 
> > > - create signed capsules
> > > - create unsigned capsules
> > > 
> > > The user shall select signing via command line parameters.
> > > 
> > > Support for signing via the tool shall not depend on board Kconfig
> > > parameters.
> > > 
> > > Best regards
> > > 
> > > Heinrich
> > > 
> > > > 
> > > > > > +endif
> > > > > >    mkeficapsule-objs := mkeficapsule.o $(LIBFDT_OBJS)
> > > > > >    hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
> > > > > > 
> > > > > > diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
> > > > > > index de0a62898886..34ff1bdd82eb 100644
> > > > > > --- a/tools/mkeficapsule.c
> > > > > > +++ b/tools/mkeficapsule.c
> > > > > > @@ -18,7 +18,17 @@
> > > > > >    #include <sys/stat.h>
> > > > > >    #include <sys/types.h>
> > > > > > 
> > > > > > -#include "fdt_host.h"
> > > > > > +#include <linux/kconfig.h>
> > > > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> > > > > 
> > > > > see above
> > > > > 
> > > > > > +#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
> > > > > > +
> > > > > > +#include <linux/libfdt.h>
> > > > > > 
> > > > > >    typedef __u8 u8;
> > > > > >    typedef __u16 u16;
> > > > > > @@ -46,6 +56,13 @@ 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;
> > > > > > +
> > > > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> > > > > 
> > > > > see above
> > > > > 
> > > > > > +static const char *opts_short = "f:r:i:I:v:D:K:P:C:m:dOh";
> > > > > > +#else
> > > > > > +static const char *opts_short = "f:r:i:I:v:D:K:Oh";
> > > > > > +#endif
> > > > > > 
> > > > > >    static struct option options[] = {
> > > > > >      {"fit", required_argument, NULL, 'f'},
> > > > > > @@ -54,6 +71,12 @@ static struct option options[] = {
> > > > > >      {"instance", required_argument, NULL, 'I'},
> > > > > >      {"dtb", required_argument, NULL, 'D'},
> > > > > >      {"public key", required_argument, NULL, 'K'},
> > > > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> > > > > > +   {"private-key", required_argument, NULL, 'P'},
> > > > > > +   {"certificate", required_argument, NULL, 'C'},
> > > > > > +   {"monotonic-count", required_argument, NULL, 'm'},
> > > > > 
> > > > > These options should not be required.
> > > > 
> > > > I don't get you. What do you mean?
> > > > 
> > > > > > +   {"dump-sig", no_argument, NULL, 'd'},
> > > > > > +#endif
> > > > > >      {"overlay", no_argument, NULL, 'O'},
> > > > > >      {"help", no_argument, NULL, 'h'},
> > > > > >      {NULL, 0, NULL, 0},
> > > > > > @@ -70,6 +93,12 @@ static void print_usage(void)
> > > > > >             "\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"
> > > > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> > > > > 
> > > > > see above
> > > > > 
> > > > > > +          "\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-O, --overlay               the dtb file is an overlay\n"
> > > > > >             "\t-h, --help                  print a help message\n",
> > > > > >             tool_name);
> > > > > > @@ -249,12 +278,167 @@ err:
> > > > > >      return ret;
> > > > > >    }
> > > > > > 
> > > > > > +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;
> > > > > > +
> > > > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> > > > > 
> > > > > see above
> > > > > 
> > > > > > +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);
> > > > > 
> > > > > Please, you use fprintf(stderr,) for error messages.
> > > > > 
> > > > > > +           ERR_print_errors_fp(stderr);
> > > > > > +   }
> > > > > > +
> > > > > > +   return key;
> > > > > > +}
> > > > > > +
> > > > > > +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);
> > > > > 
> > > > > fprintf(stderr,)
> > > > > 
> > > > > > +           ERR_print_errors_fp(stderr);
> > > > > > +   }
> > > > > > +
> > > > > > +   return cert;
> > > > > > +}
> > > > > > +
> > > > > > +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 */
> > > > > 
> > > > > PKCS7_NOATTR is a value without any documentation in the code.
> > > > 
> > > > Nak.
> > > > Those macros are part of openssl library. See openssl/pkcs7.h.
> > > > 
> > > > > Please, replace variable names by a long text describing what it missing.
> > > > > 
> > > > > > +   flags = PKCS7_BINARY | PKCS7_DETACHED;
> > > > > 
> > > > > Those constants lack documentation in the code.
> > > > 
> > > > Nak again.
> > > > 
> > > > > > +   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;
> > > > > > +}
> > > > > > +
> > > > > > +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;
> > > > > > +}
> > > > > > +#endif
> > > > > > +
> > > > > >    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;
> > > > > > @@ -266,6 +450,7 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > > > > >      printf("\tbin: %s\n\ttype: %pUl\n", bin, guid);
> > > > > >      printf("\tindex: %ld\n\tinstance: %ld\n", index, instance);
> > > > > >    #endif
> > > > > > +   auth_context.sig_size = 0;
> > > > > > 
> > > > > >      g = fopen(bin, "r");
> > > > > >      if (!g) {
> > > > > > @@ -281,11 +466,36 @@ 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 IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> > > > > 
> > > > > see above
> > > > > 
> > > > > > +   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;
> > > > > > +           }
> > > > > > +   }
> > > > > > +#endif
> > > > > > +
> > > > > >      header.capsule_guid = efi_guid_fm_capsule;
> > > > > >      header.header_size = sizeof(header);
> > > > > >      /* TODO: The current implementation ignores flags */
> > > > > > @@ -294,11 +504,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;
> > > > > > @@ -307,13 +526,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;
> > > > > > @@ -323,34 +542,61 @@ 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 IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> > > > > 
> > > > > We don't want to use #if if avoidable.
> > > > 
> > > > For this specific chunk of code, we can remove #ifdef,
> > > > but we should not remove #ifdef elsewhere.
> > > > 
> > > > -Takahiro Akashi
> > > > 
> > > > 
> > > > > > +   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;
> > > > > > +           }
> > > > > >      }
> > > > > > +#endif
> > > > > > +
> > > > > >      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);
> > > > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> > > > > > +   if (auth_context.sig_size)
> > > > > > +           OPENSSL_free(auth_context.sig_data);
> > > > > > +#endif
> > > > > > 
> > > > > >      return 0;
> > > > > > 
> > > > > > -err_3:
> > > > > > +err_4:
> > > > > >      fclose(f);
> > > > > > +err_3:
> > > > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> > > > > > +   if (auth_context.sig_size)
> > > > > > +           OPENSSL_free(auth_context.sig_data);
> > > > > > +#endif
> > > > > >    err_2:
> > > > > >      free(data);
> > > > > >    err_1:
> > > > > > @@ -359,10 +605,6 @@ err_1:
> > > > > >      return -1;
> > > > > >    }
> > > > > > 
> > > > > > -/*
> > > > > > - * Usage:
> > > > > > - *   $ mkeficapsule -f <firmware binary> <output file>
> > > > > > - */
> > > > > >    int main(int argc, char **argv)
> > > > > >    {
> > > > > >      char *file;
> > > > > > @@ -370,6 +612,8 @@ int main(int argc, char **argv)
> > > > > >      char *dtb_file;
> > > > > >      efi_guid_t *guid;
> > > > > >      unsigned long index, instance;
> > > > > > +   uint64_t mcount;
> > > > > > +   char *privkey_file, *cert_file;
> > > > > >      int c, idx;
> > > > > >      int ret;
> > > > > >      bool overlay = false;
> > > > > > @@ -380,8 +624,12 @@ int main(int argc, char **argv)
> > > > > >      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:D:K:Oh", options, &idx);
> > > > > > +           c = getopt_long(argc, argv, opts_short, options, &idx);
> > > > > >              if (c == -1)
> > > > > >                      break;
> > > > > > 
> > > > > > @@ -422,6 +670,28 @@ int main(int argc, char **argv)
> > > > > >                      }
> > > > > >                      dtb_file = optarg;
> > > > > >                      break;
> > > > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> > > > > 
> > > > > see above
> > > > > 
> > > > > Best regards
> > > > > 
> > > > > Heinrich
> > > > > 
> > > > > > +           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
> > > > > >              case 'O':
> > > > > >                      overlay = true;
> > > > > >                      break;
> > > > > > @@ -431,8 +701,12 @@ int main(int argc, char **argv)
> > > > > >              }
> > > > > >      }
> > > > > > 
> > > > > > -   /* need a fit image file or raw image file */
> > > > > > -   if (!file && !pkey_file && !dtb_file) {
> > > > > > +   /* check necessary parameters */
> > > > > > +   if ((file && (!(optind < argc) ||
> > > > > > +                 (privkey_file && !cert_file) ||
> > > > > > +                 (!privkey_file && cert_file))) ||
> > > > > > +       ((pkey_file && !dtb_file) ||
> > > > > > +        (!pkey_file && dtb_file))) {
> > > > > >              print_usage();
> > > > > >              exit(EXIT_FAILURE);
> > > > > >      }
> > > > > > @@ -442,12 +716,12 @@ int main(int argc, char **argv)
> > > > > >              if (ret == -1) {
> > > > > >                      printf("Adding public key to the dtb failed\n");
> > > > > >                      exit(EXIT_FAILURE);
> > > > > > -           } else {
> > > > > > -                   exit(EXIT_SUCCESS);
> > > > > >              }
> > > > > >      }
> > > > > > 
> > > > > > -   if (create_fwbin(argv[optind], file, guid, index, instance)
> > > > > > +   if (optind < argc &&
> > > > > > +       create_fwbin(argv[optind], file, guid, index, instance,
> > > > > > +                    mcount, privkey_file, cert_file)
> > > > > >                      < 0) {
> > > > > >              printf("Creating firmware capsule failed\n");
> > > > > >              exit(EXIT_FAILURE);
> > > > > > 
> > > > > 
> > > 
> > 
> > 
> 

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

* [PATCH 1/4] tools: mkeficapsule: add firmwware image signing
  2021-05-13  6:44             ` Masami Hiramatsu
@ 2021-05-13  6:52               ` Heinrich Schuchardt
  2021-05-13  7:38                 ` AKASHI Takahiro
  0 siblings, 1 reply; 55+ messages in thread
From: Heinrich Schuchardt @ 2021-05-13  6:52 UTC (permalink / raw)
  To: u-boot

Am 13. Mai 2021 08:44:24 MESZ schrieb Masami Hiramatsu <masami.hiramatsu@linaro.org>:
>Hi Heinrich,
>
>2021?5?13?(?) 14:50 Heinrich Schuchardt <xypron.glpk@gmx.de>:
>>
>> On 5/13/21 7:12 AM, Masami Hiramatsu wrote:
>> > Hi Heinrich,
>> >
>> > 2021?5?13?(?) 13:22 Heinrich Schuchardt <xypron.glpk@gmx.de>:
>> >>
>> >> On 5/13/21 5:08 AM, AKASHI Takahiro wrote:
>> >>> On Wed, May 12, 2021 at 10:56:41AM +0200, Heinrich Schuchardt
>wrote:
>> >>>> On 12.05.21 06:57, AKASHI Takahiro wrote:
>> >>>>> With this enhancement, mkeficapsule will be able to create a
>capsule
>> >>>>> file with a signature which will be verified later by FMP's
>SetImage().
>> >>>>>
>> >>>>> We will have to specify addtional command parameters:
>> >>>>>     -monotonic-cout <count> : monotonic count
>> >>>>>     -private-key <private key file> : private key file
>> >>>>>     -certificate <certificate file> : certificate file
>> >>>>> Only when those parameters are given, a signature will be added
>> >>>>> to a capsule file.
>> >>>>>
>> >>>>> Users are expected to maintain the monotonic count for each
>firmware
>> >>>>> image.
>> >>>>>
>> >>>>> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
>> >>>>> ---
>> >>>>>    tools/Makefile       |   4 +
>> >>>>>    tools/mkeficapsule.c | 324
>+++++++++++++++++++++++++++++++++++++++----
>> >>>>>    2 files changed, 303 insertions(+), 25 deletions(-)
>> >>>>>
>> >>>>> diff --git a/tools/Makefile b/tools/Makefile
>> >>>>> index d020c55d6644..02eae0286e20 100644
>> >>>>> --- a/tools/Makefile
>> >>>>> +++ b/tools/Makefile
>> >>>>> @@ -231,6 +231,10 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs
>> >>>>>    hostprogs-$(CONFIG_ASN1_COMPILER) += asn1_compiler
>> >>>>>    HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
>> >>>>>
>> >>>>> +ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),)
>> >>>>> +HOSTLDLIBS_mkeficapsule += \
>> >>>>> +   $(shell pkg-config --libs libssl libcrypto 2> /dev/null ||
>echo "-lssl -lcrypto")
>> >>>>
>> >>>> I don't expect any user wants to install two tool versions in
>parallel.
>> >>>>
>> >>>> The tool should always be able to add a signature.
>> >>>> Adding a signature must be optional.
>> >>>
>> >>> It seems to me that those two statements mutually contradict.
>> >>> Or do you intend to say that we should have a separate kconfig
>> >>> option to enable/disable signing feature in mkeficapsule?
>> >>>
>> >>> If so, I can agree.
>> >>>
>> >>> In either way, we should have an option to turn on/off this
>functionality
>> >>> as not all users use signed capsules.
>> >>
>> >> I want to have a single binary to distribute with Linux distros
>(e.g.
>> >> Debian/Ubuntu package u-boot-tools).
>> >
>> > I couldn't catch your point. If so, the distros can build u-boot
>with
>> > CONFIG_EFI_CAPSULE_AUTHENTICATE=y...
>>
>> Why should the tool depend on board configuration?
>
>Yeah, at this point I agreed. I think there should be a separated
>CONFIG
>for tools or forcibly link those libraries. (I think most people don't
>mind if it requires new libraries to be built, that usually happens.)
>
>> Who would want capsule updates without authentication?
>
>Hm, so you think even CONFIG_EFI_CAPSULE_AUTHENTICATE is
>only for development. Capsule must be signed, right?
>Then, all distro should build u-boot with
>CONFIG_EFI_CAPSULE_AUTHENTICATE=y, isn't it?

There will still be U-Boot images without capsule updates.

>
>> > BTW, IMHO, if u-boot.bin can not find the ESL in the device tree,
>> > it should skip authentication too.
>>
>> In this case the capsule should be rejected (if
>> CONFIG_EFI_CAPSULE_AUTHENTICATE=y).
>
>I meant U-Boot has NO key to authenticate the capsule. I think in that
>case U-Boot build process must require the key (ESL) and if user
>doesn't
>provide it, the build should fail (if it doesn't skip capsule
>authentication.)
>Or, we have no way to update U-Boot anymore.
>
>> > Then, user can choose whether enabling capsule authentication or
>not
>> > by embedding ESL into their devicetree.
>>
>> The user shall not be able to decide anything that might hamper
>> security. The U-Boot binary must dictate if a capsule is safe.
>
>Hmm, I think the root issue is that the ESL embedding process is not
>integrated into the build process yet. For the safe capsule update,
>we must enable capsule authentication with keys. (unsafe one is only
>for testing/development)
>Moreover, the key is stored in the U-Boot itself OR, in the secure
>storage
>outside of U-Boot (Hardware OTP or TPM/HSM are preferable.)
>
>Thus,
>CONFIG_EFI_CAPSULE_AUTHENTICATE must depend on (or select)
>a new config which points the path for the ESL file (this is embedded
>while
>the build process), OR, the platform driver provides key from its
>hardware
>secure storage.
>
>What would you think about this idea?

That is the direction I would like to go.

Best regards

Heinrich

>
>Thank you,
>
>>
>> Best regards
>>
>> Heinrich
>>
>> >
>> > Thank you
>> >
>> >>
>> >> This should allow both
>> >>
>> >> - create signed capsules
>> >> - create unsigned capsules
>> >>
>> >> The user shall select signing via command line parameters.
>> >>
>> >> Support for signing via the tool shall not depend on board Kconfig
>> >> parameters.
>> >>
>> >> Best regards
>> >>
>> >> Heinrich
>> >>
>> >>>
>> >>>>> +endif
>> >>>>>    mkeficapsule-objs := mkeficapsule.o $(LIBFDT_OBJS)
>> >>>>>    hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
>> >>>>>
>> >>>>> diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
>> >>>>> index de0a62898886..34ff1bdd82eb 100644
>> >>>>> --- a/tools/mkeficapsule.c
>> >>>>> +++ b/tools/mkeficapsule.c
>> >>>>> @@ -18,7 +18,17 @@
>> >>>>>    #include <sys/stat.h>
>> >>>>>    #include <sys/types.h>
>> >>>>>
>> >>>>> -#include "fdt_host.h"
>> >>>>> +#include <linux/kconfig.h>
>> >>>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>> >>>>
>> >>>> see above
>> >>>>
>> >>>>> +#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
>> >>>>> +
>> >>>>> +#include <linux/libfdt.h>
>> >>>>>
>> >>>>>    typedef __u8 u8;
>> >>>>>    typedef __u16 u16;
>> >>>>> @@ -46,6 +56,13 @@ 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;
>> >>>>> +
>> >>>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>> >>>>
>> >>>> see above
>> >>>>
>> >>>>> +static const char *opts_short = "f:r:i:I:v:D:K:P:C:m:dOh";
>> >>>>> +#else
>> >>>>> +static const char *opts_short = "f:r:i:I:v:D:K:Oh";
>> >>>>> +#endif
>> >>>>>
>> >>>>>    static struct option options[] = {
>> >>>>>      {"fit", required_argument, NULL, 'f'},
>> >>>>> @@ -54,6 +71,12 @@ static struct option options[] = {
>> >>>>>      {"instance", required_argument, NULL, 'I'},
>> >>>>>      {"dtb", required_argument, NULL, 'D'},
>> >>>>>      {"public key", required_argument, NULL, 'K'},
>> >>>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>> >>>>> +   {"private-key", required_argument, NULL, 'P'},
>> >>>>> +   {"certificate", required_argument, NULL, 'C'},
>> >>>>> +   {"monotonic-count", required_argument, NULL, 'm'},
>> >>>>
>> >>>> These options should not be required.
>> >>>
>> >>> I don't get you. What do you mean?
>> >>>
>> >>>>> +   {"dump-sig", no_argument, NULL, 'd'},
>> >>>>> +#endif
>> >>>>>      {"overlay", no_argument, NULL, 'O'},
>> >>>>>      {"help", no_argument, NULL, 'h'},
>> >>>>>      {NULL, 0, NULL, 0},
>> >>>>> @@ -70,6 +93,12 @@ static void print_usage(void)
>> >>>>>             "\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"
>> >>>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>> >>>>
>> >>>> see above
>> >>>>
>> >>>>> +          "\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-O, --overlay               the dtb file is an
>overlay\n"
>> >>>>>             "\t-h, --help                  print a help
>message\n",
>> >>>>>             tool_name);
>> >>>>> @@ -249,12 +278,167 @@ err:
>> >>>>>      return ret;
>> >>>>>    }
>> >>>>>
>> >>>>> +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;
>> >>>>> +
>> >>>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>> >>>>
>> >>>> see above
>> >>>>
>> >>>>> +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);
>> >>>>
>> >>>> Please, you use fprintf(stderr,) for error messages.
>> >>>>
>> >>>>> +           ERR_print_errors_fp(stderr);
>> >>>>> +   }
>> >>>>> +
>> >>>>> +   return key;
>> >>>>> +}
>> >>>>> +
>> >>>>> +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);
>> >>>>
>> >>>> fprintf(stderr,)
>> >>>>
>> >>>>> +           ERR_print_errors_fp(stderr);
>> >>>>> +   }
>> >>>>> +
>> >>>>> +   return cert;
>> >>>>> +}
>> >>>>> +
>> >>>>> +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 */
>> >>>>
>> >>>> PKCS7_NOATTR is a value without any documentation in the code.
>> >>>
>> >>> Nak.
>> >>> Those macros are part of openssl library. See openssl/pkcs7.h.
>> >>>
>> >>>> Please, replace variable names by a long text describing what it
>missing.
>> >>>>
>> >>>>> +   flags = PKCS7_BINARY | PKCS7_DETACHED;
>> >>>>
>> >>>> Those constants lack documentation in the code.
>> >>>
>> >>> Nak again.
>> >>>
>> >>>>> +   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;
>> >>>>> +}
>> >>>>> +
>> >>>>> +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;
>> >>>>> +}
>> >>>>> +#endif
>> >>>>> +
>> >>>>>    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;
>> >>>>> @@ -266,6 +450,7 @@ static int create_fwbin(char *path, char
>*bin, efi_guid_t *guid,
>> >>>>>      printf("\tbin: %s\n\ttype: %pUl\n", bin, guid);

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

* [PATCH 1/4] tools: mkeficapsule: add firmwware image signing
  2021-05-13  6:50             ` AKASHI Takahiro
@ 2021-05-13  6:55               ` Heinrich Schuchardt
  2021-05-13  7:23                 ` AKASHI Takahiro
  0 siblings, 1 reply; 55+ messages in thread
From: Heinrich Schuchardt @ 2021-05-13  6:55 UTC (permalink / raw)
  To: u-boot

Am 13. Mai 2021 08:50:54 MESZ schrieb AKASHI Takahiro <takahiro.akashi@linaro.org>:
>On Thu, May 13, 2021 at 07:50:52AM +0200, Heinrich Schuchardt wrote:
>> On 5/13/21 7:12 AM, Masami Hiramatsu wrote:
>> > Hi Heinrich,
>> > 
>> > 2021?5?13?(?) 13:22 Heinrich Schuchardt <xypron.glpk@gmx.de>:
>> > > 
>> > > On 5/13/21 5:08 AM, AKASHI Takahiro wrote:
>> > > > On Wed, May 12, 2021 at 10:56:41AM +0200, Heinrich Schuchardt
>wrote:
>> > > > > On 12.05.21 06:57, AKASHI Takahiro wrote:
>> > > > > > With this enhancement, mkeficapsule will be able to create
>a capsule
>> > > > > > file with a signature which will be verified later by FMP's
>SetImage().
>> > > > > > 
>> > > > > > We will have to specify addtional command parameters:
>> > > > > >     -monotonic-cout <count> : monotonic count
>> > > > > >     -private-key <private key file> : private key file
>> > > > > >     -certificate <certificate file> : certificate file
>> > > > > > Only when those parameters are given, a signature will be
>added
>> > > > > > to a capsule file.
>> > > > > > 
>> > > > > > Users are expected to maintain the monotonic count for each
>firmware
>> > > > > > image.
>> > > > > > 
>> > > > > > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
>> > > > > > ---
>> > > > > >    tools/Makefile       |   4 +
>> > > > > >    tools/mkeficapsule.c | 324
>+++++++++++++++++++++++++++++++++++++++----
>> > > > > >    2 files changed, 303 insertions(+), 25 deletions(-)
>> > > > > > 
>> > > > > > diff --git a/tools/Makefile b/tools/Makefile
>> > > > > > index d020c55d6644..02eae0286e20 100644
>> > > > > > --- a/tools/Makefile
>> > > > > > +++ b/tools/Makefile
>> > > > > > @@ -231,6 +231,10 @@ hostprogs-$(CONFIG_MIPS) +=
>mips-relocs
>> > > > > >    hostprogs-$(CONFIG_ASN1_COMPILER) += asn1_compiler
>> > > > > >    HOSTCFLAGS_asn1_compiler.o = -idirafter
>$(srctree)/include
>> > > > > > 
>> > > > > > +ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),)
>> > > > > > +HOSTLDLIBS_mkeficapsule += \
>> > > > > > +   $(shell pkg-config --libs libssl libcrypto 2> /dev/null
>|| echo "-lssl -lcrypto")
>> > > > > 
>> > > > > I don't expect any user wants to install two tool versions in
>parallel.
>> > > > > 
>> > > > > The tool should always be able to add a signature.
>> > > > > Adding a signature must be optional.
>> > > > 
>> > > > It seems to me that those two statements mutually contradict.
>> > > > Or do you intend to say that we should have a separate kconfig
>> > > > option to enable/disable signing feature in mkeficapsule?
>> > > > 
>> > > > If so, I can agree.
>> > > > 
>> > > > In either way, we should have an option to turn on/off this
>functionality
>> > > > as not all users use signed capsules.
>> > > 
>> > > I want to have a single binary to distribute with Linux distros
>(e.g.
>> > > Debian/Ubuntu package u-boot-tools).
>> > 
>> > I couldn't catch your point. If so, the distros can build u-boot
>with
>> > CONFIG_EFI_CAPSULE_AUTHENTICATE=y...
>> 
>> Why should the tool depend on board configuration?
>> Who would want capsule updates without authentication?
>
>I believe that there are bunch of users who don't need authentication
>on their own systems.
>

They should think again.

>> > 
>> > BTW, IMHO, if u-boot.bin can not find the ESL in the device tree,
>> > it should skip authentication too.
>> 
>> In this case the capsule should be rejected (if
>> CONFIG_EFI_CAPSULE_AUTHENTICATE=y).
>
>That's basically right.
>But as I mentioned in my comment against Sughosh's patch,
>the authentication process will be enforced only if the capsule has
>an attribute, IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED.
>

That would be a security desaster.

Best regards

Heinrich

>I believe that this flag should be able to be specified and managed
>outside U-Boot configuration.
>So there can be a case where FW update is performed even if
>the key/certificate is not found in the device tree.
>
>> > 
>> > Then, user can choose whether enabling capsule authentication or
>not
>> > by embedding ESL into their devicetree.
>
>Same comment above.
>
>-Takahiro Akashi
>
>> The user shall not be able to decide anything that might hamper
>> security. The U-Boot binary must dictate if a capsule is safe.
>> 
>> Best regards
>> 
>> Heinrich
>> 
>> > 
>> > Thank you
>> > 
>> > > 
>> > > This should allow both
>> > > 
>> > > - create signed capsules
>> > > - create unsigned capsules
>> > > 
>> > > The user shall select signing via command line parameters.
>> > > 
>> > > Support for signing via the tool shall not depend on board
>Kconfig
>> > > parameters.
>> > > 
>> > > Best regards
>> > > 
>> > > Heinrich
>> > > 
>> > > > 
>> > > > > > +endif
>> > > > > >    mkeficapsule-objs := mkeficapsule.o $(LIBFDT_OBJS)
>> > > > > >    hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) +=
>mkeficapsule
>> > > > > > 
>> > > > > > diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
>> > > > > > index de0a62898886..34ff1bdd82eb 100644
>> > > > > > --- a/tools/mkeficapsule.c
>> > > > > > +++ b/tools/mkeficapsule.c
>> > > > > > @@ -18,7 +18,17 @@
>> > > > > >    #include <sys/stat.h>
>> > > > > >    #include <sys/types.h>
>> > > > > > 
>> > > > > > -#include "fdt_host.h"
>> > > > > > +#include <linux/kconfig.h>
>> > > > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>> > > > > 
>> > > > > see above
>> > > > > 
>> > > > > > +#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
>> > > > > > +
>> > > > > > +#include <linux/libfdt.h>
>> > > > > > 
>> > > > > >    typedef __u8 u8;
>> > > > > >    typedef __u16 u16;
>> > > > > > @@ -46,6 +56,13 @@ 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;
>> > > > > > +
>> > > > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>> > > > > 
>> > > > > see above
>> > > > > 
>> > > > > > +static const char *opts_short = "f:r:i:I:v:D:K:P:C:m:dOh";
>> > > > > > +#else
>> > > > > > +static const char *opts_short = "f:r:i:I:v:D:K:Oh";
>> > > > > > +#endif
>> > > > > > 
>> > > > > >    static struct option options[] = {
>> > > > > >      {"fit", required_argument, NULL, 'f'},
>> > > > > > @@ -54,6 +71,12 @@ static struct option options[] = {
>> > > > > >      {"instance", required_argument, NULL, 'I'},
>> > > > > >      {"dtb", required_argument, NULL, 'D'},
>> > > > > >      {"public key", required_argument, NULL, 'K'},
>> > > > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>> > > > > > +   {"private-key", required_argument, NULL, 'P'},
>> > > > > > +   {"certificate", required_argument, NULL, 'C'},
>> > > > > > +   {"monotonic-count", required_argument, NULL, 'm'},
>> > > > > 
>> > > > > These options should not be required.
>> > > > 
>> > > > I don't get you. What do you mean?
>> > > > 
>> > > > > > +   {"dump-sig", no_argument, NULL, 'd'},
>> > > > > > +#endif
>> > > > > >      {"overlay", no_argument, NULL, 'O'},
>> > > > > >      {"help", no_argument, NULL, 'h'},
>> > > > > >      {NULL, 0, NULL, 0},
>> > > > > > @@ -70,6 +93,12 @@ static void print_usage(void)
>> > > > > >             "\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"
>> > > > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>> > > > > 
>> > > > > see above
>> > > > > 
>> > > > > > +          "\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-O, --overlay               the dtb file is
>an overlay\n"
>> > > > > >             "\t-h, --help                  print a help
>message\n",
>> > > > > >             tool_name);
>> > > > > > @@ -249,12 +278,167 @@ err:
>> > > > > >      return ret;
>> > > > > >    }
>> > > > > > 
>> > > > > > +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;
>> > > > > > +
>> > > > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>> > > > > 
>> > > > > see above
>> > > > > 
>> > > > > > +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);
>> > > > > 
>> > > > > Please, you use fprintf(stderr,) for error messages.
>> > > > > 
>> > > > > > +           ERR_print_errors_fp(stderr);
>> > > > > > +   }
>> > > > > > +
>> > > > > > +   return key;
>> > > > > > +}
>> > > > > > +
>> > > > > > +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);
>> > > > > 
>> > > > > fprintf(stderr,)
>> > > > > 
>> > > > > > +           ERR_print_errors_fp(stderr);
>> > > > > > +   }
>> > > > > > +
>> > > > > > +   return cert;
>> > > > > > +}
>> > > > > > +
>> > > > > > +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 */
>> > > > > 
>> > > > > PKCS7_NOATTR is a value without any documentation in the
>code.
>> > > > 
>> > > > Nak.
>> > > > Those macros are part of openssl library. See openssl/pkcs7.h.
>> > > > 
>> > > > > Please, replace variable names by a long text describing what
>it missing.
>> > > > > 
>> > > > > > +   flags = PKCS7_BINARY | PKCS7_DETACHED;
>> > > > > 
>> > > > > Those constants lack documentation in the code.
>> > > > 
>> > > > Nak again.
>> > > > 
>> > > > > > +   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;
>> > > > > > +}
>> > > > > > +
>> > > > > > +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;
>> > > > > > +}
>> > > > > > +#endif
>> > > > > > +
>> > > > > >    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;
>> > > > > > @@ -266,6 +450,7 @@ static int create_fwbin(char *path,
>char *bin, efi_guid_t *guid,
>> > > > > >      printf("\tbin: %s\n\ttype: %pUl\n", bin, guid);
>> > > > > >      printf("\tindex: %ld\n\tinstance: %ld\n", index,
>instance);
>> > > > > >    #endif
>> > > > > > +   auth_context.sig_size = 0;
>> > > > > > 
>> > > > > >      g = fopen(bin, "r");
>> > > > > >      if (!g) {
>> > > > > > @@ -281,11 +466,36 @@ 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 IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
>> > > > > 
>> > > > > see above
>> > > > > 
>> > > > > > +   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)) {

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

* [PATCH 3/4] tools: add fdtsig command
  2021-05-13  5:23   ` Heinrich Schuchardt
@ 2021-05-13  7:03     ` AKASHI Takahiro
  0 siblings, 0 replies; 55+ messages in thread
From: AKASHI Takahiro @ 2021-05-13  7:03 UTC (permalink / raw)
  To: u-boot

On Thu, May 13, 2021 at 07:23:43AM +0200, Heinrich Schuchardt wrote:
> On 5/12/21 6:57 AM, AKASHI Takahiro wrote:
> > This command allows us to add a certificate (or public key) to dtb blob:
> > {
> > 	signature {
> > 		capsule-key = "...";
> > 	};
> > }
> > 
> > The value is actually a signature list in terms of UEFI specificaion,
> > and used in verifying UEFI capsules.
> > 
> > The code was originally developed by Sughosh and derived from
> > mkeficapsule.c.
> 
> I have no clue how you want to use this command. Please, provide a
> description of the workflow using this command.

Please read my patch carefully, in particular, Makefile.
In this patch, fdtsig.sh, which is introduced in Patch#2, is
replaced with fdtsig (C version).

I think that the workflow is trivial.

> In your comments for patch 2 you wrote that the -K and -D option can be
> replaced by tools provided by the device-tree compiler package. Why do
> they reappear here?

?
As I said, I don't intend to ask you to merge this patch.

In the cover letter,
! NOTE:
! I temporarily include Patch#3 in order to show that it is not worth
! implementing in C as we can do the same thing with a very small
! shell script.

Patch #1, #2 and #4 are just fine.

-Takahiro Akashi


> Please, add a man-page (/doc/fdtsig.1).
> 
> 
> > 
> > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > ---
> >   Makefile       |   2 +-
> >   tools/Makefile |   2 +
> >   tools/fdtsig.c | 274 +++++++++++++++++++++++++++++++++++++++++++++++++
> >   3 files changed, 277 insertions(+), 1 deletion(-)
> >   create mode 100644 tools/fdtsig.c
> > 
> > diff --git a/Makefile b/Makefile
> > index 9806464357e0..8b40987234a0 100644
> > --- a/Makefile
> > +++ b/Makefile
> > @@ -1016,7 +1016,7 @@ quiet_cmd_lzma = LZMA    $@
> >   cmd_lzma = lzma -c -z -k -9 $< > $@
> > 
> >   quiet_cmd_fdtsig = FDTSIG  $@
> > -cmd_fdtsig = $(srctree)/tools/fdtsig.sh $(CONFIG_EFI_PKEY_FILE) $@
> > +cmd_fdtsig = $(objtree)/tools/fdtsig -K $(CONFIG_EFI_PKEY_FILE) -D $@
> > 
> >   cfg: u-boot.cfg
> > 
> > diff --git a/tools/Makefile b/tools/Makefile
> > index 71a52719620c..e6fd1dbade19 100644
> > --- a/tools/Makefile
> > +++ b/tools/Makefile
> > @@ -234,6 +234,8 @@ HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
> >   ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),)
> >   HOSTLDLIBS_mkeficapsule += \
> >   	$(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto")
> > +	fdtsig-objs	:= fdtsig.o $(LIBFDT_OBJS)
> > +	hostprogs-y += fdtsig
> >   endif
> >   hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
> > 
> > diff --git a/tools/fdtsig.c b/tools/fdtsig.c
> > new file mode 100644
> > index 000000000000..daa1e63c3b33
> > --- /dev/null
> > +++ b/tools/fdtsig.c
> > @@ -0,0 +1,274 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright 2021 Linaro Limited
> > + * The code in this file was extracted from mkeficapsule.c
> 
> That information is better described by the git log.
> 
> Please, explain here what this tool is used for.
> 
> Best regards
> 
> Heinrich
> 
> > + */
> > +
> > +#include <errno.h>
> > +#include <fcntl.h>
> > +#include <getopt.h>
> > +#include <malloc.h>
> > +#include <stdbool.h>
> > +#include <stdio.h>
> > +#include <unistd.h>
> > +
> > +#include <sys/mman.h>
> > +#include <sys/stat.h>
> > +
> > +#include <linux/libfdt.h>
> > +
> > +#define SIGNATURE_NODENAME	"signature"
> > +#define OVERLAY_NODENAME	"__overlay__"
> > +
> > +static const char *tool_name = "fdtsig";
> > +
> > +static const char *opts_short = "D:K:Oh";
> > +
> > +static struct option options[] = {
> > +	{"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},
> > +};
> > +
> > +static void print_usage(void)
> > +{
> > +	printf("Usage: %s [options]\n"
> > +	       "Options:\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;
> > +}
> > +
> > +int main(int argc, char **argv)
> > +{
> > +	char *pkey_file;
> > +	char *dtb_file;
> > +	bool overlay;
> > +	int c, idx, ret;
> > +
> > +	pkey_file = NULL;
> > +	dtb_file = NULL;
> > +	overlay = false;
> > +
> > +	for (;;) {
> > +		c = getopt_long(argc, argv, opts_short, options, &idx);
> > +		if (c == -1)
> > +			break;
> > +
> > +		switch (c) {
> > +		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;
> > +		}
> > +	}
> > +
> > +	/* check necessary parameters */
> > +	if (!pkey_file || !dtb_file) {
> > +		print_usage();
> > +		exit(EXIT_FAILURE);
> > +	}
> > +
> > +	ret = add_public_key(pkey_file, dtb_file, overlay);
> > +	if (ret == -1) {
> > +		printf("Adding public key to the dtb failed\n");
> > +		exit(EXIT_FAILURE);
> > +	}
> > +
> > +	exit(EXIT_SUCCESS);
> > +}
> > 
> 

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

* [PATCH 2/4] tools: mkeficapsule: remove device-tree related operation
  2021-05-13  5:08             ` Heinrich Schuchardt
@ 2021-05-13  7:13               ` AKASHI Takahiro
  2021-05-13 17:42                 ` Heinrich Schuchardt
  0 siblings, 1 reply; 55+ messages in thread
From: AKASHI Takahiro @ 2021-05-13  7:13 UTC (permalink / raw)
  To: u-boot

On Thu, May 13, 2021 at 07:08:12AM +0200, Heinrich Schuchardt wrote:
> On 5/13/21 4:33 AM, AKASHI Takahiro wrote:
> > On Wed, May 12, 2021 at 12:01:32PM +0200, Heinrich Schuchardt wrote:
> > > On 12.05.21 10:01, Ilias Apalodimas wrote:
> > > > On Wed, May 12, 2021 at 04:49:02PM +0900, Masami Hiramatsu wrote:
> > > > > Hi Ilias,
> > > > > 
> > > > > 2021?5?12?(?) 16:21 Ilias Apalodimas <ilias.apalodimas@linaro.org>:
> > > > > > 
> > > > > > Akashi-san,
> > > > > > 
> > > > > > On Wed, May 12, 2021 at 01:57:51PM +0900, AKASHI Takahiro wrote:
> > > > > > > As we discussed, "-K" and "-D" options have nothing to do with
> > > > > > > creating a capsule file. The same result can be obtained by
> > > > > > > using standard commands like:
> > > > > > >    === signature.dts ===
> > > > > > >    /dts-v1/;
> > > > > > >    /plugin/;
> > > > > > > 
> > > > > > >    &{/} {
> > > > > > >          signature {
> > > > > > >                  capsule-key = /incbin/("SIGNER.esl");
> > > > > > >          };
> > > > > > >    };
> > > > > > >    ===
> > > > > > >    $ dtc -@ -I dts -O dtb -o signature.dtbo signature.dts
> > > > > > >    $ fdtoverlay -i test.dtb -o test_sig.dtb -v signature.dtbo
> > > > > > > 
> > > > > > > So just remove this feature.
> > > > > > > (Effectively revert the commit 322c813f4bec ("mkeficapsule: Add support
> > > > > > > for embedding public key in a dtb").)
> > > > > > > 
> > > > > > > The same feature is implemented by a shell script (tools/fdtsig.sh).
> > > > > > 
> > > > > > 
> > > > > > The only reason I can see to keep this, is if mkeficapsule gets included
> > > > > > intro distro packages in the future.  That would make end users life a bit
> > > > > > easier, since they would need a single binary to create the whole
> > > > > > CapsuleUpdate sequence.
> > > > > 
> > > > > Hmm, I think it is better to write a manpage of mkeficapsule which
> > > > > also describes
> > > > > how to embed the key into dtb as in the above example if it is so short.
> > > > > Or, distros can package the above shell script with mkeficapsule.
> > > > > 
> > > > > Embedding a key and signing a capsule are different operations but
> > > > > using the same tool may confuse users (at least me).
> > > > 
> > > > Sure fair enough.  I am merely pointing out we need a way to explain all of
> > > > those to users.
> > > 
> > > This is currently our only documentation:
> > > 
> > > https://u-boot.readthedocs.io/en/latest/board/emulation/qemu_capsule_update.html?highlight=mkeficapsule
> > 
> > As I mentioned several times (and TODO in the cover letter),
> > this text must be reviewed, revised and generalized
> > as a platform-independent document.
> > It contains a couple of errors.
> > 
> > > For mkimage we have a man-page ./doc/mkimage.1 that is packaged with
> > > Debians u-boot-tools package. Please, provide a similar man-page as
> > > ./doc/mkeficapsule.1.
> > 
> > So after all do you agree to removing "-K/-D"?
> 
> I see no need to replicate in U-Boot what is already in the device tree
> compiler package.

This is another reason that we should remove Sughosh's change.

> In the current workflow the fdt command is used to load the public key.
> This is insecure and not usable for production.

I totally disagree.
Why is using fdt command (what do you mean by fdt command, dtc/fdtoverlay?)
insecure?

> The public key used to verify the capsule must be built into the U-Boot
> binary. This will supplant the -K and -D options.

I don't get your point. You don't understand my code.

Even with Sughosh's original patch, the public key (as I said,
it is not a public key but a X509 certificate in ESL format) is
embedded in the U-Boot's "control device tree".

Even after applying my patch, this is true.

Or are you insisting that the key should not be in the device tree?

-Takahiro Akashi

> Best regards
> 
> Heinrich
> 
> > Otherwise, I cannot complete the man page.
> > 
> > -Takahiro Akashi
> > 
> > > Best regards
> > > 
> > > Heinrich
> 

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

* [PATCH 1/4] tools: mkeficapsule: add firmwware image signing
  2021-05-13  6:55               ` Heinrich Schuchardt
@ 2021-05-13  7:23                 ` AKASHI Takahiro
  2021-05-13  8:18                   ` Masami Hiramatsu
  0 siblings, 1 reply; 55+ messages in thread
From: AKASHI Takahiro @ 2021-05-13  7:23 UTC (permalink / raw)
  To: u-boot

On Thu, May 13, 2021 at 08:55:17AM +0200, Heinrich Schuchardt wrote:
> Am 13. Mai 2021 08:50:54 MESZ schrieb AKASHI Takahiro <takahiro.akashi@linaro.org>:
> >On Thu, May 13, 2021 at 07:50:52AM +0200, Heinrich Schuchardt wrote:
> >> On 5/13/21 7:12 AM, Masami Hiramatsu wrote:
> >> > Hi Heinrich,
> >> > 
> >> > 2021?5?13?(?) 13:22 Heinrich Schuchardt <xypron.glpk@gmx.de>:
> >> > > 
> >> > > On 5/13/21 5:08 AM, AKASHI Takahiro wrote:
> >> > > > On Wed, May 12, 2021 at 10:56:41AM +0200, Heinrich Schuchardt
> >wrote:
> >> > > > > On 12.05.21 06:57, AKASHI Takahiro wrote:
> >> > > > > > With this enhancement, mkeficapsule will be able to create
> >a capsule
> >> > > > > > file with a signature which will be verified later by FMP's
> >SetImage().
> >> > > > > > 
> >> > > > > > We will have to specify addtional command parameters:
> >> > > > > >     -monotonic-cout <count> : monotonic count
> >> > > > > >     -private-key <private key file> : private key file
> >> > > > > >     -certificate <certificate file> : certificate file
> >> > > > > > Only when those parameters are given, a signature will be
> >added
> >> > > > > > to a capsule file.
> >> > > > > > 
> >> > > > > > Users are expected to maintain the monotonic count for each
> >firmware
> >> > > > > > image.
> >> > > > > > 
> >> > > > > > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> >> > > > > > ---
> >> > > > > >    tools/Makefile       |   4 +
> >> > > > > >    tools/mkeficapsule.c | 324
> >+++++++++++++++++++++++++++++++++++++++----
> >> > > > > >    2 files changed, 303 insertions(+), 25 deletions(-)
> >> > > > > > 
> >> > > > > > diff --git a/tools/Makefile b/tools/Makefile
> >> > > > > > index d020c55d6644..02eae0286e20 100644
> >> > > > > > --- a/tools/Makefile
> >> > > > > > +++ b/tools/Makefile
> >> > > > > > @@ -231,6 +231,10 @@ hostprogs-$(CONFIG_MIPS) +=
> >mips-relocs
> >> > > > > >    hostprogs-$(CONFIG_ASN1_COMPILER) += asn1_compiler
> >> > > > > >    HOSTCFLAGS_asn1_compiler.o = -idirafter
> >$(srctree)/include
> >> > > > > > 
> >> > > > > > +ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),)
> >> > > > > > +HOSTLDLIBS_mkeficapsule += \
> >> > > > > > +   $(shell pkg-config --libs libssl libcrypto 2> /dev/null
> >|| echo "-lssl -lcrypto")
> >> > > > > 
> >> > > > > I don't expect any user wants to install two tool versions in
> >parallel.
> >> > > > > 
> >> > > > > The tool should always be able to add a signature.
> >> > > > > Adding a signature must be optional.
> >> > > > 
> >> > > > It seems to me that those two statements mutually contradict.
> >> > > > Or do you intend to say that we should have a separate kconfig
> >> > > > option to enable/disable signing feature in mkeficapsule?
> >> > > > 
> >> > > > If so, I can agree.
> >> > > > 
> >> > > > In either way, we should have an option to turn on/off this
> >functionality
> >> > > > as not all users use signed capsules.
> >> > > 
> >> > > I want to have a single binary to distribute with Linux distros
> >(e.g.
> >> > > Debian/Ubuntu package u-boot-tools).
> >> > 
> >> > I couldn't catch your point. If so, the distros can build u-boot
> >with
> >> > CONFIG_EFI_CAPSULE_AUTHENTICATE=y...
> >> 
> >> Why should the tool depend on board configuration?
> >> Who would want capsule updates without authentication?
> >
> >I believe that there are bunch of users who don't need authentication
> >on their own systems.
> >
> 
> They should think again.

Why?

> >> > 
> >> > BTW, IMHO, if u-boot.bin can not find the ESL in the device tree,
> >> > it should skip authentication too.
> >> 
> >> In this case the capsule should be rejected (if
> >> CONFIG_EFI_CAPSULE_AUTHENTICATE=y).
> >
> >That's basically right.
> >But as I mentioned in my comment against Sughosh's patch,
> >the authentication process will be enforced only if the capsule has
> >an attribute, IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED.
> >
> 
> That would be a security desaster.

The requirement that I mentioned above is clearly described
in UEFI specification.
If you think that it is a disaster, please discuss the topic
in UEFI Forum first.

-Takahiro Akashi


> Best regards
> 
> Heinrich
> 
> >I believe that this flag should be able to be specified and managed
> >outside U-Boot configuration.
> >So there can be a case where FW update is performed even if
> >the key/certificate is not found in the device tree.
> >
> >> > 
> >> > Then, user can choose whether enabling capsule authentication or
> >not
> >> > by embedding ESL into their devicetree.
> >
> >Same comment above.
> >
> >-Takahiro Akashi
> >
> >> The user shall not be able to decide anything that might hamper
> >> security. The U-Boot binary must dictate if a capsule is safe.
> >> 
> >> Best regards
> >> 
> >> Heinrich
> >> 
> >> > 
> >> > Thank you
> >> > 
> >> > > 
> >> > > This should allow both
> >> > > 
> >> > > - create signed capsules
> >> > > - create unsigned capsules
> >> > > 
> >> > > The user shall select signing via command line parameters.
> >> > > 
> >> > > Support for signing via the tool shall not depend on board
> >Kconfig
> >> > > parameters.
> >> > > 
> >> > > Best regards
> >> > > 
> >> > > Heinrich
> >> > > 
> >> > > > 
> >> > > > > > +endif
> >> > > > > >    mkeficapsule-objs := mkeficapsule.o $(LIBFDT_OBJS)
> >> > > > > >    hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) +=
> >mkeficapsule
> >> > > > > > 
> >> > > > > > diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
> >> > > > > > index de0a62898886..34ff1bdd82eb 100644
> >> > > > > > --- a/tools/mkeficapsule.c
> >> > > > > > +++ b/tools/mkeficapsule.c
> >> > > > > > @@ -18,7 +18,17 @@
> >> > > > > >    #include <sys/stat.h>
> >> > > > > >    #include <sys/types.h>
> >> > > > > > 
> >> > > > > > -#include "fdt_host.h"
> >> > > > > > +#include <linux/kconfig.h>
> >> > > > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> >> > > > > 
> >> > > > > see above
> >> > > > > 
> >> > > > > > +#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
> >> > > > > > +
> >> > > > > > +#include <linux/libfdt.h>
> >> > > > > > 
> >> > > > > >    typedef __u8 u8;
> >> > > > > >    typedef __u16 u16;
> >> > > > > > @@ -46,6 +56,13 @@ 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;
> >> > > > > > +
> >> > > > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> >> > > > > 
> >> > > > > see above
> >> > > > > 
> >> > > > > > +static const char *opts_short = "f:r:i:I:v:D:K:P:C:m:dOh";
> >> > > > > > +#else
> >> > > > > > +static const char *opts_short = "f:r:i:I:v:D:K:Oh";
> >> > > > > > +#endif
> >> > > > > > 
> >> > > > > >    static struct option options[] = {
> >> > > > > >      {"fit", required_argument, NULL, 'f'},
> >> > > > > > @@ -54,6 +71,12 @@ static struct option options[] = {
> >> > > > > >      {"instance", required_argument, NULL, 'I'},
> >> > > > > >      {"dtb", required_argument, NULL, 'D'},
> >> > > > > >      {"public key", required_argument, NULL, 'K'},
> >> > > > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> >> > > > > > +   {"private-key", required_argument, NULL, 'P'},
> >> > > > > > +   {"certificate", required_argument, NULL, 'C'},
> >> > > > > > +   {"monotonic-count", required_argument, NULL, 'm'},
> >> > > > > 
> >> > > > > These options should not be required.
> >> > > > 
> >> > > > I don't get you. What do you mean?
> >> > > > 
> >> > > > > > +   {"dump-sig", no_argument, NULL, 'd'},
> >> > > > > > +#endif
> >> > > > > >      {"overlay", no_argument, NULL, 'O'},
> >> > > > > >      {"help", no_argument, NULL, 'h'},
> >> > > > > >      {NULL, 0, NULL, 0},
> >> > > > > > @@ -70,6 +93,12 @@ static void print_usage(void)
> >> > > > > >             "\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"
> >> > > > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> >> > > > > 
> >> > > > > see above
> >> > > > > 
> >> > > > > > +          "\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-O, --overlay               the dtb file is
> >an overlay\n"
> >> > > > > >             "\t-h, --help                  print a help
> >message\n",
> >> > > > > >             tool_name);
> >> > > > > > @@ -249,12 +278,167 @@ err:
> >> > > > > >      return ret;
> >> > > > > >    }
> >> > > > > > 
> >> > > > > > +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;
> >> > > > > > +
> >> > > > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> >> > > > > 
> >> > > > > see above
> >> > > > > 
> >> > > > > > +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);
> >> > > > > 
> >> > > > > Please, you use fprintf(stderr,) for error messages.
> >> > > > > 
> >> > > > > > +           ERR_print_errors_fp(stderr);
> >> > > > > > +   }
> >> > > > > > +
> >> > > > > > +   return key;
> >> > > > > > +}
> >> > > > > > +
> >> > > > > > +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);
> >> > > > > 
> >> > > > > fprintf(stderr,)
> >> > > > > 
> >> > > > > > +           ERR_print_errors_fp(stderr);
> >> > > > > > +   }
> >> > > > > > +
> >> > > > > > +   return cert;
> >> > > > > > +}
> >> > > > > > +
> >> > > > > > +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 */
> >> > > > > 
> >> > > > > PKCS7_NOATTR is a value without any documentation in the
> >code.
> >> > > > 
> >> > > > Nak.
> >> > > > Those macros are part of openssl library. See openssl/pkcs7.h.
> >> > > > 
> >> > > > > Please, replace variable names by a long text describing what
> >it missing.
> >> > > > > 
> >> > > > > > +   flags = PKCS7_BINARY | PKCS7_DETACHED;
> >> > > > > 
> >> > > > > Those constants lack documentation in the code.
> >> > > > 
> >> > > > Nak again.
> >> > > > 
> >> > > > > > +   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;
> >> > > > > > +}
> >> > > > > > +
> >> > > > > > +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;
> >> > > > > > +}
> >> > > > > > +#endif
> >> > > > > > +
> >> > > > > >    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;
> >> > > > > > @@ -266,6 +450,7 @@ static int create_fwbin(char *path,
> >char *bin, efi_guid_t *guid,
> >> > > > > >      printf("\tbin: %s\n\ttype: %pUl\n", bin, guid);
> >> > > > > >      printf("\tindex: %ld\n\tinstance: %ld\n", index,
> >instance);
> >> > > > > >    #endif
> >> > > > > > +   auth_context.sig_size = 0;
> >> > > > > > 
> >> > > > > >      g = fopen(bin, "r");
> >> > > > > >      if (!g) {
> >> > > > > > @@ -281,11 +466,36 @@ 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 IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> >> > > > > 
> >> > > > > see above
> >> > > > > 
> >> > > > > > +   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)) {
> 

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

* [PATCH 1/4] tools: mkeficapsule: add firmwware image signing
  2021-05-13  6:52               ` Heinrich Schuchardt
@ 2021-05-13  7:38                 ` AKASHI Takahiro
  0 siblings, 0 replies; 55+ messages in thread
From: AKASHI Takahiro @ 2021-05-13  7:38 UTC (permalink / raw)
  To: u-boot

On Thu, May 13, 2021 at 08:52:18AM +0200, Heinrich Schuchardt wrote:
> Am 13. Mai 2021 08:44:24 MESZ schrieb Masami Hiramatsu <masami.hiramatsu@linaro.org>:
> >Hi Heinrich,
> >
> >2021?5?13?(?) 14:50 Heinrich Schuchardt <xypron.glpk@gmx.de>:
> >>
> >> On 5/13/21 7:12 AM, Masami Hiramatsu wrote:
> >> > Hi Heinrich,
> >> >
> >> > 2021?5?13?(?) 13:22 Heinrich Schuchardt <xypron.glpk@gmx.de>:
> >> >>
> >> >> On 5/13/21 5:08 AM, AKASHI Takahiro wrote:
> >> >>> On Wed, May 12, 2021 at 10:56:41AM +0200, Heinrich Schuchardt
> >wrote:
> >> >>>> On 12.05.21 06:57, AKASHI Takahiro wrote:
> >> >>>>> With this enhancement, mkeficapsule will be able to create a
> >capsule
> >> >>>>> file with a signature which will be verified later by FMP's
> >SetImage().
> >> >>>>>
> >> >>>>> We will have to specify addtional command parameters:
> >> >>>>>     -monotonic-cout <count> : monotonic count
> >> >>>>>     -private-key <private key file> : private key file
> >> >>>>>     -certificate <certificate file> : certificate file
> >> >>>>> Only when those parameters are given, a signature will be added
> >> >>>>> to a capsule file.
> >> >>>>>
> >> >>>>> Users are expected to maintain the monotonic count for each
> >firmware
> >> >>>>> image.
> >> >>>>>
> >> >>>>> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> >> >>>>> ---
> >> >>>>>    tools/Makefile       |   4 +
> >> >>>>>    tools/mkeficapsule.c | 324
> >+++++++++++++++++++++++++++++++++++++++----
> >> >>>>>    2 files changed, 303 insertions(+), 25 deletions(-)
> >> >>>>>
> >> >>>>> diff --git a/tools/Makefile b/tools/Makefile
> >> >>>>> index d020c55d6644..02eae0286e20 100644
> >> >>>>> --- a/tools/Makefile
> >> >>>>> +++ b/tools/Makefile
> >> >>>>> @@ -231,6 +231,10 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs
> >> >>>>>    hostprogs-$(CONFIG_ASN1_COMPILER) += asn1_compiler
> >> >>>>>    HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
> >> >>>>>
> >> >>>>> +ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),)
> >> >>>>> +HOSTLDLIBS_mkeficapsule += \
> >> >>>>> +   $(shell pkg-config --libs libssl libcrypto 2> /dev/null ||
> >echo "-lssl -lcrypto")
> >> >>>>
> >> >>>> I don't expect any user wants to install two tool versions in
> >parallel.
> >> >>>>
> >> >>>> The tool should always be able to add a signature.
> >> >>>> Adding a signature must be optional.
> >> >>>
> >> >>> It seems to me that those two statements mutually contradict.
> >> >>> Or do you intend to say that we should have a separate kconfig
> >> >>> option to enable/disable signing feature in mkeficapsule?
> >> >>>
> >> >>> If so, I can agree.
> >> >>>
> >> >>> In either way, we should have an option to turn on/off this
> >functionality
> >> >>> as not all users use signed capsules.
> >> >>
> >> >> I want to have a single binary to distribute with Linux distros
> >(e.g.
> >> >> Debian/Ubuntu package u-boot-tools).
> >> >
> >> > I couldn't catch your point. If so, the distros can build u-boot
> >with
> >> > CONFIG_EFI_CAPSULE_AUTHENTICATE=y...
> >>
> >> Why should the tool depend on board configuration?
> >
> >Yeah, at this point I agreed. I think there should be a separated
> >CONFIG
> >for tools

I prefer this.

>  or forcibly link those libraries. (I think most people don't
> >mind if it requires new libraries to be built, that usually happens.)
> >
> >> Who would want capsule updates without authentication?
> >
> >Hm, so you think even CONFIG_EFI_CAPSULE_AUTHENTICATE is
> >only for development. Capsule must be signed, right?
> >Then, all distro should build u-boot with
> >CONFIG_EFI_CAPSULE_AUTHENTICATE=y, isn't it?
> 
> There will still be U-Boot images without capsule updates.

Users are not always distro.
There may be users who want to use capsule updates for their
own systems that they manage by themselves.

> >
> >> > BTW, IMHO, if u-boot.bin can not find the ESL in the device tree,
> >> > it should skip authentication too.
> >>
> >> In this case the capsule should be rejected (if
> >> CONFIG_EFI_CAPSULE_AUTHENTICATE=y).
> >
> >I meant U-Boot has NO key to authenticate the capsule. I think in that
> >case U-Boot build process must require the key (ESL) and if user
> >doesn't
> >provide it, the build should fail (if it doesn't skip capsule
> >authentication.)
> >Or, we have no way to update U-Boot anymore.
> >
> >> > Then, user can choose whether enabling capsule authentication or
> >not
> >> > by embedding ESL into their devicetree.
> >>
> >> The user shall not be able to decide anything that might hamper
> >> security. The U-Boot binary must dictate if a capsule is safe.
> >
> >Hmm, I think the root issue is that the ESL embedding process is not
> >integrated into the build process yet.

Sughosh has already submitted the patch:
https://lists.denx.de/pipermail/u-boot/2021-April/447183.html

My patch is based on top of that.

> For the safe capsule update,
> >we must enable capsule authentication with keys. (unsafe one is only
> >for testing/development)
> >Moreover, the key is stored in the U-Boot itself OR, in the secure
> >storage
> >outside of U-Boot (Hardware OTP or TPM/HSM are preferable.)
> >
> >Thus,
> >CONFIG_EFI_CAPSULE_AUTHENTICATE must depend on (or select)
> >a new config which points the path for the ESL file (this is embedded
> >while
> >the build process), OR, the platform driver provides key from its
> >hardware
> >secure storage.
> >
> >What would you think about this idea?
> 
> That is the direction I would like to go.

Which part of Masami's comment do you intend to agree to?
Sugosh's approach is the one that you and Sughosh has agreed
in the past discussion. Do you want to revert it?

-Takahiro Akashi


> Best regards
> 
> Heinrich
> 
> >
> >Thank you,
> >
> >>
> >> Best regards
> >>
> >> Heinrich
> >>
> >> >
> >> > Thank you
> >> >
> >> >>
> >> >> This should allow both
> >> >>
> >> >> - create signed capsules
> >> >> - create unsigned capsules
> >> >>
> >> >> The user shall select signing via command line parameters.
> >> >>
> >> >> Support for signing via the tool shall not depend on board Kconfig
> >> >> parameters.
> >> >>
> >> >> Best regards
> >> >>
> >> >> Heinrich
> >> >>
> >> >>>
> >> >>>>> +endif
> >> >>>>>    mkeficapsule-objs := mkeficapsule.o $(LIBFDT_OBJS)
> >> >>>>>    hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
> >> >>>>>
> >> >>>>> diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
> >> >>>>> index de0a62898886..34ff1bdd82eb 100644
> >> >>>>> --- a/tools/mkeficapsule.c
> >> >>>>> +++ b/tools/mkeficapsule.c
> >> >>>>> @@ -18,7 +18,17 @@
> >> >>>>>    #include <sys/stat.h>
> >> >>>>>    #include <sys/types.h>
> >> >>>>>
> >> >>>>> -#include "fdt_host.h"
> >> >>>>> +#include <linux/kconfig.h>
> >> >>>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> >> >>>>
> >> >>>> see above
> >> >>>>
> >> >>>>> +#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
> >> >>>>> +
> >> >>>>> +#include <linux/libfdt.h>
> >> >>>>>
> >> >>>>>    typedef __u8 u8;
> >> >>>>>    typedef __u16 u16;
> >> >>>>> @@ -46,6 +56,13 @@ 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;
> >> >>>>> +
> >> >>>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> >> >>>>
> >> >>>> see above
> >> >>>>
> >> >>>>> +static const char *opts_short = "f:r:i:I:v:D:K:P:C:m:dOh";
> >> >>>>> +#else
> >> >>>>> +static const char *opts_short = "f:r:i:I:v:D:K:Oh";
> >> >>>>> +#endif
> >> >>>>>
> >> >>>>>    static struct option options[] = {
> >> >>>>>      {"fit", required_argument, NULL, 'f'},
> >> >>>>> @@ -54,6 +71,12 @@ static struct option options[] = {
> >> >>>>>      {"instance", required_argument, NULL, 'I'},
> >> >>>>>      {"dtb", required_argument, NULL, 'D'},
> >> >>>>>      {"public key", required_argument, NULL, 'K'},
> >> >>>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> >> >>>>> +   {"private-key", required_argument, NULL, 'P'},
> >> >>>>> +   {"certificate", required_argument, NULL, 'C'},
> >> >>>>> +   {"monotonic-count", required_argument, NULL, 'm'},
> >> >>>>
> >> >>>> These options should not be required.
> >> >>>
> >> >>> I don't get you. What do you mean?
> >> >>>
> >> >>>>> +   {"dump-sig", no_argument, NULL, 'd'},
> >> >>>>> +#endif
> >> >>>>>      {"overlay", no_argument, NULL, 'O'},
> >> >>>>>      {"help", no_argument, NULL, 'h'},
> >> >>>>>      {NULL, 0, NULL, 0},
> >> >>>>> @@ -70,6 +93,12 @@ static void print_usage(void)
> >> >>>>>             "\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"
> >> >>>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> >> >>>>
> >> >>>> see above
> >> >>>>
> >> >>>>> +          "\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-O, --overlay               the dtb file is an
> >overlay\n"
> >> >>>>>             "\t-h, --help                  print a help
> >message\n",
> >> >>>>>             tool_name);
> >> >>>>> @@ -249,12 +278,167 @@ err:
> >> >>>>>      return ret;
> >> >>>>>    }
> >> >>>>>
> >> >>>>> +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;
> >> >>>>> +
> >> >>>>> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> >> >>>>
> >> >>>> see above
> >> >>>>
> >> >>>>> +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);
> >> >>>>
> >> >>>> Please, you use fprintf(stderr,) for error messages.
> >> >>>>
> >> >>>>> +           ERR_print_errors_fp(stderr);
> >> >>>>> +   }
> >> >>>>> +
> >> >>>>> +   return key;
> >> >>>>> +}
> >> >>>>> +
> >> >>>>> +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);
> >> >>>>
> >> >>>> fprintf(stderr,)
> >> >>>>
> >> >>>>> +           ERR_print_errors_fp(stderr);
> >> >>>>> +   }
> >> >>>>> +
> >> >>>>> +   return cert;
> >> >>>>> +}
> >> >>>>> +
> >> >>>>> +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 */
> >> >>>>
> >> >>>> PKCS7_NOATTR is a value without any documentation in the code.
> >> >>>
> >> >>> Nak.
> >> >>> Those macros are part of openssl library. See openssl/pkcs7.h.
> >> >>>
> >> >>>> Please, replace variable names by a long text describing what it
> >missing.
> >> >>>>
> >> >>>>> +   flags = PKCS7_BINARY | PKCS7_DETACHED;
> >> >>>>
> >> >>>> Those constants lack documentation in the code.
> >> >>>
> >> >>> Nak again.
> >> >>>
> >> >>>>> +   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;
> >> >>>>> +}
> >> >>>>> +
> >> >>>>> +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;
> >> >>>>> +}
> >> >>>>> +#endif
> >> >>>>> +
> >> >>>>>    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;
> >> >>>>> @@ -266,6 +450,7 @@ static int create_fwbin(char *path, char
> >*bin, efi_guid_t *guid,
> >> >>>>>      printf("\tbin: %s\n\ttype: %pUl\n", bin, guid);
> 

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

* [PATCH 1/4] tools: mkeficapsule: add firmwware image signing
  2021-05-13  6:45               ` Heinrich Schuchardt
@ 2021-05-13  7:45                 ` AKASHI Takahiro
  0 siblings, 0 replies; 55+ messages in thread
From: AKASHI Takahiro @ 2021-05-13  7:45 UTC (permalink / raw)
  To: u-boot

On Thu, May 13, 2021 at 08:45:10AM +0200, Heinrich Schuchardt wrote:
> Am 13. Mai 2021 08:36:05 MESZ schrieb AKASHI Takahiro <takahiro.akashi@linaro.org>:
> >On Thu, May 13, 2021 at 07:35:36AM +0200, Heinrich Schuchardt wrote:
> >> On 5/13/21 7:00 AM, AKASHI Takahiro wrote:
> >> > On Thu, May 13, 2021 at 06:22:39AM +0200, Heinrich Schuchardt
> >wrote:
> >> > > On 5/13/21 5:08 AM, AKASHI Takahiro wrote:
> >> > > > On Wed, May 12, 2021 at 10:56:41AM +0200, Heinrich Schuchardt
> >wrote:
> >> > > > > On 12.05.21 06:57, AKASHI Takahiro wrote:
> >> > > > > > With this enhancement, mkeficapsule will be able to create
> >a capsule
> >> > > > > > file with a signature which will be verified later by FMP's
> >SetImage().
> >> > > > > > 
> >> > > > > > We will have to specify addtional command parameters:
> >> > > > > >     -monotonic-cout <count> : monotonic count
> >> > > > > >     -private-key <private key file> : private key file
> >> > > > > >     -certificate <certificate file> : certificate file
> >> > > > > > Only when those parameters are given, a signature will be
> >added
> >> > > > > > to a capsule file.
> >> > > > > > 
> >> > > > > > Users are expected to maintain the monotonic count for each
> >firmware
> >> > > > > > image.
> >> > > > > > 
> >> > > > > > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> >> > > > > > ---
> >> > > > > >    tools/Makefile       |   4 +
> >> > > > > >    tools/mkeficapsule.c | 324
> >+++++++++++++++++++++++++++++++++++++++----
> >> > > > > >    2 files changed, 303 insertions(+), 25 deletions(-)
> >> > > > > > 
> >> > > > > > diff --git a/tools/Makefile b/tools/Makefile
> >> > > > > > index d020c55d6644..02eae0286e20 100644
> >> > > > > > --- a/tools/Makefile
> >> > > > > > +++ b/tools/Makefile
> >> > > > > > @@ -231,6 +231,10 @@ hostprogs-$(CONFIG_MIPS) +=
> >mips-relocs
> >> > > > > >    hostprogs-$(CONFIG_ASN1_COMPILER)	+= asn1_compiler
> >> > > > > >    HOSTCFLAGS_asn1_compiler.o = -idirafter
> >$(srctree)/include
> >> > > > > > 
> >> > > > > > +ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),)
> >> > > > > > +HOSTLDLIBS_mkeficapsule += \
> >> > > > > > +	$(shell pkg-config --libs libssl libcrypto 2> /dev/null
> >|| echo "-lssl -lcrypto")
> >> > > > > 
> >> > > > > I don't expect any user wants to install two tool versions in
> >parallel.
> >> > > > > 
> >> > > > > The tool should always be able to add a signature.
> >> > > > > Adding a signature must be optional.
> >> > > > 
> >> > > > It seems to me that those two statements mutually contradict.
> >> > > > Or do you intend to say that we should have a separate kconfig
> >> > > > option to enable/disable signing feature in mkeficapsule?
> >> > > > 
> >> > > > If so, I can agree.
> >> > > > 
> >> > > > In either way, we should have an option to turn on/off this
> >functionality
> >> > > > as not all users use signed capsules.
> >> > > 
> >> > > I want to have a single binary to distribute with Linux distros
> >(e.g.
> >> > > Debian/Ubuntu package u-boot-tools).
> >> > > 
> >> > > This should allow both
> >> > > 
> >> > > - create signed capsules
> >> > > - create unsigned capsules
> >> > > 
> >> > > The user shall select signing via command line parameters.
> >> > > 
> >> > > Support for signing via the tool shall not depend on board
> >Kconfig
> >> > > parameters.
> >> > 
> >> > That is why I proposed that we create a new kconfig option.
> >> 
> >> What do you want to configure? Signing shall always be enabled in
> >> mkeficapsule.
> >
> >I don't think so.
> 
> Capsule updates without authentication should never be rolled out in production for security reasons.

It's up to the providers.
(In this case, providers are not always distro.)
We should provide mechanism, but not enforce policy.

> >
> >> > 
> >> > Please note that enabling signing feature in mkeficapsule
> >> > requires openssl library, and we should not enforce users who don't
> >> > need this feature to install an unnecessary package.
> >> 
> >> Why? There are dozens of other packages depending on OpenSSL on a
> >> developer's machine.
> >
> >We don't expect all users have openssl-related packages on their
> >desktop.
> 
> We are not talking about users but developers here.

There is not distinct difference between users and developpers.

> I haven't seen a Linux distro without an OpenSSL package. The package management system will pull it in when u-boot-tools is installed.

Distro may have its own policy, that's fine.
We will only want to provide the mechanism with which
they want to build their own distro.

-Takahiro Akashi

> 
> Best regards
> 
> Heinrich
> 
> >
> >-Takahiro Akashi
> >
> >
> >> Best regards
> >> 
> >> Heinrich
> >> 
> >> > 
> >> > -Takahiro Akashi
> >> > 
> >> > > Best regards
> >> > > 
> >> > > Heinrich
> >> > > 
> >> > > > 
> >> > > > > > +endif
> >> > > > > >    mkeficapsule-objs	:= mkeficapsule.o $(LIBFDT_OBJS)
> >> > > > > >    hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) +=
> >mkeficapsule
> >> > > > > > 
> >> > > > > > diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
> >> > > > > > index de0a62898886..34ff1bdd82eb 100644
> >> > > > > > --- a/tools/mkeficapsule.c
> >> > > > > > +++ b/tools/mkeficapsule.c
> >> > > > > > @@ -18,7 +18,17 @@
> >> > > > > >    #include <sys/stat.h>
> >> > > > > >    #include <sys/types.h>
> >> > > > > > 
> >> > > > > > -#include "fdt_host.h"
> >> > > > > > +#include <linux/kconfig.h>
> >> > > > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> >> > > > > 
> >> > > > > see above
> >> > > > > 
> >> > > > > > +#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
> >> > > > > > +
> >> > > > > > +#include <linux/libfdt.h>
> >> > > > > > 
> >> > > > > >    typedef __u8 u8;
> >> > > > > >    typedef __u16 u16;
> >> > > > > > @@ -46,6 +56,13 @@ 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;
> >> > > > > > +
> >> > > > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> >> > > > > 
> >> > > > > see above
> >> > > > > 
> >> > > > > > +static const char *opts_short = "f:r:i:I:v:D:K:P:C:m:dOh";
> >> > > > > > +#else
> >> > > > > > +static const char *opts_short = "f:r:i:I:v:D:K:Oh";
> >> > > > > > +#endif
> >> > > > > > 
> >> > > > > >    static struct option options[] = {
> >> > > > > >    	{"fit", required_argument, NULL, 'f'},
> >> > > > > > @@ -54,6 +71,12 @@ static struct option options[] = {
> >> > > > > >    	{"instance", required_argument, NULL, 'I'},
> >> > > > > >    	{"dtb", required_argument, NULL, 'D'},
> >> > > > > >    	{"public key", required_argument, NULL, 'K'},
> >> > > > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> >> > > > > > +	{"private-key", required_argument, NULL, 'P'},
> >> > > > > > +	{"certificate", required_argument, NULL, 'C'},
> >> > > > > > +	{"monotonic-count", required_argument, NULL, 'm'},
> >> > > > > 
> >> > > > > These options should not be required.
> >> > > > 
> >> > > > I don't get you. What do you mean?
> >> > > > 
> >> > > > > > +	{"dump-sig", no_argument, NULL, 'd'},
> >> > > > > > +#endif
> >> > > > > >    	{"overlay", no_argument, NULL, 'O'},
> >> > > > > >    	{"help", no_argument, NULL, 'h'},
> >> > > > > >    	{NULL, 0, NULL, 0},
> >> > > > > > @@ -70,6 +93,12 @@ static void print_usage(void)
> >> > > > > >    	       "\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"
> >> > > > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> >> > > > > 
> >> > > > > see above
> >> > > > > 
> >> > > > > > +	       "\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-O, --overlay               the dtb file is
> >an overlay\n"
> >> > > > > >    	       "\t-h, --help                  print a help
> >message\n",
> >> > > > > >    	       tool_name);
> >> > > > > > @@ -249,12 +278,167 @@ err:
> >> > > > > >    	return ret;
> >> > > > > >    }
> >> > > > > > 
> >> > > > > > +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;
> >> > > > > > +
> >> > > > > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> >> > > > > 
> >> > > > > see above
> >> > > > > 
> >> > > > > > +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);
> >> > > > > 
> >> > > > > Please, you use fprintf(stderr,) for error messages.
> >> > > > > 
> >> > > > > > +		ERR_print_errors_fp(stderr);
> >> > > > > > +	}
> >> > > > > > +
> >> > > > > > +	return key;
> >> > > > > > +}
> >> > > > > > +
> >> > > > > > +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);
> >> > > > > 
> >> > > > > fprintf(stderr,)
> >> > > > > 
> >> > > > > > +		ERR_print_errors_fp(stderr);
> >> > > > > > +	}
> >> > > > > > +
> >> > > > > > +	return cert;
> >> > > > > > +}
> >> > > > > > +
> >> > > > > > +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 */
> >> > > > > 
> >> > > > > PKCS7_NOATTR is a value without any documentation in the
> >code.
> >> > > > 
> >> > > > Nak.
> >> > > > Those macros are part of openssl library. See openssl/pkcs7.h.
> >> > > > 
> >> > > > > Please, replace variable names by a long text describing what
> >it missing.
> >> > > > > 
> >> > > > > > +	flags = PKCS7_BINARY | PKCS7_DETACHED;
> >> > > > > 
> >> > > > > Those constants lack documentation in the code.
> >> > > > 
> >> > > > Nak again.
> >> > > > 
> >> > > > > > +	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;
> >> > > > > > +}
> >> > > > > > +
> >> > > > > > +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;
> >> > > > > > +}
> >> > > > > > +#endif
> >> > > > > > +
> >> > > > > >    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;
> >> > > > > > @@ -266,6 +450,7 @@ static int create_fwbin(char *path,
> >char *bin, efi_guid_t *guid,
> >> > > > > >    	printf("\tbin: %s\n\ttype: %pUl\n", bin, guid);
> >> > > > > >    	printf("\tindex: %ld\n\tinstance: %ld\n", index,
> >instance);
> >> > > > > >    #endif
> >> > > > > > +	auth_context.sig_size = 0;
> >> > > > > > 
> >> > > > > >    	g = fopen(bin, "r");
> >> > > > > >    	if (!g) {
> >> > > > > > @@ -281,11 +466,36 @@ 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 IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
> >> > > > > 
> >> > > > > see above
> >> > > > > 
> >> > > > > > +	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;
> >> > > > > > +		}
> >> > > > > > +	}
> >> > > > > > +#endif
> >> > > > > > +
> >> > > > > >    	header.capsule_guid = efi_guid_fm_capsule;
> >> > > > > >    	header.header_size = sizeof(header);
> >> > > > > >    	/* TODO: The current implementation ignores flags */
> >> > > > > > @@ -294,11 +504,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;
> 

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

* [PATCH 1/4] tools: mkeficapsule: add firmwware image signing
  2021-05-13  7:23                 ` AKASHI Takahiro
@ 2021-05-13  8:18                   ` Masami Hiramatsu
  2021-05-13  8:38                     ` AKASHI Takahiro
  2021-05-13 18:25                     ` Heinrich Schuchardt
  0 siblings, 2 replies; 55+ messages in thread
From: Masami Hiramatsu @ 2021-05-13  8:18 UTC (permalink / raw)
  To: u-boot

2021?5?13?(?) 16:24 AKASHI Takahiro <takahiro.akashi@linaro.org>:

> > >> > BTW, IMHO, if u-boot.bin can not find the ESL in the device tree,
> > >> > it should skip authentication too.
> > >>
> > >> In this case the capsule should be rejected (if
> > >> CONFIG_EFI_CAPSULE_AUTHENTICATE=y).
> > >
> > >That's basically right.
> > >But as I mentioned in my comment against Sughosh's patch,
> > >the authentication process will be enforced only if the capsule has
> > >an attribute, IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED.
> > >
> >
> > That would be a security desaster.
>
> The requirement that I mentioned above is clearly described
> in UEFI specification.
> If you think that it is a disaster, please discuss the topic
> in UEFI Forum first.

I confirmed UEFI specification, version 2.7, Section.23.1
the last of EFI_FIRMWARE_MANAGEMENT_PROTOCOL.GetImageInfo()

-----------------
If IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED is supported and clear, then
authentication is not required to perform the firmware image operations.
-----------------

Oh, this is really crazy because deciding whether to authenticate the
suspicious
package or not, depends on whether the package said "please
authenticate me" or not. :D

Anyway, since this behavior follows the specification, it should be
kept by default,
but also IMHO, there should be a CONFIG option to enforce capsule
authentication always.

Thank you,



-- 
Masami Hiramatsu

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

* [PATCH 1/4] tools: mkeficapsule: add firmwware image signing
  2021-05-13  8:18                   ` Masami Hiramatsu
@ 2021-05-13  8:38                     ` AKASHI Takahiro
  2021-05-13 10:27                       ` Ilias Apalodimas
  2021-05-13 10:40                       ` Heinrich Schuchardt
  2021-05-13 18:25                     ` Heinrich Schuchardt
  1 sibling, 2 replies; 55+ messages in thread
From: AKASHI Takahiro @ 2021-05-13  8:38 UTC (permalink / raw)
  To: u-boot

On Thu, May 13, 2021 at 05:18:36PM +0900, Masami Hiramatsu wrote:
> 2021?5?13?(?) 16:24 AKASHI Takahiro <takahiro.akashi@linaro.org>:
> 
> > > >> > BTW, IMHO, if u-boot.bin can not find the ESL in the device tree,
> > > >> > it should skip authentication too.
> > > >>
> > > >> In this case the capsule should be rejected (if
> > > >> CONFIG_EFI_CAPSULE_AUTHENTICATE=y).
> > > >
> > > >That's basically right.
> > > >But as I mentioned in my comment against Sughosh's patch,
> > > >the authentication process will be enforced only if the capsule has
> > > >an attribute, IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED.
> > > >
> > >
> > > That would be a security desaster.
> >
> > The requirement that I mentioned above is clearly described
> > in UEFI specification.
> > If you think that it is a disaster, please discuss the topic
> > in UEFI Forum first.
> 
> I confirmed UEFI specification, version 2.7, Section.23.1
> the last of EFI_FIRMWARE_MANAGEMENT_PROTOCOL.GetImageInfo()
> 
> -----------------
> If IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED is supported and clear, then
> authentication is not required to perform the firmware image operations.
> -----------------

Thank you for citing this.

> Oh, this is really crazy because deciding whether to authenticate the
> suspicious
> package or not, depends on whether the package said "please
> authenticate me" or not. :D

Well, the attributes can been fetched with GetInfo API, but
how it is managed depends on the implementation of FMP drivers.

As I proposed somewhere else, those attributes should be
maintained in a separate place (maybe as part of system's policy),
presumably ESRT or platform-specific internal database?

-Takahiro Akashi


> Anyway, since this behavior follows the specification, it should be
> kept by default,
> but also IMHO, there should be a CONFIG option to enforce capsule
> authentication always.
> 
> Thank you,
> 
> 
> 
> -- 
> Masami Hiramatsu

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

* [PATCH 1/4] tools: mkeficapsule: add firmwware image signing
  2021-05-13  8:38                     ` AKASHI Takahiro
@ 2021-05-13 10:27                       ` Ilias Apalodimas
  2021-05-13 16:12                         ` Masami Hiramatsu
  2021-05-13 10:40                       ` Heinrich Schuchardt
  1 sibling, 1 reply; 55+ messages in thread
From: Ilias Apalodimas @ 2021-05-13 10:27 UTC (permalink / raw)
  To: u-boot

On Thu, May 13, 2021 at 05:38:51PM +0900, AKASHI Takahiro wrote:
> On Thu, May 13, 2021 at 05:18:36PM +0900, Masami Hiramatsu wrote:
> > 2021?5?13?(?) 16:24 AKASHI Takahiro <takahiro.akashi@linaro.org>:
> > 
> > > > >> > BTW, IMHO, if u-boot.bin can not find the ESL in the device tree,
> > > > >> > it should skip authentication too.
> > > > >>
> > > > >> In this case the capsule should be rejected (if
> > > > >> CONFIG_EFI_CAPSULE_AUTHENTICATE=y).
> > > > >
> > > > >That's basically right.
> > > > >But as I mentioned in my comment against Sughosh's patch,
> > > > >the authentication process will be enforced only if the capsule has
> > > > >an attribute, IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED.
> > > > >
> > > >
> > > > That would be a security desaster.
> > >
> > > The requirement that I mentioned above is clearly described
> > > in UEFI specification.
> > > If you think that it is a disaster, please discuss the topic
> > > in UEFI Forum first.
> > 
> > I confirmed UEFI specification, version 2.7, Section.23.1
> > the last of EFI_FIRMWARE_MANAGEMENT_PROTOCOL.GetImageInfo()
> > 
> > -----------------
> > If IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED is supported and clear, then
> > authentication is not required to perform the firmware image operations.
> > -----------------
> 
> Thank you for citing this.
> 
> > Oh, this is really crazy because deciding whether to authenticate the
> > suspicious
> > package or not, depends on whether the package said "please
> > authenticate me" or not. :D
> 
> Well, the attributes can been fetched with GetInfo API, but
> how it is managed depends on the implementation of FMP drivers.
> 
> As I proposed somewhere else, those attributes should be
> maintained in a separate place (maybe as part of system's policy),
> presumably ESRT or platform-specific internal database?

FWIW I personally don't think we should even have a config option. But even
if we did it certainly must not be dictated by a hardware config.

When you install distro packages you accept whatever dependencies the
package has. mkeficapsule is a capsule creation and signing tool.  I don't
see any reason for keeping the creation and signing apart. 

Regards
/Ilias
> 
> -Takahiro Akashi
> 
> 
> > Anyway, since this behavior follows the specification, it should be
> > kept by default,
> > but also IMHO, there should be a CONFIG option to enforce capsule
> > authentication always.
> > 
> > Thank you,
> > 
> > 
> > 
> > -- 
> > Masami Hiramatsu

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

* [PATCH 1/4] tools: mkeficapsule: add firmwware image signing
  2021-05-13  8:38                     ` AKASHI Takahiro
  2021-05-13 10:27                       ` Ilias Apalodimas
@ 2021-05-13 10:40                       ` Heinrich Schuchardt
  1 sibling, 0 replies; 55+ messages in thread
From: Heinrich Schuchardt @ 2021-05-13 10:40 UTC (permalink / raw)
  To: u-boot

On 5/13/21 10:38 AM, AKASHI Takahiro wrote:
> On Thu, May 13, 2021 at 05:18:36PM +0900, Masami Hiramatsu wrote:
>> 2021?5?13?(?) 16:24 AKASHI Takahiro <takahiro.akashi@linaro.org>:
>>
>>>>>>> BTW, IMHO, if u-boot.bin can not find the ESL in the device tree,
>>>>>>> it should skip authentication too.
>>>>>>
>>>>>> In this case the capsule should be rejected (if
>>>>>> CONFIG_EFI_CAPSULE_AUTHENTICATE=y).
>>>>>
>>>>> That's basically right.
>>>>> But as I mentioned in my comment against Sughosh's patch,
>>>>> the authentication process will be enforced only if the capsule has
>>>>> an attribute, IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED.
>>>>>
>>>>
>>>> That would be a security desaster.
>>>
>>> The requirement that I mentioned above is clearly described
>>> in UEFI specification.
>>> If you think that it is a disaster, please discuss the topic
>>> in UEFI Forum first.
>>
>> I confirmed UEFI specification, version 2.7, Section.23.1
>> the last of EFI_FIRMWARE_MANAGEMENT_PROTOCOL.GetImageInfo()
>>
>> -----------------
>> If IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED is supported and clear, then
>> authentication is not required to perform the firmware image operations.
>> -----------------
>
> Thank you for citing this.

This is the fraudulent code:

lib/efi_loader/efi_firmware.c:195

         /* Check if the capsule authentication is enabled */
         if (env_get("capsule_authentication_enabled"))
                 image_info[0].attributes_setting |=
                         IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED;

It is not allowable that a user can disable image authentication by
deleting the environment.

Best regards

Heinrich

>
>> Oh, this is really crazy because deciding whether to authenticate the
>> suspicious
>> package or not, depends on whether the package said "please
>> authenticate me" or not. :D
>
> Well, the attributes can been fetched with GetInfo API, but
> how it is managed depends on the implementation of FMP drivers.
>
> As I proposed somewhere else, those attributes should be
> maintained in a separate place (maybe as part of system's policy),
> presumably ESRT or platform-specific internal database?
>
> -Takahiro Akashi
>
>
>> Anyway, since this behavior follows the specification, it should be
>> kept by default,
>> but also IMHO, there should be a CONFIG option to enforce capsule
>> authentication always.
>>
>> Thank you,
>>
>>
>>
>> --
>> Masami Hiramatsu

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

* [PATCH 1/4] tools: mkeficapsule: add firmwware image signing
  2021-05-13 10:27                       ` Ilias Apalodimas
@ 2021-05-13 16:12                         ` Masami Hiramatsu
  2021-05-13 16:32                           ` Heinrich Schuchardt
  0 siblings, 1 reply; 55+ messages in thread
From: Masami Hiramatsu @ 2021-05-13 16:12 UTC (permalink / raw)
  To: u-boot

2021?5?13?(?) 19:27 Ilias Apalodimas <ilias.apalodimas@linaro.org>:
>
> On Thu, May 13, 2021 at 05:38:51PM +0900, AKASHI Takahiro wrote:
> > On Thu, May 13, 2021 at 05:18:36PM +0900, Masami Hiramatsu wrote:
> > > 2021?5?13?(?) 16:24 AKASHI Takahiro <takahiro.akashi@linaro.org>:
> > >
> > > > > >> > BTW, IMHO, if u-boot.bin can not find the ESL in the device tree,
> > > > > >> > it should skip authentication too.
> > > > > >>
> > > > > >> In this case the capsule should be rejected (if
> > > > > >> CONFIG_EFI_CAPSULE_AUTHENTICATE=y).
> > > > > >
> > > > > >That's basically right.
> > > > > >But as I mentioned in my comment against Sughosh's patch,
> > > > > >the authentication process will be enforced only if the capsule has
> > > > > >an attribute, IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED.
> > > > > >
> > > > >
> > > > > That would be a security desaster.
> > > >
> > > > The requirement that I mentioned above is clearly described
> > > > in UEFI specification.
> > > > If you think that it is a disaster, please discuss the topic
> > > > in UEFI Forum first.
> > >
> > > I confirmed UEFI specification, version 2.7, Section.23.1
> > > the last of EFI_FIRMWARE_MANAGEMENT_PROTOCOL.GetImageInfo()
> > >
> > > -----------------
> > > If IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED is supported and clear, then
> > > authentication is not required to perform the firmware image operations.
> > > -----------------
> >
> > Thank you for citing this.
> >
> > > Oh, this is really crazy because deciding whether to authenticate the
> > > suspicious
> > > package or not, depends on whether the package said "please
> > > authenticate me" or not. :D
> >
> > Well, the attributes can been fetched with GetInfo API, but
> > how it is managed depends on the implementation of FMP drivers.
> >
> > As I proposed somewhere else, those attributes should be
> > maintained in a separate place (maybe as part of system's policy),
> > presumably ESRT or platform-specific internal database?
>
> FWIW I personally don't think we should even have a config option. But even
> if we did it certainly must not be dictated by a hardware config.
>
> When you install distro packages you accept whatever dependencies the
> package has. mkeficapsule is a capsule creation and signing tool.  I don't
> see any reason for keeping the creation and signing apart.

My question is, since the U-Boot binary is heavily dependent on the target
platform, can we split the u-boot.bin creation (may include embedding keys)
and the capsule file creation (including signing)?

Thank you,

-- 
Masami Hiramatsu

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

* [PATCH 1/4] tools: mkeficapsule: add firmwware image signing
  2021-05-13 16:12                         ` Masami Hiramatsu
@ 2021-05-13 16:32                           ` Heinrich Schuchardt
  2021-05-13 16:42                             ` Ilias Apalodimas
  2021-05-14  4:13                             ` AKASHI Takahiro
  0 siblings, 2 replies; 55+ messages in thread
From: Heinrich Schuchardt @ 2021-05-13 16:32 UTC (permalink / raw)
  To: u-boot

On 5/13/21 6:12 PM, Masami Hiramatsu wrote:
> 2021?5?13?(?) 19:27 Ilias Apalodimas <ilias.apalodimas@linaro.org>:
>>
>> On Thu, May 13, 2021 at 05:38:51PM +0900, AKASHI Takahiro wrote:
>>> On Thu, May 13, 2021 at 05:18:36PM +0900, Masami Hiramatsu wrote:
>>>> 2021?5?13?(?) 16:24 AKASHI Takahiro <takahiro.akashi@linaro.org>:
>>>>
>>>>>>>>> BTW, IMHO, if u-boot.bin can not find the ESL in the device tree,
>>>>>>>>> it should skip authentication too.
>>>>>>>>
>>>>>>>> In this case the capsule should be rejected (if
>>>>>>>> CONFIG_EFI_CAPSULE_AUTHENTICATE=y).
>>>>>>>
>>>>>>> That's basically right.
>>>>>>> But as I mentioned in my comment against Sughosh's patch,
>>>>>>> the authentication process will be enforced only if the capsule has
>>>>>>> an attribute, IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED.
>>>>>>>
>>>>>>
>>>>>> That would be a security desaster.
>>>>>
>>>>> The requirement that I mentioned above is clearly described
>>>>> in UEFI specification.
>>>>> If you think that it is a disaster, please discuss the topic
>>>>> in UEFI Forum first.
>>>>
>>>> I confirmed UEFI specification, version 2.7, Section.23.1
>>>> the last of EFI_FIRMWARE_MANAGEMENT_PROTOCOL.GetImageInfo()
>>>>
>>>> -----------------
>>>> If IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED is supported and clear, then
>>>> authentication is not required to perform the firmware image operations.
>>>> -----------------
>>>
>>> Thank you for citing this.
>>>
>>>> Oh, this is really crazy because deciding whether to authenticate the
>>>> suspicious
>>>> package or not, depends on whether the package said "please
>>>> authenticate me" or not. :D
>>>
>>> Well, the attributes can been fetched with GetInfo API, but
>>> how it is managed depends on the implementation of FMP drivers.
>>>
>>> As I proposed somewhere else, those attributes should be
>>> maintained in a separate place (maybe as part of system's policy),
>>> presumably ESRT or platform-specific internal database?
>>
>> FWIW I personally don't think we should even have a config option. But even
>> if we did it certainly must not be dictated by a hardware config.
>>
>> When you install distro packages you accept whatever dependencies the
>> package has. mkeficapsule is a capsule creation and signing tool.  I don't
>> see any reason for keeping the creation and signing apart.
>
> My question is, since the U-Boot binary is heavily dependent on the target
> platform, can we split the u-boot.bin creation (may include embedding keys)
> and the capsule file creation (including signing)?

Building U-Boot and creating a capsule are totally separate. Maybe you
get the first capsule years after you buy your board. But this should
not stop us from building mkeficapsule when building U-Boot.

If you want to build tools only, you can do so with 'make tools'. The
tools target must include mkeficapsule irrespective of configuration.

This line in tools/Makefile must be corrected:

-hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
+hostprogs-y += mkeficapsule

Best regards

Heinrich

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

* [PATCH 1/4] tools: mkeficapsule: add firmwware image signing
  2021-05-13 16:32                           ` Heinrich Schuchardt
@ 2021-05-13 16:42                             ` Ilias Apalodimas
  2021-05-14  4:50                               ` AKASHI Takahiro
  2021-05-14  4:13                             ` AKASHI Takahiro
  1 sibling, 1 reply; 55+ messages in thread
From: Ilias Apalodimas @ 2021-05-13 16:42 UTC (permalink / raw)
  To: u-boot

[...]
> > > FWIW I personally don't think we should even have a config option. But even
> > > if we did it certainly must not be dictated by a hardware config.
> > > 
> > > When you install distro packages you accept whatever dependencies the
> > > package has. mkeficapsule is a capsule creation and signing tool.  I don't
> > > see any reason for keeping the creation and signing apart.
> > 
> > My question is, since the U-Boot binary is heavily dependent on the target
> > platform, can we split the u-boot.bin creation (may include embedding keys)
> > and the capsule file creation (including signing)?
> 
> Building U-Boot and creating a capsule are totally separate. Maybe you
> get the first capsule years after you buy your board. But this should
> not stop us from building mkeficapsule when building U-Boot.
> 

Based on what was discussed in the thread waht I think would make more
sense is:
- Build u-boot and use the script Akashi sent to inject the certificate. 
  Whether we create a single binary (always signed if a config option is
  there) or 2 binaries (1 signed. 1 unsigned) is an implemetation detail
  and I am fine with either.
- Use mkefi capsule to create the final capsule

> If you want to build tools only, you can do so with 'make tools'. The
> tools target must include mkeficapsule irrespective of configuration.
> 
> This line in tools/Makefile must be corrected:
> 
> -hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
> +hostprogs-y += mkeficapsule

So that's the point exactly. Building the tool is completely disjoint from
building a u-boot binary.   Also you usually start adding config options to
an app, when it starts getting to big and you want discrete functionality. 
I don't see any reason for making a simple tool, which is supposed to do 2
things (create/sign), require config options and more over config options
*for U-Boot*. I also think it's extremely unlikely to get any working distro 
without libssl. 

> 
> Best regards
> 
> Heinrich

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

* [PATCH 2/4] tools: mkeficapsule: remove device-tree related operation
  2021-05-13  7:13               ` AKASHI Takahiro
@ 2021-05-13 17:42                 ` Heinrich Schuchardt
  2021-05-14  2:21                   ` [PATCH 2/4] tools: mkeficapsule: remove device-tree related operationy AKASHI Takahiro
  2021-05-14  2:23                   ` [PATCH 2/4] tools: mkeficapsule: remove device-tree related operation Masami Hiramatsu
  0 siblings, 2 replies; 55+ messages in thread
From: Heinrich Schuchardt @ 2021-05-13 17:42 UTC (permalink / raw)
  To: u-boot

On 5/13/21 9:13 AM, AKASHI Takahiro wrote:
> On Thu, May 13, 2021 at 07:08:12AM +0200, Heinrich Schuchardt wrote:
>> On 5/13/21 4:33 AM, AKASHI Takahiro wrote:
>>> On Wed, May 12, 2021 at 12:01:32PM +0200, Heinrich Schuchardt wrote:
>>>> On 12.05.21 10:01, Ilias Apalodimas wrote:
>>>>> On Wed, May 12, 2021 at 04:49:02PM +0900, Masami Hiramatsu wrote:
>>>>>> Hi Ilias,
>>>>>>
>>>>>> 2021?5?12?(?) 16:21 Ilias Apalodimas <ilias.apalodimas@linaro.org>:
>>>>>>>
>>>>>>> Akashi-san,
>>>>>>>
>>>>>>> On Wed, May 12, 2021 at 01:57:51PM +0900, AKASHI Takahiro wrote:
>>>>>>>> As we discussed, "-K" and "-D" options have nothing to do with
>>>>>>>> creating a capsule file. The same result can be obtained by
>>>>>>>> using standard commands like:
>>>>>>>>     === signature.dts ===
>>>>>>>>     /dts-v1/;
>>>>>>>>     /plugin/;
>>>>>>>>
>>>>>>>>     &{/} {
>>>>>>>>           signature {
>>>>>>>>                   capsule-key = /incbin/("SIGNER.esl");
>>>>>>>>           };
>>>>>>>>     };
>>>>>>>>     ===
>>>>>>>>     $ dtc -@ -I dts -O dtb -o signature.dtbo signature.dts
>>>>>>>>     $ fdtoverlay -i test.dtb -o test_sig.dtb -v signature.dtbo
>>>>>>>>
>>>>>>>> So just remove this feature.
>>>>>>>> (Effectively revert the commit 322c813f4bec ("mkeficapsule: Add support
>>>>>>>> for embedding public key in a dtb").)
>>>>>>>>
>>>>>>>> The same feature is implemented by a shell script (tools/fdtsig.sh).
>>>>>>>
>>>>>>>
>>>>>>> The only reason I can see to keep this, is if mkeficapsule gets included
>>>>>>> intro distro packages in the future.  That would make end users life a bit
>>>>>>> easier, since they would need a single binary to create the whole
>>>>>>> CapsuleUpdate sequence.
>>>>>>
>>>>>> Hmm, I think it is better to write a manpage of mkeficapsule which
>>>>>> also describes
>>>>>> how to embed the key into dtb as in the above example if it is so short.
>>>>>> Or, distros can package the above shell script with mkeficapsule.
>>>>>>
>>>>>> Embedding a key and signing a capsule are different operations but
>>>>>> using the same tool may confuse users (at least me).
>>>>>
>>>>> Sure fair enough.  I am merely pointing out we need a way to explain all of
>>>>> those to users.
>>>>
>>>> This is currently our only documentation:
>>>>
>>>> https://u-boot.readthedocs.io/en/latest/board/emulation/qemu_capsule_update.html?highlight=mkeficapsule
>>>
>>> As I mentioned several times (and TODO in the cover letter),
>>> this text must be reviewed, revised and generalized
>>> as a platform-independent document.
>>> It contains a couple of errors.
>>>
>>>> For mkimage we have a man-page ./doc/mkimage.1 that is packaged with
>>>> Debians u-boot-tools package. Please, provide a similar man-page as
>>>> ./doc/mkeficapsule.1.
>>>
>>> So after all do you agree to removing "-K/-D"?
>>
>> I see no need to replicate in U-Boot what is already in the device tree
>> compiler package.
>
> This is another reason that we should remove Sughosh's change.
>
>> In the current workflow the fdt command is used to load the public key.
>> This is insecure and not usable for production.
>
> I totally disagree.
> Why is using fdt command (what do you mean by fdt command, dtc/fdtoverlay?)
> insecure?

A user can load an insecure capsule.

The fdt command is in /cmd/fdt.c and you are referring to it in
board/emulation/qemu_capsule_update.rst.

>
>> The public key used to verify the capsule must be built into the U-Boot
>> binary. This will supplant the -K and -D options.
>
> I don't get your point. You don't understand my code.
>
> Even with Sughosh's original patch, the public key (as I said,
> it is not a public key but a X509 certificate in ESL format) is
> embedded in the U-Boot's "control device tree".

No, the ESL file it is not built into U-Boot's control device tree.

A user is loading it and updating the control device tree.

You shouldn't trust anything a user has loaded. You need at least the
public key of the root CA built somewhere into U-Boot.

The 'fdt resize' command may overwrite code. This is not what you want
to do with the control device tree.

If CONFIG_OF_LIVE=y, the active device tree is not at $fdtcontroladdr
but in a hierarchical structure. You cannot update it via the fdt command.

>
> Even after applying my patch, this is true.
>
> Or are you insisting that the key should not be in the device tree?

The public key of the root CA must not be in a place where it can be
changed by a user while the device is in deployed mode.

The device-tree based design is a good feasibility study but not
suitable for production.

Best regards

Heinrich

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

* [PATCH 1/4] tools: mkeficapsule: add firmwware image signing
  2021-05-13  8:18                   ` Masami Hiramatsu
  2021-05-13  8:38                     ` AKASHI Takahiro
@ 2021-05-13 18:25                     ` Heinrich Schuchardt
  2021-05-14  6:19                       ` AKASHI Takahiro
  1 sibling, 1 reply; 55+ messages in thread
From: Heinrich Schuchardt @ 2021-05-13 18:25 UTC (permalink / raw)
  To: u-boot

On 5/13/21 10:18 AM, Masami Hiramatsu wrote:
> 2021?5?13?(?) 16:24 AKASHI Takahiro <takahiro.akashi@linaro.org>:
>
>>>>>> BTW, IMHO, if u-boot.bin can not find the ESL in the device tree,
>>>>>> it should skip authentication too.
>>>>>
>>>>> In this case the capsule should be rejected (if
>>>>> CONFIG_EFI_CAPSULE_AUTHENTICATE=y).
>>>>
>>>> That's basically right.
>>>> But as I mentioned in my comment against Sughosh's patch,
>>>> the authentication process will be enforced only if the capsule has
>>>> an attribute, IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED.
>>>>
>>>
>>> That would be a security desaster.
>>
>> The requirement that I mentioned above is clearly described
>> in UEFI specification.
>> If you think that it is a disaster, please discuss the topic
>> in UEFI Forum first.
>
> I confirmed UEFI specification, version 2.7, Section.23.1
> the last of EFI_FIRMWARE_MANAGEMENT_PROTOCOL.GetImageInfo()
>
> -----------------
> If IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED is supported and clear, then
> authentication is not required to perform the firmware image operations.
> -----------------

IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED bit is a property of the FMP driver.

Best regards

Heinrich

>
> Oh, this is really crazy because deciding whether to authenticate the
> suspicious
> package or not, depends on whether the package said "please
> authenticate me" or not. :D
>
> Anyway, since this behavior follows the specification, it should be
> kept by default,
> but also IMHO, there should be a CONFIG option to enforce capsule
> authentication always.
>
> Thank you,
>
>
>

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

* [PATCH 2/4] tools: mkeficapsule: remove device-tree related operationy
  2021-05-13 17:42                 ` Heinrich Schuchardt
@ 2021-05-14  2:21                   ` AKASHI Takahiro
  2021-05-14  2:23                   ` [PATCH 2/4] tools: mkeficapsule: remove device-tree related operation Masami Hiramatsu
  1 sibling, 0 replies; 55+ messages in thread
From: AKASHI Takahiro @ 2021-05-14  2:21 UTC (permalink / raw)
  To: u-boot

Heinrich,

You are discussing two different issues:
1. if we should remove "-K/-D" options from mkeficapsule
2. if it is safe or not to store a key in device tree

It makes the discussion in this thread confusing.

On Thu, May 13, 2021 at 07:42:23PM +0200, Heinrich Schuchardt wrote:
> On 5/13/21 9:13 AM, AKASHI Takahiro wrote:
> > On Thu, May 13, 2021 at 07:08:12AM +0200, Heinrich Schuchardt wrote:
> > > On 5/13/21 4:33 AM, AKASHI Takahiro wrote:
> > > > On Wed, May 12, 2021 at 12:01:32PM +0200, Heinrich Schuchardt wrote:
> > > > > On 12.05.21 10:01, Ilias Apalodimas wrote:
> > > > > > On Wed, May 12, 2021 at 04:49:02PM +0900, Masami Hiramatsu wrote:
> > > > > > > Hi Ilias,
> > > > > > > 
> > > > > > > 2021?5?12?(?) 16:21 Ilias Apalodimas <ilias.apalodimas@linaro.org>:
> > > > > > > > 
> > > > > > > > Akashi-san,
> > > > > > > > 
> > > > > > > > On Wed, May 12, 2021 at 01:57:51PM +0900, AKASHI Takahiro wrote:
> > > > > > > > > As we discussed, "-K" and "-D" options have nothing to do with
> > > > > > > > > creating a capsule file. The same result can be obtained by
> > > > > > > > > using standard commands like:
> > > > > > > > >     === signature.dts ===
> > > > > > > > >     /dts-v1/;
> > > > > > > > >     /plugin/;
> > > > > > > > > 
> > > > > > > > >     &{/} {
> > > > > > > > >           signature {
> > > > > > > > >                   capsule-key = /incbin/("SIGNER.esl");
> > > > > > > > >           };
> > > > > > > > >     };
> > > > > > > > >     ===
> > > > > > > > >     $ dtc -@ -I dts -O dtb -o signature.dtbo signature.dts
> > > > > > > > >     $ fdtoverlay -i test.dtb -o test_sig.dtb -v signature.dtbo
> > > > > > > > > 
> > > > > > > > > So just remove this feature.
> > > > > > > > > (Effectively revert the commit 322c813f4bec ("mkeficapsule: Add support
> > > > > > > > > for embedding public key in a dtb").)
> > > > > > > > > 
> > > > > > > > > The same feature is implemented by a shell script (tools/fdtsig.sh).
> > > > > > > > 
> > > > > > > > 
> > > > > > > > The only reason I can see to keep this, is if mkeficapsule gets included
> > > > > > > > intro distro packages in the future.  That would make end users life a bit
> > > > > > > > easier, since they would need a single binary to create the whole
> > > > > > > > CapsuleUpdate sequence.
> > > > > > > 
> > > > > > > Hmm, I think it is better to write a manpage of mkeficapsule which
> > > > > > > also describes
> > > > > > > how to embed the key into dtb as in the above example if it is so short.
> > > > > > > Or, distros can package the above shell script with mkeficapsule.
> > > > > > > 
> > > > > > > Embedding a key and signing a capsule are different operations but
> > > > > > > using the same tool may confuse users (at least me).
> > > > > > 
> > > > > > Sure fair enough.  I am merely pointing out we need a way to explain all of
> > > > > > those to users.
> > > > > 
> > > > > This is currently our only documentation:
> > > > > 
> > > > > https://u-boot.readthedocs.io/en/latest/board/emulation/qemu_capsule_update.html?highlight=mkeficapsule
> > > > 
> > > > As I mentioned several times (and TODO in the cover letter),
> > > > this text must be reviewed, revised and generalized
> > > > as a platform-independent document.
> > > > It contains a couple of errors.
> > > > 
> > > > > For mkimage we have a man-page ./doc/mkimage.1 that is packaged with
> > > > > Debians u-boot-tools package. Please, provide a similar man-page as
> > > > > ./doc/mkeficapsule.1.
> > > > 
> > > > So after all do you agree to removing "-K/-D"?

Regarding (1), you should clarify your opinion here first.

> > > I see no need to replicate in U-Boot what is already in the device tree
> > > compiler package.
> > 
> > This is another reason that we should remove Sughosh's change.

Hereafter, you are talking about (2).

> > > In the current workflow the fdt command is used to load the public key.
> > > This is insecure and not usable for production.
> > 
> > I totally disagree.
> > Why is using fdt command (what do you mean by fdt command, dtc/fdtoverlay?)
> > insecure?
> 
> A user can load an insecure capsule.
> 
> The fdt command is in /cmd/fdt.c and you are referring to it in
> board/emulation/qemu_capsule_update.rst.

OK, you meant U-Boot's fdt command.

> > 
> > > The public key used to verify the capsule must be built into the U-Boot
> > > binary. This will supplant the -K and -D options.
> > 
> > I don't get your point. You don't understand my code.
> > 
> > Even with Sughosh's original patch, the public key (as I said,
> > it is not a public key but a X509 certificate in ESL format) is
> > embedded in the U-Boot's "control device tree".
> 
> No, the ESL file it is not built into U-Boot's control device tree.

What I meant by "control device tree" is the feature
provided by OF_CONTROL+OF_EMBED with Sughosh's patch[1]
which is also a prerequisite for my patch series.

!config OF_EMBED
!        bool "Embedded DTB for DT control"
!        help
!          If this option is enabled, the device tree will be picked up and
!          built into the U-Boot image.

> A user is loading it and updating the control device tree.
> 
> You shouldn't trust anything a user has loaded. You need at least the
> public key of the root CA built somewhere into U-Boot.
> 
> The 'fdt resize' command may overwrite code. This is not what you want
> to do with the control device tree.
> 
> If CONFIG_OF_LIVE=y, the active device tree is not at $fdtcontroladdr
> but in a hierarchical structure. You cannot update it via the fdt command.
> 
> > 
> > Even after applying my patch, this is true.
> > 
> > Or are you insisting that the key should not be in the device tree?
> 
> The public key of the root CA must not be in a place where it can be
> changed by a user while the device is in deployed mode.

UEFI secure boot and capsule update are totally independent concepts
as we discussed a long time ago. The notion of "deployed mode" is
only valid for secure boot.

> The device-tree based design is a good feasibility study but not
> suitable for production.

Nevertheless,
I agree, even I have already mentioned the similar concern in [2],
additionally saying we should turn off "fdt" command.

[1] https://lists.denx.de/pipermail/u-boot/2021-April/447183.html
[2] (oops, my message seems to have been lost.)

-Takahiro Akashi

> Best regards
> 
> Heinrich

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

* [PATCH 2/4] tools: mkeficapsule: remove device-tree related operation
  2021-05-13 17:42                 ` Heinrich Schuchardt
  2021-05-14  2:21                   ` [PATCH 2/4] tools: mkeficapsule: remove device-tree related operationy AKASHI Takahiro
@ 2021-05-14  2:23                   ` Masami Hiramatsu
  1 sibling, 0 replies; 55+ messages in thread
From: Masami Hiramatsu @ 2021-05-14  2:23 UTC (permalink / raw)
  To: u-boot

Hi Heinrich,

2021?5?14?(?) 2:42 Heinrich Schuchardt <xypron.glpk@gmx.de>:
>
> On 5/13/21 9:13 AM, AKASHI Takahiro wrote:
> > On Thu, May 13, 2021 at 07:08:12AM +0200, Heinrich Schuchardt wrote:
> >> On 5/13/21 4:33 AM, AKASHI Takahiro wrote:
> >>> On Wed, May 12, 2021 at 12:01:32PM +0200, Heinrich Schuchardt wrote:
> >>>> On 12.05.21 10:01, Ilias Apalodimas wrote:
> >>>>> On Wed, May 12, 2021 at 04:49:02PM +0900, Masami Hiramatsu wrote:
> >>>>>> Hi Ilias,
> >>>>>>
> >>>>>> 2021?5?12?(?) 16:21 Ilias Apalodimas <ilias.apalodimas@linaro.org>:
> >>>>>>>
> >>>>>>> Akashi-san,
> >>>>>>>
> >>>>>>> On Wed, May 12, 2021 at 01:57:51PM +0900, AKASHI Takahiro wrote:
> >>>>>>>> As we discussed, "-K" and "-D" options have nothing to do with
> >>>>>>>> creating a capsule file. The same result can be obtained by
> >>>>>>>> using standard commands like:
> >>>>>>>>     === signature.dts ===
> >>>>>>>>     /dts-v1/;
> >>>>>>>>     /plugin/;
> >>>>>>>>
> >>>>>>>>     &{/} {
> >>>>>>>>           signature {
> >>>>>>>>                   capsule-key = /incbin/("SIGNER.esl");
> >>>>>>>>           };
> >>>>>>>>     };
> >>>>>>>>     ===
> >>>>>>>>     $ dtc -@ -I dts -O dtb -o signature.dtbo signature.dts
> >>>>>>>>     $ fdtoverlay -i test.dtb -o test_sig.dtb -v signature.dtbo
> >>>>>>>>
> >>>>>>>> So just remove this feature.
> >>>>>>>> (Effectively revert the commit 322c813f4bec ("mkeficapsule: Add support
> >>>>>>>> for embedding public key in a dtb").)
> >>>>>>>>
> >>>>>>>> The same feature is implemented by a shell script (tools/fdtsig.sh).
> >>>>>>>
> >>>>>>>
> >>>>>>> The only reason I can see to keep this, is if mkeficapsule gets included
> >>>>>>> intro distro packages in the future.  That would make end users life a bit
> >>>>>>> easier, since they would need a single binary to create the whole
> >>>>>>> CapsuleUpdate sequence.
> >>>>>>
> >>>>>> Hmm, I think it is better to write a manpage of mkeficapsule which
> >>>>>> also describes
> >>>>>> how to embed the key into dtb as in the above example if it is so short.
> >>>>>> Or, distros can package the above shell script with mkeficapsule.
> >>>>>>
> >>>>>> Embedding a key and signing a capsule are different operations but
> >>>>>> using the same tool may confuse users (at least me).
> >>>>>
> >>>>> Sure fair enough.  I am merely pointing out we need a way to explain all of
> >>>>> those to users.
> >>>>
> >>>> This is currently our only documentation:
> >>>>
> >>>> https://u-boot.readthedocs.io/en/latest/board/emulation/qemu_capsule_update.html?highlight=mkeficapsule
> >>>
> >>> As I mentioned several times (and TODO in the cover letter),
> >>> this text must be reviewed, revised and generalized
> >>> as a platform-independent document.
> >>> It contains a couple of errors.
> >>>
> >>>> For mkimage we have a man-page ./doc/mkimage.1 that is packaged with
> >>>> Debians u-boot-tools package. Please, provide a similar man-page as
> >>>> ./doc/mkeficapsule.1.
> >>>
> >>> So after all do you agree to removing "-K/-D"?
> >>
> >> I see no need to replicate in U-Boot what is already in the device tree
> >> compiler package.
> >
> > This is another reason that we should remove Sughosh's change.
> >
> >> In the current workflow the fdt command is used to load the public key.
> >> This is insecure and not usable for production.
> >
> > I totally disagree.
> > Why is using fdt command (what do you mean by fdt command, dtc/fdtoverlay?)
> > insecure?
>
> A user can load an insecure capsule.
>
> The fdt command is in /cmd/fdt.c and you are referring to it in
> board/emulation/qemu_capsule_update.rst.

Hmm, this seems like a testing manual.

>
> >
> >> The public key used to verify the capsule must be built into the U-Boot
> >> binary. This will supplant the -K and -D options.
> >
> > I don't get your point. You don't understand my code.
> >
> > Even with Sughosh's original patch, the public key (as I said,
> > it is not a public key but a X509 certificate in ESL format) is
> > embedded in the U-Boot's "control device tree".
>
> No, the ESL file it is not built into U-Boot's control device tree.
>
> A user is loading it and updating the control device tree.

In my case (I've tested it on the DeveloperBox), the key is embedded in
the U-Boot's control device tree.

>
> You shouldn't trust anything a user has loaded. You need at least the
> public key of the root CA built somewhere into U-Boot.

However, I agreed this point. If we use the public key in the device
tree, it must be loaded as a part of U-Boot itself, and must not overwritten
by the user given fdt.

>
> The 'fdt resize' command may overwrite code. This is not what you want
> to do with the control device tree.
>
> If CONFIG_OF_LIVE=y, the active device tree is not at $fdtcontroladdr
> but in a hierarchical structure. You cannot update it via the fdt command.
>
> >
> > Even after applying my patch, this is true.
> >
> > Or are you insisting that the key should not be in the device tree?
>
> The public key of the root CA must not be in a place where it can be
> changed by a user while the device is in deployed mode.
>
> The device-tree based design is a good feasibility study but not
> suitable for production.

Therefore, there is no reason mkeficapsule keeps -K/-D anymore,
because embedding public key will be done in u-boot.bin build process
(or embedded in the hardware via platform dependent interface.)


Thank you,

-- 
Masami Hiramatsu

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

* [PATCH 1/4] tools: mkeficapsule: add firmwware image signing
  2021-05-13 16:32                           ` Heinrich Schuchardt
  2021-05-13 16:42                             ` Ilias Apalodimas
@ 2021-05-14  4:13                             ` AKASHI Takahiro
  1 sibling, 0 replies; 55+ messages in thread
From: AKASHI Takahiro @ 2021-05-14  4:13 UTC (permalink / raw)
  To: u-boot

On Thu, May 13, 2021 at 06:32:13PM +0200, Heinrich Schuchardt wrote:
> On 5/13/21 6:12 PM, Masami Hiramatsu wrote:
> > 2021?5?13?(?) 19:27 Ilias Apalodimas <ilias.apalodimas@linaro.org>:
> > > 
> > > On Thu, May 13, 2021 at 05:38:51PM +0900, AKASHI Takahiro wrote:
> > > > On Thu, May 13, 2021 at 05:18:36PM +0900, Masami Hiramatsu wrote:
> > > > > 2021?5?13?(?) 16:24 AKASHI Takahiro <takahiro.akashi@linaro.org>:
> > > > > 
> > > > > > > > > > BTW, IMHO, if u-boot.bin can not find the ESL in the device tree,
> > > > > > > > > > it should skip authentication too.
> > > > > > > > > 
> > > > > > > > > In this case the capsule should be rejected (if
> > > > > > > > > CONFIG_EFI_CAPSULE_AUTHENTICATE=y).
> > > > > > > > 
> > > > > > > > That's basically right.
> > > > > > > > But as I mentioned in my comment against Sughosh's patch,
> > > > > > > > the authentication process will be enforced only if the capsule has
> > > > > > > > an attribute, IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED.
> > > > > > > > 
> > > > > > > 
> > > > > > > That would be a security desaster.
> > > > > > 
> > > > > > The requirement that I mentioned above is clearly described
> > > > > > in UEFI specification.
> > > > > > If you think that it is a disaster, please discuss the topic
> > > > > > in UEFI Forum first.
> > > > > 
> > > > > I confirmed UEFI specification, version 2.7, Section.23.1
> > > > > the last of EFI_FIRMWARE_MANAGEMENT_PROTOCOL.GetImageInfo()
> > > > > 
> > > > > -----------------
> > > > > If IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED is supported and clear, then
> > > > > authentication is not required to perform the firmware image operations.
> > > > > -----------------
> > > > 
> > > > Thank you for citing this.
> > > > 
> > > > > Oh, this is really crazy because deciding whether to authenticate the
> > > > > suspicious
> > > > > package or not, depends on whether the package said "please
> > > > > authenticate me" or not. :D
> > > > 
> > > > Well, the attributes can been fetched with GetInfo API, but
> > > > how it is managed depends on the implementation of FMP drivers.
> > > > 
> > > > As I proposed somewhere else, those attributes should be
> > > > maintained in a separate place (maybe as part of system's policy),
> > > > presumably ESRT or platform-specific internal database?
> > > 
> > > FWIW I personally don't think we should even have a config option. But even
> > > if we did it certainly must not be dictated by a hardware config.
> > > 
> > > When you install distro packages you accept whatever dependencies the
> > > package has. mkeficapsule is a capsule creation and signing tool.  I don't
> > > see any reason for keeping the creation and signing apart.
> > 
> > My question is, since the U-Boot binary is heavily dependent on the target
> > platform, can we split the u-boot.bin creation (may include embedding keys)
> > and the capsule file creation (including signing)?
> 
> Building U-Boot and creating a capsule are totally separate. Maybe you
> get the first capsule years after you buy your board. But this should
> not stop us from building mkeficapsule when building U-Boot.
> 
> If you want to build tools only, you can do so with 'make tools'. The
> tools target must include mkeficapsule irrespective of configuration.

So far, we have been discussing whether CONFIG_EFI_CAPSULE_AUTHENTICATE
(or "host" version like CONFIG_HOST_EFI_CAPSULE_AUTHENITATE)
be honored in mkeficapsule.c or not.

> This line in tools/Makefile must be corrected:
> 
> -hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
> +hostprogs-y += mkeficapsule

There exist lots of "hostprogs-$(CONFIG_...)" targets.
I think that this is a common practice in U-Boot.

-Takahiro Akashi

> Best regards
> 
> Heinrich

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

* [PATCH 1/4] tools: mkeficapsule: add firmwware image signing
  2021-05-13 16:42                             ` Ilias Apalodimas
@ 2021-05-14  4:50                               ` AKASHI Takahiro
  2021-05-14  7:56                                 ` Ilias Apalodimas
  0 siblings, 1 reply; 55+ messages in thread
From: AKASHI Takahiro @ 2021-05-14  4:50 UTC (permalink / raw)
  To: u-boot

On Thu, May 13, 2021 at 07:42:04PM +0300, Ilias Apalodimas wrote:
> [...]
> > > > FWIW I personally don't think we should even have a config option. But even
> > > > if we did it certainly must not be dictated by a hardware config.
> > > > 
> > > > When you install distro packages you accept whatever dependencies the
> > > > package has. mkeficapsule is a capsule creation and signing tool.  I don't
> > > > see any reason for keeping the creation and signing apart.
> > > 
> > > My question is, since the U-Boot binary is heavily dependent on the target
> > > platform, can we split the u-boot.bin creation (may include embedding keys)
> > > and the capsule file creation (including signing)?
> > 
> > Building U-Boot and creating a capsule are totally separate. Maybe you
> > get the first capsule years after you buy your board. But this should
> > not stop us from building mkeficapsule when building U-Boot.
> > 
> 
> Based on what was discussed in the thread waht I think would make more
> sense is:
> - Build u-boot and use the script Akashi sent to inject the certificate. 
>   Whether we create a single binary (always signed if a config option is
>   there) or 2 binaries (1 signed. 1 unsigned) is an implemetation detail
>   and I am fine with either.

Let me make clear: "a single binary or 2 binaries" is not
an implementation detail, but it's a matter of user's (, distro's
or whoever wants to provide a capsule) policy.

> - Use mkefi capsule to create the final capsule

If signing feature is enabled in mkeficapsule, you can create
both a signed capsule and an unsigned capsule.
And yet, some users may totally had no need to authentication
for firmware update using UEFI interfaces on their systems.
For them, signing should be able to be disabled.

> 
> > If you want to build tools only, you can do so with 'make tools'. The
> > tools target must include mkeficapsule irrespective of configuration.
> > 
> > This line in tools/Makefile must be corrected:
> > 
> > -hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
> > +hostprogs-y += mkeficapsule
> 
> So that's the point exactly. Building the tool is completely disjoint from
> building a u-boot binary.   Also you usually start adding config options to
> an app, when it starts getting to big and you want discrete functionality. 

I don't get your point.
As far as we maintain CONFIG_(HOST_)_EFI_CAPSULE_AUTHENTICATE,
we can and should guarantee the compatibility.

> I don't see any reason for making a simple tool, which is supposed to do 2
> things (create/sign), require config options and more over config options
> *for U-Boot*.

I don't get you point neither.

> I also think it's extremely unlikely to get any working distro 
> without libssl. 

Again, (major) distros are ones of users.
There may be bunch of users who may build/maintain their systems
on their way and not expect any authentication.

Having said that,
coincidentally there is happening a similar discussion
about building host tools and host-specific configs among Simon, Tom
and Alex[1].
(The background for the discussion is a bit different though.)

I'd like to see and follow the direction to be agreed there.

[1] https://lists.denx.de/pipermail/u-boot/2021-May/449050.html

-Takahiro Akashi

> > 
> > Best regards
> > 
> > Heinrich

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

* [PATCH 1/4] tools: mkeficapsule: add firmwware image signing
  2021-05-13 18:25                     ` Heinrich Schuchardt
@ 2021-05-14  6:19                       ` AKASHI Takahiro
  2021-05-14  6:59                         ` Heinrich Schuchardt
  0 siblings, 1 reply; 55+ messages in thread
From: AKASHI Takahiro @ 2021-05-14  6:19 UTC (permalink / raw)
  To: u-boot

On Thu, May 13, 2021 at 08:25:56PM +0200, Heinrich Schuchardt wrote:
> On 5/13/21 10:18 AM, Masami Hiramatsu wrote:
> > 2021?5?13?(?) 16:24 AKASHI Takahiro <takahiro.akashi@linaro.org>:
> > 
> > > > > > > BTW, IMHO, if u-boot.bin can not find the ESL in the device tree,
> > > > > > > it should skip authentication too.
> > > > > > 
> > > > > > In this case the capsule should be rejected (if
> > > > > > CONFIG_EFI_CAPSULE_AUTHENTICATE=y).
> > > > > 
> > > > > That's basically right.
> > > > > But as I mentioned in my comment against Sughosh's patch,
> > > > > the authentication process will be enforced only if the capsule has
> > > > > an attribute, IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED.
> > > > > 
> > > > 
> > > > That would be a security desaster.
> > > 
> > > The requirement that I mentioned above is clearly described
> > > in UEFI specification.
> > > If you think that it is a disaster, please discuss the topic
> > > in UEFI Forum first.
> > 
> > I confirmed UEFI specification, version 2.7, Section.23.1
> > the last of EFI_FIRMWARE_MANAGEMENT_PROTOCOL.GetImageInfo()
> > 
> > -----------------
> > If IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED is supported and clear, then
> > authentication is not required to perform the firmware image operations.
> > -----------------
> 
> IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED bit is a property of the FMP driver.

Yes, it is. But if the attribute is not changeable at all,
why do we need this flag?
Why does a "firmware image descriptor" hold two distinct
member fields, "AttributesSupported" and "AttributesSetting"?
What does "Setting" mean? Who sets what, and when?

-Takahiro Akashi

> Best regards
> 
> Heinrich
> 
> > 
> > Oh, this is really crazy because deciding whether to authenticate the
> > suspicious
> > package or not, depends on whether the package said "please
> > authenticate me" or not. :D
> > 
> > Anyway, since this behavior follows the specification, it should be
> > kept by default,
> > but also IMHO, there should be a CONFIG option to enforce capsule
> > authentication always.
> > 
> > Thank you,
> > 
> > 
> > 
> 

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

* [PATCH 1/4] tools: mkeficapsule: add firmwware image signing
  2021-05-14  6:19                       ` AKASHI Takahiro
@ 2021-05-14  6:59                         ` Heinrich Schuchardt
  2021-05-14  7:13                           ` AKASHI Takahiro
  0 siblings, 1 reply; 55+ messages in thread
From: Heinrich Schuchardt @ 2021-05-14  6:59 UTC (permalink / raw)
  To: u-boot

On 5/14/21 8:19 AM, AKASHI Takahiro wrote:
> On Thu, May 13, 2021 at 08:25:56PM +0200, Heinrich Schuchardt wrote:
>> On 5/13/21 10:18 AM, Masami Hiramatsu wrote:
>>> 2021?5?13?(?) 16:24 AKASHI Takahiro <takahiro.akashi@linaro.org>:
>>>
>>>>>>>> BTW, IMHO, if u-boot.bin can not find the ESL in the device tree,
>>>>>>>> it should skip authentication too.
>>>>>>>
>>>>>>> In this case the capsule should be rejected (if
>>>>>>> CONFIG_EFI_CAPSULE_AUTHENTICATE=y).
>>>>>>
>>>>>> That's basically right.
>>>>>> But as I mentioned in my comment against Sughosh's patch,
>>>>>> the authentication process will be enforced only if the capsule has
>>>>>> an attribute, IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED.
>>>>>>
>>>>>
>>>>> That would be a security desaster.
>>>>
>>>> The requirement that I mentioned above is clearly described
>>>> in UEFI specification.
>>>> If you think that it is a disaster, please discuss the topic
>>>> in UEFI Forum first.
>>>
>>> I confirmed UEFI specification, version 2.7, Section.23.1
>>> the last of EFI_FIRMWARE_MANAGEMENT_PROTOCOL.GetImageInfo()
>>>
>>> -----------------
>>> If IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED is supported and clear, then
>>> authentication is not required to perform the firmware image operations.
>>> -----------------
>>
>> IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED bit is a property of the FMP driver.
>
> Yes, it is. But if the attribute is not changeable at all,
> why do we need this flag?
> Why does a "firmware image descriptor" hold two distinct
> member fields, "AttributesSupported" and "AttributesSetting"?
> What does "Setting" mean? Who sets what, and when?

"Setting" means value here. None of these flags is set by the user. It
is the FMP driver that keeps track of the update state and sets the
flags accordingly.

AttributesSupported indicates which bits in AttributesSetting contain
accurate information:

E.g for IMAGE_ATTRIBUTE_IN_USE

AttributesSupported | AttributesSetting | Meaning
--------------------+-------------------+--------------------
0                   | 0                 | state is unknown
0                   | 1                 | state is unknown
1                   | 0                 | image is not in use
1                   | 1                 | image is in use

Some bits indicate a property of the installed image, e.g.
IMAGE_ATTRIBUTE_UEFI_IMAGE.

Some bits indicate a system state. E.g. IMAGE_ATTRIBUTE_IN_USE indicates
if the information reported by the FMP driver is for the currently used
image. This bit might be 0 in AttributesSetting if you have not yet
executed a reset after installing the new image and 1 after the reset.

Best regards

Heinrich
>>
>>>
>>> Oh, this is really crazy because deciding whether to authenticate the
>>> suspicious
>>> package or not, depends on whether the package said "please
>>> authenticate me" or not. :D
>>>
>>> Anyway, since this behavior follows the specification, it should be
>>> kept by default,
>>> but also IMHO, there should be a CONFIG option to enforce capsule
>>> authentication always.
>>>
>>> Thank you,
>>>
>>>
>>>
>>

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

* [PATCH 1/4] tools: mkeficapsule: add firmwware image signing
  2021-05-14  6:59                         ` Heinrich Schuchardt
@ 2021-05-14  7:13                           ` AKASHI Takahiro
  2021-05-14  8:45                             ` Heinrich Schuchardt
  0 siblings, 1 reply; 55+ messages in thread
From: AKASHI Takahiro @ 2021-05-14  7:13 UTC (permalink / raw)
  To: u-boot

On Fri, May 14, 2021 at 08:59:38AM +0200, Heinrich Schuchardt wrote:
> On 5/14/21 8:19 AM, AKASHI Takahiro wrote:
> > On Thu, May 13, 2021 at 08:25:56PM +0200, Heinrich Schuchardt wrote:
> > > On 5/13/21 10:18 AM, Masami Hiramatsu wrote:
> > > > 2021?5?13?(?) 16:24 AKASHI Takahiro <takahiro.akashi@linaro.org>:
> > > > 
> > > > > > > > > BTW, IMHO, if u-boot.bin can not find the ESL in the device tree,
> > > > > > > > > it should skip authentication too.
> > > > > > > > 
> > > > > > > > In this case the capsule should be rejected (if
> > > > > > > > CONFIG_EFI_CAPSULE_AUTHENTICATE=y).
> > > > > > > 
> > > > > > > That's basically right.
> > > > > > > But as I mentioned in my comment against Sughosh's patch,
> > > > > > > the authentication process will be enforced only if the capsule has
> > > > > > > an attribute, IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED.
> > > > > > > 
> > > > > > 
> > > > > > That would be a security desaster.
> > > > > 
> > > > > The requirement that I mentioned above is clearly described
> > > > > in UEFI specification.
> > > > > If you think that it is a disaster, please discuss the topic
> > > > > in UEFI Forum first.
> > > > 
> > > > I confirmed UEFI specification, version 2.7, Section.23.1
> > > > the last of EFI_FIRMWARE_MANAGEMENT_PROTOCOL.GetImageInfo()
> > > > 
> > > > -----------------
> > > > If IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED is supported and clear, then
> > > > authentication is not required to perform the firmware image operations.
> > > > -----------------
> > > 
> > > IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED bit is a property of the FMP driver.
> > 
> > Yes, it is. But if the attribute is not changeable at all,
> > why do we need this flag?
> > Why does a "firmware image descriptor" hold two distinct
> > member fields, "AttributesSupported" and "AttributesSetting"?
> > What does "Setting" mean? Who sets what, and when?
> 
> "Setting" means value here. None of these flags is set by the user. It
> is the FMP driver that keeps track of the update state and sets the
> flags accordingly.
> 
> AttributesSupported indicates which bits in AttributesSetting contain
> accurate information:
> 
> E.g for IMAGE_ATTRIBUTE_IN_USE
> 
> AttributesSupported | AttributesSetting | Meaning
> --------------------+-------------------+--------------------
> 0                   | 0                 | state is unknown
> 0                   | 1                 | state is unknown
> 1                   | 0                 | image is not in use
> 1                   | 1                 | image is in use

We are discussing *_REQUIRED.
Can you give me the same table for *_REQUIRED?

-Takahiro Akashi


> Some bits indicate a property of the installed image, e.g.
> IMAGE_ATTRIBUTE_UEFI_IMAGE.
> 
> Some bits indicate a system state. E.g. IMAGE_ATTRIBUTE_IN_USE indicates
> if the information reported by the FMP driver is for the currently used
> image. This bit might be 0 in AttributesSetting if you have not yet
> executed a reset after installing the new image and 1 after the reset.
> 
> Best regards
> 
> Heinrich
> > > 
> > > > 
> > > > Oh, this is really crazy because deciding whether to authenticate the
> > > > suspicious
> > > > package or not, depends on whether the package said "please
> > > > authenticate me" or not. :D
> > > > 
> > > > Anyway, since this behavior follows the specification, it should be
> > > > kept by default,
> > > > but also IMHO, there should be a CONFIG option to enforce capsule
> > > > authentication always.
> > > > 
> > > > Thank you,
> > > > 
> > > > 
> > > > 
> > > 
> 

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

* [PATCH 1/4] tools: mkeficapsule: add firmwware image signing
  2021-05-14  4:50                               ` AKASHI Takahiro
@ 2021-05-14  7:56                                 ` Ilias Apalodimas
  0 siblings, 0 replies; 55+ messages in thread
From: Ilias Apalodimas @ 2021-05-14  7:56 UTC (permalink / raw)
  To: u-boot

> > 
[...]
> > Based on what was discussed in the thread waht I think would make more
> > sense is:
> > - Build u-boot and use the script Akashi sent to inject the certificate. 
> >   Whether we create a single binary (always signed if a config option is
> >   there) or 2 binaries (1 signed. 1 unsigned) is an implemetation detail
> >   and I am fine with either.
> 
> Let me make clear: "a single binary or 2 binaries" is not
> an implementation detail, but it's a matter of user's (, distro's
> or whoever wants to provide a capsule) policy.
> 
> > - Use mkefi capsule to create the final capsule
> 
> If signing feature is enabled in mkeficapsule, you can create
> both a signed capsule and an unsigned capsule.
> And yet, some users may totally had no need to authentication
> for firmware update using UEFI interfaces on their systems.
> For them, signing should be able to be disabled.
> 

They should use the non signed capsule.

> > 
> > > If you want to build tools only, you can do so with 'make tools'. The
> > > tools target must include mkeficapsule irrespective of configuration.
> > > 
> > > This line in tools/Makefile must be corrected:
> > > 
> > > -hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
> > > +hostprogs-y += mkeficapsule
> > 
> > So that's the point exactly. Building the tool is completely disjoint from
> > building a u-boot binary.   Also you usually start adding config options to
> > an app, when it starts getting to big and you want discrete functionality. 
> 
> I don't get your point.
> As far as we maintain CONFIG_(HOST_)_EFI_CAPSULE_AUTHENTICATE,
> we can and should guarantee the compatibility.
> 
> > I don't see any reason for making a simple tool, which is supposed to do 2
> > things (create/sign), require config options and more over config options
> > *for U-Boot*.
> 
> I don't get you point neither.

The point is that the tool should always have the ability to generate
authenticated capsules, regardless of the fact that someone wants to shoot 
himself in the foot.

> 
> > I also think it's extremely unlikely to get any working distro 
> > without libssl. 
> 
> Again, (major) distros are ones of users.
> There may be bunch of users who may build/maintain their systems
> on their way and not expect any authentication.
> 

And again that's completely disjoint with what the userspace tool that's
there to create your capsule can do.

[...]

Thanks
/Ilias

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

* [PATCH 1/4] tools: mkeficapsule: add firmwware image signing
  2021-05-14  7:13                           ` AKASHI Takahiro
@ 2021-05-14  8:45                             ` Heinrich Schuchardt
  2021-05-14  9:51                               ` AKASHI Takahiro
  0 siblings, 1 reply; 55+ messages in thread
From: Heinrich Schuchardt @ 2021-05-14  8:45 UTC (permalink / raw)
  To: u-boot

On 5/14/21 9:13 AM, AKASHI Takahiro wrote:
>> E.g for IMAGE_ATTRIBUTE_IN_USE
>>
>> AttributesSupported | AttributesSetting | Meaning
>> --------------------+-------------------+--------------------
>> 0                   | 0                 | state is unknown
>> 0                   | 1                 | state is unknown
>> 1                   | 0                 | image is not in use
>> 1                   | 1                 | image is in use
> We are discussing *_REQUIRED.
> Can you give me the same table for *_REQUIRED?
>
> -Takahiro Akashi
>
>

IMAGE_ATTRIBUTE_RESET_REQUIRED

AttributesSupported | AttributesSetting | Meaning
--------------------+-------------------+--------------------
0                   | 0                 | state is unknown
0                   | 1                 | state is unknown
1                   | 0                 | reset is not needed
                     |                   | to complete upgrade
1                   | 1                 | reset is needed
                     |                   | to complete upgrade


IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED

AttributesSupported | AttributesSetting | Meaning
--------------------+-------------------+--------------------
0                   | 0                 | state is unknown
0                   | 1                 | state is unknown
1                   | 0                 | signed and unsigned
                     |                   | capsules are accepted
1                   | 1                 | capsules are only
                     |                   | accepted after
                     |                   | checking the signature

For both bits AttributesSupported=0 does not make much sense.

IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED is a property of the current
image and should only be deleted by installing a new capsule.

A vendor might send you a special firmware image for unlocking your
device after registering as a developer. Xiaomi handled it like this for
one of my routers.

Best regards

Heinrich

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

* [PATCH 1/4] tools: mkeficapsule: add firmwware image signing
  2021-05-14  8:45                             ` Heinrich Schuchardt
@ 2021-05-14  9:51                               ` AKASHI Takahiro
  2021-05-14 10:08                                 ` Heinrich Schuchardt
  2021-05-14 13:09                                 ` Masami Hiramatsu
  0 siblings, 2 replies; 55+ messages in thread
From: AKASHI Takahiro @ 2021-05-14  9:51 UTC (permalink / raw)
  To: u-boot

Heinrich,

Can you please reply to each of my replies?
Otherwise, I don't know which one of my comments/opinions you agree to
and which one not.

On Fri, May 14, 2021 at 10:45:48AM +0200, Heinrich Schuchardt wrote:
> On 5/14/21 9:13 AM, AKASHI Takahiro wrote:
> > > E.g for IMAGE_ATTRIBUTE_IN_USE
> > > 
> > > AttributesSupported | AttributesSetting | Meaning
> > > --------------------+-------------------+--------------------
> > > 0                   | 0                 | state is unknown
> > > 0                   | 1                 | state is unknown
> > > 1                   | 0                 | image is not in use
> > > 1                   | 1                 | image is in use
> > We are discussing *_REQUIRED.
> > Can you give me the same table for *_REQUIRED?
> > 
> > -Takahiro Akashi
> > 
> > 
> 
> IMAGE_ATTRIBUTE_RESET_REQUIRED
> 
> AttributesSupported | AttributesSetting | Meaning
> --------------------+-------------------+--------------------
> 0                   | 0                 | state is unknown
> 0                   | 1                 | state is unknown
> 1                   | 0                 | reset is not needed
>                     |                   | to complete upgrade
> 1                   | 1                 | reset is needed
>                     |                   | to complete upgrade
> 
> 
> IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED
> 
> AttributesSupported | AttributesSetting | Meaning
> --------------------+-------------------+--------------------
> 0                   | 0                 | state is unknown
> 0                   | 1                 | state is unknown
> 1                   | 0                 | signed and unsigned
>                     |                   | capsules are accepted
> 1                   | 1                 | capsules are only
>                     |                   | accepted after
>                     |                   | checking the signature

So what?
This table shows there is a case where the authentication will be
skipped even if CONFIG_EFI_CAPSULE_AUTHETICATE is on and
it is completely compliant with UEFI specification.

That is what I and Masami was discussing.

> > > > > But as I mentioned in my comment against Sughosh's patch,
> > > > > the authentication process will be enforced only if the capsule has
> > > > > an attribute, IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED.
> > > > >
> > > >
> > > > That would be a security desaster.

So I said that you should discuss the topic in UEFI forum first
if you think so.

-Takahiro Akashi


> For both bits AttributesSupported=0 does not make much sense.
> 
> IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED is a property of the current
> image and should only be deleted by installing a new capsule.
> 
> A vendor might send you a special firmware image for unlocking your
> device after registering as a developer. Xiaomi handled it like this for
> one of my routers.
> 
> Best regards
> 
> Heinrich

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

* [PATCH 1/4] tools: mkeficapsule: add firmwware image signing
  2021-05-14  9:51                               ` AKASHI Takahiro
@ 2021-05-14 10:08                                 ` Heinrich Schuchardt
  2021-05-14 13:09                                 ` Masami Hiramatsu
  1 sibling, 0 replies; 55+ messages in thread
From: Heinrich Schuchardt @ 2021-05-14 10:08 UTC (permalink / raw)
  To: u-boot

On 5/14/21 11:51 AM, AKASHI Takahiro wrote:
> Heinrich,
>
> Can you please reply to each of my replies?
> Otherwise, I don't know which one of my comments/opinions you agree to
> and which one not.
>
> On Fri, May 14, 2021 at 10:45:48AM +0200, Heinrich Schuchardt wrote:
>> On 5/14/21 9:13 AM, AKASHI Takahiro wrote:
>>>> E.g for IMAGE_ATTRIBUTE_IN_USE
>>>>
>>>> AttributesSupported | AttributesSetting | Meaning
>>>> --------------------+-------------------+--------------------
>>>> 0                   | 0                 | state is unknown
>>>> 0                   | 1                 | state is unknown
>>>> 1                   | 0                 | image is not in use
>>>> 1                   | 1                 | image is in use
>>> We are discussing *_REQUIRED.
>>> Can you give me the same table for *_REQUIRED?
>>>
>>> -Takahiro Akashi
>>>
>>>
>>
>> IMAGE_ATTRIBUTE_RESET_REQUIRED
>>
>> AttributesSupported | AttributesSetting | Meaning
>> --------------------+-------------------+--------------------
>> 0                   | 0                 | state is unknown
>> 0                   | 1                 | state is unknown
>> 1                   | 0                 | reset is not needed
>>                      |                   | to complete upgrade
>> 1                   | 1                 | reset is needed
>>                      |                   | to complete upgrade
>>
>>
>> IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED
>>
>> AttributesSupported | AttributesSetting | Meaning
>> --------------------+-------------------+--------------------
>> 0                   | 0                 | state is unknown
>> 0                   | 1                 | state is unknown
>> 1                   | 0                 | signed and unsigned
>>                     |                   | capsules are accepted
>> 1                   | 1                 | capsules are only
>>                     |                   | accepted after
>>                     |                   | checking the signature
>
> So what?
> This table shows there is a case where the authentication will be
> skipped even if CONFIG_EFI_CAPSULE_AUTHETICATE is on and
> it is completely compliant with UEFI specification.

No. You have to set IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED=1 if
CONFIG_EFI_CAPSULE_AUTHENTICATE=y.

Best regards

Heinrich

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

* [PATCH 1/4] tools: mkeficapsule: add firmwware image signing
  2021-05-14  9:51                               ` AKASHI Takahiro
  2021-05-14 10:08                                 ` Heinrich Schuchardt
@ 2021-05-14 13:09                                 ` Masami Hiramatsu
  2021-05-14 13:39                                   ` Ilias Apalodimas
  2021-05-15  2:03                                   ` Heinrich Schuchardt
  1 sibling, 2 replies; 55+ messages in thread
From: Masami Hiramatsu @ 2021-05-14 13:09 UTC (permalink / raw)
  To: u-boot

Hi all,

I think it's time to summarize the topics on this thread.

1. tools/mkeficapsule, config options dependency
  - The tools, especially useful and distributable tools like
mkeficapsule should not be changed by the target board configuration.
  - Since there are target boards which don't need capsule
authentication, it should be configurable. That also can optimize the
library dependency.

2. tools/mkeficapsule, revert -K/-D options
  - Since these options are for embedding a public key in the
devicetree, that is not related to the capsule file. Also, the same
feature can be provided by a simple shell script.

3. capsule authentication, key embedding method
  - Embedding key in the devicetree is too fragile, especially, the
document says overwriting new device tree including key with fdt
command. That is not for the product, only for proof of concept.
  - Such a key should be embedded in the U-Boot, or hardware secure
storage so that the user can not change it.
    (BTW, I think there are more options, like embedding keys in SCP
firmware, TF-A, or OP-TEE, outside of U-Boot)

4. capsule authentication, authentication enablement
  - The UEFI spec said IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED can be
supported but cleared (for the current running firmware). This means
it is possible that the authentication feature is supported, but not
enabled.
  - For ensuring security, if U-Boot is compiled with
CONFIG_EFI_CAPSULE_AUTHETICATE=y,
IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED must always be set.

Are there any other topics on this thread? and any other comments on
these topics?

Thank you,


2021?5?14?(?) 18:51 AKASHI Takahiro <takahiro.akashi@linaro.org>:

>
> Heinrich,
>
> Can you please reply to each of my replies?
> Otherwise, I don't know which one of my comments/opinions you agree to
> and which one not.
>
> On Fri, May 14, 2021 at 10:45:48AM +0200, Heinrich Schuchardt wrote:
> > On 5/14/21 9:13 AM, AKASHI Takahiro wrote:
> > > > E.g for IMAGE_ATTRIBUTE_IN_USE
> > > >
> > > > AttributesSupported | AttributesSetting | Meaning
> > > > --------------------+-------------------+--------------------
> > > > 0                   | 0                 | state is unknown
> > > > 0                   | 1                 | state is unknown
> > > > 1                   | 0                 | image is not in use
> > > > 1                   | 1                 | image is in use
> > > We are discussing *_REQUIRED.
> > > Can you give me the same table for *_REQUIRED?
> > >
> > > -Takahiro Akashi
> > >
> > >
> >
> > IMAGE_ATTRIBUTE_RESET_REQUIRED
> >
> > AttributesSupported | AttributesSetting | Meaning
> > --------------------+-------------------+--------------------
> > 0                   | 0                 | state is unknown
> > 0                   | 1                 | state is unknown
> > 1                   | 0                 | reset is not needed
> >                     |                   | to complete upgrade
> > 1                   | 1                 | reset is needed
> >                     |                   | to complete upgrade
> >
> >
> > IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED
> >
> > AttributesSupported | AttributesSetting | Meaning
> > --------------------+-------------------+--------------------
> > 0                   | 0                 | state is unknown
> > 0                   | 1                 | state is unknown
> > 1                   | 0                 | signed and unsigned
> >                     |                   | capsules are accepted
> > 1                   | 1                 | capsules are only
> >                     |                   | accepted after
> >                     |                   | checking the signature
>
> So what?
> This table shows there is a case where the authentication will be
> skipped even if CONFIG_EFI_CAPSULE_AUTHETICATE is on and
> it is completely compliant with UEFI specification.
>
> That is what I and Masami was discussing.
>
> > > > > > But as I mentioned in my comment against Sughosh's patch,
> > > > > > the authentication process will be enforced only if the capsule has
> > > > > > an attribute, IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED.
> > > > > >
> > > > >
> > > > > That would be a security desaster.
>
> So I said that you should discuss the topic in UEFI forum first
> if you think so.
>
> -Takahiro Akashi
>
>
> > For both bits AttributesSupported=0 does not make much sense.
> >
> > IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED is a property of the current
> > image and should only be deleted by installing a new capsule.
> >
> > A vendor might send you a special firmware image for unlocking your
> > device after registering as a developer. Xiaomi handled it like this for
> > one of my routers.
> >
> > Best regards
> >
> > Heinrich



--
Masami Hiramatsu

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

* [PATCH 1/4] tools: mkeficapsule: add firmwware image signing
  2021-05-14 13:09                                 ` Masami Hiramatsu
@ 2021-05-14 13:39                                   ` Ilias Apalodimas
  2021-05-15  2:03                                   ` Heinrich Schuchardt
  1 sibling, 0 replies; 55+ messages in thread
From: Ilias Apalodimas @ 2021-05-14 13:39 UTC (permalink / raw)
  To: u-boot

On Fri, May 14, 2021 at 10:09:46PM +0900, Masami Hiramatsu wrote:
> Hi all,
> 
> I think it's time to summarize the topics on this thread.
> 
> 1. tools/mkeficapsule, config options dependency
>   - The tools, especially useful and distributable tools like
> mkeficapsule should not be changed by the target board configuration.
>   - Since there are target boards which don't need capsule
> authentication, it should be configurable. That also can optimize the
> library dependency.
> 
> 2. tools/mkeficapsule, revert -K/-D options
>   - Since these options are for embedding a public key in the
> devicetree, that is not related to the capsule file. Also, the same
> feature can be provided by a simple shell script.
> 
> 3. capsule authentication, key embedding method
>   - Embedding key in the devicetree is too fragile, especially, the
> document says overwriting new device tree including key with fdt
> command. That is not for the product, only for proof of concept.
>   - Such a key should be embedded in the U-Boot, or hardware secure
> storage so that the user can not change it.
>     (BTW, I think there are more options, like embedding keys in SCP
> firmware, TF-A, or OP-TEE, outside of U-Boot)
> 
> 4. capsule authentication, authentication enablement
>   - The UEFI spec said IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED can be
> supported but cleared (for the current running firmware). This means
> it is possible that the authentication feature is supported, but not
> enabled.
>   - For ensuring security, if U-Boot is compiled with
> CONFIG_EFI_CAPSULE_AUTHETICATE=y,
> IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED must always be set.
> 
> Are there any other topics on this thread? and any other comments on
> these topics?

I think you already mentioned that, but the key retrieval, should be a
callback of some sort that each vendor/hardware can define to his own
special function and we keep the default as 'key is embedded in U-Boot'.
All of the above sound reasonable. I still think (1) is kinda useless, but
I'll leave up to the maintainers.

Thanks
/Ilias

> 
> Thank you,
> 
> 
> 2021?5?14?(?) 18:51 AKASHI Takahiro <takahiro.akashi@linaro.org>:
> 
> >
> > Heinrich,
> >
> > Can you please reply to each of my replies?
> > Otherwise, I don't know which one of my comments/opinions you agree to
> > and which one not.
> >
> > On Fri, May 14, 2021 at 10:45:48AM +0200, Heinrich Schuchardt wrote:
> > > On 5/14/21 9:13 AM, AKASHI Takahiro wrote:
> > > > > E.g for IMAGE_ATTRIBUTE_IN_USE
> > > > >
> > > > > AttributesSupported | AttributesSetting | Meaning
> > > > > --------------------+-------------------+--------------------
> > > > > 0                   | 0                 | state is unknown
> > > > > 0                   | 1                 | state is unknown
> > > > > 1                   | 0                 | image is not in use
> > > > > 1                   | 1                 | image is in use
> > > > We are discussing *_REQUIRED.
> > > > Can you give me the same table for *_REQUIRED?
> > > >
> > > > -Takahiro Akashi
> > > >
> > > >
> > >
> > > IMAGE_ATTRIBUTE_RESET_REQUIRED
> > >
> > > AttributesSupported | AttributesSetting | Meaning
> > > --------------------+-------------------+--------------------
> > > 0                   | 0                 | state is unknown
> > > 0                   | 1                 | state is unknown
> > > 1                   | 0                 | reset is not needed
> > >                     |                   | to complete upgrade
> > > 1                   | 1                 | reset is needed
> > >                     |                   | to complete upgrade
> > >
> > >
> > > IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED
> > >
> > > AttributesSupported | AttributesSetting | Meaning
> > > --------------------+-------------------+--------------------
> > > 0                   | 0                 | state is unknown
> > > 0                   | 1                 | state is unknown
> > > 1                   | 0                 | signed and unsigned
> > >                     |                   | capsules are accepted
> > > 1                   | 1                 | capsules are only
> > >                     |                   | accepted after
> > >                     |                   | checking the signature
> >
> > So what?
> > This table shows there is a case where the authentication will be
> > skipped even if CONFIG_EFI_CAPSULE_AUTHETICATE is on and
> > it is completely compliant with UEFI specification.
> >
> > That is what I and Masami was discussing.
> >
> > > > > > > But as I mentioned in my comment against Sughosh's patch,
> > > > > > > the authentication process will be enforced only if the capsule has
> > > > > > > an attribute, IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED.
> > > > > > >
> > > > > >
> > > > > > That would be a security desaster.
> >
> > So I said that you should discuss the topic in UEFI forum first
> > if you think so.
> >
> > -Takahiro Akashi
> >
> >
> > > For both bits AttributesSupported=0 does not make much sense.
> > >
> > > IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED is a property of the current
> > > image and should only be deleted by installing a new capsule.
> > >
> > > A vendor might send you a special firmware image for unlocking your
> > > device after registering as a developer. Xiaomi handled it like this for
> > > one of my routers.
> > >
> > > Best regards
> > >
> > > Heinrich
> 
> 
> 
> --
> Masami Hiramatsu

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

* [PATCH 1/4] tools: mkeficapsule: add firmwware image signing
  2021-05-14 13:09                                 ` Masami Hiramatsu
  2021-05-14 13:39                                   ` Ilias Apalodimas
@ 2021-05-15  2:03                                   ` Heinrich Schuchardt
  2021-05-15  2:14                                     ` Masami Hiramatsu
  1 sibling, 1 reply; 55+ messages in thread
From: Heinrich Schuchardt @ 2021-05-15  2:03 UTC (permalink / raw)
  To: u-boot

On 5/14/21 3:09 PM, Masami Hiramatsu wrote:
> Hi all,
>
> I think it's time to summarize the topics on this thread.
>
> 1. tools/mkeficapsule, config options dependency
>    - The tools, especially useful and distributable tools like
> mkeficapsule should not be changed by the target board configuration.
>    - Since there are target boards which don't need capsule
> authentication, it should be configurable. That also can optimize the
> library dependency.

Thank you for providing this summary.

You described that the tool shall not depend on the target board
configuration. Your sentence starting with "Since" contradicts this.

As Ilias pointed out all Linux distributions come with an OpenSSL
package. The library dependency is nothing to worry about.

Capsule updates without authentication don't not make much sense in a
world full of attacks.

Hence, a configuration switch for the tool is not needed.

Best regards

Heinrich

>
> 2. tools/mkeficapsule, revert -K/-D options
>    - Since these options are for embedding a public key in the
> devicetree, that is not related to the capsule file. Also, the same
> feature can be provided by a simple shell script.
>
> 3. capsule authentication, key embedding method
>    - Embedding key in the devicetree is too fragile, especially, the
> document says overwriting new device tree including key with fdt
> command. That is not for the product, only for proof of concept.
>    - Such a key should be embedded in the U-Boot, or hardware secure
> storage so that the user can not change it.
>      (BTW, I think there are more options, like embedding keys in SCP
> firmware, TF-A, or OP-TEE, outside of U-Boot)
>
> 4. capsule authentication, authentication enablement
>    - The UEFI spec said IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED can be
> supported but cleared (for the current running firmware). This means
> it is possible that the authentication feature is supported, but not
> enabled.
>    - For ensuring security, if U-Boot is compiled with
> CONFIG_EFI_CAPSULE_AUTHETICATE=y,
> IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED must always be set.
>
> Are there any other topics on this thread? and any other comments on
> these topics?
>
> Thank you,

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

* [PATCH 1/4] tools: mkeficapsule: add firmwware image signing
  2021-05-15  2:03                                   ` Heinrich Schuchardt
@ 2021-05-15  2:14                                     ` Masami Hiramatsu
  0 siblings, 0 replies; 55+ messages in thread
From: Masami Hiramatsu @ 2021-05-15  2:14 UTC (permalink / raw)
  To: u-boot

Hi Heinrich,

2021?5?15?(?) 11:03 Heinrich Schuchardt <xypron.glpk@gmx.de>:
>
> On 5/14/21 3:09 PM, Masami Hiramatsu wrote:
> > Hi all,
> >
> > I think it's time to summarize the topics on this thread.
> >
> > 1. tools/mkeficapsule, config options dependency
> >    - The tools, especially useful and distributable tools like
> > mkeficapsule should not be changed by the target board configuration.
> >    - Since there are target boards which don't need capsule
> > authentication, it should be configurable. That also can optimize the
> > library dependency.
>
> Thank you for providing this summary.
>
> You described that the tool shall not depend on the target board
> configuration. Your sentence starting with "Since" contradicts this.

Ah, sorry for the confusion. Each bullet shows a different opinion on the topic.


> As Ilias pointed out all Linux distributions come with an OpenSSL
> package. The library dependency is nothing to worry about.

OK, so this is for topic #1.

>
> Capsule updates without authentication don't not make much sense in a
> world full of attacks.

and this is for topic #1 and maybe related to #4?

>
> Hence, a configuration switch for the tool is not needed.

Thanks for clarifying your opinion!

>
> Best regards
>
> Heinrich
>
> >
> > 2. tools/mkeficapsule, revert -K/-D options
> >    - Since these options are for embedding a public key in the
> > devicetree, that is not related to the capsule file. Also, the same
> > feature can be provided by a simple shell script.
> >
> > 3. capsule authentication, key embedding method
> >    - Embedding key in the devicetree is too fragile, especially, the
> > document says overwriting new device tree including key with fdt
> > command. That is not for the product, only for proof of concept.
> >    - Such a key should be embedded in the U-Boot, or hardware secure
> > storage so that the user can not change it.
> >      (BTW, I think there are more options, like embedding keys in SCP
> > firmware, TF-A, or OP-TEE, outside of U-Boot)
> >
> > 4. capsule authentication, authentication enablement
> >    - The UEFI spec said IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED can be
> > supported but cleared (for the current running firmware). This means
> > it is possible that the authentication feature is supported, but not
> > enabled.
> >    - For ensuring security, if U-Boot is compiled with
> > CONFIG_EFI_CAPSULE_AUTHETICATE=y,
> > IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED must always be set.
> >
> > Are there any other topics on this thread? and any other comments on
> > these topics?
> >
> > Thank you,



-- 
Masami Hiramatsu

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

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

Thread overview: 55+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-05-12  4:57 [PATCH 0/4] efi_loader: capsule: improve capsule authentication support AKASHI Takahiro
2021-05-12  4:57 ` [PATCH 1/4] tools: mkeficapsule: add firmwware image signing AKASHI Takahiro
2021-05-12  8:56   ` Heinrich Schuchardt
2021-05-13  3:08     ` AKASHI Takahiro
2021-05-13  4:22       ` Heinrich Schuchardt
2021-05-13  5:00         ` AKASHI Takahiro
2021-05-13  5:35           ` Heinrich Schuchardt
2021-05-13  6:36             ` AKASHI Takahiro
2021-05-13  6:45               ` Heinrich Schuchardt
2021-05-13  7:45                 ` AKASHI Takahiro
2021-05-13  5:12         ` Masami Hiramatsu
2021-05-13  5:50           ` Heinrich Schuchardt
2021-05-13  6:44             ` Masami Hiramatsu
2021-05-13  6:52               ` Heinrich Schuchardt
2021-05-13  7:38                 ` AKASHI Takahiro
2021-05-13  6:50             ` AKASHI Takahiro
2021-05-13  6:55               ` Heinrich Schuchardt
2021-05-13  7:23                 ` AKASHI Takahiro
2021-05-13  8:18                   ` Masami Hiramatsu
2021-05-13  8:38                     ` AKASHI Takahiro
2021-05-13 10:27                       ` Ilias Apalodimas
2021-05-13 16:12                         ` Masami Hiramatsu
2021-05-13 16:32                           ` Heinrich Schuchardt
2021-05-13 16:42                             ` Ilias Apalodimas
2021-05-14  4:50                               ` AKASHI Takahiro
2021-05-14  7:56                                 ` Ilias Apalodimas
2021-05-14  4:13                             ` AKASHI Takahiro
2021-05-13 10:40                       ` Heinrich Schuchardt
2021-05-13 18:25                     ` Heinrich Schuchardt
2021-05-14  6:19                       ` AKASHI Takahiro
2021-05-14  6:59                         ` Heinrich Schuchardt
2021-05-14  7:13                           ` AKASHI Takahiro
2021-05-14  8:45                             ` Heinrich Schuchardt
2021-05-14  9:51                               ` AKASHI Takahiro
2021-05-14 10:08                                 ` Heinrich Schuchardt
2021-05-14 13:09                                 ` Masami Hiramatsu
2021-05-14 13:39                                   ` Ilias Apalodimas
2021-05-15  2:03                                   ` Heinrich Schuchardt
2021-05-15  2:14                                     ` Masami Hiramatsu
2021-05-12  4:57 ` [PATCH 2/4] tools: mkeficapsule: remove device-tree related operation AKASHI Takahiro
2021-05-12  7:20   ` Ilias Apalodimas
2021-05-12  7:49     ` Masami Hiramatsu
2021-05-12  8:01       ` Ilias Apalodimas
2021-05-12 10:01         ` Heinrich Schuchardt
2021-05-13  2:33           ` AKASHI Takahiro
2021-05-13  5:08             ` Heinrich Schuchardt
2021-05-13  7:13               ` AKASHI Takahiro
2021-05-13 17:42                 ` Heinrich Schuchardt
2021-05-14  2:21                   ` [PATCH 2/4] tools: mkeficapsule: remove device-tree related operationy AKASHI Takahiro
2021-05-14  2:23                   ` [PATCH 2/4] tools: mkeficapsule: remove device-tree related operation Masami Hiramatsu
2021-05-12  4:57 ` [PATCH 3/4] tools: add fdtsig command AKASHI Takahiro
2021-05-13  5:23   ` Heinrich Schuchardt
2021-05-13  7:03     ` AKASHI Takahiro
2021-05-12  4:57 ` [PATCH 4/4] test/py: efi_capsule: add image authentication test AKASHI Takahiro
2021-05-12  5:04 ` [PATCH 0/4] efi_loader: capsule: improve capsule authentication support Heinrich Schuchardt

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.