diff mbox series

[v4,05/11] tools: mkeficapsule: add firmwware image signing

Message ID 20211007062340.72207-6-takahiro.akashi@linaro.org
State Superseded
Headers show
Series efi_loader: capsule: improve capsule authentication support | expand

Commit Message

AKASHI Takahiro Oct. 7, 2021, 6:23 a.m. UTC
With this enhancement, mkeficapsule will be able to sign a capsule
file when it is created. A signature added will be used later
in the verification at FMP's SetImage() call.

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

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

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>

---
 tools/Kconfig        |   7 +
 tools/Makefile       |   8 +-
 tools/mkeficapsule.c | 435 +++++++++++++++++++++++++++++++++++++++----
 3 files changed, 416 insertions(+), 34 deletions(-)

-- 
2.33.0

Comments

Masami Hiramatsu Oct. 20, 2021, 8:17 a.m. UTC | #1
Hello Akashi-san,

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

Thank you,

2021年10月7日(木) 15:25 AKASHI Takahiro <takahiro.akashi@linaro.org>:
>

> With this enhancement, mkeficapsule will be able to sign a capsule

> file when it is created. A signature added will be used later

> in the verification at FMP's SetImage() call.

>

> To do that, We need specify additional command parameters:

>   -monotonic-cout <count> : monotonic count

>   -private-key <private key file> : private key file

>   -certificate <certificate file> : certificate file

> Only when all of those parameters are given, a signature will be added

> to a capsule file.

>

> Users are expected to maintain and increment the monotonic count at

> every time of the update for each firmware image.

>

> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>

> ---

>  tools/Kconfig        |   7 +

>  tools/Makefile       |   8 +-

>  tools/mkeficapsule.c | 435 +++++++++++++++++++++++++++++++++++++++----

>  3 files changed, 416 insertions(+), 34 deletions(-)

>

> diff --git a/tools/Kconfig b/tools/Kconfig

> index d6f82cd949b5..9a37ed035311 100644

> --- a/tools/Kconfig

> +++ b/tools/Kconfig

> @@ -20,4 +20,11 @@ config TOOLS_LIBCRYPTO

>           This selection does not affect target features, such as runtime FIT

>           signature verification.

>

> +config TOOLS_MKEFICAPSULE

> +       bool "Build efimkcapsule command"

> +       default y if EFI_CAPSULE_ON_DISK

> +       help

> +         This command allows users to create a UEFI capsule file and,

> +         optionally sign that file. If you want to enable UEFI capsule

> +         update feature on your target, you certainly need this.

>  endmenu

> diff --git a/tools/Makefile b/tools/Makefile

> index 4a86321f6467..6ea3033dbfb8 100644

> --- a/tools/Makefile

> +++ b/tools/Makefile

> @@ -237,8 +237,12 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs

>  hostprogs-$(CONFIG_ASN1_COMPILER)      += asn1_compiler

>  HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include

>

> -mkeficapsule-objs      := mkeficapsule.o $(LIBFDT_OBJS)

> -hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule

> +HOSTLDLIBS_mkeficapsule += -luuid

> +ifeq ($(CONFIG_TOOLS_LIBCRYPTO),y)

> +HOSTLDLIBS_mkeficapsule += \

> +       $(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto")

> +endif

> +hostprogs-$(CONFIG_TOOLS_MKEFICAPSULE) += mkeficapsule

>

>  # We build some files with extra pedantic flags to try to minimize things

>  # that won't build on some weird host compiler -- though there are lots of

> diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c

> index 4995ba4e0c2a..5541e4bda894 100644

> --- a/tools/mkeficapsule.c

> +++ b/tools/mkeficapsule.c

> @@ -15,6 +15,16 @@

>  #include <sys/stat.h>

>  #include <sys/types.h>

>

> +#include <linux/kconfig.h>

> +#ifdef CONFIG_TOOLS_LIBCRYPTO

> +#include <openssl/asn1.h>

> +#include <openssl/bio.h>

> +#include <openssl/evp.h>

> +#include <openssl/err.h>

> +#include <openssl/pem.h>

> +#include <openssl/pkcs7.h>

> +#endif

> +

>  typedef __u8 u8;

>  typedef __u16 u16;

>  typedef __u32 u32;

> @@ -38,12 +48,25 @@ efi_guid_t efi_guid_image_type_uboot_fit =

>                 EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID;

>  efi_guid_t efi_guid_image_type_uboot_raw =

>                 EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID;

> +efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;

> +

> +#ifdef CONFIG_TOOLS_LIBCRYPTO

> +static const char *opts_short = "f:r:i:I:v:p:c:m:dh";

> +#else

> +static const char *opts_short = "f:r:i:I:v:h";

> +#endif

>

>  static struct option options[] = {

>         {"fit", required_argument, NULL, 'f'},

>         {"raw", required_argument, NULL, 'r'},

>         {"index", required_argument, NULL, 'i'},

>         {"instance", required_argument, NULL, 'I'},

> +#ifdef CONFIG_TOOLS_LIBCRYPTO

> +       {"private-key", required_argument, NULL, 'p'},

> +       {"certificate", required_argument, NULL, 'c'},

> +       {"monotonic-count", required_argument, NULL, 'm'},

> +       {"dump-sig", no_argument, NULL, 'd'},

> +#endif

>         {"help", no_argument, NULL, 'h'},

>         {NULL, 0, NULL, 0},

>  };

> @@ -57,16 +80,280 @@ static void print_usage(void)

>                "\t-r, --raw <raw image>       new raw image file\n"

>                "\t-i, --index <index>         update image index\n"

>                "\t-I, --instance <instance>   update hardware instance\n"

> +#ifdef CONFIG_TOOLS_LIBCRYPTO

> +              "\t-p, --private-key <privkey file>  private key file\n"

> +              "\t-c, --certificate <cert file>     signer's certificate file\n"

> +              "\t-m, --monotonic-count <count>     monotonic count\n"

> +              "\t-d, --dump_sig              dump signature (*.p7)\n"

> +#endif

>                "\t-h, --help                  print a help message\n",

>                tool_name);

>  }

>

> +/**

> + * auth_context - authentication context

> + * @key_file:  Path to a private key file

> + * @cert_file: Path to a certificate file

> + * @image_data:        Pointer to firmware data

> + * @image_size:        Size of firmware data

> + * @auth:      Authentication header

> + * @sig_data:  Signature data

> + * @sig_size:  Size of signature data

> + *

> + * Data structure used in create_auth_data(). @key_file through

> + * @image_size are input parameters. @auth, @sig_data and @sig_size

> + * are filled in by create_auth_data().

> + */

> +struct auth_context {

> +       char *key_file;

> +       char *cert_file;

> +       u8 *image_data;

> +       size_t image_size;

> +       struct efi_firmware_image_authentication auth;

> +       u8 *sig_data;

> +       size_t sig_size;

> +};

> +

> +static int dump_sig;

> +

> +#ifdef CONFIG_TOOLS_LIBCRYPTO

> +/**

> + * fileio-read_pkey - read out a private key

> + * @filename:  Path to a private key file

> + *

> + * Read out a private key file and parse it into "EVP_PKEY" structure.

> + *

> + * Return:

> + * * Pointer to private key structure  - on success

> + * * NULL - on failure

> + */

> +static EVP_PKEY *fileio_read_pkey(const char *filename)

> +{

> +       EVP_PKEY *key = NULL;

> +       BIO *bio;

> +

> +       bio = BIO_new_file(filename, "r");

> +       if (!bio)

> +               goto out;

> +

> +       key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);

> +

> +out:

> +       BIO_free_all(bio);

> +       if (!key) {

> +               printf("Can't load key from file '%s'\n", filename);

> +               ERR_print_errors_fp(stderr);

> +       }

> +

> +       return key;

> +}

> +

> +/**

> + * fileio-read_cert - read out a certificate

> + * @filename:  Path to a certificate file

> + *

> + * Read out a certificate file and parse it into "X509" structure.

> + *

> + * Return:

> + * * Pointer to certificate structure  - on success

> + * * NULL - on failure

> + */

> +static X509 *fileio_read_cert(const char *filename)

> +{

> +       X509 *cert = NULL;

> +       BIO *bio;

> +

> +       bio = BIO_new_file(filename, "r");

> +       if (!bio)

> +               goto out;

> +

> +       cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);

> +

> +out:

> +       BIO_free_all(bio);

> +       if (!cert) {

> +               printf("Can't load certificate from file '%s'\n", filename);

> +               ERR_print_errors_fp(stderr);

> +       }

> +

> +       return cert;

> +}

> +

> +/**

> + * create_auth_data - compose authentication data in capsule

> + * @auth_context:      Pointer to authentication context

> + *

> + * Fill up an authentication header (.auth) and signature data (.sig_data)

> + * in @auth_context, using library functions from openssl.

> + * All the parameters in @auth_context must be filled in by a caller.

> + *

> + * Return:

> + * * 0  - on success

> + * * -1 - on failure

> + */

> +static int create_auth_data(struct auth_context *ctx)

> +{

> +       EVP_PKEY *key = NULL;

> +       X509 *cert = NULL;

> +       BIO *data_bio = NULL;

> +       const EVP_MD *md;

> +       PKCS7 *p7;

> +       int flags, ret = -1;

> +

> +       OpenSSL_add_all_digests();

> +       OpenSSL_add_all_ciphers();

> +       ERR_load_crypto_strings();

> +

> +       key = fileio_read_pkey(ctx->key_file);

> +       if (!key)

> +               goto err;

> +       cert = fileio_read_cert(ctx->cert_file);

> +       if (!cert)

> +               goto err;

> +

> +       /*

> +        * create a BIO, containing:

> +        *  * firmware image

> +        *  * monotonic count

> +        * in this order!

> +        * See EDK2's FmpAuthenticatedHandlerRsa2048Sha256()

> +        */

> +       data_bio = BIO_new(BIO_s_mem());

> +       BIO_write(data_bio, ctx->image_data, ctx->image_size);

> +       BIO_write(data_bio, &ctx->auth.monotonic_count,

> +                 sizeof(ctx->auth.monotonic_count));

> +

> +       md = EVP_get_digestbyname("SHA256");

> +       if (!md)

> +               goto err;

> +

> +       /* create signature */

> +       /* TODO: maybe add PKCS7_NOATTR and PKCS7_NOSMIMECAP */

> +       flags = PKCS7_BINARY | PKCS7_DETACHED;

> +       p7 = PKCS7_sign(NULL, NULL, NULL, data_bio, flags | PKCS7_PARTIAL);

> +       if (!p7)

> +               goto err;

> +       if (!PKCS7_sign_add_signer(p7, cert, key, md, flags))

> +               goto err;

> +       if (!PKCS7_final(p7, data_bio, flags))

> +               goto err;

> +

> +       /* convert pkcs7 into DER */

> +       ctx->sig_data = NULL;

> +       ctx->sig_size = ASN1_item_i2d((ASN1_VALUE *)p7, &ctx->sig_data,

> +                                     ASN1_ITEM_rptr(PKCS7));

> +       if (!ctx->sig_size)

> +               goto err;

> +

> +       /* fill auth_info */

> +       ctx->auth.auth_info.hdr.dwLength = sizeof(ctx->auth.auth_info)

> +                                               + ctx->sig_size;

> +       ctx->auth.auth_info.hdr.wRevision = WIN_CERT_REVISION_2_0;

> +       ctx->auth.auth_info.hdr.wCertificateType = WIN_CERT_TYPE_EFI_GUID;

> +       memcpy(&ctx->auth.auth_info.cert_type, &efi_guid_cert_type_pkcs7,

> +              sizeof(efi_guid_cert_type_pkcs7));

> +

> +       ret = 0;

> +err:

> +       BIO_free_all(data_bio);

> +       EVP_PKEY_free(key);

> +       X509_free(cert);

> +

> +       return ret;

> +}

> +

> +/**

> + * dump_signature - dump out a signature

> + * @path:      Path to a capsule file

> + * @signature: Signature data

> + * @sig_size:  Size of signature data

> + *

> + * Signature data pointed to by @signature will be saved into

> + * a file whose file name is @path with ".p7" suffix.

> + *

> + * Return:

> + * * 0  - on success

> + * * -1 - on failure

> + */

> +static int dump_signature(const char *path, u8 *signature, size_t sig_size)

> +{

> +       char *sig_path;

> +       FILE *f;

> +       size_t size;

> +       int ret = -1;

> +

> +       sig_path = malloc(strlen(path) + 3 + 1);

> +       if (!sig_path)

> +               return ret;

> +

> +       sprintf(sig_path, "%s.p7", path);

> +       f = fopen(sig_path, "w");

> +       if (!f)

> +               goto err;

> +

> +       size = fwrite(signature, 1, sig_size, f);

> +       if (size == sig_size)

> +               ret = 0;

> +

> +       fclose(f);

> +err:

> +       free(sig_path);

> +       return ret;

> +}

> +

> +/**

> + * free_sig_data - free out signature data

> + * @ctx:       Pointer to authentication context

> + *

> + * Free signature data allocated in create_auth_data().

> + */

> +static void free_sig_data(struct auth_context *ctx)

> +{

> +       if (ctx->sig_size)

> +               OPENSSL_free(ctx->sig_data);

> +}

> +#else

> +static int create_auth_data(struct auth_context *ctx)

> +{

> +       return 0;

> +}

> +

> +static int dump_signature(const char *path, u8 *signature, size_t sig_size)

> +{

> +       return 0;

> +}

> +

> +static void free_sig_data(struct auth_context *ctx) {}

> +#endif

> +

> +/**

> + * create_fwbin - create an uefi capsule file

> + * @path:      Path to a created capsule file

> + * @bin:       Path to a firmware binary to encapsulate

> + * @guid:      GUID of related FMP driver

> + * @index:     Index number in capsule

> + * @instance:  Instance number in capsule

> + * @mcount:    Monotonic count in authentication information

> + * @private_file:      Path to a private key file

> + * @cert_file: Path to a certificate file

> + *

> + * This function actually does the job of creating an uefi capsule file.

> + * All the arguments must be supplied.

> + * If either @private_file ror @cert_file is NULL, the capsule file

> + * won't be signed.

> + *

> + * Return:

> + * * 0  - on success

> + * * -1 - on failure

> + */

>  static int create_fwbin(char *path, char *bin, efi_guid_t *guid,

> -                       unsigned long index, unsigned long instance)

> +                       unsigned long index, unsigned long instance,

> +                       uint64_t mcount, char *privkey_file, char *cert_file)

>  {

>         struct efi_capsule_header header;

>         struct efi_firmware_management_capsule_header capsule;

>         struct efi_firmware_management_capsule_image_header image;

> +       struct auth_context auth_context;

>         FILE *f, *g;

>         struct stat bin_stat;

>         u8 *data;

> @@ -76,8 +363,9 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,

>  #ifdef DEBUG

>         printf("For output: %s\n", path);

>         printf("\tbin: %s\n\ttype: %pUl\n", bin, guid);

> -       printf("\tindex: %ld\n\tinstance: %ld\n", index, instance);

> +       printf("\tindex: %lu\n\tinstance: %lu\n", index, instance);

>  #endif

> +       auth_context.sig_size = 0;

>

>         g = fopen(bin, "r");

>         if (!g) {

> @@ -93,11 +381,34 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,

>                 printf("cannot allocate memory: %zx\n", (size_t)bin_stat.st_size);

>                 goto err_1;

>         }

> -       f = fopen(path, "w");

> -       if (!f) {

> -               printf("cannot open %s\n", path);

> +

> +       size = fread(data, 1, bin_stat.st_size, g);

> +       if (size < bin_stat.st_size) {

> +               printf("read failed (%zx)\n", size);

>                 goto err_2;

>         }

> +

> +       /* first, calculate signature to determine its size */

> +       if (privkey_file && cert_file) {

> +               auth_context.key_file = privkey_file;

> +               auth_context.cert_file = cert_file;

> +               auth_context.auth.monotonic_count = mcount;

> +               auth_context.image_data = data;

> +               auth_context.image_size = bin_stat.st_size;

> +

> +               if (create_auth_data(&auth_context)) {

> +                       printf("Signing firmware image failed\n");

> +                       goto err_3;

> +               }

> +

> +               if (dump_sig &&

> +                   dump_signature(path, auth_context.sig_data,

> +                                  auth_context.sig_size)) {

> +                       printf("Creating signature file failed\n");

> +                       goto err_3;

> +               }

> +       }

> +

>         header.capsule_guid = efi_guid_fm_capsule;

>         header.header_size = sizeof(header);

>         /* TODO: The current implementation ignores flags */

> @@ -106,11 +417,20 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,

>                                         + sizeof(capsule) + sizeof(u64)

>                                         + sizeof(image)

>                                         + bin_stat.st_size;

> +       if (auth_context.sig_size)

> +               header.capsule_image_size += sizeof(auth_context.auth)

> +                               + auth_context.sig_size;

> +

> +       f = fopen(path, "w");

> +       if (!f) {

> +               printf("cannot open %s\n", path);

> +               goto err_3;

> +       }

>

>         size = fwrite(&header, 1, sizeof(header), f);

>         if (size < sizeof(header)) {

>                 printf("write failed (%zx)\n", size);

> -               goto err_3;

> +               goto err_4;

>         }

>

>         capsule.version = 0x00000001;

> @@ -119,13 +439,13 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,

>         size = fwrite(&capsule, 1, sizeof(capsule), f);

>         if (size < (sizeof(capsule))) {

>                 printf("write failed (%zx)\n", size);

> -               goto err_3;

> +               goto err_4;

>         }

>         offset = sizeof(capsule) + sizeof(u64);

>         size = fwrite(&offset, 1, sizeof(offset), f);

>         if (size < sizeof(offset)) {

>                 printf("write failed (%zx)\n", size);

> -               goto err_3;

> +               goto err_4;

>         }

>

>         image.version = 0x00000003;

> @@ -135,34 +455,53 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,

>         image.reserved[1] = 0;

>         image.reserved[2] = 0;

>         image.update_image_size = bin_stat.st_size;

> +       if (auth_context.sig_size)

> +               image.update_image_size += sizeof(auth_context.auth)

> +                               + auth_context.sig_size;

>         image.update_vendor_code_size = 0; /* none */

>         image.update_hardware_instance = instance;

>         image.image_capsule_support = 0;

> +       if (auth_context.sig_size)

> +               image.image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION;

>

>         size = fwrite(&image, 1, sizeof(image), f);

>         if (size < sizeof(image)) {

>                 printf("write failed (%zx)\n", size);

> -               goto err_3;

> +               goto err_4;

>         }

> -       size = fread(data, 1, bin_stat.st_size, g);

> -       if (size < bin_stat.st_size) {

> -               printf("read failed (%zx)\n", size);

> -               goto err_3;

> +

> +       if (auth_context.sig_size) {

> +               size = fwrite(&auth_context.auth, 1,

> +                             sizeof(auth_context.auth), f);

> +               if (size < sizeof(auth_context.auth)) {

> +                       printf("write failed (%zx)\n", size);

> +                       goto err_4;

> +               }

> +               size = fwrite(auth_context.sig_data, 1,

> +                             auth_context.sig_size, f);

> +               if (size < auth_context.sig_size) {

> +                       printf("write failed (%zx)\n", size);

> +                       goto err_4;

> +               }

>         }

> +

>         size = fwrite(data, 1, bin_stat.st_size, f);

>         if (size < bin_stat.st_size) {

>                 printf("write failed (%zx)\n", size);

> -               goto err_3;

> +               goto err_4;

>         }

>

>         fclose(f);

>         fclose(g);

>         free(data);

> +       free_sig_data(&auth_context);

>

>         return 0;

>

> -err_3:

> +err_4:

>         fclose(f);

> +err_3:

> +       free_sig_data(&auth_context);

>  err_2:

>         free(data);

>  err_1:

> @@ -171,23 +510,37 @@ err_1:

>         return -1;

>  }

>

> -/*

> - * Usage:

> - *   $ mkeficapsule -f <firmware binary> <output file>

> +/**

> + * main - main entry function of mkeficapsule

> + * @argc:      Number of arguments

> + * @argv:      Array of pointers to arguments

> + *

> + * Create an uefi capsule file, optionally signing it.

> + * Parse all the arguments and pass them on to create_fwbin().

> + *

> + * Return:

> + * * 0  - on success

> + * * -1 - on failure

>   */

>  int main(int argc, char **argv)

>  {

>         char *file;

>         efi_guid_t *guid;

>         unsigned long index, instance;

> +       uint64_t mcount;

> +       char *privkey_file, *cert_file;

>         int c, idx;

>

>         file = NULL;

>         guid = NULL;

>         index = 0;

>         instance = 0;

> +       mcount = 0;

> +       privkey_file = NULL;

> +       cert_file = NULL;

> +       dump_sig = 0;

>         for (;;) {

> -               c = getopt_long(argc, argv, "f:r:i:I:v:h", options, &idx);

> +               c = getopt_long(argc, argv, opts_short, options, &idx);

>                 if (c == -1)

>                         break;

>

> @@ -214,29 +567,47 @@ int main(int argc, char **argv)

>                 case 'I':

>                         instance = strtoul(optarg, NULL, 0);

>                         break;

> +#ifdef CONFIG_TOOLS_LIBCRYPTO

> +               case 'p':

> +                       if (privkey_file) {

> +                               printf("Private Key already specified\n");

> +                               return -1;

> +                       }

> +                       privkey_file = optarg;

> +                       break;

> +               case 'c':

> +                       if (cert_file) {

> +                               printf("Certificate file already specified\n");

> +                               return -1;

> +                       }

> +                       cert_file = optarg;

> +                       break;

> +               case 'm':

> +                       mcount = strtoul(optarg, NULL, 0);

> +                       break;

> +               case 'd':

> +                       dump_sig = 1;

> +                       break;

> +#endif /* CONFIG_TOOLS_LIBCRYPTO */

>                 case 'h':

>                         print_usage();

>                         return 0;

>                 }

>         }

>

> -       /* need an output file */

> -       if (argc != optind + 1) {

> -               print_usage();

> -               exit(EXIT_FAILURE);

> -       }

> -

> -       /* need a fit image file or raw image file */

> -       if (!file) {

> +       /* check necessary parameters */

> +       if ((argc != optind + 1) || !file ||

> +           ((privkey_file && !cert_file) ||

> +            (!privkey_file && cert_file))) {

>                 print_usage();

> -               exit(EXIT_SUCCESS);

> +               return -1;

>         }

>

> -       if (create_fwbin(argv[optind], file, guid, index, instance)

> -                       < 0) {

> +       if (create_fwbin(argv[optind], file, guid, index, instance,

> +                        mcount, privkey_file, cert_file) < 0) {

>                 printf("Creating firmware capsule failed\n");

> -               exit(EXIT_FAILURE);

> +               return -1;

>         }

>

> -       exit(EXIT_SUCCESS);

> +       return 0;

>  }

> --

> 2.33.0

>



-- 
Masami Hiramatsu
AKASHI Takahiro Oct. 25, 2021, 3:12 a.m. UTC | #2
Hi, Masami,

On Wed, Oct 20, 2021 at 05:17:12PM +0900, Masami Hiramatsu wrote:
> Hello Akashi-san,

> 

> Can you split this patch out from this series?

> It seems that the series is doing several different things. This

> capsule signing has no alternatives, but others are modifying or

> replacing the current existing feature. In other words, this capsule

> signing feature has no alternatives, but others we can continue to

> use.


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

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

-Takahiro Akashi

> Thank you,

> 

> 2021年10月7日(木) 15:25 AKASHI Takahiro <takahiro.akashi@linaro.org>:

> >

> > With this enhancement, mkeficapsule will be able to sign a capsule

> > file when it is created. A signature added will be used later

> > in the verification at FMP's SetImage() call.

> >

> > To do that, We need specify additional command parameters:

> >   -monotonic-cout <count> : monotonic count

> >   -private-key <private key file> : private key file

> >   -certificate <certificate file> : certificate file

> > Only when all of those parameters are given, a signature will be added

> > to a capsule file.

> >

> > Users are expected to maintain and increment the monotonic count at

> > every time of the update for each firmware image.

> >

> > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>

> > ---

> >  tools/Kconfig        |   7 +

> >  tools/Makefile       |   8 +-

> >  tools/mkeficapsule.c | 435 +++++++++++++++++++++++++++++++++++++++----

> >  3 files changed, 416 insertions(+), 34 deletions(-)

> >

> > diff --git a/tools/Kconfig b/tools/Kconfig

> > index d6f82cd949b5..9a37ed035311 100644

> > --- a/tools/Kconfig

> > +++ b/tools/Kconfig

> > @@ -20,4 +20,11 @@ config TOOLS_LIBCRYPTO

> >           This selection does not affect target features, such as runtime FIT

> >           signature verification.

> >

> > +config TOOLS_MKEFICAPSULE

> > +       bool "Build efimkcapsule command"

> > +       default y if EFI_CAPSULE_ON_DISK

> > +       help

> > +         This command allows users to create a UEFI capsule file and,

> > +         optionally sign that file. If you want to enable UEFI capsule

> > +         update feature on your target, you certainly need this.

> >  endmenu

> > diff --git a/tools/Makefile b/tools/Makefile

> > index 4a86321f6467..6ea3033dbfb8 100644

> > --- a/tools/Makefile

> > +++ b/tools/Makefile

> > @@ -237,8 +237,12 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs

> >  hostprogs-$(CONFIG_ASN1_COMPILER)      += asn1_compiler

> >  HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include

> >

> > -mkeficapsule-objs      := mkeficapsule.o $(LIBFDT_OBJS)

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

> > +HOSTLDLIBS_mkeficapsule += -luuid

> > +ifeq ($(CONFIG_TOOLS_LIBCRYPTO),y)

> > +HOSTLDLIBS_mkeficapsule += \

> > +       $(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto")

> > +endif

> > +hostprogs-$(CONFIG_TOOLS_MKEFICAPSULE) += mkeficapsule

> >

> >  # We build some files with extra pedantic flags to try to minimize things

> >  # that won't build on some weird host compiler -- though there are lots of

> > diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c

> > index 4995ba4e0c2a..5541e4bda894 100644

> > --- a/tools/mkeficapsule.c

> > +++ b/tools/mkeficapsule.c

> > @@ -15,6 +15,16 @@

> >  #include <sys/stat.h>

> >  #include <sys/types.h>

> >

> > +#include <linux/kconfig.h>

> > +#ifdef CONFIG_TOOLS_LIBCRYPTO

> > +#include <openssl/asn1.h>

> > +#include <openssl/bio.h>

> > +#include <openssl/evp.h>

> > +#include <openssl/err.h>

> > +#include <openssl/pem.h>

> > +#include <openssl/pkcs7.h>

> > +#endif

> > +

> >  typedef __u8 u8;

> >  typedef __u16 u16;

> >  typedef __u32 u32;

> > @@ -38,12 +48,25 @@ efi_guid_t efi_guid_image_type_uboot_fit =

> >                 EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID;

> >  efi_guid_t efi_guid_image_type_uboot_raw =

> >                 EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID;

> > +efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;

> > +

> > +#ifdef CONFIG_TOOLS_LIBCRYPTO

> > +static const char *opts_short = "f:r:i:I:v:p:c:m:dh";

> > +#else

> > +static const char *opts_short = "f:r:i:I:v:h";

> > +#endif

> >

> >  static struct option options[] = {

> >         {"fit", required_argument, NULL, 'f'},

> >         {"raw", required_argument, NULL, 'r'},

> >         {"index", required_argument, NULL, 'i'},

> >         {"instance", required_argument, NULL, 'I'},

> > +#ifdef CONFIG_TOOLS_LIBCRYPTO

> > +       {"private-key", required_argument, NULL, 'p'},

> > +       {"certificate", required_argument, NULL, 'c'},

> > +       {"monotonic-count", required_argument, NULL, 'm'},

> > +       {"dump-sig", no_argument, NULL, 'd'},

> > +#endif

> >         {"help", no_argument, NULL, 'h'},

> >         {NULL, 0, NULL, 0},

> >  };

> > @@ -57,16 +80,280 @@ static void print_usage(void)

> >                "\t-r, --raw <raw image>       new raw image file\n"

> >                "\t-i, --index <index>         update image index\n"

> >                "\t-I, --instance <instance>   update hardware instance\n"

> > +#ifdef CONFIG_TOOLS_LIBCRYPTO

> > +              "\t-p, --private-key <privkey file>  private key file\n"

> > +              "\t-c, --certificate <cert file>     signer's certificate file\n"

> > +              "\t-m, --monotonic-count <count>     monotonic count\n"

> > +              "\t-d, --dump_sig              dump signature (*.p7)\n"

> > +#endif

> >                "\t-h, --help                  print a help message\n",

> >                tool_name);

> >  }

> >

> > +/**

> > + * auth_context - authentication context

> > + * @key_file:  Path to a private key file

> > + * @cert_file: Path to a certificate file

> > + * @image_data:        Pointer to firmware data

> > + * @image_size:        Size of firmware data

> > + * @auth:      Authentication header

> > + * @sig_data:  Signature data

> > + * @sig_size:  Size of signature data

> > + *

> > + * Data structure used in create_auth_data(). @key_file through

> > + * @image_size are input parameters. @auth, @sig_data and @sig_size

> > + * are filled in by create_auth_data().

> > + */

> > +struct auth_context {

> > +       char *key_file;

> > +       char *cert_file;

> > +       u8 *image_data;

> > +       size_t image_size;

> > +       struct efi_firmware_image_authentication auth;

> > +       u8 *sig_data;

> > +       size_t sig_size;

> > +};

> > +

> > +static int dump_sig;

> > +

> > +#ifdef CONFIG_TOOLS_LIBCRYPTO

> > +/**

> > + * fileio-read_pkey - read out a private key

> > + * @filename:  Path to a private key file

> > + *

> > + * Read out a private key file and parse it into "EVP_PKEY" structure.

> > + *

> > + * Return:

> > + * * Pointer to private key structure  - on success

> > + * * NULL - on failure

> > + */

> > +static EVP_PKEY *fileio_read_pkey(const char *filename)

> > +{

> > +       EVP_PKEY *key = NULL;

> > +       BIO *bio;

> > +

> > +       bio = BIO_new_file(filename, "r");

> > +       if (!bio)

> > +               goto out;

> > +

> > +       key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);

> > +

> > +out:

> > +       BIO_free_all(bio);

> > +       if (!key) {

> > +               printf("Can't load key from file '%s'\n", filename);

> > +               ERR_print_errors_fp(stderr);

> > +       }

> > +

> > +       return key;

> > +}

> > +

> > +/**

> > + * fileio-read_cert - read out a certificate

> > + * @filename:  Path to a certificate file

> > + *

> > + * Read out a certificate file and parse it into "X509" structure.

> > + *

> > + * Return:

> > + * * Pointer to certificate structure  - on success

> > + * * NULL - on failure

> > + */

> > +static X509 *fileio_read_cert(const char *filename)

> > +{

> > +       X509 *cert = NULL;

> > +       BIO *bio;

> > +

> > +       bio = BIO_new_file(filename, "r");

> > +       if (!bio)

> > +               goto out;

> > +

> > +       cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);

> > +

> > +out:

> > +       BIO_free_all(bio);

> > +       if (!cert) {

> > +               printf("Can't load certificate from file '%s'\n", filename);

> > +               ERR_print_errors_fp(stderr);

> > +       }

> > +

> > +       return cert;

> > +}

