[PATCH 0/4] efi_loader: capsule: improve capsule authentication support

As I discussed in [1], I have made a couple of improvements on the current implemenation of capsule update.
Among others, this patch series 1. add signing feature to mkeficapsule 2. remove dtb operation from mkeficapsule 3. add pytest for capsule authentication (on sandbox)
NOTE: I temporarily include Patch#3 in order to show that it is not worth implementing in C as we can do the same thing with a very small shell script.
My intent is *NOT* to merge Patch#3 in upstream.
Prerequisite patches ==================== See Sughosh's [2] and my [3].
Test ==== * passed the pytest which is included in this patch series on sandbox built locally.
Todo ==== * review and update the document for capsule update doc/board/emulation/qemu_capsule_update.rst (but not in this patch series)
[1] https://lists.denx.de/pipermail/u-boot/2021-April/447918.html [2] https://lists.denx.de/pipermail/u-boot/2021-April/447183.html [3] https://lists.denx.de/pipermail/u-boot/2021-May/449347.html https://lists.denx.de/pipermail/u-boot/2021-May/449348.html https://lists.denx.de/pipermail/u-boot/2021-May/449349.html https://lists.denx.de/pipermail/u-boot/2021-May/449350.html https://lists.denx.de/pipermail/u-boot/2021-May/449351.html
Changes ======= Initial release (May 12, 2021) * based on v2021.07-rc2
AKASHI Takahiro (4): tools: mkeficapsule: add firmwware image signing tools: mkeficapsule: remove device-tree related operation tools: add fdtsig command test/py: efi_capsule: add image authentication test
Makefile | 7 +- .../py/tests/test_efi_capsule/capsule_defs.py | 5 + test/py/tests/test_efi_capsule/conftest.py | 35 +- test/py/tests/test_efi_capsule/signature.dts | 8 + .../test_capsule_firmware_signed.py | 234 +++++++++ tools/Makefile | 7 +- tools/fdtsig.c | 274 +++++++++++ tools/fdtsig.sh | 40 ++ tools/mkeficapsule.c | 455 ++++++++++-------- 9 files changed, 856 insertions(+), 209 deletions(-) create mode 100644 test/py/tests/test_efi_capsule/signature.dts create mode 100644 test/py/tests/test_efi_capsule/test_capsule_firmware_signed.py create mode 100644 tools/fdtsig.c create mode 100755 tools/fdtsig.sh

With this enhancement, mkeficapsule will be able to create a capsule file with a signature which will be verified later by FMP's SetImage().
We will have to specify addtional command parameters: -monotonic-cout <count> : monotonic count -private-key <private key file> : private key file -certificate <certificate file> : certificate file Only when those parameters are given, a signature will be added to a capsule file.
Users are expected to maintain the monotonic count for each firmware image.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- tools/Makefile | 4 + tools/mkeficapsule.c | 324 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 303 insertions(+), 25 deletions(-)
diff --git a/tools/Makefile b/tools/Makefile index d020c55d6644..02eae0286e20 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -231,6 +231,10 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs hostprogs-$(CONFIG_ASN1_COMPILER) += asn1_compiler HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
+ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),) +HOSTLDLIBS_mkeficapsule += \ + $(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto") +endif mkeficapsule-objs := mkeficapsule.o $(LIBFDT_OBJS) hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c index de0a62898886..34ff1bdd82eb 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -18,7 +18,17 @@ #include <sys/stat.h> #include <sys/types.h>
-#include "fdt_host.h" +#include <linux/kconfig.h> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE) +#include <openssl/asn1.h> +#include <openssl/bio.h> +#include <openssl/evp.h> +#include <openssl/err.h> +#include <openssl/pem.h> +#include <openssl/pkcs7.h> +#endif + +#include <linux/libfdt.h>
typedef __u8 u8; typedef __u16 u16; @@ -46,6 +56,13 @@ efi_guid_t efi_guid_image_type_uboot_fit = EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID; efi_guid_t efi_guid_image_type_uboot_raw = EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID; +efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID; + +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE) +static const char *opts_short = "f:r:i:I:v:D:K:P:C:m:dOh"; +#else +static const char *opts_short = "f:r:i:I:v:D:K:Oh"; +#endif
static struct option options[] = { {"fit", required_argument, NULL, 'f'}, @@ -54,6 +71,12 @@ static struct option options[] = { {"instance", required_argument, NULL, 'I'}, {"dtb", required_argument, NULL, 'D'}, {"public key", required_argument, NULL, 'K'}, +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE) + {"private-key", required_argument, NULL, 'P'}, + {"certificate", required_argument, NULL, 'C'}, + {"monotonic-count", required_argument, NULL, 'm'}, + {"dump-sig", no_argument, NULL, 'd'}, +#endif {"overlay", no_argument, NULL, 'O'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0}, @@ -70,6 +93,12 @@ static void print_usage(void) "\t-I, --instance <instance> update hardware instance\n" "\t-K, --public-key <key file> public key esl file\n" "\t-D, --dtb <dtb file> dtb file\n" +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE) + "\t-P, --private-key <privkey file> private key file\n" + "\t-C, --certificate <cert file> signer's certificate file\n" + "\t-m, --monotonic-count <count> monotonic count\n" + "\t-d, --dump_sig dump signature (*.p7)\n" +#endif "\t-O, --overlay the dtb file is an overlay\n" "\t-h, --help print a help message\n", tool_name); @@ -249,12 +278,167 @@ err: return ret; }
+struct auth_context { + char *key_file; + char *cert_file; + u8 *image_data; + size_t image_size; + struct efi_firmware_image_authentication auth; + u8 *sig_data; + size_t sig_size; +}; + +static int dump_sig; + +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE) +static EVP_PKEY *fileio_read_pkey(const char *filename) +{ + EVP_PKEY *key = NULL; + BIO *bio; + + bio = BIO_new_file(filename, "r"); + if (!bio) + goto out; + + key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); + +out: + BIO_free_all(bio); + if (!key) { + printf("Can't load key from file '%s'\n", filename); + ERR_print_errors_fp(stderr); + } + + return key; +} + +static X509 *fileio_read_cert(const char *filename) +{ + X509 *cert = NULL; + BIO *bio; + + bio = BIO_new_file(filename, "r"); + if (!bio) + goto out; + + cert = PEM_read_bio_X509(bio, NULL, NULL, NULL); + +out: + BIO_free_all(bio); + if (!cert) { + printf("Can't load certificate from file '%s'\n", filename); + ERR_print_errors_fp(stderr); + } + + return cert; +} + +static int create_auth_data(struct auth_context *ctx) +{ + EVP_PKEY *key = NULL; + X509 *cert = NULL; + BIO *data_bio = NULL; + const EVP_MD *md; + PKCS7 *p7; + int flags, ret = -1; + + OpenSSL_add_all_digests(); + OpenSSL_add_all_ciphers(); + ERR_load_crypto_strings(); + + key = fileio_read_pkey(ctx->key_file); + if (!key) + goto err; + cert = fileio_read_cert(ctx->cert_file); + if (!cert) + goto err; + + /* + * create a BIO, containing: + * * firmware image + * * monotonic count + * in this order! + * See EDK2's FmpAuthenticatedHandlerRsa2048Sha256() + */ + data_bio = BIO_new(BIO_s_mem()); + BIO_write(data_bio, ctx->image_data, ctx->image_size); + BIO_write(data_bio, &ctx->auth.monotonic_count, + sizeof(ctx->auth.monotonic_count)); + + md = EVP_get_digestbyname("SHA256"); + if (!md) + goto err; + + /* create signature */ + /* TODO: maybe add PKCS7_NOATTR and PKCS7_NOSMIMECAP */ + flags = PKCS7_BINARY | PKCS7_DETACHED; + p7 = PKCS7_sign(NULL, NULL, NULL, data_bio, flags | PKCS7_PARTIAL); + if (!p7) + goto err; + if (!PKCS7_sign_add_signer(p7, cert, key, md, flags)) + goto err; + if (!PKCS7_final(p7, data_bio, flags)) + goto err; + + /* convert pkcs7 into DER */ + ctx->sig_data = NULL; + ctx->sig_size = ASN1_item_i2d((ASN1_VALUE *)p7, &ctx->sig_data, + ASN1_ITEM_rptr(PKCS7)); + if (!ctx->sig_size) + goto err; + + /* fill auth_info */ + ctx->auth.auth_info.hdr.dwLength = sizeof(ctx->auth.auth_info) + + ctx->sig_size; + ctx->auth.auth_info.hdr.wRevision = WIN_CERT_REVISION_2_0; + ctx->auth.auth_info.hdr.wCertificateType = WIN_CERT_TYPE_EFI_GUID; + memcpy(&ctx->auth.auth_info.cert_type, &efi_guid_cert_type_pkcs7, + sizeof(efi_guid_cert_type_pkcs7)); + + ret = 0; +err: + BIO_free_all(data_bio); + EVP_PKEY_free(key); + X509_free(cert); + + return ret; +} + +static int dump_signature(const char *path, u8 *signature, size_t sig_size) +{ + char *sig_path; + FILE *f; + size_t size; + int ret = -1; + + sig_path = malloc(strlen(path) + 3 + 1); + if (!sig_path) + return ret; + + sprintf(sig_path, "%s.p7", path); + f = fopen(sig_path, "w"); + if (!f) + goto err; + + size = fwrite(signature, 1, sig_size, f); + if (size == sig_size) + ret = 0; + + fclose(f); +err: + free(sig_path); + return ret; +} +#endif + static int create_fwbin(char *path, char *bin, efi_guid_t *guid, - unsigned long index, unsigned long instance) + unsigned long index, unsigned long instance, + uint64_t mcount, char *privkey_file, char *cert_file) { struct efi_capsule_header header; struct efi_firmware_management_capsule_header capsule; struct efi_firmware_management_capsule_image_header image; + struct auth_context auth_context; FILE *f, *g; struct stat bin_stat; u8 *data; @@ -266,6 +450,7 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, printf("\tbin: %s\n\ttype: %pUl\n", bin, guid); printf("\tindex: %ld\n\tinstance: %ld\n", index, instance); #endif + auth_context.sig_size = 0;
g = fopen(bin, "r"); if (!g) { @@ -281,11 +466,36 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, printf("cannot allocate memory: %zx\n", (size_t)bin_stat.st_size); goto err_1; } - f = fopen(path, "w"); - if (!f) { - printf("cannot open %s\n", path); + + size = fread(data, 1, bin_stat.st_size, g); + if (size < bin_stat.st_size) { + printf("read failed (%zx)\n", size); goto err_2; } + + /* first, calculate signature to determine its size */ +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE) + if (privkey_file && cert_file) { + auth_context.key_file = privkey_file; + auth_context.cert_file = cert_file; + auth_context.auth.monotonic_count = mcount; + auth_context.image_data = data; + auth_context.image_size = bin_stat.st_size; + + if (create_auth_data(&auth_context)) { + printf("Signing firmware image failed\n"); + goto err_3; + } + + if (dump_sig && + dump_signature(path, auth_context.sig_data, + auth_context.sig_size)) { + printf("Creating signature file failed\n"); + goto err_3; + } + } +#endif + header.capsule_guid = efi_guid_fm_capsule; header.header_size = sizeof(header); /* TODO: The current implementation ignores flags */ @@ -294,11 +504,20 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, + sizeof(capsule) + sizeof(u64) + sizeof(image) + bin_stat.st_size; + if (auth_context.sig_size) + header.capsule_image_size += sizeof(auth_context.auth) + + auth_context.sig_size; + + f = fopen(path, "w"); + if (!f) { + printf("cannot open %s\n", path); + goto err_3; + }
size = fwrite(&header, 1, sizeof(header), f); if (size < sizeof(header)) { printf("write failed (%zx)\n", size); - goto err_3; + goto err_4; }
capsule.version = 0x00000001; @@ -307,13 +526,13 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, size = fwrite(&capsule, 1, sizeof(capsule), f); if (size < (sizeof(capsule))) { printf("write failed (%zx)\n", size); - goto err_3; + goto err_4; } offset = sizeof(capsule) + sizeof(u64); size = fwrite(&offset, 1, sizeof(offset), f); if (size < sizeof(offset)) { printf("write failed (%zx)\n", size); - goto err_3; + goto err_4; }
image.version = 0x00000003; @@ -323,34 +542,61 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, image.reserved[1] = 0; image.reserved[2] = 0; image.update_image_size = bin_stat.st_size; + if (auth_context.sig_size) + image.update_image_size += sizeof(auth_context.auth) + + auth_context.sig_size; image.update_vendor_code_size = 0; /* none */ image.update_hardware_instance = instance; image.image_capsule_support = 0; + if (auth_context.sig_size) + image.image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION;
size = fwrite(&image, 1, sizeof(image), f); if (size < sizeof(image)) { printf("write failed (%zx)\n", size); - goto err_3; + goto err_4; } - size = fread(data, 1, bin_stat.st_size, g); - if (size < bin_stat.st_size) { - printf("read failed (%zx)\n", size); - goto err_3; + +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE) + if (auth_context.sig_size) { + size = fwrite(&auth_context.auth, 1, + sizeof(auth_context.auth), f); + if (size < sizeof(auth_context.auth)) { + printf("write failed (%zx)\n", size); + goto err_4; + } + size = fwrite(auth_context.sig_data, 1, + auth_context.sig_size, f); + if (size < auth_context.sig_size) { + printf("write failed (%zx)\n", size); + goto err_4; + } } +#endif + size = fwrite(data, 1, bin_stat.st_size, f); if (size < bin_stat.st_size) { printf("write failed (%zx)\n", size); - goto err_3; + goto err_4; }
fclose(f); fclose(g); free(data); +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE) + if (auth_context.sig_size) + OPENSSL_free(auth_context.sig_data); +#endif
return 0;
-err_3: +err_4: fclose(f); +err_3: +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE) + if (auth_context.sig_size) + OPENSSL_free(auth_context.sig_data); +#endif err_2: free(data); err_1: @@ -359,10 +605,6 @@ err_1: return -1; }
-/* - * Usage: - * $ mkeficapsule -f <firmware binary> <output file> - */ int main(int argc, char **argv) { char *file; @@ -370,6 +612,8 @@ int main(int argc, char **argv) char *dtb_file; efi_guid_t *guid; unsigned long index, instance; + uint64_t mcount; + char *privkey_file, *cert_file; int c, idx; int ret; bool overlay = false; @@ -380,8 +624,12 @@ int main(int argc, char **argv) guid = NULL; index = 0; instance = 0; + mcount = 0; + privkey_file = NULL; + cert_file = NULL; + dump_sig = 0; for (;;) { - c = getopt_long(argc, argv, "f:r:i:I:v:D:K:Oh", options, &idx); + c = getopt_long(argc, argv, opts_short, options, &idx); if (c == -1) break;
@@ -422,6 +670,28 @@ int main(int argc, char **argv) } dtb_file = optarg; break; +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE) + case 'P': + if (privkey_file) { + printf("Private Key already specified\n"); + return -1; + } + privkey_file = optarg; + break; + case 'C': + if (cert_file) { + printf("Certificate file already specified\n"); + return -1; + } + cert_file = optarg; + break; + case 'm': + mcount = strtoul(optarg, NULL, 0); + break; + case 'd': + dump_sig = 1; + break; +#endif case 'O': overlay = true; break; @@ -431,8 +701,12 @@ int main(int argc, char **argv) } }
- /* need a fit image file or raw image file */ - if (!file && !pkey_file && !dtb_file) { + /* check necessary parameters */ + if ((file && (!(optind < argc) || + (privkey_file && !cert_file) || + (!privkey_file && cert_file))) || + ((pkey_file && !dtb_file) || + (!pkey_file && dtb_file))) { print_usage(); exit(EXIT_FAILURE); } @@ -442,12 +716,12 @@ int main(int argc, char **argv) if (ret == -1) { printf("Adding public key to the dtb failed\n"); exit(EXIT_FAILURE); - } else { - exit(EXIT_SUCCESS); } }
- if (create_fwbin(argv[optind], file, guid, index, instance) + if (optind < argc && + create_fwbin(argv[optind], file, guid, index, instance, + mcount, privkey_file, cert_file) < 0) { printf("Creating firmware capsule failed\n"); exit(EXIT_FAILURE);

On 12.05.21 06:57, AKASHI Takahiro wrote:
With this enhancement, mkeficapsule will be able to create a capsule file with a signature which will be verified later by FMP's SetImage().
We will have to specify addtional command parameters: -monotonic-cout <count> : monotonic count -private-key <private key file> : private key file -certificate <certificate file> : certificate file Only when those parameters are given, a signature will be added to a capsule file.
Users are expected to maintain the monotonic count for each firmware image.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
tools/Makefile | 4 + tools/mkeficapsule.c | 324 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 303 insertions(+), 25 deletions(-)
diff --git a/tools/Makefile b/tools/Makefile index d020c55d6644..02eae0286e20 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -231,6 +231,10 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs hostprogs-$(CONFIG_ASN1_COMPILER) += asn1_compiler HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
+ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),) +HOSTLDLIBS_mkeficapsule += \
- $(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto")
I don't expect any user wants to install two tool versions in parallel.
The tool should always be able to add a signature. Adding a signature must be optional.
+endif mkeficapsule-objs := mkeficapsule.o $(LIBFDT_OBJS) hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c index de0a62898886..34ff1bdd82eb 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -18,7 +18,17 @@ #include <sys/stat.h> #include <sys/types.h>
-#include "fdt_host.h" +#include <linux/kconfig.h> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
+#include <openssl/asn1.h> +#include <openssl/bio.h> +#include <openssl/evp.h> +#include <openssl/err.h> +#include <openssl/pem.h> +#include <openssl/pkcs7.h> +#endif
+#include <linux/libfdt.h>
typedef __u8 u8; typedef __u16 u16; @@ -46,6 +56,13 @@ efi_guid_t efi_guid_image_type_uboot_fit = EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID; efi_guid_t efi_guid_image_type_uboot_raw = EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID; +efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
+static const char *opts_short = "f:r:i:I:v:D:K:P:C:m:dOh"; +#else +static const char *opts_short = "f:r:i:I:v:D:K:Oh"; +#endif
static struct option options[] = { {"fit", required_argument, NULL, 'f'}, @@ -54,6 +71,12 @@ static struct option options[] = { {"instance", required_argument, NULL, 'I'}, {"dtb", required_argument, NULL, 'D'}, {"public key", required_argument, NULL, 'K'}, +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
- {"private-key", required_argument, NULL, 'P'},
- {"certificate", required_argument, NULL, 'C'},
- {"monotonic-count", required_argument, NULL, 'm'},
These options should not be required.
- {"dump-sig", no_argument, NULL, 'd'},
+#endif {"overlay", no_argument, NULL, 'O'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0}, @@ -70,6 +93,12 @@ static void print_usage(void) "\t-I, --instance <instance> update hardware instance\n" "\t-K, --public-key <key file> public key esl file\n" "\t-D, --dtb <dtb file> dtb file\n" +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
"\t-P, --private-key <privkey file> private key file\n"
"\t-C, --certificate <cert file> signer's certificate file\n"
"\t-m, --monotonic-count <count> monotonic count\n"
"\t-d, --dump_sig dump signature (*.p7)\n"
+#endif "\t-O, --overlay the dtb file is an overlay\n" "\t-h, --help print a help message\n", tool_name); @@ -249,12 +278,167 @@ err: return ret; }
+struct auth_context {
- char *key_file;
- char *cert_file;
- u8 *image_data;
- size_t image_size;
- struct efi_firmware_image_authentication auth;
- u8 *sig_data;
- size_t sig_size;
+};
+static int dump_sig;
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
+static EVP_PKEY *fileio_read_pkey(const char *filename) +{
- EVP_PKEY *key = NULL;
- BIO *bio;
- bio = BIO_new_file(filename, "r");
- if (!bio)
goto out;
- key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
+out:
- BIO_free_all(bio);
- if (!key) {
printf("Can't load key from file '%s'\n", filename);
Please, you use fprintf(stderr,) for error messages.
ERR_print_errors_fp(stderr);
- }
- return key;
+}
+static X509 *fileio_read_cert(const char *filename) +{
- X509 *cert = NULL;
- BIO *bio;
- bio = BIO_new_file(filename, "r");
- if (!bio)
goto out;
- cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+out:
- BIO_free_all(bio);
- if (!cert) {
printf("Can't load certificate from file '%s'\n", filename);
fprintf(stderr,)
ERR_print_errors_fp(stderr);
- }
- return cert;
+}
+static int create_auth_data(struct auth_context *ctx) +{
- EVP_PKEY *key = NULL;
- X509 *cert = NULL;
- BIO *data_bio = NULL;
- const EVP_MD *md;
- PKCS7 *p7;
- int flags, ret = -1;
- OpenSSL_add_all_digests();
- OpenSSL_add_all_ciphers();
- ERR_load_crypto_strings();
- key = fileio_read_pkey(ctx->key_file);
- if (!key)
goto err;
- cert = fileio_read_cert(ctx->cert_file);
- if (!cert)
goto err;
- /*
* create a BIO, containing:
* * firmware image
* * monotonic count
* in this order!
* See EDK2's FmpAuthenticatedHandlerRsa2048Sha256()
*/
- data_bio = BIO_new(BIO_s_mem());
- BIO_write(data_bio, ctx->image_data, ctx->image_size);
- BIO_write(data_bio, &ctx->auth.monotonic_count,
sizeof(ctx->auth.monotonic_count));
- md = EVP_get_digestbyname("SHA256");
- if (!md)
goto err;
- /* create signature */
- /* TODO: maybe add PKCS7_NOATTR and PKCS7_NOSMIMECAP */
PKCS7_NOATTR is a value without any documentation in the code.
Please, replace variable names by a long text describing what it missing.
- flags = PKCS7_BINARY | PKCS7_DETACHED;
Those constants lack documentation in the code.
- p7 = PKCS7_sign(NULL, NULL, NULL, data_bio, flags | PKCS7_PARTIAL);
- if (!p7)
goto err;
- if (!PKCS7_sign_add_signer(p7, cert, key, md, flags))
goto err;
- if (!PKCS7_final(p7, data_bio, flags))
goto err;
- /* convert pkcs7 into DER */
- ctx->sig_data = NULL;
- ctx->sig_size = ASN1_item_i2d((ASN1_VALUE *)p7, &ctx->sig_data,
ASN1_ITEM_rptr(PKCS7));
- if (!ctx->sig_size)
goto err;
- /* fill auth_info */
- ctx->auth.auth_info.hdr.dwLength = sizeof(ctx->auth.auth_info)
+ ctx->sig_size;
- ctx->auth.auth_info.hdr.wRevision = WIN_CERT_REVISION_2_0;
- ctx->auth.auth_info.hdr.wCertificateType = WIN_CERT_TYPE_EFI_GUID;
- memcpy(&ctx->auth.auth_info.cert_type, &efi_guid_cert_type_pkcs7,
sizeof(efi_guid_cert_type_pkcs7));
- ret = 0;
+err:
- BIO_free_all(data_bio);
- EVP_PKEY_free(key);
- X509_free(cert);
- return ret;
+}
+static int dump_signature(const char *path, u8 *signature, size_t sig_size) +{
- char *sig_path;
- FILE *f;
- size_t size;
- int ret = -1;
- sig_path = malloc(strlen(path) + 3 + 1);
- if (!sig_path)
return ret;
- sprintf(sig_path, "%s.p7", path);
- f = fopen(sig_path, "w");
- if (!f)
goto err;
- size = fwrite(signature, 1, sig_size, f);
- if (size == sig_size)
ret = 0;
- fclose(f);
+err:
- free(sig_path);
- return ret;
+} +#endif
static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
unsigned long index, unsigned long instance)
unsigned long index, unsigned long instance,
uint64_t mcount, char *privkey_file, char *cert_file)
{ struct efi_capsule_header header; struct efi_firmware_management_capsule_header capsule; struct efi_firmware_management_capsule_image_header image;
- struct auth_context auth_context; FILE *f, *g; struct stat bin_stat; u8 *data;
@@ -266,6 +450,7 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, printf("\tbin: %s\n\ttype: %pUl\n", bin, guid); printf("\tindex: %ld\n\tinstance: %ld\n", index, instance); #endif
auth_context.sig_size = 0;
g = fopen(bin, "r"); if (!g) {
@@ -281,11 +466,36 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, printf("cannot allocate memory: %zx\n", (size_t)bin_stat.st_size); goto err_1; }
- f = fopen(path, "w");
- if (!f) {
printf("cannot open %s\n", path);
- size = fread(data, 1, bin_stat.st_size, g);
- if (size < bin_stat.st_size) {
goto err_2; }printf("read failed (%zx)\n", size);
- /* first, calculate signature to determine its size */
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
- if (privkey_file && cert_file) {
auth_context.key_file = privkey_file;
auth_context.cert_file = cert_file;
auth_context.auth.monotonic_count = mcount;
auth_context.image_data = data;
auth_context.image_size = bin_stat.st_size;
if (create_auth_data(&auth_context)) {
printf("Signing firmware image failed\n");
goto err_3;
}
if (dump_sig &&
dump_signature(path, auth_context.sig_data,
auth_context.sig_size)) {
printf("Creating signature file failed\n");
goto err_3;
}
- }
+#endif
- header.capsule_guid = efi_guid_fm_capsule; header.header_size = sizeof(header); /* TODO: The current implementation ignores flags */
@@ -294,11 +504,20 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, + sizeof(capsule) + sizeof(u64) + sizeof(image) + bin_stat.st_size;
if (auth_context.sig_size)
header.capsule_image_size += sizeof(auth_context.auth)
+ auth_context.sig_size;
f = fopen(path, "w");
if (!f) {
printf("cannot open %s\n", path);
goto err_3;
}
size = fwrite(&header, 1, sizeof(header), f); if (size < sizeof(header)) { printf("write failed (%zx)\n", size);
goto err_3;
goto err_4;
}
capsule.version = 0x00000001;
@@ -307,13 +526,13 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, size = fwrite(&capsule, 1, sizeof(capsule), f); if (size < (sizeof(capsule))) { printf("write failed (%zx)\n", size);
goto err_3;
} offset = sizeof(capsule) + sizeof(u64); size = fwrite(&offset, 1, sizeof(offset), f); if (size < sizeof(offset)) { printf("write failed (%zx)\n", size);goto err_4;
goto err_3;
goto err_4;
}
image.version = 0x00000003;
@@ -323,34 +542,61 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, image.reserved[1] = 0; image.reserved[2] = 0; image.update_image_size = bin_stat.st_size;
if (auth_context.sig_size)
image.update_image_size += sizeof(auth_context.auth)
+ auth_context.sig_size;
image.update_vendor_code_size = 0; /* none */ image.update_hardware_instance = instance; image.image_capsule_support = 0;
if (auth_context.sig_size)
image.image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION;
size = fwrite(&image, 1, sizeof(image), f); if (size < sizeof(image)) { printf("write failed (%zx)\n", size);
goto err_3;
}goto err_4;
- size = fread(data, 1, bin_stat.st_size, g);
- if (size < bin_stat.st_size) {
printf("read failed (%zx)\n", size);
goto err_3;
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
We don't want to use #if if avoidable.
- if (auth_context.sig_size) {
size = fwrite(&auth_context.auth, 1,
sizeof(auth_context.auth), f);
if (size < sizeof(auth_context.auth)) {
printf("write failed (%zx)\n", size);
goto err_4;
}
size = fwrite(auth_context.sig_data, 1,
auth_context.sig_size, f);
if (size < auth_context.sig_size) {
printf("write failed (%zx)\n", size);
goto err_4;
}}
+#endif
- size = fwrite(data, 1, bin_stat.st_size, f); if (size < bin_stat.st_size) { printf("write failed (%zx)\n", size);
goto err_3;
goto err_4;
}
fclose(f); fclose(g); free(data);
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
- if (auth_context.sig_size)
OPENSSL_free(auth_context.sig_data);
+#endif
return 0;
-err_3: +err_4: fclose(f); +err_3: +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
- if (auth_context.sig_size)
OPENSSL_free(auth_context.sig_data);
+#endif err_2: free(data); err_1: @@ -359,10 +605,6 @@ err_1: return -1; }
-/*
- Usage:
- $ mkeficapsule -f <firmware binary> <output file>
- */
int main(int argc, char **argv) { char *file; @@ -370,6 +612,8 @@ int main(int argc, char **argv) char *dtb_file; efi_guid_t *guid; unsigned long index, instance;
- uint64_t mcount;
- char *privkey_file, *cert_file; int c, idx; int ret; bool overlay = false;
@@ -380,8 +624,12 @@ int main(int argc, char **argv) guid = NULL; index = 0; instance = 0;
- mcount = 0;
- privkey_file = NULL;
- cert_file = NULL;
- dump_sig = 0; for (;;) {
c = getopt_long(argc, argv, "f:r:i:I:v:D:K:Oh", options, &idx);
if (c == -1) break;c = getopt_long(argc, argv, opts_short, options, &idx);
@@ -422,6 +670,28 @@ int main(int argc, char **argv) } dtb_file = optarg; break; +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
Best regards
Heinrich
case 'P':
if (privkey_file) {
printf("Private Key already specified\n");
return -1;
}
privkey_file = optarg;
break;
case 'C':
if (cert_file) {
printf("Certificate file already specified\n");
return -1;
}
cert_file = optarg;
break;
case 'm':
mcount = strtoul(optarg, NULL, 0);
break;
case 'd':
dump_sig = 1;
break;
+#endif case 'O': overlay = true; break; @@ -431,8 +701,12 @@ int main(int argc, char **argv) } }
- /* need a fit image file or raw image file */
- if (!file && !pkey_file && !dtb_file) {
- /* check necessary parameters */
- if ((file && (!(optind < argc) ||
(privkey_file && !cert_file) ||
(!privkey_file && cert_file))) ||
((pkey_file && !dtb_file) ||
print_usage(); exit(EXIT_FAILURE); }(!pkey_file && dtb_file))) {
@@ -442,12 +716,12 @@ int main(int argc, char **argv) if (ret == -1) { printf("Adding public key to the dtb failed\n"); exit(EXIT_FAILURE);
} else {
exit(EXIT_SUCCESS);
} }
if (create_fwbin(argv[optind], file, guid, index, instance)
- if (optind < argc &&
create_fwbin(argv[optind], file, guid, index, instance,
printf("Creating firmware capsule failed\n"); exit(EXIT_FAILURE);mcount, privkey_file, cert_file) < 0) {

On Wed, May 12, 2021 at 10:56:41AM +0200, Heinrich Schuchardt wrote:
On 12.05.21 06:57, AKASHI Takahiro wrote:
With this enhancement, mkeficapsule will be able to create a capsule file with a signature which will be verified later by FMP's SetImage().
We will have to specify addtional command parameters: -monotonic-cout <count> : monotonic count -private-key <private key file> : private key file -certificate <certificate file> : certificate file Only when those parameters are given, a signature will be added to a capsule file.
Users are expected to maintain the monotonic count for each firmware image.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
tools/Makefile | 4 + tools/mkeficapsule.c | 324 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 303 insertions(+), 25 deletions(-)
diff --git a/tools/Makefile b/tools/Makefile index d020c55d6644..02eae0286e20 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -231,6 +231,10 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs hostprogs-$(CONFIG_ASN1_COMPILER) += asn1_compiler HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
+ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),) +HOSTLDLIBS_mkeficapsule += \
- $(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto")
I don't expect any user wants to install two tool versions in parallel.
The tool should always be able to add a signature. Adding a signature must be optional.
It seems to me that those two statements mutually contradict. Or do you intend to say that we should have a separate kconfig option to enable/disable signing feature in mkeficapsule?
If so, I can agree.
In either way, we should have an option to turn on/off this functionality as not all users use signed capsules.
+endif mkeficapsule-objs := mkeficapsule.o $(LIBFDT_OBJS) hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c index de0a62898886..34ff1bdd82eb 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -18,7 +18,17 @@ #include <sys/stat.h> #include <sys/types.h>
-#include "fdt_host.h" +#include <linux/kconfig.h> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
+#include <openssl/asn1.h> +#include <openssl/bio.h> +#include <openssl/evp.h> +#include <openssl/err.h> +#include <openssl/pem.h> +#include <openssl/pkcs7.h> +#endif
+#include <linux/libfdt.h>
typedef __u8 u8; typedef __u16 u16; @@ -46,6 +56,13 @@ efi_guid_t efi_guid_image_type_uboot_fit = EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID; efi_guid_t efi_guid_image_type_uboot_raw = EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID; +efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
+static const char *opts_short = "f:r:i:I:v:D:K:P:C:m:dOh"; +#else +static const char *opts_short = "f:r:i:I:v:D:K:Oh"; +#endif
static struct option options[] = { {"fit", required_argument, NULL, 'f'}, @@ -54,6 +71,12 @@ static struct option options[] = { {"instance", required_argument, NULL, 'I'}, {"dtb", required_argument, NULL, 'D'}, {"public key", required_argument, NULL, 'K'}, +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
- {"private-key", required_argument, NULL, 'P'},
- {"certificate", required_argument, NULL, 'C'},
- {"monotonic-count", required_argument, NULL, 'm'},
These options should not be required.
I don't get you. What do you mean?
- {"dump-sig", no_argument, NULL, 'd'},
+#endif {"overlay", no_argument, NULL, 'O'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0}, @@ -70,6 +93,12 @@ static void print_usage(void) "\t-I, --instance <instance> update hardware instance\n" "\t-K, --public-key <key file> public key esl file\n" "\t-D, --dtb <dtb file> dtb file\n" +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
"\t-P, --private-key <privkey file> private key file\n"
"\t-C, --certificate <cert file> signer's certificate file\n"
"\t-m, --monotonic-count <count> monotonic count\n"
"\t-d, --dump_sig dump signature (*.p7)\n"
+#endif "\t-O, --overlay the dtb file is an overlay\n" "\t-h, --help print a help message\n", tool_name); @@ -249,12 +278,167 @@ err: return ret; }
+struct auth_context {
- char *key_file;
- char *cert_file;
- u8 *image_data;
- size_t image_size;
- struct efi_firmware_image_authentication auth;
- u8 *sig_data;
- size_t sig_size;
+};
+static int dump_sig;
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
+static EVP_PKEY *fileio_read_pkey(const char *filename) +{
- EVP_PKEY *key = NULL;
- BIO *bio;
- bio = BIO_new_file(filename, "r");
- if (!bio)
goto out;
- key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
+out:
- BIO_free_all(bio);
- if (!key) {
printf("Can't load key from file '%s'\n", filename);
Please, you use fprintf(stderr,) for error messages.
ERR_print_errors_fp(stderr);
- }
- return key;
+}
+static X509 *fileio_read_cert(const char *filename) +{
- X509 *cert = NULL;
- BIO *bio;
- bio = BIO_new_file(filename, "r");
- if (!bio)
goto out;
- cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+out:
- BIO_free_all(bio);
- if (!cert) {
printf("Can't load certificate from file '%s'\n", filename);
fprintf(stderr,)
ERR_print_errors_fp(stderr);
- }
- return cert;
+}
+static int create_auth_data(struct auth_context *ctx) +{
- EVP_PKEY *key = NULL;
- X509 *cert = NULL;
- BIO *data_bio = NULL;
- const EVP_MD *md;
- PKCS7 *p7;
- int flags, ret = -1;
- OpenSSL_add_all_digests();
- OpenSSL_add_all_ciphers();
- ERR_load_crypto_strings();
- key = fileio_read_pkey(ctx->key_file);
- if (!key)
goto err;
- cert = fileio_read_cert(ctx->cert_file);
- if (!cert)
goto err;
- /*
* create a BIO, containing:
* * firmware image
* * monotonic count
* in this order!
* See EDK2's FmpAuthenticatedHandlerRsa2048Sha256()
*/
- data_bio = BIO_new(BIO_s_mem());
- BIO_write(data_bio, ctx->image_data, ctx->image_size);
- BIO_write(data_bio, &ctx->auth.monotonic_count,
sizeof(ctx->auth.monotonic_count));
- md = EVP_get_digestbyname("SHA256");
- if (!md)
goto err;
- /* create signature */
- /* TODO: maybe add PKCS7_NOATTR and PKCS7_NOSMIMECAP */
PKCS7_NOATTR is a value without any documentation in the code.
Nak. Those macros are part of openssl library. See openssl/pkcs7.h.
Please, replace variable names by a long text describing what it missing.
- flags = PKCS7_BINARY | PKCS7_DETACHED;
Those constants lack documentation in the code.
Nak again.
- p7 = PKCS7_sign(NULL, NULL, NULL, data_bio, flags | PKCS7_PARTIAL);
- if (!p7)
goto err;
- if (!PKCS7_sign_add_signer(p7, cert, key, md, flags))
goto err;
- if (!PKCS7_final(p7, data_bio, flags))
goto err;
- /* convert pkcs7 into DER */
- ctx->sig_data = NULL;
- ctx->sig_size = ASN1_item_i2d((ASN1_VALUE *)p7, &ctx->sig_data,
ASN1_ITEM_rptr(PKCS7));
- if (!ctx->sig_size)
goto err;
- /* fill auth_info */
- ctx->auth.auth_info.hdr.dwLength = sizeof(ctx->auth.auth_info)
+ ctx->sig_size;
- ctx->auth.auth_info.hdr.wRevision = WIN_CERT_REVISION_2_0;
- ctx->auth.auth_info.hdr.wCertificateType = WIN_CERT_TYPE_EFI_GUID;
- memcpy(&ctx->auth.auth_info.cert_type, &efi_guid_cert_type_pkcs7,
sizeof(efi_guid_cert_type_pkcs7));
- ret = 0;
+err:
- BIO_free_all(data_bio);
- EVP_PKEY_free(key);
- X509_free(cert);
- return ret;
+}
+static int dump_signature(const char *path, u8 *signature, size_t sig_size) +{
- char *sig_path;
- FILE *f;
- size_t size;
- int ret = -1;
- sig_path = malloc(strlen(path) + 3 + 1);
- if (!sig_path)
return ret;
- sprintf(sig_path, "%s.p7", path);
- f = fopen(sig_path, "w");
- if (!f)
goto err;
- size = fwrite(signature, 1, sig_size, f);
- if (size == sig_size)
ret = 0;
- fclose(f);
+err:
- free(sig_path);
- return ret;
+} +#endif
static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
unsigned long index, unsigned long instance)
unsigned long index, unsigned long instance,
uint64_t mcount, char *privkey_file, char *cert_file)
{ struct efi_capsule_header header; struct efi_firmware_management_capsule_header capsule; struct efi_firmware_management_capsule_image_header image;
- struct auth_context auth_context; FILE *f, *g; struct stat bin_stat; u8 *data;
@@ -266,6 +450,7 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, printf("\tbin: %s\n\ttype: %pUl\n", bin, guid); printf("\tindex: %ld\n\tinstance: %ld\n", index, instance); #endif
auth_context.sig_size = 0;
g = fopen(bin, "r"); if (!g) {
@@ -281,11 +466,36 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, printf("cannot allocate memory: %zx\n", (size_t)bin_stat.st_size); goto err_1; }
- f = fopen(path, "w");
- if (!f) {
printf("cannot open %s\n", path);
- size = fread(data, 1, bin_stat.st_size, g);
- if (size < bin_stat.st_size) {
goto err_2; }printf("read failed (%zx)\n", size);
- /* first, calculate signature to determine its size */
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
- if (privkey_file && cert_file) {
auth_context.key_file = privkey_file;
auth_context.cert_file = cert_file;
auth_context.auth.monotonic_count = mcount;
auth_context.image_data = data;
auth_context.image_size = bin_stat.st_size;
if (create_auth_data(&auth_context)) {
printf("Signing firmware image failed\n");
goto err_3;
}
if (dump_sig &&
dump_signature(path, auth_context.sig_data,
auth_context.sig_size)) {
printf("Creating signature file failed\n");
goto err_3;
}
- }
+#endif
- header.capsule_guid = efi_guid_fm_capsule; header.header_size = sizeof(header); /* TODO: The current implementation ignores flags */
@@ -294,11 +504,20 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, + sizeof(capsule) + sizeof(u64) + sizeof(image) + bin_stat.st_size;
if (auth_context.sig_size)
header.capsule_image_size += sizeof(auth_context.auth)
+ auth_context.sig_size;
f = fopen(path, "w");
if (!f) {
printf("cannot open %s\n", path);
goto err_3;
}
size = fwrite(&header, 1, sizeof(header), f); if (size < sizeof(header)) { printf("write failed (%zx)\n", size);
goto err_3;
goto err_4;
}
capsule.version = 0x00000001;
@@ -307,13 +526,13 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, size = fwrite(&capsule, 1, sizeof(capsule), f); if (size < (sizeof(capsule))) { printf("write failed (%zx)\n", size);
goto err_3;
} offset = sizeof(capsule) + sizeof(u64); size = fwrite(&offset, 1, sizeof(offset), f); if (size < sizeof(offset)) { printf("write failed (%zx)\n", size);goto err_4;
goto err_3;
goto err_4;
}
image.version = 0x00000003;
@@ -323,34 +542,61 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, image.reserved[1] = 0; image.reserved[2] = 0; image.update_image_size = bin_stat.st_size;
if (auth_context.sig_size)
image.update_image_size += sizeof(auth_context.auth)
+ auth_context.sig_size;
image.update_vendor_code_size = 0; /* none */ image.update_hardware_instance = instance; image.image_capsule_support = 0;
if (auth_context.sig_size)
image.image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION;
size = fwrite(&image, 1, sizeof(image), f); if (size < sizeof(image)) { printf("write failed (%zx)\n", size);
goto err_3;
}goto err_4;
- size = fread(data, 1, bin_stat.st_size, g);
- if (size < bin_stat.st_size) {
printf("read failed (%zx)\n", size);
goto err_3;
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
We don't want to use #if if avoidable.
For this specific chunk of code, we can remove #ifdef, but we should not remove #ifdef elsewhere.
-Takahiro Akashi
- if (auth_context.sig_size) {
size = fwrite(&auth_context.auth, 1,
sizeof(auth_context.auth), f);
if (size < sizeof(auth_context.auth)) {
printf("write failed (%zx)\n", size);
goto err_4;
}
size = fwrite(auth_context.sig_data, 1,
auth_context.sig_size, f);
if (size < auth_context.sig_size) {
printf("write failed (%zx)\n", size);
goto err_4;
}}
+#endif
- size = fwrite(data, 1, bin_stat.st_size, f); if (size < bin_stat.st_size) { printf("write failed (%zx)\n", size);
goto err_3;
goto err_4;
}
fclose(f); fclose(g); free(data);
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
- if (auth_context.sig_size)
OPENSSL_free(auth_context.sig_data);
+#endif
return 0;
-err_3: +err_4: fclose(f); +err_3: +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
- if (auth_context.sig_size)
OPENSSL_free(auth_context.sig_data);
+#endif err_2: free(data); err_1: @@ -359,10 +605,6 @@ err_1: return -1; }
-/*
- Usage:
- $ mkeficapsule -f <firmware binary> <output file>
- */
int main(int argc, char **argv) { char *file; @@ -370,6 +612,8 @@ int main(int argc, char **argv) char *dtb_file; efi_guid_t *guid; unsigned long index, instance;
- uint64_t mcount;
- char *privkey_file, *cert_file; int c, idx; int ret; bool overlay = false;
@@ -380,8 +624,12 @@ int main(int argc, char **argv) guid = NULL; index = 0; instance = 0;
- mcount = 0;
- privkey_file = NULL;
- cert_file = NULL;
- dump_sig = 0; for (;;) {
c = getopt_long(argc, argv, "f:r:i:I:v:D:K:Oh", options, &idx);
if (c == -1) break;c = getopt_long(argc, argv, opts_short, options, &idx);
@@ -422,6 +670,28 @@ int main(int argc, char **argv) } dtb_file = optarg; break; +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
Best regards
Heinrich
case 'P':
if (privkey_file) {
printf("Private Key already specified\n");
return -1;
}
privkey_file = optarg;
break;
case 'C':
if (cert_file) {
printf("Certificate file already specified\n");
return -1;
}
cert_file = optarg;
break;
case 'm':
mcount = strtoul(optarg, NULL, 0);
break;
case 'd':
dump_sig = 1;
break;
+#endif case 'O': overlay = true; break; @@ -431,8 +701,12 @@ int main(int argc, char **argv) } }
- /* need a fit image file or raw image file */
- if (!file && !pkey_file && !dtb_file) {
- /* check necessary parameters */
- if ((file && (!(optind < argc) ||
(privkey_file && !cert_file) ||
(!privkey_file && cert_file))) ||
((pkey_file && !dtb_file) ||
print_usage(); exit(EXIT_FAILURE); }(!pkey_file && dtb_file))) {
@@ -442,12 +716,12 @@ int main(int argc, char **argv) if (ret == -1) { printf("Adding public key to the dtb failed\n"); exit(EXIT_FAILURE);
} else {
exit(EXIT_SUCCESS);
} }
if (create_fwbin(argv[optind], file, guid, index, instance)
- if (optind < argc &&
create_fwbin(argv[optind], file, guid, index, instance,
printf("Creating firmware capsule failed\n"); exit(EXIT_FAILURE);mcount, privkey_file, cert_file) < 0) {

On 5/13/21 5:08 AM, AKASHI Takahiro wrote:
On Wed, May 12, 2021 at 10:56:41AM +0200, Heinrich Schuchardt wrote:
On 12.05.21 06:57, AKASHI Takahiro wrote:
With this enhancement, mkeficapsule will be able to create a capsule file with a signature which will be verified later by FMP's SetImage().
We will have to specify addtional command parameters: -monotonic-cout <count> : monotonic count -private-key <private key file> : private key file -certificate <certificate file> : certificate file Only when those parameters are given, a signature will be added to a capsule file.
Users are expected to maintain the monotonic count for each firmware image.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
tools/Makefile | 4 + tools/mkeficapsule.c | 324 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 303 insertions(+), 25 deletions(-)
diff --git a/tools/Makefile b/tools/Makefile index d020c55d6644..02eae0286e20 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -231,6 +231,10 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs hostprogs-$(CONFIG_ASN1_COMPILER) += asn1_compiler HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
+ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),) +HOSTLDLIBS_mkeficapsule += \
- $(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto")
I don't expect any user wants to install two tool versions in parallel.
The tool should always be able to add a signature. Adding a signature must be optional.
It seems to me that those two statements mutually contradict. Or do you intend to say that we should have a separate kconfig option to enable/disable signing feature in mkeficapsule?
If so, I can agree.
In either way, we should have an option to turn on/off this functionality as not all users use signed capsules.
I want to have a single binary to distribute with Linux distros (e.g. Debian/Ubuntu package u-boot-tools).
This should allow both
- create signed capsules - create unsigned capsules
The user shall select signing via command line parameters.
Support for signing via the tool shall not depend on board Kconfig parameters.
Best regards
Heinrich
+endif mkeficapsule-objs := mkeficapsule.o $(LIBFDT_OBJS) hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c index de0a62898886..34ff1bdd82eb 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -18,7 +18,17 @@ #include <sys/stat.h> #include <sys/types.h>
-#include "fdt_host.h" +#include <linux/kconfig.h> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
+#include <openssl/asn1.h> +#include <openssl/bio.h> +#include <openssl/evp.h> +#include <openssl/err.h> +#include <openssl/pem.h> +#include <openssl/pkcs7.h> +#endif
+#include <linux/libfdt.h>
typedef __u8 u8; typedef __u16 u16; @@ -46,6 +56,13 @@ efi_guid_t efi_guid_image_type_uboot_fit = EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID; efi_guid_t efi_guid_image_type_uboot_raw = EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID; +efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
+static const char *opts_short = "f:r:i:I:v:D:K:P:C:m:dOh"; +#else +static const char *opts_short = "f:r:i:I:v:D:K:Oh"; +#endif
static struct option options[] = { {"fit", required_argument, NULL, 'f'}, @@ -54,6 +71,12 @@ static struct option options[] = { {"instance", required_argument, NULL, 'I'}, {"dtb", required_argument, NULL, 'D'}, {"public key", required_argument, NULL, 'K'}, +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
- {"private-key", required_argument, NULL, 'P'},
- {"certificate", required_argument, NULL, 'C'},
- {"monotonic-count", required_argument, NULL, 'm'},
These options should not be required.
I don't get you. What do you mean?
- {"dump-sig", no_argument, NULL, 'd'},
+#endif {"overlay", no_argument, NULL, 'O'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0}, @@ -70,6 +93,12 @@ static void print_usage(void) "\t-I, --instance <instance> update hardware instance\n" "\t-K, --public-key <key file> public key esl file\n" "\t-D, --dtb <dtb file> dtb file\n" +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
"\t-P, --private-key <privkey file> private key file\n"
"\t-C, --certificate <cert file> signer's certificate file\n"
"\t-m, --monotonic-count <count> monotonic count\n"
"\t-d, --dump_sig dump signature (*.p7)\n"
+#endif "\t-O, --overlay the dtb file is an overlay\n" "\t-h, --help print a help message\n", tool_name); @@ -249,12 +278,167 @@ err: return ret; }
+struct auth_context {
- char *key_file;
- char *cert_file;
- u8 *image_data;
- size_t image_size;
- struct efi_firmware_image_authentication auth;
- u8 *sig_data;
- size_t sig_size;
+};
+static int dump_sig;
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
+static EVP_PKEY *fileio_read_pkey(const char *filename) +{
- EVP_PKEY *key = NULL;
- BIO *bio;
- bio = BIO_new_file(filename, "r");
- if (!bio)
goto out;
- key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
+out:
- BIO_free_all(bio);
- if (!key) {
printf("Can't load key from file '%s'\n", filename);
Please, you use fprintf(stderr,) for error messages.
ERR_print_errors_fp(stderr);
- }
- return key;
+}
+static X509 *fileio_read_cert(const char *filename) +{
- X509 *cert = NULL;
- BIO *bio;
- bio = BIO_new_file(filename, "r");
- if (!bio)
goto out;
- cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+out:
- BIO_free_all(bio);
- if (!cert) {
printf("Can't load certificate from file '%s'\n", filename);
fprintf(stderr,)
ERR_print_errors_fp(stderr);
- }
- return cert;
+}
+static int create_auth_data(struct auth_context *ctx) +{
- EVP_PKEY *key = NULL;
- X509 *cert = NULL;
- BIO *data_bio = NULL;
- const EVP_MD *md;
- PKCS7 *p7;
- int flags, ret = -1;
- OpenSSL_add_all_digests();
- OpenSSL_add_all_ciphers();
- ERR_load_crypto_strings();
- key = fileio_read_pkey(ctx->key_file);
- if (!key)
goto err;
- cert = fileio_read_cert(ctx->cert_file);
- if (!cert)
goto err;
- /*
* create a BIO, containing:
* * firmware image
* * monotonic count
* in this order!
* See EDK2's FmpAuthenticatedHandlerRsa2048Sha256()
*/
- data_bio = BIO_new(BIO_s_mem());
- BIO_write(data_bio, ctx->image_data, ctx->image_size);
- BIO_write(data_bio, &ctx->auth.monotonic_count,
sizeof(ctx->auth.monotonic_count));
- md = EVP_get_digestbyname("SHA256");
- if (!md)
goto err;
- /* create signature */
- /* TODO: maybe add PKCS7_NOATTR and PKCS7_NOSMIMECAP */
PKCS7_NOATTR is a value without any documentation in the code.
Nak. Those macros are part of openssl library. See openssl/pkcs7.h.
Please, replace variable names by a long text describing what it missing.
- flags = PKCS7_BINARY | PKCS7_DETACHED;
Those constants lack documentation in the code.
Nak again.
- p7 = PKCS7_sign(NULL, NULL, NULL, data_bio, flags | PKCS7_PARTIAL);
- if (!p7)
goto err;
- if (!PKCS7_sign_add_signer(p7, cert, key, md, flags))
goto err;
- if (!PKCS7_final(p7, data_bio, flags))
goto err;
- /* convert pkcs7 into DER */
- ctx->sig_data = NULL;
- ctx->sig_size = ASN1_item_i2d((ASN1_VALUE *)p7, &ctx->sig_data,
ASN1_ITEM_rptr(PKCS7));
- if (!ctx->sig_size)
goto err;
- /* fill auth_info */
- ctx->auth.auth_info.hdr.dwLength = sizeof(ctx->auth.auth_info)
+ ctx->sig_size;
- ctx->auth.auth_info.hdr.wRevision = WIN_CERT_REVISION_2_0;
- ctx->auth.auth_info.hdr.wCertificateType = WIN_CERT_TYPE_EFI_GUID;
- memcpy(&ctx->auth.auth_info.cert_type, &efi_guid_cert_type_pkcs7,
sizeof(efi_guid_cert_type_pkcs7));
- ret = 0;
+err:
- BIO_free_all(data_bio);
- EVP_PKEY_free(key);
- X509_free(cert);
- return ret;
+}
+static int dump_signature(const char *path, u8 *signature, size_t sig_size) +{
- char *sig_path;
- FILE *f;
- size_t size;
- int ret = -1;
- sig_path = malloc(strlen(path) + 3 + 1);
- if (!sig_path)
return ret;
- sprintf(sig_path, "%s.p7", path);
- f = fopen(sig_path, "w");
- if (!f)
goto err;
- size = fwrite(signature, 1, sig_size, f);
- if (size == sig_size)
ret = 0;
- fclose(f);
+err:
- free(sig_path);
- return ret;
+} +#endif
- static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
unsigned long index, unsigned long instance)
unsigned long index, unsigned long instance,
{ struct efi_capsule_header header; struct efi_firmware_management_capsule_header capsule; struct efi_firmware_management_capsule_image_header image;uint64_t mcount, char *privkey_file, char *cert_file)
- struct auth_context auth_context; FILE *f, *g; struct stat bin_stat; u8 *data;
@@ -266,6 +450,7 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, printf("\tbin: %s\n\ttype: %pUl\n", bin, guid); printf("\tindex: %ld\n\tinstance: %ld\n", index, instance); #endif
auth_context.sig_size = 0;
g = fopen(bin, "r"); if (!g) {
@@ -281,11 +466,36 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, printf("cannot allocate memory: %zx\n", (size_t)bin_stat.st_size); goto err_1; }
- f = fopen(path, "w");
- if (!f) {
printf("cannot open %s\n", path);
- size = fread(data, 1, bin_stat.st_size, g);
- if (size < bin_stat.st_size) {
goto err_2; }printf("read failed (%zx)\n", size);
- /* first, calculate signature to determine its size */
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
- if (privkey_file && cert_file) {
auth_context.key_file = privkey_file;
auth_context.cert_file = cert_file;
auth_context.auth.monotonic_count = mcount;
auth_context.image_data = data;
auth_context.image_size = bin_stat.st_size;
if (create_auth_data(&auth_context)) {
printf("Signing firmware image failed\n");
goto err_3;
}
if (dump_sig &&
dump_signature(path, auth_context.sig_data,
auth_context.sig_size)) {
printf("Creating signature file failed\n");
goto err_3;
}
- }
+#endif
- header.capsule_guid = efi_guid_fm_capsule; header.header_size = sizeof(header); /* TODO: The current implementation ignores flags */
@@ -294,11 +504,20 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, + sizeof(capsule) + sizeof(u64) + sizeof(image) + bin_stat.st_size;
if (auth_context.sig_size)
header.capsule_image_size += sizeof(auth_context.auth)
+ auth_context.sig_size;
f = fopen(path, "w");
if (!f) {
printf("cannot open %s\n", path);
goto err_3;
}
size = fwrite(&header, 1, sizeof(header), f); if (size < sizeof(header)) { printf("write failed (%zx)\n", size);
goto err_3;
goto err_4;
}
capsule.version = 0x00000001;
@@ -307,13 +526,13 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, size = fwrite(&capsule, 1, sizeof(capsule), f); if (size < (sizeof(capsule))) { printf("write failed (%zx)\n", size);
goto err_3;
} offset = sizeof(capsule) + sizeof(u64); size = fwrite(&offset, 1, sizeof(offset), f); if (size < sizeof(offset)) { printf("write failed (%zx)\n", size);goto err_4;
goto err_3;
goto err_4;
}
image.version = 0x00000003;
@@ -323,34 +542,61 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, image.reserved[1] = 0; image.reserved[2] = 0; image.update_image_size = bin_stat.st_size;
if (auth_context.sig_size)
image.update_image_size += sizeof(auth_context.auth)
+ auth_context.sig_size;
image.update_vendor_code_size = 0; /* none */ image.update_hardware_instance = instance; image.image_capsule_support = 0;
if (auth_context.sig_size)
image.image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION;
size = fwrite(&image, 1, sizeof(image), f); if (size < sizeof(image)) { printf("write failed (%zx)\n", size);
goto err_3;
}goto err_4;
- size = fread(data, 1, bin_stat.st_size, g);
- if (size < bin_stat.st_size) {
printf("read failed (%zx)\n", size);
goto err_3;
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
We don't want to use #if if avoidable.
For this specific chunk of code, we can remove #ifdef, but we should not remove #ifdef elsewhere.
-Takahiro Akashi
- if (auth_context.sig_size) {
size = fwrite(&auth_context.auth, 1,
sizeof(auth_context.auth), f);
if (size < sizeof(auth_context.auth)) {
printf("write failed (%zx)\n", size);
goto err_4;
}
size = fwrite(auth_context.sig_data, 1,
auth_context.sig_size, f);
if (size < auth_context.sig_size) {
printf("write failed (%zx)\n", size);
goto err_4;
}}
+#endif
- size = fwrite(data, 1, bin_stat.st_size, f); if (size < bin_stat.st_size) { printf("write failed (%zx)\n", size);
goto err_3;
goto err_4;
}
fclose(f); fclose(g); free(data);
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
- if (auth_context.sig_size)
OPENSSL_free(auth_context.sig_data);
+#endif
return 0;
-err_3: +err_4: fclose(f); +err_3: +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
- if (auth_context.sig_size)
OPENSSL_free(auth_context.sig_data);
+#endif err_2: free(data); err_1: @@ -359,10 +605,6 @@ err_1: return -1; }
-/*
- Usage:
- $ mkeficapsule -f <firmware binary> <output file>
- */ int main(int argc, char **argv) { char *file;
@@ -370,6 +612,8 @@ int main(int argc, char **argv) char *dtb_file; efi_guid_t *guid; unsigned long index, instance;
- uint64_t mcount;
- char *privkey_file, *cert_file; int c, idx; int ret; bool overlay = false;
@@ -380,8 +624,12 @@ int main(int argc, char **argv) guid = NULL; index = 0; instance = 0;
- mcount = 0;
- privkey_file = NULL;
- cert_file = NULL;
- dump_sig = 0; for (;;) {
c = getopt_long(argc, argv, "f:r:i:I:v:D:K:Oh", options, &idx);
if (c == -1) break;c = getopt_long(argc, argv, opts_short, options, &idx);
@@ -422,6 +670,28 @@ int main(int argc, char **argv) } dtb_file = optarg; break; +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
Best regards
Heinrich
case 'P':
if (privkey_file) {
printf("Private Key already specified\n");
return -1;
}
privkey_file = optarg;
break;
case 'C':
if (cert_file) {
printf("Certificate file already specified\n");
return -1;
}
cert_file = optarg;
break;
case 'm':
mcount = strtoul(optarg, NULL, 0);
break;
case 'd':
dump_sig = 1;
break;
+#endif case 'O': overlay = true; break; @@ -431,8 +701,12 @@ int main(int argc, char **argv) } }
- /* need a fit image file or raw image file */
- if (!file && !pkey_file && !dtb_file) {
- /* check necessary parameters */
- if ((file && (!(optind < argc) ||
(privkey_file && !cert_file) ||
(!privkey_file && cert_file))) ||
((pkey_file && !dtb_file) ||
print_usage(); exit(EXIT_FAILURE); }(!pkey_file && dtb_file))) {
@@ -442,12 +716,12 @@ int main(int argc, char **argv) if (ret == -1) { printf("Adding public key to the dtb failed\n"); exit(EXIT_FAILURE);
} else {
exit(EXIT_SUCCESS);
} }
if (create_fwbin(argv[optind], file, guid, index, instance)
- if (optind < argc &&
create_fwbin(argv[optind], file, guid, index, instance,
printf("Creating firmware capsule failed\n"); exit(EXIT_FAILURE);mcount, privkey_file, cert_file) < 0) {

On Thu, May 13, 2021 at 06:22:39AM +0200, Heinrich Schuchardt wrote:
On 5/13/21 5:08 AM, AKASHI Takahiro wrote:
On Wed, May 12, 2021 at 10:56:41AM +0200, Heinrich Schuchardt wrote:
On 12.05.21 06:57, AKASHI Takahiro wrote:
With this enhancement, mkeficapsule will be able to create a capsule file with a signature which will be verified later by FMP's SetImage().
We will have to specify addtional command parameters: -monotonic-cout <count> : monotonic count -private-key <private key file> : private key file -certificate <certificate file> : certificate file Only when those parameters are given, a signature will be added to a capsule file.
Users are expected to maintain the monotonic count for each firmware image.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
tools/Makefile | 4 + tools/mkeficapsule.c | 324 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 303 insertions(+), 25 deletions(-)
diff --git a/tools/Makefile b/tools/Makefile index d020c55d6644..02eae0286e20 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -231,6 +231,10 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs hostprogs-$(CONFIG_ASN1_COMPILER) += asn1_compiler HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
+ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),) +HOSTLDLIBS_mkeficapsule += \
- $(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto")
I don't expect any user wants to install two tool versions in parallel.
The tool should always be able to add a signature. Adding a signature must be optional.
It seems to me that those two statements mutually contradict. Or do you intend to say that we should have a separate kconfig option to enable/disable signing feature in mkeficapsule?
If so, I can agree.
In either way, we should have an option to turn on/off this functionality as not all users use signed capsules.
I want to have a single binary to distribute with Linux distros (e.g. Debian/Ubuntu package u-boot-tools).
This should allow both
- create signed capsules
- create unsigned capsules
The user shall select signing via command line parameters.
Support for signing via the tool shall not depend on board Kconfig parameters.
That is why I proposed that we create a new kconfig option.
Please note that enabling signing feature in mkeficapsule requires openssl library, and we should not enforce users who don't need this feature to install an unnecessary package.
-Takahiro Akashi
Best regards
Heinrich
+endif mkeficapsule-objs := mkeficapsule.o $(LIBFDT_OBJS) hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c index de0a62898886..34ff1bdd82eb 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -18,7 +18,17 @@ #include <sys/stat.h> #include <sys/types.h>
-#include "fdt_host.h" +#include <linux/kconfig.h> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
+#include <openssl/asn1.h> +#include <openssl/bio.h> +#include <openssl/evp.h> +#include <openssl/err.h> +#include <openssl/pem.h> +#include <openssl/pkcs7.h> +#endif
+#include <linux/libfdt.h>
typedef __u8 u8; typedef __u16 u16; @@ -46,6 +56,13 @@ efi_guid_t efi_guid_image_type_uboot_fit = EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID; efi_guid_t efi_guid_image_type_uboot_raw = EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID; +efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
+static const char *opts_short = "f:r:i:I:v:D:K:P:C:m:dOh"; +#else +static const char *opts_short = "f:r:i:I:v:D:K:Oh"; +#endif
static struct option options[] = { {"fit", required_argument, NULL, 'f'}, @@ -54,6 +71,12 @@ static struct option options[] = { {"instance", required_argument, NULL, 'I'}, {"dtb", required_argument, NULL, 'D'}, {"public key", required_argument, NULL, 'K'}, +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
- {"private-key", required_argument, NULL, 'P'},
- {"certificate", required_argument, NULL, 'C'},
- {"monotonic-count", required_argument, NULL, 'm'},
These options should not be required.
I don't get you. What do you mean?
- {"dump-sig", no_argument, NULL, 'd'},
+#endif {"overlay", no_argument, NULL, 'O'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0}, @@ -70,6 +93,12 @@ static void print_usage(void) "\t-I, --instance <instance> update hardware instance\n" "\t-K, --public-key <key file> public key esl file\n" "\t-D, --dtb <dtb file> dtb file\n" +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
"\t-P, --private-key <privkey file> private key file\n"
"\t-C, --certificate <cert file> signer's certificate file\n"
"\t-m, --monotonic-count <count> monotonic count\n"
"\t-d, --dump_sig dump signature (*.p7)\n"
+#endif "\t-O, --overlay the dtb file is an overlay\n" "\t-h, --help print a help message\n", tool_name); @@ -249,12 +278,167 @@ err: return ret; }
+struct auth_context {
- char *key_file;
- char *cert_file;
- u8 *image_data;
- size_t image_size;
- struct efi_firmware_image_authentication auth;
- u8 *sig_data;
- size_t sig_size;
+};
+static int dump_sig;
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
+static EVP_PKEY *fileio_read_pkey(const char *filename) +{
- EVP_PKEY *key = NULL;
- BIO *bio;
- bio = BIO_new_file(filename, "r");
- if (!bio)
goto out;
- key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
+out:
- BIO_free_all(bio);
- if (!key) {
printf("Can't load key from file '%s'\n", filename);
Please, you use fprintf(stderr,) for error messages.
ERR_print_errors_fp(stderr);
- }
- return key;
+}
+static X509 *fileio_read_cert(const char *filename) +{
- X509 *cert = NULL;
- BIO *bio;
- bio = BIO_new_file(filename, "r");
- if (!bio)
goto out;
- cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+out:
- BIO_free_all(bio);
- if (!cert) {
printf("Can't load certificate from file '%s'\n", filename);
fprintf(stderr,)
ERR_print_errors_fp(stderr);
- }
- return cert;
+}
+static int create_auth_data(struct auth_context *ctx) +{
- EVP_PKEY *key = NULL;
- X509 *cert = NULL;
- BIO *data_bio = NULL;
- const EVP_MD *md;
- PKCS7 *p7;
- int flags, ret = -1;
- OpenSSL_add_all_digests();
- OpenSSL_add_all_ciphers();
- ERR_load_crypto_strings();
- key = fileio_read_pkey(ctx->key_file);
- if (!key)
goto err;
- cert = fileio_read_cert(ctx->cert_file);
- if (!cert)
goto err;
- /*
* create a BIO, containing:
* * firmware image
* * monotonic count
* in this order!
* See EDK2's FmpAuthenticatedHandlerRsa2048Sha256()
*/
- data_bio = BIO_new(BIO_s_mem());
- BIO_write(data_bio, ctx->image_data, ctx->image_size);
- BIO_write(data_bio, &ctx->auth.monotonic_count,
sizeof(ctx->auth.monotonic_count));
- md = EVP_get_digestbyname("SHA256");
- if (!md)
goto err;
- /* create signature */
- /* TODO: maybe add PKCS7_NOATTR and PKCS7_NOSMIMECAP */
PKCS7_NOATTR is a value without any documentation in the code.
Nak. Those macros are part of openssl library. See openssl/pkcs7.h.
Please, replace variable names by a long text describing what it missing.
- flags = PKCS7_BINARY | PKCS7_DETACHED;
Those constants lack documentation in the code.
Nak again.
- p7 = PKCS7_sign(NULL, NULL, NULL, data_bio, flags | PKCS7_PARTIAL);
- if (!p7)
goto err;
- if (!PKCS7_sign_add_signer(p7, cert, key, md, flags))
goto err;
- if (!PKCS7_final(p7, data_bio, flags))
goto err;
- /* convert pkcs7 into DER */
- ctx->sig_data = NULL;
- ctx->sig_size = ASN1_item_i2d((ASN1_VALUE *)p7, &ctx->sig_data,
ASN1_ITEM_rptr(PKCS7));
- if (!ctx->sig_size)
goto err;
- /* fill auth_info */
- ctx->auth.auth_info.hdr.dwLength = sizeof(ctx->auth.auth_info)
+ ctx->sig_size;
- ctx->auth.auth_info.hdr.wRevision = WIN_CERT_REVISION_2_0;
- ctx->auth.auth_info.hdr.wCertificateType = WIN_CERT_TYPE_EFI_GUID;
- memcpy(&ctx->auth.auth_info.cert_type, &efi_guid_cert_type_pkcs7,
sizeof(efi_guid_cert_type_pkcs7));
- ret = 0;
+err:
- BIO_free_all(data_bio);
- EVP_PKEY_free(key);
- X509_free(cert);
- return ret;
+}
+static int dump_signature(const char *path, u8 *signature, size_t sig_size) +{
- char *sig_path;
- FILE *f;
- size_t size;
- int ret = -1;
- sig_path = malloc(strlen(path) + 3 + 1);
- if (!sig_path)
return ret;
- sprintf(sig_path, "%s.p7", path);
- f = fopen(sig_path, "w");
- if (!f)
goto err;
- size = fwrite(signature, 1, sig_size, f);
- if (size == sig_size)
ret = 0;
- fclose(f);
+err:
- free(sig_path);
- return ret;
+} +#endif
- static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
unsigned long index, unsigned long instance)
unsigned long index, unsigned long instance,
{ struct efi_capsule_header header; struct efi_firmware_management_capsule_header capsule; struct efi_firmware_management_capsule_image_header image;uint64_t mcount, char *privkey_file, char *cert_file)
- struct auth_context auth_context; FILE *f, *g; struct stat bin_stat; u8 *data;
@@ -266,6 +450,7 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, printf("\tbin: %s\n\ttype: %pUl\n", bin, guid); printf("\tindex: %ld\n\tinstance: %ld\n", index, instance); #endif
auth_context.sig_size = 0;
g = fopen(bin, "r"); if (!g) {
@@ -281,11 +466,36 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, printf("cannot allocate memory: %zx\n", (size_t)bin_stat.st_size); goto err_1; }
- f = fopen(path, "w");
- if (!f) {
printf("cannot open %s\n", path);
- size = fread(data, 1, bin_stat.st_size, g);
- if (size < bin_stat.st_size) {
goto err_2; }printf("read failed (%zx)\n", size);
- /* first, calculate signature to determine its size */
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
- if (privkey_file && cert_file) {
auth_context.key_file = privkey_file;
auth_context.cert_file = cert_file;
auth_context.auth.monotonic_count = mcount;
auth_context.image_data = data;
auth_context.image_size = bin_stat.st_size;
if (create_auth_data(&auth_context)) {
printf("Signing firmware image failed\n");
goto err_3;
}
if (dump_sig &&
dump_signature(path, auth_context.sig_data,
auth_context.sig_size)) {
printf("Creating signature file failed\n");
goto err_3;
}
- }
+#endif
- header.capsule_guid = efi_guid_fm_capsule; header.header_size = sizeof(header); /* TODO: The current implementation ignores flags */
@@ -294,11 +504,20 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, + sizeof(capsule) + sizeof(u64) + sizeof(image) + bin_stat.st_size;
if (auth_context.sig_size)
header.capsule_image_size += sizeof(auth_context.auth)
+ auth_context.sig_size;
f = fopen(path, "w");
if (!f) {
printf("cannot open %s\n", path);
goto err_3;
}
size = fwrite(&header, 1, sizeof(header), f); if (size < sizeof(header)) { printf("write failed (%zx)\n", size);
goto err_3;
goto err_4;
}
capsule.version = 0x00000001;
@@ -307,13 +526,13 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, size = fwrite(&capsule, 1, sizeof(capsule), f); if (size < (sizeof(capsule))) { printf("write failed (%zx)\n", size);
goto err_3;
} offset = sizeof(capsule) + sizeof(u64); size = fwrite(&offset, 1, sizeof(offset), f); if (size < sizeof(offset)) { printf("write failed (%zx)\n", size);goto err_4;
goto err_3;
goto err_4;
}
image.version = 0x00000003;
@@ -323,34 +542,61 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, image.reserved[1] = 0; image.reserved[2] = 0; image.update_image_size = bin_stat.st_size;
if (auth_context.sig_size)
image.update_image_size += sizeof(auth_context.auth)
+ auth_context.sig_size;
image.update_vendor_code_size = 0; /* none */ image.update_hardware_instance = instance; image.image_capsule_support = 0;
if (auth_context.sig_size)
image.image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION;
size = fwrite(&image, 1, sizeof(image), f); if (size < sizeof(image)) { printf("write failed (%zx)\n", size);
goto err_3;
}goto err_4;
- size = fread(data, 1, bin_stat.st_size, g);
- if (size < bin_stat.st_size) {
printf("read failed (%zx)\n", size);
goto err_3;
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
We don't want to use #if if avoidable.
For this specific chunk of code, we can remove #ifdef, but we should not remove #ifdef elsewhere.
-Takahiro Akashi
- if (auth_context.sig_size) {
size = fwrite(&auth_context.auth, 1,
sizeof(auth_context.auth), f);
if (size < sizeof(auth_context.auth)) {
printf("write failed (%zx)\n", size);
goto err_4;
}
size = fwrite(auth_context.sig_data, 1,
auth_context.sig_size, f);
if (size < auth_context.sig_size) {
printf("write failed (%zx)\n", size);
goto err_4;
}}
+#endif
- size = fwrite(data, 1, bin_stat.st_size, f); if (size < bin_stat.st_size) { printf("write failed (%zx)\n", size);
goto err_3;
goto err_4;
}
fclose(f); fclose(g); free(data);
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
- if (auth_context.sig_size)
OPENSSL_free(auth_context.sig_data);
+#endif
return 0;
-err_3: +err_4: fclose(f); +err_3: +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
- if (auth_context.sig_size)
OPENSSL_free(auth_context.sig_data);
+#endif err_2: free(data); err_1: @@ -359,10 +605,6 @@ err_1: return -1; }
-/*
- Usage:
- $ mkeficapsule -f <firmware binary> <output file>
- */ int main(int argc, char **argv) { char *file;
@@ -370,6 +612,8 @@ int main(int argc, char **argv) char *dtb_file; efi_guid_t *guid; unsigned long index, instance;
- uint64_t mcount;
- char *privkey_file, *cert_file; int c, idx; int ret; bool overlay = false;
@@ -380,8 +624,12 @@ int main(int argc, char **argv) guid = NULL; index = 0; instance = 0;
- mcount = 0;
- privkey_file = NULL;
- cert_file = NULL;
- dump_sig = 0; for (;;) {
c = getopt_long(argc, argv, "f:r:i:I:v:D:K:Oh", options, &idx);
if (c == -1) break;c = getopt_long(argc, argv, opts_short, options, &idx);
@@ -422,6 +670,28 @@ int main(int argc, char **argv) } dtb_file = optarg; break; +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
Best regards
Heinrich
case 'P':
if (privkey_file) {
printf("Private Key already specified\n");
return -1;
}
privkey_file = optarg;
break;
case 'C':
if (cert_file) {
printf("Certificate file already specified\n");
return -1;
}
cert_file = optarg;
break;
case 'm':
mcount = strtoul(optarg, NULL, 0);
break;
case 'd':
dump_sig = 1;
break;
+#endif case 'O': overlay = true; break; @@ -431,8 +701,12 @@ int main(int argc, char **argv) } }
- /* need a fit image file or raw image file */
- if (!file && !pkey_file && !dtb_file) {
- /* check necessary parameters */
- if ((file && (!(optind < argc) ||
(privkey_file && !cert_file) ||
(!privkey_file && cert_file))) ||
((pkey_file && !dtb_file) ||
print_usage(); exit(EXIT_FAILURE); }(!pkey_file && dtb_file))) {
@@ -442,12 +716,12 @@ int main(int argc, char **argv) if (ret == -1) { printf("Adding public key to the dtb failed\n"); exit(EXIT_FAILURE);
} else {
exit(EXIT_SUCCESS);
} }
if (create_fwbin(argv[optind], file, guid, index, instance)
- if (optind < argc &&
create_fwbin(argv[optind], file, guid, index, instance,
printf("Creating firmware capsule failed\n"); exit(EXIT_FAILURE);mcount, privkey_file, cert_file) < 0) {

On 5/13/21 7:00 AM, AKASHI Takahiro wrote:
On Thu, May 13, 2021 at 06:22:39AM +0200, Heinrich Schuchardt wrote:
On 5/13/21 5:08 AM, AKASHI Takahiro wrote:
On Wed, May 12, 2021 at 10:56:41AM +0200, Heinrich Schuchardt wrote:
On 12.05.21 06:57, AKASHI Takahiro wrote:
With this enhancement, mkeficapsule will be able to create a capsule file with a signature which will be verified later by FMP's SetImage().
We will have to specify addtional command parameters: -monotonic-cout <count> : monotonic count -private-key <private key file> : private key file -certificate <certificate file> : certificate file Only when those parameters are given, a signature will be added to a capsule file.
Users are expected to maintain the monotonic count for each firmware image.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
tools/Makefile | 4 + tools/mkeficapsule.c | 324 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 303 insertions(+), 25 deletions(-)
diff --git a/tools/Makefile b/tools/Makefile index d020c55d6644..02eae0286e20 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -231,6 +231,10 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs hostprogs-$(CONFIG_ASN1_COMPILER) += asn1_compiler HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
+ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),) +HOSTLDLIBS_mkeficapsule += \
- $(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto")
I don't expect any user wants to install two tool versions in parallel.
The tool should always be able to add a signature. Adding a signature must be optional.
It seems to me that those two statements mutually contradict. Or do you intend to say that we should have a separate kconfig option to enable/disable signing feature in mkeficapsule?
If so, I can agree.
In either way, we should have an option to turn on/off this functionality as not all users use signed capsules.
I want to have a single binary to distribute with Linux distros (e.g. Debian/Ubuntu package u-boot-tools).
This should allow both
- create signed capsules
- create unsigned capsules
The user shall select signing via command line parameters.
Support for signing via the tool shall not depend on board Kconfig parameters.
That is why I proposed that we create a new kconfig option.
What do you want to configure? Signing shall always be enabled in mkeficapsule.
Please note that enabling signing feature in mkeficapsule requires openssl library, and we should not enforce users who don't need this feature to install an unnecessary package.
Why? There are dozens of other packages depending on OpenSSL on a developer's machine.
Best regards
Heinrich
-Takahiro Akashi
Best regards
Heinrich
+endif mkeficapsule-objs := mkeficapsule.o $(LIBFDT_OBJS) hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c index de0a62898886..34ff1bdd82eb 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -18,7 +18,17 @@ #include <sys/stat.h> #include <sys/types.h>
-#include "fdt_host.h" +#include <linux/kconfig.h> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
+#include <openssl/asn1.h> +#include <openssl/bio.h> +#include <openssl/evp.h> +#include <openssl/err.h> +#include <openssl/pem.h> +#include <openssl/pkcs7.h> +#endif
+#include <linux/libfdt.h>
typedef __u8 u8; typedef __u16 u16; @@ -46,6 +56,13 @@ efi_guid_t efi_guid_image_type_uboot_fit = EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID; efi_guid_t efi_guid_image_type_uboot_raw = EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID; +efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
+static const char *opts_short = "f:r:i:I:v:D:K:P:C:m:dOh"; +#else +static const char *opts_short = "f:r:i:I:v:D:K:Oh"; +#endif
static struct option options[] = { {"fit", required_argument, NULL, 'f'}, @@ -54,6 +71,12 @@ static struct option options[] = { {"instance", required_argument, NULL, 'I'}, {"dtb", required_argument, NULL, 'D'}, {"public key", required_argument, NULL, 'K'}, +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
- {"private-key", required_argument, NULL, 'P'},
- {"certificate", required_argument, NULL, 'C'},
- {"monotonic-count", required_argument, NULL, 'm'},
These options should not be required.
I don't get you. What do you mean?
- {"dump-sig", no_argument, NULL, 'd'},
+#endif {"overlay", no_argument, NULL, 'O'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0}, @@ -70,6 +93,12 @@ static void print_usage(void) "\t-I, --instance <instance> update hardware instance\n" "\t-K, --public-key <key file> public key esl file\n" "\t-D, --dtb <dtb file> dtb file\n" +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
"\t-P, --private-key <privkey file> private key file\n"
"\t-C, --certificate <cert file> signer's certificate file\n"
"\t-m, --monotonic-count <count> monotonic count\n"
"\t-d, --dump_sig dump signature (*.p7)\n"
+#endif "\t-O, --overlay the dtb file is an overlay\n" "\t-h, --help print a help message\n", tool_name); @@ -249,12 +278,167 @@ err: return ret; }
+struct auth_context {
- char *key_file;
- char *cert_file;
- u8 *image_data;
- size_t image_size;
- struct efi_firmware_image_authentication auth;
- u8 *sig_data;
- size_t sig_size;
+};
+static int dump_sig;
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
+static EVP_PKEY *fileio_read_pkey(const char *filename) +{
- EVP_PKEY *key = NULL;
- BIO *bio;
- bio = BIO_new_file(filename, "r");
- if (!bio)
goto out;
- key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
+out:
- BIO_free_all(bio);
- if (!key) {
printf("Can't load key from file '%s'\n", filename);
Please, you use fprintf(stderr,) for error messages.
ERR_print_errors_fp(stderr);
- }
- return key;
+}
+static X509 *fileio_read_cert(const char *filename) +{
- X509 *cert = NULL;
- BIO *bio;
- bio = BIO_new_file(filename, "r");
- if (!bio)
goto out;
- cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+out:
- BIO_free_all(bio);
- if (!cert) {
printf("Can't load certificate from file '%s'\n", filename);
fprintf(stderr,)
ERR_print_errors_fp(stderr);
- }
- return cert;
+}
+static int create_auth_data(struct auth_context *ctx) +{
- EVP_PKEY *key = NULL;
- X509 *cert = NULL;
- BIO *data_bio = NULL;
- const EVP_MD *md;
- PKCS7 *p7;
- int flags, ret = -1;
- OpenSSL_add_all_digests();
- OpenSSL_add_all_ciphers();
- ERR_load_crypto_strings();
- key = fileio_read_pkey(ctx->key_file);
- if (!key)
goto err;
- cert = fileio_read_cert(ctx->cert_file);
- if (!cert)
goto err;
- /*
* create a BIO, containing:
* * firmware image
* * monotonic count
* in this order!
* See EDK2's FmpAuthenticatedHandlerRsa2048Sha256()
*/
- data_bio = BIO_new(BIO_s_mem());
- BIO_write(data_bio, ctx->image_data, ctx->image_size);
- BIO_write(data_bio, &ctx->auth.monotonic_count,
sizeof(ctx->auth.monotonic_count));
- md = EVP_get_digestbyname("SHA256");
- if (!md)
goto err;
- /* create signature */
- /* TODO: maybe add PKCS7_NOATTR and PKCS7_NOSMIMECAP */
PKCS7_NOATTR is a value without any documentation in the code.
Nak. Those macros are part of openssl library. See openssl/pkcs7.h.
Please, replace variable names by a long text describing what it missing.
- flags = PKCS7_BINARY | PKCS7_DETACHED;
Those constants lack documentation in the code.
Nak again.
- p7 = PKCS7_sign(NULL, NULL, NULL, data_bio, flags | PKCS7_PARTIAL);
- if (!p7)
goto err;
- if (!PKCS7_sign_add_signer(p7, cert, key, md, flags))
goto err;
- if (!PKCS7_final(p7, data_bio, flags))
goto err;
- /* convert pkcs7 into DER */
- ctx->sig_data = NULL;
- ctx->sig_size = ASN1_item_i2d((ASN1_VALUE *)p7, &ctx->sig_data,
ASN1_ITEM_rptr(PKCS7));
- if (!ctx->sig_size)
goto err;
- /* fill auth_info */
- ctx->auth.auth_info.hdr.dwLength = sizeof(ctx->auth.auth_info)
+ ctx->sig_size;
- ctx->auth.auth_info.hdr.wRevision = WIN_CERT_REVISION_2_0;
- ctx->auth.auth_info.hdr.wCertificateType = WIN_CERT_TYPE_EFI_GUID;
- memcpy(&ctx->auth.auth_info.cert_type, &efi_guid_cert_type_pkcs7,
sizeof(efi_guid_cert_type_pkcs7));
- ret = 0;
+err:
- BIO_free_all(data_bio);
- EVP_PKEY_free(key);
- X509_free(cert);
- return ret;
+}
+static int dump_signature(const char *path, u8 *signature, size_t sig_size) +{
- char *sig_path;
- FILE *f;
- size_t size;
- int ret = -1;
- sig_path = malloc(strlen(path) + 3 + 1);
- if (!sig_path)
return ret;
- sprintf(sig_path, "%s.p7", path);
- f = fopen(sig_path, "w");
- if (!f)
goto err;
- size = fwrite(signature, 1, sig_size, f);
- if (size == sig_size)
ret = 0;
- fclose(f);
+err:
- free(sig_path);
- return ret;
+} +#endif
- static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
unsigned long index, unsigned long instance)
unsigned long index, unsigned long instance,
{ struct efi_capsule_header header; struct efi_firmware_management_capsule_header capsule; struct efi_firmware_management_capsule_image_header image;uint64_t mcount, char *privkey_file, char *cert_file)
- struct auth_context auth_context; FILE *f, *g; struct stat bin_stat; u8 *data;
@@ -266,6 +450,7 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, printf("\tbin: %s\n\ttype: %pUl\n", bin, guid); printf("\tindex: %ld\n\tinstance: %ld\n", index, instance); #endif
auth_context.sig_size = 0;
g = fopen(bin, "r"); if (!g) {
@@ -281,11 +466,36 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, printf("cannot allocate memory: %zx\n", (size_t)bin_stat.st_size); goto err_1; }
- f = fopen(path, "w");
- if (!f) {
printf("cannot open %s\n", path);
- size = fread(data, 1, bin_stat.st_size, g);
- if (size < bin_stat.st_size) {
}printf("read failed (%zx)\n", size); goto err_2;
- /* first, calculate signature to determine its size */
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
- if (privkey_file && cert_file) {
auth_context.key_file = privkey_file;
auth_context.cert_file = cert_file;
auth_context.auth.monotonic_count = mcount;
auth_context.image_data = data;
auth_context.image_size = bin_stat.st_size;
if (create_auth_data(&auth_context)) {
printf("Signing firmware image failed\n");
goto err_3;
}
if (dump_sig &&
dump_signature(path, auth_context.sig_data,
auth_context.sig_size)) {
printf("Creating signature file failed\n");
goto err_3;
}
- }
+#endif
- header.capsule_guid = efi_guid_fm_capsule; header.header_size = sizeof(header); /* TODO: The current implementation ignores flags */
@@ -294,11 +504,20 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, + sizeof(capsule) + sizeof(u64) + sizeof(image) + bin_stat.st_size;
if (auth_context.sig_size)
header.capsule_image_size += sizeof(auth_context.auth)
+ auth_context.sig_size;
f = fopen(path, "w");
if (!f) {
printf("cannot open %s\n", path);
goto err_3;
}
size = fwrite(&header, 1, sizeof(header), f); if (size < sizeof(header)) { printf("write failed (%zx)\n", size);
goto err_3;
goto err_4;
}
capsule.version = 0x00000001;
@@ -307,13 +526,13 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, size = fwrite(&capsule, 1, sizeof(capsule), f); if (size < (sizeof(capsule))) { printf("write failed (%zx)\n", size);
goto err_3;
} offset = sizeof(capsule) + sizeof(u64); size = fwrite(&offset, 1, sizeof(offset), f); if (size < sizeof(offset)) { printf("write failed (%zx)\n", size);goto err_4;
goto err_3;
goto err_4;
}
image.version = 0x00000003;
@@ -323,34 +542,61 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, image.reserved[1] = 0; image.reserved[2] = 0; image.update_image_size = bin_stat.st_size;
if (auth_context.sig_size)
image.update_image_size += sizeof(auth_context.auth)
+ auth_context.sig_size;
image.update_vendor_code_size = 0; /* none */ image.update_hardware_instance = instance; image.image_capsule_support = 0;
if (auth_context.sig_size)
image.image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION;
size = fwrite(&image, 1, sizeof(image), f); if (size < sizeof(image)) { printf("write failed (%zx)\n", size);
goto err_3;
}goto err_4;
- size = fread(data, 1, bin_stat.st_size, g);
- if (size < bin_stat.st_size) {
printf("read failed (%zx)\n", size);
goto err_3;
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
We don't want to use #if if avoidable.
For this specific chunk of code, we can remove #ifdef, but we should not remove #ifdef elsewhere.
-Takahiro Akashi
- if (auth_context.sig_size) {
size = fwrite(&auth_context.auth, 1,
sizeof(auth_context.auth), f);
if (size < sizeof(auth_context.auth)) {
printf("write failed (%zx)\n", size);
goto err_4;
}
size = fwrite(auth_context.sig_data, 1,
auth_context.sig_size, f);
if (size < auth_context.sig_size) {
printf("write failed (%zx)\n", size);
goto err_4;
}}
+#endif
- size = fwrite(data, 1, bin_stat.st_size, f); if (size < bin_stat.st_size) { printf("write failed (%zx)\n", size);
goto err_3;
goto err_4;
}
fclose(f); fclose(g); free(data);
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
- if (auth_context.sig_size)
OPENSSL_free(auth_context.sig_data);
+#endif
return 0;
-err_3: +err_4: fclose(f); +err_3: +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
- if (auth_context.sig_size)
OPENSSL_free(auth_context.sig_data);
+#endif err_2: free(data); err_1: @@ -359,10 +605,6 @@ err_1: return -1; }
-/*
- Usage:
- $ mkeficapsule -f <firmware binary> <output file>
- */ int main(int argc, char **argv) { char *file;
@@ -370,6 +612,8 @@ int main(int argc, char **argv) char *dtb_file; efi_guid_t *guid; unsigned long index, instance;
- uint64_t mcount;
- char *privkey_file, *cert_file; int c, idx; int ret; bool overlay = false;
@@ -380,8 +624,12 @@ int main(int argc, char **argv) guid = NULL; index = 0; instance = 0;
- mcount = 0;
- privkey_file = NULL;
- cert_file = NULL;
- dump_sig = 0; for (;;) {
c = getopt_long(argc, argv, "f:r:i:I:v:D:K:Oh", options, &idx);
c = getopt_long(argc, argv, opts_short, options, &idx); if (c == -1) break;
@@ -422,6 +670,28 @@ int main(int argc, char **argv) } dtb_file = optarg; break; +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
Best regards
Heinrich
case 'P':
if (privkey_file) {
printf("Private Key already specified\n");
return -1;
}
privkey_file = optarg;
break;
case 'C':
if (cert_file) {
printf("Certificate file already specified\n");
return -1;
}
cert_file = optarg;
break;
case 'm':
mcount = strtoul(optarg, NULL, 0);
break;
case 'd':
dump_sig = 1;
break;
+#endif case 'O': overlay = true; break; @@ -431,8 +701,12 @@ int main(int argc, char **argv) } }
- /* need a fit image file or raw image file */
- if (!file && !pkey_file && !dtb_file) {
- /* check necessary parameters */
- if ((file && (!(optind < argc) ||
(privkey_file && !cert_file) ||
(!privkey_file && cert_file))) ||
((pkey_file && !dtb_file) ||
}(!pkey_file && dtb_file))) { print_usage(); exit(EXIT_FAILURE);
@@ -442,12 +716,12 @@ int main(int argc, char **argv) if (ret == -1) { printf("Adding public key to the dtb failed\n"); exit(EXIT_FAILURE);
} else {
exit(EXIT_SUCCESS); }
}
if (create_fwbin(argv[optind], file, guid, index, instance)
- if (optind < argc &&
create_fwbin(argv[optind], file, guid, index, instance,
mcount, privkey_file, cert_file) < 0) { printf("Creating firmware capsule failed\n"); exit(EXIT_FAILURE);

On Thu, May 13, 2021 at 07:35:36AM +0200, Heinrich Schuchardt wrote:
On 5/13/21 7:00 AM, AKASHI Takahiro wrote:
On Thu, May 13, 2021 at 06:22:39AM +0200, Heinrich Schuchardt wrote:
On 5/13/21 5:08 AM, AKASHI Takahiro wrote:
On Wed, May 12, 2021 at 10:56:41AM +0200, Heinrich Schuchardt wrote:
On 12.05.21 06:57, AKASHI Takahiro wrote:
With this enhancement, mkeficapsule will be able to create a capsule file with a signature which will be verified later by FMP's SetImage().
We will have to specify addtional command parameters: -monotonic-cout <count> : monotonic count -private-key <private key file> : private key file -certificate <certificate file> : certificate file Only when those parameters are given, a signature will be added to a capsule file.
Users are expected to maintain the monotonic count for each firmware image.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
tools/Makefile | 4 + tools/mkeficapsule.c | 324 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 303 insertions(+), 25 deletions(-)
diff --git a/tools/Makefile b/tools/Makefile index d020c55d6644..02eae0286e20 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -231,6 +231,10 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs hostprogs-$(CONFIG_ASN1_COMPILER) += asn1_compiler HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
+ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),) +HOSTLDLIBS_mkeficapsule += \
- $(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto")
I don't expect any user wants to install two tool versions in parallel.
The tool should always be able to add a signature. Adding a signature must be optional.
It seems to me that those two statements mutually contradict. Or do you intend to say that we should have a separate kconfig option to enable/disable signing feature in mkeficapsule?
If so, I can agree.
In either way, we should have an option to turn on/off this functionality as not all users use signed capsules.
I want to have a single binary to distribute with Linux distros (e.g. Debian/Ubuntu package u-boot-tools).
This should allow both
- create signed capsules
- create unsigned capsules
The user shall select signing via command line parameters.
Support for signing via the tool shall not depend on board Kconfig parameters.
That is why I proposed that we create a new kconfig option.
What do you want to configure? Signing shall always be enabled in mkeficapsule.
I don't think so.
Please note that enabling signing feature in mkeficapsule requires openssl library, and we should not enforce users who don't need this feature to install an unnecessary package.
Why? There are dozens of other packages depending on OpenSSL on a developer's machine.
We don't expect all users have openssl-related packages on their desktop.
-Takahiro Akashi
Best regards
Heinrich
-Takahiro Akashi
Best regards
Heinrich
+endif mkeficapsule-objs := mkeficapsule.o $(LIBFDT_OBJS) hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c index de0a62898886..34ff1bdd82eb 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -18,7 +18,17 @@ #include <sys/stat.h> #include <sys/types.h>
-#include "fdt_host.h" +#include <linux/kconfig.h> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
+#include <openssl/asn1.h> +#include <openssl/bio.h> +#include <openssl/evp.h> +#include <openssl/err.h> +#include <openssl/pem.h> +#include <openssl/pkcs7.h> +#endif
+#include <linux/libfdt.h>
typedef __u8 u8; typedef __u16 u16; @@ -46,6 +56,13 @@ efi_guid_t efi_guid_image_type_uboot_fit = EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID; efi_guid_t efi_guid_image_type_uboot_raw = EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID; +efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
+static const char *opts_short = "f:r:i:I:v:D:K:P:C:m:dOh"; +#else +static const char *opts_short = "f:r:i:I:v:D:K:Oh"; +#endif
static struct option options[] = { {"fit", required_argument, NULL, 'f'}, @@ -54,6 +71,12 @@ static struct option options[] = { {"instance", required_argument, NULL, 'I'}, {"dtb", required_argument, NULL, 'D'}, {"public key", required_argument, NULL, 'K'}, +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
- {"private-key", required_argument, NULL, 'P'},
- {"certificate", required_argument, NULL, 'C'},
- {"monotonic-count", required_argument, NULL, 'm'},
These options should not be required.
I don't get you. What do you mean?
- {"dump-sig", no_argument, NULL, 'd'},
+#endif {"overlay", no_argument, NULL, 'O'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0}, @@ -70,6 +93,12 @@ static void print_usage(void) "\t-I, --instance <instance> update hardware instance\n" "\t-K, --public-key <key file> public key esl file\n" "\t-D, --dtb <dtb file> dtb file\n" +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
"\t-P, --private-key <privkey file> private key file\n"
"\t-C, --certificate <cert file> signer's certificate file\n"
"\t-m, --monotonic-count <count> monotonic count\n"
"\t-d, --dump_sig dump signature (*.p7)\n"
+#endif "\t-O, --overlay the dtb file is an overlay\n" "\t-h, --help print a help message\n", tool_name); @@ -249,12 +278,167 @@ err: return ret; }
+struct auth_context {
- char *key_file;
- char *cert_file;
- u8 *image_data;
- size_t image_size;
- struct efi_firmware_image_authentication auth;
- u8 *sig_data;
- size_t sig_size;
+};
+static int dump_sig;
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
+static EVP_PKEY *fileio_read_pkey(const char *filename) +{
- EVP_PKEY *key = NULL;
- BIO *bio;
- bio = BIO_new_file(filename, "r");
- if (!bio)
goto out;
- key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
+out:
- BIO_free_all(bio);
- if (!key) {
printf("Can't load key from file '%s'\n", filename);
Please, you use fprintf(stderr,) for error messages.
ERR_print_errors_fp(stderr);
- }
- return key;
+}
+static X509 *fileio_read_cert(const char *filename) +{
- X509 *cert = NULL;
- BIO *bio;
- bio = BIO_new_file(filename, "r");
- if (!bio)
goto out;
- cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+out:
- BIO_free_all(bio);
- if (!cert) {
printf("Can't load certificate from file '%s'\n", filename);
fprintf(stderr,)
ERR_print_errors_fp(stderr);
- }
- return cert;
+}
+static int create_auth_data(struct auth_context *ctx) +{
- EVP_PKEY *key = NULL;
- X509 *cert = NULL;
- BIO *data_bio = NULL;
- const EVP_MD *md;
- PKCS7 *p7;
- int flags, ret = -1;
- OpenSSL_add_all_digests();
- OpenSSL_add_all_ciphers();
- ERR_load_crypto_strings();
- key = fileio_read_pkey(ctx->key_file);
- if (!key)
goto err;
- cert = fileio_read_cert(ctx->cert_file);
- if (!cert)
goto err;
- /*
* create a BIO, containing:
* * firmware image
* * monotonic count
* in this order!
* See EDK2's FmpAuthenticatedHandlerRsa2048Sha256()
*/
- data_bio = BIO_new(BIO_s_mem());
- BIO_write(data_bio, ctx->image_data, ctx->image_size);
- BIO_write(data_bio, &ctx->auth.monotonic_count,
sizeof(ctx->auth.monotonic_count));
- md = EVP_get_digestbyname("SHA256");
- if (!md)
goto err;
- /* create signature */
- /* TODO: maybe add PKCS7_NOATTR and PKCS7_NOSMIMECAP */
PKCS7_NOATTR is a value without any documentation in the code.
Nak. Those macros are part of openssl library. See openssl/pkcs7.h.
Please, replace variable names by a long text describing what it missing.
- flags = PKCS7_BINARY | PKCS7_DETACHED;
Those constants lack documentation in the code.
Nak again.
- p7 = PKCS7_sign(NULL, NULL, NULL, data_bio, flags | PKCS7_PARTIAL);
- if (!p7)
goto err;
- if (!PKCS7_sign_add_signer(p7, cert, key, md, flags))
goto err;
- if (!PKCS7_final(p7, data_bio, flags))
goto err;
- /* convert pkcs7 into DER */
- ctx->sig_data = NULL;
- ctx->sig_size = ASN1_item_i2d((ASN1_VALUE *)p7, &ctx->sig_data,
ASN1_ITEM_rptr(PKCS7));
- if (!ctx->sig_size)
goto err;
- /* fill auth_info */
- ctx->auth.auth_info.hdr.dwLength = sizeof(ctx->auth.auth_info)
+ ctx->sig_size;
- ctx->auth.auth_info.hdr.wRevision = WIN_CERT_REVISION_2_0;
- ctx->auth.auth_info.hdr.wCertificateType = WIN_CERT_TYPE_EFI_GUID;
- memcpy(&ctx->auth.auth_info.cert_type, &efi_guid_cert_type_pkcs7,
sizeof(efi_guid_cert_type_pkcs7));
- ret = 0;
+err:
- BIO_free_all(data_bio);
- EVP_PKEY_free(key);
- X509_free(cert);
- return ret;
+}
+static int dump_signature(const char *path, u8 *signature, size_t sig_size) +{
- char *sig_path;
- FILE *f;
- size_t size;
- int ret = -1;
- sig_path = malloc(strlen(path) + 3 + 1);
- if (!sig_path)
return ret;
- sprintf(sig_path, "%s.p7", path);
- f = fopen(sig_path, "w");
- if (!f)
goto err;
- size = fwrite(signature, 1, sig_size, f);
- if (size == sig_size)
ret = 0;
- fclose(f);
+err:
- free(sig_path);
- return ret;
+} +#endif
- static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
unsigned long index, unsigned long instance)
unsigned long index, unsigned long instance,
{ struct efi_capsule_header header; struct efi_firmware_management_capsule_header capsule; struct efi_firmware_management_capsule_image_header image;uint64_t mcount, char *privkey_file, char *cert_file)
- struct auth_context auth_context; FILE *f, *g; struct stat bin_stat; u8 *data;
@@ -266,6 +450,7 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, printf("\tbin: %s\n\ttype: %pUl\n", bin, guid); printf("\tindex: %ld\n\tinstance: %ld\n", index, instance); #endif
auth_context.sig_size = 0;
g = fopen(bin, "r"); if (!g) {
@@ -281,11 +466,36 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, printf("cannot allocate memory: %zx\n", (size_t)bin_stat.st_size); goto err_1; }
- f = fopen(path, "w");
- if (!f) {
printf("cannot open %s\n", path);
- size = fread(data, 1, bin_stat.st_size, g);
- if (size < bin_stat.st_size) {
}printf("read failed (%zx)\n", size); goto err_2;
- /* first, calculate signature to determine its size */
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
- if (privkey_file && cert_file) {
auth_context.key_file = privkey_file;
auth_context.cert_file = cert_file;
auth_context.auth.monotonic_count = mcount;
auth_context.image_data = data;
auth_context.image_size = bin_stat.st_size;
if (create_auth_data(&auth_context)) {
printf("Signing firmware image failed\n");
goto err_3;
}
if (dump_sig &&
dump_signature(path, auth_context.sig_data,
auth_context.sig_size)) {
printf("Creating signature file failed\n");
goto err_3;
}
- }
+#endif
- header.capsule_guid = efi_guid_fm_capsule; header.header_size = sizeof(header); /* TODO: The current implementation ignores flags */
@@ -294,11 +504,20 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, + sizeof(capsule) + sizeof(u64) + sizeof(image) + bin_stat.st_size;
if (auth_context.sig_size)
header.capsule_image_size += sizeof(auth_context.auth)
+ auth_context.sig_size;
f = fopen(path, "w");
if (!f) {
printf("cannot open %s\n", path);
goto err_3;
}
size = fwrite(&header, 1, sizeof(header), f); if (size < sizeof(header)) { printf("write failed (%zx)\n", size);
goto err_3;
goto err_4;
}
capsule.version = 0x00000001;
@@ -307,13 +526,13 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, size = fwrite(&capsule, 1, sizeof(capsule), f); if (size < (sizeof(capsule))) { printf("write failed (%zx)\n", size);
goto err_3;
} offset = sizeof(capsule) + sizeof(u64); size = fwrite(&offset, 1, sizeof(offset), f); if (size < sizeof(offset)) { printf("write failed (%zx)\n", size);goto err_4;
goto err_3;
goto err_4;
}
image.version = 0x00000003;
@@ -323,34 +542,61 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, image.reserved[1] = 0; image.reserved[2] = 0; image.update_image_size = bin_stat.st_size;
if (auth_context.sig_size)
image.update_image_size += sizeof(auth_context.auth)
+ auth_context.sig_size;
image.update_vendor_code_size = 0; /* none */ image.update_hardware_instance = instance; image.image_capsule_support = 0;
if (auth_context.sig_size)
image.image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION;
size = fwrite(&image, 1, sizeof(image), f); if (size < sizeof(image)) { printf("write failed (%zx)\n", size);
goto err_3;
}goto err_4;
- size = fread(data, 1, bin_stat.st_size, g);
- if (size < bin_stat.st_size) {
printf("read failed (%zx)\n", size);
goto err_3;
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
We don't want to use #if if avoidable.
For this specific chunk of code, we can remove #ifdef, but we should not remove #ifdef elsewhere.
-Takahiro Akashi
- if (auth_context.sig_size) {
size = fwrite(&auth_context.auth, 1,
sizeof(auth_context.auth), f);
if (size < sizeof(auth_context.auth)) {
printf("write failed (%zx)\n", size);
goto err_4;
}
size = fwrite(auth_context.sig_data, 1,
auth_context.sig_size, f);
if (size < auth_context.sig_size) {
printf("write failed (%zx)\n", size);
goto err_4;
}}
+#endif
- size = fwrite(data, 1, bin_stat.st_size, f); if (size < bin_stat.st_size) { printf("write failed (%zx)\n", size);
goto err_3;
goto err_4;
}
fclose(f); fclose(g); free(data);
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
- if (auth_context.sig_size)
OPENSSL_free(auth_context.sig_data);
+#endif
return 0;
-err_3: +err_4: fclose(f); +err_3: +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
- if (auth_context.sig_size)
OPENSSL_free(auth_context.sig_data);
+#endif err_2: free(data); err_1: @@ -359,10 +605,6 @@ err_1: return -1; }
-/*
- Usage:
- $ mkeficapsule -f <firmware binary> <output file>
- */ int main(int argc, char **argv) { char *file;
@@ -370,6 +612,8 @@ int main(int argc, char **argv) char *dtb_file; efi_guid_t *guid; unsigned long index, instance;
- uint64_t mcount;
- char *privkey_file, *cert_file; int c, idx; int ret; bool overlay = false;
@@ -380,8 +624,12 @@ int main(int argc, char **argv) guid = NULL; index = 0; instance = 0;
- mcount = 0;
- privkey_file = NULL;
- cert_file = NULL;
- dump_sig = 0; for (;;) {
c = getopt_long(argc, argv, "f:r:i:I:v:D:K:Oh", options, &idx);
c = getopt_long(argc, argv, opts_short, options, &idx); if (c == -1) break;
@@ -422,6 +670,28 @@ int main(int argc, char **argv) } dtb_file = optarg; break; +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
Best regards
Heinrich
case 'P':
if (privkey_file) {
printf("Private Key already specified\n");
return -1;
}
privkey_file = optarg;
break;
case 'C':
if (cert_file) {
printf("Certificate file already specified\n");
return -1;
}
cert_file = optarg;
break;
case 'm':
mcount = strtoul(optarg, NULL, 0);
break;
case 'd':
dump_sig = 1;
break;
+#endif case 'O': overlay = true; break; @@ -431,8 +701,12 @@ int main(int argc, char **argv) } }
- /* need a fit image file or raw image file */
- if (!file && !pkey_file && !dtb_file) {
- /* check necessary parameters */
- if ((file && (!(optind < argc) ||
(privkey_file && !cert_file) ||
(!privkey_file && cert_file))) ||
((pkey_file && !dtb_file) ||
}(!pkey_file && dtb_file))) { print_usage(); exit(EXIT_FAILURE);
@@ -442,12 +716,12 @@ int main(int argc, char **argv) if (ret == -1) { printf("Adding public key to the dtb failed\n"); exit(EXIT_FAILURE);
} else {
exit(EXIT_SUCCESS); }
}
if (create_fwbin(argv[optind], file, guid, index, instance)
- if (optind < argc &&
create_fwbin(argv[optind], file, guid, index, instance,
mcount, privkey_file, cert_file) < 0) { printf("Creating firmware capsule failed\n"); exit(EXIT_FAILURE);

Am 13. Mai 2021 08:36:05 MESZ schrieb AKASHI Takahiro takahiro.akashi@linaro.org:
On Thu, May 13, 2021 at 07:35:36AM +0200, Heinrich Schuchardt wrote:
On 5/13/21 7:00 AM, AKASHI Takahiro wrote:
On Thu, May 13, 2021 at 06:22:39AM +0200, Heinrich Schuchardt
wrote:
On 5/13/21 5:08 AM, AKASHI Takahiro wrote:
On Wed, May 12, 2021 at 10:56:41AM +0200, Heinrich Schuchardt
wrote:
On 12.05.21 06:57, AKASHI Takahiro wrote: > With this enhancement, mkeficapsule will be able to create
a capsule
> file with a signature which will be verified later by FMP's
SetImage().
> > We will have to specify addtional command parameters: > -monotonic-cout <count> : monotonic count > -private-key <private key file> : private key file > -certificate <certificate file> : certificate file > Only when those parameters are given, a signature will be
added
> to a capsule file. > > Users are expected to maintain the monotonic count for each
firmware
> image. > > Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org > --- > tools/Makefile | 4 + > tools/mkeficapsule.c | 324
+++++++++++++++++++++++++++++++++++++++----
> 2 files changed, 303 insertions(+), 25 deletions(-) > > diff --git a/tools/Makefile b/tools/Makefile > index d020c55d6644..02eae0286e20 100644 > --- a/tools/Makefile > +++ b/tools/Makefile > @@ -231,6 +231,10 @@ hostprogs-$(CONFIG_MIPS) +=
mips-relocs
> hostprogs-$(CONFIG_ASN1_COMPILER) += asn1_compiler > HOSTCFLAGS_asn1_compiler.o = -idirafter
$(srctree)/include
> > +ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),) > +HOSTLDLIBS_mkeficapsule += \ > + $(shell pkg-config --libs libssl libcrypto 2> /dev/null
|| echo "-lssl -lcrypto")
I don't expect any user wants to install two tool versions in
parallel.
The tool should always be able to add a signature. Adding a signature must be optional.
It seems to me that those two statements mutually contradict. Or do you intend to say that we should have a separate kconfig option to enable/disable signing feature in mkeficapsule?
If so, I can agree.
In either way, we should have an option to turn on/off this
functionality
as not all users use signed capsules.
I want to have a single binary to distribute with Linux distros
(e.g.
Debian/Ubuntu package u-boot-tools).
This should allow both
- create signed capsules
- create unsigned capsules
The user shall select signing via command line parameters.
Support for signing via the tool shall not depend on board
Kconfig
parameters.
That is why I proposed that we create a new kconfig option.
What do you want to configure? Signing shall always be enabled in mkeficapsule.
I don't think so.
Capsule updates without authentication should never be rolled out in production for security reasons.
Please note that enabling signing feature in mkeficapsule requires openssl library, and we should not enforce users who don't need this feature to install an unnecessary package.
Why? There are dozens of other packages depending on OpenSSL on a developer's machine.
We don't expect all users have openssl-related packages on their desktop.
We are not talking about users but developers here.
I haven't seen a Linux distro without an OpenSSL package. The package management system will pull it in when u-boot-tools is installed.
Best regards
Heinrich
-Takahiro Akashi
Best regards
Heinrich
-Takahiro Akashi
Best regards
Heinrich
> +endif > mkeficapsule-objs := mkeficapsule.o $(LIBFDT_OBJS) > hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) +=
mkeficapsule
> > diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c > index de0a62898886..34ff1bdd82eb 100644 > --- a/tools/mkeficapsule.c > +++ b/tools/mkeficapsule.c > @@ -18,7 +18,17 @@ > #include <sys/stat.h> > #include <sys/types.h> > > -#include "fdt_host.h" > +#include <linux/kconfig.h> > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
> +#include <openssl/asn1.h> > +#include <openssl/bio.h> > +#include <openssl/evp.h> > +#include <openssl/err.h> > +#include <openssl/pem.h> > +#include <openssl/pkcs7.h> > +#endif > + > +#include <linux/libfdt.h> > > typedef __u8 u8; > typedef __u16 u16; > @@ -46,6 +56,13 @@ efi_guid_t efi_guid_image_type_uboot_fit
=
> EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID; > efi_guid_t efi_guid_image_type_uboot_raw = > EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID; > +efi_guid_t efi_guid_cert_type_pkcs7 =
EFI_CERT_TYPE_PKCS7_GUID;
> + > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
> +static const char *opts_short = "f:r:i:I:v:D:K:P:C:m:dOh"; > +#else > +static const char *opts_short = "f:r:i:I:v:D:K:Oh"; > +#endif > > static struct option options[] = { > {"fit", required_argument, NULL, 'f'}, > @@ -54,6 +71,12 @@ static struct option options[] = { > {"instance", required_argument, NULL, 'I'}, > {"dtb", required_argument, NULL, 'D'}, > {"public key", required_argument, NULL, 'K'}, > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE) > + {"private-key", required_argument, NULL, 'P'}, > + {"certificate", required_argument, NULL, 'C'}, > + {"monotonic-count", required_argument, NULL, 'm'},
These options should not be required.
I don't get you. What do you mean?
> + {"dump-sig", no_argument, NULL, 'd'}, > +#endif > {"overlay", no_argument, NULL, 'O'}, > {"help", no_argument, NULL, 'h'}, > {NULL, 0, NULL, 0}, > @@ -70,6 +93,12 @@ static void print_usage(void) > "\t-I, --instance <instance> update hardware
instance\n"
> "\t-K, --public-key <key file> public key esl
file\n"
> "\t-D, --dtb <dtb file> dtb file\n" > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
> + "\t-P, --private-key <privkey file> private key
file\n"
> + "\t-C, --certificate <cert file> signer's
certificate file\n"
> + "\t-m, --monotonic-count <count> monotonic
count\n"
> + "\t-d, --dump_sig dump signature
(*.p7)\n"
> +#endif > "\t-O, --overlay the dtb file is
an overlay\n"
> "\t-h, --help print a help
message\n",
> tool_name); > @@ -249,12 +278,167 @@ err: > return ret; > } > > +struct auth_context { > + char *key_file; > + char *cert_file; > + u8 *image_data; > + size_t image_size; > + struct efi_firmware_image_authentication auth; > + u8 *sig_data; > + size_t sig_size; > +}; > + > +static int dump_sig; > + > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
> +static EVP_PKEY *fileio_read_pkey(const char *filename) > +{ > + EVP_PKEY *key = NULL; > + BIO *bio; > + > + bio = BIO_new_file(filename, "r"); > + if (!bio) > + goto out; > + > + key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); > + > +out: > + BIO_free_all(bio); > + if (!key) { > + printf("Can't load key from file '%s'\n", filename);
Please, you use fprintf(stderr,) for error messages.
> + ERR_print_errors_fp(stderr); > + } > + > + return key; > +} > + > +static X509 *fileio_read_cert(const char *filename) > +{ > + X509 *cert = NULL; > + BIO *bio; > + > + bio = BIO_new_file(filename, "r"); > + if (!bio) > + goto out; > + > + cert = PEM_read_bio_X509(bio, NULL, NULL, NULL); > + > +out: > + BIO_free_all(bio); > + if (!cert) { > + printf("Can't load certificate from file '%s'\n",
filename);
fprintf(stderr,)
> + ERR_print_errors_fp(stderr); > + } > + > + return cert; > +} > + > +static int create_auth_data(struct auth_context *ctx) > +{ > + EVP_PKEY *key = NULL; > + X509 *cert = NULL; > + BIO *data_bio = NULL; > + const EVP_MD *md; > + PKCS7 *p7; > + int flags, ret = -1; > + > + OpenSSL_add_all_digests(); > + OpenSSL_add_all_ciphers(); > + ERR_load_crypto_strings(); > + > + key = fileio_read_pkey(ctx->key_file); > + if (!key) > + goto err; > + cert = fileio_read_cert(ctx->cert_file); > + if (!cert) > + goto err; > + > + /* > + * create a BIO, containing: > + * * firmware image > + * * monotonic count > + * in this order! > + * See EDK2's FmpAuthenticatedHandlerRsa2048Sha256() > + */ > + data_bio = BIO_new(BIO_s_mem()); > + BIO_write(data_bio, ctx->image_data, ctx->image_size); > + BIO_write(data_bio, &ctx->auth.monotonic_count, > + sizeof(ctx->auth.monotonic_count)); > + > + md = EVP_get_digestbyname("SHA256"); > + if (!md) > + goto err; > + > + /* create signature */ > + /* TODO: maybe add PKCS7_NOATTR and PKCS7_NOSMIMECAP */
PKCS7_NOATTR is a value without any documentation in the
code.
Nak. Those macros are part of openssl library. See openssl/pkcs7.h.
Please, replace variable names by a long text describing what
it missing.
> + flags = PKCS7_BINARY | PKCS7_DETACHED;
Those constants lack documentation in the code.
Nak again.
> + p7 = PKCS7_sign(NULL, NULL, NULL, data_bio, flags |
PKCS7_PARTIAL);
> + if (!p7) > + goto err; > + if (!PKCS7_sign_add_signer(p7, cert, key, md, flags)) > + goto err; > + if (!PKCS7_final(p7, data_bio, flags)) > + goto err; > + > + /* convert pkcs7 into DER */ > + ctx->sig_data = NULL; > + ctx->sig_size = ASN1_item_i2d((ASN1_VALUE *)p7,
&ctx->sig_data,
> + ASN1_ITEM_rptr(PKCS7)); > + if (!ctx->sig_size) > + goto err; > + > + /* fill auth_info */ > + ctx->auth.auth_info.hdr.dwLength =
sizeof(ctx->auth.auth_info)
> + + ctx->sig_size; > + ctx->auth.auth_info.hdr.wRevision =
WIN_CERT_REVISION_2_0;
> + ctx->auth.auth_info.hdr.wCertificateType =
WIN_CERT_TYPE_EFI_GUID;
> + memcpy(&ctx->auth.auth_info.cert_type,
&efi_guid_cert_type_pkcs7,
> + sizeof(efi_guid_cert_type_pkcs7)); > + > + ret = 0; > +err: > + BIO_free_all(data_bio); > + EVP_PKEY_free(key); > + X509_free(cert); > + > + return ret; > +} > + > +static int dump_signature(const char *path, u8 *signature,
size_t sig_size)
> +{ > + char *sig_path; > + FILE *f; > + size_t size; > + int ret = -1; > + > + sig_path = malloc(strlen(path) + 3 + 1); > + if (!sig_path) > + return ret; > + > + sprintf(sig_path, "%s.p7", path); > + f = fopen(sig_path, "w"); > + if (!f) > + goto err; > + > + size = fwrite(signature, 1, sig_size, f); > + if (size == sig_size) > + ret = 0; > + > + fclose(f); > +err: > + free(sig_path); > + return ret; > +} > +#endif > + > static int create_fwbin(char *path, char *bin,
efi_guid_t *guid,
> - unsigned long index, unsigned long instance) > + unsigned long index, unsigned long instance, > + uint64_t mcount, char *privkey_file, char *cert_file) > { > struct efi_capsule_header header; > struct efi_firmware_management_capsule_header capsule; > struct efi_firmware_management_capsule_image_header
image;
> + struct auth_context auth_context; > FILE *f, *g; > struct stat bin_stat; > u8 *data; > @@ -266,6 +450,7 @@ static int create_fwbin(char *path,
char *bin, efi_guid_t *guid,
> printf("\tbin: %s\n\ttype: %pUl\n", bin, guid); > printf("\tindex: %ld\n\tinstance: %ld\n", index,
instance);
> #endif > + auth_context.sig_size = 0; > > g = fopen(bin, "r"); > if (!g) { > @@ -281,11 +466,36 @@ static int create_fwbin(char *path,
char *bin, efi_guid_t *guid,
> printf("cannot allocate memory: %zx\n",
(size_t)bin_stat.st_size);
> goto err_1; > } > - f = fopen(path, "w"); > - if (!f) { > - printf("cannot open %s\n", path); > + > + size = fread(data, 1, bin_stat.st_size, g); > + if (size < bin_stat.st_size) { > + printf("read failed (%zx)\n", size); > goto err_2; > } > + > + /* first, calculate signature to determine its size */ > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
> + if (privkey_file && cert_file) { > + auth_context.key_file = privkey_file; > + auth_context.cert_file = cert_file; > + auth_context.auth.monotonic_count = mcount; > + auth_context.image_data = data; > + auth_context.image_size = bin_stat.st_size; > + > + if (create_auth_data(&auth_context)) { > + printf("Signing firmware image failed\n"); > + goto err_3; > + } > + > + if (dump_sig && > + dump_signature(path, auth_context.sig_data, > + auth_context.sig_size)) { > + printf("Creating signature file failed\n"); > + goto err_3; > + } > + } > +#endif > + > header.capsule_guid = efi_guid_fm_capsule; > header.header_size = sizeof(header); > /* TODO: The current implementation ignores flags */ > @@ -294,11 +504,20 @@ static int create_fwbin(char *path,
char *bin, efi_guid_t *guid,
> + sizeof(capsule) + sizeof(u64) > + sizeof(image) > + bin_stat.st_size; > + if (auth_context.sig_size) > + header.capsule_image_size += sizeof(auth_context.auth) > + + auth_context.sig_size; > + > + f = fopen(path, "w"); > + if (!f) { > + printf("cannot open %s\n", path); > + goto err_3;

On Thu, May 13, 2021 at 08:45:10AM +0200, Heinrich Schuchardt wrote:
Am 13. Mai 2021 08:36:05 MESZ schrieb AKASHI Takahiro takahiro.akashi@linaro.org:
On Thu, May 13, 2021 at 07:35:36AM +0200, Heinrich Schuchardt wrote:
On 5/13/21 7:00 AM, AKASHI Takahiro wrote:
On Thu, May 13, 2021 at 06:22:39AM +0200, Heinrich Schuchardt
wrote:
On 5/13/21 5:08 AM, AKASHI Takahiro wrote:
On Wed, May 12, 2021 at 10:56:41AM +0200, Heinrich Schuchardt
wrote:
> On 12.05.21 06:57, AKASHI Takahiro wrote: > > With this enhancement, mkeficapsule will be able to create
a capsule
> > file with a signature which will be verified later by FMP's
SetImage().
> > > > We will have to specify addtional command parameters: > > -monotonic-cout <count> : monotonic count > > -private-key <private key file> : private key file > > -certificate <certificate file> : certificate file > > Only when those parameters are given, a signature will be
added
> > to a capsule file. > > > > Users are expected to maintain the monotonic count for each
firmware
> > image. > > > > Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org > > --- > > tools/Makefile | 4 + > > tools/mkeficapsule.c | 324
+++++++++++++++++++++++++++++++++++++++----
> > 2 files changed, 303 insertions(+), 25 deletions(-) > > > > diff --git a/tools/Makefile b/tools/Makefile > > index d020c55d6644..02eae0286e20 100644 > > --- a/tools/Makefile > > +++ b/tools/Makefile > > @@ -231,6 +231,10 @@ hostprogs-$(CONFIG_MIPS) +=
mips-relocs
> > hostprogs-$(CONFIG_ASN1_COMPILER) += asn1_compiler > > HOSTCFLAGS_asn1_compiler.o = -idirafter
$(srctree)/include
> > > > +ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),) > > +HOSTLDLIBS_mkeficapsule += \ > > + $(shell pkg-config --libs libssl libcrypto 2> /dev/null
|| echo "-lssl -lcrypto")
> > I don't expect any user wants to install two tool versions in
parallel.
> > The tool should always be able to add a signature. > Adding a signature must be optional.
It seems to me that those two statements mutually contradict. Or do you intend to say that we should have a separate kconfig option to enable/disable signing feature in mkeficapsule?
If so, I can agree.
In either way, we should have an option to turn on/off this
functionality
as not all users use signed capsules.
I want to have a single binary to distribute with Linux distros
(e.g.
Debian/Ubuntu package u-boot-tools).
This should allow both
- create signed capsules
- create unsigned capsules
The user shall select signing via command line parameters.
Support for signing via the tool shall not depend on board
Kconfig
parameters.
That is why I proposed that we create a new kconfig option.
What do you want to configure? Signing shall always be enabled in mkeficapsule.
I don't think so.
Capsule updates without authentication should never be rolled out in production for security reasons.
It's up to the providers. (In this case, providers are not always distro.) We should provide mechanism, but not enforce policy.
Please note that enabling signing feature in mkeficapsule requires openssl library, and we should not enforce users who don't need this feature to install an unnecessary package.
Why? There are dozens of other packages depending on OpenSSL on a developer's machine.
We don't expect all users have openssl-related packages on their desktop.
We are not talking about users but developers here.
There is not distinct difference between users and developpers.
I haven't seen a Linux distro without an OpenSSL package. The package management system will pull it in when u-boot-tools is installed.
Distro may have its own policy, that's fine. We will only want to provide the mechanism with which they want to build their own distro.
-Takahiro Akashi
Best regards
Heinrich
-Takahiro Akashi
Best regards
Heinrich
-Takahiro Akashi
Best regards
Heinrich
> > +endif > > mkeficapsule-objs := mkeficapsule.o $(LIBFDT_OBJS) > > hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) +=
mkeficapsule
> > > > diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c > > index de0a62898886..34ff1bdd82eb 100644 > > --- a/tools/mkeficapsule.c > > +++ b/tools/mkeficapsule.c > > @@ -18,7 +18,17 @@ > > #include <sys/stat.h> > > #include <sys/types.h> > > > > -#include "fdt_host.h" > > +#include <linux/kconfig.h> > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE) > > see above > > > +#include <openssl/asn1.h> > > +#include <openssl/bio.h> > > +#include <openssl/evp.h> > > +#include <openssl/err.h> > > +#include <openssl/pem.h> > > +#include <openssl/pkcs7.h> > > +#endif > > + > > +#include <linux/libfdt.h> > > > > typedef __u8 u8; > > typedef __u16 u16; > > @@ -46,6 +56,13 @@ efi_guid_t efi_guid_image_type_uboot_fit
=
> > EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID; > > efi_guid_t efi_guid_image_type_uboot_raw = > > EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID; > > +efi_guid_t efi_guid_cert_type_pkcs7 =
EFI_CERT_TYPE_PKCS7_GUID;
> > + > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE) > > see above > > > +static const char *opts_short = "f:r:i:I:v:D:K:P:C:m:dOh"; > > +#else > > +static const char *opts_short = "f:r:i:I:v:D:K:Oh"; > > +#endif > > > > static struct option options[] = { > > {"fit", required_argument, NULL, 'f'}, > > @@ -54,6 +71,12 @@ static struct option options[] = { > > {"instance", required_argument, NULL, 'I'}, > > {"dtb", required_argument, NULL, 'D'}, > > {"public key", required_argument, NULL, 'K'}, > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE) > > + {"private-key", required_argument, NULL, 'P'}, > > + {"certificate", required_argument, NULL, 'C'}, > > + {"monotonic-count", required_argument, NULL, 'm'}, > > These options should not be required.
I don't get you. What do you mean?
> > + {"dump-sig", no_argument, NULL, 'd'}, > > +#endif > > {"overlay", no_argument, NULL, 'O'}, > > {"help", no_argument, NULL, 'h'}, > > {NULL, 0, NULL, 0}, > > @@ -70,6 +93,12 @@ static void print_usage(void) > > "\t-I, --instance <instance> update hardware
instance\n"
> > "\t-K, --public-key <key file> public key esl
file\n"
> > "\t-D, --dtb <dtb file> dtb file\n" > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE) > > see above > > > + "\t-P, --private-key <privkey file> private key
file\n"
> > + "\t-C, --certificate <cert file> signer's
certificate file\n"
> > + "\t-m, --monotonic-count <count> monotonic
count\n"
> > + "\t-d, --dump_sig dump signature
(*.p7)\n"
> > +#endif > > "\t-O, --overlay the dtb file is
an overlay\n"
> > "\t-h, --help print a help
message\n",
> > tool_name); > > @@ -249,12 +278,167 @@ err: > > return ret; > > } > > > > +struct auth_context { > > + char *key_file; > > + char *cert_file; > > + u8 *image_data; > > + size_t image_size; > > + struct efi_firmware_image_authentication auth; > > + u8 *sig_data; > > + size_t sig_size; > > +}; > > + > > +static int dump_sig; > > + > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE) > > see above > > > +static EVP_PKEY *fileio_read_pkey(const char *filename) > > +{ > > + EVP_PKEY *key = NULL; > > + BIO *bio; > > + > > + bio = BIO_new_file(filename, "r"); > > + if (!bio) > > + goto out; > > + > > + key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); > > + > > +out: > > + BIO_free_all(bio); > > + if (!key) { > > + printf("Can't load key from file '%s'\n", filename); > > Please, you use fprintf(stderr,) for error messages. > > > + ERR_print_errors_fp(stderr); > > + } > > + > > + return key; > > +} > > + > > +static X509 *fileio_read_cert(const char *filename) > > +{ > > + X509 *cert = NULL; > > + BIO *bio; > > + > > + bio = BIO_new_file(filename, "r"); > > + if (!bio) > > + goto out; > > + > > + cert = PEM_read_bio_X509(bio, NULL, NULL, NULL); > > + > > +out: > > + BIO_free_all(bio); > > + if (!cert) { > > + printf("Can't load certificate from file '%s'\n",
filename);
> > fprintf(stderr,) > > > + ERR_print_errors_fp(stderr); > > + } > > + > > + return cert; > > +} > > + > > +static int create_auth_data(struct auth_context *ctx) > > +{ > > + EVP_PKEY *key = NULL; > > + X509 *cert = NULL; > > + BIO *data_bio = NULL; > > + const EVP_MD *md; > > + PKCS7 *p7; > > + int flags, ret = -1; > > + > > + OpenSSL_add_all_digests(); > > + OpenSSL_add_all_ciphers(); > > + ERR_load_crypto_strings(); > > + > > + key = fileio_read_pkey(ctx->key_file); > > + if (!key) > > + goto err; > > + cert = fileio_read_cert(ctx->cert_file); > > + if (!cert) > > + goto err; > > + > > + /* > > + * create a BIO, containing: > > + * * firmware image > > + * * monotonic count > > + * in this order! > > + * See EDK2's FmpAuthenticatedHandlerRsa2048Sha256() > > + */ > > + data_bio = BIO_new(BIO_s_mem()); > > + BIO_write(data_bio, ctx->image_data, ctx->image_size); > > + BIO_write(data_bio, &ctx->auth.monotonic_count, > > + sizeof(ctx->auth.monotonic_count)); > > + > > + md = EVP_get_digestbyname("SHA256"); > > + if (!md) > > + goto err; > > + > > + /* create signature */ > > + /* TODO: maybe add PKCS7_NOATTR and PKCS7_NOSMIMECAP */ > > PKCS7_NOATTR is a value without any documentation in the
code.
Nak. Those macros are part of openssl library. See openssl/pkcs7.h.
> Please, replace variable names by a long text describing what
it missing.
> > > + flags = PKCS7_BINARY | PKCS7_DETACHED; > > Those constants lack documentation in the code.
Nak again.
> > + p7 = PKCS7_sign(NULL, NULL, NULL, data_bio, flags |
PKCS7_PARTIAL);
> > + if (!p7) > > + goto err; > > + if (!PKCS7_sign_add_signer(p7, cert, key, md, flags)) > > + goto err; > > + if (!PKCS7_final(p7, data_bio, flags)) > > + goto err; > > + > > + /* convert pkcs7 into DER */ > > + ctx->sig_data = NULL; > > + ctx->sig_size = ASN1_item_i2d((ASN1_VALUE *)p7,
&ctx->sig_data,
> > + ASN1_ITEM_rptr(PKCS7)); > > + if (!ctx->sig_size) > > + goto err; > > + > > + /* fill auth_info */ > > + ctx->auth.auth_info.hdr.dwLength =
sizeof(ctx->auth.auth_info)
> > + + ctx->sig_size; > > + ctx->auth.auth_info.hdr.wRevision =
WIN_CERT_REVISION_2_0;
> > + ctx->auth.auth_info.hdr.wCertificateType =
WIN_CERT_TYPE_EFI_GUID;
> > + memcpy(&ctx->auth.auth_info.cert_type,
&efi_guid_cert_type_pkcs7,
> > + sizeof(efi_guid_cert_type_pkcs7)); > > + > > + ret = 0; > > +err: > > + BIO_free_all(data_bio); > > + EVP_PKEY_free(key); > > + X509_free(cert); > > + > > + return ret; > > +} > > + > > +static int dump_signature(const char *path, u8 *signature,
size_t sig_size)
> > +{ > > + char *sig_path; > > + FILE *f; > > + size_t size; > > + int ret = -1; > > + > > + sig_path = malloc(strlen(path) + 3 + 1); > > + if (!sig_path) > > + return ret; > > + > > + sprintf(sig_path, "%s.p7", path); > > + f = fopen(sig_path, "w"); > > + if (!f) > > + goto err; > > + > > + size = fwrite(signature, 1, sig_size, f); > > + if (size == sig_size) > > + ret = 0; > > + > > + fclose(f); > > +err: > > + free(sig_path); > > + return ret; > > +} > > +#endif > > + > > static int create_fwbin(char *path, char *bin,
efi_guid_t *guid,
> > - unsigned long index, unsigned long instance) > > + unsigned long index, unsigned long instance, > > + uint64_t mcount, char *privkey_file, char *cert_file) > > { > > struct efi_capsule_header header; > > struct efi_firmware_management_capsule_header capsule; > > struct efi_firmware_management_capsule_image_header
image;
> > + struct auth_context auth_context; > > FILE *f, *g; > > struct stat bin_stat; > > u8 *data; > > @@ -266,6 +450,7 @@ static int create_fwbin(char *path,
char *bin, efi_guid_t *guid,
> > printf("\tbin: %s\n\ttype: %pUl\n", bin, guid); > > printf("\tindex: %ld\n\tinstance: %ld\n", index,
instance);
> > #endif > > + auth_context.sig_size = 0; > > > > g = fopen(bin, "r"); > > if (!g) { > > @@ -281,11 +466,36 @@ static int create_fwbin(char *path,
char *bin, efi_guid_t *guid,
> > printf("cannot allocate memory: %zx\n",
(size_t)bin_stat.st_size);
> > goto err_1; > > } > > - f = fopen(path, "w"); > > - if (!f) { > > - printf("cannot open %s\n", path); > > + > > + size = fread(data, 1, bin_stat.st_size, g); > > + if (size < bin_stat.st_size) { > > + printf("read failed (%zx)\n", size); > > goto err_2; > > } > > + > > + /* first, calculate signature to determine its size */ > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE) > > see above > > > + if (privkey_file && cert_file) { > > + auth_context.key_file = privkey_file; > > + auth_context.cert_file = cert_file; > > + auth_context.auth.monotonic_count = mcount; > > + auth_context.image_data = data; > > + auth_context.image_size = bin_stat.st_size; > > + > > + if (create_auth_data(&auth_context)) { > > + printf("Signing firmware image failed\n"); > > + goto err_3; > > + } > > + > > + if (dump_sig && > > + dump_signature(path, auth_context.sig_data, > > + auth_context.sig_size)) { > > + printf("Creating signature file failed\n"); > > + goto err_3; > > + } > > + } > > +#endif > > + > > header.capsule_guid = efi_guid_fm_capsule; > > header.header_size = sizeof(header); > > /* TODO: The current implementation ignores flags */ > > @@ -294,11 +504,20 @@ static int create_fwbin(char *path,
char *bin, efi_guid_t *guid,
> > + sizeof(capsule) + sizeof(u64) > > + sizeof(image) > > + bin_stat.st_size; > > + if (auth_context.sig_size) > > + header.capsule_image_size += sizeof(auth_context.auth) > > + + auth_context.sig_size; > > + > > + f = fopen(path, "w"); > > + if (!f) { > > + printf("cannot open %s\n", path); > > + goto err_3;

Hi Heinrich,
2021年5月13日(木) 13:22 Heinrich Schuchardt xypron.glpk@gmx.de:
On 5/13/21 5:08 AM, AKASHI Takahiro wrote:
On Wed, May 12, 2021 at 10:56:41AM +0200, Heinrich Schuchardt wrote:
On 12.05.21 06:57, AKASHI Takahiro wrote:
With this enhancement, mkeficapsule will be able to create a capsule file with a signature which will be verified later by FMP's SetImage().
We will have to specify addtional command parameters: -monotonic-cout <count> : monotonic count -private-key <private key file> : private key file -certificate <certificate file> : certificate file Only when those parameters are given, a signature will be added to a capsule file.
Users are expected to maintain the monotonic count for each firmware image.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
tools/Makefile | 4 + tools/mkeficapsule.c | 324 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 303 insertions(+), 25 deletions(-)
diff --git a/tools/Makefile b/tools/Makefile index d020c55d6644..02eae0286e20 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -231,6 +231,10 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs hostprogs-$(CONFIG_ASN1_COMPILER) += asn1_compiler HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
+ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),) +HOSTLDLIBS_mkeficapsule += \
- $(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto")
I don't expect any user wants to install two tool versions in parallel.
The tool should always be able to add a signature. Adding a signature must be optional.
It seems to me that those two statements mutually contradict. Or do you intend to say that we should have a separate kconfig option to enable/disable signing feature in mkeficapsule?
If so, I can agree.
In either way, we should have an option to turn on/off this functionality as not all users use signed capsules.
I want to have a single binary to distribute with Linux distros (e.g. Debian/Ubuntu package u-boot-tools).
I couldn't catch your point. If so, the distros can build u-boot with CONFIG_EFI_CAPSULE_AUTHENTICATE=y...
BTW, IMHO, if u-boot.bin can not find the ESL in the device tree, it should skip authentication too.
Then, user can choose whether enabling capsule authentication or not by embedding ESL into their devicetree.
Thank you
This should allow both
- create signed capsules
- create unsigned capsules
The user shall select signing via command line parameters.
Support for signing via the tool shall not depend on board Kconfig parameters.
Best regards
Heinrich
+endif mkeficapsule-objs := mkeficapsule.o $(LIBFDT_OBJS) hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c index de0a62898886..34ff1bdd82eb 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -18,7 +18,17 @@ #include <sys/stat.h> #include <sys/types.h>
-#include "fdt_host.h" +#include <linux/kconfig.h> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
+#include <openssl/asn1.h> +#include <openssl/bio.h> +#include <openssl/evp.h> +#include <openssl/err.h> +#include <openssl/pem.h> +#include <openssl/pkcs7.h> +#endif
+#include <linux/libfdt.h>
typedef __u8 u8; typedef __u16 u16; @@ -46,6 +56,13 @@ efi_guid_t efi_guid_image_type_uboot_fit = EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID; efi_guid_t efi_guid_image_type_uboot_raw = EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID; +efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
+static const char *opts_short = "f:r:i:I:v:D:K:P:C:m:dOh"; +#else +static const char *opts_short = "f:r:i:I:v:D:K:Oh"; +#endif
static struct option options[] = { {"fit", required_argument, NULL, 'f'}, @@ -54,6 +71,12 @@ static struct option options[] = { {"instance", required_argument, NULL, 'I'}, {"dtb", required_argument, NULL, 'D'}, {"public key", required_argument, NULL, 'K'}, +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
- {"private-key", required_argument, NULL, 'P'},
- {"certificate", required_argument, NULL, 'C'},
- {"monotonic-count", required_argument, NULL, 'm'},
These options should not be required.
I don't get you. What do you mean?
- {"dump-sig", no_argument, NULL, 'd'},
+#endif {"overlay", no_argument, NULL, 'O'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0}, @@ -70,6 +93,12 @@ static void print_usage(void) "\t-I, --instance <instance> update hardware instance\n" "\t-K, --public-key <key file> public key esl file\n" "\t-D, --dtb <dtb file> dtb file\n" +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
"\t-P, --private-key <privkey file> private key file\n"
"\t-C, --certificate <cert file> signer's certificate file\n"
"\t-m, --monotonic-count <count> monotonic count\n"
"\t-d, --dump_sig dump signature (*.p7)\n"
+#endif "\t-O, --overlay the dtb file is an overlay\n" "\t-h, --help print a help message\n", tool_name); @@ -249,12 +278,167 @@ err: return ret; }
+struct auth_context {
- char *key_file;
- char *cert_file;
- u8 *image_data;
- size_t image_size;
- struct efi_firmware_image_authentication auth;
- u8 *sig_data;
- size_t sig_size;
+};
+static int dump_sig;
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
+static EVP_PKEY *fileio_read_pkey(const char *filename) +{
- EVP_PKEY *key = NULL;
- BIO *bio;
- bio = BIO_new_file(filename, "r");
- if (!bio)
goto out;
- key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
+out:
- BIO_free_all(bio);
- if (!key) {
printf("Can't load key from file '%s'\n", filename);
Please, you use fprintf(stderr,) for error messages.
ERR_print_errors_fp(stderr);
- }
- return key;
+}
+static X509 *fileio_read_cert(const char *filename) +{
- X509 *cert = NULL;
- BIO *bio;
- bio = BIO_new_file(filename, "r");
- if (!bio)
goto out;
- cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+out:
- BIO_free_all(bio);
- if (!cert) {
printf("Can't load certificate from file '%s'\n", filename);
fprintf(stderr,)
ERR_print_errors_fp(stderr);
- }
- return cert;
+}
+static int create_auth_data(struct auth_context *ctx) +{
- EVP_PKEY *key = NULL;
- X509 *cert = NULL;
- BIO *data_bio = NULL;
- const EVP_MD *md;
- PKCS7 *p7;
- int flags, ret = -1;
- OpenSSL_add_all_digests();
- OpenSSL_add_all_ciphers();
- ERR_load_crypto_strings();
- key = fileio_read_pkey(ctx->key_file);
- if (!key)
goto err;
- cert = fileio_read_cert(ctx->cert_file);
- if (!cert)
goto err;
- /*
- create a BIO, containing:
- firmware image
- monotonic count
- in this order!
- See EDK2's FmpAuthenticatedHandlerRsa2048Sha256()
- */
- data_bio = BIO_new(BIO_s_mem());
- BIO_write(data_bio, ctx->image_data, ctx->image_size);
- BIO_write(data_bio, &ctx->auth.monotonic_count,
sizeof(ctx->auth.monotonic_count));
- md = EVP_get_digestbyname("SHA256");
- if (!md)
goto err;
- /* create signature */
- /* TODO: maybe add PKCS7_NOATTR and PKCS7_NOSMIMECAP */
PKCS7_NOATTR is a value without any documentation in the code.
Nak. Those macros are part of openssl library. See openssl/pkcs7.h.
Please, replace variable names by a long text describing what it missing.
- flags = PKCS7_BINARY | PKCS7_DETACHED;
Those constants lack documentation in the code.
Nak again.
- p7 = PKCS7_sign(NULL, NULL, NULL, data_bio, flags | PKCS7_PARTIAL);
- if (!p7)
goto err;
- if (!PKCS7_sign_add_signer(p7, cert, key, md, flags))
goto err;
- if (!PKCS7_final(p7, data_bio, flags))
goto err;
- /* convert pkcs7 into DER */
- ctx->sig_data = NULL;
- ctx->sig_size = ASN1_item_i2d((ASN1_VALUE *)p7, &ctx->sig_data,
ASN1_ITEM_rptr(PKCS7));
- if (!ctx->sig_size)
goto err;
- /* fill auth_info */
- ctx->auth.auth_info.hdr.dwLength = sizeof(ctx->auth.auth_info)
+ ctx->sig_size;
- ctx->auth.auth_info.hdr.wRevision = WIN_CERT_REVISION_2_0;
- ctx->auth.auth_info.hdr.wCertificateType = WIN_CERT_TYPE_EFI_GUID;
- memcpy(&ctx->auth.auth_info.cert_type, &efi_guid_cert_type_pkcs7,
sizeof(efi_guid_cert_type_pkcs7));
- ret = 0;
+err:
- BIO_free_all(data_bio);
- EVP_PKEY_free(key);
- X509_free(cert);
- return ret;
+}
+static int dump_signature(const char *path, u8 *signature, size_t sig_size) +{
- char *sig_path;
- FILE *f;
- size_t size;
- int ret = -1;
- sig_path = malloc(strlen(path) + 3 + 1);
- if (!sig_path)
return ret;
- sprintf(sig_path, "%s.p7", path);
- f = fopen(sig_path, "w");
- if (!f)
goto err;
- size = fwrite(signature, 1, sig_size, f);
- if (size == sig_size)
ret = 0;
- fclose(f);
+err:
- free(sig_path);
- return ret;
+} +#endif
- static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
unsigned long index, unsigned long instance)
unsigned long index, unsigned long instance,
{ struct efi_capsule_header header; struct efi_firmware_management_capsule_header capsule; struct efi_firmware_management_capsule_image_header image;uint64_t mcount, char *privkey_file, char *cert_file)
- struct auth_context auth_context; FILE *f, *g; struct stat bin_stat; u8 *data;
@@ -266,6 +450,7 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, printf("\tbin: %s\n\ttype: %pUl\n", bin, guid); printf("\tindex: %ld\n\tinstance: %ld\n", index, instance); #endif
auth_context.sig_size = 0;
g = fopen(bin, "r"); if (!g) {
@@ -281,11 +466,36 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, printf("cannot allocate memory: %zx\n", (size_t)bin_stat.st_size); goto err_1; }
- f = fopen(path, "w");
- if (!f) {
printf("cannot open %s\n", path);
- size = fread(data, 1, bin_stat.st_size, g);
- if (size < bin_stat.st_size) {
}printf("read failed (%zx)\n", size); goto err_2;
- /* first, calculate signature to determine its size */
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
- if (privkey_file && cert_file) {
auth_context.key_file = privkey_file;
auth_context.cert_file = cert_file;
auth_context.auth.monotonic_count = mcount;
auth_context.image_data = data;
auth_context.image_size = bin_stat.st_size;
if (create_auth_data(&auth_context)) {
printf("Signing firmware image failed\n");
goto err_3;
}
if (dump_sig &&
dump_signature(path, auth_context.sig_data,
auth_context.sig_size)) {
printf("Creating signature file failed\n");
goto err_3;
}
- }
+#endif
- header.capsule_guid = efi_guid_fm_capsule; header.header_size = sizeof(header); /* TODO: The current implementation ignores flags */
@@ -294,11 +504,20 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, + sizeof(capsule) + sizeof(u64) + sizeof(image) + bin_stat.st_size;
if (auth_context.sig_size)
header.capsule_image_size += sizeof(auth_context.auth)
+ auth_context.sig_size;
f = fopen(path, "w");
if (!f) {
printf("cannot open %s\n", path);
goto err_3;
}
size = fwrite(&header, 1, sizeof(header), f); if (size < sizeof(header)) { printf("write failed (%zx)\n", size);
goto err_3;
goto err_4;
}
capsule.version = 0x00000001;
@@ -307,13 +526,13 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, size = fwrite(&capsule, 1, sizeof(capsule), f); if (size < (sizeof(capsule))) { printf("write failed (%zx)\n", size);
goto err_3;
} offset = sizeof(capsule) + sizeof(u64); size = fwrite(&offset, 1, sizeof(offset), f); if (size < sizeof(offset)) { printf("write failed (%zx)\n", size);goto err_4;
goto err_3;
goto err_4;
}
image.version = 0x00000003;
@@ -323,34 +542,61 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, image.reserved[1] = 0; image.reserved[2] = 0; image.update_image_size = bin_stat.st_size;
if (auth_context.sig_size)
image.update_image_size += sizeof(auth_context.auth)
+ auth_context.sig_size;
image.update_vendor_code_size = 0; /* none */ image.update_hardware_instance = instance; image.image_capsule_support = 0;
if (auth_context.sig_size)
image.image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION;
size = fwrite(&image, 1, sizeof(image), f); if (size < sizeof(image)) { printf("write failed (%zx)\n", size);
goto err_3;
}goto err_4;
- size = fread(data, 1, bin_stat.st_size, g);
- if (size < bin_stat.st_size) {
printf("read failed (%zx)\n", size);
goto err_3;
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
We don't want to use #if if avoidable.
For this specific chunk of code, we can remove #ifdef, but we should not remove #ifdef elsewhere.
-Takahiro Akashi
- if (auth_context.sig_size) {
size = fwrite(&auth_context.auth, 1,
sizeof(auth_context.auth), f);
if (size < sizeof(auth_context.auth)) {
printf("write failed (%zx)\n", size);
goto err_4;
}
size = fwrite(auth_context.sig_data, 1,
auth_context.sig_size, f);
if (size < auth_context.sig_size) {
printf("write failed (%zx)\n", size);
goto err_4;
}}
+#endif
- size = fwrite(data, 1, bin_stat.st_size, f); if (size < bin_stat.st_size) { printf("write failed (%zx)\n", size);
goto err_3;
goto err_4;
}
fclose(f); fclose(g); free(data);
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
- if (auth_context.sig_size)
OPENSSL_free(auth_context.sig_data);
+#endif
return 0;
-err_3: +err_4: fclose(f); +err_3: +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
- if (auth_context.sig_size)
OPENSSL_free(auth_context.sig_data);
+#endif err_2: free(data); err_1: @@ -359,10 +605,6 @@ err_1: return -1; }
-/*
- Usage:
- $ mkeficapsule -f <firmware binary> <output file>
- */ int main(int argc, char **argv) { char *file;
@@ -370,6 +612,8 @@ int main(int argc, char **argv) char *dtb_file; efi_guid_t *guid; unsigned long index, instance;
- uint64_t mcount;
- char *privkey_file, *cert_file; int c, idx; int ret; bool overlay = false;
@@ -380,8 +624,12 @@ int main(int argc, char **argv) guid = NULL; index = 0; instance = 0;
- mcount = 0;
- privkey_file = NULL;
- cert_file = NULL;
- dump_sig = 0; for (;;) {
c = getopt_long(argc, argv, "f:r:i:I:v:D:K:Oh", options, &idx);
c = getopt_long(argc, argv, opts_short, options, &idx); if (c == -1) break;
@@ -422,6 +670,28 @@ int main(int argc, char **argv) } dtb_file = optarg; break; +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
Best regards
Heinrich
case 'P':
if (privkey_file) {
printf("Private Key already specified\n");
return -1;
}
privkey_file = optarg;
break;
case 'C':
if (cert_file) {
printf("Certificate file already specified\n");
return -1;
}
cert_file = optarg;
break;
case 'm':
mcount = strtoul(optarg, NULL, 0);
break;
case 'd':
dump_sig = 1;
break;
+#endif case 'O': overlay = true; break; @@ -431,8 +701,12 @@ int main(int argc, char **argv) } }
- /* need a fit image file or raw image file */
- if (!file && !pkey_file && !dtb_file) {
- /* check necessary parameters */
- if ((file && (!(optind < argc) ||
(privkey_file && !cert_file) ||
(!privkey_file && cert_file))) ||
((pkey_file && !dtb_file) ||
}(!pkey_file && dtb_file))) { print_usage(); exit(EXIT_FAILURE);
@@ -442,12 +716,12 @@ int main(int argc, char **argv) if (ret == -1) { printf("Adding public key to the dtb failed\n"); exit(EXIT_FAILURE);
} else {
exit(EXIT_SUCCESS); }
}
if (create_fwbin(argv[optind], file, guid, index, instance)
- if (optind < argc &&
create_fwbin(argv[optind], file, guid, index, instance,
mcount, privkey_file, cert_file) < 0) { printf("Creating firmware capsule failed\n"); exit(EXIT_FAILURE);

On 5/13/21 7:12 AM, Masami Hiramatsu wrote:
Hi Heinrich,
2021年5月13日(木) 13:22 Heinrich Schuchardt xypron.glpk@gmx.de:
On 5/13/21 5:08 AM, AKASHI Takahiro wrote:
On Wed, May 12, 2021 at 10:56:41AM +0200, Heinrich Schuchardt wrote:
On 12.05.21 06:57, AKASHI Takahiro wrote:
With this enhancement, mkeficapsule will be able to create a capsule file with a signature which will be verified later by FMP's SetImage().
We will have to specify addtional command parameters: -monotonic-cout <count> : monotonic count -private-key <private key file> : private key file -certificate <certificate file> : certificate file Only when those parameters are given, a signature will be added to a capsule file.
Users are expected to maintain the monotonic count for each firmware image.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
tools/Makefile | 4 + tools/mkeficapsule.c | 324 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 303 insertions(+), 25 deletions(-)
diff --git a/tools/Makefile b/tools/Makefile index d020c55d6644..02eae0286e20 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -231,6 +231,10 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs hostprogs-$(CONFIG_ASN1_COMPILER) += asn1_compiler HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
+ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),) +HOSTLDLIBS_mkeficapsule += \
- $(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto")
I don't expect any user wants to install two tool versions in parallel.
The tool should always be able to add a signature. Adding a signature must be optional.
It seems to me that those two statements mutually contradict. Or do you intend to say that we should have a separate kconfig option to enable/disable signing feature in mkeficapsule?
If so, I can agree.
In either way, we should have an option to turn on/off this functionality as not all users use signed capsules.
I want to have a single binary to distribute with Linux distros (e.g. Debian/Ubuntu package u-boot-tools).
I couldn't catch your point. If so, the distros can build u-boot with CONFIG_EFI_CAPSULE_AUTHENTICATE=y...
Why should the tool depend on board configuration? Who would want capsule updates without authentication?
BTW, IMHO, if u-boot.bin can not find the ESL in the device tree, it should skip authentication too.
In this case the capsule should be rejected (if CONFIG_EFI_CAPSULE_AUTHENTICATE=y).
Then, user can choose whether enabling capsule authentication or not by embedding ESL into their devicetree.
The user shall not be able to decide anything that might hamper security. The U-Boot binary must dictate if a capsule is safe.
Best regards
Heinrich
Thank you
This should allow both
- create signed capsules
- create unsigned capsules
The user shall select signing via command line parameters.
Support for signing via the tool shall not depend on board Kconfig parameters.
Best regards
Heinrich
+endif mkeficapsule-objs := mkeficapsule.o $(LIBFDT_OBJS) hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c index de0a62898886..34ff1bdd82eb 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -18,7 +18,17 @@ #include <sys/stat.h> #include <sys/types.h>
-#include "fdt_host.h" +#include <linux/kconfig.h> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
+#include <openssl/asn1.h> +#include <openssl/bio.h> +#include <openssl/evp.h> +#include <openssl/err.h> +#include <openssl/pem.h> +#include <openssl/pkcs7.h> +#endif
+#include <linux/libfdt.h>
typedef __u8 u8; typedef __u16 u16; @@ -46,6 +56,13 @@ efi_guid_t efi_guid_image_type_uboot_fit = EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID; efi_guid_t efi_guid_image_type_uboot_raw = EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID; +efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
+static const char *opts_short = "f:r:i:I:v:D:K:P:C:m:dOh"; +#else +static const char *opts_short = "f:r:i:I:v:D:K:Oh"; +#endif
static struct option options[] = { {"fit", required_argument, NULL, 'f'}, @@ -54,6 +71,12 @@ static struct option options[] = { {"instance", required_argument, NULL, 'I'}, {"dtb", required_argument, NULL, 'D'}, {"public key", required_argument, NULL, 'K'}, +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
- {"private-key", required_argument, NULL, 'P'},
- {"certificate", required_argument, NULL, 'C'},
- {"monotonic-count", required_argument, NULL, 'm'},
These options should not be required.
I don't get you. What do you mean?
- {"dump-sig", no_argument, NULL, 'd'},
+#endif {"overlay", no_argument, NULL, 'O'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0}, @@ -70,6 +93,12 @@ static void print_usage(void) "\t-I, --instance <instance> update hardware instance\n" "\t-K, --public-key <key file> public key esl file\n" "\t-D, --dtb <dtb file> dtb file\n" +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
"\t-P, --private-key <privkey file> private key file\n"
"\t-C, --certificate <cert file> signer's certificate file\n"
"\t-m, --monotonic-count <count> monotonic count\n"
"\t-d, --dump_sig dump signature (*.p7)\n"
+#endif "\t-O, --overlay the dtb file is an overlay\n" "\t-h, --help print a help message\n", tool_name); @@ -249,12 +278,167 @@ err: return ret; }
+struct auth_context {
- char *key_file;
- char *cert_file;
- u8 *image_data;
- size_t image_size;
- struct efi_firmware_image_authentication auth;
- u8 *sig_data;
- size_t sig_size;
+};
+static int dump_sig;
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
+static EVP_PKEY *fileio_read_pkey(const char *filename) +{
- EVP_PKEY *key = NULL;
- BIO *bio;
- bio = BIO_new_file(filename, "r");
- if (!bio)
goto out;
- key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
+out:
- BIO_free_all(bio);
- if (!key) {
printf("Can't load key from file '%s'\n", filename);
Please, you use fprintf(stderr,) for error messages.
ERR_print_errors_fp(stderr);
- }
- return key;
+}
+static X509 *fileio_read_cert(const char *filename) +{
- X509 *cert = NULL;
- BIO *bio;
- bio = BIO_new_file(filename, "r");
- if (!bio)
goto out;
- cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+out:
- BIO_free_all(bio);
- if (!cert) {
printf("Can't load certificate from file '%s'\n", filename);
fprintf(stderr,)
ERR_print_errors_fp(stderr);
- }
- return cert;
+}
+static int create_auth_data(struct auth_context *ctx) +{
- EVP_PKEY *key = NULL;
- X509 *cert = NULL;
- BIO *data_bio = NULL;
- const EVP_MD *md;
- PKCS7 *p7;
- int flags, ret = -1;
- OpenSSL_add_all_digests();
- OpenSSL_add_all_ciphers();
- ERR_load_crypto_strings();
- key = fileio_read_pkey(ctx->key_file);
- if (!key)
goto err;
- cert = fileio_read_cert(ctx->cert_file);
- if (!cert)
goto err;
- /*
- create a BIO, containing:
- firmware image
- monotonic count
- in this order!
- See EDK2's FmpAuthenticatedHandlerRsa2048Sha256()
- */
- data_bio = BIO_new(BIO_s_mem());
- BIO_write(data_bio, ctx->image_data, ctx->image_size);
- BIO_write(data_bio, &ctx->auth.monotonic_count,
sizeof(ctx->auth.monotonic_count));
- md = EVP_get_digestbyname("SHA256");
- if (!md)
goto err;
- /* create signature */
- /* TODO: maybe add PKCS7_NOATTR and PKCS7_NOSMIMECAP */
PKCS7_NOATTR is a value without any documentation in the code.
Nak. Those macros are part of openssl library. See openssl/pkcs7.h.
Please, replace variable names by a long text describing what it missing.
- flags = PKCS7_BINARY | PKCS7_DETACHED;
Those constants lack documentation in the code.
Nak again.
- p7 = PKCS7_sign(NULL, NULL, NULL, data_bio, flags | PKCS7_PARTIAL);
- if (!p7)
goto err;
- if (!PKCS7_sign_add_signer(p7, cert, key, md, flags))
goto err;
- if (!PKCS7_final(p7, data_bio, flags))
goto err;
- /* convert pkcs7 into DER */
- ctx->sig_data = NULL;
- ctx->sig_size = ASN1_item_i2d((ASN1_VALUE *)p7, &ctx->sig_data,
ASN1_ITEM_rptr(PKCS7));
- if (!ctx->sig_size)
goto err;
- /* fill auth_info */
- ctx->auth.auth_info.hdr.dwLength = sizeof(ctx->auth.auth_info)
+ ctx->sig_size;
- ctx->auth.auth_info.hdr.wRevision = WIN_CERT_REVISION_2_0;
- ctx->auth.auth_info.hdr.wCertificateType = WIN_CERT_TYPE_EFI_GUID;
- memcpy(&ctx->auth.auth_info.cert_type, &efi_guid_cert_type_pkcs7,
sizeof(efi_guid_cert_type_pkcs7));
- ret = 0;
+err:
- BIO_free_all(data_bio);
- EVP_PKEY_free(key);
- X509_free(cert);
- return ret;
+}
+static int dump_signature(const char *path, u8 *signature, size_t sig_size) +{
- char *sig_path;
- FILE *f;
- size_t size;
- int ret = -1;
- sig_path = malloc(strlen(path) + 3 + 1);
- if (!sig_path)
return ret;
- sprintf(sig_path, "%s.p7", path);
- f = fopen(sig_path, "w");
- if (!f)
goto err;
- size = fwrite(signature, 1, sig_size, f);
- if (size == sig_size)
ret = 0;
- fclose(f);
+err:
- free(sig_path);
- return ret;
+} +#endif
- static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
unsigned long index, unsigned long instance)
unsigned long index, unsigned long instance,
{ struct efi_capsule_header header; struct efi_firmware_management_capsule_header capsule; struct efi_firmware_management_capsule_image_header image;uint64_t mcount, char *privkey_file, char *cert_file)
- struct auth_context auth_context; FILE *f, *g; struct stat bin_stat; u8 *data;
@@ -266,6 +450,7 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, printf("\tbin: %s\n\ttype: %pUl\n", bin, guid); printf("\tindex: %ld\n\tinstance: %ld\n", index, instance); #endif
auth_context.sig_size = 0;
g = fopen(bin, "r"); if (!g) {
@@ -281,11 +466,36 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, printf("cannot allocate memory: %zx\n", (size_t)bin_stat.st_size); goto err_1; }
- f = fopen(path, "w");
- if (!f) {
printf("cannot open %s\n", path);
- size = fread(data, 1, bin_stat.st_size, g);
- if (size < bin_stat.st_size) {
}printf("read failed (%zx)\n", size); goto err_2;
- /* first, calculate signature to determine its size */
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
- if (privkey_file && cert_file) {
auth_context.key_file = privkey_file;
auth_context.cert_file = cert_file;
auth_context.auth.monotonic_count = mcount;
auth_context.image_data = data;
auth_context.image_size = bin_stat.st_size;
if (create_auth_data(&auth_context)) {
printf("Signing firmware image failed\n");
goto err_3;
}
if (dump_sig &&
dump_signature(path, auth_context.sig_data,
auth_context.sig_size)) {
printf("Creating signature file failed\n");
goto err_3;
}
- }
+#endif
- header.capsule_guid = efi_guid_fm_capsule; header.header_size = sizeof(header); /* TODO: The current implementation ignores flags */
@@ -294,11 +504,20 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, + sizeof(capsule) + sizeof(u64) + sizeof(image) + bin_stat.st_size;
if (auth_context.sig_size)
header.capsule_image_size += sizeof(auth_context.auth)
+ auth_context.sig_size;
f = fopen(path, "w");
if (!f) {
printf("cannot open %s\n", path);
goto err_3;
}
size = fwrite(&header, 1, sizeof(header), f); if (size < sizeof(header)) { printf("write failed (%zx)\n", size);
goto err_3;
goto err_4;
}
capsule.version = 0x00000001;
@@ -307,13 +526,13 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, size = fwrite(&capsule, 1, sizeof(capsule), f); if (size < (sizeof(capsule))) { printf("write failed (%zx)\n", size);
goto err_3;
} offset = sizeof(capsule) + sizeof(u64); size = fwrite(&offset, 1, sizeof(offset), f); if (size < sizeof(offset)) { printf("write failed (%zx)\n", size);goto err_4;
goto err_3;
goto err_4;
}
image.version = 0x00000003;
@@ -323,34 +542,61 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, image.reserved[1] = 0; image.reserved[2] = 0; image.update_image_size = bin_stat.st_size;
if (auth_context.sig_size)
image.update_image_size += sizeof(auth_context.auth)
+ auth_context.sig_size;
image.update_vendor_code_size = 0; /* none */ image.update_hardware_instance = instance; image.image_capsule_support = 0;
if (auth_context.sig_size)
image.image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION;
size = fwrite(&image, 1, sizeof(image), f); if (size < sizeof(image)) { printf("write failed (%zx)\n", size);
goto err_3;
}goto err_4;
- size = fread(data, 1, bin_stat.st_size, g);
- if (size < bin_stat.st_size) {
printf("read failed (%zx)\n", size);
goto err_3;
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
We don't want to use #if if avoidable.
For this specific chunk of code, we can remove #ifdef, but we should not remove #ifdef elsewhere.
-Takahiro Akashi
- if (auth_context.sig_size) {
size = fwrite(&auth_context.auth, 1,
sizeof(auth_context.auth), f);
if (size < sizeof(auth_context.auth)) {
printf("write failed (%zx)\n", size);
goto err_4;
}
size = fwrite(auth_context.sig_data, 1,
auth_context.sig_size, f);
if (size < auth_context.sig_size) {
printf("write failed (%zx)\n", size);
goto err_4;
}}
+#endif
- size = fwrite(data, 1, bin_stat.st_size, f); if (size < bin_stat.st_size) { printf("write failed (%zx)\n", size);
goto err_3;
goto err_4;
}
fclose(f); fclose(g); free(data);
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
- if (auth_context.sig_size)
OPENSSL_free(auth_context.sig_data);
+#endif
return 0;
-err_3: +err_4: fclose(f); +err_3: +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
- if (auth_context.sig_size)
OPENSSL_free(auth_context.sig_data);
+#endif err_2: free(data); err_1: @@ -359,10 +605,6 @@ err_1: return -1; }
-/*
- Usage:
- $ mkeficapsule -f <firmware binary> <output file>
- */ int main(int argc, char **argv) { char *file;
@@ -370,6 +612,8 @@ int main(int argc, char **argv) char *dtb_file; efi_guid_t *guid; unsigned long index, instance;
- uint64_t mcount;
- char *privkey_file, *cert_file; int c, idx; int ret; bool overlay = false;
@@ -380,8 +624,12 @@ int main(int argc, char **argv) guid = NULL; index = 0; instance = 0;
- mcount = 0;
- privkey_file = NULL;
- cert_file = NULL;
- dump_sig = 0; for (;;) {
c = getopt_long(argc, argv, "f:r:i:I:v:D:K:Oh", options, &idx);
c = getopt_long(argc, argv, opts_short, options, &idx); if (c == -1) break;
@@ -422,6 +670,28 @@ int main(int argc, char **argv) } dtb_file = optarg; break; +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
Best regards
Heinrich
case 'P':
if (privkey_file) {
printf("Private Key already specified\n");
return -1;
}
privkey_file = optarg;
break;
case 'C':
if (cert_file) {
printf("Certificate file already specified\n");
return -1;
}
cert_file = optarg;
break;
case 'm':
mcount = strtoul(optarg, NULL, 0);
break;
case 'd':
dump_sig = 1;
break;
+#endif case 'O': overlay = true; break; @@ -431,8 +701,12 @@ int main(int argc, char **argv) } }
- /* need a fit image file or raw image file */
- if (!file && !pkey_file && !dtb_file) {
- /* check necessary parameters */
- if ((file && (!(optind < argc) ||
(privkey_file && !cert_file) ||
(!privkey_file && cert_file))) ||
((pkey_file && !dtb_file) ||
}(!pkey_file && dtb_file))) { print_usage(); exit(EXIT_FAILURE);
@@ -442,12 +716,12 @@ int main(int argc, char **argv) if (ret == -1) { printf("Adding public key to the dtb failed\n"); exit(EXIT_FAILURE);
} else {
exit(EXIT_SUCCESS); }
}
if (create_fwbin(argv[optind], file, guid, index, instance)
- if (optind < argc &&
create_fwbin(argv[optind], file, guid, index, instance,
mcount, privkey_file, cert_file) < 0) { printf("Creating firmware capsule failed\n"); exit(EXIT_FAILURE);

Hi Heinrich,
2021年5月13日(木) 14:50 Heinrich Schuchardt xypron.glpk@gmx.de:
On 5/13/21 7:12 AM, Masami Hiramatsu wrote:
Hi Heinrich,
2021年5月13日(木) 13:22 Heinrich Schuchardt xypron.glpk@gmx.de:
On 5/13/21 5:08 AM, AKASHI Takahiro wrote:
On Wed, May 12, 2021 at 10:56:41AM +0200, Heinrich Schuchardt wrote:
On 12.05.21 06:57, AKASHI Takahiro wrote:
With this enhancement, mkeficapsule will be able to create a capsule file with a signature which will be verified later by FMP's SetImage().
We will have to specify addtional command parameters: -monotonic-cout <count> : monotonic count -private-key <private key file> : private key file -certificate <certificate file> : certificate file Only when those parameters are given, a signature will be added to a capsule file.
Users are expected to maintain the monotonic count for each firmware image.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
tools/Makefile | 4 + tools/mkeficapsule.c | 324 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 303 insertions(+), 25 deletions(-)
diff --git a/tools/Makefile b/tools/Makefile index d020c55d6644..02eae0286e20 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -231,6 +231,10 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs hostprogs-$(CONFIG_ASN1_COMPILER) += asn1_compiler HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
+ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),) +HOSTLDLIBS_mkeficapsule += \
- $(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto")
I don't expect any user wants to install two tool versions in parallel.
The tool should always be able to add a signature. Adding a signature must be optional.
It seems to me that those two statements mutually contradict. Or do you intend to say that we should have a separate kconfig option to enable/disable signing feature in mkeficapsule?
If so, I can agree.
In either way, we should have an option to turn on/off this functionality as not all users use signed capsules.
I want to have a single binary to distribute with Linux distros (e.g. Debian/Ubuntu package u-boot-tools).
I couldn't catch your point. If so, the distros can build u-boot with CONFIG_EFI_CAPSULE_AUTHENTICATE=y...
Why should the tool depend on board configuration?
Yeah, at this point I agreed. I think there should be a separated CONFIG for tools or forcibly link those libraries. (I think most people don't mind if it requires new libraries to be built, that usually happens.)
Who would want capsule updates without authentication?
Hm, so you think even CONFIG_EFI_CAPSULE_AUTHENTICATE is only for development. Capsule must be signed, right? Then, all distro should build u-boot with CONFIG_EFI_CAPSULE_AUTHENTICATE=y, isn't it?
BTW, IMHO, if u-boot.bin can not find the ESL in the device tree, it should skip authentication too.
In this case the capsule should be rejected (if CONFIG_EFI_CAPSULE_AUTHENTICATE=y).
I meant U-Boot has NO key to authenticate the capsule. I think in that case U-Boot build process must require the key (ESL) and if user doesn't provide it, the build should fail (if it doesn't skip capsule authentication.) Or, we have no way to update U-Boot anymore.
Then, user can choose whether enabling capsule authentication or not by embedding ESL into their devicetree.
The user shall not be able to decide anything that might hamper security. The U-Boot binary must dictate if a capsule is safe.
Hmm, I think the root issue is that the ESL embedding process is not integrated into the build process yet. For the safe capsule update, we must enable capsule authentication with keys. (unsafe one is only for testing/development) Moreover, the key is stored in the U-Boot itself OR, in the secure storage outside of U-Boot (Hardware OTP or TPM/HSM are preferable.)
Thus, CONFIG_EFI_CAPSULE_AUTHENTICATE must depend on (or select) a new config which points the path for the ESL file (this is embedded while the build process), OR, the platform driver provides key from its hardware secure storage.
What would you think about this idea?
Thank you,
Best regards
Heinrich
Thank you
This should allow both
- create signed capsules
- create unsigned capsules
The user shall select signing via command line parameters.
Support for signing via the tool shall not depend on board Kconfig parameters.
Best regards
Heinrich
+endif mkeficapsule-objs := mkeficapsule.o $(LIBFDT_OBJS) hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c index de0a62898886..34ff1bdd82eb 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -18,7 +18,17 @@ #include <sys/stat.h> #include <sys/types.h>
-#include "fdt_host.h" +#include <linux/kconfig.h> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
+#include <openssl/asn1.h> +#include <openssl/bio.h> +#include <openssl/evp.h> +#include <openssl/err.h> +#include <openssl/pem.h> +#include <openssl/pkcs7.h> +#endif
+#include <linux/libfdt.h>
typedef __u8 u8; typedef __u16 u16; @@ -46,6 +56,13 @@ efi_guid_t efi_guid_image_type_uboot_fit = EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID; efi_guid_t efi_guid_image_type_uboot_raw = EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID; +efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
+static const char *opts_short = "f:r:i:I:v:D:K:P:C:m:dOh"; +#else +static const char *opts_short = "f:r:i:I:v:D:K:Oh"; +#endif
static struct option options[] = { {"fit", required_argument, NULL, 'f'}, @@ -54,6 +71,12 @@ static struct option options[] = { {"instance", required_argument, NULL, 'I'}, {"dtb", required_argument, NULL, 'D'}, {"public key", required_argument, NULL, 'K'}, +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
- {"private-key", required_argument, NULL, 'P'},
- {"certificate", required_argument, NULL, 'C'},
- {"monotonic-count", required_argument, NULL, 'm'},
These options should not be required.
I don't get you. What do you mean?
- {"dump-sig", no_argument, NULL, 'd'},
+#endif {"overlay", no_argument, NULL, 'O'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0}, @@ -70,6 +93,12 @@ static void print_usage(void) "\t-I, --instance <instance> update hardware instance\n" "\t-K, --public-key <key file> public key esl file\n" "\t-D, --dtb <dtb file> dtb file\n" +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
"\t-P, --private-key <privkey file> private key file\n"
"\t-C, --certificate <cert file> signer's certificate file\n"
"\t-m, --monotonic-count <count> monotonic count\n"
"\t-d, --dump_sig dump signature (*.p7)\n"
+#endif "\t-O, --overlay the dtb file is an overlay\n" "\t-h, --help print a help message\n", tool_name); @@ -249,12 +278,167 @@ err: return ret; }
+struct auth_context {
- char *key_file;
- char *cert_file;
- u8 *image_data;
- size_t image_size;
- struct efi_firmware_image_authentication auth;
- u8 *sig_data;
- size_t sig_size;
+};
+static int dump_sig;
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
+static EVP_PKEY *fileio_read_pkey(const char *filename) +{
- EVP_PKEY *key = NULL;
- BIO *bio;
- bio = BIO_new_file(filename, "r");
- if (!bio)
goto out;
- key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
+out:
- BIO_free_all(bio);
- if (!key) {
printf("Can't load key from file '%s'\n", filename);
Please, you use fprintf(stderr,) for error messages.
ERR_print_errors_fp(stderr);
- }
- return key;
+}
+static X509 *fileio_read_cert(const char *filename) +{
- X509 *cert = NULL;
- BIO *bio;
- bio = BIO_new_file(filename, "r");
- if (!bio)
goto out;
- cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+out:
- BIO_free_all(bio);
- if (!cert) {
printf("Can't load certificate from file '%s'\n", filename);
fprintf(stderr,)
ERR_print_errors_fp(stderr);
- }
- return cert;
+}
+static int create_auth_data(struct auth_context *ctx) +{
- EVP_PKEY *key = NULL;
- X509 *cert = NULL;
- BIO *data_bio = NULL;
- const EVP_MD *md;
- PKCS7 *p7;
- int flags, ret = -1;
- OpenSSL_add_all_digests();
- OpenSSL_add_all_ciphers();
- ERR_load_crypto_strings();
- key = fileio_read_pkey(ctx->key_file);
- if (!key)
goto err;
- cert = fileio_read_cert(ctx->cert_file);
- if (!cert)
goto err;
- /*
- create a BIO, containing:
- firmware image
- monotonic count
- in this order!
- See EDK2's FmpAuthenticatedHandlerRsa2048Sha256()
- */
- data_bio = BIO_new(BIO_s_mem());
- BIO_write(data_bio, ctx->image_data, ctx->image_size);
- BIO_write(data_bio, &ctx->auth.monotonic_count,
sizeof(ctx->auth.monotonic_count));
- md = EVP_get_digestbyname("SHA256");
- if (!md)
goto err;
- /* create signature */
- /* TODO: maybe add PKCS7_NOATTR and PKCS7_NOSMIMECAP */
PKCS7_NOATTR is a value without any documentation in the code.
Nak. Those macros are part of openssl library. See openssl/pkcs7.h.
Please, replace variable names by a long text describing what it missing.
- flags = PKCS7_BINARY | PKCS7_DETACHED;
Those constants lack documentation in the code.
Nak again.
- p7 = PKCS7_sign(NULL, NULL, NULL, data_bio, flags | PKCS7_PARTIAL);
- if (!p7)
goto err;
- if (!PKCS7_sign_add_signer(p7, cert, key, md, flags))
goto err;
- if (!PKCS7_final(p7, data_bio, flags))
goto err;
- /* convert pkcs7 into DER */
- ctx->sig_data = NULL;
- ctx->sig_size = ASN1_item_i2d((ASN1_VALUE *)p7, &ctx->sig_data,
ASN1_ITEM_rptr(PKCS7));
- if (!ctx->sig_size)
goto err;
- /* fill auth_info */
- ctx->auth.auth_info.hdr.dwLength = sizeof(ctx->auth.auth_info)
+ ctx->sig_size;
- ctx->auth.auth_info.hdr.wRevision = WIN_CERT_REVISION_2_0;
- ctx->auth.auth_info.hdr.wCertificateType = WIN_CERT_TYPE_EFI_GUID;
- memcpy(&ctx->auth.auth_info.cert_type, &efi_guid_cert_type_pkcs7,
sizeof(efi_guid_cert_type_pkcs7));
- ret = 0;
+err:
- BIO_free_all(data_bio);
- EVP_PKEY_free(key);
- X509_free(cert);
- return ret;
+}
+static int dump_signature(const char *path, u8 *signature, size_t sig_size) +{
- char *sig_path;
- FILE *f;
- size_t size;
- int ret = -1;
- sig_path = malloc(strlen(path) + 3 + 1);
- if (!sig_path)
return ret;
- sprintf(sig_path, "%s.p7", path);
- f = fopen(sig_path, "w");
- if (!f)
goto err;
- size = fwrite(signature, 1, sig_size, f);
- if (size == sig_size)
ret = 0;
- fclose(f);
+err:
- free(sig_path);
- return ret;
+} +#endif
- static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
unsigned long index, unsigned long instance)
unsigned long index, unsigned long instance,
{ struct efi_capsule_header header; struct efi_firmware_management_capsule_header capsule; struct efi_firmware_management_capsule_image_header image;uint64_t mcount, char *privkey_file, char *cert_file)
- struct auth_context auth_context; FILE *f, *g; struct stat bin_stat; u8 *data;
@@ -266,6 +450,7 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, printf("\tbin: %s\n\ttype: %pUl\n", bin, guid); printf("\tindex: %ld\n\tinstance: %ld\n", index, instance); #endif
auth_context.sig_size = 0;
g = fopen(bin, "r"); if (!g) {
@@ -281,11 +466,36 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, printf("cannot allocate memory: %zx\n", (size_t)bin_stat.st_size); goto err_1; }
- f = fopen(path, "w");
- if (!f) {
printf("cannot open %s\n", path);
- size = fread(data, 1, bin_stat.st_size, g);
- if (size < bin_stat.st_size) {
}printf("read failed (%zx)\n", size); goto err_2;
- /* first, calculate signature to determine its size */
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
- if (privkey_file && cert_file) {
auth_context.key_file = privkey_file;
auth_context.cert_file = cert_file;
auth_context.auth.monotonic_count = mcount;
auth_context.image_data = data;
auth_context.image_size = bin_stat.st_size;
if (create_auth_data(&auth_context)) {
printf("Signing firmware image failed\n");
goto err_3;
}
if (dump_sig &&
dump_signature(path, auth_context.sig_data,
auth_context.sig_size)) {
printf("Creating signature file failed\n");
goto err_3;
}
- }
+#endif
- header.capsule_guid = efi_guid_fm_capsule; header.header_size = sizeof(header); /* TODO: The current implementation ignores flags */
@@ -294,11 +504,20 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, + sizeof(capsule) + sizeof(u64) + sizeof(image) + bin_stat.st_size;
if (auth_context.sig_size)
header.capsule_image_size += sizeof(auth_context.auth)
+ auth_context.sig_size;
f = fopen(path, "w");
if (!f) {
printf("cannot open %s\n", path);
goto err_3;
}
size = fwrite(&header, 1, sizeof(header), f); if (size < sizeof(header)) { printf("write failed (%zx)\n", size);
goto err_3;
goto err_4;
}
capsule.version = 0x00000001;
@@ -307,13 +526,13 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, size = fwrite(&capsule, 1, sizeof(capsule), f); if (size < (sizeof(capsule))) { printf("write failed (%zx)\n", size);
goto err_3;
} offset = sizeof(capsule) + sizeof(u64); size = fwrite(&offset, 1, sizeof(offset), f); if (size < sizeof(offset)) { printf("write failed (%zx)\n", size);goto err_4;
goto err_3;
goto err_4;
}
image.version = 0x00000003;
@@ -323,34 +542,61 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, image.reserved[1] = 0; image.reserved[2] = 0; image.update_image_size = bin_stat.st_size;
if (auth_context.sig_size)
image.update_image_size += sizeof(auth_context.auth)
+ auth_context.sig_size;
image.update_vendor_code_size = 0; /* none */ image.update_hardware_instance = instance; image.image_capsule_support = 0;
if (auth_context.sig_size)
image.image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION;
size = fwrite(&image, 1, sizeof(image), f); if (size < sizeof(image)) { printf("write failed (%zx)\n", size);
goto err_3;
}goto err_4;
- size = fread(data, 1, bin_stat.st_size, g);
- if (size < bin_stat.st_size) {
printf("read failed (%zx)\n", size);
goto err_3;
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
We don't want to use #if if avoidable.
For this specific chunk of code, we can remove #ifdef, but we should not remove #ifdef elsewhere.
-Takahiro Akashi
- if (auth_context.sig_size) {
size = fwrite(&auth_context.auth, 1,
sizeof(auth_context.auth), f);
if (size < sizeof(auth_context.auth)) {
printf("write failed (%zx)\n", size);
goto err_4;
}
size = fwrite(auth_context.sig_data, 1,
auth_context.sig_size, f);
if (size < auth_context.sig_size) {
printf("write failed (%zx)\n", size);
goto err_4;
}}
+#endif
- size = fwrite(data, 1, bin_stat.st_size, f); if (size < bin_stat.st_size) { printf("write failed (%zx)\n", size);
goto err_3;
goto err_4;
}
fclose(f); fclose(g); free(data);
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
- if (auth_context.sig_size)
OPENSSL_free(auth_context.sig_data);
+#endif
return 0;
-err_3: +err_4: fclose(f); +err_3: +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
- if (auth_context.sig_size)
OPENSSL_free(auth_context.sig_data);
+#endif err_2: free(data); err_1: @@ -359,10 +605,6 @@ err_1: return -1; }
-/*
- Usage:
- $ mkeficapsule -f <firmware binary> <output file>
- */ int main(int argc, char **argv) { char *file;
@@ -370,6 +612,8 @@ int main(int argc, char **argv) char *dtb_file; efi_guid_t *guid; unsigned long index, instance;
- uint64_t mcount;
- char *privkey_file, *cert_file; int c, idx; int ret; bool overlay = false;
@@ -380,8 +624,12 @@ int main(int argc, char **argv) guid = NULL; index = 0; instance = 0;
- mcount = 0;
- privkey_file = NULL;
- cert_file = NULL;
- dump_sig = 0; for (;;) {
c = getopt_long(argc, argv, "f:r:i:I:v:D:K:Oh", options, &idx);
c = getopt_long(argc, argv, opts_short, options, &idx); if (c == -1) break;
@@ -422,6 +670,28 @@ int main(int argc, char **argv) } dtb_file = optarg; break; +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
Best regards
Heinrich
case 'P':
if (privkey_file) {
printf("Private Key already specified\n");
return -1;
}
privkey_file = optarg;
break;
case 'C':
if (cert_file) {
printf("Certificate file already specified\n");
return -1;
}
cert_file = optarg;
break;
case 'm':
mcount = strtoul(optarg, NULL, 0);
break;
case 'd':
dump_sig = 1;
break;
+#endif case 'O': overlay = true; break; @@ -431,8 +701,12 @@ int main(int argc, char **argv) } }
- /* need a fit image file or raw image file */
- if (!file && !pkey_file && !dtb_file) {
- /* check necessary parameters */
- if ((file && (!(optind < argc) ||
(privkey_file && !cert_file) ||
(!privkey_file && cert_file))) ||
((pkey_file && !dtb_file) ||
}(!pkey_file && dtb_file))) { print_usage(); exit(EXIT_FAILURE);
@@ -442,12 +716,12 @@ int main(int argc, char **argv) if (ret == -1) { printf("Adding public key to the dtb failed\n"); exit(EXIT_FAILURE);
} else {
exit(EXIT_SUCCESS); }
}
if (create_fwbin(argv[optind], file, guid, index, instance)
- if (optind < argc &&
create_fwbin(argv[optind], file, guid, index, instance,
mcount, privkey_file, cert_file) < 0) { printf("Creating firmware capsule failed\n"); exit(EXIT_FAILURE);

Am 13. Mai 2021 08:44:24 MESZ schrieb Masami Hiramatsu masami.hiramatsu@linaro.org:
Hi Heinrich,
2021年5月13日(木) 14:50 Heinrich Schuchardt xypron.glpk@gmx.de:
On 5/13/21 7:12 AM, Masami Hiramatsu wrote:
Hi Heinrich,
2021年5月13日(木) 13:22 Heinrich Schuchardt xypron.glpk@gmx.de:
On 5/13/21 5:08 AM, AKASHI Takahiro wrote:
On Wed, May 12, 2021 at 10:56:41AM +0200, Heinrich Schuchardt
wrote:
On 12.05.21 06:57, AKASHI Takahiro wrote: > With this enhancement, mkeficapsule will be able to create a
capsule
> file with a signature which will be verified later by FMP's
SetImage().
> > We will have to specify addtional command parameters: > -monotonic-cout <count> : monotonic count > -private-key <private key file> : private key file > -certificate <certificate file> : certificate file > Only when those parameters are given, a signature will be added > to a capsule file. > > Users are expected to maintain the monotonic count for each
firmware
> image. > > Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org > --- > tools/Makefile | 4 + > tools/mkeficapsule.c | 324
+++++++++++++++++++++++++++++++++++++++----
> 2 files changed, 303 insertions(+), 25 deletions(-) > > diff --git a/tools/Makefile b/tools/Makefile > index d020c55d6644..02eae0286e20 100644 > --- a/tools/Makefile > +++ b/tools/Makefile > @@ -231,6 +231,10 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs > hostprogs-$(CONFIG_ASN1_COMPILER) += asn1_compiler > HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include > > +ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),) > +HOSTLDLIBS_mkeficapsule += \ > + $(shell pkg-config --libs libssl libcrypto 2> /dev/null ||
echo "-lssl -lcrypto")
I don't expect any user wants to install two tool versions in
parallel.
The tool should always be able to add a signature. Adding a signature must be optional.
It seems to me that those two statements mutually contradict. Or do you intend to say that we should have a separate kconfig option to enable/disable signing feature in mkeficapsule?
If so, I can agree.
In either way, we should have an option to turn on/off this
functionality
as not all users use signed capsules.
I want to have a single binary to distribute with Linux distros
(e.g.
Debian/Ubuntu package u-boot-tools).
I couldn't catch your point. If so, the distros can build u-boot
with
CONFIG_EFI_CAPSULE_AUTHENTICATE=y...
Why should the tool depend on board configuration?
Yeah, at this point I agreed. I think there should be a separated CONFIG for tools or forcibly link those libraries. (I think most people don't mind if it requires new libraries to be built, that usually happens.)
Who would want capsule updates without authentication?
Hm, so you think even CONFIG_EFI_CAPSULE_AUTHENTICATE is only for development. Capsule must be signed, right? Then, all distro should build u-boot with CONFIG_EFI_CAPSULE_AUTHENTICATE=y, isn't it?
There will still be U-Boot images without capsule updates.
BTW, IMHO, if u-boot.bin can not find the ESL in the device tree, it should skip authentication too.
In this case the capsule should be rejected (if CONFIG_EFI_CAPSULE_AUTHENTICATE=y).
I meant U-Boot has NO key to authenticate the capsule. I think in that case U-Boot build process must require the key (ESL) and if user doesn't provide it, the build should fail (if it doesn't skip capsule authentication.) Or, we have no way to update U-Boot anymore.
Then, user can choose whether enabling capsule authentication or
not
by embedding ESL into their devicetree.
The user shall not be able to decide anything that might hamper security. The U-Boot binary must dictate if a capsule is safe.
Hmm, I think the root issue is that the ESL embedding process is not integrated into the build process yet. For the safe capsule update, we must enable capsule authentication with keys. (unsafe one is only for testing/development) Moreover, the key is stored in the U-Boot itself OR, in the secure storage outside of U-Boot (Hardware OTP or TPM/HSM are preferable.)
Thus, CONFIG_EFI_CAPSULE_AUTHENTICATE must depend on (or select) a new config which points the path for the ESL file (this is embedded while the build process), OR, the platform driver provides key from its hardware secure storage.
What would you think about this idea?
That is the direction I would like to go.
Best regards
Heinrich
Thank you,
Best regards
Heinrich
Thank you
This should allow both
- create signed capsules
- create unsigned capsules
The user shall select signing via command line parameters.
Support for signing via the tool shall not depend on board Kconfig parameters.
Best regards
Heinrich
> +endif > mkeficapsule-objs := mkeficapsule.o $(LIBFDT_OBJS) > hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule > > diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c > index de0a62898886..34ff1bdd82eb 100644 > --- a/tools/mkeficapsule.c > +++ b/tools/mkeficapsule.c > @@ -18,7 +18,17 @@ > #include <sys/stat.h> > #include <sys/types.h> > > -#include "fdt_host.h" > +#include <linux/kconfig.h> > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
> +#include <openssl/asn1.h> > +#include <openssl/bio.h> > +#include <openssl/evp.h> > +#include <openssl/err.h> > +#include <openssl/pem.h> > +#include <openssl/pkcs7.h> > +#endif > + > +#include <linux/libfdt.h> > > typedef __u8 u8; > typedef __u16 u16; > @@ -46,6 +56,13 @@ efi_guid_t efi_guid_image_type_uboot_fit = > EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID; > efi_guid_t efi_guid_image_type_uboot_raw = > EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID; > +efi_guid_t efi_guid_cert_type_pkcs7 =
EFI_CERT_TYPE_PKCS7_GUID;
> + > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
> +static const char *opts_short = "f:r:i:I:v:D:K:P:C:m:dOh"; > +#else > +static const char *opts_short = "f:r:i:I:v:D:K:Oh"; > +#endif > > static struct option options[] = { > {"fit", required_argument, NULL, 'f'}, > @@ -54,6 +71,12 @@ static struct option options[] = { > {"instance", required_argument, NULL, 'I'}, > {"dtb", required_argument, NULL, 'D'}, > {"public key", required_argument, NULL, 'K'}, > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE) > + {"private-key", required_argument, NULL, 'P'}, > + {"certificate", required_argument, NULL, 'C'}, > + {"monotonic-count", required_argument, NULL, 'm'},
These options should not be required.
I don't get you. What do you mean?
> + {"dump-sig", no_argument, NULL, 'd'}, > +#endif > {"overlay", no_argument, NULL, 'O'}, > {"help", no_argument, NULL, 'h'}, > {NULL, 0, NULL, 0}, > @@ -70,6 +93,12 @@ static void print_usage(void) > "\t-I, --instance <instance> update hardware
instance\n"
> "\t-K, --public-key <key file> public key esl
file\n"
> "\t-D, --dtb <dtb file> dtb file\n" > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
> + "\t-P, --private-key <privkey file> private key
file\n"
> + "\t-C, --certificate <cert file> signer's
certificate file\n"
> + "\t-m, --monotonic-count <count> monotonic
count\n"
> + "\t-d, --dump_sig dump signature
(*.p7)\n"
> +#endif > "\t-O, --overlay the dtb file is an
overlay\n"
> "\t-h, --help print a help
message\n",
> tool_name); > @@ -249,12 +278,167 @@ err: > return ret; > } > > +struct auth_context { > + char *key_file; > + char *cert_file; > + u8 *image_data; > + size_t image_size; > + struct efi_firmware_image_authentication auth; > + u8 *sig_data; > + size_t sig_size; > +}; > + > +static int dump_sig; > + > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
> +static EVP_PKEY *fileio_read_pkey(const char *filename) > +{ > + EVP_PKEY *key = NULL; > + BIO *bio; > + > + bio = BIO_new_file(filename, "r"); > + if (!bio) > + goto out; > + > + key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); > + > +out: > + BIO_free_all(bio); > + if (!key) { > + printf("Can't load key from file '%s'\n",
filename);
Please, you use fprintf(stderr,) for error messages.
> + ERR_print_errors_fp(stderr); > + } > + > + return key; > +} > + > +static X509 *fileio_read_cert(const char *filename) > +{ > + X509 *cert = NULL; > + BIO *bio; > + > + bio = BIO_new_file(filename, "r"); > + if (!bio) > + goto out; > + > + cert = PEM_read_bio_X509(bio, NULL, NULL, NULL); > + > +out: > + BIO_free_all(bio); > + if (!cert) { > + printf("Can't load certificate from file '%s'\n",
filename);
fprintf(stderr,)
> + ERR_print_errors_fp(stderr); > + } > + > + return cert; > +} > + > +static int create_auth_data(struct auth_context *ctx) > +{ > + EVP_PKEY *key = NULL; > + X509 *cert = NULL; > + BIO *data_bio = NULL; > + const EVP_MD *md; > + PKCS7 *p7; > + int flags, ret = -1; > + > + OpenSSL_add_all_digests(); > + OpenSSL_add_all_ciphers(); > + ERR_load_crypto_strings(); > + > + key = fileio_read_pkey(ctx->key_file); > + if (!key) > + goto err; > + cert = fileio_read_cert(ctx->cert_file); > + if (!cert) > + goto err; > + > + /* > + * create a BIO, containing: > + * * firmware image > + * * monotonic count > + * in this order! > + * See EDK2's FmpAuthenticatedHandlerRsa2048Sha256() > + */ > + data_bio = BIO_new(BIO_s_mem()); > + BIO_write(data_bio, ctx->image_data, ctx->image_size); > + BIO_write(data_bio, &ctx->auth.monotonic_count, > + sizeof(ctx->auth.monotonic_count)); > + > + md = EVP_get_digestbyname("SHA256"); > + if (!md) > + goto err; > + > + /* create signature */ > + /* TODO: maybe add PKCS7_NOATTR and PKCS7_NOSMIMECAP */
PKCS7_NOATTR is a value without any documentation in the code.
Nak. Those macros are part of openssl library. See openssl/pkcs7.h.
Please, replace variable names by a long text describing what it
missing.
> + flags = PKCS7_BINARY | PKCS7_DETACHED;
Those constants lack documentation in the code.
Nak again.
> + p7 = PKCS7_sign(NULL, NULL, NULL, data_bio, flags |
PKCS7_PARTIAL);
> + if (!p7) > + goto err; > + if (!PKCS7_sign_add_signer(p7, cert, key, md, flags)) > + goto err; > + if (!PKCS7_final(p7, data_bio, flags)) > + goto err; > + > + /* convert pkcs7 into DER */ > + ctx->sig_data = NULL; > + ctx->sig_size = ASN1_item_i2d((ASN1_VALUE *)p7,
&ctx->sig_data,
> + ASN1_ITEM_rptr(PKCS7)); > + if (!ctx->sig_size) > + goto err; > + > + /* fill auth_info */ > + ctx->auth.auth_info.hdr.dwLength =
sizeof(ctx->auth.auth_info)
> + + ctx->sig_size; > + ctx->auth.auth_info.hdr.wRevision = WIN_CERT_REVISION_2_0; > + ctx->auth.auth_info.hdr.wCertificateType =
WIN_CERT_TYPE_EFI_GUID;
> + memcpy(&ctx->auth.auth_info.cert_type,
&efi_guid_cert_type_pkcs7,
> + sizeof(efi_guid_cert_type_pkcs7)); > + > + ret = 0; > +err: > + BIO_free_all(data_bio); > + EVP_PKEY_free(key); > + X509_free(cert); > + > + return ret; > +} > + > +static int dump_signature(const char *path, u8 *signature,
size_t sig_size)
> +{ > + char *sig_path; > + FILE *f; > + size_t size; > + int ret = -1; > + > + sig_path = malloc(strlen(path) + 3 + 1); > + if (!sig_path) > + return ret; > + > + sprintf(sig_path, "%s.p7", path); > + f = fopen(sig_path, "w"); > + if (!f) > + goto err; > + > + size = fwrite(signature, 1, sig_size, f); > + if (size == sig_size) > + ret = 0; > + > + fclose(f); > +err: > + free(sig_path); > + return ret; > +} > +#endif > + > static int create_fwbin(char *path, char *bin, efi_guid_t
*guid,
> - unsigned long index, unsigned long
instance)
> + unsigned long index, unsigned long
instance,
> + uint64_t mcount, char *privkey_file, char
*cert_file)
> { > struct efi_capsule_header header; > struct efi_firmware_management_capsule_header capsule; > struct efi_firmware_management_capsule_image_header image; > + struct auth_context auth_context; > FILE *f, *g; > struct stat bin_stat; > u8 *data; > @@ -266,6 +450,7 @@ static int create_fwbin(char *path, char
*bin, efi_guid_t *guid,
> printf("\tbin: %s\n\ttype: %pUl\n", bin, guid);

On Thu, May 13, 2021 at 08:52:18AM +0200, Heinrich Schuchardt wrote:
Am 13. Mai 2021 08:44:24 MESZ schrieb Masami Hiramatsu masami.hiramatsu@linaro.org:
Hi Heinrich,
2021年5月13日(木) 14:50 Heinrich Schuchardt xypron.glpk@gmx.de:
On 5/13/21 7:12 AM, Masami Hiramatsu wrote:
Hi Heinrich,
2021年5月13日(木) 13:22 Heinrich Schuchardt xypron.glpk@gmx.de:
On 5/13/21 5:08 AM, AKASHI Takahiro wrote:
On Wed, May 12, 2021 at 10:56:41AM +0200, Heinrich Schuchardt
wrote:
> On 12.05.21 06:57, AKASHI Takahiro wrote: >> With this enhancement, mkeficapsule will be able to create a
capsule
>> file with a signature which will be verified later by FMP's
SetImage().
>> >> We will have to specify addtional command parameters: >> -monotonic-cout <count> : monotonic count >> -private-key <private key file> : private key file >> -certificate <certificate file> : certificate file >> Only when those parameters are given, a signature will be added >> to a capsule file. >> >> Users are expected to maintain the monotonic count for each
firmware
>> image. >> >> Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org >> --- >> tools/Makefile | 4 + >> tools/mkeficapsule.c | 324
+++++++++++++++++++++++++++++++++++++++----
>> 2 files changed, 303 insertions(+), 25 deletions(-) >> >> diff --git a/tools/Makefile b/tools/Makefile >> index d020c55d6644..02eae0286e20 100644 >> --- a/tools/Makefile >> +++ b/tools/Makefile >> @@ -231,6 +231,10 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs >> hostprogs-$(CONFIG_ASN1_COMPILER) += asn1_compiler >> HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include >> >> +ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),) >> +HOSTLDLIBS_mkeficapsule += \ >> + $(shell pkg-config --libs libssl libcrypto 2> /dev/null ||
echo "-lssl -lcrypto")
> > I don't expect any user wants to install two tool versions in
parallel.
> > The tool should always be able to add a signature. > Adding a signature must be optional.
It seems to me that those two statements mutually contradict. Or do you intend to say that we should have a separate kconfig option to enable/disable signing feature in mkeficapsule?
If so, I can agree.
In either way, we should have an option to turn on/off this
functionality
as not all users use signed capsules.
I want to have a single binary to distribute with Linux distros
(e.g.
Debian/Ubuntu package u-boot-tools).
I couldn't catch your point. If so, the distros can build u-boot
with
CONFIG_EFI_CAPSULE_AUTHENTICATE=y...
Why should the tool depend on board configuration?
Yeah, at this point I agreed. I think there should be a separated CONFIG for tools
I prefer this.
or forcibly link those libraries. (I think most people don't
mind if it requires new libraries to be built, that usually happens.)
Who would want capsule updates without authentication?
Hm, so you think even CONFIG_EFI_CAPSULE_AUTHENTICATE is only for development. Capsule must be signed, right? Then, all distro should build u-boot with CONFIG_EFI_CAPSULE_AUTHENTICATE=y, isn't it?
There will still be U-Boot images without capsule updates.
Users are not always distro. There may be users who want to use capsule updates for their own systems that they manage by themselves.
BTW, IMHO, if u-boot.bin can not find the ESL in the device tree, it should skip authentication too.
In this case the capsule should be rejected (if CONFIG_EFI_CAPSULE_AUTHENTICATE=y).
I meant U-Boot has NO key to authenticate the capsule. I think in that case U-Boot build process must require the key (ESL) and if user doesn't provide it, the build should fail (if it doesn't skip capsule authentication.) Or, we have no way to update U-Boot anymore.
Then, user can choose whether enabling capsule authentication or
not
by embedding ESL into their devicetree.
The user shall not be able to decide anything that might hamper security. The U-Boot binary must dictate if a capsule is safe.
Hmm, I think the root issue is that the ESL embedding process is not integrated into the build process yet.
Sughosh has already submitted the patch: https://lists.denx.de/pipermail/u-boot/2021-April/447183.html
My patch is based on top of that.
For the safe capsule update,
we must enable capsule authentication with keys. (unsafe one is only for testing/development) Moreover, the key is stored in the U-Boot itself OR, in the secure storage outside of U-Boot (Hardware OTP or TPM/HSM are preferable.)
Thus, CONFIG_EFI_CAPSULE_AUTHENTICATE must depend on (or select) a new config which points the path for the ESL file (this is embedded while the build process), OR, the platform driver provides key from its hardware secure storage.
What would you think about this idea?
That is the direction I would like to go.
Which part of Masami's comment do you intend to agree to? Sugosh's approach is the one that you and Sughosh has agreed in the past discussion. Do you want to revert it?
-Takahiro Akashi
Best regards
Heinrich
Thank you,
Best regards
Heinrich
Thank you
This should allow both
- create signed capsules
- create unsigned capsules
The user shall select signing via command line parameters.
Support for signing via the tool shall not depend on board Kconfig parameters.
Best regards
Heinrich
>> +endif >> mkeficapsule-objs := mkeficapsule.o $(LIBFDT_OBJS) >> hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule >> >> diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c >> index de0a62898886..34ff1bdd82eb 100644 >> --- a/tools/mkeficapsule.c >> +++ b/tools/mkeficapsule.c >> @@ -18,7 +18,17 @@ >> #include <sys/stat.h> >> #include <sys/types.h> >> >> -#include "fdt_host.h" >> +#include <linux/kconfig.h> >> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE) > > see above > >> +#include <openssl/asn1.h> >> +#include <openssl/bio.h> >> +#include <openssl/evp.h> >> +#include <openssl/err.h> >> +#include <openssl/pem.h> >> +#include <openssl/pkcs7.h> >> +#endif >> + >> +#include <linux/libfdt.h> >> >> typedef __u8 u8; >> typedef __u16 u16; >> @@ -46,6 +56,13 @@ efi_guid_t efi_guid_image_type_uboot_fit = >> EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID; >> efi_guid_t efi_guid_image_type_uboot_raw = >> EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID; >> +efi_guid_t efi_guid_cert_type_pkcs7 =
EFI_CERT_TYPE_PKCS7_GUID;
>> + >> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE) > > see above > >> +static const char *opts_short = "f:r:i:I:v:D:K:P:C:m:dOh"; >> +#else >> +static const char *opts_short = "f:r:i:I:v:D:K:Oh"; >> +#endif >> >> static struct option options[] = { >> {"fit", required_argument, NULL, 'f'}, >> @@ -54,6 +71,12 @@ static struct option options[] = { >> {"instance", required_argument, NULL, 'I'}, >> {"dtb", required_argument, NULL, 'D'}, >> {"public key", required_argument, NULL, 'K'}, >> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE) >> + {"private-key", required_argument, NULL, 'P'}, >> + {"certificate", required_argument, NULL, 'C'}, >> + {"monotonic-count", required_argument, NULL, 'm'}, > > These options should not be required.
I don't get you. What do you mean?
>> + {"dump-sig", no_argument, NULL, 'd'}, >> +#endif >> {"overlay", no_argument, NULL, 'O'}, >> {"help", no_argument, NULL, 'h'}, >> {NULL, 0, NULL, 0}, >> @@ -70,6 +93,12 @@ static void print_usage(void) >> "\t-I, --instance <instance> update hardware
instance\n"
>> "\t-K, --public-key <key file> public key esl
file\n"
>> "\t-D, --dtb <dtb file> dtb file\n" >> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE) > > see above > >> + "\t-P, --private-key <privkey file> private key
file\n"
>> + "\t-C, --certificate <cert file> signer's
certificate file\n"
>> + "\t-m, --monotonic-count <count> monotonic
count\n"
>> + "\t-d, --dump_sig dump signature
(*.p7)\n"
>> +#endif >> "\t-O, --overlay the dtb file is an
overlay\n"
>> "\t-h, --help print a help
message\n",
>> tool_name); >> @@ -249,12 +278,167 @@ err: >> return ret; >> } >> >> +struct auth_context { >> + char *key_file; >> + char *cert_file; >> + u8 *image_data; >> + size_t image_size; >> + struct efi_firmware_image_authentication auth; >> + u8 *sig_data; >> + size_t sig_size; >> +}; >> + >> +static int dump_sig; >> + >> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE) > > see above > >> +static EVP_PKEY *fileio_read_pkey(const char *filename) >> +{ >> + EVP_PKEY *key = NULL; >> + BIO *bio; >> + >> + bio = BIO_new_file(filename, "r"); >> + if (!bio) >> + goto out; >> + >> + key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); >> + >> +out: >> + BIO_free_all(bio); >> + if (!key) { >> + printf("Can't load key from file '%s'\n",
filename);
> > Please, you use fprintf(stderr,) for error messages. > >> + ERR_print_errors_fp(stderr); >> + } >> + >> + return key; >> +} >> + >> +static X509 *fileio_read_cert(const char *filename) >> +{ >> + X509 *cert = NULL; >> + BIO *bio; >> + >> + bio = BIO_new_file(filename, "r"); >> + if (!bio) >> + goto out; >> + >> + cert = PEM_read_bio_X509(bio, NULL, NULL, NULL); >> + >> +out: >> + BIO_free_all(bio); >> + if (!cert) { >> + printf("Can't load certificate from file '%s'\n",
filename);
> > fprintf(stderr,) > >> + ERR_print_errors_fp(stderr); >> + } >> + >> + return cert; >> +} >> + >> +static int create_auth_data(struct auth_context *ctx) >> +{ >> + EVP_PKEY *key = NULL; >> + X509 *cert = NULL; >> + BIO *data_bio = NULL; >> + const EVP_MD *md; >> + PKCS7 *p7; >> + int flags, ret = -1; >> + >> + OpenSSL_add_all_digests(); >> + OpenSSL_add_all_ciphers(); >> + ERR_load_crypto_strings(); >> + >> + key = fileio_read_pkey(ctx->key_file); >> + if (!key) >> + goto err; >> + cert = fileio_read_cert(ctx->cert_file); >> + if (!cert) >> + goto err; >> + >> + /* >> + * create a BIO, containing: >> + * * firmware image >> + * * monotonic count >> + * in this order! >> + * See EDK2's FmpAuthenticatedHandlerRsa2048Sha256() >> + */ >> + data_bio = BIO_new(BIO_s_mem()); >> + BIO_write(data_bio, ctx->image_data, ctx->image_size); >> + BIO_write(data_bio, &ctx->auth.monotonic_count, >> + sizeof(ctx->auth.monotonic_count)); >> + >> + md = EVP_get_digestbyname("SHA256"); >> + if (!md) >> + goto err; >> + >> + /* create signature */ >> + /* TODO: maybe add PKCS7_NOATTR and PKCS7_NOSMIMECAP */ > > PKCS7_NOATTR is a value without any documentation in the code.
Nak. Those macros are part of openssl library. See openssl/pkcs7.h.
> Please, replace variable names by a long text describing what it
missing.
> >> + flags = PKCS7_BINARY | PKCS7_DETACHED; > > Those constants lack documentation in the code.
Nak again.
>> + p7 = PKCS7_sign(NULL, NULL, NULL, data_bio, flags |
PKCS7_PARTIAL);
>> + if (!p7) >> + goto err; >> + if (!PKCS7_sign_add_signer(p7, cert, key, md, flags)) >> + goto err; >> + if (!PKCS7_final(p7, data_bio, flags)) >> + goto err; >> + >> + /* convert pkcs7 into DER */ >> + ctx->sig_data = NULL; >> + ctx->sig_size = ASN1_item_i2d((ASN1_VALUE *)p7,
&ctx->sig_data,
>> + ASN1_ITEM_rptr(PKCS7)); >> + if (!ctx->sig_size) >> + goto err; >> + >> + /* fill auth_info */ >> + ctx->auth.auth_info.hdr.dwLength =
sizeof(ctx->auth.auth_info)
>> + + ctx->sig_size; >> + ctx->auth.auth_info.hdr.wRevision = WIN_CERT_REVISION_2_0; >> + ctx->auth.auth_info.hdr.wCertificateType =
WIN_CERT_TYPE_EFI_GUID;
>> + memcpy(&ctx->auth.auth_info.cert_type,
&efi_guid_cert_type_pkcs7,
>> + sizeof(efi_guid_cert_type_pkcs7)); >> + >> + ret = 0; >> +err: >> + BIO_free_all(data_bio); >> + EVP_PKEY_free(key); >> + X509_free(cert); >> + >> + return ret; >> +} >> + >> +static int dump_signature(const char *path, u8 *signature,
size_t sig_size)
>> +{ >> + char *sig_path; >> + FILE *f; >> + size_t size; >> + int ret = -1; >> + >> + sig_path = malloc(strlen(path) + 3 + 1); >> + if (!sig_path) >> + return ret; >> + >> + sprintf(sig_path, "%s.p7", path); >> + f = fopen(sig_path, "w"); >> + if (!f) >> + goto err; >> + >> + size = fwrite(signature, 1, sig_size, f); >> + if (size == sig_size) >> + ret = 0; >> + >> + fclose(f); >> +err: >> + free(sig_path); >> + return ret; >> +} >> +#endif >> + >> static int create_fwbin(char *path, char *bin, efi_guid_t
*guid,
>> - unsigned long index, unsigned long
instance)
>> + unsigned long index, unsigned long
instance,
>> + uint64_t mcount, char *privkey_file, char
*cert_file)
>> { >> struct efi_capsule_header header; >> struct efi_firmware_management_capsule_header capsule; >> struct efi_firmware_management_capsule_image_header image; >> + struct auth_context auth_context; >> FILE *f, *g; >> struct stat bin_stat; >> u8 *data; >> @@ -266,6 +450,7 @@ static int create_fwbin(char *path, char
*bin, efi_guid_t *guid,
>> printf("\tbin: %s\n\ttype: %pUl\n", bin, guid);

On Thu, May 13, 2021 at 07:50:52AM +0200, Heinrich Schuchardt wrote:
On 5/13/21 7:12 AM, Masami Hiramatsu wrote:
Hi Heinrich,
2021年5月13日(木) 13:22 Heinrich Schuchardt xypron.glpk@gmx.de:
On 5/13/21 5:08 AM, AKASHI Takahiro wrote:
On Wed, May 12, 2021 at 10:56:41AM +0200, Heinrich Schuchardt wrote:
On 12.05.21 06:57, AKASHI Takahiro wrote:
With this enhancement, mkeficapsule will be able to create a capsule file with a signature which will be verified later by FMP's SetImage().
We will have to specify addtional command parameters: -monotonic-cout <count> : monotonic count -private-key <private key file> : private key file -certificate <certificate file> : certificate file Only when those parameters are given, a signature will be added to a capsule file.
Users are expected to maintain the monotonic count for each firmware image.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
tools/Makefile | 4 + tools/mkeficapsule.c | 324 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 303 insertions(+), 25 deletions(-)
diff --git a/tools/Makefile b/tools/Makefile index d020c55d6644..02eae0286e20 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -231,6 +231,10 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs hostprogs-$(CONFIG_ASN1_COMPILER) += asn1_compiler HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
+ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),) +HOSTLDLIBS_mkeficapsule += \
- $(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto")
I don't expect any user wants to install two tool versions in parallel.
The tool should always be able to add a signature. Adding a signature must be optional.
It seems to me that those two statements mutually contradict. Or do you intend to say that we should have a separate kconfig option to enable/disable signing feature in mkeficapsule?
If so, I can agree.
In either way, we should have an option to turn on/off this functionality as not all users use signed capsules.
I want to have a single binary to distribute with Linux distros (e.g. Debian/Ubuntu package u-boot-tools).
I couldn't catch your point. If so, the distros can build u-boot with CONFIG_EFI_CAPSULE_AUTHENTICATE=y...
Why should the tool depend on board configuration? Who would want capsule updates without authentication?
I believe that there are bunch of users who don't need authentication on their own systems.
BTW, IMHO, if u-boot.bin can not find the ESL in the device tree, it should skip authentication too.
In this case the capsule should be rejected (if CONFIG_EFI_CAPSULE_AUTHENTICATE=y).
That's basically right. But as I mentioned in my comment against Sughosh's patch, the authentication process will be enforced only if the capsule has an attribute, IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED.
I believe that this flag should be able to be specified and managed outside U-Boot configuration. So there can be a case where FW update is performed even if the key/certificate is not found in the device tree.
Then, user can choose whether enabling capsule authentication or not by embedding ESL into their devicetree.
Same comment above.
-Takahiro Akashi
The user shall not be able to decide anything that might hamper security. The U-Boot binary must dictate if a capsule is safe.
Best regards
Heinrich
Thank you
This should allow both
- create signed capsules
- create unsigned capsules
The user shall select signing via command line parameters.
Support for signing via the tool shall not depend on board Kconfig parameters.
Best regards
Heinrich
+endif mkeficapsule-objs := mkeficapsule.o $(LIBFDT_OBJS) hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c index de0a62898886..34ff1bdd82eb 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -18,7 +18,17 @@ #include <sys/stat.h> #include <sys/types.h>
-#include "fdt_host.h" +#include <linux/kconfig.h> +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
+#include <openssl/asn1.h> +#include <openssl/bio.h> +#include <openssl/evp.h> +#include <openssl/err.h> +#include <openssl/pem.h> +#include <openssl/pkcs7.h> +#endif
+#include <linux/libfdt.h>
typedef __u8 u8; typedef __u16 u16; @@ -46,6 +56,13 @@ efi_guid_t efi_guid_image_type_uboot_fit = EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID; efi_guid_t efi_guid_image_type_uboot_raw = EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID; +efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
+static const char *opts_short = "f:r:i:I:v:D:K:P:C:m:dOh"; +#else +static const char *opts_short = "f:r:i:I:v:D:K:Oh"; +#endif
static struct option options[] = { {"fit", required_argument, NULL, 'f'}, @@ -54,6 +71,12 @@ static struct option options[] = { {"instance", required_argument, NULL, 'I'}, {"dtb", required_argument, NULL, 'D'}, {"public key", required_argument, NULL, 'K'}, +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
- {"private-key", required_argument, NULL, 'P'},
- {"certificate", required_argument, NULL, 'C'},
- {"monotonic-count", required_argument, NULL, 'm'},
These options should not be required.
I don't get you. What do you mean?
- {"dump-sig", no_argument, NULL, 'd'},
+#endif {"overlay", no_argument, NULL, 'O'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0}, @@ -70,6 +93,12 @@ static void print_usage(void) "\t-I, --instance <instance> update hardware instance\n" "\t-K, --public-key <key file> public key esl file\n" "\t-D, --dtb <dtb file> dtb file\n" +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
"\t-P, --private-key <privkey file> private key file\n"
"\t-C, --certificate <cert file> signer's certificate file\n"
"\t-m, --monotonic-count <count> monotonic count\n"
"\t-d, --dump_sig dump signature (*.p7)\n"
+#endif "\t-O, --overlay the dtb file is an overlay\n" "\t-h, --help print a help message\n", tool_name); @@ -249,12 +278,167 @@ err: return ret; }
+struct auth_context {
- char *key_file;
- char *cert_file;
- u8 *image_data;
- size_t image_size;
- struct efi_firmware_image_authentication auth;
- u8 *sig_data;
- size_t sig_size;
+};
+static int dump_sig;
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
+static EVP_PKEY *fileio_read_pkey(const char *filename) +{
- EVP_PKEY *key = NULL;
- BIO *bio;
- bio = BIO_new_file(filename, "r");
- if (!bio)
goto out;
- key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
+out:
- BIO_free_all(bio);
- if (!key) {
printf("Can't load key from file '%s'\n", filename);
Please, you use fprintf(stderr,) for error messages.
ERR_print_errors_fp(stderr);
- }
- return key;
+}
+static X509 *fileio_read_cert(const char *filename) +{
- X509 *cert = NULL;
- BIO *bio;
- bio = BIO_new_file(filename, "r");
- if (!bio)
goto out;
- cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+out:
- BIO_free_all(bio);
- if (!cert) {
printf("Can't load certificate from file '%s'\n", filename);
fprintf(stderr,)
ERR_print_errors_fp(stderr);
- }
- return cert;
+}
+static int create_auth_data(struct auth_context *ctx) +{
- EVP_PKEY *key = NULL;
- X509 *cert = NULL;
- BIO *data_bio = NULL;
- const EVP_MD *md;
- PKCS7 *p7;
- int flags, ret = -1;
- OpenSSL_add_all_digests();
- OpenSSL_add_all_ciphers();
- ERR_load_crypto_strings();
- key = fileio_read_pkey(ctx->key_file);
- if (!key)
goto err;
- cert = fileio_read_cert(ctx->cert_file);
- if (!cert)
goto err;
- /*
- create a BIO, containing:
- firmware image
- monotonic count
- in this order!
- See EDK2's FmpAuthenticatedHandlerRsa2048Sha256()
- */
- data_bio = BIO_new(BIO_s_mem());
- BIO_write(data_bio, ctx->image_data, ctx->image_size);
- BIO_write(data_bio, &ctx->auth.monotonic_count,
sizeof(ctx->auth.monotonic_count));
- md = EVP_get_digestbyname("SHA256");
- if (!md)
goto err;
- /* create signature */
- /* TODO: maybe add PKCS7_NOATTR and PKCS7_NOSMIMECAP */
PKCS7_NOATTR is a value without any documentation in the code.
Nak. Those macros are part of openssl library. See openssl/pkcs7.h.
Please, replace variable names by a long text describing what it missing.
- flags = PKCS7_BINARY | PKCS7_DETACHED;
Those constants lack documentation in the code.
Nak again.
- p7 = PKCS7_sign(NULL, NULL, NULL, data_bio, flags | PKCS7_PARTIAL);
- if (!p7)
goto err;
- if (!PKCS7_sign_add_signer(p7, cert, key, md, flags))
goto err;
- if (!PKCS7_final(p7, data_bio, flags))
goto err;
- /* convert pkcs7 into DER */
- ctx->sig_data = NULL;
- ctx->sig_size = ASN1_item_i2d((ASN1_VALUE *)p7, &ctx->sig_data,
ASN1_ITEM_rptr(PKCS7));
- if (!ctx->sig_size)
goto err;
- /* fill auth_info */
- ctx->auth.auth_info.hdr.dwLength = sizeof(ctx->auth.auth_info)
+ ctx->sig_size;
- ctx->auth.auth_info.hdr.wRevision = WIN_CERT_REVISION_2_0;
- ctx->auth.auth_info.hdr.wCertificateType = WIN_CERT_TYPE_EFI_GUID;
- memcpy(&ctx->auth.auth_info.cert_type, &efi_guid_cert_type_pkcs7,
sizeof(efi_guid_cert_type_pkcs7));
- ret = 0;
+err:
- BIO_free_all(data_bio);
- EVP_PKEY_free(key);
- X509_free(cert);
- return ret;
+}
+static int dump_signature(const char *path, u8 *signature, size_t sig_size) +{
- char *sig_path;
- FILE *f;
- size_t size;
- int ret = -1;
- sig_path = malloc(strlen(path) + 3 + 1);
- if (!sig_path)
return ret;
- sprintf(sig_path, "%s.p7", path);
- f = fopen(sig_path, "w");
- if (!f)
goto err;
- size = fwrite(signature, 1, sig_size, f);
- if (size == sig_size)
ret = 0;
- fclose(f);
+err:
- free(sig_path);
- return ret;
+} +#endif
- static int create_fwbin(char *path, char *bin, efi_guid_t *guid,
unsigned long index, unsigned long instance)
unsigned long index, unsigned long instance,
{ struct efi_capsule_header header; struct efi_firmware_management_capsule_header capsule; struct efi_firmware_management_capsule_image_header image;uint64_t mcount, char *privkey_file, char *cert_file)
- struct auth_context auth_context; FILE *f, *g; struct stat bin_stat; u8 *data;
@@ -266,6 +450,7 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, printf("\tbin: %s\n\ttype: %pUl\n", bin, guid); printf("\tindex: %ld\n\tinstance: %ld\n", index, instance); #endif
auth_context.sig_size = 0;
g = fopen(bin, "r"); if (!g) {
@@ -281,11 +466,36 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, printf("cannot allocate memory: %zx\n", (size_t)bin_stat.st_size); goto err_1; }
- f = fopen(path, "w");
- if (!f) {
printf("cannot open %s\n", path);
- size = fread(data, 1, bin_stat.st_size, g);
- if (size < bin_stat.st_size) {
}printf("read failed (%zx)\n", size); goto err_2;
- /* first, calculate signature to determine its size */
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
- if (privkey_file && cert_file) {
auth_context.key_file = privkey_file;
auth_context.cert_file = cert_file;
auth_context.auth.monotonic_count = mcount;
auth_context.image_data = data;
auth_context.image_size = bin_stat.st_size;
if (create_auth_data(&auth_context)) {
printf("Signing firmware image failed\n");
goto err_3;
}
if (dump_sig &&
dump_signature(path, auth_context.sig_data,
auth_context.sig_size)) {
printf("Creating signature file failed\n");
goto err_3;
}
- }
+#endif
- header.capsule_guid = efi_guid_fm_capsule; header.header_size = sizeof(header); /* TODO: The current implementation ignores flags */
@@ -294,11 +504,20 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, + sizeof(capsule) + sizeof(u64) + sizeof(image) + bin_stat.st_size;
if (auth_context.sig_size)
header.capsule_image_size += sizeof(auth_context.auth)
+ auth_context.sig_size;
f = fopen(path, "w");
if (!f) {
printf("cannot open %s\n", path);
goto err_3;
}
size = fwrite(&header, 1, sizeof(header), f); if (size < sizeof(header)) { printf("write failed (%zx)\n", size);
goto err_3;
goto err_4;
}
capsule.version = 0x00000001;
@@ -307,13 +526,13 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, size = fwrite(&capsule, 1, sizeof(capsule), f); if (size < (sizeof(capsule))) { printf("write failed (%zx)\n", size);
goto err_3;
} offset = sizeof(capsule) + sizeof(u64); size = fwrite(&offset, 1, sizeof(offset), f); if (size < sizeof(offset)) { printf("write failed (%zx)\n", size);goto err_4;
goto err_3;
goto err_4;
}
image.version = 0x00000003;
@@ -323,34 +542,61 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, image.reserved[1] = 0; image.reserved[2] = 0; image.update_image_size = bin_stat.st_size;
if (auth_context.sig_size)
image.update_image_size += sizeof(auth_context.auth)
+ auth_context.sig_size;
image.update_vendor_code_size = 0; /* none */ image.update_hardware_instance = instance; image.image_capsule_support = 0;
if (auth_context.sig_size)
image.image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION;
size = fwrite(&image, 1, sizeof(image), f); if (size < sizeof(image)) { printf("write failed (%zx)\n", size);
goto err_3;
}goto err_4;
- size = fread(data, 1, bin_stat.st_size, g);
- if (size < bin_stat.st_size) {
printf("read failed (%zx)\n", size);
goto err_3;
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
We don't want to use #if if avoidable.
For this specific chunk of code, we can remove #ifdef, but we should not remove #ifdef elsewhere.
-Takahiro Akashi
- if (auth_context.sig_size) {
size = fwrite(&auth_context.auth, 1,
sizeof(auth_context.auth), f);
if (size < sizeof(auth_context.auth)) {
printf("write failed (%zx)\n", size);
goto err_4;
}
size = fwrite(auth_context.sig_data, 1,
auth_context.sig_size, f);
if (size < auth_context.sig_size) {
printf("write failed (%zx)\n", size);
goto err_4;
}}
+#endif
- size = fwrite(data, 1, bin_stat.st_size, f); if (size < bin_stat.st_size) { printf("write failed (%zx)\n", size);
goto err_3;
goto err_4;
}
fclose(f); fclose(g); free(data);
+#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
- if (auth_context.sig_size)
OPENSSL_free(auth_context.sig_data);
+#endif
return 0;
-err_3: +err_4: fclose(f); +err_3: +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
- if (auth_context.sig_size)
OPENSSL_free(auth_context.sig_data);
+#endif err_2: free(data); err_1: @@ -359,10 +605,6 @@ err_1: return -1; }
-/*
- Usage:
- $ mkeficapsule -f <firmware binary> <output file>
- */ int main(int argc, char **argv) { char *file;
@@ -370,6 +612,8 @@ int main(int argc, char **argv) char *dtb_file; efi_guid_t *guid; unsigned long index, instance;
- uint64_t mcount;
- char *privkey_file, *cert_file; int c, idx; int ret; bool overlay = false;
@@ -380,8 +624,12 @@ int main(int argc, char **argv) guid = NULL; index = 0; instance = 0;
- mcount = 0;
- privkey_file = NULL;
- cert_file = NULL;
- dump_sig = 0; for (;;) {
c = getopt_long(argc, argv, "f:r:i:I:v:D:K:Oh", options, &idx);
c = getopt_long(argc, argv, opts_short, options, &idx); if (c == -1) break;
@@ -422,6 +670,28 @@ int main(int argc, char **argv) } dtb_file = optarg; break; +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
Best regards
Heinrich
case 'P':
if (privkey_file) {
printf("Private Key already specified\n");
return -1;
}
privkey_file = optarg;
break;
case 'C':
if (cert_file) {
printf("Certificate file already specified\n");
return -1;
}
cert_file = optarg;
break;
case 'm':
mcount = strtoul(optarg, NULL, 0);
break;
case 'd':
dump_sig = 1;
break;
+#endif case 'O': overlay = true; break; @@ -431,8 +701,12 @@ int main(int argc, char **argv) } }
- /* need a fit image file or raw image file */
- if (!file && !pkey_file && !dtb_file) {
- /* check necessary parameters */
- if ((file && (!(optind < argc) ||
(privkey_file && !cert_file) ||
(!privkey_file && cert_file))) ||
((pkey_file && !dtb_file) ||
}(!pkey_file && dtb_file))) { print_usage(); exit(EXIT_FAILURE);
@@ -442,12 +716,12 @@ int main(int argc, char **argv) if (ret == -1) { printf("Adding public key to the dtb failed\n"); exit(EXIT_FAILURE);
} else {
exit(EXIT_SUCCESS); }
}
if (create_fwbin(argv[optind], file, guid, index, instance)
- if (optind < argc &&
create_fwbin(argv[optind], file, guid, index, instance,
mcount, privkey_file, cert_file) < 0) { printf("Creating firmware capsule failed\n"); exit(EXIT_FAILURE);

Am 13. Mai 2021 08:50:54 MESZ schrieb AKASHI Takahiro takahiro.akashi@linaro.org:
On Thu, May 13, 2021 at 07:50:52AM +0200, Heinrich Schuchardt wrote:
On 5/13/21 7:12 AM, Masami Hiramatsu wrote:
Hi Heinrich,
2021年5月13日(木) 13:22 Heinrich Schuchardt xypron.glpk@gmx.de:
On 5/13/21 5:08 AM, AKASHI Takahiro wrote:
On Wed, May 12, 2021 at 10:56:41AM +0200, Heinrich Schuchardt
wrote:
On 12.05.21 06:57, AKASHI Takahiro wrote: > With this enhancement, mkeficapsule will be able to create
a capsule
> file with a signature which will be verified later by FMP's
SetImage().
> > We will have to specify addtional command parameters: > -monotonic-cout <count> : monotonic count > -private-key <private key file> : private key file > -certificate <certificate file> : certificate file > Only when those parameters are given, a signature will be
added
> to a capsule file. > > Users are expected to maintain the monotonic count for each
firmware
> image. > > Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org > --- > tools/Makefile | 4 + > tools/mkeficapsule.c | 324
+++++++++++++++++++++++++++++++++++++++----
> 2 files changed, 303 insertions(+), 25 deletions(-) > > diff --git a/tools/Makefile b/tools/Makefile > index d020c55d6644..02eae0286e20 100644 > --- a/tools/Makefile > +++ b/tools/Makefile > @@ -231,6 +231,10 @@ hostprogs-$(CONFIG_MIPS) +=
mips-relocs
> hostprogs-$(CONFIG_ASN1_COMPILER) += asn1_compiler > HOSTCFLAGS_asn1_compiler.o = -idirafter
$(srctree)/include
> > +ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),) > +HOSTLDLIBS_mkeficapsule += \ > + $(shell pkg-config --libs libssl libcrypto 2> /dev/null
|| echo "-lssl -lcrypto")
I don't expect any user wants to install two tool versions in
parallel.
The tool should always be able to add a signature. Adding a signature must be optional.
It seems to me that those two statements mutually contradict. Or do you intend to say that we should have a separate kconfig option to enable/disable signing feature in mkeficapsule?
If so, I can agree.
In either way, we should have an option to turn on/off this
functionality
as not all users use signed capsules.
I want to have a single binary to distribute with Linux distros
(e.g.
Debian/Ubuntu package u-boot-tools).
I couldn't catch your point. If so, the distros can build u-boot
with
CONFIG_EFI_CAPSULE_AUTHENTICATE=y...
Why should the tool depend on board configuration? Who would want capsule updates without authentication?
I believe that there are bunch of users who don't need authentication on their own systems.
They should think again.
BTW, IMHO, if u-boot.bin can not find the ESL in the device tree, it should skip authentication too.
In this case the capsule should be rejected (if CONFIG_EFI_CAPSULE_AUTHENTICATE=y).
That's basically right. But as I mentioned in my comment against Sughosh's patch, the authentication process will be enforced only if the capsule has an attribute, IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED.
That would be a security desaster.
Best regards
Heinrich
I believe that this flag should be able to be specified and managed outside U-Boot configuration. So there can be a case where FW update is performed even if the key/certificate is not found in the device tree.
Then, user can choose whether enabling capsule authentication or
not
by embedding ESL into their devicetree.
Same comment above.
-Takahiro Akashi
The user shall not be able to decide anything that might hamper security. The U-Boot binary must dictate if a capsule is safe.
Best regards
Heinrich
Thank you
This should allow both
- create signed capsules
- create unsigned capsules
The user shall select signing via command line parameters.
Support for signing via the tool shall not depend on board
Kconfig
parameters.
Best regards
Heinrich
> +endif > mkeficapsule-objs := mkeficapsule.o $(LIBFDT_OBJS) > hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) +=
mkeficapsule
> > diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c > index de0a62898886..34ff1bdd82eb 100644 > --- a/tools/mkeficapsule.c > +++ b/tools/mkeficapsule.c > @@ -18,7 +18,17 @@ > #include <sys/stat.h> > #include <sys/types.h> > > -#include "fdt_host.h" > +#include <linux/kconfig.h> > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
> +#include <openssl/asn1.h> > +#include <openssl/bio.h> > +#include <openssl/evp.h> > +#include <openssl/err.h> > +#include <openssl/pem.h> > +#include <openssl/pkcs7.h> > +#endif > + > +#include <linux/libfdt.h> > > typedef __u8 u8; > typedef __u16 u16; > @@ -46,6 +56,13 @@ efi_guid_t efi_guid_image_type_uboot_fit
=
> EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID; > efi_guid_t efi_guid_image_type_uboot_raw = > EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID; > +efi_guid_t efi_guid_cert_type_pkcs7 =
EFI_CERT_TYPE_PKCS7_GUID;
> + > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
> +static const char *opts_short = "f:r:i:I:v:D:K:P:C:m:dOh"; > +#else > +static const char *opts_short = "f:r:i:I:v:D:K:Oh"; > +#endif > > static struct option options[] = { > {"fit", required_argument, NULL, 'f'}, > @@ -54,6 +71,12 @@ static struct option options[] = { > {"instance", required_argument, NULL, 'I'}, > {"dtb", required_argument, NULL, 'D'}, > {"public key", required_argument, NULL, 'K'}, > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE) > + {"private-key", required_argument, NULL, 'P'}, > + {"certificate", required_argument, NULL, 'C'}, > + {"monotonic-count", required_argument, NULL, 'm'},
These options should not be required.
I don't get you. What do you mean?
> + {"dump-sig", no_argument, NULL, 'd'}, > +#endif > {"overlay", no_argument, NULL, 'O'}, > {"help", no_argument, NULL, 'h'}, > {NULL, 0, NULL, 0}, > @@ -70,6 +93,12 @@ static void print_usage(void) > "\t-I, --instance <instance> update hardware
instance\n"
> "\t-K, --public-key <key file> public key esl
file\n"
> "\t-D, --dtb <dtb file> dtb file\n" > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
> + "\t-P, --private-key <privkey file> private key
file\n"
> + "\t-C, --certificate <cert file> signer's
certificate file\n"
> + "\t-m, --monotonic-count <count> monotonic
count\n"
> + "\t-d, --dump_sig dump signature
(*.p7)\n"
> +#endif > "\t-O, --overlay the dtb file is
an overlay\n"
> "\t-h, --help print a help
message\n",
> tool_name); > @@ -249,12 +278,167 @@ err: > return ret; > } > > +struct auth_context { > + char *key_file; > + char *cert_file; > + u8 *image_data; > + size_t image_size; > + struct efi_firmware_image_authentication auth; > + u8 *sig_data; > + size_t sig_size; > +}; > + > +static int dump_sig; > + > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
> +static EVP_PKEY *fileio_read_pkey(const char *filename) > +{ > + EVP_PKEY *key = NULL; > + BIO *bio; > + > + bio = BIO_new_file(filename, "r"); > + if (!bio) > + goto out; > + > + key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); > + > +out: > + BIO_free_all(bio); > + if (!key) { > + printf("Can't load key from file '%s'\n",
filename);
Please, you use fprintf(stderr,) for error messages.
> + ERR_print_errors_fp(stderr); > + } > + > + return key; > +} > + > +static X509 *fileio_read_cert(const char *filename) > +{ > + X509 *cert = NULL; > + BIO *bio; > + > + bio = BIO_new_file(filename, "r"); > + if (!bio) > + goto out; > + > + cert = PEM_read_bio_X509(bio, NULL, NULL, NULL); > + > +out: > + BIO_free_all(bio); > + if (!cert) { > + printf("Can't load certificate from file
'%s'\n", filename);
fprintf(stderr,)
> + ERR_print_errors_fp(stderr); > + } > + > + return cert; > +} > + > +static int create_auth_data(struct auth_context *ctx) > +{ > + EVP_PKEY *key = NULL; > + X509 *cert = NULL; > + BIO *data_bio = NULL; > + const EVP_MD *md; > + PKCS7 *p7; > + int flags, ret = -1; > + > + OpenSSL_add_all_digests(); > + OpenSSL_add_all_ciphers(); > + ERR_load_crypto_strings(); > + > + key = fileio_read_pkey(ctx->key_file); > + if (!key) > + goto err; > + cert = fileio_read_cert(ctx->cert_file); > + if (!cert) > + goto err; > + > + /* > + * create a BIO, containing: > + * * firmware image > + * * monotonic count > + * in this order! > + * See EDK2's FmpAuthenticatedHandlerRsa2048Sha256() > + */ > + data_bio = BIO_new(BIO_s_mem()); > + BIO_write(data_bio, ctx->image_data, ctx->image_size); > + BIO_write(data_bio, &ctx->auth.monotonic_count, > + sizeof(ctx->auth.monotonic_count)); > + > + md = EVP_get_digestbyname("SHA256"); > + if (!md) > + goto err; > + > + /* create signature */ > + /* TODO: maybe add PKCS7_NOATTR and PKCS7_NOSMIMECAP */
PKCS7_NOATTR is a value without any documentation in the
code.
Nak. Those macros are part of openssl library. See openssl/pkcs7.h.
Please, replace variable names by a long text describing what
it missing.
> + flags = PKCS7_BINARY | PKCS7_DETACHED;
Those constants lack documentation in the code.
Nak again.
> + p7 = PKCS7_sign(NULL, NULL, NULL, data_bio, flags |
PKCS7_PARTIAL);
> + if (!p7) > + goto err; > + if (!PKCS7_sign_add_signer(p7, cert, key, md, flags)) > + goto err; > + if (!PKCS7_final(p7, data_bio, flags)) > + goto err; > + > + /* convert pkcs7 into DER */ > + ctx->sig_data = NULL; > + ctx->sig_size = ASN1_item_i2d((ASN1_VALUE *)p7,
&ctx->sig_data,
> + ASN1_ITEM_rptr(PKCS7)); > + if (!ctx->sig_size) > + goto err; > + > + /* fill auth_info */ > + ctx->auth.auth_info.hdr.dwLength =
sizeof(ctx->auth.auth_info)
> + +
ctx->sig_size;
> + ctx->auth.auth_info.hdr.wRevision =
WIN_CERT_REVISION_2_0;
> + ctx->auth.auth_info.hdr.wCertificateType =
WIN_CERT_TYPE_EFI_GUID;
> + memcpy(&ctx->auth.auth_info.cert_type,
&efi_guid_cert_type_pkcs7,
> + sizeof(efi_guid_cert_type_pkcs7)); > + > + ret = 0; > +err: > + BIO_free_all(data_bio); > + EVP_PKEY_free(key); > + X509_free(cert); > + > + return ret; > +} > + > +static int dump_signature(const char *path, u8 *signature,
size_t sig_size)
> +{ > + char *sig_path; > + FILE *f; > + size_t size; > + int ret = -1; > + > + sig_path = malloc(strlen(path) + 3 + 1); > + if (!sig_path) > + return ret; > + > + sprintf(sig_path, "%s.p7", path); > + f = fopen(sig_path, "w"); > + if (!f) > + goto err; > + > + size = fwrite(signature, 1, sig_size, f); > + if (size == sig_size) > + ret = 0; > + > + fclose(f); > +err: > + free(sig_path); > + return ret; > +} > +#endif > + > static int create_fwbin(char *path, char *bin,
efi_guid_t *guid,
> - unsigned long index, unsigned long
instance)
> + unsigned long index, unsigned long
instance,
> + uint64_t mcount, char *privkey_file,
char *cert_file)
> { > struct efi_capsule_header header; > struct efi_firmware_management_capsule_header capsule; > struct efi_firmware_management_capsule_image_header
image;
> + struct auth_context auth_context; > FILE *f, *g; > struct stat bin_stat; > u8 *data; > @@ -266,6 +450,7 @@ static int create_fwbin(char *path,
char *bin, efi_guid_t *guid,
> printf("\tbin: %s\n\ttype: %pUl\n", bin, guid); > printf("\tindex: %ld\n\tinstance: %ld\n", index,
instance);
> #endif > + auth_context.sig_size = 0; > > g = fopen(bin, "r"); > if (!g) { > @@ -281,11 +466,36 @@ static int create_fwbin(char *path,
char *bin, efi_guid_t *guid,
> printf("cannot allocate memory: %zx\n",
(size_t)bin_stat.st_size);
> goto err_1; > } > - f = fopen(path, "w"); > - if (!f) { > - printf("cannot open %s\n", path); > + > + size = fread(data, 1, bin_stat.st_size, g); > + if (size < bin_stat.st_size) { > + printf("read failed (%zx)\n", size); > goto err_2; > } > + > + /* first, calculate signature to determine its size */ > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)
see above
> + if (privkey_file && cert_file) { > + auth_context.key_file = privkey_file; > + auth_context.cert_file = cert_file; > + auth_context.auth.monotonic_count = mcount; > + auth_context.image_data = data; > + auth_context.image_size = bin_stat.st_size; > + > + if (create_auth_data(&auth_context)) { > + printf("Signing firmware image
failed\n");
> + goto err_3; > + } > + > + if (dump_sig && > + dump_signature(path, auth_context.sig_data, > + auth_context.sig_size)) {

On Thu, May 13, 2021 at 08:55:17AM +0200, Heinrich Schuchardt wrote:
Am 13. Mai 2021 08:50:54 MESZ schrieb AKASHI Takahiro takahiro.akashi@linaro.org:
On Thu, May 13, 2021 at 07:50:52AM +0200, Heinrich Schuchardt wrote:
On 5/13/21 7:12 AM, Masami Hiramatsu wrote:
Hi Heinrich,
2021年5月13日(木) 13:22 Heinrich Schuchardt xypron.glpk@gmx.de:
On 5/13/21 5:08 AM, AKASHI Takahiro wrote:
On Wed, May 12, 2021 at 10:56:41AM +0200, Heinrich Schuchardt
wrote:
> On 12.05.21 06:57, AKASHI Takahiro wrote: > > With this enhancement, mkeficapsule will be able to create
a capsule
> > file with a signature which will be verified later by FMP's
SetImage().
> > > > We will have to specify addtional command parameters: > > -monotonic-cout <count> : monotonic count > > -private-key <private key file> : private key file > > -certificate <certificate file> : certificate file > > Only when those parameters are given, a signature will be
added
> > to a capsule file. > > > > Users are expected to maintain the monotonic count for each
firmware
> > image. > > > > Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org > > --- > > tools/Makefile | 4 + > > tools/mkeficapsule.c | 324
+++++++++++++++++++++++++++++++++++++++----
> > 2 files changed, 303 insertions(+), 25 deletions(-) > > > > diff --git a/tools/Makefile b/tools/Makefile > > index d020c55d6644..02eae0286e20 100644 > > --- a/tools/Makefile > > +++ b/tools/Makefile > > @@ -231,6 +231,10 @@ hostprogs-$(CONFIG_MIPS) +=
mips-relocs
> > hostprogs-$(CONFIG_ASN1_COMPILER) += asn1_compiler > > HOSTCFLAGS_asn1_compiler.o = -idirafter
$(srctree)/include
> > > > +ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),) > > +HOSTLDLIBS_mkeficapsule += \ > > + $(shell pkg-config --libs libssl libcrypto 2> /dev/null
|| echo "-lssl -lcrypto")
> > I don't expect any user wants to install two tool versions in
parallel.
> > The tool should always be able to add a signature. > Adding a signature must be optional.
It seems to me that those two statements mutually contradict. Or do you intend to say that we should have a separate kconfig option to enable/disable signing feature in mkeficapsule?
If so, I can agree.
In either way, we should have an option to turn on/off this
functionality
as not all users use signed capsules.
I want to have a single binary to distribute with Linux distros
(e.g.
Debian/Ubuntu package u-boot-tools).
I couldn't catch your point. If so, the distros can build u-boot
with
CONFIG_EFI_CAPSULE_AUTHENTICATE=y...
Why should the tool depend on board configuration? Who would want capsule updates without authentication?
I believe that there are bunch of users who don't need authentication on their own systems.
They should think again.
Why?
BTW, IMHO, if u-boot.bin can not find the ESL in the device tree, it should skip authentication too.
In this case the capsule should be rejected (if CONFIG_EFI_CAPSULE_AUTHENTICATE=y).
That's basically right. But as I mentioned in my comment against Sughosh's patch, the authentication process will be enforced only if the capsule has an attribute, IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED.
That would be a security desaster.
The requirement that I mentioned above is clearly described in UEFI specification. If you think that it is a disaster, please discuss the topic in UEFI Forum first.
-Takahiro Akashi
Best regards
Heinrich
I believe that this flag should be able to be specified and managed outside U-Boot configuration. So there can be a case where FW update is performed even if the key/certificate is not found in the device tree.
Then, user can choose whether enabling capsule authentication or
not
by embedding ESL into their devicetree.
Same comment above.
-Takahiro Akashi
The user shall not be able to decide anything that might hamper security. The U-Boot binary must dictate if a capsule is safe.
Best regards
Heinrich
Thank you
This should allow both
- create signed capsules
- create unsigned capsules
The user shall select signing via command line parameters.
Support for signing via the tool shall not depend on board
Kconfig
parameters.
Best regards
Heinrich
> > +endif > > mkeficapsule-objs := mkeficapsule.o $(LIBFDT_OBJS) > > hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) +=
mkeficapsule
> > > > diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c > > index de0a62898886..34ff1bdd82eb 100644 > > --- a/tools/mkeficapsule.c > > +++ b/tools/mkeficapsule.c > > @@ -18,7 +18,17 @@ > > #include <sys/stat.h> > > #include <sys/types.h> > > > > -#include "fdt_host.h" > > +#include <linux/kconfig.h> > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE) > > see above > > > +#include <openssl/asn1.h> > > +#include <openssl/bio.h> > > +#include <openssl/evp.h> > > +#include <openssl/err.h> > > +#include <openssl/pem.h> > > +#include <openssl/pkcs7.h> > > +#endif > > + > > +#include <linux/libfdt.h> > > > > typedef __u8 u8; > > typedef __u16 u16; > > @@ -46,6 +56,13 @@ efi_guid_t efi_guid_image_type_uboot_fit
=
> > EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID; > > efi_guid_t efi_guid_image_type_uboot_raw = > > EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID; > > +efi_guid_t efi_guid_cert_type_pkcs7 =
EFI_CERT_TYPE_PKCS7_GUID;
> > + > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE) > > see above > > > +static const char *opts_short = "f:r:i:I:v:D:K:P:C:m:dOh"; > > +#else > > +static const char *opts_short = "f:r:i:I:v:D:K:Oh"; > > +#endif > > > > static struct option options[] = { > > {"fit", required_argument, NULL, 'f'}, > > @@ -54,6 +71,12 @@ static struct option options[] = { > > {"instance", required_argument, NULL, 'I'}, > > {"dtb", required_argument, NULL, 'D'}, > > {"public key", required_argument, NULL, 'K'}, > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE) > > + {"private-key", required_argument, NULL, 'P'}, > > + {"certificate", required_argument, NULL, 'C'}, > > + {"monotonic-count", required_argument, NULL, 'm'}, > > These options should not be required.
I don't get you. What do you mean?
> > + {"dump-sig", no_argument, NULL, 'd'}, > > +#endif > > {"overlay", no_argument, NULL, 'O'}, > > {"help", no_argument, NULL, 'h'}, > > {NULL, 0, NULL, 0}, > > @@ -70,6 +93,12 @@ static void print_usage(void) > > "\t-I, --instance <instance> update hardware
instance\n"
> > "\t-K, --public-key <key file> public key esl
file\n"
> > "\t-D, --dtb <dtb file> dtb file\n" > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE) > > see above > > > + "\t-P, --private-key <privkey file> private key
file\n"
> > + "\t-C, --certificate <cert file> signer's
certificate file\n"
> > + "\t-m, --monotonic-count <count> monotonic
count\n"
> > + "\t-d, --dump_sig dump signature
(*.p7)\n"
> > +#endif > > "\t-O, --overlay the dtb file is
an overlay\n"
> > "\t-h, --help print a help
message\n",
> > tool_name); > > @@ -249,12 +278,167 @@ err: > > return ret; > > } > > > > +struct auth_context { > > + char *key_file; > > + char *cert_file; > > + u8 *image_data; > > + size_t image_size; > > + struct efi_firmware_image_authentication auth; > > + u8 *sig_data; > > + size_t sig_size; > > +}; > > + > > +static int dump_sig; > > + > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE) > > see above > > > +static EVP_PKEY *fileio_read_pkey(const char *filename) > > +{ > > + EVP_PKEY *key = NULL; > > + BIO *bio; > > + > > + bio = BIO_new_file(filename, "r"); > > + if (!bio) > > + goto out; > > + > > + key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); > > + > > +out: > > + BIO_free_all(bio); > > + if (!key) { > > + printf("Can't load key from file '%s'\n",
filename);
> > Please, you use fprintf(stderr,) for error messages. > > > + ERR_print_errors_fp(stderr); > > + } > > + > > + return key; > > +} > > + > > +static X509 *fileio_read_cert(const char *filename) > > +{ > > + X509 *cert = NULL; > > + BIO *bio; > > + > > + bio = BIO_new_file(filename, "r"); > > + if (!bio) > > + goto out; > > + > > + cert = PEM_read_bio_X509(bio, NULL, NULL, NULL); > > + > > +out: > > + BIO_free_all(bio); > > + if (!cert) { > > + printf("Can't load certificate from file
'%s'\n", filename);
> > fprintf(stderr,) > > > + ERR_print_errors_fp(stderr); > > + } > > + > > + return cert; > > +} > > + > > +static int create_auth_data(struct auth_context *ctx) > > +{ > > + EVP_PKEY *key = NULL; > > + X509 *cert = NULL; > > + BIO *data_bio = NULL; > > + const EVP_MD *md; > > + PKCS7 *p7; > > + int flags, ret = -1; > > + > > + OpenSSL_add_all_digests(); > > + OpenSSL_add_all_ciphers(); > > + ERR_load_crypto_strings(); > > + > > + key = fileio_read_pkey(ctx->key_file); > > + if (!key) > > + goto err; > > + cert = fileio_read_cert(ctx->cert_file); > > + if (!cert) > > + goto err; > > + > > + /* > > + * create a BIO, containing: > > + * * firmware image > > + * * monotonic count > > + * in this order! > > + * See EDK2's FmpAuthenticatedHandlerRsa2048Sha256() > > + */ > > + data_bio = BIO_new(BIO_s_mem()); > > + BIO_write(data_bio, ctx->image_data, ctx->image_size); > > + BIO_write(data_bio, &ctx->auth.monotonic_count, > > + sizeof(ctx->auth.monotonic_count)); > > + > > + md = EVP_get_digestbyname("SHA256"); > > + if (!md) > > + goto err; > > + > > + /* create signature */ > > + /* TODO: maybe add PKCS7_NOATTR and PKCS7_NOSMIMECAP */ > > PKCS7_NOATTR is a value without any documentation in the
code.
Nak. Those macros are part of openssl library. See openssl/pkcs7.h.
> Please, replace variable names by a long text describing what
it missing.
> > > + flags = PKCS7_BINARY | PKCS7_DETACHED; > > Those constants lack documentation in the code.
Nak again.
> > + p7 = PKCS7_sign(NULL, NULL, NULL, data_bio, flags |
PKCS7_PARTIAL);
> > + if (!p7) > > + goto err; > > + if (!PKCS7_sign_add_signer(p7, cert, key, md, flags)) > > + goto err; > > + if (!PKCS7_final(p7, data_bio, flags)) > > + goto err; > > + > > + /* convert pkcs7 into DER */ > > + ctx->sig_data = NULL; > > + ctx->sig_size = ASN1_item_i2d((ASN1_VALUE *)p7,
&ctx->sig_data,
> > + ASN1_ITEM_rptr(PKCS7)); > > + if (!ctx->sig_size) > > + goto err; > > + > > + /* fill auth_info */ > > + ctx->auth.auth_info.hdr.dwLength =
sizeof(ctx->auth.auth_info)
> > + +
ctx->sig_size;
> > + ctx->auth.auth_info.hdr.wRevision =
WIN_CERT_REVISION_2_0;
> > + ctx->auth.auth_info.hdr.wCertificateType =
WIN_CERT_TYPE_EFI_GUID;
> > + memcpy(&ctx->auth.auth_info.cert_type,
&efi_guid_cert_type_pkcs7,
> > + sizeof(efi_guid_cert_type_pkcs7)); > > + > > + ret = 0; > > +err: > > + BIO_free_all(data_bio); > > + EVP_PKEY_free(key); > > + X509_free(cert); > > + > > + return ret; > > +} > > + > > +static int dump_signature(const char *path, u8 *signature,
size_t sig_size)
> > +{ > > + char *sig_path; > > + FILE *f; > > + size_t size; > > + int ret = -1; > > + > > + sig_path = malloc(strlen(path) + 3 + 1); > > + if (!sig_path) > > + return ret; > > + > > + sprintf(sig_path, "%s.p7", path); > > + f = fopen(sig_path, "w"); > > + if (!f) > > + goto err; > > + > > + size = fwrite(signature, 1, sig_size, f); > > + if (size == sig_size) > > + ret = 0; > > + > > + fclose(f); > > +err: > > + free(sig_path); > > + return ret; > > +} > > +#endif > > + > > static int create_fwbin(char *path, char *bin,
efi_guid_t *guid,
> > - unsigned long index, unsigned long
instance)
> > + unsigned long index, unsigned long
instance,
> > + uint64_t mcount, char *privkey_file,
char *cert_file)
> > { > > struct efi_capsule_header header; > > struct efi_firmware_management_capsule_header capsule; > > struct efi_firmware_management_capsule_image_header
image;
> > + struct auth_context auth_context; > > FILE *f, *g; > > struct stat bin_stat; > > u8 *data; > > @@ -266,6 +450,7 @@ static int create_fwbin(char *path,
char *bin, efi_guid_t *guid,
> > printf("\tbin: %s\n\ttype: %pUl\n", bin, guid); > > printf("\tindex: %ld\n\tinstance: %ld\n", index,
instance);
> > #endif > > + auth_context.sig_size = 0; > > > > g = fopen(bin, "r"); > > if (!g) { > > @@ -281,11 +466,36 @@ static int create_fwbin(char *path,
char *bin, efi_guid_t *guid,
> > printf("cannot allocate memory: %zx\n",
(size_t)bin_stat.st_size);
> > goto err_1; > > } > > - f = fopen(path, "w"); > > - if (!f) { > > - printf("cannot open %s\n", path); > > + > > + size = fread(data, 1, bin_stat.st_size, g); > > + if (size < bin_stat.st_size) { > > + printf("read failed (%zx)\n", size); > > goto err_2; > > } > > + > > + /* first, calculate signature to determine its size */ > > +#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE) > > see above > > > + if (privkey_file && cert_file) { > > + auth_context.key_file = privkey_file; > > + auth_context.cert_file = cert_file; > > + auth_context.auth.monotonic_count = mcount; > > + auth_context.image_data = data; > > + auth_context.image_size = bin_stat.st_size; > > + > > + if (create_auth_data(&auth_context)) { > > + printf("Signing firmware image
failed\n");
> > + goto err_3; > > + } > > + > > + if (dump_sig && > > + dump_signature(path, auth_context.sig_data, > > + auth_context.sig_size)) {

2021年5月13日(木) 16:24 AKASHI Takahiro takahiro.akashi@linaro.org:
BTW, IMHO, if u-boot.bin can not find the ESL in the device tree, it should skip authentication too.
In this case the capsule should be rejected (if CONFIG_EFI_CAPSULE_AUTHENTICATE=y).
That's basically right. But as I mentioned in my comment against Sughosh's patch, the authentication process will be enforced only if the capsule has an attribute, IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED.
That would be a security desaster.
The requirement that I mentioned above is clearly described in UEFI specification. If you think that it is a disaster, please discuss the topic in UEFI Forum first.
I confirmed UEFI specification, version 2.7, Section.23.1 the last of EFI_FIRMWARE_MANAGEMENT_PROTOCOL.GetImageInfo()
----------------- If IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED is supported and clear, then authentication is not required to perform the firmware image operations. -----------------
Oh, this is really crazy because deciding whether to authenticate the suspicious package or not, depends on whether the package said "please authenticate me" or not. :D
Anyway, since this behavior follows the specification, it should be kept by default, but also IMHO, there should be a CONFIG option to enforce capsule authentication always.
Thank you,

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

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

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

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

[...]
FWIW I personally don't think we should even have a config option. But even if we did it certainly must not be dictated by a hardware config.
When you install distro packages you accept whatever dependencies the package has. mkeficapsule is a capsule creation and signing tool. I don't see any reason for keeping the creation and signing apart.
My question is, since the U-Boot binary is heavily dependent on the target platform, can we split the u-boot.bin creation (may include embedding keys) and the capsule file creation (including signing)?
Building U-Boot and creating a capsule are totally separate. Maybe you get the first capsule years after you buy your board. But this should not stop us from building mkeficapsule when building U-Boot.
Based on what was discussed in the thread waht I think would make more sense is: - Build u-boot and use the script Akashi sent to inject the certificate. Whether we create a single binary (always signed if a config option is there) or 2 binaries (1 signed. 1 unsigned) is an implemetation detail and I am fine with either. - Use mkefi capsule to create the final capsule
If you want to build tools only, you can do so with 'make tools'. The tools target must include mkeficapsule irrespective of configuration.
This line in tools/Makefile must be corrected:
-hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule +hostprogs-y += mkeficapsule
So that's the point exactly. Building the tool is completely disjoint from building a u-boot binary. Also you usually start adding config options to an app, when it starts getting to big and you want discrete functionality. I don't see any reason for making a simple tool, which is supposed to do 2 things (create/sign), require config options and more over config options *for U-Boot*. I also think it's extremely unlikely to get any working distro without libssl.
Best regards
Heinrich

On Thu, May 13, 2021 at 07:42:04PM +0300, Ilias Apalodimas wrote:
[...]
FWIW I personally don't think we should even have a config option. But even if we did it certainly must not be dictated by a hardware config.
When you install distro packages you accept whatever dependencies the package has. mkeficapsule is a capsule creation and signing tool. I don't see any reason for keeping the creation and signing apart.
My question is, since the U-Boot binary is heavily dependent on the target platform, can we split the u-boot.bin creation (may include embedding keys) and the capsule file creation (including signing)?
Building U-Boot and creating a capsule are totally separate. Maybe you get the first capsule years after you buy your board. But this should not stop us from building mkeficapsule when building U-Boot.
Based on what was discussed in the thread waht I think would make more sense is:
- Build u-boot and use the script Akashi sent to inject the certificate. Whether we create a single binary (always signed if a config option is there) or 2 binaries (1 signed. 1 unsigned) is an implemetation detail and I am fine with either.
Let me make clear: "a single binary or 2 binaries" is not an implementation detail, but it's a matter of user's (, distro's or whoever wants to provide a capsule) policy.
- Use mkefi capsule to create the final capsule
If signing feature is enabled in mkeficapsule, you can create both a signed capsule and an unsigned capsule. And yet, some users may totally had no need to authentication for firmware update using UEFI interfaces on their systems. For them, signing should be able to be disabled.
If you want to build tools only, you can do so with 'make tools'. The tools target must include mkeficapsule irrespective of configuration.
This line in tools/Makefile must be corrected:
-hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule +hostprogs-y += mkeficapsule
So that's the point exactly. Building the tool is completely disjoint from building a u-boot binary. Also you usually start adding config options to an app, when it starts getting to big and you want discrete functionality.
I don't get your point. As far as we maintain CONFIG_(HOST_)_EFI_CAPSULE_AUTHENTICATE, we can and should guarantee the compatibility.
I don't see any reason for making a simple tool, which is supposed to do 2 things (create/sign), require config options and more over config options *for U-Boot*.
I don't get you point neither.
I also think it's extremely unlikely to get any working distro without libssl.
Again, (major) distros are ones of users. There may be bunch of users who may build/maintain their systems on their way and not expect any authentication.
Having said that, coincidentally there is happening a similar discussion about building host tools and host-specific configs among Simon, Tom and Alex[1]. (The background for the discussion is a bit different though.)
I'd like to see and follow the direction to be agreed there.
[1] https://lists.denx.de/pipermail/u-boot/2021-May/449050.html
-Takahiro Akashi
Best regards
Heinrich

[...]
Based on what was discussed in the thread waht I think would make more sense is:
- Build u-boot and use the script Akashi sent to inject the certificate. Whether we create a single binary (always signed if a config option is there) or 2 binaries (1 signed. 1 unsigned) is an implemetation detail and I am fine with either.
Let me make clear: "a single binary or 2 binaries" is not an implementation detail, but it's a matter of user's (, distro's or whoever wants to provide a capsule) policy.
- Use mkefi capsule to create the final capsule
If signing feature is enabled in mkeficapsule, you can create both a signed capsule and an unsigned capsule. And yet, some users may totally had no need to authentication for firmware update using UEFI interfaces on their systems. For them, signing should be able to be disabled.
They should use the non signed capsule.
If you want to build tools only, you can do so with 'make tools'. The tools target must include mkeficapsule irrespective of configuration.
This line in tools/Makefile must be corrected:
-hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule +hostprogs-y += mkeficapsule
So that's the point exactly. Building the tool is completely disjoint from building a u-boot binary. Also you usually start adding config options to an app, when it starts getting to big and you want discrete functionality.
I don't get your point. As far as we maintain CONFIG_(HOST_)_EFI_CAPSULE_AUTHENTICATE, we can and should guarantee the compatibility.
I don't see any reason for making a simple tool, which is supposed to do 2 things (create/sign), require config options and more over config options *for U-Boot*.
I don't get you point neither.
The point is that the tool should always have the ability to generate authenticated capsules, regardless of the fact that someone wants to shoot himself in the foot.
I also think it's extremely unlikely to get any working distro without libssl.
Again, (major) distros are ones of users. There may be bunch of users who may build/maintain their systems on their way and not expect any authentication.
And again that's completely disjoint with what the userspace tool that's there to create your capsule can do.
[...]
Thanks /Ilias

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

On 5/13/21 10:38 AM, AKASHI Takahiro wrote:
On Thu, May 13, 2021 at 05:18:36PM +0900, Masami Hiramatsu wrote:
2021年5月13日(木) 16:24 AKASHI Takahiro takahiro.akashi@linaro.org:
> BTW, IMHO, if u-boot.bin can not find the ESL in the device tree, > it should skip authentication too.
In this case the capsule should be rejected (if CONFIG_EFI_CAPSULE_AUTHENTICATE=y).
That's basically right. But as I mentioned in my comment against Sughosh's patch, the authentication process will be enforced only if the capsule has an attribute, IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED.
That would be a security desaster.
The requirement that I mentioned above is clearly described in UEFI specification. If you think that it is a disaster, please discuss the topic in UEFI Forum first.
I confirmed UEFI specification, version 2.7, Section.23.1 the last of EFI_FIRMWARE_MANAGEMENT_PROTOCOL.GetImageInfo()
If IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED is supported and clear, then authentication is not required to perform the firmware image operations.
Thank you for citing this.
This is the fraudulent code:
lib/efi_loader/efi_firmware.c:195
/* Check if the capsule authentication is enabled */ if (env_get("capsule_authentication_enabled")) image_info[0].attributes_setting |= IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED;
It is not allowable that a user can disable image authentication by deleting the environment.
Best regards
Heinrich
Oh, this is really crazy because deciding whether to authenticate the suspicious package or not, depends on whether the package said "please authenticate me" or not. :D
Well, the attributes can been fetched with GetInfo API, but how it is managed depends on the implementation of FMP drivers.
As I proposed somewhere else, those attributes should be maintained in a separate place (maybe as part of system's policy), presumably ESRT or platform-specific internal database?
-Takahiro Akashi
Anyway, since this behavior follows the specification, it should be kept by default, but also IMHO, there should be a CONFIG option to enforce capsule authentication always.
Thank you,
-- Masami Hiramatsu

On 5/13/21 10:18 AM, Masami Hiramatsu wrote:
2021年5月13日(木) 16:24 AKASHI Takahiro takahiro.akashi@linaro.org:
BTW, IMHO, if u-boot.bin can not find the ESL in the device tree, it should skip authentication too.
In this case the capsule should be rejected (if CONFIG_EFI_CAPSULE_AUTHENTICATE=y).
That's basically right. But as I mentioned in my comment against Sughosh's patch, the authentication process will be enforced only if the capsule has an attribute, IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED.
That would be a security desaster.
The requirement that I mentioned above is clearly described in UEFI specification. If you think that it is a disaster, please discuss the topic in UEFI Forum first.
I confirmed UEFI specification, version 2.7, Section.23.1 the last of EFI_FIRMWARE_MANAGEMENT_PROTOCOL.GetImageInfo()
If IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED is supported and clear, then authentication is not required to perform the firmware image operations.
IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED bit is a property of the FMP driver.
Best regards
Heinrich
Oh, this is really crazy because deciding whether to authenticate the suspicious package or not, depends on whether the package said "please authenticate me" or not. :D
Anyway, since this behavior follows the specification, it should be kept by default, but also IMHO, there should be a CONFIG option to enforce capsule authentication always.
Thank you,

On Thu, May 13, 2021 at 08:25:56PM +0200, Heinrich Schuchardt wrote:
On 5/13/21 10:18 AM, Masami Hiramatsu wrote:
2021年5月13日(木) 16:24 AKASHI Takahiro takahiro.akashi@linaro.org:
> BTW, IMHO, if u-boot.bin can not find the ESL in the device tree, > it should skip authentication too.
In this case the capsule should be rejected (if CONFIG_EFI_CAPSULE_AUTHENTICATE=y).
That's basically right. But as I mentioned in my comment against Sughosh's patch, the authentication process will be enforced only if the capsule has an attribute, IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED.
That would be a security desaster.
The requirement that I mentioned above is clearly described in UEFI specification. If you think that it is a disaster, please discuss the topic in UEFI Forum first.
I confirmed UEFI specification, version 2.7, Section.23.1 the last of EFI_FIRMWARE_MANAGEMENT_PROTOCOL.GetImageInfo()
If IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED is supported and clear, then authentication is not required to perform the firmware image operations.
IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED bit is a property of the FMP driver.
Yes, it is. But if the attribute is not changeable at all, why do we need this flag? Why does a "firmware image descriptor" hold two distinct member fields, "AttributesSupported" and "AttributesSetting"? What does "Setting" mean? Who sets what, and when?
-Takahiro Akashi
Best regards
Heinrich
Oh, this is really crazy because deciding whether to authenticate the suspicious package or not, depends on whether the package said "please authenticate me" or not. :D
Anyway, since this behavior follows the specification, it should be kept by default, but also IMHO, there should be a CONFIG option to enforce capsule authentication always.
Thank you,

On 5/14/21 8:19 AM, AKASHI Takahiro wrote:
On Thu, May 13, 2021 at 08:25:56PM +0200, Heinrich Schuchardt wrote:
On 5/13/21 10:18 AM, Masami Hiramatsu wrote:
2021年5月13日(木) 16:24 AKASHI Takahiro takahiro.akashi@linaro.org:
>> BTW, IMHO, if u-boot.bin can not find the ESL in the device tree, >> it should skip authentication too. > > In this case the capsule should be rejected (if > CONFIG_EFI_CAPSULE_AUTHENTICATE=y).
That's basically right. But as I mentioned in my comment against Sughosh's patch, the authentication process will be enforced only if the capsule has an attribute, IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED.
That would be a security desaster.
The requirement that I mentioned above is clearly described in UEFI specification. If you think that it is a disaster, please discuss the topic in UEFI Forum first.
I confirmed UEFI specification, version 2.7, Section.23.1 the last of EFI_FIRMWARE_MANAGEMENT_PROTOCOL.GetImageInfo()
If IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED is supported and clear, then authentication is not required to perform the firmware image operations.
IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED bit is a property of the FMP driver.
Yes, it is. But if the attribute is not changeable at all, why do we need this flag? Why does a "firmware image descriptor" hold two distinct member fields, "AttributesSupported" and "AttributesSetting"? What does "Setting" mean? Who sets what, and when?
"Setting" means value here. None of these flags is set by the user. It is the FMP driver that keeps track of the update state and sets the flags accordingly.
AttributesSupported indicates which bits in AttributesSetting contain accurate information:
E.g for IMAGE_ATTRIBUTE_IN_USE
AttributesSupported | AttributesSetting | Meaning --------------------+-------------------+-------------------- 0 | 0 | state is unknown 0 | 1 | state is unknown 1 | 0 | image is not in use 1 | 1 | image is in use
Some bits indicate a property of the installed image, e.g. IMAGE_ATTRIBUTE_UEFI_IMAGE.
Some bits indicate a system state. E.g. IMAGE_ATTRIBUTE_IN_USE indicates if the information reported by the FMP driver is for the currently used image. This bit might be 0 in AttributesSetting if you have not yet executed a reset after installing the new image and 1 after the reset.
Best regards
Heinrich
Oh, this is really crazy because deciding whether to authenticate the suspicious package or not, depends on whether the package said "please authenticate me" or not. :D
Anyway, since this behavior follows the specification, it should be kept by default, but also IMHO, there should be a CONFIG option to enforce capsule authentication always.
Thank you,

On Fri, May 14, 2021 at 08:59:38AM +0200, Heinrich Schuchardt wrote:
On 5/14/21 8:19 AM, AKASHI Takahiro wrote:
On Thu, May 13, 2021 at 08:25:56PM +0200, Heinrich Schuchardt wrote:
On 5/13/21 10:18 AM, Masami Hiramatsu wrote:
2021年5月13日(木) 16:24 AKASHI Takahiro takahiro.akashi@linaro.org:
> > > BTW, IMHO, if u-boot.bin can not find the ESL in the device tree, > > > it should skip authentication too. > > > > In this case the capsule should be rejected (if > > CONFIG_EFI_CAPSULE_AUTHENTICATE=y). > > That's basically right. > But as I mentioned in my comment against Sughosh's patch, > the authentication process will be enforced only if the capsule has > an attribute, IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED. >
That would be a security desaster.
The requirement that I mentioned above is clearly described in UEFI specification. If you think that it is a disaster, please discuss the topic in UEFI Forum first.
I confirmed UEFI specification, version 2.7, Section.23.1 the last of EFI_FIRMWARE_MANAGEMENT_PROTOCOL.GetImageInfo()
If IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED is supported and clear, then authentication is not required to perform the firmware image operations.
IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED bit is a property of the FMP driver.
Yes, it is. But if the attribute is not changeable at all, why do we need this flag? Why does a "firmware image descriptor" hold two distinct member fields, "AttributesSupported" and "AttributesSetting"? What does "Setting" mean? Who sets what, and when?
"Setting" means value here. None of these flags is set by the user. It is the FMP driver that keeps track of the update state and sets the flags accordingly.
AttributesSupported indicates which bits in AttributesSetting contain accurate information:
E.g for IMAGE_ATTRIBUTE_IN_USE
AttributesSupported | AttributesSetting | Meaning --------------------+-------------------+-------------------- 0 | 0 | state is unknown 0 | 1 | state is unknown 1 | 0 | image is not in use 1 | 1 | image is in use
We are discussing *_REQUIRED. Can you give me the same table for *_REQUIRED?
-Takahiro Akashi
Some bits indicate a property of the installed image, e.g. IMAGE_ATTRIBUTE_UEFI_IMAGE.
Some bits indicate a system state. E.g. IMAGE_ATTRIBUTE_IN_USE indicates if the information reported by the FMP driver is for the currently used image. This bit might be 0 in AttributesSetting if you have not yet executed a reset after installing the new image and 1 after the reset.
Best regards
Heinrich
Oh, this is really crazy because deciding whether to authenticate the suspicious package or not, depends on whether the package said "please authenticate me" or not. :D
Anyway, since this behavior follows the specification, it should be kept by default, but also IMHO, there should be a CONFIG option to enforce capsule authentication always.
Thank you,

On 5/14/21 9:13 AM, AKASHI Takahiro wrote:
E.g for IMAGE_ATTRIBUTE_IN_USE
AttributesSupported | AttributesSetting | Meaning --------------------+-------------------+-------------------- 0 | 0 | state is unknown 0 | 1 | state is unknown 1 | 0 | image is not in use 1 | 1 | image is in use
We are discussing *_REQUIRED. Can you give me the same table for *_REQUIRED?
-Takahiro Akashi
IMAGE_ATTRIBUTE_RESET_REQUIRED
AttributesSupported | AttributesSetting | Meaning --------------------+-------------------+-------------------- 0 | 0 | state is unknown 0 | 1 | state is unknown 1 | 0 | reset is not needed | | to complete upgrade 1 | 1 | reset is needed | | to complete upgrade
IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED
AttributesSupported | AttributesSetting | Meaning --------------------+-------------------+-------------------- 0 | 0 | state is unknown 0 | 1 | state is unknown 1 | 0 | signed and unsigned | | capsules are accepted 1 | 1 | capsules are only | | accepted after | | checking the signature
For both bits AttributesSupported=0 does not make much sense.
IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED is a property of the current image and should only be deleted by installing a new capsule.
A vendor might send you a special firmware image for unlocking your device after registering as a developer. Xiaomi handled it like this for one of my routers.
Best regards
Heinrich

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

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

Hi all,
I think it's time to summarize the topics on this thread.
1. tools/mkeficapsule, config options dependency - The tools, especially useful and distributable tools like mkeficapsule should not be changed by the target board configuration. - Since there are target boards which don't need capsule authentication, it should be configurable. That also can optimize the library dependency.
2. tools/mkeficapsule, revert -K/-D options - Since these options are for embedding a public key in the devicetree, that is not related to the capsule file. Also, the same feature can be provided by a simple shell script.
3. capsule authentication, key embedding method - Embedding key in the devicetree is too fragile, especially, the document says overwriting new device tree including key with fdt command. That is not for the product, only for proof of concept. - Such a key should be embedded in the U-Boot, or hardware secure storage so that the user can not change it. (BTW, I think there are more options, like embedding keys in SCP firmware, TF-A, or OP-TEE, outside of U-Boot)
4. capsule authentication, authentication enablement - The UEFI spec said IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED can be supported but cleared (for the current running firmware). This means it is possible that the authentication feature is supported, but not enabled. - For ensuring security, if U-Boot is compiled with CONFIG_EFI_CAPSULE_AUTHETICATE=y, IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED must always be set.
Are there any other topics on this thread? and any other comments on these topics?
Thank you,
2021年5月14日(金) 18:51 AKASHI Takahiro takahiro.akashi@linaro.org:
Heinrich,
Can you please reply to each of my replies? Otherwise, I don't know which one of my comments/opinions you agree to and which one not.
On Fri, May 14, 2021 at 10:45:48AM +0200, Heinrich Schuchardt wrote:
On 5/14/21 9:13 AM, AKASHI Takahiro wrote:
E.g for IMAGE_ATTRIBUTE_IN_USE
AttributesSupported | AttributesSetting | Meaning --------------------+-------------------+-------------------- 0 | 0 | state is unknown 0 | 1 | state is unknown 1 | 0 | image is not in use 1 | 1 | image is in use
We are discussing *_REQUIRED. Can you give me the same table for *_REQUIRED?
-Takahiro Akashi
IMAGE_ATTRIBUTE_RESET_REQUIRED
AttributesSupported | AttributesSetting | Meaning --------------------+-------------------+-------------------- 0 | 0 | state is unknown 0 | 1 | state is unknown 1 | 0 | reset is not needed | | to complete upgrade 1 | 1 | reset is needed | | to complete upgrade
IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED
AttributesSupported | AttributesSetting | Meaning --------------------+-------------------+-------------------- 0 | 0 | state is unknown 0 | 1 | state is unknown 1 | 0 | signed and unsigned | | capsules are accepted 1 | 1 | capsules are only | | accepted after | | checking the signature
So what? This table shows there is a case where the authentication will be skipped even if CONFIG_EFI_CAPSULE_AUTHETICATE is on and it is completely compliant with UEFI specification.
That is what I and Masami was discussing.
But as I mentioned in my comment against Sughosh's patch, the authentication process will be enforced only if the capsule has an attribute, IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED.
That would be a security desaster.
So I said that you should discuss the topic in UEFI forum first if you think so.
-Takahiro Akashi
For both bits AttributesSupported=0 does not make much sense.
IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED is a property of the current image and should only be deleted by installing a new capsule.
A vendor might send you a special firmware image for unlocking your device after registering as a developer. Xiaomi handled it like this for one of my routers.
Best regards
Heinrich
-- Masami Hiramatsu

On Fri, May 14, 2021 at 10:09:46PM +0900, Masami Hiramatsu wrote:
Hi all,
I think it's time to summarize the topics on this thread.
- tools/mkeficapsule, config options dependency
- The tools, especially useful and distributable tools like
mkeficapsule should not be changed by the target board configuration.
- Since there are target boards which don't need capsule
authentication, it should be configurable. That also can optimize the library dependency.
- tools/mkeficapsule, revert -K/-D options
- Since these options are for embedding a public key in the
devicetree, that is not related to the capsule file. Also, the same feature can be provided by a simple shell script.
- capsule authentication, key embedding method
- Embedding key in the devicetree is too fragile, especially, the
document says overwriting new device tree including key with fdt command. That is not for the product, only for proof of concept.
- Such a key should be embedded in the U-Boot, or hardware secure
storage so that the user can not change it. (BTW, I think there are more options, like embedding keys in SCP firmware, TF-A, or OP-TEE, outside of U-Boot)
- capsule authentication, authentication enablement
- The UEFI spec said IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED can be
supported but cleared (for the current running firmware). This means it is possible that the authentication feature is supported, but not enabled.
- For ensuring security, if U-Boot is compiled with
CONFIG_EFI_CAPSULE_AUTHETICATE=y, IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED must always be set.
Are there any other topics on this thread? and any other comments on these topics?
I think you already mentioned that, but the key retrieval, should be a callback of some sort that each vendor/hardware can define to his own special function and we keep the default as 'key is embedded in U-Boot'. All of the above sound reasonable. I still think (1) is kinda useless, but I'll leave up to the maintainers.
Thanks /Ilias
Thank you,
2021年5月14日(金) 18:51 AKASHI Takahiro takahiro.akashi@linaro.org:
Heinrich,
Can you please reply to each of my replies? Otherwise, I don't know which one of my comments/opinions you agree to and which one not.
On Fri, May 14, 2021 at 10:45:48AM +0200, Heinrich Schuchardt wrote:
On 5/14/21 9:13 AM, AKASHI Takahiro wrote:
E.g for IMAGE_ATTRIBUTE_IN_USE
AttributesSupported | AttributesSetting | Meaning --------------------+-------------------+-------------------- 0 | 0 | state is unknown 0 | 1 | state is unknown 1 | 0 | image is not in use 1 | 1 | image is in use
We are discussing *_REQUIRED. Can you give me the same table for *_REQUIRED?
-Takahiro Akashi
IMAGE_ATTRIBUTE_RESET_REQUIRED
AttributesSupported | AttributesSetting | Meaning --------------------+-------------------+-------------------- 0 | 0 | state is unknown 0 | 1 | state is unknown 1 | 0 | reset is not needed | | to complete upgrade 1 | 1 | reset is needed | | to complete upgrade
IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED
AttributesSupported | AttributesSetting | Meaning --------------------+-------------------+-------------------- 0 | 0 | state is unknown 0 | 1 | state is unknown 1 | 0 | signed and unsigned | | capsules are accepted 1 | 1 | capsules are only | | accepted after | | checking the signature
So what? This table shows there is a case where the authentication will be skipped even if CONFIG_EFI_CAPSULE_AUTHETICATE is on and it is completely compliant with UEFI specification.
That is what I and Masami was discussing.
> But as I mentioned in my comment against Sughosh's patch, > the authentication process will be enforced only if the capsule has > an attribute, IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED. >
That would be a security desaster.
So I said that you should discuss the topic in UEFI forum first if you think so.
-Takahiro Akashi
For both bits AttributesSupported=0 does not make much sense.
IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED is a property of the current image and should only be deleted by installing a new capsule.
A vendor might send you a special firmware image for unlocking your device after registering as a developer. Xiaomi handled it like this for one of my routers.
Best regards
Heinrich
-- Masami Hiramatsu

On 5/14/21 3:09 PM, Masami Hiramatsu wrote:
Hi all,
I think it's time to summarize the topics on this thread.
- tools/mkeficapsule, config options dependency
- The tools, especially useful and distributable tools like
mkeficapsule should not be changed by the target board configuration.
- Since there are target boards which don't need capsule
authentication, it should be configurable. That also can optimize the library dependency.
Thank you for providing this summary.
You described that the tool shall not depend on the target board configuration. Your sentence starting with "Since" contradicts this.
As Ilias pointed out all Linux distributions come with an OpenSSL package. The library dependency is nothing to worry about.
Capsule updates without authentication don't not make much sense in a world full of attacks.
Hence, a configuration switch for the tool is not needed.
Best regards
Heinrich
- tools/mkeficapsule, revert -K/-D options
- Since these options are for embedding a public key in the
devicetree, that is not related to the capsule file. Also, the same feature can be provided by a simple shell script.
- capsule authentication, key embedding method
- Embedding key in the devicetree is too fragile, especially, the
document says overwriting new device tree including key with fdt command. That is not for the product, only for proof of concept.
- Such a key should be embedded in the U-Boot, or hardware secure
storage so that the user can not change it. (BTW, I think there are more options, like embedding keys in SCP firmware, TF-A, or OP-TEE, outside of U-Boot)
- capsule authentication, authentication enablement
- The UEFI spec said IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED can be
supported but cleared (for the current running firmware). This means it is possible that the authentication feature is supported, but not enabled.
- For ensuring security, if U-Boot is compiled with
CONFIG_EFI_CAPSULE_AUTHETICATE=y, IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED must always be set.
Are there any other topics on this thread? and any other comments on these topics?
Thank you,

Hi Heinrich,
2021年5月15日(土) 11:03 Heinrich Schuchardt xypron.glpk@gmx.de:
On 5/14/21 3:09 PM, Masami Hiramatsu wrote:
Hi all,
I think it's time to summarize the topics on this thread.
- tools/mkeficapsule, config options dependency
- The tools, especially useful and distributable tools like
mkeficapsule should not be changed by the target board configuration.
- Since there are target boards which don't need capsule
authentication, it should be configurable. That also can optimize the library dependency.
Thank you for providing this summary.
You described that the tool shall not depend on the target board configuration. Your sentence starting with "Since" contradicts this.
Ah, sorry for the confusion. Each bullet shows a different opinion on the topic.
As Ilias pointed out all Linux distributions come with an OpenSSL package. The library dependency is nothing to worry about.
OK, so this is for topic #1.
Capsule updates without authentication don't not make much sense in a world full of attacks.
and this is for topic #1 and maybe related to #4?
Hence, a configuration switch for the tool is not needed.
Thanks for clarifying your opinion!
Best regards
Heinrich
- tools/mkeficapsule, revert -K/-D options
- Since these options are for embedding a public key in the
devicetree, that is not related to the capsule file. Also, the same feature can be provided by a simple shell script.
- capsule authentication, key embedding method
- Embedding key in the devicetree is too fragile, especially, the
document says overwriting new device tree including key with fdt command. That is not for the product, only for proof of concept.
- Such a key should be embedded in the U-Boot, or hardware secure
storage so that the user can not change it. (BTW, I think there are more options, like embedding keys in SCP firmware, TF-A, or OP-TEE, outside of U-Boot)
- capsule authentication, authentication enablement
- The UEFI spec said IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED can be
supported but cleared (for the current running firmware). This means it is possible that the authentication feature is supported, but not enabled.
- For ensuring security, if U-Boot is compiled with
CONFIG_EFI_CAPSULE_AUTHETICATE=y, IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED must always be set.
Are there any other topics on this thread? and any other comments on these topics?
Thank you,

As we discussed, "-K" and "-D" options have nothing to do with creating a capsule file. The same result can be obtained by using standard commands like: === signature.dts === /dts-v1/; /plugin/;
&{/} { signature { capsule-key = /incbin/("SIGNER.esl"); }; }; === $ dtc -@ -I dts -O dtb -o signature.dtbo signature.dts $ fdtoverlay -i test.dtb -o test_sig.dtb -v signature.dtbo
So just remove this feature. (Effectively revert the commit 322c813f4bec ("mkeficapsule: Add support for embedding public key in a dtb").)
The same feature is implemented by a shell script (tools/fdtsig.sh).
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- Makefile | 7 +- tools/Makefile | 1 - tools/fdtsig.sh | 40 ++++++++ tools/mkeficapsule.c | 235 ++----------------------------------------- 4 files changed, 50 insertions(+), 233 deletions(-) create mode 100755 tools/fdtsig.sh
diff --git a/Makefile b/Makefile index 63b1f0143220..9806464357e0 100644 --- a/Makefile +++ b/Makefile @@ -1015,9 +1015,8 @@ cmd_pad_cat = $(cmd_objcopy) && $(append) || { rm -f $@; false; } quiet_cmd_lzma = LZMA $@ cmd_lzma = lzma -c -z -k -9 $< > $@
-quiet_cmd_mkeficapsule = MKEFICAPSULE $@ -cmd_mkeficapsule = $(objtree)/tools/mkeficapsule -K $(CONFIG_EFI_PKEY_FILE) \ - -D $@ +quiet_cmd_fdtsig = FDTSIG $@ +cmd_fdtsig = $(srctree)/tools/fdtsig.sh $(CONFIG_EFI_PKEY_FILE) $@
cfg: u-boot.cfg
@@ -1114,7 +1113,7 @@ dtbs: dts/dt.dtb ifeq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE)$(CONFIG_EFI_PKEY_DTB_EMBED),yy) dts/dt.dtb: u-boot tools $(Q)$(MAKE) $(build)=dts dtbs - $(call cmd,mkeficapsule) + $(call cmd,fdtsig) else dts/dt.dtb: u-boot $(Q)$(MAKE) $(build)=dts dtbs diff --git a/tools/Makefile b/tools/Makefile index 02eae0286e20..71a52719620c 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -235,7 +235,6 @@ ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),) HOSTLDLIBS_mkeficapsule += \ $(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto") endif -mkeficapsule-objs := mkeficapsule.o $(LIBFDT_OBJS) hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
# We build some files with extra pedantic flags to try to minimize things diff --git a/tools/fdtsig.sh b/tools/fdtsig.sh new file mode 100755 index 000000000000..aaa0a9190845 --- /dev/null +++ b/tools/fdtsig.sh @@ -0,0 +1,40 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0+ +# +# script to add a certificate (efi-signature-list) to dtb blob + +usage() { + if [ -n "$*" ]; then + echo "ERROR: $*" + fi + echo "Usage: "$(basename $0) " <esl file> <dtb file>" +} + +if [ "$#" -ne 2 ]; then + usage "Arguments missing" + exit 1 +fi + +ESL=$1 +DTB=$2 +NEW_DTB=$(basename $DTB)_tmp +SIG=signature + +cat << 'EOF' > $SIG.dts +/dts-v1/; +/plugin/; + +&{/} { + signature { + capsule-key = /incbin/("ESL"); + }; +}; +EOF + +sed -in "s/ESL/$ESL/" $SIG.dts + +dtc -@ -I dts -O dtb -o $SIG.dtbo $SIG.dts +fdtoverlay -i $DTB -o $NEW_DTB -v $SIG.dtbo +mv $NEW_DTB $DTB + +rm $SIG.dts $SIG.dtbo $NEW_DTB diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c index 34ff1bdd82eb..97ce68ec83ee 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -4,17 +4,13 @@ * Author: AKASHI Takahiro */
-#include <errno.h> #include <getopt.h> #include <malloc.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <unistd.h> #include <linux/types.h> - -#include <sys/mman.h> #include <sys/stat.h> #include <sys/types.h>
@@ -28,8 +24,6 @@ #include <openssl/pkcs7.h> #endif
-#include <linux/libfdt.h> - typedef __u8 u8; typedef __u16 u16; typedef __u32 u32; @@ -39,9 +33,6 @@ typedef __s32 s32;
#define aligned_u64 __aligned_u64
-#define SIGNATURE_NODENAME "signature" -#define OVERLAY_NODENAME "__overlay__" - #ifndef __packed #define __packed __attribute__((packed)) #endif @@ -59,9 +50,9 @@ efi_guid_t efi_guid_image_type_uboot_raw = efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
#if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE) -static const char *opts_short = "f:r:i:I:v:D:K:P:C:m:dOh"; +static const char *opts_short = "f:r:i:I:v:P:C:m:dh"; #else -static const char *opts_short = "f:r:i:I:v:D:K:Oh"; +static const char *opts_short = "f:r:i:I:v:h"; #endif
static struct option options[] = { @@ -69,15 +60,12 @@ static struct option options[] = { {"raw", required_argument, NULL, 'r'}, {"index", required_argument, NULL, 'i'}, {"instance", required_argument, NULL, 'I'}, - {"dtb", required_argument, NULL, 'D'}, - {"public key", required_argument, NULL, 'K'}, #if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE) {"private-key", required_argument, NULL, 'P'}, {"certificate", required_argument, NULL, 'C'}, {"monotonic-count", required_argument, NULL, 'm'}, {"dump-sig", no_argument, NULL, 'd'}, #endif - {"overlay", no_argument, NULL, 'O'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0}, }; @@ -104,180 +92,6 @@ static void print_usage(void) tool_name); }
-static int fdt_add_pub_key_data(void *sptr, void *dptr, size_t key_size, - bool overlay) -{ - int parent; - int ov_node; - int frag_node; - int ret = 0; - - if (overlay) { - /* - * The signature would be stored in the - * first fragment node of the overlay - */ - frag_node = fdt_first_subnode(dptr, 0); - if (frag_node == -FDT_ERR_NOTFOUND) { - fprintf(stderr, - "Couldn't find the fragment node: %s\n", - fdt_strerror(frag_node)); - goto done; - } - - ov_node = fdt_subnode_offset(dptr, frag_node, OVERLAY_NODENAME); - if (ov_node == -FDT_ERR_NOTFOUND) { - fprintf(stderr, - "Couldn't find the __overlay__ node: %s\n", - fdt_strerror(ov_node)); - goto done; - } - } else { - ov_node = 0; - } - - parent = fdt_subnode_offset(dptr, ov_node, SIGNATURE_NODENAME); - if (parent == -FDT_ERR_NOTFOUND) { - parent = fdt_add_subnode(dptr, ov_node, SIGNATURE_NODENAME); - if (parent < 0) { - ret = parent; - if (ret != -FDT_ERR_NOSPACE) { - fprintf(stderr, - "Couldn't create signature node: %s\n", - fdt_strerror(parent)); - } - } - } - if (ret) - goto done; - - /* Write the key to the FDT node */ - ret = fdt_setprop(dptr, parent, "capsule-key", - sptr, key_size); - -done: - if (ret) - ret = ret == -FDT_ERR_NOSPACE ? -ENOSPC : -EIO; - - return ret; -} - -static int add_public_key(const char *pkey_file, const char *dtb_file, - bool overlay) -{ - int ret; - int srcfd = -1; - int destfd = -1; - void *sptr = NULL; - void *dptr = NULL; - off_t src_size; - struct stat pub_key; - struct stat dtb; - - /* Find out the size of the public key */ - srcfd = open(pkey_file, O_RDONLY); - if (srcfd == -1) { - fprintf(stderr, "%s: Can't open %s: %s\n", - __func__, pkey_file, strerror(errno)); - ret = -1; - goto err; - } - - ret = fstat(srcfd, &pub_key); - if (ret == -1) { - fprintf(stderr, "%s: Can't stat %s: %s\n", - __func__, pkey_file, strerror(errno)); - ret = -1; - goto err; - } - - src_size = pub_key.st_size; - - /* mmap the public key esl file */ - sptr = mmap(0, src_size, PROT_READ, MAP_SHARED, srcfd, 0); - if (sptr == MAP_FAILED) { - fprintf(stderr, "%s: Failed to mmap %s:%s\n", - __func__, pkey_file, strerror(errno)); - ret = -1; - goto err; - } - - /* Open the dest FDT */ - destfd = open(dtb_file, O_RDWR); - if (destfd == -1) { - fprintf(stderr, "%s: Can't open %s: %s\n", - __func__, dtb_file, strerror(errno)); - ret = -1; - goto err; - } - - ret = fstat(destfd, &dtb); - if (ret == -1) { - fprintf(stderr, "%s: Can't stat %s: %s\n", - __func__, dtb_file, strerror(errno)); - goto err; - } - - dtb.st_size += src_size + 0x30; - if (ftruncate(destfd, dtb.st_size)) { - fprintf(stderr, "%s: Can't expand %s: %s\n", - __func__, dtb_file, strerror(errno)); - ret = -1; - goto err; - } - - errno = 0; - /* mmap the dtb file */ - dptr = mmap(0, dtb.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, - destfd, 0); - if (dptr == MAP_FAILED) { - fprintf(stderr, "%s: Failed to mmap %s:%s\n", - __func__, dtb_file, strerror(errno)); - ret = -1; - goto err; - } - - if (fdt_check_header(dptr)) { - fprintf(stderr, "%s: Invalid FDT header\n", __func__); - ret = -1; - goto err; - } - - ret = fdt_open_into(dptr, dptr, dtb.st_size); - if (ret) { - fprintf(stderr, "%s: Cannot expand FDT: %s\n", - __func__, fdt_strerror(ret)); - ret = -1; - goto err; - } - - /* Copy the esl file to the expanded FDT */ - ret = fdt_add_pub_key_data(sptr, dptr, src_size, overlay); - if (ret < 0) { - fprintf(stderr, "%s: Unable to add public key to the FDT\n", - __func__); - ret = -1; - goto err; - } - - ret = 0; - -err: - if (sptr) - munmap(sptr, src_size); - - if (dptr) - munmap(dptr, dtb.st_size); - - if (srcfd != -1) - close(srcfd); - - if (destfd != -1) - close(destfd); - - return ret; -} - struct auth_context { char *key_file; char *cert_file; @@ -608,19 +422,13 @@ err_1: int main(int argc, char **argv) { char *file; - char *pkey_file; - char *dtb_file; efi_guid_t *guid; unsigned long index, instance; uint64_t mcount; char *privkey_file, *cert_file; int c, idx; - int ret; - bool overlay = false;
file = NULL; - pkey_file = NULL; - dtb_file = NULL; guid = NULL; index = 0; instance = 0; @@ -656,20 +464,6 @@ int main(int argc, char **argv) case 'I': instance = strtoul(optarg, NULL, 0); break; - case 'K': - if (pkey_file) { - printf("Public Key already specified\n"); - return -1; - } - pkey_file = optarg; - break; - case 'D': - if (dtb_file) { - printf("DTB file already specified\n"); - return -1; - } - dtb_file = optarg; - break; #if IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE) case 'P': if (privkey_file) { @@ -692,9 +486,6 @@ int main(int argc, char **argv) dump_sig = 1; break; #endif - case 'O': - overlay = true; - break; case 'h': print_usage(); return 0; @@ -702,27 +493,15 @@ int main(int argc, char **argv) }
/* check necessary parameters */ - if ((file && (!(optind < argc) || - (privkey_file && !cert_file) || - (!privkey_file && cert_file))) || - ((pkey_file && !dtb_file) || - (!pkey_file && dtb_file))) { + if (file && (!(optind < argc) || + (privkey_file && !cert_file) || + (!privkey_file && cert_file))) { print_usage(); exit(EXIT_FAILURE); }
- if (pkey_file && dtb_file) { - ret = add_public_key(pkey_file, dtb_file, overlay); - if (ret == -1) { - printf("Adding public key to the dtb failed\n"); - exit(EXIT_FAILURE); - } - } - - if (optind < argc && - create_fwbin(argv[optind], file, guid, index, instance, - mcount, privkey_file, cert_file) - < 0) { + if (create_fwbin(argv[optind], file, guid, index, instance, + mcount, privkey_file, cert_file) < 0) { printf("Creating firmware capsule failed\n"); exit(EXIT_FAILURE); }

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

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

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

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

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

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

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

On 5/13/21 9:13 AM, AKASHI Takahiro wrote:
On Thu, May 13, 2021 at 07:08:12AM +0200, Heinrich Schuchardt wrote:
On 5/13/21 4:33 AM, AKASHI Takahiro wrote:
On Wed, May 12, 2021 at 12:01:32PM +0200, Heinrich Schuchardt wrote:
On 12.05.21 10:01, Ilias Apalodimas wrote:
On Wed, May 12, 2021 at 04:49:02PM +0900, Masami Hiramatsu wrote:
Hi Ilias,
2021年5月12日(水) 16:21 Ilias Apalodimas ilias.apalodimas@linaro.org: > > Akashi-san, > > On Wed, May 12, 2021 at 01:57:51PM +0900, AKASHI Takahiro wrote: >> As we discussed, "-K" and "-D" options have nothing to do with >> creating a capsule file. The same result can be obtained by >> using standard commands like: >> === signature.dts === >> /dts-v1/; >> /plugin/; >> >> &{/} { >> signature { >> capsule-key = /incbin/("SIGNER.esl"); >> }; >> }; >> === >> $ dtc -@ -I dts -O dtb -o signature.dtbo signature.dts >> $ fdtoverlay -i test.dtb -o test_sig.dtb -v signature.dtbo >> >> So just remove this feature. >> (Effectively revert the commit 322c813f4bec ("mkeficapsule: Add support >> for embedding public key in a dtb").) >> >> The same feature is implemented by a shell script (tools/fdtsig.sh). > > > The only reason I can see to keep this, is if mkeficapsule gets included > intro distro packages in the future. That would make end users life a bit > easier, since they would need a single binary to create the whole > CapsuleUpdate sequence.
Hmm, I think it is better to write a manpage of mkeficapsule which also describes how to embed the key into dtb as in the above example if it is so short. Or, distros can package the above shell script with mkeficapsule.
Embedding a key and signing a capsule are different operations but using the same tool may confuse users (at least me).
Sure fair enough. I am merely pointing out we need a way to explain all of those to users.
This is currently our only documentation:
https://u-boot.readthedocs.io/en/latest/board/emulation/qemu_capsule_update....
As I mentioned several times (and TODO in the cover letter), this text must be reviewed, revised and generalized as a platform-independent document. It contains a couple of errors.
For mkimage we have a man-page ./doc/mkimage.1 that is packaged with Debians u-boot-tools package. Please, provide a similar man-page as ./doc/mkeficapsule.1.
So after all do you agree to removing "-K/-D"?
I see no need to replicate in U-Boot what is already in the device tree compiler package.
This is another reason that we should remove Sughosh's change.
In the current workflow the fdt command is used to load the public key. This is insecure and not usable for production.
I totally disagree. Why is using fdt command (what do you mean by fdt command, dtc/fdtoverlay?) insecure?
A user can load an insecure capsule.
The fdt command is in /cmd/fdt.c and you are referring to it in board/emulation/qemu_capsule_update.rst.
The public key used to verify the capsule must be built into the U-Boot binary. This will supplant the -K and -D options.
I don't get your point. You don't understand my code.
Even with Sughosh's original patch, the public key (as I said, it is not a public key but a X509 certificate in ESL format) is embedded in the U-Boot's "control device tree".
No, the ESL file it is not built into U-Boot's control device tree.
A user is loading it and updating the control device tree.
You shouldn't trust anything a user has loaded. You need at least the public key of the root CA built somewhere into U-Boot.
The 'fdt resize' command may overwrite code. This is not what you want to do with the control device tree.
If CONFIG_OF_LIVE=y, the active device tree is not at $fdtcontroladdr but in a hierarchical structure. You cannot update it via the fdt command.
Even after applying my patch, this is true.
Or are you insisting that the key should not be in the device tree?
The public key of the root CA must not be in a place where it can be changed by a user while the device is in deployed mode.
The device-tree based design is a good feasibility study but not suitable for production.
Best regards
Heinrich

Heinrich,
You are discussing two different issues: 1. if we should remove "-K/-D" options from mkeficapsule 2. if it is safe or not to store a key in device tree
It makes the discussion in this thread confusing.
On Thu, May 13, 2021 at 07:42:23PM +0200, Heinrich Schuchardt wrote:
On 5/13/21 9:13 AM, AKASHI Takahiro wrote:
On Thu, May 13, 2021 at 07:08:12AM +0200, Heinrich Schuchardt wrote:
On 5/13/21 4:33 AM, AKASHI Takahiro wrote:
On Wed, May 12, 2021 at 12:01:32PM +0200, Heinrich Schuchardt wrote:
On 12.05.21 10:01, Ilias Apalodimas wrote:
On Wed, May 12, 2021 at 04:49:02PM +0900, Masami Hiramatsu wrote: > Hi Ilias, > > 2021年5月12日(水) 16:21 Ilias Apalodimas ilias.apalodimas@linaro.org: > > > > Akashi-san, > > > > On Wed, May 12, 2021 at 01:57:51PM +0900, AKASHI Takahiro wrote: > > > As we discussed, "-K" and "-D" options have nothing to do with > > > creating a capsule file. The same result can be obtained by > > > using standard commands like: > > > === signature.dts === > > > /dts-v1/; > > > /plugin/; > > > > > > &{/} { > > > signature { > > > capsule-key = /incbin/("SIGNER.esl"); > > > }; > > > }; > > > === > > > $ dtc -@ -I dts -O dtb -o signature.dtbo signature.dts > > > $ fdtoverlay -i test.dtb -o test_sig.dtb -v signature.dtbo > > > > > > So just remove this feature. > > > (Effectively revert the commit 322c813f4bec ("mkeficapsule: Add support > > > for embedding public key in a dtb").) > > > > > > The same feature is implemented by a shell script (tools/fdtsig.sh). > > > > > > The only reason I can see to keep this, is if mkeficapsule gets included > > intro distro packages in the future. That would make end users life a bit > > easier, since they would need a single binary to create the whole > > CapsuleUpdate sequence. > > Hmm, I think it is better to write a manpage of mkeficapsule which > also describes > how to embed the key into dtb as in the above example if it is so short. > Or, distros can package the above shell script with mkeficapsule. > > Embedding a key and signing a capsule are different operations but > using the same tool may confuse users (at least me).
Sure fair enough. I am merely pointing out we need a way to explain all of those to users.
This is currently our only documentation:
https://u-boot.readthedocs.io/en/latest/board/emulation/qemu_capsule_update....
As I mentioned several times (and TODO in the cover letter), this text must be reviewed, revised and generalized as a platform-independent document. It contains a couple of errors.
For mkimage we have a man-page ./doc/mkimage.1 that is packaged with Debians u-boot-tools package. Please, provide a similar man-page as ./doc/mkeficapsule.1.
So after all do you agree to removing "-K/-D"?
Regarding (1), you should clarify your opinion here first.
I see no need to replicate in U-Boot what is already in the device tree compiler package.
This is another reason that we should remove Sughosh's change.
Hereafter, you are talking about (2).
In the current workflow the fdt command is used to load the public key. This is insecure and not usable for production.
I totally disagree. Why is using fdt command (what do you mean by fdt command, dtc/fdtoverlay?) insecure?
A user can load an insecure capsule.
The fdt command is in /cmd/fdt.c and you are referring to it in board/emulation/qemu_capsule_update.rst.
OK, you meant U-Boot's fdt command.
The public key used to verify the capsule must be built into the U-Boot binary. This will supplant the -K and -D options.
I don't get your point. You don't understand my code.
Even with Sughosh's original patch, the public key (as I said, it is not a public key but a X509 certificate in ESL format) is embedded in the U-Boot's "control device tree".
No, the ESL file it is not built into U-Boot's control device tree.
What I meant by "control device tree" is the feature provided by OF_CONTROL+OF_EMBED with Sughosh's patch[1] which is also a prerequisite for my patch series.
!config OF_EMBED ! bool "Embedded DTB for DT control" ! help ! If this option is enabled, the device tree will be picked up and ! built into the U-Boot image.
A user is loading it and updating the control device tree.
You shouldn't trust anything a user has loaded. You need at least the public key of the root CA built somewhere into U-Boot.
The 'fdt resize' command may overwrite code. This is not what you want to do with the control device tree.
If CONFIG_OF_LIVE=y, the active device tree is not at $fdtcontroladdr but in a hierarchical structure. You cannot update it via the fdt command.
Even after applying my patch, this is true.
Or are you insisting that the key should not be in the device tree?
The public key of the root CA must not be in a place where it can be changed by a user while the device is in deployed mode.
UEFI secure boot and capsule update are totally independent concepts as we discussed a long time ago. The notion of "deployed mode" is only valid for secure boot.
The device-tree based design is a good feasibility study but not suitable for production.
Nevertheless, I agree, even I have already mentioned the similar concern in [2], additionally saying we should turn off "fdt" command.
[1] https://lists.denx.de/pipermail/u-boot/2021-April/447183.html [2] (oops, my message seems to have been lost.)
-Takahiro Akashi
Best regards
Heinrich

Hi Heinrich,
2021年5月14日(金) 2:42 Heinrich Schuchardt xypron.glpk@gmx.de:
On 5/13/21 9:13 AM, AKASHI Takahiro wrote:
On Thu, May 13, 2021 at 07:08:12AM +0200, Heinrich Schuchardt wrote:
On 5/13/21 4:33 AM, AKASHI Takahiro wrote:
On Wed, May 12, 2021 at 12:01:32PM +0200, Heinrich Schuchardt wrote:
On 12.05.21 10:01, Ilias Apalodimas wrote:
On Wed, May 12, 2021 at 04:49:02PM +0900, Masami Hiramatsu wrote: > Hi Ilias, > > 2021年5月12日(水) 16:21 Ilias Apalodimas ilias.apalodimas@linaro.org: >> >> Akashi-san, >> >> On Wed, May 12, 2021 at 01:57:51PM +0900, AKASHI Takahiro wrote: >>> As we discussed, "-K" and "-D" options have nothing to do with >>> creating a capsule file. The same result can be obtained by >>> using standard commands like: >>> === signature.dts === >>> /dts-v1/; >>> /plugin/; >>> >>> &{/} { >>> signature { >>> capsule-key = /incbin/("SIGNER.esl"); >>> }; >>> }; >>> === >>> $ dtc -@ -I dts -O dtb -o signature.dtbo signature.dts >>> $ fdtoverlay -i test.dtb -o test_sig.dtb -v signature.dtbo >>> >>> So just remove this feature. >>> (Effectively revert the commit 322c813f4bec ("mkeficapsule: Add support >>> for embedding public key in a dtb").) >>> >>> The same feature is implemented by a shell script (tools/fdtsig.sh). >> >> >> The only reason I can see to keep this, is if mkeficapsule gets included >> intro distro packages in the future. That would make end users life a bit >> easier, since they would need a single binary to create the whole >> CapsuleUpdate sequence. > > Hmm, I think it is better to write a manpage of mkeficapsule which > also describes > how to embed the key into dtb as in the above example if it is so short. > Or, distros can package the above shell script with mkeficapsule. > > Embedding a key and signing a capsule are different operations but > using the same tool may confuse users (at least me).
Sure fair enough. I am merely pointing out we need a way to explain all of those to users.
This is currently our only documentation:
https://u-boot.readthedocs.io/en/latest/board/emulation/qemu_capsule_update....
As I mentioned several times (and TODO in the cover letter), this text must be reviewed, revised and generalized as a platform-independent document. It contains a couple of errors.
For mkimage we have a man-page ./doc/mkimage.1 that is packaged with Debians u-boot-tools package. Please, provide a similar man-page as ./doc/mkeficapsule.1.
So after all do you agree to removing "-K/-D"?
I see no need to replicate in U-Boot what is already in the device tree compiler package.
This is another reason that we should remove Sughosh's change.
In the current workflow the fdt command is used to load the public key. This is insecure and not usable for production.
I totally disagree. Why is using fdt command (what do you mean by fdt command, dtc/fdtoverlay?) insecure?
A user can load an insecure capsule.
The fdt command is in /cmd/fdt.c and you are referring to it in board/emulation/qemu_capsule_update.rst.
Hmm, this seems like a testing manual.
The public key used to verify the capsule must be built into the U-Boot binary. This will supplant the -K and -D options.
I don't get your point. You don't understand my code.
Even with Sughosh's original patch, the public key (as I said, it is not a public key but a X509 certificate in ESL format) is embedded in the U-Boot's "control device tree".
No, the ESL file it is not built into U-Boot's control device tree.
A user is loading it and updating the control device tree.
In my case (I've tested it on the DeveloperBox), the key is embedded in the U-Boot's control device tree.
You shouldn't trust anything a user has loaded. You need at least the public key of the root CA built somewhere into U-Boot.
However, I agreed this point. If we use the public key in the device tree, it must be loaded as a part of U-Boot itself, and must not overwritten by the user given fdt.
The 'fdt resize' command may overwrite code. This is not what you want to do with the control device tree.
If CONFIG_OF_LIVE=y, the active device tree is not at $fdtcontroladdr but in a hierarchical structure. You cannot update it via the fdt command.
Even after applying my patch, this is true.
Or are you insisting that the key should not be in the device tree?
The public key of the root CA must not be in a place where it can be changed by a user while the device is in deployed mode.
The device-tree based design is a good feasibility study but not suitable for production.
Therefore, there is no reason mkeficapsule keeps -K/-D anymore, because embedding public key will be done in u-boot.bin build process (or embedded in the hardware via platform dependent interface.)
Thank you,

This command allows us to add a certificate (or public key) to dtb blob: { signature { capsule-key = "..."; }; }
The value is actually a signature list in terms of UEFI specificaion, and used in verifying UEFI capsules.
The code was originally developed by Sughosh and derived from mkeficapsule.c.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- Makefile | 2 +- tools/Makefile | 2 + tools/fdtsig.c | 274 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 277 insertions(+), 1 deletion(-) create mode 100644 tools/fdtsig.c
diff --git a/Makefile b/Makefile index 9806464357e0..8b40987234a0 100644 --- a/Makefile +++ b/Makefile @@ -1016,7 +1016,7 @@ quiet_cmd_lzma = LZMA $@ cmd_lzma = lzma -c -z -k -9 $< > $@
quiet_cmd_fdtsig = FDTSIG $@ -cmd_fdtsig = $(srctree)/tools/fdtsig.sh $(CONFIG_EFI_PKEY_FILE) $@ +cmd_fdtsig = $(objtree)/tools/fdtsig -K $(CONFIG_EFI_PKEY_FILE) -D $@
cfg: u-boot.cfg
diff --git a/tools/Makefile b/tools/Makefile index 71a52719620c..e6fd1dbade19 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -234,6 +234,8 @@ HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),) HOSTLDLIBS_mkeficapsule += \ $(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto") + fdtsig-objs := fdtsig.o $(LIBFDT_OBJS) + hostprogs-y += fdtsig endif hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
diff --git a/tools/fdtsig.c b/tools/fdtsig.c new file mode 100644 index 000000000000..daa1e63c3b33 --- /dev/null +++ b/tools/fdtsig.c @@ -0,0 +1,274 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2021 Linaro Limited + * The code in this file was extracted from mkeficapsule.c + */ + +#include <errno.h> +#include <fcntl.h> +#include <getopt.h> +#include <malloc.h> +#include <stdbool.h> +#include <stdio.h> +#include <unistd.h> + +#include <sys/mman.h> +#include <sys/stat.h> + +#include <linux/libfdt.h> + +#define SIGNATURE_NODENAME "signature" +#define OVERLAY_NODENAME "__overlay__" + +static const char *tool_name = "fdtsig"; + +static const char *opts_short = "D:K:Oh"; + +static struct option options[] = { + {"dtb", required_argument, NULL, 'D'}, + {"public key", required_argument, NULL, 'K'}, + {"overlay", no_argument, NULL, 'O'}, + {"help", no_argument, NULL, 'h'}, + {NULL, 0, NULL, 0}, +}; + +static void print_usage(void) +{ + printf("Usage: %s [options]\n" + "Options:\n" + + "\t-K, --public-key <key file> public key esl file\n" + "\t-D, --dtb <dtb file> dtb file\n" + "\t-O, --overlay the dtb file is an overlay\n" + "\t-h, --help print a help message\n", + tool_name); +} + +static int fdt_add_pub_key_data(void *sptr, void *dptr, size_t key_size, + bool overlay) +{ + int parent; + int ov_node; + int frag_node; + int ret = 0; + + if (overlay) { + /* + * The signature would be stored in the + * first fragment node of the overlay + */ + frag_node = fdt_first_subnode(dptr, 0); + if (frag_node == -FDT_ERR_NOTFOUND) { + fprintf(stderr, + "Couldn't find the fragment node: %s\n", + fdt_strerror(frag_node)); + goto done; + } + + ov_node = fdt_subnode_offset(dptr, frag_node, OVERLAY_NODENAME); + if (ov_node == -FDT_ERR_NOTFOUND) { + fprintf(stderr, + "Couldn't find the __overlay__ node: %s\n", + fdt_strerror(ov_node)); + goto done; + } + } else { + ov_node = 0; + } + + parent = fdt_subnode_offset(dptr, ov_node, SIGNATURE_NODENAME); + if (parent == -FDT_ERR_NOTFOUND) { + parent = fdt_add_subnode(dptr, ov_node, SIGNATURE_NODENAME); + if (parent < 0) { + ret = parent; + if (ret != -FDT_ERR_NOSPACE) { + fprintf(stderr, + "Couldn't create signature node: %s\n", + fdt_strerror(parent)); + } + } + } + if (ret) + goto done; + + /* Write the key to the FDT node */ + ret = fdt_setprop(dptr, parent, "capsule-key", + sptr, key_size); + +done: + if (ret) + ret = ret == -FDT_ERR_NOSPACE ? -ENOSPC : -EIO; + + return ret; +} + +static int add_public_key(const char *pkey_file, const char *dtb_file, + bool overlay) +{ + int ret; + int srcfd = -1; + int destfd = -1; + void *sptr = NULL; + void *dptr = NULL; + off_t src_size; + struct stat pub_key; + struct stat dtb; + + /* Find out the size of the public key */ + srcfd = open(pkey_file, O_RDONLY); + if (srcfd == -1) { + fprintf(stderr, "%s: Can't open %s: %s\n", + __func__, pkey_file, strerror(errno)); + ret = -1; + goto err; + } + + ret = fstat(srcfd, &pub_key); + if (ret == -1) { + fprintf(stderr, "%s: Can't stat %s: %s\n", + __func__, pkey_file, strerror(errno)); + ret = -1; + goto err; + } + + src_size = pub_key.st_size; + + /* mmap the public key esl file */ + sptr = mmap(0, src_size, PROT_READ, MAP_SHARED, srcfd, 0); + if (sptr == MAP_FAILED) { + fprintf(stderr, "%s: Failed to mmap %s:%s\n", + __func__, pkey_file, strerror(errno)); + ret = -1; + goto err; + } + + /* Open the dest FDT */ + destfd = open(dtb_file, O_RDWR); + if (destfd == -1) { + fprintf(stderr, "%s: Can't open %s: %s\n", + __func__, dtb_file, strerror(errno)); + ret = -1; + goto err; + } + + ret = fstat(destfd, &dtb); + if (ret == -1) { + fprintf(stderr, "%s: Can't stat %s: %s\n", + __func__, dtb_file, strerror(errno)); + goto err; + } + + dtb.st_size += src_size + 0x30; + if (ftruncate(destfd, dtb.st_size)) { + fprintf(stderr, "%s: Can't expand %s: %s\n", + __func__, dtb_file, strerror(errno)); + ret = -1; + goto err; + } + + errno = 0; + /* mmap the dtb file */ + dptr = mmap(0, dtb.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, + destfd, 0); + if (dptr == MAP_FAILED) { + fprintf(stderr, "%s: Failed to mmap %s:%s\n", + __func__, dtb_file, strerror(errno)); + ret = -1; + goto err; + } + + if (fdt_check_header(dptr)) { + fprintf(stderr, "%s: Invalid FDT header\n", __func__); + ret = -1; + goto err; + } + + ret = fdt_open_into(dptr, dptr, dtb.st_size); + if (ret) { + fprintf(stderr, "%s: Cannot expand FDT: %s\n", + __func__, fdt_strerror(ret)); + ret = -1; + goto err; + } + + /* Copy the esl file to the expanded FDT */ + ret = fdt_add_pub_key_data(sptr, dptr, src_size, overlay); + if (ret < 0) { + fprintf(stderr, "%s: Unable to add public key to the FDT\n", + __func__); + ret = -1; + goto err; + } + + ret = 0; + +err: + if (sptr) + munmap(sptr, src_size); + + if (dptr) + munmap(dptr, dtb.st_size); + + if (srcfd != -1) + close(srcfd); + + if (destfd != -1) + close(destfd); + + return ret; +} + +int main(int argc, char **argv) +{ + char *pkey_file; + char *dtb_file; + bool overlay; + int c, idx, ret; + + pkey_file = NULL; + dtb_file = NULL; + overlay = false; + + for (;;) { + c = getopt_long(argc, argv, opts_short, options, &idx); + if (c == -1) + break; + + switch (c) { + case 'K': + if (pkey_file) { + printf("Public Key already specified\n"); + return -1; + } + pkey_file = optarg; + break; + case 'D': + if (dtb_file) { + printf("DTB file already specified\n"); + return -1; + } + dtb_file = optarg; + break; + case 'O': + overlay = true; + break; + case 'h': + print_usage(); + return 0; + } + } + + /* check necessary parameters */ + if (!pkey_file || !dtb_file) { + print_usage(); + exit(EXIT_FAILURE); + } + + ret = add_public_key(pkey_file, dtb_file, overlay); + if (ret == -1) { + printf("Adding public key to the dtb failed\n"); + exit(EXIT_FAILURE); + } + + exit(EXIT_SUCCESS); +}

On 5/12/21 6:57 AM, AKASHI Takahiro wrote:
This command allows us to add a certificate (or public key) to dtb blob: { signature { capsule-key = "..."; }; }
The value is actually a signature list in terms of UEFI specificaion, and used in verifying UEFI capsules.
The code was originally developed by Sughosh and derived from mkeficapsule.c.
I have no clue how you want to use this command. Please, provide a description of the workflow using this command.
In your comments for patch 2 you wrote that the -K and -D option can be replaced by tools provided by the device-tree compiler package. Why do they reappear here?
Please, add a man-page (/doc/fdtsig.1).
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
Makefile | 2 +- tools/Makefile | 2 + tools/fdtsig.c | 274 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 277 insertions(+), 1 deletion(-) create mode 100644 tools/fdtsig.c
diff --git a/Makefile b/Makefile index 9806464357e0..8b40987234a0 100644 --- a/Makefile +++ b/Makefile @@ -1016,7 +1016,7 @@ quiet_cmd_lzma = LZMA $@ cmd_lzma = lzma -c -z -k -9 $< > $@
quiet_cmd_fdtsig = FDTSIG $@ -cmd_fdtsig = $(srctree)/tools/fdtsig.sh $(CONFIG_EFI_PKEY_FILE) $@ +cmd_fdtsig = $(objtree)/tools/fdtsig -K $(CONFIG_EFI_PKEY_FILE) -D $@
cfg: u-boot.cfg
diff --git a/tools/Makefile b/tools/Makefile index 71a52719620c..e6fd1dbade19 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -234,6 +234,8 @@ HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),) HOSTLDLIBS_mkeficapsule += \ $(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto")
- fdtsig-objs := fdtsig.o $(LIBFDT_OBJS)
- hostprogs-y += fdtsig endif hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
diff --git a/tools/fdtsig.c b/tools/fdtsig.c new file mode 100644 index 000000000000..daa1e63c3b33 --- /dev/null +++ b/tools/fdtsig.c @@ -0,0 +1,274 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright 2021 Linaro Limited
- The code in this file was extracted from mkeficapsule.c
That information is better described by the git log.
Please, explain here what this tool is used for.
Best regards
Heinrich
- */
+#include <errno.h> +#include <fcntl.h> +#include <getopt.h> +#include <malloc.h> +#include <stdbool.h> +#include <stdio.h> +#include <unistd.h>
+#include <sys/mman.h> +#include <sys/stat.h>
+#include <linux/libfdt.h>
+#define SIGNATURE_NODENAME "signature" +#define OVERLAY_NODENAME "__overlay__"
+static const char *tool_name = "fdtsig";
+static const char *opts_short = "D:K:Oh";
+static struct option options[] = {
- {"dtb", required_argument, NULL, 'D'},
- {"public key", required_argument, NULL, 'K'},
- {"overlay", no_argument, NULL, 'O'},
- {"help", no_argument, NULL, 'h'},
- {NULL, 0, NULL, 0},
+};
+static void print_usage(void) +{
- printf("Usage: %s [options]\n"
"Options:\n"
"\t-K, --public-key <key file> public key esl file\n"
"\t-D, --dtb <dtb file> dtb file\n"
"\t-O, --overlay the dtb file is an overlay\n"
"\t-h, --help print a help message\n",
tool_name);
+}
+static int fdt_add_pub_key_data(void *sptr, void *dptr, size_t key_size,
bool overlay)
+{
- int parent;
- int ov_node;
- int frag_node;
- int ret = 0;
- if (overlay) {
/*
* The signature would be stored in the
* first fragment node of the overlay
*/
frag_node = fdt_first_subnode(dptr, 0);
if (frag_node == -FDT_ERR_NOTFOUND) {
fprintf(stderr,
"Couldn't find the fragment node: %s\n",
fdt_strerror(frag_node));
goto done;
}
ov_node = fdt_subnode_offset(dptr, frag_node, OVERLAY_NODENAME);
if (ov_node == -FDT_ERR_NOTFOUND) {
fprintf(stderr,
"Couldn't find the __overlay__ node: %s\n",
fdt_strerror(ov_node));
goto done;
}
- } else {
ov_node = 0;
- }
- parent = fdt_subnode_offset(dptr, ov_node, SIGNATURE_NODENAME);
- if (parent == -FDT_ERR_NOTFOUND) {
parent = fdt_add_subnode(dptr, ov_node, SIGNATURE_NODENAME);
if (parent < 0) {
ret = parent;
if (ret != -FDT_ERR_NOSPACE) {
fprintf(stderr,
"Couldn't create signature node: %s\n",
fdt_strerror(parent));
}
}
- }
- if (ret)
goto done;
- /* Write the key to the FDT node */
- ret = fdt_setprop(dptr, parent, "capsule-key",
sptr, key_size);
+done:
- if (ret)
ret = ret == -FDT_ERR_NOSPACE ? -ENOSPC : -EIO;
- return ret;
+}
+static int add_public_key(const char *pkey_file, const char *dtb_file,
bool overlay)
+{
- int ret;
- int srcfd = -1;
- int destfd = -1;
- void *sptr = NULL;
- void *dptr = NULL;
- off_t src_size;
- struct stat pub_key;
- struct stat dtb;
- /* Find out the size of the public key */
- srcfd = open(pkey_file, O_RDONLY);
- if (srcfd == -1) {
fprintf(stderr, "%s: Can't open %s: %s\n",
__func__, pkey_file, strerror(errno));
ret = -1;
goto err;
- }
- ret = fstat(srcfd, &pub_key);
- if (ret == -1) {
fprintf(stderr, "%s: Can't stat %s: %s\n",
__func__, pkey_file, strerror(errno));
ret = -1;
goto err;
- }
- src_size = pub_key.st_size;
- /* mmap the public key esl file */
- sptr = mmap(0, src_size, PROT_READ, MAP_SHARED, srcfd, 0);
- if (sptr == MAP_FAILED) {
fprintf(stderr, "%s: Failed to mmap %s:%s\n",
__func__, pkey_file, strerror(errno));
ret = -1;
goto err;
- }
- /* Open the dest FDT */
- destfd = open(dtb_file, O_RDWR);
- if (destfd == -1) {
fprintf(stderr, "%s: Can't open %s: %s\n",
__func__, dtb_file, strerror(errno));
ret = -1;
goto err;
- }
- ret = fstat(destfd, &dtb);
- if (ret == -1) {
fprintf(stderr, "%s: Can't stat %s: %s\n",
__func__, dtb_file, strerror(errno));
goto err;
- }
- dtb.st_size += src_size + 0x30;
- if (ftruncate(destfd, dtb.st_size)) {
fprintf(stderr, "%s: Can't expand %s: %s\n",
__func__, dtb_file, strerror(errno));
ret = -1;
goto err;
- }
- errno = 0;
- /* mmap the dtb file */
- dptr = mmap(0, dtb.st_size, PROT_READ | PROT_WRITE, MAP_SHARED,
destfd, 0);
- if (dptr == MAP_FAILED) {
fprintf(stderr, "%s: Failed to mmap %s:%s\n",
__func__, dtb_file, strerror(errno));
ret = -1;
goto err;
- }
- if (fdt_check_header(dptr)) {
fprintf(stderr, "%s: Invalid FDT header\n", __func__);
ret = -1;
goto err;
- }
- ret = fdt_open_into(dptr, dptr, dtb.st_size);
- if (ret) {
fprintf(stderr, "%s: Cannot expand FDT: %s\n",
__func__, fdt_strerror(ret));
ret = -1;
goto err;
- }
- /* Copy the esl file to the expanded FDT */
- ret = fdt_add_pub_key_data(sptr, dptr, src_size, overlay);
- if (ret < 0) {
fprintf(stderr, "%s: Unable to add public key to the FDT\n",
__func__);
ret = -1;
goto err;
- }
- ret = 0;
+err:
- if (sptr)
munmap(sptr, src_size);
- if (dptr)
munmap(dptr, dtb.st_size);
- if (srcfd != -1)
close(srcfd);
- if (destfd != -1)
close(destfd);
- return ret;
+}
+int main(int argc, char **argv) +{
- char *pkey_file;
- char *dtb_file;
- bool overlay;
- int c, idx, ret;
- pkey_file = NULL;
- dtb_file = NULL;
- overlay = false;
- for (;;) {
c = getopt_long(argc, argv, opts_short, options, &idx);
if (c == -1)
break;
switch (c) {
case 'K':
if (pkey_file) {
printf("Public Key already specified\n");
return -1;
}
pkey_file = optarg;
break;
case 'D':
if (dtb_file) {
printf("DTB file already specified\n");
return -1;
}
dtb_file = optarg;
break;
case 'O':
overlay = true;
break;
case 'h':
print_usage();
return 0;
}
- }
- /* check necessary parameters */
- if (!pkey_file || !dtb_file) {
print_usage();
exit(EXIT_FAILURE);
- }
- ret = add_public_key(pkey_file, dtb_file, overlay);
- if (ret == -1) {
printf("Adding public key to the dtb failed\n");
exit(EXIT_FAILURE);
- }
- exit(EXIT_SUCCESS);
+}

On Thu, May 13, 2021 at 07:23:43AM +0200, Heinrich Schuchardt wrote:
On 5/12/21 6:57 AM, AKASHI Takahiro wrote:
This command allows us to add a certificate (or public key) to dtb blob: { signature { capsule-key = "..."; }; }
The value is actually a signature list in terms of UEFI specificaion, and used in verifying UEFI capsules.
The code was originally developed by Sughosh and derived from mkeficapsule.c.
I have no clue how you want to use this command. Please, provide a description of the workflow using this command.
Please read my patch carefully, in particular, Makefile. In this patch, fdtsig.sh, which is introduced in Patch#2, is replaced with fdtsig (C version).
I think that the workflow is trivial.
In your comments for patch 2 you wrote that the -K and -D option can be replaced by tools provided by the device-tree compiler package. Why do they reappear here?
? As I said, I don't intend to ask you to merge this patch.
In the cover letter, ! NOTE: ! I temporarily include Patch#3 in order to show that it is not worth ! implementing in C as we can do the same thing with a very small ! shell script.
Patch #1, #2 and #4 are just fine.
-Takahiro Akashi
Please, add a man-page (/doc/fdtsig.1).
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
Makefile | 2 +- tools/Makefile | 2 + tools/fdtsig.c | 274 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 277 insertions(+), 1 deletion(-) create mode 100644 tools/fdtsig.c
diff --git a/Makefile b/Makefile index 9806464357e0..8b40987234a0 100644 --- a/Makefile +++ b/Makefile @@ -1016,7 +1016,7 @@ quiet_cmd_lzma = LZMA $@ cmd_lzma = lzma -c -z -k -9 $< > $@
quiet_cmd_fdtsig = FDTSIG $@ -cmd_fdtsig = $(srctree)/tools/fdtsig.sh $(CONFIG_EFI_PKEY_FILE) $@ +cmd_fdtsig = $(objtree)/tools/fdtsig -K $(CONFIG_EFI_PKEY_FILE) -D $@
cfg: u-boot.cfg
diff --git a/tools/Makefile b/tools/Makefile index 71a52719620c..e6fd1dbade19 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -234,6 +234,8 @@ HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include ifneq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),) HOSTLDLIBS_mkeficapsule += \ $(shell pkg-config --libs libssl libcrypto 2> /dev/null || echo "-lssl -lcrypto")
- fdtsig-objs := fdtsig.o $(LIBFDT_OBJS)
- hostprogs-y += fdtsig endif hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule
diff --git a/tools/fdtsig.c b/tools/fdtsig.c new file mode 100644 index 000000000000..daa1e63c3b33 --- /dev/null +++ b/tools/fdtsig.c @@ -0,0 +1,274 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright 2021 Linaro Limited
- The code in this file was extracted from mkeficapsule.c
That information is better described by the git log.
Please, explain here what this tool is used for.
Best regards
Heinrich
- */
+#include <errno.h> +#include <fcntl.h> +#include <getopt.h> +#include <malloc.h> +#include <stdbool.h> +#include <stdio.h> +#include <unistd.h>
+#include <sys/mman.h> +#include <sys/stat.h>
+#include <linux/libfdt.h>
+#define SIGNATURE_NODENAME "signature" +#define OVERLAY_NODENAME "__overlay__"
+static const char *tool_name = "fdtsig";
+static const char *opts_short = "D:K:Oh";
+static struct option options[] = {
- {"dtb", required_argument, NULL, 'D'},
- {"public key", required_argument, NULL, 'K'},
- {"overlay", no_argument, NULL, 'O'},
- {"help", no_argument, NULL, 'h'},
- {NULL, 0, NULL, 0},
+};
+static void print_usage(void) +{
- printf("Usage: %s [options]\n"
"Options:\n"
"\t-K, --public-key <key file> public key esl file\n"
"\t-D, --dtb <dtb file> dtb file\n"
"\t-O, --overlay the dtb file is an overlay\n"
"\t-h, --help print a help message\n",
tool_name);
+}
+static int fdt_add_pub_key_data(void *sptr, void *dptr, size_t key_size,
bool overlay)
+{
- int parent;
- int ov_node;
- int frag_node;
- int ret = 0;
- if (overlay) {
/*
* The signature would be stored in the
* first fragment node of the overlay
*/
frag_node = fdt_first_subnode(dptr, 0);
if (frag_node == -FDT_ERR_NOTFOUND) {
fprintf(stderr,
"Couldn't find the fragment node: %s\n",
fdt_strerror(frag_node));
goto done;
}
ov_node = fdt_subnode_offset(dptr, frag_node, OVERLAY_NODENAME);
if (ov_node == -FDT_ERR_NOTFOUND) {
fprintf(stderr,
"Couldn't find the __overlay__ node: %s\n",
fdt_strerror(ov_node));
goto done;
}
- } else {
ov_node = 0;
- }
- parent = fdt_subnode_offset(dptr, ov_node, SIGNATURE_NODENAME);
- if (parent == -FDT_ERR_NOTFOUND) {
parent = fdt_add_subnode(dptr, ov_node, SIGNATURE_NODENAME);
if (parent < 0) {
ret = parent;
if (ret != -FDT_ERR_NOSPACE) {
fprintf(stderr,
"Couldn't create signature node: %s\n",
fdt_strerror(parent));
}
}
- }
- if (ret)
goto done;
- /* Write the key to the FDT node */
- ret = fdt_setprop(dptr, parent, "capsule-key",
sptr, key_size);
+done:
- if (ret)
ret = ret == -FDT_ERR_NOSPACE ? -ENOSPC : -EIO;
- return ret;
+}
+static int add_public_key(const char *pkey_file, const char *dtb_file,
bool overlay)
+{
- int ret;
- int srcfd = -1;
- int destfd = -1;
- void *sptr = NULL;
- void *dptr = NULL;
- off_t src_size;
- struct stat pub_key;
- struct stat dtb;
- /* Find out the size of the public key */
- srcfd = open(pkey_file, O_RDONLY);
- if (srcfd == -1) {
fprintf(stderr, "%s: Can't open %s: %s\n",
__func__, pkey_file, strerror(errno));
ret = -1;
goto err;
- }
- ret = fstat(srcfd, &pub_key);
- if (ret == -1) {
fprintf(stderr, "%s: Can't stat %s: %s\n",
__func__, pkey_file, strerror(errno));
ret = -1;
goto err;
- }
- src_size = pub_key.st_size;
- /* mmap the public key esl file */
- sptr = mmap(0, src_size, PROT_READ, MAP_SHARED, srcfd, 0);
- if (sptr == MAP_FAILED) {
fprintf(stderr, "%s: Failed to mmap %s:%s\n",
__func__, pkey_file, strerror(errno));
ret = -1;
goto err;
- }
- /* Open the dest FDT */
- destfd = open(dtb_file, O_RDWR);
- if (destfd == -1) {
fprintf(stderr, "%s: Can't open %s: %s\n",
__func__, dtb_file, strerror(errno));
ret = -1;
goto err;
- }
- ret = fstat(destfd, &dtb);
- if (ret == -1) {
fprintf(stderr, "%s: Can't stat %s: %s\n",
__func__, dtb_file, strerror(errno));
goto err;
- }
- dtb.st_size += src_size + 0x30;
- if (ftruncate(destfd, dtb.st_size)) {
fprintf(stderr, "%s: Can't expand %s: %s\n",
__func__, dtb_file, strerror(errno));
ret = -1;
goto err;
- }
- errno = 0;
- /* mmap the dtb file */
- dptr = mmap(0, dtb.st_size, PROT_READ | PROT_WRITE, MAP_SHARED,
destfd, 0);
- if (dptr == MAP_FAILED) {
fprintf(stderr, "%s: Failed to mmap %s:%s\n",
__func__, dtb_file, strerror(errno));
ret = -1;
goto err;
- }
- if (fdt_check_header(dptr)) {
fprintf(stderr, "%s: Invalid FDT header\n", __func__);
ret = -1;
goto err;
- }
- ret = fdt_open_into(dptr, dptr, dtb.st_size);
- if (ret) {
fprintf(stderr, "%s: Cannot expand FDT: %s\n",
__func__, fdt_strerror(ret));
ret = -1;
goto err;
- }
- /* Copy the esl file to the expanded FDT */
- ret = fdt_add_pub_key_data(sptr, dptr, src_size, overlay);
- if (ret < 0) {
fprintf(stderr, "%s: Unable to add public key to the FDT\n",
__func__);
ret = -1;
goto err;
- }
- ret = 0;
+err:
- if (sptr)
munmap(sptr, src_size);
- if (dptr)
munmap(dptr, dtb.st_size);
- if (srcfd != -1)
close(srcfd);
- if (destfd != -1)
close(destfd);
- return ret;
+}
+int main(int argc, char **argv) +{
- char *pkey_file;
- char *dtb_file;
- bool overlay;
- int c, idx, ret;
- pkey_file = NULL;
- dtb_file = NULL;
- overlay = false;
- for (;;) {
c = getopt_long(argc, argv, opts_short, options, &idx);
if (c == -1)
break;
switch (c) {
case 'K':
if (pkey_file) {
printf("Public Key already specified\n");
return -1;
}
pkey_file = optarg;
break;
case 'D':
if (dtb_file) {
printf("DTB file already specified\n");
return -1;
}
dtb_file = optarg;
break;
case 'O':
overlay = true;
break;
case 'h':
print_usage();
return 0;
}
- }
- /* check necessary parameters */
- if (!pkey_file || !dtb_file) {
print_usage();
exit(EXIT_FAILURE);
- }
- ret = add_public_key(pkey_file, dtb_file, overlay);
- if (ret == -1) {
printf("Adding public key to the dtb failed\n");
exit(EXIT_FAILURE);
- }
- exit(EXIT_SUCCESS);
+}

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

Am 12. Mai 2021 06:57:49 MESZ schrieb AKASHI Takahiro takahiro.akashi@linaro.org:
As I discussed in [1], I have made a couple of improvements on the current implemenation of capsule update.
Among others, this patch series
- add signing feature to mkeficapsule
- remove dtb operation from mkeficapsule
- add pytest for capsule authentication (on sandbox)
NOTE: I temporarily include Patch#3 in order to show that it is not worth implementing in C as we can do the same thing with a very small shell script.
My intent is *NOT* to merge Patch#3 in upstream.
Should you need to resend the series, please, put "DO NOT MERGE" into the title of patch 3.
Best regards
Heinrich
Prerequisite patches
See Sughosh's [2] and my [3].
Test
- passed the pytest which is included in this patch series
on sandbox built locally.
Todo
- review and update the document for capsule update doc/board/emulation/qemu_capsule_update.rst
(but not in this patch series)
[1] https://lists.denx.de/pipermail/u-boot/2021-April/447918.html [2] https://lists.denx.de/pipermail/u-boot/2021-April/447183.html [3] https://lists.denx.de/pipermail/u-boot/2021-May/449347.html https://lists.denx.de/pipermail/u-boot/2021-May/449348.html https://lists.denx.de/pipermail/u-boot/2021-May/449349.html https://lists.denx.de/pipermail/u-boot/2021-May/449350.html https://lists.denx.de/pipermail/u-boot/2021-May/449351.html
Changes
Initial release (May 12, 2021)
- based on v2021.07-rc2
AKASHI Takahiro (4): tools: mkeficapsule: add firmwware image signing tools: mkeficapsule: remove device-tree related operation tools: add fdtsig command test/py: efi_capsule: add image authentication test
Makefile | 7 +- .../py/tests/test_efi_capsule/capsule_defs.py | 5 + test/py/tests/test_efi_capsule/conftest.py | 35 +- test/py/tests/test_efi_capsule/signature.dts | 8 + .../test_capsule_firmware_signed.py | 234 +++++++++ tools/Makefile | 7 +- tools/fdtsig.c | 274 +++++++++++ tools/fdtsig.sh | 40 ++ tools/mkeficapsule.c | 455 ++++++++++-------- 9 files changed, 856 insertions(+), 209 deletions(-) create mode 100644 test/py/tests/test_efi_capsule/signature.dts create mode 100644 test/py/tests/test_efi_capsule/test_capsule_firmware_signed.py create mode 100644 tools/fdtsig.c create mode 100755 tools/fdtsig.sh
participants (4)
-
AKASHI Takahiro
-
Heinrich Schuchardt
-
Ilias Apalodimas
-
Masami Hiramatsu