All of lore.kernel.org
 help / color / mirror / Atom feed
From: AKASHI Takahiro <takahiro.akashi@linaro.org>
To: Heinrich Schuchardt <xypron.glpk@gmx.de>
Cc: sjg@chromium.org, ilias.apalodimas@linaro.org,
	sughosh.ganu@linaro.org, masami.hiramatsu@linaro.org,
	mark.kettenis@xs4all.nl, u-boot@lists.denx.de,
	Tom Rini <trini@konsulko.com>
Subject: Re: [PATCH v11 2/9] tools: mkeficapsule: add firmware image signing
Date: Mon, 14 Feb 2022 09:54:20 +0900	[thread overview]
Message-ID: <20220214005420.GB39639@laputa> (raw)
In-Reply-To: <8f054639-b37c-8636-4097-ce91087f4926@gmx.de>

Heinrich,

On Fri, Feb 11, 2022 at 08:16:34PM +0100, Heinrich Schuchardt wrote:
> On 2/9/22 11:10, AKASHI Takahiro wrote:
> > With this enhancement, mkeficapsule will be able to sign a capsule
> > file when it is created. A signature added will be used later
> > in the verification at FMP's SetImage() call.
> > 
> > To do that, we need specify additional command parameters:
> >    -monotonic-cout <count> : monotonic count
> >    -private-key <private key file> : private key file
> >    -certificate <certificate file> : certificate file
> > Only when all of those parameters are given, a signature will be added
> > to a capsule file.
> > 
> > Users are expected to maintain and increment the monotonic count at
> > every time of the update for each firmware image.
> > 
> > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > Reviewed-by: Simon Glass <sjg@chromium.org>
> > Acked-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
> > ---
> >   .azure-pipelines.yml |   2 +-
> >   tools/Makefile       |   1 +
> >   tools/eficapsule.h   | 115 +++++++++++++
> >   tools/mkeficapsule.c | 380 +++++++++++++++++++++++++++++++++++++++----
> >   4 files changed, 463 insertions(+), 35 deletions(-)
> >   create mode 100644 tools/eficapsule.h
> > 
> > diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml
> > index f2aa332be5cc..aecc9cb88441 100644
> > --- a/.azure-pipelines.yml
> > +++ b/.azure-pipelines.yml
> > @@ -25,7 +25,7 @@ stages:
> >             %CD:~0,2%\msys64\usr\bin\bash -lc "pacman --noconfirm -Syyuu"
> >           displayName: 'Update MSYS2'
> >         - script: |
> > -          %CD:~0,2%\msys64\usr\bin\bash -lc "pacman --noconfirm --needed -Sy make gcc bison flex diffutils openssl-devel"
> > +          %CD:~0,2%\msys64\usr\bin\bash -lc "pacman --noconfirm --needed -Sy make gcc bison flex diffutils openssl-devel libgnutls-devel"
> >           displayName: 'Install Toolchain'
> >         - script: |
> >             echo make tools-only_defconfig tools-only NO_SDL=1 > build-tools.sh
> > diff --git a/tools/Makefile b/tools/Makefile
> > index 766c0674f4a0..8da07d60a755 100644
> > --- a/tools/Makefile
> > +++ b/tools/Makefile
> > @@ -238,6 +238,7 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs
> >   hostprogs-$(CONFIG_ASN1_COMPILER)	+= asn1_compiler
> >   HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
> > 
> > +HOSTLDLIBS_mkeficapsule += -lgnutls
> >   hostprogs-$(CONFIG_TOOLS_MKEFICAPSULE) += mkeficapsule
> > 
> >   # We build some files with extra pedantic flags to try to minimize things
> > diff --git a/tools/eficapsule.h b/tools/eficapsule.h
> > new file mode 100644
> > index 000000000000..8c1560bb0671
> > --- /dev/null
> > +++ b/tools/eficapsule.h
> > @@ -0,0 +1,115 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright 2021 Linaro Limited
> > + *		Author: AKASHI Takahiro
> > + *
> > + * derived from efi.h and efi_api.h to make the file POSIX-compliant
> > + */
> > +
> > +#ifndef _EFI_CAPSULE_H
> > +#define _EFI_CAPSULE_H
> > +
> > +#include <stdint.h>
> > +#include <pe.h> /* WIN_CERTIFICATE */
> > +
> > +/*
> > + * Gcc's predefined attributes are not recognized by clang.
> > + */
> > +#ifndef __packed
> > +#define __packed	__attribute__((__packed__))
> > +#endif
> > +
> > +#ifndef __aligned
> > +#define __aligned(x)	__attribute__((__aligned__(x)))
> > +#endif
> > +
> > +typedef struct {
> > +	uint8_t b[16];
> > +} efi_guid_t __aligned(8);
> > +
> > +#define EFI_GUID(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7) \
> > +	{{ (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, \
> > +		((a) >> 24) & 0xff, \
> > +		(b) & 0xff, ((b) >> 8) & 0xff, \
> > +		(c) & 0xff, ((c) >> 8) & 0xff, \
> > +		(d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) } }
> > +
> > +#define EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID \
> > +	EFI_GUID(0x6dcbd5ed, 0xe82d, 0x4c44, 0xbd, 0xa1, \
> > +		 0x71, 0x94, 0x19, 0x9a, 0xd9, 0x2a)
> > +
> > +#define EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID \
> > +	EFI_GUID(0xae13ff2d, 0x9ad4, 0x4e25, 0x9a, 0xc8, \
> > +		 0x6d, 0x80, 0xb3, 0xb2, 0x21, 0x47)
> > +
> > +#define EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID \
> > +	EFI_GUID(0xe2bb9c06, 0x70e9, 0x4b14, 0x97, 0xa3, \
> > +		 0x5a, 0x79, 0x13, 0x17, 0x6e, 0x3f)
> > +
> > +#define EFI_CERT_TYPE_PKCS7_GUID \
> > +	EFI_GUID(0x4aafd29d, 0x68df, 0x49ee, 0x8a, 0xa9, \
> > +		 0x34, 0x7d, 0x37, 0x56, 0x65, 0xa7)
> > +
> > +/* flags */
> > +#define CAPSULE_FLAGS_PERSIST_ACROSS_RESET      0x00010000
> > +
> > +struct efi_capsule_header {
> > +	efi_guid_t capsule_guid;
> > +	uint32_t header_size;
> > +	uint32_t flags;
> > +	uint32_t capsule_image_size;
> > +} __packed;
> > +
> > +struct efi_firmware_management_capsule_header {
> > +	uint32_t version;
> > +	uint16_t embedded_driver_count;
> > +	uint16_t payload_item_count;
> > +	uint32_t item_offset_list[];
> > +} __packed;
> > +
> > +/* image_capsule_support */
> > +#define CAPSULE_SUPPORT_AUTHENTICATION          0x0000000000000001
> > +
> > +struct efi_firmware_management_capsule_image_header {
> > +	uint32_t version;
> > +	efi_guid_t update_image_type_id;
> > +	uint8_t update_image_index;
> > +	uint8_t reserved[3];
> > +	uint32_t update_image_size;
> > +	uint32_t update_vendor_code_size;
> > +	uint64_t update_hardware_instance;
> > +	uint64_t image_capsule_support;
> > +} __packed;
> > +
> > +/**
> > + * win_certificate_uefi_guid - A certificate that encapsulates
> > + * a GUID-specific signature
> > + *
> > + * @hdr:	Windows certificate header
> > + * @cert_type:	Certificate type
> > + * @cert_data:	Certificate data
> > + */
> > +struct win_certificate_uefi_guid {
> > +	WIN_CERTIFICATE	hdr;
> > +	efi_guid_t cert_type;
> > +	uint8_t cert_data[];
> > +} __packed;
> > +
> > +/**
> > + * efi_firmware_image_authentication - Capsule authentication method
> > + * descriptor
> > + *
> > + * This structure describes an authentication information for
> > + * a capsule with IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED set
> > + * and should be included as part of the capsule.
> > + * Only EFI_CERT_TYPE_PKCS7_GUID is accepted.
> > + *
> > + * @monotonic_count: Count to prevent replay
> > + * @auth_info: Authentication info
> > + */
> > +struct efi_firmware_image_authentication {
> > +	uint64_t monotonic_count;
> > +	struct win_certificate_uefi_guid auth_info;
> > +} __packed;
> > +
> > +#endif /* _EFI_CAPSULE_H */
> > diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
> > index 243fd6e48370..b996c66ad26a 100644
> > --- a/tools/mkeficapsule.c
> > +++ b/tools/mkeficapsule.c
> > @@ -16,21 +16,13 @@
> >   #include <sys/stat.h>
> >   #include <sys/types.h>
> > 
> > -typedef __u8 u8;
> > -typedef __u16 u16;
> > -typedef __u32 u32;
> > -typedef __u64 u64;
> > -typedef __s16 s16;
> > -typedef __s32 s32;
> > +#include <linux/kconfig.h>
> > 
> > -#define aligned_u64 __aligned_u64
> > +#include <gnutls/gnutls.h>
> > +#include <gnutls/pkcs7.h>
> > +#include <gnutls/abstract.h>
> > 
> > -#ifndef __packed
> > -#define __packed __attribute__((packed))
> > -#endif
> > -
> > -#include <efi.h>
> > -#include <efi_api.h>
> > +#include "eficapsule.h"
> > 
> >   static const char *tool_name = "mkeficapsule";
> > 
> > @@ -39,12 +31,19 @@ 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;
> > +
> > +static const char *opts_short = "f:r:i:I:v:p:c:m:dh";
> > 
> >   static struct option options[] = {
> >   	{"fit", required_argument, NULL, 'f'},
> >   	{"raw", required_argument, NULL, 'r'},
> >   	{"index", required_argument, NULL, 'i'},
> >   	{"instance", required_argument, NULL, 'I'},
> > +	{"private-key", required_argument, NULL, 'p'},
> > +	{"certificate", required_argument, NULL, 'c'},
> > +	{"monotonic-count", required_argument, NULL, 'm'},
> > +	{"dump-sig", no_argument, NULL, 'd'},
> >   	{"help", no_argument, NULL, 'h'},
> >   	{NULL, 0, NULL, 0},
> >   };
> > @@ -58,10 +57,40 @@ static void print_usage(void)
> >   		"\t-r, --raw <raw image>       new raw image file\n"
> >   		"\t-i, --index <index>         update image index\n"
> >   		"\t-I, --instance <instance>   update hardware instance\n"
> > +		"\t-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"
> >   		"\t-h, --help                  print a help message\n",
> >   		tool_name);
> >   }
> > 
> > +/**
> > + * auth_context - authentication context
> > + * @key_file:	Path to a private key file
> > + * @cert_file:	Path to a certificate file
> > + * @image_data:	Pointer to firmware data
> > + * @image_size:	Size of firmware data
> > + * @auth:	Authentication header
> > + * @sig_data:	Signature data
> > + * @sig_size:	Size of signature data
> > + *
> > + * Data structure used in create_auth_data(). @key_file through
> > + * @image_size are input parameters. @auth, @sig_data and @sig_size
> > + * are filled in by create_auth_data().
> > + */
> > +struct auth_context {
> > +	char *key_file;
> > +	char *cert_file;
> > +	uint8_t *image_data;
> > +	size_t image_size;
> > +	struct efi_firmware_image_authentication auth;
> > +	uint8_t *sig_data;
> > +	size_t sig_size;
> > +};
> > +
> > +static int dump_sig;
> > +
> >   /**
> >    * read_bin_file - read a firmware binary file
> >    * @bin:	Path to a firmware binary file
> > @@ -75,7 +104,7 @@ static void print_usage(void)
> >    * * 0  - on success
> >    * * -1 - on failure
> >    */
> > -static int read_bin_file(char *bin, void **data, off_t *bin_size)
> > +static int read_bin_file(char *bin, uint8_t **data, off_t *bin_size)
> >   {
> >   	FILE *g;
> >   	struct stat bin_stat;
> > @@ -147,6 +176,205 @@ static int write_capsule_file(FILE *f, void *data, size_t size, const char *msg)
> >   	return 0;
> >   }
> > 
> > +/**
> > + * create_auth_data - compose authentication data in capsule
> > + * @auth_context:	Pointer to authentication context
> > + *
> > + * Fill up an authentication header (.auth) and signature data (.sig_data)
> > + * in @auth_context, using library functions from openssl.
> > + * All the parameters in @auth_context must be filled in by a caller.
> > + *
> > + * Return:
> > + * * 0  - on success
> > + * * -1 - on failure
> > + */
> > +static int create_auth_data(struct auth_context *ctx)
> > +{
> > +	gnutls_datum_t cert;
> > +	gnutls_datum_t key;
> > +	off_t file_size;
> > +	gnutls_privkey_t pkey;
> > +	gnutls_x509_crt_t x509;
> > +	gnutls_pkcs7_t pkcs7;
> > +	gnutls_datum_t data;
> > +	gnutls_datum_t signature;
> > +	int ret;
> > +
> > +	ret = read_bin_file(ctx->cert_file, &cert.data, &file_size);
> > +	if (ret < 0)
> > +		return -1;
> > +	if (file_size > UINT_MAX)
> > +		return -1;
> > +	cert.size = file_size;
> > +
> > +	ret = read_bin_file(ctx->key_file, &key.data, &file_size);
> > +	if (ret < 0)
> > +		return -1;
> > +	if (ret < 0)
> > +		return -1;
> > +	if (file_size > UINT_MAX)
> > +		return -1;
> > +	key.size = file_size;
> > +
> > +	/*
> > +	 * For debugging,
> > +	 * gnutls_global_set_time_function(mytime);
> > +	 * gnutls_global_set_log_function(tls_log_func);
> > +	 * gnutls_global_set_log_level(6);
> > +	 */
> > +
> > +	ret = gnutls_privkey_init(&pkey);
> > +	if (ret < 0) {
> > +		fprintf(stderr, "error in gnutls_privkey_init(): %s\n",
> > +			gnutls_strerror(ret));
> > +		return -1;
> > +	}
> > +
> > +	ret = gnutls_x509_crt_init(&x509);
> > +	if (ret < 0) {
> > +		fprintf(stderr, "error in gnutls_x509_crt_init(): %s\n",
> > +			gnutls_strerror(ret));
> > +		return -1;
> > +	}
> > +
> > +	/* load a private key */
> > +	ret = gnutls_privkey_import_x509_raw(pkey, &key, GNUTLS_X509_FMT_PEM,
> > +					     0, 0);
> > +	if (ret < 0) {
> > +		fprintf(stderr,
> > +			"error in gnutls_privkey_import_x509_raw(): %s\n",
> > +			gnutls_strerror(ret));
> > +		return -1;
> > +	}
> > +
> > +	/* load x509 certificate */
> > +	ret = gnutls_x509_crt_import(x509, &cert, GNUTLS_X509_FMT_PEM);
> > +	if (ret < 0) {
> > +		fprintf(stderr, "error in gnutls_x509_crt_import(): %s\n",
> > +			gnutls_strerror(ret));
> > +		return -1;
> > +	}
> > +
> > +	/* generate a PKCS #7 structure */
> > +	ret = gnutls_pkcs7_init(&pkcs7);
> > +	if (ret < 0) {
> > +		fprintf(stderr, "error in gnutls_pkcs7_init(): %s\n",
> > +			gnutls_strerror(ret));
> > +		return -1;
> > +	}
> > +
> > +	/* sign */
> > +	/*
> > +	 * Data should have
> > +	 *  * firmware image
> > +	 *  * monotonic count
> > +	 * in this order!
> > +	 * See EDK2's FmpAuthenticatedHandlerRsa2048Sha256()
> > +	 */
> > +	data.size = ctx->image_size + sizeof(ctx->auth.monotonic_count);
> > +	data.data = malloc(data.size);
> > +	if (!data.data) {
> > +		fprintf(stderr, "allocating memory (0x%x) failed\n", data.size);
> > +		return -1;
> > +	}
> > +	memcpy(data.data, ctx->image_data, ctx->image_size);
> > +	memcpy(data.data + ctx->image_size, &ctx->auth.monotonic_count,
> > +	       sizeof(ctx->auth.monotonic_count));
> > +
> > +	ret = gnutls_pkcs7_sign(pkcs7, x509, pkey, &data, NULL, NULL,
> > +				GNUTLS_DIG_SHA256,
> > +				/* GNUTLS_PKCS7_EMBED_DATA? */
> > +				GNUTLS_PKCS7_INCLUDE_CERT |
> > +				GNUTLS_PKCS7_INCLUDE_TIME);
> > +	if (ret < 0) {
> > +		fprintf(stderr, "error in gnutls_pkcs7)sign(): %s\n",
> > +			gnutls_strerror(ret));
> > +		return -1;
> > +	}
> > +
> > +	/* export */
> > +	ret = gnutls_pkcs7_export2(pkcs7, GNUTLS_X509_FMT_DER, &signature);
> > +	if (ret < 0) {
> > +		fprintf(stderr, "error in gnutls_pkcs7_export2: %s\n",
> > +			gnutls_strerror(ret));
> > +		return -1;
> > +	}
> > +	ctx->sig_data = signature.data;
> > +	ctx->sig_size = signature.size;
> > +
> > +	/* 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));
> > +
> > +	/*
> > +	 * For better clean-ups,
> > +	 * gnutls_pkcs7_deinit(pkcs7);
> > +	 * gnutls_privkey_deinit(pkey);
> > +	 * gnutls_x509_crt_deinit(x509);
> > +	 * free(cert.data);
> > +	 * free(key.data);
> > +	 * if error
> > +	 *   gnutls_free(signature.data);
> > +	 */
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * dump_signature - dump out a signature
> > + * @path:	Path to a capsule file
> > + * @signature:	Signature data
> > + * @sig_size:	Size of signature data
> > + *
> > + * Signature data pointed to by @signature will be saved into
> > + * a file whose file name is @path with ".p7" suffix.
> > + *
> > + * Return:
> > + * * 0  - on success
> > + * * -1 - on failure
> > + */
> > +static int dump_signature(const char *path, uint8_t *signature, size_t sig_size)
> > +{
> > +	char *sig_path;
> > +	FILE *f;
> > +	size_t size;
> > +	int ret = -1;
> > +
> > +	sig_path = malloc(strlen(path) + 3 + 1);
> > +	if (!sig_path)
> > +		return ret;
> > +
> > +	sprintf(sig_path, "%s.p7", path);
> > +	f = fopen(sig_path, "w");
> > +	if (!f)
> > +		goto err;
> > +
> > +	size = fwrite(signature, 1, sig_size, f);
> > +	if (size == sig_size)
> > +		ret = 0;
> > +
> > +	fclose(f);
> > +err:
> > +	free(sig_path);
> > +	return ret;
> > +}
> > +
> > +/**
> > + * free_sig_data - free out signature data
> > + * @ctx:	Pointer to authentication context
> > + *
> > + * Free signature data allocated in create_auth_data().
> > + */
> > +static void free_sig_data(struct auth_context *ctx)
> > +{
> > +	if (ctx->sig_size)
> > +		gnutls_free(ctx->sig_data);
> > +}
> > +
> >   /**
> >    * create_fwbin - create an uefi capsule file
> >    * @path:	Path to a created capsule file
> > @@ -168,23 +396,25 @@ static int write_capsule_file(FILE *f, void *data, size_t size, const char *msg)
> >    * * -1 - on failure
> >    */
> >   static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> > -			unsigned long index, unsigned long instance)
> > +			unsigned long index, unsigned long instance,
> > +			uint64_t mcount, char *privkey_file, char *cert_file)
> >   {
> >   	struct efi_capsule_header header;
> >   	struct efi_firmware_management_capsule_header capsule;
> >   	struct efi_firmware_management_capsule_image_header image;
> > +	struct auth_context auth_context;
> >   	FILE *f;
> > -	void *data;
> > +	uint8_t *data;
> >   	off_t bin_size;
> > -	u64 offset;
> > +	uint64_t offset;
> >   	int ret;
> > 
> >   #ifdef DEBUG
> > -	printf("For output: %s\n", path);
> > -	printf("\tbin: %s\n\ttype: %pUl\n", bin, guid);
> > -	printf("\tindex: %ld\n\tinstance: %ld\n", index, instance);
> > +	fprintf(stderr, "For output: %s\n", path);
> > +	fprintf(stderr, "\tbin: %s\n\ttype: %pUl\n", bin, guid);
> > +	fprintf(stderr, "\tindex: %lu\n\tinstance: %lu\n", index, instance);
> >   #endif
> > -
> > +	auth_context.sig_size = 0;
> >   	f = NULL;
> >   	data = NULL;
> >   	ret = -1;
> > @@ -195,6 +425,27 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> >   	if (read_bin_file(bin, &data, &bin_size))
> >   		goto err;
> > 
> > +	/* first, calculate signature to determine its size */
> > +	if (privkey_file && cert_file) {
> > +		auth_context.key_file = privkey_file;
> > +		auth_context.cert_file = cert_file;
> > +		auth_context.auth.monotonic_count = mcount;
> > +		auth_context.image_data = data;
> > +		auth_context.image_size = bin_size;
> > +
> > +		if (create_auth_data(&auth_context)) {
> > +			fprintf(stderr, "Signing firmware image failed\n");
> > +			goto err;
> > +		}
> > +
> > +		if (dump_sig &&
> > +		    dump_signature(path, auth_context.sig_data,
> > +				   auth_context.sig_size)) {
> > +			fprintf(stderr, "Creating signature file failed\n");
> > +			goto err;
> > +		}
> > +	}
> > +
> >   	/*
> >   	 * write a capsule file
> >   	 */
> > @@ -212,9 +463,12 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> >   	/* TODO: The current implementation ignores flags */
> >   	header.flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET;
> >   	header.capsule_image_size = sizeof(header)
> > -					+ sizeof(capsule) + sizeof(u64)
> > +					+ sizeof(capsule) + sizeof(uint64_t)
> >   					+ sizeof(image)
> >   					+ bin_size;
> > +	if (auth_context.sig_size)
> > +		header.capsule_image_size += sizeof(auth_context.auth)
> > +				+ auth_context.sig_size;
> >   	if (write_capsule_file(f, &header, sizeof(header),
> >   			       "Capsule header"))
> >   		goto err;
> > @@ -230,7 +484,7 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> >   			       "Firmware capsule header"))
> >   		goto err;
> > 
> > -	offset = sizeof(capsule) + sizeof(u64);
> > +	offset = sizeof(capsule) + sizeof(uint64_t);
> >   	if (write_capsule_file(f, &offset, sizeof(offset),
> >   			       "Offset to capsule image"))
> >   		goto err;
> > @@ -245,13 +499,32 @@ 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_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;
> >   	if (write_capsule_file(f, &image, sizeof(image),
> >   			       "Firmware capsule image header"))
> >   		goto err;
> > 
> > +	/*
> > +	 * signature
> > +	 */
> > +	if (auth_context.sig_size) {
> > +		if (write_capsule_file(f, &auth_context.auth,
> > +				       sizeof(auth_context.auth),
> > +				       "Authentication header"))
> > +			goto err;
> > +
> > +		if (write_capsule_file(f, auth_context.sig_data,
> > +				       auth_context.sig_size, "Signature"))
> > +			goto err;
> > +	}
> > +
> >   	/*
> >   	 * firmware binary
> >   	 */
> > @@ -262,28 +535,43 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
> >   err:
> >   	if (f)
> >   		fclose(f);
> > +	free_sig_data(&auth_context);
> >   	free(data);
> > 
> >   	return ret;
> >   }
> > 
> > -/*
> > - * Usage:
> > - *   $ mkeficapsule -f <firmware binary> <output file>
> > +/**
> > + * main - main entry function of mkeficapsule
> > + * @argc:	Number of arguments
> > + * @argv:	Array of pointers to arguments
> > + *
> > + * Create an uefi capsule file, optionally signing it.
> > + * Parse all the arguments and pass them on to create_fwbin().
> > + *
> > + * Return:
> > + * * 0  - on success
> > + * * -1 - on failure
> >    */
> >   int main(int argc, char **argv)
> >   {
> >   	char *file;
> >   	efi_guid_t *guid;
> >   	unsigned long index, instance;
> > +	uint64_t mcount;
> > +	char *privkey_file, *cert_file;
> >   	int c, idx;
> > 
> >   	file = NULL;
> >   	guid = NULL;
> >   	index = 0;
> >   	instance = 0;
> > +	mcount = 0;
> > +	privkey_file = NULL;
> > +	cert_file = NULL;
> > +	dump_sig = 0;
> >   	for (;;) {
> > -		c = getopt_long(argc, argv, "f:r:i:I:v:h", options, &idx);
> > +		c = getopt_long(argc, argv, opts_short, options, &idx);
> >   		if (c == -1)
> >   			break;
> > 
> > @@ -291,7 +579,7 @@ int main(int argc, char **argv)
> >   		case 'f':
> >   			if (file) {
> >   				fprintf(stderr, "Image already specified\n");
> > -				return -1;
> > +				exit(EXIT_FAILURE);
> >   			}
> >   			file = optarg;
> >   			guid = &efi_guid_image_type_uboot_fit;
> > @@ -299,7 +587,7 @@ int main(int argc, char **argv)
> >   		case 'r':
> >   			if (file) {
> >   				fprintf(stderr, "Image already specified\n");
> > -				return -1;
> > +				exit(EXIT_FAILURE);
> >   			}
> >   			file = optarg;
> >   			guid = &efi_guid_image_type_uboot_raw;
> > @@ -310,14 +598,38 @@ int main(int argc, char **argv)
> >   		case 'I':
> >   			instance = strtoul(optarg, NULL, 0);
> >   			break;
> > +		case 'p':
> > +			if (privkey_file) {
> > +				fprintf(stderr,
> > +					"Private Key already specified\n");
> > +				exit(EXIT_FAILURE);
> > +			}
> > +			privkey_file = optarg;
> > +			break;
> > +		case 'c':
> > +			if (cert_file) {
> > +				fprintf(stderr,
> > +					"Certificate file already specified\n");
> > +				exit(EXIT_FAILURE);
> > +			}
> > +			cert_file = optarg;
> > +			break;
> > +		case 'm':
> > +			mcount = strtoul(optarg, NULL, 0);
> > +			break;
> > +		case 'd':
> > +			dump_sig = 1;
> > +			break;
> >   		case 'h':
> >   			print_usage();
> > -			return 0;
> > +			exit(EXIT_SUCCESS);
> >   		}
> >   	}
> > 
> > -	/* need an output file */
> > -	if (argc != optind + 1) {
> > +	/* check necessary parameters */
> > +	if ((argc != optind + 1) || !file ||
> > +	    ((privkey_file && !cert_file) ||
> > +	     (!privkey_file && cert_file))) {
> >   		print_usage();
> >   		exit(EXIT_FAILURE);
> >   	}
> > @@ -328,8 +640,8 @@ int main(int argc, char **argv)
> >   		exit(EXIT_SUCCESS);
> >   	}
> > 
> > -	if (create_fwbin(argv[optind], file, guid, index, instance)
> > -			< 0) {
> > +	if (create_fwbin(argv[optind], file, guid, index, instance,
> > +			 mcount, privkey_file, cert_file) < 0) {
> >   		fprintf(stderr, "Creating firmware capsule failed\n");
> >   		exit(EXIT_FAILURE);
> >   	}
> 
> Tom just caught the following warnings when building sandobox_defconfig
> with clang. Unfortunately -Werror is not enabled for tools yet.
> 
> In file included from tools/mkeficapsule.c:24:
> ./tools/eficapsule.h:93:18: warning: field 'hdr' with variable sized
> type 'WIN_CERTIFICATE' (aka 'struct _WIN_CERTIFICATE') not at the end of
> a struct or class is a GNU extension [-Wgnu-variable-sized-type-not-at-end]
>         WIN_CERTIFICATE hdr;
>                         ^
> tools/mkeficapsule.c:88:43: warning: field 'auth' with variable sized
> type 'struct efi_firmware_image_authentication' not at the end of a
> struct or class is a GNU extension [-Wgnu-variable-sized-type-not-at-end]
>         struct efi_firmware_image_authentication auth;
>                                                  ^
> 
> As the zero sized members are not referenced we can remove them. The
> following diff seems to fix the problem:

The fix itself is fine, but

"Zero-sized members" is a common practice in UEFI specification and
a number of instances can also be seen in include/efi_api.h.

If this kind of technique is an "error", we should fix the header files
in the first place.

-Takahiro Akashi


> 
> diff --git a/tools/eficapsule.h b/tools/eficapsule.h
> index 8c1560bb06..69c9c58c2f 100644
> --- a/tools/eficapsule.h
> +++ b/tools/eficapsule.h
> @@ -10,7 +10,6 @@
>  #define _EFI_CAPSULE_H
> 
>  #include <stdint.h>
> -#include <pe.h> /* WIN_CERTIFICATE */
> 
>  /*
>   * Gcc's predefined attributes are not recognized by clang.
> @@ -85,14 +84,16 @@ struct efi_firmware_management_capsule_image_header {
>   * win_certificate_uefi_guid - A certificate that encapsulates
>   * a GUID-specific signature
>   *
> - * @hdr:       Windows certificate header
> + * @hdr:       Windows certificate header, cf. WIN_CERTIFICATE
>   * @cert_type: Certificate type
> - * @cert_data: Certificate data
>   */
>  struct win_certificate_uefi_guid {
> -       WIN_CERTIFICATE hdr;
> +       struct {
> +               uint32_t dwLength;
> +               uint16_t wRevision;
> +               uint16_t wCertificateType;
> +       } hdr;
>         efi_guid_t cert_type;
> -       uint8_t cert_data[];
>  } __packed;
> 
>  /**
> diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c
> index 550f5f88e3..f7590e482f 100644
> --- a/tools/mkeficapsule.c
> +++ b/tools/mkeficapsule.c
> @@ -5,6 +5,7 @@
>   */
> 
>  #include <getopt.h>
> +#include <pe.h>
>  #include <stdbool.h>
>  #include <stdint.h>
>  #include <stdio.h>
> 
> Best regards
> 
> Heinrich

  reply	other threads:[~2022-02-14  0:54 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-02-09 10:10 [PATCH v11 0/9] efi_loader: capsule: improve capsule authentication support AKASHI Takahiro
2022-02-09 10:10 ` [PATCH v11 1/9] tools: build mkeficapsule with tools-only_defconfig AKASHI Takahiro
2022-02-09 10:10 ` [PATCH v11 2/9] tools: mkeficapsule: add firmware image signing AKASHI Takahiro
2022-02-11 19:16   ` Heinrich Schuchardt
2022-02-14  0:54     ` AKASHI Takahiro [this message]
2022-02-19 23:11       ` Simon Glass
2022-02-21  0:43         ` AKASHI Takahiro
2022-02-21 18:59           ` Heinrich Schuchardt
2022-03-13  6:05             ` Simon Glass
2022-02-09 10:10 ` [PATCH v11 3/9] tools: mkeficapsule: add man page AKASHI Takahiro
2022-02-09 10:10 ` [PATCH v11 4/9] doc: update UEFI document for usage of mkeficapsule AKASHI Takahiro
2022-02-09 10:10 ` [PATCH v11 5/9] test/py: efi_capsule: add image authentication test AKASHI Takahiro
2022-02-11 19:25   ` Heinrich Schuchardt
2022-02-14  0:43     ` AKASHI Takahiro
2022-02-16  8:40       ` Heinrich Schuchardt
2022-02-09 10:10 ` [PATCH v11 6/9] tools: mkeficapsule: allow for specifying GUID explicitly AKASHI Takahiro
2022-02-09 10:10 ` [PATCH v11 7/9] test/py: efi_capsule: align with the syntax change of mkeficapsule AKASHI Takahiro
2022-02-09 10:10 ` [PATCH v11 8/9] test/py: efi_capsule: add a test for "--guid" option AKASHI Takahiro
2022-02-09 10:10 ` [PATCH v11 9/9] test/py: efi_capsule: check the results in case of CAPSULE_AUTHENTICATE AKASHI Takahiro

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220214005420.GB39639@laputa \
    --to=takahiro.akashi@linaro.org \
    --cc=ilias.apalodimas@linaro.org \
    --cc=mark.kettenis@xs4all.nl \
    --cc=masami.hiramatsu@linaro.org \
    --cc=sjg@chromium.org \
    --cc=sughosh.ganu@linaro.org \
    --cc=trini@konsulko.com \
    --cc=u-boot@lists.denx.de \
    --cc=xypron.glpk@gmx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.