> > +

> > +/**

> > + * create_auth_data - compose authentication data in capsule

> > + * @auth_context:      Pointer to authentication context

> > + *

> > + * Fill up an authentication header (.auth) and signature data (.sig_data)

> > + * in @auth_context, using library functions from openssl.

> > + * All the parameters in @auth_context must be filled in by a caller.

> > + *

> > + * Return:

> > + * * 0  - on success

> > + * * -1 - on failure

> > + */

> > +static int create_auth_data(struct auth_context *ctx)

> > +{

> > +       EVP_PKEY *key = NULL;

> > +       X509 *cert = NULL;

> > +       BIO *data_bio = NULL;

> > +       const EVP_MD *md;

> > +       PKCS7 *p7;

> > +       int flags, ret = -1;

> > +

> > +       OpenSSL_add_all_digests();

> > +       OpenSSL_add_all_ciphers();

> > +       ERR_load_crypto_strings();

> > +

> > +       key = fileio_read_pkey(ctx->key_file);

> > +       if (!key)

> > +               goto err;

> > +       cert = fileio_read_cert(ctx->cert_file);

> > +       if (!cert)

> > +               goto err;

> > +

> > +       /*

> > +        * create a BIO, containing:

> > +        *  * firmware image

> > +        *  * monotonic count

> > +        * in this order!

> > +        * See EDK2's FmpAuthenticatedHandlerRsa2048Sha256()

> > +        */

> > +       data_bio = BIO_new(BIO_s_mem());

> > +       BIO_write(data_bio, ctx->image_data, ctx->image_size);

> > +       BIO_write(data_bio, &ctx->auth.monotonic_count,

> > +                 sizeof(ctx->auth.monotonic_count));

> > +

> > +       md = EVP_get_digestbyname("SHA256");

> > +       if (!md)

> > +               goto err;

> > +

> > +       /* create signature */

> > +       /* TODO: maybe add PKCS7_NOATTR and PKCS7_NOSMIMECAP */

> > +       flags = PKCS7_BINARY | PKCS7_DETACHED;

> > +       p7 = PKCS7_sign(NULL, NULL, NULL, data_bio, flags | PKCS7_PARTIAL);

> > +       if (!p7)

> > +               goto err;

> > +       if (!PKCS7_sign_add_signer(p7, cert, key, md, flags))

> > +               goto err;

> > +       if (!PKCS7_final(p7, data_bio, flags))

> > +               goto err;

> > +

> > +       /* convert pkcs7 into DER */

> > +       ctx->sig_data = NULL;

> > +       ctx->sig_size = ASN1_item_i2d((ASN1_VALUE *)p7, &ctx->sig_data,

> > +                                     ASN1_ITEM_rptr(PKCS7));

> > +       if (!ctx->sig_size)

> > +               goto err;

> > +

> > +       /* fill auth_info */

> > +       ctx->auth.auth_info.hdr.dwLength = sizeof(ctx->auth.auth_info)

> > +                                               + ctx->sig_size;

> > +       ctx->auth.auth_info.hdr.wRevision = WIN_CERT_REVISION_2_0;

> > +       ctx->auth.auth_info.hdr.wCertificateType = WIN_CERT_TYPE_EFI_GUID;

> > +       memcpy(&ctx->auth.auth_info.cert_type, &efi_guid_cert_type_pkcs7,

> > +              sizeof(efi_guid_cert_type_pkcs7));

> > +

> > +       ret = 0;

> > +err:

> > +       BIO_free_all(data_bio);

> > +       EVP_PKEY_free(key);

> > +       X509_free(cert);

> > +

> > +       return ret;

> > +}

> > +

> > +/**

> > + * dump_signature - dump out a signature

> > + * @path:      Path to a capsule file

> > + * @signature: Signature data

> > + * @sig_size:  Size of signature data

> > + *

> > + * Signature data pointed to by @signature will be saved into

> > + * a file whose file name is @path with ".p7" suffix.

> > + *

> > + * Return:

> > + * * 0  - on success

> > + * * -1 - on failure

> > + */

> > +static int dump_signature(const char *path, u8 *signature, size_t sig_size)

> > +{

> > +       char *sig_path;

> > +       FILE *f;

> > +       size_t size;

> > +       int ret = -1;

> > +

> > +       sig_path = malloc(strlen(path) + 3 + 1);

> > +       if (!sig_path)

> > +               return ret;

> > +

> > +       sprintf(sig_path, "%s.p7", path);

> > +       f = fopen(sig_path, "w");

> > +       if (!f)

> > +               goto err;

> > +

> > +       size = fwrite(signature, 1, sig_size, f);

> > +       if (size == sig_size)

> > +               ret = 0;

> > +

> > +       fclose(f);

> > +err:

> > +       free(sig_path);

> > +       return ret;

> > +}

> > +

> > +/**

> > + * free_sig_data - free out signature data

> > + * @ctx:       Pointer to authentication context

> > + *

> > + * Free signature data allocated in create_auth_data().

> > + */

> > +static void free_sig_data(struct auth_context *ctx)

> > +{

> > +       if (ctx->sig_size)

> > +               OPENSSL_free(ctx->sig_data);

> > +}

> > +#else

> > +static int create_auth_data(struct auth_context *ctx)

> > +{

> > +       return 0;

> > +}

> > +

> > +static int dump_signature(const char *path, u8 *signature, size_t sig_size)

> > +{

> > +       return 0;

> > +}

> > +

> > +static void free_sig_data(struct auth_context *ctx) {}

> > +#endif

> > +

> > +/**

> > + * create_fwbin - create an uefi capsule file

> > + * @path:      Path to a created capsule file

> > + * @bin:       Path to a firmware binary to encapsulate

> > + * @guid:      GUID of related FMP driver

> > + * @index:     Index number in capsule

> > + * @instance:  Instance number in capsule

> > + * @mcount:    Monotonic count in authentication information

> > + * @private_file:      Path to a private key file

> > + * @cert_file: Path to a certificate file

> > + *

> > + * This function actually does the job of creating an uefi capsule file.

> > + * All the arguments must be supplied.

> > + * If either @private_file ror @cert_file is NULL, the capsule file

> > + * won't be signed.

> > + *

> > + * Return:

> > + * * 0  - on success

> > + * * -1 - on failure

> > + */

> >  static int create_fwbin(char *path, char *bin, efi_guid_t *guid,

> > -                       unsigned long index, unsigned long instance)

> > +                       unsigned long index, unsigned long instance,

> > +                       uint64_t mcount, char *privkey_file, char *cert_file)

> >  {

> >         struct efi_capsule_header header;

> >         struct efi_firmware_management_capsule_header capsule;

> >         struct efi_firmware_management_capsule_image_header image;

> > +       struct auth_context auth_context;

> >         FILE *f, *g;

> >         struct stat bin_stat;

> >         u8 *data;

> > @@ -76,8 +363,9 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,

> >  #ifdef DEBUG

> >         printf("For output: %s\n", path);

> >         printf("\tbin: %s\n\ttype: %pUl\n", bin, guid);

> > -       printf("\tindex: %ld\n\tinstance: %ld\n", index, instance);

> > +       printf("\tindex: %lu\n\tinstance: %lu\n", index, instance);

> >  #endif

> > +       auth_context.sig_size = 0;

> >

> >         g = fopen(bin, "r");

> >         if (!g) {

> > @@ -93,11 +381,34 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,

> >                 printf("cannot allocate memory: %zx\n", (size_t)bin_stat.st_size);

> >                 goto err_1;

> >         }

> > -       f = fopen(path, "w");

> > -       if (!f) {

> > -               printf("cannot open %s\n", path);

> > +

> > +       size = fread(data, 1, bin_stat.st_size, g);

> > +       if (size < bin_stat.st_size) {

> > +               printf("read failed (%zx)\n", size);

> >                 goto err_2;

> >         }

> > +

> > +       /* first, calculate signature to determine its size */

> > +       if (privkey_file && cert_file) {

> > +               auth_context.key_file = privkey_file;

> > +               auth_context.cert_file = cert_file;

> > +               auth_context.auth.monotonic_count = mcount;

> > +               auth_context.image_data = data;

> > +               auth_context.image_size = bin_stat.st_size;

> > +

> > +               if (create_auth_data(&auth_context)) {

> > +                       printf("Signing firmware image failed\n");

> > +                       goto err_3;

> > +               }

> > +

> > +               if (dump_sig &&

> > +                   dump_signature(path, auth_context.sig_data,

> > +                                  auth_context.sig_size)) {

> > +                       printf("Creating signature file failed\n");

> > +                       goto err_3;

> > +               }

> > +       }

> > +

> >         header.capsule_guid = efi_guid_fm_capsule;

> >         header.header_size = sizeof(header);

> >         /* TODO: The current implementation ignores flags */

> > @@ -106,11 +417,20 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,

> >                                         + sizeof(capsule) + sizeof(u64)

> >                                         + sizeof(image)

> >                                         + bin_stat.st_size;

> > +       if (auth_context.sig_size)

> > +               header.capsule_image_size += sizeof(auth_context.auth)

> > +                               + auth_context.sig_size;

> > +

> > +       f = fopen(path, "w");

> > +       if (!f) {

> > +               printf("cannot open %s\n", path);

> > +               goto err_3;

> > +       }

> >

> >         size = fwrite(&header, 1, sizeof(header), f);

> >         if (size < sizeof(header)) {

> >                 printf("write failed (%zx)\n", size);

> > -               goto err_3;

> > +               goto err_4;

> >         }

> >

> >         capsule.version = 0x00000001;

> > @@ -119,13 +439,13 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,

> >         size = fwrite(&capsule, 1, sizeof(capsule), f);

> >         if (size < (sizeof(capsule))) {

> >                 printf("write failed (%zx)\n", size);

> > -               goto err_3;

> > +               goto err_4;

> >         }

> >         offset = sizeof(capsule) + sizeof(u64);

> >         size = fwrite(&offset, 1, sizeof(offset), f);

> >         if (size < sizeof(offset)) {

> >                 printf("write failed (%zx)\n", size);

> > -               goto err_3;

> > +               goto err_4;

> >         }

> >

> >         image.version = 0x00000003;

> > @@ -135,34 +455,53 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,

> >         image.reserved[1] = 0;

> >         image.reserved[2] = 0;

> >         image.update_image_size = bin_stat.st_size;

> > +       if (auth_context.sig_size)

> > +               image.update_image_size += sizeof(auth_context.auth)

> > +                               + auth_context.sig_size;

> >         image.update_vendor_code_size = 0; /* none */

> >         image.update_hardware_instance = instance;

> >         image.image_capsule_support = 0;

> > +       if (auth_context.sig_size)

> > +               image.image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION;

> >

> >         size = fwrite(&image, 1, sizeof(image), f);

> >         if (size < sizeof(image)) {

> >                 printf("write failed (%zx)\n", size);

> > -               goto err_3;

> > +               goto err_4;

> >         }

> > -       size = fread(data, 1, bin_stat.st_size, g);

> > -       if (size < bin_stat.st_size) {

> > -               printf("read failed (%zx)\n", size);

> > -               goto err_3;

> > +

> > +       if (auth_context.sig_size) {

> > +               size = fwrite(&auth_context.auth, 1,

> > +                             sizeof(auth_context.auth), f);

> > +               if (size < sizeof(auth_context.auth)) {

> > +                       printf("write failed (%zx)\n", size);

> > +                       goto err_4;

> > +               }

> > +               size = fwrite(auth_context.sig_data, 1,

> > +                             auth_context.sig_size, f);

> > +               if (size < auth_context.sig_size) {

> > +                       printf("write failed (%zx)\n", size);

> > +                       goto err_4;

> > +               }

> >         }

> > +

> >         size = fwrite(data, 1, bin_stat.st_size, f);

> >         if (size < bin_stat.st_size) {

> >                 printf("write failed (%zx)\n", size);

> > -               goto err_3;

> > +               goto err_4;

> >         }

> >

> >         fclose(f);

> >         fclose(g);

> >         free(data);

> > +       free_sig_data(&auth_context);

> >

> >         return 0;

> >

> > -err_3:

> > +err_4:

> >         fclose(f);

> > +err_3:

> > +       free_sig_data(&auth_context);

> >  err_2:

> >         free(data);

> >  err_1:

> > @@ -171,23 +510,37 @@ err_1:

> >         return -1;

> >  }

> >

> > -/*

> > - * Usage:

> > - *   $ mkeficapsule -f <firmware binary> <output file>

> > +/**

> > + * main - main entry function of mkeficapsule

> > + * @argc:      Number of arguments

> > + * @argv:      Array of pointers to arguments

> > + *

> > + * Create an uefi capsule file, optionally signing it.

> > + * Parse all the arguments and pass them on to create_fwbin().

> > + *

> > + * Return:

> > + * * 0  - on success

> > + * * -1 - on failure

> >   */

> >  int main(int argc, char **argv)

> >  {

> >         char *file;

> >         efi_guid_t *guid;

> >         unsigned long index, instance;

> > +       uint64_t mcount;

> > +       char *privkey_file, *cert_file;

> >         int c, idx;

> >

> >         file = NULL;

> >         guid = NULL;

> >         index = 0;

> >         instance = 0;

> > +       mcount = 0;

> > +       privkey_file = NULL;

> > +       cert_file = NULL;

> > +       dump_sig = 0;

> >         for (;;) {

> > -               c = getopt_long(argc, argv, "f:r:i:I:v:h", options, &idx);

> > +               c = getopt_long(argc, argv, opts_short, options, &idx);

> >                 if (c == -1)

> >                         break;

> >

> > @@ -214,29 +567,47 @@ int main(int argc, char **argv)

> >                 case 'I':

> >                         instance = strtoul(optarg, NULL, 0);

> >                         break;

> > +#ifdef CONFIG_TOOLS_LIBCRYPTO

> > +               case 'p':

> > +                       if (privkey_file) {

> > +                               printf("Private Key already specified\n");

> > +                               return -1;

> > +                       }

> > +                       privkey_file = optarg;

> > +                       break;

> > +               case 'c':

> > +                       if (cert_file) {

> > +                               printf("Certificate file already specified\n");

> > +                               return -1;

> > +                       }

> > +                       cert_file = optarg;

> > +                       break;

> > +               case 'm':

> > +                       mcount = strtoul(optarg, NULL, 0);

> > +                       break;

> > +               case 'd':

> > +                       dump_sig = 1;

> > +                       break;

> > +#endif /* CONFIG_TOOLS_LIBCRYPTO */

> >                 case 'h':

> >                         print_usage();

> >                         return 0;

> >                 }

> >         }

> >

> > -       /* need an output file */

> > -       if (argc != optind + 1) {

> > -               print_usage();

> > -               exit(EXIT_FAILURE);

> > -       }

> > -

> > -       /* need a fit image file or raw image file */

> > -       if (!file) {

> > +       /* check necessary parameters */

> > +       if ((argc != optind + 1) || !file ||

> > +           ((privkey_file && !cert_file) ||

> > +            (!privkey_file && cert_file))) {

> >                 print_usage();

> > -               exit(EXIT_SUCCESS);

> > +               return -1;

> >         }

> >

> > -       if (create_fwbin(argv[optind], file, guid, index, instance)

> > -                       < 0) {

> > +       if (create_fwbin(argv[optind], file, guid, index, instance,

> > +                        mcount, privkey_file, cert_file) < 0) {

> >                 printf("Creating firmware capsule failed\n");

> > -               exit(EXIT_FAILURE);

> > +               return -1;

> >         }

> >

> > -       exit(EXIT_SUCCESS);

> > +       return 0;

> >  }

> > --

> > 2.33.0

> >

> 

> 

> -- 

> Masami Hiramatsu
Masami Hiramatsu Oct. 25, 2021, 5:40 a.m. UTC | #3
Hi Takahiro,

2021年10月25日(月) 12:12 AKASHI Takahiro <takahiro.akashi@linaro.org>:
>

> Hi, Masami,

>

> On Wed, Oct 20, 2021 at 05:17:12PM +0900, Masami Hiramatsu wrote:

> > Hello Akashi-san,

> >

> > Can you split this patch out from this series?

> > It seems that the series is doing several different things. This

> > capsule signing has no alternatives, but others are modifying or

> > replacing the current existing feature. In other words, this capsule

> > signing feature has no alternatives, but others we can continue to

> > use.

>

> What do you specifically mean by "existing feature" or "others we can

> continue to use"?

>

> All the commits, I think, in this series are closely related with

> each other.


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

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

Thank you,


>

> -Takahiro Akashi

>

> > Thank you,

> >

> > 2021年10月7日(木) 15:25 AKASHI Takahiro <takahiro.akashi@linaro.org>:

> > >

> > > With this enhancement, mkeficapsule will be able to sign a capsule

> > > file when it is created. A signature added will be used later

> > > in the verification at FMP's SetImage() call.

> > >

> > > To do that, We need specify additional command parameters:

> > >   -monotonic-cout <count> : monotonic count

> > >   -private-key <private key file> : private key file

> > >   -certificate <certificate file> : certificate file

> > > Only when all of those parameters are given, a signature will be added

> > > to a capsule file.

> > >

> > > Users are expected to maintain and increment the monotonic count at

> > > every time of the update for each firmware image.

> > >

> > > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>

> > > ---

> > >  tools/Kconfig        |   7 +

> > >  tools/Makefile       |   8 +-

> > >  tools/mkeficapsule.c | 435 +++++++++++++++++++++++++++++++++++++++----

> > >  3 files changed, 416 insertions(+), 34 deletions(-)

> > >

> > > diff --git a/tools/Kconfig b/tools/Kconfig

> > > index d6f82cd949b5..9a37ed035311 100644

> > > --- a/tools/Kconfig

> > > +++ b/tools/Kconfig

> > > @@ -20,4 +20,11 @@ config TOOLS_LIBCRYPTO

> > >           This selection does not affect target features, such as runtime FIT

> > >           signature verification.

> > >

> > > +config TOOLS_MKEFICAPSULE

> > > +       bool "Build efimkcapsule command"

> > > +       default y if EFI_CAPSULE_ON_DISK

> > > +       help

> > > +         This command allows users to create a UEFI capsule file and,

> > > +         optionally sign that file. If you want to enable UEFI capsule

> > > +         update feature on your target, you certainly need this.

> > >  endmenu

> > > diff --git a/tools/Makefile b/tools/Makefile

> > > index 4a86321f6467..6ea3033dbfb8 100644

> > > --- a/tools/Makefile

> > > +++ b/tools/Makefile

> > > @@ -237,8 +237,12 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs

> > >  hostprogs-$(CONFIG_ASN1_COMPILER)      += asn1_compiler

> > >  HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include

> > >

> > > -mkeficapsule-objs      := mkeficapsule.o $(LIBFDT_OBJS)

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

> > > +HOSTLDLIBS_mkeficapsule += -luuid

> > > +ifeq ($(CONFIG_TOOLS_LIBCRYPTO),y)

> > > +HOSTLDLIBS_mkeficapsule += \

> > > +       $(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto")

> > > +endif

> > > +hostprogs-$(CONFIG_TOOLS_MKEFICAPSULE) += mkeficapsule

> > >

> > >  # We build some files with extra pedantic flags to try to minimize things

> > >  # that won't build on some weird host compiler -- though there are lots of

> > > diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c

> > > index 4995ba4e0c2a..5541e4bda894 100644

> > > --- a/tools/mkeficapsule.c

> > > +++ b/tools/mkeficapsule.c

> > > @@ -15,6 +15,16 @@

> > >  #include <sys/stat.h>

> > >  #include <sys/types.h>

> > >

> > > +#include <linux/kconfig.h>

> > > +#ifdef CONFIG_TOOLS_LIBCRYPTO

> > > +#include <openssl/asn1.h>

> > > +#include <openssl/bio.h>

> > > +#include <openssl/evp.h>

> > > +#include <openssl/err.h>

> > > +#include <openssl/pem.h>

> > > +#include <openssl/pkcs7.h>

> > > +#endif

> > > +

> > >  typedef __u8 u8;

> > >  typedef __u16 u16;

> > >  typedef __u32 u32;

> > > @@ -38,12 +48,25 @@ efi_guid_t efi_guid_image_type_uboot_fit =

> > >                 EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID;

> > >  efi_guid_t efi_guid_image_type_uboot_raw =

> > >                 EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID;

> > > +efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;

> > > +

> > > +#ifdef CONFIG_TOOLS_LIBCRYPTO

> > > +static const char *opts_short = "f:r:i:I:v:p:c:m:dh";

> > > +#else

> > > +static const char *opts_short = "f:r:i:I:v:h";

> > > +#endif

> > >

> > >  static struct option options[] = {

> > >         {"fit", required_argument, NULL, 'f'},

> > >         {"raw", required_argument, NULL, 'r'},

> > >         {"index", required_argument, NULL, 'i'},

> > >         {"instance", required_argument, NULL, 'I'},

> > > +#ifdef CONFIG_TOOLS_LIBCRYPTO

> > > +       {"private-key", required_argument, NULL, 'p'},

> > > +       {"certificate", required_argument, NULL, 'c'},

> > > +       {"monotonic-count", required_argument, NULL, 'm'},

> > > +       {"dump-sig", no_argument, NULL, 'd'},

> > > +#endif

> > >         {"help", no_argument, NULL, 'h'},

> > >         {NULL, 0, NULL, 0},

> > >  };

> > > @@ -57,16 +80,280 @@ static void print_usage(void)

> > >                "\t-r, --raw <raw image>       new raw image file\n"

> > >                "\t-i, --index <index>         update image index\n"

> > >                "\t-I, --instance <instance>   update hardware instance\n"

> > > +#ifdef CONFIG_TOOLS_LIBCRYPTO

> > > +              "\t-p, --private-key <privkey file>  private key file\n"

> > > +              "\t-c, --certificate <cert file>     signer's certificate file\n"

> > > +              "\t-m, --monotonic-count <count>     monotonic count\n"

> > > +              "\t-d, --dump_sig              dump signature (*.p7)\n"

> > > +#endif

> > >                "\t-h, --help                  print a help message\n",

> > >                tool_name);

> > >  }

> > >

> > > +/**

> > > + * auth_context - authentication context

> > > + * @key_file:  Path to a private key file

> > > + * @cert_file: Path to a certificate file

> > > + * @image_data:        Pointer to firmware data

> > > + * @image_size:        Size of firmware data

> > > + * @auth:      Authentication header

> > > + * @sig_data:  Signature data

> > > + * @sig_size:  Size of signature data

> > > + *

> > > + * Data structure used in create_auth_data(). @key_file through

> > > + * @image_size are input parameters. @auth, @sig_data and @sig_size

> > > + * are filled in by create_auth_data().

> > > + */

> > > +struct auth_context {

> > > +       char *key_file;

> > > +       char *cert_file;

> > > +       u8 *image_data;

> > > +       size_t image_size;

> > > +       struct efi_firmware_image_authentication auth;

> > > +       u8 *sig_data;

> > > +       size_t sig_size;

> > > +};

> > > +

> > > +static int dump_sig;

> > > +

> > > +#ifdef CONFIG_TOOLS_LIBCRYPTO

> > > +/**

> > > + * fileio-read_pkey - read out a private key

> > > + * @filename:  Path to a private key file

> > > + *

> > > + * Read out a private key file and parse it into "EVP_PKEY" structure.

> > > + *

> > > + * Return:

> > > + * * Pointer to private key structure  - on success

> > > + * * NULL - on failure

> > > + */

> > > +static EVP_PKEY *fileio_read_pkey(const char *filename)

> > > +{

> > > +       EVP_PKEY *key = NULL;

> > > +       BIO *bio;

> > > +

> > > +       bio = BIO_new_file(filename, "r");

> > > +       if (!bio)

> > > +               goto out;

> > > +

> > > +       key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);

> > > +

> > > +out:

> > > +       BIO_free_all(bio);

> > > +       if (!key) {

> > > +               printf("Can't load key from file '%s'\n", filename);

> > > +               ERR_print_errors_fp(stderr);

> > > +       }

> > > +

> > > +       return key;

> > > +}

> > > +

> > > +/**

> > > + * fileio-read_cert - read out a certificate

> > > + * @filename:  Path to a certificate file

> > > + *

> > > + * Read out a certificate file and parse it into "X509" structure.

> > > + *

> > > + * Return:

> > > + * * Pointer to certificate structure  - on success

> > > + * * NULL - on failure

> > > + */

> > > +static X509 *fileio_read_cert(const char *filename)

> > > +{

> > > +       X509 *cert = NULL;

> > > +       BIO *bio;

> > > +

> > > +       bio = BIO_new_file(filename, "r");

> > > +       if (!bio)

> > > +               goto out;

> > > +

> > > +       cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);

> > > +

> > > +out:

> > > +       BIO_free_all(bio);

> > > +       if (!cert) {

> > > +               printf("Can't load certificate from file '%s'\n", filename);

> > > +               ERR_print_errors_fp(stderr);

> > > +       }

> > > +

> > > +       return cert;

> > > +}

