diff mbox series

[1/4] tools: mkeficapsule: add firmwware image signing

Message ID 20210512045753.62288-2-takahiro.akashi@linaro.org
State New
Headers show
Series efi_loader: capsule: improve capsule authentication support | expand

Commit Message

AKASHI Takahiro May 12, 2021, 4:57 a.m. UTC
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(-)

-- 
2.31.0

Comments

Heinrich Schuchardt May 12, 2021, 8:56 a.m. UTC | #1
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);

>
AKASHI Takahiro May 13, 2021, 3:08 a.m. UTC | #2
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);

> >

>
Heinrich Schuchardt May 13, 2021, 4:22 a.m. UTC | #3
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);

>>>

>>
AKASHI Takahiro May 13, 2021, 5 a.m. UTC | #4
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);

> > > > 

> > > 

>
Masami Hiramatsu May 13, 2021, 5:12 a.m. UTC | #5
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
Heinrich Schuchardt May 13, 2021, 5:35 a.m. UTC | #6
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);

>>>>>

>>>>

>>
Heinrich Schuchardt May 13, 2021, 5:50 a.m. UTC | #7
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);

>>>>>

>>>>

>>

>

>
AKASHI Takahiro May 13, 2021, 6:36 a.m. UTC | #8
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);

> > > > > > 

> > > > > 

> > > 

>
Masami Hiramatsu May 13, 2021, 6:44 a.m. UTC | #9
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
Heinrich Schuchardt May 13, 2021, 6:45 a.m. UTC | #10
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;
AKASHI Takahiro May 13, 2021, 6:50 a.m. UTC | #11
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);

> > > > > > 

> > > > > 

> > > 

> > 

> > 

>
Heinrich Schuchardt May 13, 2021, 6:52 a.m. UTC | #12
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);
Heinrich Schuchardt May 13, 2021, 6:55 a.m. UTC | #13
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)) {
AKASHI Takahiro May 13, 2021, 7:23 a.m. UTC | #14
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)) {

>
AKASHI Takahiro May 13, 2021, 7:38 a.m. UTC | #15
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);

>
AKASHI Takahiro May 13, 2021, 7:45 a.m. UTC | #16
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;

>
Masami Hiramatsu May 13, 2021, 8:18 a.m. UTC | #17
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
AKASHI Takahiro May 13, 2021, 8:38 a.m. UTC | #18
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
Ilias Apalodimas May 13, 2021, 10:27 a.m. UTC | #19
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
Heinrich Schuchardt May 13, 2021, 10:40 a.m. UTC | #20
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
Masami Hiramatsu May 13, 2021, 4:12 p.m. UTC | #21
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
Heinrich Schuchardt May 13, 2021, 4:32 p.m. UTC | #22
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
Ilias Apalodimas May 13, 2021, 4:42 p.m. UTC | #23
[...]
> > > 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
Heinrich Schuchardt May 13, 2021, 6:25 p.m. UTC | #24
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,

>

>

>
AKASHI Takahiro May 14, 2021, 4:13 a.m. UTC | #25
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
AKASHI Takahiro May 14, 2021, 4:50 a.m. UTC | #26
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
AKASHI Takahiro May 14, 2021, 6:19 a.m. UTC | #27
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,

> > 

> > 

> > 

>
Heinrich Schuchardt May 14, 2021, 6:59 a.m. UTC | #28
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,

>>>

>>>

>>>

>>
AKASHI Takahiro May 14, 2021, 7:13 a.m. UTC | #29
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,

> > > > 

> > > > 

> > > > 

> > > 

>
Ilias Apalodimas May 14, 2021, 7:56 a.m. UTC | #30
> > 

[...]
> > 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
Heinrich Schuchardt May 14, 2021, 8:45 a.m. UTC | #31
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
AKASHI Takahiro May 14, 2021, 9:51 a.m. UTC | #32
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
Heinrich Schuchardt May 14, 2021, 10:08 a.m. UTC | #33
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
Masami Hiramatsu May 14, 2021, 1:09 p.m. UTC | #34
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
Ilias Apalodimas May 14, 2021, 1:39 p.m. UTC | #35
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
Heinrich Schuchardt May 15, 2021, 2:03 a.m. UTC | #36
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,
Masami Hiramatsu May 15, 2021, 2:14 a.m. UTC | #37
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
diff mbox series

Patch

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