> > > +

> > > +/**

> > > + * create_auth_data - compose authentication data in capsule

> > > + * @auth_context:      Pointer to authentication context

> > > + *

> > > + * Fill up an authentication header (.auth) and signature data (.sig_data)

> > > + * in @auth_context, using library functions from openssl.

> > > + * All the parameters in @auth_context must be filled in by a caller.

> > > + *

> > > + * Return:

> > > + * * 0  - on success

> > > + * * -1 - on failure

> > > + */

> > > +static int create_auth_data(struct auth_context *ctx)

> > > +{

> > > +       EVP_PKEY *key = NULL;

> > > +       X509 *cert = NULL;

> > > +       BIO *data_bio = NULL;

> > > +       const EVP_MD *md;

> > > +       PKCS7 *p7;

> > > +       int flags, ret = -1;

> > > +

> > > +       OpenSSL_add_all_digests();

> > > +       OpenSSL_add_all_ciphers();

> > > +       ERR_load_crypto_strings();

> > > +

> > > +       key = fileio_read_pkey(ctx->key_file);

> > > +       if (!key)

> > > +               goto err;

> > > +       cert = fileio_read_cert(ctx->cert_file);

> > > +       if (!cert)

> > > +               goto err;

> > > +

> > > +       /*

> > > +        * create a BIO, containing:

> > > +        *  * firmware image

> > > +        *  * monotonic count

> > > +        * in this order!

> > > +        * See EDK2's FmpAuthenticatedHandlerRsa2048Sha256()

> > > +        */

> > > +       data_bio = BIO_new(BIO_s_mem());

> > > +       BIO_write(data_bio, ctx->image_data, ctx->image_size);

> > > +       BIO_write(data_bio, &ctx->auth.monotonic_count,

> > > +                 sizeof(ctx->auth.monotonic_count));

> > > +

> > > +       md = EVP_get_digestbyname("SHA256");

> > > +       if (!md)

> > > +               goto err;

> > > +

> > > +       /* create signature */

> > > +       /* TODO: maybe add PKCS7_NOATTR and PKCS7_NOSMIMECAP */

> > > +       flags = PKCS7_BINARY | PKCS7_DETACHED;

> > > +       p7 = PKCS7_sign(NULL, NULL, NULL, data_bio, flags | PKCS7_PARTIAL);

> > > +       if (!p7)

> > > +               goto err;

> > > +       if (!PKCS7_sign_add_signer(p7, cert, key, md, flags))

> > > +               goto err;

> > > +       if (!PKCS7_final(p7, data_bio, flags))

> > > +               goto err;

> > > +

> > > +       /* convert pkcs7 into DER */

> > > +       ctx->sig_data = NULL;

> > > +       ctx->sig_size = ASN1_item_i2d((ASN1_VALUE *)p7, &ctx->sig_data,

> > > +                                     ASN1_ITEM_rptr(PKCS7));

> > > +       if (!ctx->sig_size)

> > > +               goto err;

> > > +

> > > +       /* fill auth_info */

> > > +       ctx->auth.auth_info.hdr.dwLength = sizeof(ctx->auth.auth_info)

> > > +                                               + ctx->sig_size;

> > > +       ctx->auth.auth_info.hdr.wRevision = WIN_CERT_REVISION_2_0;

> > > +       ctx->auth.auth_info.hdr.wCertificateType = WIN_CERT_TYPE_EFI_GUID;

> > > +       memcpy(&ctx->auth.auth_info.cert_type, &efi_guid_cert_type_pkcs7,

> > > +              sizeof(efi_guid_cert_type_pkcs7));

> > > +

> > > +       ret = 0;

> > > +err:

> > > +       BIO_free_all(data_bio);

> > > +       EVP_PKEY_free(key);

> > > +       X509_free(cert);

> > > +

> > > +       return ret;

> > > +}

> > > +

> > > +/**

> > > + * dump_signature - dump out a signature

> > > + * @path:      Path to a capsule file

> > > + * @signature: Signature data

> > > + * @sig_size:  Size of signature data

> > > + *

> > > + * Signature data pointed to by @signature will be saved into

> > > + * a file whose file name is @path with ".p7" suffix.

> > > + *

> > > + * Return:

> > > + * * 0  - on success

> > > + * * -1 - on failure

> > > + */

> > > +static int dump_signature(const char *path, u8 *signature, size_t sig_size)

> > > +{

> > > +       char *sig_path;

> > > +       FILE *f;

> > > +       size_t size;

> > > +       int ret = -1;

> > > +

> > > +       sig_path = malloc(strlen(path) + 3 + 1);

> > > +       if (!sig_path)

> > > +               return ret;

> > > +

> > > +       sprintf(sig_path, "%s.p7", path);

> > > +       f = fopen(sig_path, "w");

> > > +       if (!f)

> > > +               goto err;

> > > +

> > > +       size = fwrite(signature, 1, sig_size, f);

> > > +       if (size == sig_size)

> > > +               ret = 0;

> > > +

> > > +       fclose(f);

> > > +err:

> > > +       free(sig_path);

> > > +       return ret;

> > > +}

> > > +

> > > +/**

> > > + * free_sig_data - free out signature data

> > > + * @ctx:       Pointer to authentication context

> > > + *

> > > + * Free signature data allocated in create_auth_data().

> > > + */

> > > +static void free_sig_data(struct auth_context *ctx)

> > > +{

> > > +       if (ctx->sig_size)

> > > +               OPENSSL_free(ctx->sig_data);

> > > +}

> > > +#else

> > > +static int create_auth_data(struct auth_context *ctx)

> > > +{

> > > +       return 0;

> > > +}

> > > +

> > > +static int dump_signature(const char *path, u8 *signature, size_t sig_size)

> > > +{

> > > +       return 0;

> > > +}

> > > +

> > > +static void free_sig_data(struct auth_context *ctx) {}

> > > +#endif

> > > +

> > > +/**

> > > + * create_fwbin - create an uefi capsule file

> > > + * @path:      Path to a created capsule file

> > > + * @bin:       Path to a firmware binary to encapsulate

> > > + * @guid:      GUID of related FMP driver

> > > + * @index:     Index number in capsule

> > > + * @instance:  Instance number in capsule

> > > + * @mcount:    Monotonic count in authentication information

> > > + * @private_file:      Path to a private key file

> > > + * @cert_file: Path to a certificate file

> > > + *

> > > + * This function actually does the job of creating an uefi capsule file.

> > > + * All the arguments must be supplied.

> > > + * If either @private_file ror @cert_file is NULL, the capsule file

> > > + * won't be signed.

> > > + *

> > > + * Return:

> > > + * * 0  - on success

> > > + * * -1 - on failure

> > > + */

> > >  static int create_fwbin(char *path, char *bin, efi_guid_t *guid,

> > > -                       unsigned long index, unsigned long instance)

> > > +                       unsigned long index, unsigned long instance,

> > > +                       uint64_t mcount, char *privkey_file, char *cert_file)

> > >  {

> > >         struct efi_capsule_header header;

> > >         struct efi_firmware_management_capsule_header capsule;

> > >         struct efi_firmware_management_capsule_image_header image;

> > > +       struct auth_context auth_context;

> > >         FILE *f, *g;

> > >         struct stat bin_stat;

> > >         u8 *data;

> > > @@ -76,8 +363,9 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,

> > >  #ifdef DEBUG

> > >         printf("For output: %s\n", path);

> > >         printf("\tbin: %s\n\ttype: %pUl\n", bin, guid);

> > > -       printf("\tindex: %ld\n\tinstance: %ld\n", index, instance);

> > > +       printf("\tindex: %lu\n\tinstance: %lu\n", index, instance);

> > >  #endif

> > > +       auth_context.sig_size = 0;

> > >

> > >         g = fopen(bin, "r");

> > >         if (!g) {

> > > @@ -93,11 +381,34 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,

> > >                 printf("cannot allocate memory: %zx\n", (size_t)bin_stat.st_size);

> > >                 goto err_1;

> > >         }

> > > -       f = fopen(path, "w");

> > > -       if (!f) {

> > > -               printf("cannot open %s\n", path);

> > > +

> > > +       size = fread(data, 1, bin_stat.st_size, g);

> > > +       if (size < bin_stat.st_size) {

> > > +               printf("read failed (%zx)\n", size);

> > >                 goto err_2;

> > >         }

> > > +

> > > +       /* first, calculate signature to determine its size */

> > > +       if (privkey_file && cert_file) {

> > > +               auth_context.key_file = privkey_file;

> > > +               auth_context.cert_file = cert_file;

> > > +               auth_context.auth.monotonic_count = mcount;

> > > +               auth_context.image_data = data;

> > > +               auth_context.image_size = bin_stat.st_size;

> > > +

> > > +               if (create_auth_data(&auth_context)) {

> > > +                       printf("Signing firmware image failed\n");

> > > +                       goto err_3;

> > > +               }

> > > +

> > > +               if (dump_sig &&

> > > +                   dump_signature(path, auth_context.sig_data,

> > > +                                  auth_context.sig_size)) {

> > > +                       printf("Creating signature file failed\n");

> > > +                       goto err_3;

> > > +               }

> > > +       }

> > > +

> > >         header.capsule_guid = efi_guid_fm_capsule;

> > >         header.header_size = sizeof(header);

> > >         /* TODO: The current implementation ignores flags */

> > > @@ -106,11 +417,20 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,

> > >                                         + sizeof(capsule) + sizeof(u64)

> > >                                         + sizeof(image)

> > >                                         + bin_stat.st_size;

> > > +       if (auth_context.sig_size)

> > > +               header.capsule_image_size += sizeof(auth_context.auth)

> > > +                               + auth_context.sig_size;

> > > +

> > > +       f = fopen(path, "w");

> > > +       if (!f) {

> > > +               printf("cannot open %s\n", path);

> > > +               goto err_3;

> > > +       }

> > >

> > >         size = fwrite(&header, 1, sizeof(header), f);

> > >         if (size < sizeof(header)) {

> > >                 printf("write failed (%zx)\n", size);

> > > -               goto err_3;

> > > +               goto err_4;

> > >         }

> > >

> > >         capsule.version = 0x00000001;

> > > @@ -119,13 +439,13 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,

> > >         size = fwrite(&capsule, 1, sizeof(capsule), f);

> > >         if (size < (sizeof(capsule))) {

> > >                 printf("write failed (%zx)\n", size);

> > > -               goto err_3;

> > > +               goto err_4;

> > >         }

> > >         offset = sizeof(capsule) + sizeof(u64);

> > >         size = fwrite(&offset, 1, sizeof(offset), f);

> > >         if (size < sizeof(offset)) {

> > >                 printf("write failed (%zx)\n", size);

> > > -               goto err_3;

> > > +               goto err_4;

> > >         }

> > >

> > >         image.version = 0x00000003;

> > > @@ -135,34 +455,53 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,

> > >         image.reserved[1] = 0;

> > >         image.reserved[2] = 0;

> > >         image.update_image_size = bin_stat.st_size;

> > > +       if (auth_context.sig_size)

> > > +               image.update_image_size += sizeof(auth_context.auth)

> > > +                               + auth_context.sig_size;

> > >         image.update_vendor_code_size = 0; /* none */

> > >         image.update_hardware_instance = instance;

> > >         image.image_capsule_support = 0;

> > > +       if (auth_context.sig_size)

> > > +               image.image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION;

> > >

> > >         size = fwrite(&image, 1, sizeof(image), f);

> > >         if (size < sizeof(image)) {

> > >                 printf("write failed (%zx)\n", size);

> > > -               goto err_3;

> > > +               goto err_4;

> > >         }

> > > -       size = fread(data, 1, bin_stat.st_size, g);

> > > -       if (size < bin_stat.st_size) {

> > > -               printf("read failed (%zx)\n", size);

> > > -               goto err_3;

> > > +

> > > +       if (auth_context.sig_size) {

> > > +               size = fwrite(&auth_context.auth, 1,

> > > +                             sizeof(auth_context.auth), f);

> > > +               if (size < sizeof(auth_context.auth)) {

> > > +                       printf("write failed (%zx)\n", size);

> > > +                       goto err_4;

> > > +               }

> > > +               size = fwrite(auth_context.sig_data, 1,

> > > +                             auth_context.sig_size, f);

> > > +               if (size < auth_context.sig_size) {

> > > +                       printf("write failed (%zx)\n", size);

> > > +                       goto err_4;

> > > +               }

> > >         }

> > > +

> > >         size = fwrite(data, 1, bin_stat.st_size, f);

> > >         if (size < bin_stat.st_size) {

> > >                 printf("write failed (%zx)\n", size);

> > > -               goto err_3;

> > > +               goto err_4;

> > >         }

> > >

> > >         fclose(f);

> > >         fclose(g);

> > >         free(data);

> > > +       free_sig_data(&auth_context);

> > >

> > >         return 0;

> > >

> > > -err_3:

> > > +err_4:

> > >         fclose(f);

> > > +err_3:

> > > +       free_sig_data(&auth_context);

> > >  err_2:

> > >         free(data);

> > >  err_1:

> > > @@ -171,23 +510,37 @@ err_1:

> > >         return -1;

> > >  }

> > >

> > > -/*

> > > - * Usage:

> > > - *   $ mkeficapsule -f <firmware binary> <output file>

> > > +/**

> > > + * main - main entry function of mkeficapsule

> > > + * @argc:      Number of arguments

> > > + * @argv:      Array of pointers to arguments

> > > + *

> > > + * Create an uefi capsule file, optionally signing it.

> > > + * Parse all the arguments and pass them on to create_fwbin().

> > > + *

> > > + * Return:

> > > + * * 0  - on success

> > > + * * -1 - on failure

> > >   */

> > >  int main(int argc, char **argv)

> > >  {

> > >         char *file;

> > >         efi_guid_t *guid;

> > >         unsigned long index, instance;

> > > +       uint64_t mcount;

> > > +       char *privkey_file, *cert_file;

> > >         int c, idx;

> > >

> > >         file = NULL;

> > >         guid = NULL;

> > >         index = 0;

> > >         instance = 0;

> > > +       mcount = 0;

> > > +       privkey_file = NULL;

> > > +       cert_file = NULL;

> > > +       dump_sig = 0;

> > >         for (;;) {

> > > -               c = getopt_long(argc, argv, "f:r:i:I:v:h", options, &idx);

> > > +               c = getopt_long(argc, argv, opts_short, options, &idx);

> > >                 if (c == -1)

> > >                         break;

> > >

> > > @@ -214,29 +567,47 @@ int main(int argc, char **argv)

> > >                 case 'I':

> > >                         instance = strtoul(optarg, NULL, 0);

> > >                         break;

> > > +#ifdef CONFIG_TOOLS_LIBCRYPTO

> > > +               case 'p':

> > > +                       if (privkey_file) {

> > > +                               printf("Private Key already specified\n");

> > > +                               return -1;

> > > +                       }

> > > +                       privkey_file = optarg;

> > > +                       break;

> > > +               case 'c':

> > > +                       if (cert_file) {

> > > +                               printf("Certificate file already specified\n");

> > > +                               return -1;

> > > +                       }

> > > +                       cert_file = optarg;

> > > +                       break;

> > > +               case 'm':

> > > +                       mcount = strtoul(optarg, NULL, 0);

> > > +                       break;

> > > +               case 'd':

> > > +                       dump_sig = 1;

> > > +                       break;

> > > +#endif /* CONFIG_TOOLS_LIBCRYPTO */

> > >                 case 'h':

> > >                         print_usage();

> > >                         return 0;

> > >                 }

> > >         }

> > >

> > > -       /* need an output file */

> > > -       if (argc != optind + 1) {

> > > -               print_usage();

> > > -               exit(EXIT_FAILURE);

> > > -       }

> > > -

> > > -       /* need a fit image file or raw image file */

> > > -       if (!file) {

> > > +       /* check necessary parameters */

> > > +       if ((argc != optind + 1) || !file ||

> > > +           ((privkey_file && !cert_file) ||

> > > +            (!privkey_file && cert_file))) {

> > >                 print_usage();

> > > -               exit(EXIT_SUCCESS);

> > > +               return -1;

> > >         }

> > >

> > > -       if (create_fwbin(argv[optind], file, guid, index, instance)

> > > -                       < 0) {

> > > +       if (create_fwbin(argv[optind], file, guid, index, instance,

> > > +                        mcount, privkey_file, cert_file) < 0) {

> > >                 printf("Creating firmware capsule failed\n");

> > > -               exit(EXIT_FAILURE);

> > > +               return -1;

> > >         }

> > >

> > > -       exit(EXIT_SUCCESS);

> > > +       return 0;

> > >  }

> > > --

> > > 2.33.0

> > >

> >

> >

> > --

> > Masami Hiramatsu




-- 
Masami Hiramatsu
AKASHI Takahiro Oct. 25, 2021, 6:09 a.m. UTC | #4
On Mon, Oct 25, 2021 at 02:40:11PM +0900, Masami Hiramatsu wrote:
> Hi Takahiro,

> 

> 2021年10月25日(月) 12:12 AKASHI Takahiro <takahiro.akashi@linaro.org>:

> >

> > Hi, Masami,

> >

> > On Wed, Oct 20, 2021 at 05:17:12PM +0900, Masami Hiramatsu wrote:

> > > Hello Akashi-san,

> > >

> > > Can you split this patch out from this series?

> > > It seems that the series is doing several different things. This

> > > capsule signing has no alternatives, but others are modifying or

> > > replacing the current existing feature. In other words, this capsule

> > > signing feature has no alternatives, but others we can continue to

> > > use.

> >

> > What do you specifically mean by "existing feature" or "others we can

> > continue to use"?

> >

> > All the commits, I think, in this series are closely related with

> > each other.

> 

> I meant I can use the devicetree to embed the authentication key in the

> U-Boot itself (currently U-Boot expects that.) Or is there any issue with

> putting the key in the devicetree?


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

> Without this patch, I can not sign the capsule file for U-Boot, since

> the GenerateCapsule in EDK2 supports old format (is that updated?)


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

-Takahiro Akashi

> Thank you,

> 

> 

> >

> > -Takahiro Akashi

> >

> > > Thank you,

> > >

> > > 2021年10月7日(木) 15:25 AKASHI Takahiro <takahiro.akashi@linaro.org>:

> > > >

> > > > With this enhancement, mkeficapsule will be able to sign a capsule

> > > > file when it is created. A signature added will be used later

> > > > in the verification at FMP's SetImage() call.

> > > >

> > > > To do that, We need specify additional command parameters:

> > > >   -monotonic-cout <count> : monotonic count

> > > >   -private-key <private key file> : private key file

> > > >   -certificate <certificate file> : certificate file

> > > > Only when all of those parameters are given, a signature will be added

> > > > to a capsule file.

> > > >

> > > > Users are expected to maintain and increment the monotonic count at

> > > > every time of the update for each firmware image.

> > > >

> > > > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>

> > > > ---

> > > >  tools/Kconfig        |   7 +

> > > >  tools/Makefile       |   8 +-

> > > >  tools/mkeficapsule.c | 435 +++++++++++++++++++++++++++++++++++++++----

> > > >  3 files changed, 416 insertions(+), 34 deletions(-)

> > > >

> > > > diff --git a/tools/Kconfig b/tools/Kconfig

> > > > index d6f82cd949b5..9a37ed035311 100644

> > > > --- a/tools/Kconfig

> > > > +++ b/tools/Kconfig

> > > > @@ -20,4 +20,11 @@ config TOOLS_LIBCRYPTO

> > > >           This selection does not affect target features, such as runtime FIT

> > > >           signature verification.

> > > >

> > > > +config TOOLS_MKEFICAPSULE

> > > > +       bool "Build efimkcapsule command"

> > > > +       default y if EFI_CAPSULE_ON_DISK

> > > > +       help

> > > > +         This command allows users to create a UEFI capsule file and,

> > > > +         optionally sign that file. If you want to enable UEFI capsule

> > > > +         update feature on your target, you certainly need this.

> > > >  endmenu

> > > > diff --git a/tools/Makefile b/tools/Makefile

> > > > index 4a86321f6467..6ea3033dbfb8 100644

> > > > --- a/tools/Makefile

> > > > +++ b/tools/Makefile

> > > > @@ -237,8 +237,12 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs

> > > >  hostprogs-$(CONFIG_ASN1_COMPILER)      += asn1_compiler

> > > >  HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include

> > > >

> > > > -mkeficapsule-objs      := mkeficapsule.o $(LIBFDT_OBJS)

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

> > > > +HOSTLDLIBS_mkeficapsule += -luuid

> > > > +ifeq ($(CONFIG_TOOLS_LIBCRYPTO),y)

> > > > +HOSTLDLIBS_mkeficapsule += \

> > > > +       $(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto")

> > > > +endif

> > > > +hostprogs-$(CONFIG_TOOLS_MKEFICAPSULE) += mkeficapsule

> > > >

> > > >  # We build some files with extra pedantic flags to try to minimize things

> > > >  # that won't build on some weird host compiler -- though there are lots of

> > > > diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c

> > > > index 4995ba4e0c2a..5541e4bda894 100644

> > > > --- a/tools/mkeficapsule.c

> > > > +++ b/tools/mkeficapsule.c

> > > > @@ -15,6 +15,16 @@

> > > >  #include <sys/stat.h>

> > > >  #include <sys/types.h>

> > > >

> > > > +#include <linux/kconfig.h>

> > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO

> > > > +#include <openssl/asn1.h>

> > > > +#include <openssl/bio.h>

> > > > +#include <openssl/evp.h>

> > > > +#include <openssl/err.h>

> > > > +#include <openssl/pem.h>

> > > > +#include <openssl/pkcs7.h>

> > > > +#endif

> > > > +

> > > >  typedef __u8 u8;

> > > >  typedef __u16 u16;

> > > >  typedef __u32 u32;

> > > > @@ -38,12 +48,25 @@ efi_guid_t efi_guid_image_type_uboot_fit =

> > > >                 EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID;

> > > >  efi_guid_t efi_guid_image_type_uboot_raw =

> > > >                 EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID;

> > > > +efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;

> > > > +

> > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO

> > > > +static const char *opts_short = "f:r:i:I:v:p:c:m:dh";

> > > > +#else

> > > > +static const char *opts_short = "f:r:i:I:v:h";

> > > > +#endif

> > > >

> > > >  static struct option options[] = {

> > > >         {"fit", required_argument, NULL, 'f'},

> > > >         {"raw", required_argument, NULL, 'r'},

> > > >         {"index", required_argument, NULL, 'i'},

> > > >         {"instance", required_argument, NULL, 'I'},

> > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO

> > > > +       {"private-key", required_argument, NULL, 'p'},

> > > > +       {"certificate", required_argument, NULL, 'c'},

> > > > +       {"monotonic-count", required_argument, NULL, 'm'},

> > > > +       {"dump-sig", no_argument, NULL, 'd'},

> > > > +#endif

> > > >         {"help", no_argument, NULL, 'h'},

> > > >         {NULL, 0, NULL, 0},

> > > >  };

> > > > @@ -57,16 +80,280 @@ static void print_usage(void)

> > > >                "\t-r, --raw <raw image>       new raw image file\n"

> > > >                "\t-i, --index <index>         update image index\n"

> > > >                "\t-I, --instance <instance>   update hardware instance\n"

> > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO

> > > > +              "\t-p, --private-key <privkey file>  private key file\n"

> > > > +              "\t-c, --certificate <cert file>     signer's certificate file\n"

> > > > +              "\t-m, --monotonic-count <count>     monotonic count\n"

> > > > +              "\t-d, --dump_sig              dump signature (*.p7)\n"

> > > > +#endif

> > > >                "\t-h, --help                  print a help message\n",

> > > >                tool_name);

> > > >  }

> > > >

> > > > +/**

> > > > + * auth_context - authentication context

> > > > + * @key_file:  Path to a private key file

> > > > + * @cert_file: Path to a certificate file

> > > > + * @image_data:        Pointer to firmware data

> > > > + * @image_size:        Size of firmware data

> > > > + * @auth:      Authentication header

> > > > + * @sig_data:  Signature data

> > > > + * @sig_size:  Size of signature data

> > > > + *

> > > > + * Data structure used in create_auth_data(). @key_file through

> > > > + * @image_size are input parameters. @auth, @sig_data and @sig_size

> > > > + * are filled in by create_auth_data().

> > > > + */

> > > > +struct auth_context {

> > > > +       char *key_file;

> > > > +       char *cert_file;

> > > > +       u8 *image_data;

> > > > +       size_t image_size;

> > > > +       struct efi_firmware_image_authentication auth;

> > > > +       u8 *sig_data;

> > > > +       size_t sig_size;

> > > > +};

> > > > +

> > > > +static int dump_sig;

> > > > +

> > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO

> > > > +/**

> > > > + * fileio-read_pkey - read out a private key

> > > > + * @filename:  Path to a private key file

> > > > + *

> > > > + * Read out a private key file and parse it into "EVP_PKEY" structure.

> > > > + *

> > > > + * Return:

> > > > + * * Pointer to private key structure  - on success

> > > > + * * NULL - on failure

> > > > + */

> > > > +static EVP_PKEY *fileio_read_pkey(const char *filename)

> > > > +{

> > > > +       EVP_PKEY *key = NULL;

> > > > +       BIO *bio;

> > > > +

> > > > +       bio = BIO_new_file(filename, "r");

> > > > +       if (!bio)

> > > > +               goto out;

> > > > +

> > > > +       key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);

> > > > +

> > > > +out:

> > > > +       BIO_free_all(bio);

> > > > +       if (!key) {

> > > > +               printf("Can't load key from file '%s'\n", filename);

> > > > +               ERR_print_errors_fp(stderr);

> > > > +       }

> > > > +

> > > > +       return key;

> > > > +}

> > > > +

> > > > +/**

> > > > + * fileio-read_cert - read out a certificate

> > > > + * @filename:  Path to a certificate file

> > > > + *

> > > > + * Read out a certificate file and parse it into "X509" structure.

> > > > + *

> > > > + * Return:

> > > > + * * Pointer to certificate structure  - on success

> > > > + * * NULL - on failure

> > > > + */

> > > > +static X509 *fileio_read_cert(const char *filename)

> > > > +{

> > > > +       X509 *cert = NULL;

> > > > +       BIO *bio;

> > > > +

> > > > +       bio = BIO_new_file(filename, "r");

> > > > +       if (!bio)

> > > > +               goto out;

> > > > +

> > > > +       cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);

> > > > +

> > > > +out:

> > > > +       BIO_free_all(bio);

> > > > +       if (!cert) {

> > > > +               printf("Can't load certificate from file '%s'\n", filename);

> > > > +               ERR_print_errors_fp(stderr);

> > > > +       }

> > > > +

> > > > +       return cert;

> > > > +}

> > > > +

> > > > +/**

> > > > + * create_auth_data - compose authentication data in capsule

> > > > + * @auth_context:      Pointer to authentication context

> > > > + *

> > > > + * Fill up an authentication header (.auth) and signature data (.sig_data)

> > > > + * in @auth_context, using library functions from openssl.

> > > > + * All the parameters in @auth_context must be filled in by a caller.

> > > > + *

> > > > + * Return:

> > > > + * * 0  - on success

> > > > + * * -1 - on failure

> > > > + */

> > > > +static int create_auth_data(struct auth_context *ctx)

> > > > +{

> > > > +       EVP_PKEY *key = NULL;

> > > > +       X509 *cert = NULL;

> > > > +       BIO *data_bio = NULL;

> > > > +       const EVP_MD *md;

> > > > +       PKCS7 *p7;

> > > > +       int flags, ret = -1;

> > > > +

> > > > +       OpenSSL_add_all_digests();

> > > > +       OpenSSL_add_all_ciphers();

> > > > +       ERR_load_crypto_strings();

> > > > +

> > > > +       key = fileio_read_pkey(ctx->key_file);

> > > > +       if (!key)

> > > > +               goto err;

> > > > +       cert = fileio_read_cert(ctx->cert_file);

> > > > +       if (!cert)

> > > > +               goto err;

> > > > +

> > > > +       /*

> > > > +        * create a BIO, containing:

> > > > +        *  * firmware image

> > > > +        *  * monotonic count

> > > > +        * in this order!

> > > > +        * See EDK2's FmpAuthenticatedHandlerRsa2048Sha256()

> > > > +        */

> > > > +       data_bio = BIO_new(BIO_s_mem());

> > > > +       BIO_write(data_bio, ctx->image_data, ctx->image_size);

> > > > +       BIO_write(data_bio, &ctx->auth.monotonic_count,

> > > > +                 sizeof(ctx->auth.monotonic_count));

> > > > +

> > > > +       md = EVP_get_digestbyname("SHA256");

> > > > +       if (!md)

> > > > +               goto err;

> > > > +

> > > > +       /* create signature */

> > > > +       /* TODO: maybe add PKCS7_NOATTR and PKCS7_NOSMIMECAP */

> > > > +       flags = PKCS7_BINARY | PKCS7_DETACHED;

> > > > +       p7 = PKCS7_sign(NULL, NULL, NULL, data_bio, flags | PKCS7_PARTIAL);

> > > > +       if (!p7)

> > > > +               goto err;

> > > > +       if (!PKCS7_sign_add_signer(p7, cert, key, md, flags))

> > > > +               goto err;

> > > > +       if (!PKCS7_final(p7, data_bio, flags))

> > > > +               goto err;

> > > > +

> > > > +       /* convert pkcs7 into DER */

> > > > +       ctx->sig_data = NULL;

> > > > +       ctx->sig_size = ASN1_item_i2d((ASN1_VALUE *)p7, &ctx->sig_data,

> > > > +                                     ASN1_ITEM_rptr(PKCS7));

> > > > +       if (!ctx->sig_size)

> > > > +               goto err;

> > > > +

> > > > +       /* fill auth_info */

> > > > +       ctx->auth.auth_info.hdr.dwLength = sizeof(ctx->auth.auth_info)

> > > > +                                               + ctx->sig_size;

> > > > +       ctx->auth.auth_info.hdr.wRevision = WIN_CERT_REVISION_2_0;

> > > > +       ctx->auth.auth_info.hdr.wCertificateType = WIN_CERT_TYPE_EFI_GUID;

> > > > +       memcpy(&ctx->auth.auth_info.cert_type, &efi_guid_cert_type_pkcs7,

> > > > +              sizeof(efi_guid_cert_type_pkcs7));

> > > > +

> > > > +       ret = 0;

> > > > +err:

> > > > +       BIO_free_all(data_bio);

> > > > +       EVP_PKEY_free(key);

> > > > +       X509_free(cert);

> > > > +

> > > > +       return ret;

> > > > +}

> > > > +

> > > > +/**

> > > > + * dump_signature - dump out a signature

> > > > + * @path:      Path to a capsule file

> > > > + * @signature: Signature data

> > > > + * @sig_size:  Size of signature data

> > > > + *

> > > > + * Signature data pointed to by @signature will be saved into

> > > > + * a file whose file name is @path with ".p7" suffix.

> > > > + *

> > > > + * Return:

> > > > + * * 0  - on success

> > > > + * * -1 - on failure

> > > > + */

> > > > +static int dump_signature(const char *path, u8 *signature, size_t sig_size)

> > > > +{

> > > > +       char *sig_path;

> > > > +       FILE *f;

> > > > +       size_t size;

> > > > +       int ret = -1;

> > > > +

> > > > +       sig_path = malloc(strlen(path) + 3 + 1);

> > > > +       if (!sig_path)

> > > > +               return ret;

> > > > +

> > > > +       sprintf(sig_path, "%s.p7", path);

> > > > +       f = fopen(sig_path, "w");

> > > > +       if (!f)

> > > > +               goto err;

> > > > +

> > > > +       size = fwrite(signature, 1, sig_size, f);

> > > > +       if (size == sig_size)

> > > > +               ret = 0;

> > > > +

> > > > +       fclose(f);

> > > > +err:

> > > > +       free(sig_path);

> > > > +       return ret;

> > > > +}

> > > > +

> > > > +/**

> > > > + * free_sig_data - free out signature data

> > > > + * @ctx:       Pointer to authentication context

> > > > + *

> > > > + * Free signature data allocated in create_auth_data().

> > > > + */

> > > > +static void free_sig_data(struct auth_context *ctx)

> > > > +{

> > > > +       if (ctx->sig_size)

> > > > +               OPENSSL_free(ctx->sig_data);

> > > > +}

> > > > +#else

> > > > +static int create_auth_data(struct auth_context *ctx)

> > > > +{

> > > > +       return 0;

> > > > +}

> > > > +

> > > > +static int dump_signature(const char *path, u8 *signature, size_t sig_size)

> > > > +{

> > > > +       return 0;

> > > > +}

> > > > +

> > > > +static void free_sig_data(struct auth_context *ctx) {}

> > > > +#endif

> > > > +

> > > > +/**

> > > > + * create_fwbin - create an uefi capsule file

> > > > + * @path:      Path to a created capsule file

> > > > + * @bin:       Path to a firmware binary to encapsulate

> > > > + * @guid:      GUID of related FMP driver

> > > > + * @index:     Index number in capsule

> > > > + * @instance:  Instance number in capsule

> > > > + * @mcount:    Monotonic count in authentication information

> > > > + * @private_file:      Path to a private key file

> > > > + * @cert_file: Path to a certificate file

> > > > + *

> > > > + * This function actually does the job of creating an uefi capsule file.

> > > > + * All the arguments must be supplied.

> > > > + * If either @private_file ror @cert_file is NULL, the capsule file

> > > > + * won't be signed.

> > > > + *

> > > > + * Return:

> > > > + * * 0  - on success

> > > > + * * -1 - on failure

> > > > + */

> > > >  static int create_fwbin(char *path, char *bin, efi_guid_t *guid,

> > > > -                       unsigned long index, unsigned long instance)

> > > > +                       unsigned long index, unsigned long instance,

> > > > +                       uint64_t mcount, char *privkey_file, char *cert_file)

> > > >  {

> > > >         struct efi_capsule_header header;

> > > >         struct efi_firmware_management_capsule_header capsule;

> > > >         struct efi_firmware_management_capsule_image_header image;

> > > > +       struct auth_context auth_context;

> > > >         FILE *f, *g;

> > > >         struct stat bin_stat;

> > > >         u8 *data;

> > > > @@ -76,8 +363,9 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,

> > > >  #ifdef DEBUG

> > > >         printf("For output: %s\n", path);

> > > >         printf("\tbin: %s\n\ttype: %pUl\n", bin, guid);

> > > > -       printf("\tindex: %ld\n\tinstance: %ld\n", index, instance);

> > > > +       printf("\tindex: %lu\n\tinstance: %lu\n", index, instance);

> > > >  #endif

> > > > +       auth_context.sig_size = 0;

> > > >

> > > >         g = fopen(bin, "r");

> > > >         if (!g) {

> > > > @@ -93,11 +381,34 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,

> > > >                 printf("cannot allocate memory: %zx\n", (size_t)bin_stat.st_size);

> > > >                 goto err_1;

> > > >         }

> > > > -       f = fopen(path, "w");

> > > > -       if (!f) {

> > > > -               printf("cannot open %s\n", path);

> > > > +

> > > > +       size = fread(data, 1, bin_stat.st_size, g);

> > > > +       if (size < bin_stat.st_size) {

> > > > +               printf("read failed (%zx)\n", size);

> > > >                 goto err_2;

> > > >         }

> > > > +

> > > > +       /* first, calculate signature to determine its size */

> > > > +       if (privkey_file && cert_file) {

> > > > +               auth_context.key_file = privkey_file;

> > > > +               auth_context.cert_file = cert_file;

> > > > +               auth_context.auth.monotonic_count = mcount;

> > > > +               auth_context.image_data = data;

> > > > +               auth_context.image_size = bin_stat.st_size;

> > > > +

> > > > +               if (create_auth_data(&auth_context)) {

> > > > +                       printf("Signing firmware image failed\n");

> > > > +                       goto err_3;

> > > > +               }

> > > > +

> > > > +               if (dump_sig &&

> > > > +                   dump_signature(path, auth_context.sig_data,

> > > > +                                  auth_context.sig_size)) {

> > > > +                       printf("Creating signature file failed\n");

> > > > +                       goto err_3;

> > > > +               }

> > > > +       }

> > > > +

> > > >         header.capsule_guid = efi_guid_fm_capsule;

> > > >         header.header_size = sizeof(header);

> > > >         /* TODO: The current implementation ignores flags */

> > > > @@ -106,11 +417,20 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,

> > > >                                         + sizeof(capsule) + sizeof(u64)

> > > >                                         + sizeof(image)

> > > >                                         + bin_stat.st_size;

> > > > +       if (auth_context.sig_size)

> > > > +               header.capsule_image_size += sizeof(auth_context.auth)

> > > > +                               + auth_context.sig_size;

> > > > +

> > > > +       f = fopen(path, "w");

> > > > +       if (!f) {

> > > > +               printf("cannot open %s\n", path);

> > > > +               goto err_3;

> > > > +       }

> > > >

> > > >         size = fwrite(&header, 1, sizeof(header), f);

> > > >         if (size < sizeof(header)) {

> > > >                 printf("write failed (%zx)\n", size);

> > > > -               goto err_3;

> > > > +               goto err_4;

> > > >         }

> > > >

> > > >         capsule.version = 0x00000001;

> > > > @@ -119,13 +439,13 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,

> > > >         size = fwrite(&capsule, 1, sizeof(capsule), f);

> > > >         if (size < (sizeof(capsule))) {

> > > >                 printf("write failed (%zx)\n", size);

> > > > -               goto err_3;

> > > > +               goto err_4;

> > > >         }

> > > >         offset = sizeof(capsule) + sizeof(u64);

> > > >         size = fwrite(&offset, 1, sizeof(offset), f);

> > > >         if (size < sizeof(offset)) {

> > > >                 printf("write failed (%zx)\n", size);

> > > > -               goto err_3;

> > > > +               goto err_4;

> > > >         }

> > > >

> > > >         image.version = 0x00000003;

> > > > @@ -135,34 +455,53 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,

> > > >         image.reserved[1] = 0;

> > > >         image.reserved[2] = 0;

> > > >         image.update_image_size = bin_stat.st_size;

> > > > +       if (auth_context.sig_size)

> > > > +               image.update_image_size += sizeof(auth_context.auth)

> > > > +                               + auth_context.sig_size;

> > > >         image.update_vendor_code_size = 0; /* none */

> > > >         image.update_hardware_instance = instance;

> > > >         image.image_capsule_support = 0;

> > > > +       if (auth_context.sig_size)

> > > > +               image.image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION;

> > > >

> > > >         size = fwrite(&image, 1, sizeof(image), f);

> > > >         if (size < sizeof(image)) {

> > > >                 printf("write failed (%zx)\n", size);

> > > > -               goto err_3;

> > > > +               goto err_4;

> > > >         }

> > > > -       size = fread(data, 1, bin_stat.st_size, g);

> > > > -       if (size < bin_stat.st_size) {

> > > > -               printf("read failed (%zx)\n", size);

> > > > -               goto err_3;

> > > > +

> > > > +       if (auth_context.sig_size) {

> > > > +               size = fwrite(&auth_context.auth, 1,

> > > > +                             sizeof(auth_context.auth), f);

> > > > +               if (size < sizeof(auth_context.auth)) {

> > > > +                       printf("write failed (%zx)\n", size);

> > > > +                       goto err_4;

> > > > +               }

> > > > +               size = fwrite(auth_context.sig_data, 1,

> > > > +                             auth_context.sig_size, f);

> > > > +               if (size < auth_context.sig_size) {

> > > > +                       printf("write failed (%zx)\n", size);

> > > > +                       goto err_4;

> > > > +               }

> > > >         }

> > > > +

> > > >         size = fwrite(data, 1, bin_stat.st_size, f);

> > > >         if (size < bin_stat.st_size) {

> > > >                 printf("write failed (%zx)\n", size);

> > > > -               goto err_3;

> > > > +               goto err_4;

> > > >         }

> > > >

> > > >         fclose(f);

> > > >         fclose(g);

> > > >         free(data);

> > > > +       free_sig_data(&auth_context);

> > > >

> > > >         return 0;

> > > >

> > > > -err_3:

> > > > +err_4:

> > > >         fclose(f);

> > > > +err_3:

> > > > +       free_sig_data(&auth_context);

> > > >  err_2:

> > > >         free(data);

> > > >  err_1:

> > > > @@ -171,23 +510,37 @@ err_1:

> > > >         return -1;

> > > >  }

> > > >

> > > > -/*

> > > > - * Usage:

> > > > - *   $ mkeficapsule -f <firmware binary> <output file>

> > > > +/**

> > > > + * main - main entry function of mkeficapsule

> > > > + * @argc:      Number of arguments

> > > > + * @argv:      Array of pointers to arguments

> > > > + *

> > > > + * Create an uefi capsule file, optionally signing it.

> > > > + * Parse all the arguments and pass them on to create_fwbin().

> > > > + *

> > > > + * Return:

> > > > + * * 0  - on success

> > > > + * * -1 - on failure

> > > >   */

> > > >  int main(int argc, char **argv)

> > > >  {

> > > >         char *file;

> > > >         efi_guid_t *guid;

> > > >         unsigned long index, instance;

> > > > +       uint64_t mcount;

> > > > +       char *privkey_file, *cert_file;

> > > >         int c, idx;

> > > >

> > > >         file = NULL;

> > > >         guid = NULL;

> > > >         index = 0;

> > > >         instance = 0;

> > > > +       mcount = 0;

> > > > +       privkey_file = NULL;

> > > > +       cert_file = NULL;

> > > > +       dump_sig = 0;

> > > >         for (;;) {

> > > > -               c = getopt_long(argc, argv, "f:r:i:I:v:h", options, &idx);

> > > > +               c = getopt_long(argc, argv, opts_short, options, &idx);

> > > >                 if (c == -1)

> > > >                         break;

> > > >

> > > > @@ -214,29 +567,47 @@ int main(int argc, char **argv)

> > > >                 case 'I':

> > > >                         instance = strtoul(optarg, NULL, 0);

> > > >                         break;

> > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO

> > > > +               case 'p':

> > > > +                       if (privkey_file) {

> > > > +                               printf("Private Key already specified\n");

> > > > +                               return -1;

> > > > +                       }

> > > > +                       privkey_file = optarg;

> > > > +                       break;

> > > > +               case 'c':

> > > > +                       if (cert_file) {

> > > > +                               printf("Certificate file already specified\n");

> > > > +                               return -1;

> > > > +                       }

> > > > +                       cert_file = optarg;

> > > > +                       break;

> > > > +               case 'm':

> > > > +                       mcount = strtoul(optarg, NULL, 0);

> > > > +                       break;

> > > > +               case 'd':

> > > > +                       dump_sig = 1;

> > > > +                       break;

> > > > +#endif /* CONFIG_TOOLS_LIBCRYPTO */

> > > >                 case 'h':

> > > >                         print_usage();

> > > >                         return 0;

> > > >                 }

> > > >         }

> > > >

> > > > -       /* need an output file */

> > > > -       if (argc != optind + 1) {

> > > > -               print_usage();

> > > > -               exit(EXIT_FAILURE);

> > > > -       }

> > > > -

> > > > -       /* need a fit image file or raw image file */

> > > > -       if (!file) {

> > > > +       /* check necessary parameters */

> > > > +       if ((argc != optind + 1) || !file ||

> > > > +           ((privkey_file && !cert_file) ||

> > > > +            (!privkey_file && cert_file))) {

> > > >                 print_usage();

> > > > -               exit(EXIT_SUCCESS);

> > > > +               return -1;

> > > >         }

> > > >

> > > > -       if (create_fwbin(argv[optind], file, guid, index, instance)

> > > > -                       < 0) {

> > > > +       if (create_fwbin(argv[optind], file, guid, index, instance,

> > > > +                        mcount, privkey_file, cert_file) < 0) {

> > > >                 printf("Creating firmware capsule failed\n");

> > > > -               exit(EXIT_FAILURE);

> > > > +               return -1;

> > > >         }

> > > >

> > > > -       exit(EXIT_SUCCESS);

> > > > +       return 0;

> > > >  }

> > > > --

> > > > 2.33.0

> > > >

> > >

> > >

> > > --

> > > Masami Hiramatsu

> 

> 

> 

> -- 

> Masami Hiramatsu
Masami Hiramatsu Oct. 25, 2021, 7:04 a.m. UTC | #5
Hi Takahiro,

2021年10月25日(月) 15:09 AKASHI Takahiro <takahiro.akashi@linaro.org>:
>

> On Mon, Oct 25, 2021 at 02:40:11PM +0900, Masami Hiramatsu wrote:

> > Hi Takahiro,

> >

> > 2021年10月25日(月) 12:12 AKASHI Takahiro <takahiro.akashi@linaro.org>:

> > >

> > > Hi, Masami,

> > >

> > > On Wed, Oct 20, 2021 at 05:17:12PM +0900, Masami Hiramatsu wrote:

> > > > Hello Akashi-san,

> > > >

> > > > Can you split this patch out from this series?

> > > > It seems that the series is doing several different things. This

> > > > capsule signing has no alternatives, but others are modifying or

> > > > replacing the current existing feature. In other words, this capsule

> > > > signing feature has no alternatives, but others we can continue to

> > > > use.

> > >

> > > What do you specifically mean by "existing feature" or "others we can

> > > continue to use"?

> > >

> > > All the commits, I think, in this series are closely related with

> > > each other.

> >

> > I meant I can use the devicetree to embed the authentication key in the

> > U-Boot itself (currently U-Boot expects that.) Or is there any issue with

> > putting the key in the devicetree?

>

> I don't get why this particular patch be separated from the others.

> Some are prerequisite "revert" commits, some are updates on documents,

> and some are test scriptsa All changes can and should be merged at one time.


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

Thank you,

>

> > Without this patch, I can not sign the capsule file for U-Boot, since

> > the GenerateCapsule in EDK2 supports old format (is that updated?)

>

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

> for EDK2?

>

> -Takahiro Akashi

>

> > Thank you,

> >

> >

> > >

> > > -Takahiro Akashi

> > >

> > > > Thank you,

> > > >

> > > > 2021年10月7日(木) 15:25 AKASHI Takahiro <takahiro.akashi@linaro.org>:

> > > > >

> > > > > With this enhancement, mkeficapsule will be able to sign a capsule

> > > > > file when it is created. A signature added will be used later

> > > > > in the verification at FMP's SetImage() call.

> > > > >

> > > > > To do that, We need specify additional command parameters:

> > > > >   -monotonic-cout <count> : monotonic count

> > > > >   -private-key <private key file> : private key file

> > > > >   -certificate <certificate file> : certificate file

> > > > > Only when all of those parameters are given, a signature will be added

> > > > > to a capsule file.

> > > > >

> > > > > Users are expected to maintain and increment the monotonic count at

> > > > > every time of the update for each firmware image.

> > > > >

> > > > > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>

> > > > > ---

> > > > >  tools/Kconfig        |   7 +

> > > > >  tools/Makefile       |   8 +-

> > > > >  tools/mkeficapsule.c | 435 +++++++++++++++++++++++++++++++++++++++----

> > > > >  3 files changed, 416 insertions(+), 34 deletions(-)

> > > > >

> > > > > diff --git a/tools/Kconfig b/tools/Kconfig

> > > > > index d6f82cd949b5..9a37ed035311 100644

> > > > > --- a/tools/Kconfig

> > > > > +++ b/tools/Kconfig

> > > > > @@ -20,4 +20,11 @@ config TOOLS_LIBCRYPTO

> > > > >           This selection does not affect target features, such as runtime FIT

> > > > >           signature verification.

> > > > >

> > > > > +config TOOLS_MKEFICAPSULE

> > > > > +       bool "Build efimkcapsule command"

> > > > > +       default y if EFI_CAPSULE_ON_DISK

> > > > > +       help

> > > > > +         This command allows users to create a UEFI capsule file and,

> > > > > +         optionally sign that file. If you want to enable UEFI capsule

> > > > > +         update feature on your target, you certainly need this.

> > > > >  endmenu

> > > > > diff --git a/tools/Makefile b/tools/Makefile

> > > > > index 4a86321f6467..6ea3033dbfb8 100644

> > > > > --- a/tools/Makefile

> > > > > +++ b/tools/Makefile

> > > > > @@ -237,8 +237,12 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs

> > > > >  hostprogs-$(CONFIG_ASN1_COMPILER)      += asn1_compiler

> > > > >  HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include

> > > > >

> > > > > -mkeficapsule-objs      := mkeficapsule.o $(LIBFDT_OBJS)

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

> > > > > +HOSTLDLIBS_mkeficapsule += -luuid

> > > > > +ifeq ($(CONFIG_TOOLS_LIBCRYPTO),y)

> > > > > +HOSTLDLIBS_mkeficapsule += \

> > > > > +       $(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto")

> > > > > +endif

> > > > > +hostprogs-$(CONFIG_TOOLS_MKEFICAPSULE) += mkeficapsule

> > > > >

> > > > >  # We build some files with extra pedantic flags to try to minimize things

> > > > >  # that won't build on some weird host compiler -- though there are lots of

> > > > > diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c

> > > > > index 4995ba4e0c2a..5541e4bda894 100644

> > > > > --- a/tools/mkeficapsule.c

> > > > > +++ b/tools/mkeficapsule.c

> > > > > @@ -15,6 +15,16 @@

> > > > >  #include <sys/stat.h>

> > > > >  #include <sys/types.h>

> > > > >

> > > > > +#include <linux/kconfig.h>

> > > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO

> > > > > +#include <openssl/asn1.h>

> > > > > +#include <openssl/bio.h>

> > > > > +#include <openssl/evp.h>

> > > > > +#include <openssl/err.h>

> > > > > +#include <openssl/pem.h>

> > > > > +#include <openssl/pkcs7.h>

> > > > > +#endif

> > > > > +

> > > > >  typedef __u8 u8;

> > > > >  typedef __u16 u16;

> > > > >  typedef __u32 u32;

> > > > > @@ -38,12 +48,25 @@ efi_guid_t efi_guid_image_type_uboot_fit =

> > > > >                 EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID;

> > > > >  efi_guid_t efi_guid_image_type_uboot_raw =

> > > > >                 EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID;

> > > > > +efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;

> > > > > +

> > > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO

> > > > > +static const char *opts_short = "f:r:i:I:v:p:c:m:dh";

> > > > > +#else

> > > > > +static const char *opts_short = "f:r:i:I:v:h";

> > > > > +#endif

> > > > >

> > > > >  static struct option options[] = {

> > > > >         {"fit", required_argument, NULL, 'f'},

> > > > >         {"raw", required_argument, NULL, 'r'},

> > > > >         {"index", required_argument, NULL, 'i'},

> > > > >         {"instance", required_argument, NULL, 'I'},

> > > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO

> > > > > +       {"private-key", required_argument, NULL, 'p'},

> > > > > +       {"certificate", required_argument, NULL, 'c'},

> > > > > +       {"monotonic-count", required_argument, NULL, 'm'},

> > > > > +       {"dump-sig", no_argument, NULL, 'd'},

> > > > > +#endif

> > > > >         {"help", no_argument, NULL, 'h'},

> > > > >         {NULL, 0, NULL, 0},

> > > > >  };

> > > > > @@ -57,16 +80,280 @@ static void print_usage(void)

> > > > >                "\t-r, --raw <raw image>       new raw image file\n"

> > > > >                "\t-i, --index <index>         update image index\n"

> > > > >                "\t-I, --instance <instance>   update hardware instance\n"

> > > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO

> > > > > +              "\t-p, --private-key <privkey file>  private key file\n"

> > > > > +              "\t-c, --certificate <cert file>     signer's certificate file\n"

> > > > > +              "\t-m, --monotonic-count <count>     monotonic count\n"

> > > > > +              "\t-d, --dump_sig              dump signature (*.p7)\n"

> > > > > +#endif

> > > > >                "\t-h, --help                  print a help message\n",

> > > > >                tool_name);

> > > > >  }

> > > > >

> > > > > +/**

> > > > > + * auth_context - authentication context

> > > > > + * @key_file:  Path to a private key file

> > > > > + * @cert_file: Path to a certificate file

> > > > > + * @image_data:        Pointer to firmware data

> > > > > + * @image_size:        Size of firmware data

> > > > > + * @auth:      Authentication header

> > > > > + * @sig_data:  Signature data

> > > > > + * @sig_size:  Size of signature data

> > > > > + *

> > > > > + * Data structure used in create_auth_data(). @key_file through

> > > > > + * @image_size are input parameters. @auth, @sig_data and @sig_size

> > > > > + * are filled in by create_auth_data().

> > > > > + */

> > > > > +struct auth_context {

> > > > > +       char *key_file;

> > > > > +       char *cert_file;

> > > > > +       u8 *image_data;

> > > > > +       size_t image_size;

> > > > > +       struct efi_firmware_image_authentication auth;

> > > > > +       u8 *sig_data;

> > > > > +       size_t sig_size;

> > > > > +};

> > > > > +

> > > > > +static int dump_sig;

> > > > > +

> > > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO

> > > > > +/**

> > > > > + * fileio-read_pkey - read out a private key

> > > > > + * @filename:  Path to a private key file

> > > > > + *

> > > > > + * Read out a private key file and parse it into "EVP_PKEY" structure.

> > > > > + *

> > > > > + * Return:

> > > > > + * * Pointer to private key structure  - on success

> > > > > + * * NULL - on failure

> > > > > + */

> > > > > +static EVP_PKEY *fileio_read_pkey(const char *filename)

> > > > > +{

> > > > > +       EVP_PKEY *key = NULL;

> > > > > +       BIO *bio;

> > > > > +

> > > > > +       bio = BIO_new_file(filename, "r");

> > > > > +       if (!bio)

> > > > > +               goto out;

> > > > > +

> > > > > +       key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);

> > > > > +

> > > > > +out:

> > > > > +       BIO_free_all(bio);

> > > > > +       if (!key) {

> > > > > +               printf("Can't load key from file '%s'\n", filename);

> > > > > +               ERR_print_errors_fp(stderr);

> > > > > +       }

> > > > > +

> > > > > +       return key;

> > > > > +}

> > > > > +

> > > > > +/**

> > > > > + * fileio-read_cert - read out a certificate

> > > > > + * @filename:  Path to a certificate file

> > > > > + *

> > > > > + * Read out a certificate file and parse it into "X509" structure.

> > > > > + *

> > > > > + * Return:

> > > > > + * * Pointer to certificate structure  - on success

> > > > > + * * NULL - on failure

> > > > > + */

> > > > > +static X509 *fileio_read_cert(const char *filename)

> > > > > +{

> > > > > +       X509 *cert = NULL;

> > > > > +       BIO *bio;

> > > > > +

> > > > > +       bio = BIO_new_file(filename, "r");

> > > > > +       if (!bio)

> > > > > +               goto out;

> > > > > +

> > > > > +       cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);

> > > > > +

> > > > > +out:

> > > > > +       BIO_free_all(bio);

> > > > > +       if (!cert) {

> > > > > +               printf("Can't load certificate from file '%s'\n", filename);

> > > > > +               ERR_print_errors_fp(stderr);

> > > > > +       }

> > > > > +

> > > > > +       return cert;

> > > > > +}

> > > > > +

> > > > > +/**

> > > > > + * create_auth_data - compose authentication data in capsule

> > > > > + * @auth_context:      Pointer to authentication context

> > > > > + *

> > > > > + * Fill up an authentication header (.auth) and signature data (.sig_data)

> > > > > + * in @auth_context, using library functions from openssl.

> > > > > + * All the parameters in @auth_context must be filled in by a caller.

> > > > > + *

> > > > > + * Return:

> > > > > + * * 0  - on success

> > > > > + * * -1 - on failure

> > > > > + */

> > > > > +static int create_auth_data(struct auth_context *ctx)

> > > > > +{

> > > > > +       EVP_PKEY *key = NULL;

> > > > > +       X509 *cert = NULL;

> > > > > +       BIO *data_bio = NULL;

> > > > > +       const EVP_MD *md;

> > > > > +       PKCS7 *p7;

> > > > > +       int flags, ret = -1;

> > > > > +

> > > > > +       OpenSSL_add_all_digests();

> > > > > +       OpenSSL_add_all_ciphers();

> > > > > +       ERR_load_crypto_strings();

> > > > > +

> > > > > +       key = fileio_read_pkey(ctx->key_file);

> > > > > +       if (!key)

> > > > > +               goto err;

> > > > > +       cert = fileio_read_cert(ctx->cert_file);

> > > > > +       if (!cert)

> > > > > +               goto err;

> > > > > +

> > > > > +       /*

> > > > > +        * create a BIO, containing:

> > > > > +        *  * firmware image

> > > > > +        *  * monotonic count

> > > > > +        * in this order!

> > > > > +        * See EDK2's FmpAuthenticatedHandlerRsa2048Sha256()

> > > > > +        */

> > > > > +       data_bio = BIO_new(BIO_s_mem());

> > > > > +       BIO_write(data_bio, ctx->image_data, ctx->image_size);

> > > > > +       BIO_write(data_bio, &ctx->auth.monotonic_count,

> > > > > +                 sizeof(ctx->auth.monotonic_count));

> > > > > +

> > > > > +       md = EVP_get_digestbyname("SHA256");

> > > > > +       if (!md)

> > > > > +               goto err;

> > > > > +

> > > > > +       /* create signature */

> > > > > +       /* TODO: maybe add PKCS7_NOATTR and PKCS7_NOSMIMECAP */

> > > > > +       flags = PKCS7_BINARY | PKCS7_DETACHED;

> > > > > +       p7 = PKCS7_sign(NULL, NULL, NULL, data_bio, flags | PKCS7_PARTIAL);

> > > > > +       if (!p7)

> > > > > +               goto err;

> > > > > +       if (!PKCS7_sign_add_signer(p7, cert, key, md, flags))

> > > > > +               goto err;

> > > > > +       if (!PKCS7_final(p7, data_bio, flags))

> > > > > +               goto err;

> > > > > +

> > > > > +       /* convert pkcs7 into DER */

> > > > > +       ctx->sig_data = NULL;

> > > > > +       ctx->sig_size = ASN1_item_i2d((ASN1_VALUE *)p7, &ctx->sig_data,

> > > > > +                                     ASN1_ITEM_rptr(PKCS7));

> > > > > +       if (!ctx->sig_size)

> > > > > +               goto err;

> > > > > +

> > > > > +       /* fill auth_info */

> > > > > +       ctx->auth.auth_info.hdr.dwLength = sizeof(ctx->auth.auth_info)

> > > > > +                                               + ctx->sig_size;

> > > > > +       ctx->auth.auth_info.hdr.wRevision = WIN_CERT_REVISION_2_0;

> > > > > +       ctx->auth.auth_info.hdr.wCertificateType = WIN_CERT_TYPE_EFI_GUID;

> > > > > +       memcpy(&ctx->auth.auth_info.cert_type, &efi_guid_cert_type_pkcs7,

> > > > > +              sizeof(efi_guid_cert_type_pkcs7));

> > > > > +

> > > > > +       ret = 0;

> > > > > +err:

> > > > > +       BIO_free_all(data_bio);

> > > > > +       EVP_PKEY_free(key);

> > > > > +       X509_free(cert);

> > > > > +

> > > > > +       return ret;

> > > > > +}

> > > > > +

> > > > > +/**

> > > > > + * dump_signature - dump out a signature

> > > > > + * @path:      Path to a capsule file

> > > > > + * @signature: Signature data

> > > > > + * @sig_size:  Size of signature data

> > > > > + *

> > > > > + * Signature data pointed to by @signature will be saved into

> > > > > + * a file whose file name is @path with ".p7" suffix.

> > > > > + *

> > > > > + * Return:

> > > > > + * * 0  - on success

> > > > > + * * -1 - on failure

> > > > > + */

> > > > > +static int dump_signature(const char *path, u8 *signature, size_t sig_size)

> > > > > +{

> > > > > +       char *sig_path;

> > > > > +       FILE *f;

> > > > > +       size_t size;

> > > > > +       int ret = -1;

> > > > > +

> > > > > +       sig_path = malloc(strlen(path) + 3 + 1);

> > > > > +       if (!sig_path)

> > > > > +               return ret;

> > > > > +

> > > > > +       sprintf(sig_path, "%s.p7", path);

> > > > > +       f = fopen(sig_path, "w");

> > > > > +       if (!f)

> > > > > +               goto err;

> > > > > +

> > > > > +       size = fwrite(signature, 1, sig_size, f);

> > > > > +       if (size == sig_size)

> > > > > +               ret = 0;

> > > > > +

> > > > > +       fclose(f);

> > > > > +err:

> > > > > +       free(sig_path);

> > > > > +       return ret;

> > > > > +}

> > > > > +

> > > > > +/**

> > > > > + * free_sig_data - free out signature data

> > > > > + * @ctx:       Pointer to authentication context

> > > > > + *

> > > > > + * Free signature data allocated in create_auth_data().

> > > > > + */

> > > > > +static void free_sig_data(struct auth_context *ctx)

> > > > > +{

> > > > > +       if (ctx->sig_size)

> > > > > +               OPENSSL_free(ctx->sig_data);

> > > > > +}

> > > > > +#else

> > > > > +static int create_auth_data(struct auth_context *ctx)

> > > > > +{

> > > > > +       return 0;

> > > > > +}

> > > > > +

> > > > > +static int dump_signature(const char *path, u8 *signature, size_t sig_size)

> > > > > +{

> > > > > +       return 0;

> > > > > +}

> > > > > +

> > > > > +static void free_sig_data(struct auth_context *ctx) {}

> > > > > +#endif

> > > > > +

> > > > > +/**

> > > > > + * create_fwbin - create an uefi capsule file

> > > > > + * @path:      Path to a created capsule file

> > > > > + * @bin:       Path to a firmware binary to encapsulate

> > > > > + * @guid:      GUID of related FMP driver

> > > > > + * @index:     Index number in capsule

> > > > > + * @instance:  Instance number in capsule

> > > > > + * @mcount:    Monotonic count in authentication information

> > > > > + * @private_file:      Path to a private key file

> > > > > + * @cert_file: Path to a certificate file

> > > > > + *

> > > > > + * This function actually does the job of creating an uefi capsule file.

> > > > > + * All the arguments must be supplied.

> > > > > + * If either @private_file ror @cert_file is NULL, the capsule file

> > > > > + * won't be signed.

> > > > > + *

> > > > > + * Return:

> > > > > + * * 0  - on success

> > > > > + * * -1 - on failure

> > > > > + */

> > > > >  static int create_fwbin(char *path, char *bin, efi_guid_t *guid,

> > > > > -                       unsigned long index, unsigned long instance)

> > > > > +                       unsigned long index, unsigned long instance,

> > > > > +                       uint64_t mcount, char *privkey_file, char *cert_file)

> > > > >  {

> > > > >         struct efi_capsule_header header;

> > > > >         struct efi_firmware_management_capsule_header capsule;

> > > > >         struct efi_firmware_management_capsule_image_header image;

> > > > > +       struct auth_context auth_context;

> > > > >         FILE *f, *g;

> > > > >         struct stat bin_stat;

> > > > >         u8 *data;

> > > > > @@ -76,8 +363,9 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,

> > > > >  #ifdef DEBUG

> > > > >         printf("For output: %s\n", path);

> > > > >         printf("\tbin: %s\n\ttype: %pUl\n", bin, guid);

> > > > > -       printf("\tindex: %ld\n\tinstance: %ld\n", index, instance);

> > > > > +       printf("\tindex: %lu\n\tinstance: %lu\n", index, instance);

> > > > >  #endif

> > > > > +       auth_context.sig_size = 0;

> > > > >

> > > > >         g = fopen(bin, "r");

> > > > >         if (!g) {

> > > > > @@ -93,11 +381,34 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,

> > > > >                 printf("cannot allocate memory: %zx\n", (size_t)bin_stat.st_size);

> > > > >                 goto err_1;

> > > > >         }

> > > > > -       f = fopen(path, "w");

> > > > > -       if (!f) {

> > > > > -               printf("cannot open %s\n", path);

> > > > > +

> > > > > +       size = fread(data, 1, bin_stat.st_size, g);

> > > > > +       if (size < bin_stat.st_size) {

> > > > > +               printf("read failed (%zx)\n", size);

> > > > >                 goto err_2;

> > > > >         }

> > > > > +

> > > > > +       /* first, calculate signature to determine its size */

> > > > > +       if (privkey_file && cert_file) {

> > > > > +               auth_context.key_file = privkey_file;

> > > > > +               auth_context.cert_file = cert_file;

> > > > > +               auth_context.auth.monotonic_count = mcount;

> > > > > +               auth_context.image_data = data;

> > > > > +               auth_context.image_size = bin_stat.st_size;

> > > > > +

> > > > > +               if (create_auth_data(&auth_context)) {

> > > > > +                       printf("Signing firmware image failed\n");

> > > > > +                       goto err_3;

> > > > > +               }

> > > > > +

> > > > > +               if (dump_sig &&

> > > > > +                   dump_signature(path, auth_context.sig_data,

> > > > > +                                  auth_context.sig_size)) {

> > > > > +                       printf("Creating signature file failed\n");

> > > > > +                       goto err_3;

> > > > > +               }

> > > > > +       }

> > > > > +

> > > > >         header.capsule_guid = efi_guid_fm_capsule;

> > > > >         header.header_size = sizeof(header);

> > > > >         /* TODO: The current implementation ignores flags */

> > > > > @@ -106,11 +417,20 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,

> > > > >                                         + sizeof(capsule) + sizeof(u64)

> > > > >                                         + sizeof(image)

> > > > >                                         + bin_stat.st_size;

> > > > > +       if (auth_context.sig_size)

> > > > > +               header.capsule_image_size += sizeof(auth_context.auth)

> > > > > +                               + auth_context.sig_size;

> > > > > +

> > > > > +       f = fopen(path, "w");

> > > > > +       if (!f) {

> > > > > +               printf("cannot open %s\n", path);

> > > > > +               goto err_3;

> > > > > +       }

> > > > >

> > > > >         size = fwrite(&header, 1, sizeof(header), f);

> > > > >         if (size < sizeof(header)) {

> > > > >                 printf("write failed (%zx)\n", size);

> > > > > -               goto err_3;

> > > > > +               goto err_4;

> > > > >         }

> > > > >

> > > > >         capsule.version = 0x00000001;

> > > > > @@ -119,13 +439,13 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,

> > > > >         size = fwrite(&capsule, 1, sizeof(capsule), f);

> > > > >         if (size < (sizeof(capsule))) {

> > > > >                 printf("write failed (%zx)\n", size);

> > > > > -               goto err_3;

> > > > > +               goto err_4;

> > > > >         }

> > > > >         offset = sizeof(capsule) + sizeof(u64);

> > > > >         size = fwrite(&offset, 1, sizeof(offset), f);

> > > > >         if (size < sizeof(offset)) {

> > > > >                 printf("write failed (%zx)\n", size);

> > > > > -               goto err_3;

> > > > > +               goto err_4;

> > > > >         }

> > > > >

> > > > >         image.version = 0x00000003;

> > > > > @@ -135,34 +455,53 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,

> > > > >         image.reserved[1] = 0;

> > > > >         image.reserved[2] = 0;

> > > > >         image.update_image_size = bin_stat.st_size;

> > > > > +       if (auth_context.sig_size)

> > > > > +               image.update_image_size += sizeof(auth_context.auth)

> > > > > +                               + auth_context.sig_size;

> > > > >         image.update_vendor_code_size = 0; /* none */

> > > > >         image.update_hardware_instance = instance;

> > > > >         image.image_capsule_support = 0;

> > > > > +       if (auth_context.sig_size)

> > > > > +               image.image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION;

> > > > >

> > > > >         size = fwrite(&image, 1, sizeof(image), f);

> > > > >         if (size < sizeof(image)) {

> > > > >                 printf("write failed (%zx)\n", size);

> > > > > -               goto err_3;

> > > > > +               goto err_4;

> > > > >         }

> > > > > -       size = fread(data, 1, bin_stat.st_size, g);

> > > > > -       if (size < bin_stat.st_size) {

> > > > > -               printf("read failed (%zx)\n", size);

> > > > > -               goto err_3;

> > > > > +

> > > > > +       if (auth_context.sig_size) {

> > > > > +               size = fwrite(&auth_context.auth, 1,

> > > > > +                             sizeof(auth_context.auth), f);

> > > > > +               if (size < sizeof(auth_context.auth)) {

> > > > > +                       printf("write failed (%zx)\n", size);

> > > > > +                       goto err_4;

> > > > > +               }

> > > > > +               size = fwrite(auth_context.sig_data, 1,

> > > > > +                             auth_context.sig_size, f);

> > > > > +               if (size < auth_context.sig_size) {

> > > > > +                       printf("write failed (%zx)\n", size);

> > > > > +                       goto err_4;

> > > > > +               }

> > > > >         }

> > > > > +

> > > > >         size = fwrite(data, 1, bin_stat.st_size, f);

> > > > >         if (size < bin_stat.st_size) {

> > > > >                 printf("write failed (%zx)\n", size);

> > > > > -               goto err_3;

> > > > > +               goto err_4;

> > > > >         }

> > > > >

> > > > >         fclose(f);

> > > > >         fclose(g);

> > > > >         free(data);

> > > > > +       free_sig_data(&auth_context);

> > > > >

> > > > >         return 0;

> > > > >

> > > > > -err_3:

> > > > > +err_4:

> > > > >         fclose(f);

> > > > > +err_3:

> > > > > +       free_sig_data(&auth_context);

> > > > >  err_2:

> > > > >         free(data);

> > > > >  err_1:

> > > > > @@ -171,23 +510,37 @@ err_1:

> > > > >         return -1;

> > > > >  }

> > > > >

> > > > > -/*

> > > > > - * Usage:

> > > > > - *   $ mkeficapsule -f <firmware binary> <output file>

> > > > > +/**

> > > > > + * main - main entry function of mkeficapsule

> > > > > + * @argc:      Number of arguments

> > > > > + * @argv:      Array of pointers to arguments

> > > > > + *

> > > > > + * Create an uefi capsule file, optionally signing it.

> > > > > + * Parse all the arguments and pass them on to create_fwbin().

> > > > > + *

> > > > > + * Return:

> > > > > + * * 0  - on success

> > > > > + * * -1 - on failure

> > > > >   */

> > > > >  int main(int argc, char **argv)

> > > > >  {

> > > > >         char *file;

> > > > >         efi_guid_t *guid;

> > > > >         unsigned long index, instance;

> > > > > +       uint64_t mcount;

> > > > > +       char *privkey_file, *cert_file;

> > > > >         int c, idx;

> > > > >

> > > > >         file = NULL;

> > > > >         guid = NULL;

> > > > >         index = 0;

> > > > >         instance = 0;

> > > > > +       mcount = 0;

> > > > > +       privkey_file = NULL;

> > > > > +       cert_file = NULL;

> > > > > +       dump_sig = 0;

> > > > >         for (;;) {

> > > > > -               c = getopt_long(argc, argv, "f:r:i:I:v:h", options, &idx);

> > > > > +               c = getopt_long(argc, argv, opts_short, options, &idx);

> > > > >                 if (c == -1)

> > > > >                         break;

> > > > >

> > > > > @@ -214,29 +567,47 @@ int main(int argc, char **argv)

> > > > >                 case 'I':

> > > > >                         instance = strtoul(optarg, NULL, 0);

> > > > >                         break;

> > > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO

> > > > > +               case 'p':

> > > > > +                       if (privkey_file) {

> > > > > +                               printf("Private Key already specified\n");

> > > > > +                               return -1;

> > > > > +                       }

> > > > > +                       privkey_file = optarg;

> > > > > +                       break;

> > > > > +               case 'c':

> > > > > +                       if (cert_file) {

> > > > > +                               printf("Certificate file already specified\n");

> > > > > +                               return -1;

> > > > > +                       }

> > > > > +                       cert_file = optarg;

> > > > > +                       break;

> > > > > +               case 'm':

> > > > > +                       mcount = strtoul(optarg, NULL, 0);

> > > > > +                       break;

> > > > > +               case 'd':

> > > > > +                       dump_sig = 1;

> > > > > +                       break;

> > > > > +#endif /* CONFIG_TOOLS_LIBCRYPTO */

> > > > >                 case 'h':

> > > > >                         print_usage();

> > > > >                         return 0;

> > > > >                 }

> > > > >         }

> > > > >

> > > > > -       /* need an output file */

> > > > > -       if (argc != optind + 1) {

> > > > > -               print_usage();

> > > > > -               exit(EXIT_FAILURE);

> > > > > -       }

> > > > > -

> > > > > -       /* need a fit image file or raw image file */

> > > > > -       if (!file) {

> > > > > +       /* check necessary parameters */

> > > > > +       if ((argc != optind + 1) || !file ||

> > > > > +           ((privkey_file && !cert_file) ||

> > > > > +            (!privkey_file && cert_file))) {

> > > > >                 print_usage();

> > > > > -               exit(EXIT_SUCCESS);

> > > > > +               return -1;

> > > > >         }

> > > > >

> > > > > -       if (create_fwbin(argv[optind], file, guid, index, instance)

> > > > > -                       < 0) {

> > > > > +       if (create_fwbin(argv[optind], file, guid, index, instance,

> > > > > +                        mcount, privkey_file, cert_file) < 0) {

> > > > >                 printf("Creating firmware capsule failed\n");

> > > > > -               exit(EXIT_FAILURE);

> > > > > +               return -1;

> > > > >         }

> > > > >

> > > > > -       exit(EXIT_SUCCESS);

> > > > > +       return 0;

> > > > >  }

> > > > > --

> > > > > 2.33.0

> > > > >

> > > >

> > > >

> > > > --

> > > > Masami Hiramatsu

> >

> >

> >

> > --

> > Masami Hiramatsu




--
Masami Hiramatsu
Sughosh Ganu Oct. 25, 2021, 9:58 a.m. UTC | #6
hi Masami,

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

> Hi Takahiro,

>

> 2021年10月25日(月) 12:12 AKASHI Takahiro <takahiro.akashi@linaro.org>:

> >

> > Hi, Masami,

> >

> > On Wed, Oct 20, 2021 at 05:17:12PM +0900, Masami Hiramatsu wrote:

> > > Hello Akashi-san,

> > >

> > > Can you split this patch out from this series?

> > > It seems that the series is doing several different things. This

> > > capsule signing has no alternatives, but others are modifying or

> > > replacing the current existing feature. In other words, this capsule

> > > signing feature has no alternatives, but others we can continue to

> > > use.

> >

> > What do you specifically mean by "existing feature" or "others we can

> > continue to use"?

> >

> > All the commits, I think, in this series are closely related with

> > each other.

>

> I meant I can use the devicetree to embed the authentication key in the

> U-Boot itself (currently U-Boot expects that.) Or is there any issue with

> putting the key in the devicetree?

>

> Without this patch, I can not sign the capsule file for U-Boot, since

> the GenerateCapsule in EDK2 supports old format (is that updated?)

>


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

-sughosh


>

> Thank you,

>

>

> >

> > -Takahiro Akashi

> >

> > > Thank you,

> > >

> > > 2021年10月7日(木) 15:25 AKASHI Takahiro <takahiro.akashi@linaro.org>:

> > > >

> > > > With this enhancement, mkeficapsule will be able to sign a capsule

> > > > file when it is created. A signature added will be used later

> > > > in the verification at FMP's SetImage() call.

> > > >

> > > > To do that, We need specify additional command parameters:

> > > >   -monotonic-cout <count> : monotonic count

> > > >   -private-key <private key file> : private key file

> > > >   -certificate <certificate file> : certificate file

> > > > Only when all of those parameters are given, a signature will be

> added

> > > > to a capsule file.

> > > >

> > > > Users are expected to maintain and increment the monotonic count at

> > > > every time of the update for each firmware image.

> > > >

> > > > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>

> > > > ---

> > > >  tools/Kconfig        |   7 +

> > > >  tools/Makefile       |   8 +-

> > > >  tools/mkeficapsule.c | 435

> +++++++++++++++++++++++++++++++++++++++----

> > > >  3 files changed, 416 insertions(+), 34 deletions(-)

> > > >

> > > > diff --git a/tools/Kconfig b/tools/Kconfig

> > > > index d6f82cd949b5..9a37ed035311 100644

> > > > --- a/tools/Kconfig

> > > > +++ b/tools/Kconfig

> > > > @@ -20,4 +20,11 @@ config TOOLS_LIBCRYPTO

> > > >           This selection does not affect target features, such as

> runtime FIT

> > > >           signature verification.

> > > >

> > > > +config TOOLS_MKEFICAPSULE

> > > > +       bool "Build efimkcapsule command"

> > > > +       default y if EFI_CAPSULE_ON_DISK

> > > > +       help

> > > > +         This command allows users to create a UEFI capsule file

> and,

> > > > +         optionally sign that file. If you want to enable UEFI

> capsule

> > > > +         update feature on your target, you certainly need this.

> > > >  endmenu

> > > > diff --git a/tools/Makefile b/tools/Makefile

> > > > index 4a86321f6467..6ea3033dbfb8 100644

> > > > --- a/tools/Makefile

> > > > +++ b/tools/Makefile

> > > > @@ -237,8 +237,12 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs

> > > >  hostprogs-$(CONFIG_ASN1_COMPILER)      += asn1_compiler

> > > >  HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include

> > > >

> > > > -mkeficapsule-objs      := mkeficapsule.o $(LIBFDT_OBJS)

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

> > > > +HOSTLDLIBS_mkeficapsule += -luuid

> > > > +ifeq ($(CONFIG_TOOLS_LIBCRYPTO),y)

> > > > +HOSTLDLIBS_mkeficapsule += \

> > > > +       $(shell pkg-config --libs libssl libcrypto 2> /dev/null ||

> echo "-lssl -lcrypto")

> > > > +endif

> > > > +hostprogs-$(CONFIG_TOOLS_MKEFICAPSULE) += mkeficapsule

> > > >

> > > >  # We build some files with extra pedantic flags to try to minimize

> things

> > > >  # that won't build on some weird host compiler -- though there are

> lots of

> > > > diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c

> > > > index 4995ba4e0c2a..5541e4bda894 100644

> > > > --- a/tools/mkeficapsule.c

> > > > +++ b/tools/mkeficapsule.c

> > > > @@ -15,6 +15,16 @@

> > > >  #include <sys/stat.h>

> > > >  #include <sys/types.h>

> > > >

> > > > +#include <linux/kconfig.h>

> > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO

> > > > +#include <openssl/asn1.h>

> > > > +#include <openssl/bio.h>

> > > > +#include <openssl/evp.h>

> > > > +#include <openssl/err.h>

> > > > +#include <openssl/pem.h>

> > > > +#include <openssl/pkcs7.h>

> > > > +#endif

> > > > +

> > > >  typedef __u8 u8;

> > > >  typedef __u16 u16;

> > > >  typedef __u32 u32;

> > > > @@ -38,12 +48,25 @@ efi_guid_t efi_guid_image_type_uboot_fit =

> > > >                 EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID;

> > > >  efi_guid_t efi_guid_image_type_uboot_raw =

> > > >                 EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID;

> > > > +efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;

> > > > +

> > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO

> > > > +static const char *opts_short = "f:r:i:I:v:p:c:m:dh";

> > > > +#else

> > > > +static const char *opts_short = "f:r:i:I:v:h";

> > > > +#endif

> > > >

> > > >  static struct option options[] = {

> > > >         {"fit", required_argument, NULL, 'f'},

> > > >         {"raw", required_argument, NULL, 'r'},

> > > >         {"index", required_argument, NULL, 'i'},

> > > >         {"instance", required_argument, NULL, 'I'},

> > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO

> > > > +       {"private-key", required_argument, NULL, 'p'},

> > > > +       {"certificate", required_argument, NULL, 'c'},

> > > > +       {"monotonic-count", required_argument, NULL, 'm'},

> > > > +       {"dump-sig", no_argument, NULL, 'd'},

> > > > +#endif

> > > >         {"help", no_argument, NULL, 'h'},

> > > >         {NULL, 0, NULL, 0},

> > > >  };

> > > > @@ -57,16 +80,280 @@ static void print_usage(void)

> > > >                "\t-r, --raw <raw image>       new raw image file\n"

> > > >                "\t-i, --index <index>         update image index\n"

> > > >                "\t-I, --instance <instance>   update hardware

> instance\n"

> > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO

> > > > +              "\t-p, --private-key <privkey file>  private key

> file\n"

> > > > +              "\t-c, --certificate <cert file>     signer's

> certificate file\n"

> > > > +              "\t-m, --monotonic-count <count>     monotonic

> count\n"

> > > > +              "\t-d, --dump_sig              dump signature

> (*.p7)\n"

> > > > +#endif

> > > >                "\t-h, --help                  print a help

> message\n",

> > > >                tool_name);

> > > >  }

> > > >

> > > > +/**

> > > > + * auth_context - authentication context

> > > > + * @key_file:  Path to a private key file

> > > > + * @cert_file: Path to a certificate file

> > > > + * @image_data:        Pointer to firmware data

> > > > + * @image_size:        Size of firmware data

> > > > + * @auth:      Authentication header

> > > > + * @sig_data:  Signature data

> > > > + * @sig_size:  Size of signature data

> > > > + *

> > > > + * Data structure used in create_auth_data(). @key_file through

> > > > + * @image_size are input parameters. @auth, @sig_data and @sig_size

> > > > + * are filled in by create_auth_data().

> > > > + */

> > > > +struct auth_context {

> > > > +       char *key_file;

> > > > +       char *cert_file;

> > > > +       u8 *image_data;

> > > > +       size_t image_size;

> > > > +       struct efi_firmware_image_authentication auth;

> > > > +       u8 *sig_data;

> > > > +       size_t sig_size;

> > > > +};

> > > > +

> > > > +static int dump_sig;

> > > > +

> > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO

> > > > +/**

> > > > + * fileio-read_pkey - read out a private key

> > > > + * @filename:  Path to a private key file

> > > > + *

> > > > + * Read out a private key file and parse it into "EVP_PKEY"

> structure.

> > > > + *

> > > > + * Return:

> > > > + * * Pointer to private key structure  - on success

> > > > + * * NULL - on failure

> > > > + */

> > > > +static EVP_PKEY *fileio_read_pkey(const char *filename)

> > > > +{

> > > > +       EVP_PKEY *key = NULL;

> > > > +       BIO *bio;

> > > > +

> > > > +       bio = BIO_new_file(filename, "r");

> > > > +       if (!bio)

> > > > +               goto out;

> > > > +

> > > > +       key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);

> > > > +

> > > > +out:

> > > > +       BIO_free_all(bio);

> > > > +       if (!key) {

> > > > +               printf("Can't load key from file '%s'\n", filename);

> > > > +               ERR_print_errors_fp(stderr);

> > > > +       }

> > > > +

> > > > +       return key;

> > > > +}

> > > > +

> > > > +/**

> > > > + * fileio-read_cert - read out a certificate

> > > > + * @filename:  Path to a certificate file

> > > > + *

> > > > + * Read out a certificate file and parse it into "X509" structure.

> > > > + *

> > > > + * Return:

> > > > + * * Pointer to certificate structure  - on success

> > > > + * * NULL - on failure

> > > > + */

> > > > +static X509 *fileio_read_cert(const char *filename)

> > > > +{

> > > > +       X509 *cert = NULL;

> > > > +       BIO *bio;

> > > > +

> > > > +       bio = BIO_new_file(filename, "r");

> > > > +       if (!bio)

> > > > +               goto out;

> > > > +

> > > > +       cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);

> > > > +

> > > > +out:

> > > > +       BIO_free_all(bio);

> > > > +       if (!cert) {

> > > > +               printf("Can't load certificate from file '%s'\n",

> filename);

> > > > +               ERR_print_errors_fp(stderr);

> > > > +       }

> > > > +

> > > > +       return cert;

> > > > +}

> > > > +

> > > > +/**

> > > > + * create_auth_data - compose authentication data in capsule

> > > > + * @auth_context:      Pointer to authentication context

> > > > + *

> > > > + * Fill up an authentication header (.auth) and signature data

> (.sig_data)

> > > > + * in @auth_context, using library functions from openssl.

> > > > + * All the parameters in @auth_context must be filled in by a

> caller.

> > > > + *

> > > > + * Return:

> > > > + * * 0  - on success

> > > > + * * -1 - on failure

> > > > + */

> > > > +static int create_auth_data(struct auth_context *ctx)

> > > > +{

> > > > +       EVP_PKEY *key = NULL;

> > > > +       X509 *cert = NULL;

> > > > +       BIO *data_bio = NULL;

> > > > +       const EVP_MD *md;

> > > > +       PKCS7 *p7;

> > > > +       int flags, ret = -1;

> > > > +

> > > > +       OpenSSL_add_all_digests();

> > > > +       OpenSSL_add_all_ciphers();

> > > > +       ERR_load_crypto_strings();

> > > > +

> > > > +       key = fileio_read_pkey(ctx->key_file);

> > > > +       if (!key)

> > > > +               goto err;

> > > > +       cert = fileio_read_cert(ctx->cert_file);

> > > > +       if (!cert)

> > > > +               goto err;

> > > > +

> > > > +       /*

> > > > +        * create a BIO, containing:

> > > > +        *  * firmware image

> > > > +        *  * monotonic count

> > > > +        * in this order!

> > > > +        * See EDK2's FmpAuthenticatedHandlerRsa2048Sha256()

> > > > +        */

> > > > +       data_bio = BIO_new(BIO_s_mem());

> > > > +       BIO_write(data_bio, ctx->image_data, ctx->image_size);

> > > > +       BIO_write(data_bio, &ctx->auth.monotonic_count,

> > > > +                 sizeof(ctx->auth.monotonic_count));

> > > > +

> > > > +       md = EVP_get_digestbyname("SHA256");

> > > > +       if (!md)

> > > > +               goto err;

> > > > +

> > > > +       /* create signature */

> > > > +       /* TODO: maybe add PKCS7_NOATTR and PKCS7_NOSMIMECAP */

> > > > +       flags = PKCS7_BINARY | PKCS7_DETACHED;

> > > > +       p7 = PKCS7_sign(NULL, NULL, NULL, data_bio, flags |

> PKCS7_PARTIAL);

> > > > +       if (!p7)

> > > > +               goto err;

> > > > +       if (!PKCS7_sign_add_signer(p7, cert, key, md, flags))

> > > > +               goto err;

> > > > +       if (!PKCS7_final(p7, data_bio, flags))

> > > > +               goto err;

> > > > +

> > > > +       /* convert pkcs7 into DER */

> > > > +       ctx->sig_data = NULL;

> > > > +       ctx->sig_size = ASN1_item_i2d((ASN1_VALUE *)p7,

> &ctx->sig_data,

> > > > +                                     ASN1_ITEM_rptr(PKCS7));

> > > > +       if (!ctx->sig_size)

> > > > +               goto err;

> > > > +

> > > > +       /* fill auth_info */

> > > > +       ctx->auth.auth_info.hdr.dwLength =

> sizeof(ctx->auth.auth_info)

> > > > +                                               + ctx->sig_size;

> > > > +       ctx->auth.auth_info.hdr.wRevision = WIN_CERT_REVISION_2_0;

> > > > +       ctx->auth.auth_info.hdr.wCertificateType =

> WIN_CERT_TYPE_EFI_GUID;

> > > > +       memcpy(&ctx->auth.auth_info.cert_type,

> &efi_guid_cert_type_pkcs7,

> > > > +              sizeof(efi_guid_cert_type_pkcs7));

> > > > +

> > > > +       ret = 0;

> > > > +err:

> > > > +       BIO_free_all(data_bio);

> > > > +       EVP_PKEY_free(key);

> > > > +       X509_free(cert);

> > > > +

> > > > +       return ret;

> > > > +}

> > > > +

> > > > +/**

> > > > + * dump_signature - dump out a signature

> > > > + * @path:      Path to a capsule file

> > > > + * @signature: Signature data

> > > > + * @sig_size:  Size of signature data

> > > > + *

> > > > + * Signature data pointed to by @signature will be saved into

> > > > + * a file whose file name is @path with ".p7" suffix.

> > > > + *

> > > > + * Return:

> > > > + * * 0  - on success

> > > > + * * -1 - on failure

> > > > + */

> > > > +static int dump_signature(const char *path, u8 *signature, size_t

> sig_size)

> > > > +{

> > > > +       char *sig_path;

> > > > +       FILE *f;

> > > > +       size_t size;

> > > > +       int ret = -1;

> > > > +

> > > > +       sig_path = malloc(strlen(path) + 3 + 1);

> > > > +       if (!sig_path)

> > > > +               return ret;

> > > > +

> > > > +       sprintf(sig_path, "%s.p7", path);

> > > > +       f = fopen(sig_path, "w");

> > > > +       if (!f)

> > > > +               goto err;

> > > > +

> > > > +       size = fwrite(signature, 1, sig_size, f);

> > > > +       if (size == sig_size)

> > > > +               ret = 0;

> > > > +

> > > > +       fclose(f);

> > > > +err:

> > > > +       free(sig_path);

> > > > +       return ret;

> > > > +}

> > > > +

> > > > +/**

> > > > + * free_sig_data - free out signature data

> > > > + * @ctx:       Pointer to authentication context

> > > > + *

> > > > + * Free signature data allocated in create_auth_data().

> > > > + */

> > > > +static void free_sig_data(struct auth_context *ctx)

> > > > +{

> > > > +       if (ctx->sig_size)

> > > > +               OPENSSL_free(ctx->sig_data);

> > > > +}

> > > > +#else

> > > > +static int create_auth_data(struct auth_context *ctx)

> > > > +{

> > > > +       return 0;

> > > > +}

> > > > +

> > > > +static int dump_signature(const char *path, u8 *signature, size_t

> sig_size)

> > > > +{

> > > > +       return 0;

> > > > +}

> > > > +

> > > > +static void free_sig_data(struct auth_context *ctx) {}

> > > > +#endif

> > > > +

> > > > +/**

> > > > + * create_fwbin - create an uefi capsule file

> > > > + * @path:      Path to a created capsule file

> > > > + * @bin:       Path to a firmware binary to encapsulate

> > > > + * @guid:      GUID of related FMP driver

> > > > + * @index:     Index number in capsule

> > > > + * @instance:  Instance number in capsule

> > > > + * @mcount:    Monotonic count in authentication information

> > > > + * @private_file:      Path to a private key file

> > > > + * @cert_file: Path to a certificate file

> > > > + *

> > > > + * This function actually does the job of creating an uefi capsule

> file.

> > > > + * All the arguments must be supplied.

> > > > + * If either @private_file ror @cert_file is NULL, the capsule file

> > > > + * won't be signed.

> > > > + *

> > > > + * Return:

> > > > + * * 0  - on success

> > > > + * * -1 - on failure

> > > > + */

> > > >  static int create_fwbin(char *path, char *bin, efi_guid_t *guid,

> > > > -                       unsigned long index, unsigned long instance)

> > > > +                       unsigned long index, unsigned long instance,

> > > > +                       uint64_t mcount, char *privkey_file, char

> *cert_file)

> > > >  {

> > > >         struct efi_capsule_header header;

> > > >         struct efi_firmware_management_capsule_header capsule;

> > > >         struct efi_firmware_management_capsule_image_header image;

> > > > +       struct auth_context auth_context;

> > > >         FILE *f, *g;

> > > >         struct stat bin_stat;

> > > >         u8 *data;

> > > > @@ -76,8 +363,9 @@ static int create_fwbin(char *path, char *bin,

> efi_guid_t *guid,

> > > >  #ifdef DEBUG

> > > >         printf("For output: %s\n", path);

> > > >         printf("\tbin: %s\n\ttype: %pUl\n", bin, guid);

> > > > -       printf("\tindex: %ld\n\tinstance: %ld\n", index, instance);

> > > > +       printf("\tindex: %lu\n\tinstance: %lu\n", index, instance);

> > > >  #endif

> > > > +       auth_context.sig_size = 0;

> > > >

> > > >         g = fopen(bin, "r");

> > > >         if (!g) {

> > > > @@ -93,11 +381,34 @@ static int create_fwbin(char *path, char *bin,

> efi_guid_t *guid,

> > > >                 printf("cannot allocate memory: %zx\n",

> (size_t)bin_stat.st_size);

> > > >                 goto err_1;

> > > >         }

> > > > -       f = fopen(path, "w");

> > > > -       if (!f) {

> > > > -               printf("cannot open %s\n", path);

> > > > +

> > > > +       size = fread(data, 1, bin_stat.st_size, g);

> > > > +       if (size < bin_stat.st_size) {

> > > > +               printf("read failed (%zx)\n", size);

> > > >                 goto err_2;

> > > >         }

> > > > +

> > > > +       /* first, calculate signature to determine its size */

> > > > +       if (privkey_file && cert_file) {

> > > > +               auth_context.key_file = privkey_file;

> > > > +               auth_context.cert_file = cert_file;

> > > > +               auth_context.auth.monotonic_count = mcount;

> > > > +               auth_context.image_data = data;

> > > > +               auth_context.image_size = bin_stat.st_size;

> > > > +

> > > > +               if (create_auth_data(&auth_context)) {

> > > > +                       printf("Signing firmware image failed\n");

> > > > +                       goto err_3;

> > > > +               }

> > > > +

> > > > +               if (dump_sig &&

> > > > +                   dump_signature(path, auth_context.sig_data,

> > > > +                                  auth_context.sig_size)) {

> > > > +                       printf("Creating signature file failed\n");

> > > > +                       goto err_3;

> > > > +               }

> > > > +       }

> > > > +

> > > >         header.capsule_guid = efi_guid_fm_capsule;

> > > >         header.header_size = sizeof(header);

> > > >         /* TODO: The current implementation ignores flags */

> > > > @@ -106,11 +417,20 @@ static int create_fwbin(char *path, char *bin,

> efi_guid_t *guid,

> > > >                                         + sizeof(capsule) +

> sizeof(u64)

> > > >                                         + sizeof(image)

> > > >                                         + bin_stat.st_size;

> > > > +       if (auth_context.sig_size)

> > > > +               header.capsule_image_size +=

> sizeof(auth_context.auth)

> > > > +                               + auth_context.sig_size;

> > > > +

> > > > +       f = fopen(path, "w");

> > > > +       if (!f) {

> > > > +               printf("cannot open %s\n", path);

> > > > +               goto err_3;

> > > > +       }

> > > >

> > > >         size = fwrite(&header, 1, sizeof(header), f);

> > > >         if (size < sizeof(header)) {

> > > >                 printf("write failed (%zx)\n", size);

> > > > -               goto err_3;

> > > > +               goto err_4;

> > > >         }

> > > >

> > > >         capsule.version = 0x00000001;

> > > > @@ -119,13 +439,13 @@ static int create_fwbin(char *path, char *bin,

> efi_guid_t *guid,

> > > >         size = fwrite(&capsule, 1, sizeof(capsule), f);

> > > >         if (size < (sizeof(capsule))) {

> > > >                 printf("write failed (%zx)\n", size);

> > > > -               goto err_3;

> > > > +               goto err_4;

> > > >         }

> > > >         offset = sizeof(capsule) + sizeof(u64);

> > > >         size = fwrite(&offset, 1, sizeof(offset), f);

> > > >         if (size < sizeof(offset)) {

> > > >                 printf("write failed (%zx)\n", size);

> > > > -               goto err_3;

> > > > +               goto err_4;

> > > >         }

> > > >

> > > >         image.version = 0x00000003;

> > > > @@ -135,34 +455,53 @@ static int create_fwbin(char *path, char *bin,

> efi_guid_t *guid,

> > > >         image.reserved[1] = 0;

> > > >         image.reserved[2] = 0;

> > > >         image.update_image_size = bin_stat.st_size;

> > > > +       if (auth_context.sig_size)

> > > > +               image.update_image_size += sizeof(auth_context.auth)

> > > > +                               + auth_context.sig_size;

> > > >         image.update_vendor_code_size = 0; /* none */

> > > >         image.update_hardware_instance = instance;

> > > >         image.image_capsule_support = 0;

> > > > +       if (auth_context.sig_size)

> > > > +               image.image_capsule_support |=

> CAPSULE_SUPPORT_AUTHENTICATION;

> > > >

> > > >         size = fwrite(&image, 1, sizeof(image), f);

> > > >         if (size < sizeof(image)) {

> > > >                 printf("write failed (%zx)\n", size);

> > > > -               goto err_3;

> > > > +               goto err_4;

> > > >         }

> > > > -       size = fread(data, 1, bin_stat.st_size, g);

> > > > -       if (size < bin_stat.st_size) {

> > > > -               printf("read failed (%zx)\n", size);

> > > > -               goto err_3;

> > > > +

> > > > +       if (auth_context.sig_size) {

> > > > +               size = fwrite(&auth_context.auth, 1,

> > > > +                             sizeof(auth_context.auth), f);

> > > > +               if (size < sizeof(auth_context.auth)) {

> > > > +                       printf("write failed (%zx)\n", size);

> > > > +                       goto err_4;

> > > > +               }

> > > > +               size = fwrite(auth_context.sig_data, 1,

> > > > +                             auth_context.sig_size, f);

> > > > +               if (size < auth_context.sig_size) {

> > > > +                       printf("write failed (%zx)\n", size);

> > > > +                       goto err_4;

> > > > +               }

> > > >         }

> > > > +

> > > >         size = fwrite(data, 1, bin_stat.st_size, f);

> > > >         if (size < bin_stat.st_size) {

> > > >                 printf("write failed (%zx)\n", size);

> > > > -               goto err_3;

> > > > +               goto err_4;

> > > >         }

> > > >

> > > >         fclose(f);

> > > >         fclose(g);

> > > >         free(data);

> > > > +       free_sig_data(&auth_context);

> > > >

> > > >         return 0;

> > > >

> > > > -err_3:

> > > > +err_4:

> > > >         fclose(f);

> > > > +err_3:

> > > > +       free_sig_data(&auth_context);

> > > >  err_2:

> > > >         free(data);

> > > >  err_1:

> > > > @@ -171,23 +510,37 @@ err_1:

> > > >         return -1;

> > > >  }

> > > >

> > > > -/*

> > > > - * Usage:

> > > > - *   $ mkeficapsule -f <firmware binary> <output file>

> > > > +/**

> > > > + * main - main entry function of mkeficapsule

> > > > + * @argc:      Number of arguments

> > > > + * @argv:      Array of pointers to arguments

> > > > + *

> > > > + * Create an uefi capsule file, optionally signing it.

> > > > + * Parse all the arguments and pass them on to create_fwbin().

> > > > + *

> > > > + * Return:

> > > > + * * 0  - on success

> > > > + * * -1 - on failure

> > > >   */

> > > >  int main(int argc, char **argv)

> > > >  {

> > > >         char *file;

> > > >         efi_guid_t *guid;

> > > >         unsigned long index, instance;

> > > > +       uint64_t mcount;

> > > > +       char *privkey_file, *cert_file;

> > > >         int c, idx;

> > > >

> > > >         file = NULL;

> > > >         guid = NULL;

> > > >         index = 0;

> > > >         instance = 0;

> > > > +       mcount = 0;

> > > > +       privkey_file = NULL;

> > > > +       cert_file = NULL;

> > > > +       dump_sig = 0;

> > > >         for (;;) {

> > > > -               c = getopt_long(argc, argv, "f:r:i:I:v:h", options,

> &idx);

> > > > +               c = getopt_long(argc, argv, opts_short, options,

> &idx);

> > > >                 if (c == -1)

> > > >                         break;

> > > >

> > > > @@ -214,29 +567,47 @@ int main(int argc, char **argv)

> > > >                 case 'I':

> > > >                         instance = strtoul(optarg, NULL, 0);

> > > >                         break;

> > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO

> > > > +               case 'p':

> > > > +                       if (privkey_file) {

> > > > +                               printf("Private Key already

> specified\n");

> > > > +                               return -1;

> > > > +                       }

> > > > +                       privkey_file = optarg;

> > > > +                       break;

> > > > +               case 'c':

> > > > +                       if (cert_file) {

> > > > +                               printf("Certificate file already

> specified\n");

> > > > +                               return -1;

> > > > +                       }

> > > > +                       cert_file = optarg;

> > > > +                       break;

> > > > +               case 'm':

> > > > +                       mcount = strtoul(optarg, NULL, 0);

> > > > +                       break;

> > > > +               case 'd':

> > > > +                       dump_sig = 1;

> > > > +                       break;

> > > > +#endif /* CONFIG_TOOLS_LIBCRYPTO */

> > > >                 case 'h':

> > > >                         print_usage();

> > > >                         return 0;

> > > >                 }

> > > >         }

> > > >

> > > > -       /* need an output file */

> > > > -       if (argc != optind + 1) {

> > > > -               print_usage();

> > > > -               exit(EXIT_FAILURE);

> > > > -       }

> > > > -

> > > > -       /* need a fit image file or raw image file */

> > > > -       if (!file) {

> > > > +       /* check necessary parameters */

> > > > +       if ((argc != optind + 1) || !file ||

> > > > +           ((privkey_file && !cert_file) ||

> > > > +            (!privkey_file && cert_file))) {

> > > >                 print_usage();

> > > > -               exit(EXIT_SUCCESS);

> > > > +               return -1;

> > > >         }

> > > >

> > > > -       if (create_fwbin(argv[optind], file, guid, index, instance)

> > > > -                       < 0) {

> > > > +       if (create_fwbin(argv[optind], file, guid, index, instance,

> > > > +                        mcount, privkey_file, cert_file) < 0) {

> > > >                 printf("Creating firmware capsule failed\n");

> > > > -               exit(EXIT_FAILURE);

> > > > +               return -1;

> > > >         }

> > > >

> > > > -       exit(EXIT_SUCCESS);

> > > > +       return 0;

> > > >  }

> > > > --

> > > > 2.33.0

> > > >

> > >

> > >

> > > --

> > > Masami Hiramatsu

>

>

>

> --

> Masami Hiramatsu

>
Masami Hiramatsu Oct. 25, 2021, 12:46 p.m. UTC | #7
Hi Sughosh,

2021年10月25日(月) 18:59 Sughosh Ganu <sughosh.ganu@linaro.org>:
>

> hi Masami,

>

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

>>

>> Hi Takahiro,

>>

>> 2021年10月25日(月) 12:12 AKASHI Takahiro <takahiro.akashi@linaro.org>:

>> >

>> > Hi, Masami,

>> >

>> > On Wed, Oct 20, 2021 at 05:17:12PM +0900, Masami Hiramatsu wrote:

>> > > Hello Akashi-san,

>> > >

>> > > Can you split this patch out from this series?

>> > > It seems that the series is doing several different things. This

>> > > capsule signing has no alternatives, but others are modifying or

>> > > replacing the current existing feature. In other words, this capsule

>> > > signing feature has no alternatives, but others we can continue to

>> > > use.

>> >

>> > What do you specifically mean by "existing feature" or "others we can

>> > continue to use"?

>> >

>> > All the commits, I think, in this series are closely related with

>> > each other.

>>

>> I meant I can use the devicetree to embed the authentication key in the

>> U-Boot itself (currently U-Boot expects that.) Or is there any issue with

>> putting the key in the devicetree?

>>

>> Without this patch, I can not sign the capsule file for U-Boot, since

>> the GenerateCapsule in EDK2 supports old format (is that updated?)

>

>

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


Good information!

Thank you for update me :)

>

> -sughosh

>

>>

>>

>> Thank you,

>>

>>

>> >

>> > -Takahiro Akashi

>> >

>> > > Thank you,

>> > >

>> > > 2021年10月7日(木) 15:25 AKASHI Takahiro <takahiro.akashi@linaro.org>:

>> > > >

>> > > > With this enhancement, mkeficapsule will be able to sign a capsule

>> > > > file when it is created. A signature added will be used later

>> > > > in the verification at FMP's SetImage() call.

>> > > >

>> > > > To do that, We need specify additional command parameters:

>> > > >   -monotonic-cout <count> : monotonic count

>> > > >   -private-key <private key file> : private key file

>> > > >   -certificate <certificate file> : certificate file

>> > > > Only when all of those parameters are given, a signature will be added

>> > > > to a capsule file.

>> > > >

>> > > > Users are expected to maintain and increment the monotonic count at

>> > > > every time of the update for each firmware image.

>> > > >

>> > > > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>

>> > > > ---

>> > > >  tools/Kconfig        |   7 +

>> > > >  tools/Makefile       |   8 +-

>> > > >  tools/mkeficapsule.c | 435 +++++++++++++++++++++++++++++++++++++++----

>> > > >  3 files changed, 416 insertions(+), 34 deletions(-)

>> > > >

>> > > > diff --git a/tools/Kconfig b/tools/Kconfig

>> > > > index d6f82cd949b5..9a37ed035311 100644

>> > > > --- a/tools/Kconfig

>> > > > +++ b/tools/Kconfig

>> > > > @@ -20,4 +20,11 @@ config TOOLS_LIBCRYPTO

>> > > >           This selection does not affect target features, such as runtime FIT

>> > > >           signature verification.

>> > > >

>> > > > +config TOOLS_MKEFICAPSULE

>> > > > +       bool "Build efimkcapsule command"

>> > > > +       default y if EFI_CAPSULE_ON_DISK

>> > > > +       help

>> > > > +         This command allows users to create a UEFI capsule file and,

>> > > > +         optionally sign that file. If you want to enable UEFI capsule

>> > > > +         update feature on your target, you certainly need this.

>> > > >  endmenu

>> > > > diff --git a/tools/Makefile b/tools/Makefile

>> > > > index 4a86321f6467..6ea3033dbfb8 100644

>> > > > --- a/tools/Makefile

>> > > > +++ b/tools/Makefile

>> > > > @@ -237,8 +237,12 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs

>> > > >  hostprogs-$(CONFIG_ASN1_COMPILER)      += asn1_compiler

>> > > >  HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include

>> > > >

>> > > > -mkeficapsule-objs      := mkeficapsule.o $(LIBFDT_OBJS)

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

>> > > > +HOSTLDLIBS_mkeficapsule += -luuid

>> > > > +ifeq ($(CONFIG_TOOLS_LIBCRYPTO),y)

>> > > > +HOSTLDLIBS_mkeficapsule += \

>> > > > +       $(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto")

>> > > > +endif

>> > > > +hostprogs-$(CONFIG_TOOLS_MKEFICAPSULE) += mkeficapsule

>> > > >

>> > > >  # We build some files with extra pedantic flags to try to minimize things

>> > > >  # that won't build on some weird host compiler -- though there are lots of

>> > > > diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c

>> > > > index 4995ba4e0c2a..5541e4bda894 100644

>> > > > --- a/tools/mkeficapsule.c

>> > > > +++ b/tools/mkeficapsule.c

>> > > > @@ -15,6 +15,16 @@

>> > > >  #include <sys/stat.h>

>> > > >  #include <sys/types.h>

>> > > >

>> > > > +#include <linux/kconfig.h>

>> > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO

>> > > > +#include <openssl/asn1.h>

>> > > > +#include <openssl/bio.h>

>> > > > +#include <openssl/evp.h>

>> > > > +#include <openssl/err.h>

>> > > > +#include <openssl/pem.h>

>> > > > +#include <openssl/pkcs7.h>

>> > > > +#endif

>> > > > +

>> > > >  typedef __u8 u8;

>> > > >  typedef __u16 u16;

>> > > >  typedef __u32 u32;

>> > > > @@ -38,12 +48,25 @@ efi_guid_t efi_guid_image_type_uboot_fit =

>> > > >                 EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID;

>> > > >  efi_guid_t efi_guid_image_type_uboot_raw =

>> > > >                 EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID;

>> > > > +efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;

>> > > > +

>> > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO

>> > > > +static const char *opts_short = "f:r:i:I:v:p:c:m:dh";

>> > > > +#else

>> > > > +static const char *opts_short = "f:r:i:I:v:h";

>> > > > +#endif

>> > > >

>> > > >  static struct option options[] = {

>> > > >         {"fit", required_argument, NULL, 'f'},

>> > > >         {"raw", required_argument, NULL, 'r'},

>> > > >         {"index", required_argument, NULL, 'i'},

>> > > >         {"instance", required_argument, NULL, 'I'},

>> > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO

>> > > > +       {"private-key", required_argument, NULL, 'p'},

>> > > > +       {"certificate", required_argument, NULL, 'c'},

>> > > > +       {"monotonic-count", required_argument, NULL, 'm'},

>> > > > +       {"dump-sig", no_argument, NULL, 'd'},

>> > > > +#endif

>> > > >         {"help", no_argument, NULL, 'h'},

>> > > >         {NULL, 0, NULL, 0},

>> > > >  };

>> > > > @@ -57,16 +80,280 @@ static void print_usage(void)

>> > > >                "\t-r, --raw <raw image>       new raw image file\n"

>> > > >                "\t-i, --index <index>         update image index\n"

>> > > >                "\t-I, --instance <instance>   update hardware instance\n"

>> > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO

>> > > > +              "\t-p, --private-key <privkey file>  private key file\n"

>> > > > +              "\t-c, --certificate <cert file>     signer's certificate file\n"

>> > > > +              "\t-m, --monotonic-count <count>     monotonic count\n"

>> > > > +              "\t-d, --dump_sig              dump signature (*.p7)\n"

>> > > > +#endif

>> > > >                "\t-h, --help                  print a help message\n",

>> > > >                tool_name);

>> > > >  }

>> > > >

>> > > > +/**

>> > > > + * auth_context - authentication context

>> > > > + * @key_file:  Path to a private key file

>> > > > + * @cert_file: Path to a certificate file

>> > > > + * @image_data:        Pointer to firmware data

>> > > > + * @image_size:        Size of firmware data

>> > > > + * @auth:      Authentication header

>> > > > + * @sig_data:  Signature data

>> > > > + * @sig_size:  Size of signature data

>> > > > + *

>> > > > + * Data structure used in create_auth_data(). @key_file through

>> > > > + * @image_size are input parameters. @auth, @sig_data and @sig_size

>> > > > + * are filled in by create_auth_data().

>> > > > + */

>> > > > +struct auth_context {

>> > > > +       char *key_file;

>> > > > +       char *cert_file;

>> > > > +       u8 *image_data;

>> > > > +       size_t image_size;

>> > > > +       struct efi_firmware_image_authentication auth;

>> > > > +       u8 *sig_data;

>> > > > +       size_t sig_size;

>> > > > +};

>> > > > +

>> > > > +static int dump_sig;

>> > > > +

>> > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO

>> > > > +/**

>> > > > + * fileio-read_pkey - read out a private key

>> > > > + * @filename:  Path to a private key file

>> > > > + *

>> > > > + * Read out a private key file and parse it into "EVP_PKEY" structure.

>> > > > + *

>> > > > + * Return:

>> > > > + * * Pointer to private key structure  - on success

>> > > > + * * NULL - on failure

>> > > > + */

>> > > > +static EVP_PKEY *fileio_read_pkey(const char *filename)

>> > > > +{

>> > > > +       EVP_PKEY *key = NULL;

>> > > > +       BIO *bio;

>> > > > +

>> > > > +       bio = BIO_new_file(filename, "r");

>> > > > +       if (!bio)

>> > > > +               goto out;

>> > > > +

>> > > > +       key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);

>> > > > +

>> > > > +out:

>> > > > +       BIO_free_all(bio);

>> > > > +       if (!key) {

>> > > > +               printf("Can't load key from file '%s'\n", filename);

>> > > > +               ERR_print_errors_fp(stderr);

>> > > > +       }

>> > > > +

>> > > > +       return key;

>> > > > +}

>> > > > +

>> > > > +/**

>> > > > + * fileio-read_cert - read out a certificate

>> > > > + * @filename:  Path to a certificate file

>> > > > + *

>> > > > + * Read out a certificate file and parse it into "X509" structure.

>> > > > + *

>> > > > + * Return:

>> > > > + * * Pointer to certificate structure  - on success

>> > > > + * * NULL - on failure

>> > > > + */

>> > > > +static X509 *fileio_read_cert(const char *filename)

>> > > > +{

>> > > > +       X509 *cert = NULL;

>> > > > +       BIO *bio;

>> > > > +

>> > > > +       bio = BIO_new_file(filename, "r");

>> > > > +       if (!bio)

>> > > > +               goto out;

>> > > > +

>> > > > +       cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);

>> > > > +

>> > > > +out:

>> > > > +       BIO_free_all(bio);

>> > > > +       if (!cert) {

>> > > > +               printf("Can't load certificate from file '%s'\n", filename);

>> > > > +               ERR_print_errors_fp(stderr);

>> > > > +       }

>> > > > +

>> > > > +       return cert;

>> > > > +}

>> > > > +

>> > > > +/**

>> > > > + * create_auth_data - compose authentication data in capsule

>> > > > + * @auth_context:      Pointer to authentication context

>> > > > + *

>> > > > + * Fill up an authentication header (.auth) and signature data (.sig_data)

>> > > > + * in @auth_context, using library functions from openssl.

>> > > > + * All the parameters in @auth_context must be filled in by a caller.

>> > > > + *

>> > > > + * Return:

>> > > > + * * 0  - on success

>> > > > + * * -1 - on failure

>> > > > + */

>> > > > +static int create_auth_data(struct auth_context *ctx)

>> > > > +{

>> > > > +       EVP_PKEY *key = NULL;

>> > > > +       X509 *cert = NULL;

>> > > > +       BIO *data_bio = NULL;

>> > > > +       const EVP_MD *md;

>> > > > +       PKCS7 *p7;

>> > > > +       int flags, ret = -1;

>> > > > +

>> > > > +       OpenSSL_add_all_digests();

>> > > > +       OpenSSL_add_all_ciphers();

>> > > > +       ERR_load_crypto_strings();

>> > > > +

>> > > > +       key = fileio_read_pkey(ctx->key_file);

>> > > > +       if (!key)

>> > > > +               goto err;

>> > > > +       cert = fileio_read_cert(ctx->cert_file);

>> > > > +       if (!cert)

>> > > > +               goto err;

>> > > > +

>> > > > +       /*

>> > > > +        * create a BIO, containing:

>> > > > +        *  * firmware image

>> > > > +        *  * monotonic count

>> > > > +        * in this order!

>> > > > +        * See EDK2's FmpAuthenticatedHandlerRsa2048Sha256()

>> > > > +        */

>> > > > +       data_bio = BIO_new(BIO_s_mem());

>> > > > +       BIO_write(data_bio, ctx->image_data, ctx->image_size);

>> > > > +       BIO_write(data_bio, &ctx->auth.monotonic_count,

>> > > > +                 sizeof(ctx->auth.monotonic_count));

>> > > > +

>> > > > +       md = EVP_get_digestbyname("SHA256");

>> > > > +       if (!md)

>> > > > +               goto err;

>> > > > +

>> > > > +       /* create signature */

>> > > > +       /* TODO: maybe add PKCS7_NOATTR and PKCS7_NOSMIMECAP */

>> > > > +       flags = PKCS7_BINARY | PKCS7_DETACHED;

>> > > > +       p7 = PKCS7_sign(NULL, NULL, NULL, data_bio, flags | PKCS7_PARTIAL);

>> > > > +       if (!p7)

>> > > > +               goto err;

>> > > > +       if (!PKCS7_sign_add_signer(p7, cert, key, md, flags))

>> > > > +               goto err;

>> > > > +       if (!PKCS7_final(p7, data_bio, flags))

>> > > > +               goto err;

>> > > > +

>> > > > +       /* convert pkcs7 into DER */

>> > > > +       ctx->sig_data = NULL;

>> > > > +       ctx->sig_size = ASN1_item_i2d((ASN1_VALUE *)p7, &ctx->sig_data,

>> > > > +                                     ASN1_ITEM_rptr(PKCS7));

>> > > > +       if (!ctx->sig_size)

>> > > > +               goto err;

>> > > > +

>> > > > +       /* fill auth_info */

>> > > > +       ctx->auth.auth_info.hdr.dwLength = sizeof(ctx->auth.auth_info)

>> > > > +                                               + ctx->sig_size;

>> > > > +       ctx->auth.auth_info.hdr.wRevision = WIN_CERT_REVISION_2_0;

>> > > > +       ctx->auth.auth_info.hdr.wCertificateType = WIN_CERT_TYPE_EFI_GUID;

>> > > > +       memcpy(&ctx->auth.auth_info.cert_type, &efi_guid_cert_type_pkcs7,

>> > > > +              sizeof(efi_guid_cert_type_pkcs7));

>> > > > +

>> > > > +       ret = 0;

>> > > > +err:

>> > > > +       BIO_free_all(data_bio);

>> > > > +       EVP_PKEY_free(key);

>> > > > +       X509_free(cert);

>> > > > +

>> > > > +       return ret;

>> > > > +}

>> > > > +

>> > > > +/**

>> > > > + * dump_signature - dump out a signature

>> > > > + * @path:      Path to a capsule file

>> > > > + * @signature: Signature data

>> > > > + * @sig_size:  Size of signature data

>> > > > + *

>> > > > + * Signature data pointed to by @signature will be saved into

>> > > > + * a file whose file name is @path with ".p7" suffix.

>> > > > + *

>> > > > + * Return:

>> > > > + * * 0  - on success

>> > > > + * * -1 - on failure

>> > > > + */

>> > > > +static int dump_signature(const char *path, u8 *signature, size_t sig_size)

>> > > > +{

>> > > > +       char *sig_path;

>> > > > +       FILE *f;

>> > > > +       size_t size;

>> > > > +       int ret = -1;

>> > > > +

>> > > > +       sig_path = malloc(strlen(path) + 3 + 1);

>> > > > +       if (!sig_path)

>> > > > +               return ret;

>> > > > +

>> > > > +       sprintf(sig_path, "%s.p7", path);

>> > > > +       f = fopen(sig_path, "w");

>> > > > +       if (!f)

>> > > > +               goto err;

>> > > > +

>> > > > +       size = fwrite(signature, 1, sig_size, f);

>> > > > +       if (size == sig_size)

>> > > > +               ret = 0;

>> > > > +

>> > > > +       fclose(f);

>> > > > +err:

>> > > > +       free(sig_path);

>> > > > +       return ret;

>> > > > +}

>> > > > +

>> > > > +/**

>> > > > + * free_sig_data - free out signature data

>> > > > + * @ctx:       Pointer to authentication context

>> > > > + *

>> > > > + * Free signature data allocated in create_auth_data().

>> > > > + */

>> > > > +static void free_sig_data(struct auth_context *ctx)

>> > > > +{

>> > > > +       if (ctx->sig_size)

>> > > > +               OPENSSL_free(ctx->sig_data);

>> > > > +}

>> > > > +#else

>> > > > +static int create_auth_data(struct auth_context *ctx)

>> > > > +{

>> > > > +       return 0;

>> > > > +}

>> > > > +

>> > > > +static int dump_signature(const char *path, u8 *signature, size_t sig_size)

>> > > > +{

>> > > > +       return 0;

>> > > > +}

>> > > > +

>> > > > +static void free_sig_data(struct auth_context *ctx) {}

>> > > > +#endif

>> > > > +

>> > > > +/**

>> > > > + * create_fwbin - create an uefi capsule file

>> > > > + * @path:      Path to a created capsule file

>> > > > + * @bin:       Path to a firmware binary to encapsulate

>> > > > + * @guid:      GUID of related FMP driver

>> > > > + * @index:     Index number in capsule

>> > > > + * @instance:  Instance number in capsule

>> > > > + * @mcount:    Monotonic count in authentication information

>> > > > + * @private_file:      Path to a private key file

>> > > > + * @cert_file: Path to a certificate file

>> > > > + *

>> > > > + * This function actually does the job of creating an uefi capsule file.

>> > > > + * All the arguments must be supplied.

>> > > > + * If either @private_file ror @cert_file is NULL, the capsule file

>> > > > + * won't be signed.

>> > > > + *

>> > > > + * Return:

>> > > > + * * 0  - on success

>> > > > + * * -1 - on failure

>> > > > + */

>> > > >  static int create_fwbin(char *path, char *bin, efi_guid_t *guid,

>> > > > -                       unsigned long index, unsigned long instance)

>> > > > +                       unsigned long index, unsigned long instance,

>> > > > +                       uint64_t mcount, char *privkey_file, char *cert_file)

>> > > >  {

>> > > >         struct efi_capsule_header header;

>> > > >         struct efi_firmware_management_capsule_header capsule;

>> > > >         struct efi_firmware_management_capsule_image_header image;

>> > > > +       struct auth_context auth_context;

>> > > >         FILE *f, *g;

>> > > >         struct stat bin_stat;

>> > > >         u8 *data;

>> > > > @@ -76,8 +363,9 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,

>> > > >  #ifdef DEBUG

>> > > >         printf("For output: %s\n", path);

>> > > >         printf("\tbin: %s\n\ttype: %pUl\n", bin, guid);

>> > > > -       printf("\tindex: %ld\n\tinstance: %ld\n", index, instance);

>> > > > +       printf("\tindex: %lu\n\tinstance: %lu\n", index, instance);

>> > > >  #endif

>> > > > +       auth_context.sig_size = 0;

>> > > >

>> > > >         g = fopen(bin, "r");

>> > > >         if (!g) {

>> > > > @@ -93,11 +381,34 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,

>> > > >                 printf("cannot allocate memory: %zx\n", (size_t)bin_stat.st_size);

>> > > >                 goto err_1;

>> > > >         }

>> > > > -       f = fopen(path, "w");

>> > > > -       if (!f) {

>> > > > -               printf("cannot open %s\n", path);

>> > > > +

>> > > > +       size = fread(data, 1, bin_stat.st_size, g);

>> > > > +       if (size < bin_stat.st_size) {

>> > > > +               printf("read failed (%zx)\n", size);

>> > > >                 goto err_2;

>> > > >         }

>> > > > +

>> > > > +       /* first, calculate signature to determine its size */

>> > > > +       if (privkey_file && cert_file) {

>> > > > +               auth_context.key_file = privkey_file;

>> > > > +               auth_context.cert_file = cert_file;

>> > > > +               auth_context.auth.monotonic_count = mcount;

>> > > > +               auth_context.image_data = data;

>> > > > +               auth_context.image_size = bin_stat.st_size;

>> > > > +

>> > > > +               if (create_auth_data(&auth_context)) {

>> > > > +                       printf("Signing firmware image failed\n");

>> > > > +                       goto err_3;

>> > > > +               }

>> > > > +

>> > > > +               if (dump_sig &&

>> > > > +                   dump_signature(path, auth_context.sig_data,

>> > > > +                                  auth_context.sig_size)) {

>> > > > +                       printf("Creating signature file failed\n");

>> > > > +                       goto err_3;

>> > > > +               }

>> > > > +       }

>> > > > +

>> > > >         header.capsule_guid = efi_guid_fm_capsule;

>> > > >         header.header_size = sizeof(header);

>> > > >         /* TODO: The current implementation ignores flags */

>> > > > @@ -106,11 +417,20 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,

>> > > >                                         + sizeof(capsule) + sizeof(u64)

>> > > >                                         + sizeof(image)

>> > > >                                         + bin_stat.st_size;

>> > > > +       if (auth_context.sig_size)

>> > > > +               header.capsule_image_size += sizeof(auth_context.auth)

>> > > > +                               + auth_context.sig_size;

>> > > > +

>> > > > +       f = fopen(path, "w");

>> > > > +       if (!f) {

>> > > > +               printf("cannot open %s\n", path);

>> > > > +               goto err_3;

>> > > > +       }

>> > > >

>> > > >         size = fwrite(&header, 1, sizeof(header), f);

>> > > >         if (size < sizeof(header)) {

>> > > >                 printf("write failed (%zx)\n", size);

>> > > > -               goto err_3;

>> > > > +               goto err_4;

>> > > >         }

>> > > >

>> > > >         capsule.version = 0x00000001;

>> > > > @@ -119,13 +439,13 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,

>> > > >         size = fwrite(&capsule, 1, sizeof(capsule), f);

>> > > >         if (size < (sizeof(capsule))) {

>> > > >                 printf("write failed (%zx)\n", size);

>> > > > -               goto err_3;

>> > > > +               goto err_4;

>> > > >         }

>> > > >         offset = sizeof(capsule) + sizeof(u64);

>> > > >         size = fwrite(&offset, 1, sizeof(offset), f);

>> > > >         if (size < sizeof(offset)) {

>> > > >                 printf("write failed (%zx)\n", size);

>> > > > -               goto err_3;

>> > > > +               goto err_4;

>> > > >         }

>> > > >

>> > > >         image.version = 0x00000003;

>> > > > @@ -135,34 +455,53 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid,

>> > > >         image.reserved[1] = 0;

>> > > >         image.reserved[2] = 0;

>> > > >         image.update_image_size = bin_stat.st_size;

>> > > > +       if (auth_context.sig_size)

>> > > > +               image.update_image_size += sizeof(auth_context.auth)

>> > > > +                               + auth_context.sig_size;

>> > > >         image.update_vendor_code_size = 0; /* none */

>> > > >         image.update_hardware_instance = instance;

>> > > >         image.image_capsule_support = 0;

>> > > > +       if (auth_context.sig_size)

>> > > > +               image.image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION;

>> > > >

>> > > >         size = fwrite(&image, 1, sizeof(image), f);

>> > > >         if (size < sizeof(image)) {

>> > > >                 printf("write failed (%zx)\n", size);

>> > > > -               goto err_3;

>> > > > +               goto err_4;

>> > > >         }

>> > > > -       size = fread(data, 1, bin_stat.st_size, g);

>> > > > -       if (size < bin_stat.st_size) {

>> > > > -               printf("read failed (%zx)\n", size);

>> > > > -               goto err_3;

>> > > > +

>> > > > +       if (auth_context.sig_size) {

>> > > > +               size = fwrite(&auth_context.auth, 1,

>> > > > +                             sizeof(auth_context.auth), f);

>> > > > +               if (size < sizeof(auth_context.auth)) {

>> > > > +                       printf("write failed (%zx)\n", size);

>> > > > +                       goto err_4;

>> > > > +               }

>> > > > +               size = fwrite(auth_context.sig_data, 1,

>> > > > +                             auth_context.sig_size, f);

>> > > > +               if (size < auth_context.sig_size) {

>> > > > +                       printf("write failed (%zx)\n", size);

>> > > > +                       goto err_4;

>> > > > +               }

>> > > >         }

>> > > > +

>> > > >         size = fwrite(data, 1, bin_stat.st_size, f);

>> > > >         if (size < bin_stat.st_size) {

>> > > >                 printf("write failed (%zx)\n", size);

>> > > > -               goto err_3;

>> > > > +               goto err_4;

>> > > >         }

>> > > >

>> > > >         fclose(f);

>> > > >         fclose(g);

>> > > >         free(data);

>> > > > +       free_sig_data(&auth_context);

>> > > >

>> > > >         return 0;

>> > > >

>> > > > -err_3:

>> > > > +err_4:

>> > > >         fclose(f);

>> > > > +err_3:

>> > > > +       free_sig_data(&auth_context);

>> > > >  err_2:

>> > > >         free(data);

>> > > >  err_1:

>> > > > @@ -171,23 +510,37 @@ err_1:

>> > > >         return -1;

>> > > >  }

>> > > >

>> > > > -/*

>> > > > - * Usage:

>> > > > - *   $ mkeficapsule -f <firmware binary> <output file>

>> > > > +/**

>> > > > + * main - main entry function of mkeficapsule

>> > > > + * @argc:      Number of arguments

>> > > > + * @argv:      Array of pointers to arguments

>> > > > + *

>> > > > + * Create an uefi capsule file, optionally signing it.

>> > > > + * Parse all the arguments and pass them on to create_fwbin().

>> > > > + *

>> > > > + * Return:

>> > > > + * * 0  - on success

>> > > > + * * -1 - on failure

>> > > >   */

>> > > >  int main(int argc, char **argv)

>> > > >  {

>> > > >         char *file;

>> > > >         efi_guid_t *guid;

>> > > >         unsigned long index, instance;

>> > > > +       uint64_t mcount;

>> > > > +       char *privkey_file, *cert_file;

>> > > >         int c, idx;

>> > > >

>> > > >         file = NULL;

>> > > >         guid = NULL;

>> > > >         index = 0;

>> > > >         instance = 0;

>> > > > +       mcount = 0;

>> > > > +       privkey_file = NULL;

>> > > > +       cert_file = NULL;

>> > > > +       dump_sig = 0;

>> > > >         for (;;) {

>> > > > -               c = getopt_long(argc, argv, "f:r:i:I:v:h", options, &idx);

>> > > > +               c = getopt_long(argc, argv, opts_short, options, &idx);

>> > > >                 if (c == -1)

>> > > >                         break;

>> > > >

>> > > > @@ -214,29 +567,47 @@ int main(int argc, char **argv)

>> > > >                 case 'I':

>> > > >                         instance = strtoul(optarg, NULL, 0);

>> > > >                         break;

>> > > > +#ifdef CONFIG_TOOLS_LIBCRYPTO

>> > > > +               case 'p':

>> > > > +                       if (privkey_file) {

>> > > > +                               printf("Private Key already specified\n");

>> > > > +                               return -1;

>> > > > +                       }

>> > > > +                       privkey_file = optarg;

>> > > > +                       break;

>> > > > +               case 'c':

>> > > > +                       if (cert_file) {

>> > > > +                               printf("Certificate file already specified\n");

>> > > > +                               return -1;

>> > > > +                       }

>> > > > +                       cert_file = optarg;

>> > > > +                       break;

>> > > > +               case 'm':

>> > > > +                       mcount = strtoul(optarg, NULL, 0);

>> > > > +                       break;

>> > > > +               case 'd':

>> > > > +                       dump_sig = 1;

>> > > > +                       break;

>> > > > +#endif /* CONFIG_TOOLS_LIBCRYPTO */

>> > > >                 case 'h':

>> > > >                         print_usage();

>> > > >                         return 0;

>> > > >                 }

>> > > >         }

>> > > >

>> > > > -       /* need an output file */

>> > > > -       if (argc != optind + 1) {

>> > > > -               print_usage();

>> > > > -               exit(EXIT_FAILURE);

>> > > > -       }

>> > > > -

>> > > > -       /* need a fit image file or raw image file */

>> > > > -       if (!file) {

>> > > > +       /* check necessary parameters */

>> > > > +       if ((argc != optind + 1) || !file ||

>> > > > +           ((privkey_file && !cert_file) ||

>> > > > +            (!privkey_file && cert_file))) {

>> > > >                 print_usage();

>> > > > -               exit(EXIT_SUCCESS);

>> > > > +               return -1;

>> > > >         }

>> > > >

>> > > > -       if (create_fwbin(argv[optind], file, guid, index, instance)

>> > > > -                       < 0) {

>> > > > +       if (create_fwbin(argv[optind], file, guid, index, instance,

>> > > > +                        mcount, privkey_file, cert_file) < 0) {

>> > > >                 printf("Creating firmware capsule failed\n");

>> > > > -               exit(EXIT_FAILURE);

>> > > > +               return -1;

>> > > >         }

>> > > >

>> > > > -       exit(EXIT_SUCCESS);

>> > > > +       return 0;

>> > > >  }

>> > > > --

>> > > > 2.33.0

>> > > >

>> > >

>> > >

>> > > --

>> > > Masami Hiramatsu

>>

>>

>>

>> --

>> Masami Hiramatsu




-- 
Masami Hiramatsu
diff mbox series

Patch

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