[PATCH v4 00/18] image: add a stage pre-load

This serie adds a stage pre-load before launching an image. This stage is used to read a header before the image and this header contains the signature of the full image. So u-boot may check the full image before using any data of the image.
The support of this header is added to binman, and a command verify checks the signature of a blob and set the u-boot env variable "loadaddr_verified" to the beginning of the "real" image.
The support of this header is only added to binman, but it may also be added to mkimage.
Changelog: v4: - add a config SANDBOX_BIN - enhance help for asn1 and oid - change the format of the pre-load header - add the support of pre-load header in binman - add py test for pre-load header - add a command verify v3: - move image-pre-load.c to /boot - update mkimage to add public key in u-boot device tree - add script gen_pre_load_header.sh v2: - move the code to image-pre-load - add support of stage pre-load for spl - add support of stage pre-load on spl_ram
Philippe Reynes (18): arch: sandbox: add an config SANDBOX_BINMAN arch: sandbox: dts: sandbox.dts: add empty binman node configs: sandbox_defconfig: enable SANDBOX_BINMAN lib: Kconfig: enhance help for ASN1 lib: Kconfig: enhance the help of OID_REGISTRY lib: allow to build asn1 decoder and oid registry in SPL lib: crypto: allow to build crypyo in SPL lib: rsa: allow rsa verify with pkey in SPL boot: image: add a stage pre-load cmd: bootm: add a stage pre-load common: spl: fit_ram: allow to use image pre load mkimage: add public key for image pre-load stage Makefile: provide sah-key to binman tools: binman: add support for pre-load header configs: sandbox_defconfig: enable stage pre-load in bootm test: py: vboot: add test for global image signature cmd: verify: initial import configs: sandbox_defconfig: enable config CMD_VERIFY
Makefile | 1 + arch/sandbox/Kconfig | 7 + arch/sandbox/dts/sandbox.dts | 3 + boot/Kconfig | 55 +++ boot/Makefile | 1 + boot/bootm.c | 33 ++ boot/image-pre-load.c | 408 ++++++++++++++++++ cmd/Kconfig | 17 + cmd/Makefile | 1 + cmd/bootm.c | 2 +- cmd/verify.c | 53 +++ common/spl/spl_ram.c | 21 +- configs/sandbox_defconfig | 6 + include/image.h | 30 ++ lib/Kconfig | 37 +- lib/Makefile | 10 +- lib/crypto/Kconfig | 29 ++ lib/crypto/Makefile | 19 +- lib/rsa/Kconfig | 19 + test/py/tests/test_vboot.py | 119 ++++- test/py/tests/vboot/sandbox-binman-pss.dts | 25 ++ test/py/tests/vboot/sandbox-binman.dts | 24 ++ .../tests/vboot/sandbox-u-boot-global-pss.dts | 25 ++ test/py/tests/vboot/sandbox-u-boot-global.dts | 24 ++ test/py/tests/vboot/simple-images.its | 36 ++ tools/binman/etype/pre_load.py | 156 +++++++ tools/fit_image.c | 3 + tools/image-host.c | 114 +++++ 28 files changed, 1253 insertions(+), 25 deletions(-) create mode 100644 boot/image-pre-load.c create mode 100644 cmd/verify.c create mode 100644 test/py/tests/vboot/sandbox-binman-pss.dts create mode 100644 test/py/tests/vboot/sandbox-binman.dts create mode 100644 test/py/tests/vboot/sandbox-u-boot-global-pss.dts create mode 100644 test/py/tests/vboot/sandbox-u-boot-global.dts create mode 100644 test/py/tests/vboot/simple-images.its create mode 100644 tools/binman/etype/pre_load.py

Add an config SANDBOX_BINMAN to enable binman on sandbox. It is useful for test.
Signed-off-by: Philippe Reynes philippe.reynes@softathome.com --- arch/sandbox/Kconfig | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/arch/sandbox/Kconfig b/arch/sandbox/Kconfig index 477c51960d..6d1f36a0e1 100644 --- a/arch/sandbox/Kconfig +++ b/arch/sandbox/Kconfig @@ -29,6 +29,13 @@ config SANDBOX_SPL bool "Enable SPL for sandbox" select SUPPORT_SPL
+config SANDBOX_BINMAN + bool "Enable binman" + select BINMAN + help + Enable the support of binman on sandbox. + It may be useful for test. + config SYS_CONFIG_NAME default "sandbox_spl" if SANDBOX_SPL default "sandbox" if !SANDBOX_SPL

Hi Philippe,
On Mon, 31 Jan 2022 at 07:56, Philippe Reynes philippe.reynes@softathome.com wrote:
Add an config SANDBOX_BINMAN to enable binman on sandbox. It is useful for test.
Signed-off-by: Philippe Reynes philippe.reynes@softathome.com
arch/sandbox/Kconfig | 7 +++++++ 1 file changed, 7 insertions(+)
Instead, could you 'imply' it in the sandbox Kconfig? e.g.
imply BINMAN if TARGET_SANDBOX
diff --git a/arch/sandbox/Kconfig b/arch/sandbox/Kconfig index 477c51960d..6d1f36a0e1 100644 --- a/arch/sandbox/Kconfig +++ b/arch/sandbox/Kconfig @@ -29,6 +29,13 @@ config SANDBOX_SPL bool "Enable SPL for sandbox" select SUPPORT_SPL
+config SANDBOX_BINMAN
bool "Enable binman"
select BINMAN
help
Enable the support of binman on sandbox.
It may be useful for test.
config SYS_CONFIG_NAME default "sandbox_spl" if SANDBOX_SPL default "sandbox" if !SANDBOX_SPL -- 2.17.1
Regards, Simon

Add an empty node /binman on sandbox to avoid an error on u-boot when binman is enabled.
Signed-off-by: Philippe Reynes philippe.reynes@softathome.com --- arch/sandbox/dts/sandbox.dts | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/arch/sandbox/dts/sandbox.dts b/arch/sandbox/dts/sandbox.dts index 127f168f02..e6f50f5e9b 100644 --- a/arch/sandbox/dts/sandbox.dts +++ b/arch/sandbox/dts/sandbox.dts @@ -21,6 +21,9 @@ spi0 = &spi; };
+ binman { + }; + memory { reg = <0 CONFIG_SYS_SDRAM_SIZE>; };

On Mon, 31 Jan 2022 at 07:56, Philippe Reynes philippe.reynes@softathome.com wrote:
Add an empty node /binman on sandbox to avoid an error on u-boot when binman is enabled.
Signed-off-by: Philippe Reynes philippe.reynes@softathome.com
arch/sandbox/dts/sandbox.dts | 3 +++ 1 file changed, 3 insertions(+)
Reviewed-by: Simon Glass sjg@chromium.org

Enable binman on sandbox. It will be used on test unit.
Signed-off-by: Philippe Reynes philippe.reynes@softathome.com --- configs/sandbox_defconfig | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 19cde87397..749b0f0155 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -7,6 +7,7 @@ CONFIG_ENV_SIZE=0x2000 CONFIG_DEFAULT_DEVICE_TREE="sandbox" CONFIG_PRE_CON_BUF_ADDR=0xf0000 CONFIG_BOOTSTAGE_STASH_ADDR=0x0 +CONFIG_SANDBOX_BINMAN=y CONFIG_DEBUG_UART=y CONFIG_DISTRO_DEFAULTS=y CONFIG_SYS_LOAD_ADDR=0x0 @@ -297,6 +298,7 @@ CONFIG_WDT_GPIO=y CONFIG_WDT_SANDBOX=y CONFIG_FS_CBFS=y CONFIG_FS_CRAMFS=y +# CONFIG_BINMAN_FDT is not set CONFIG_CMD_DHRYSTONE=y CONFIG_ECDSA=y CONFIG_ECDSA_VERIFY=y

Enhance the help for configs ASN1_COMPILER and ASN1_decoder.
Signed-off-by: Philippe Reynes philippe.reynes@softathome.com --- lib/Kconfig | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/lib/Kconfig b/lib/Kconfig index 3c6fa99b1a..b0e5d60b3d 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -791,11 +791,23 @@ endmenu
config ASN1_COMPILER bool + help + ASN.1 (Abstract Syntax Notation One) is a standard interface + description language for defining data structures that can be + serialized and deserialized in a cross-platform way. It is + broadly used in telecommunications and computer networking, + and especially in cryptography (https://en.wikipedia.org/wiki/ASN.1). + This option enables the support of the asn1 compiler.
config ASN1_DECODER bool help - Enable asn1 decoder library. + ASN.1 (Abstract Syntax Notation One) is a standard interface + description language for defining data structures that can be + serialized and deserialized in a cross-platform way. It is + broadly used in telecommunications and computer networking, + and especially in cryptography (https://en.wikipedia.org/wiki/ASN.1). + This option enables the support of the asn1 decoder.
config OID_REGISTRY bool

Enhance the help for the config OID_REGISTRY.
Signed-off-by: Philippe Reynes philippe.reynes@softathome.com --- lib/Kconfig | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/lib/Kconfig b/lib/Kconfig index b0e5d60b3d..e749826f22 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -812,6 +812,10 @@ config ASN1_DECODER config OID_REGISTRY bool help + In computing, object identifiers or OIDs are an identifier mechanism + standardized by the International Telecommunication Union (ITU) and + ISO/IEC for naming any object, concept, or "thing" with a globally + unambiguous persistent name (https://en.wikipedia.org/wiki/Object_identifier). Enable fast lookup object identifier registry.
config SMBIOS_PARSER

This commit adds the options: - SPL_ASN1_DECODER - SPL_OID_REGISTRY
Signed-off-by: Philippe Reynes philippe.reynes@softathome.com --- lib/Kconfig | 19 +++++++++++++++++++ lib/Makefile | 7 +++++-- 2 files changed, 24 insertions(+), 2 deletions(-)
diff --git a/lib/Kconfig b/lib/Kconfig index e749826f22..effe735365 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -809,6 +809,16 @@ config ASN1_DECODER and especially in cryptography (https://en.wikipedia.org/wiki/ASN.1). This option enables the support of the asn1 decoder.
+config SPL_ASN1_DECODER + bool + help + ASN.1 (Abstract Syntax Notation One) is a standard interface + description language for defining data structures that can be + serialized and deserialized in a cross-platform way. It is + broadly used in telecommunications and computer networking, + and especially in cryptography (https://en.wikipedia.org/wiki/ASN.1). + This option enables the support of the asn1 decoder in the SPL. + config OID_REGISTRY bool help @@ -818,6 +828,15 @@ config OID_REGISTRY unambiguous persistent name (https://en.wikipedia.org/wiki/Object_identifier). Enable fast lookup object identifier registry.
+config SPL_OID_REGISTRY + bool + help + In computing, object identifiers or OIDs are an identifier mechanism + standardized by the International Telecommunication Union (ITU) and + ISO/IEC for naming any object, concept, or "thing" with a globally + unambiguous persistent name (https://en.wikipedia.org/wiki/Object_identifier). + Enable fast lookup object identifier registry in the SPL. + config SMBIOS_PARSER bool "SMBIOS parser" help diff --git a/lib/Makefile b/lib/Makefile index 11b03d1cbe..fc284d68f8 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -17,7 +17,6 @@ obj-$(CONFIG_OF_LIVE) += of_live.o obj-$(CONFIG_CMD_DHRYSTONE) += dhry/ obj-$(CONFIG_ARCH_AT91) += at91/ obj-$(CONFIG_OPTEE_LIB) += optee/ -obj-$(CONFIG_ASN1_DECODER) += asn1_decoder.o obj-y += crypto/
obj-$(CONFIG_AES) += aes.o @@ -74,6 +73,7 @@ obj-$(CONFIG_SHA1) += sha1.o obj-$(CONFIG_SHA256) += sha256.o obj-$(CONFIG_SHA512) += sha512.o obj-$(CONFIG_CRYPT_PW) += crypt/ +obj-$(CONFIG_$(SPL_)ASN1_DECODER) += asn1_decoder.o
obj-$(CONFIG_$(SPL_)ZLIB) += zlib/ obj-$(CONFIG_$(SPL_)ZSTD) += zstd/ @@ -135,9 +135,9 @@ obj-$(CONFIG_$(SPL_TPL_)STRTO) += strto.o else # Main U-Boot always uses the full printf support obj-y += vsprintf.o strto.o -obj-$(CONFIG_OID_REGISTRY) += oid_registry.o obj-$(CONFIG_SSCANF) += sscanf.o endif +obj-$(CONFIG_$(SPL_)OID_REGISTRY) += oid_registry.o
obj-y += abuf.o obj-y += date.o @@ -148,6 +148,9 @@ obj-$(CONFIG_LIB_ELF) += elf.o # Build a fast OID lookup registry from include/linux/oid_registry.h # $(obj)/oid_registry.o: $(obj)/oid_registry_data.c +ifdef CONFIG_SPL_BUILD +CFLAGS_oid_registry.o += -I$(obj) +endif
$(obj)/oid_registry_data.c: $(srctree)/include/linux/oid_registry.h \ $(srctree)/scripts/build_OID_registry

This commit adds the options: - SPL_ASYMMETRIC_KEY_TYPE - SPL_ASYMMETRIC_PUBLIC_KEY_SUBTYPE - SPL_RSA_PUBLIC_KEY_PARSER
Signed-off-by: Philippe Reynes philippe.reynes@softathome.com --- lib/Makefile | 3 ++- lib/crypto/Kconfig | 29 +++++++++++++++++++++++++++++ lib/crypto/Makefile | 19 +++++++++++++------ 3 files changed, 44 insertions(+), 7 deletions(-)
diff --git a/lib/Makefile b/lib/Makefile index fc284d68f8..7aaee3488b 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -17,7 +17,6 @@ obj-$(CONFIG_OF_LIVE) += of_live.o obj-$(CONFIG_CMD_DHRYSTONE) += dhry/ obj-$(CONFIG_ARCH_AT91) += at91/ obj-$(CONFIG_OPTEE_LIB) += optee/ -obj-y += crypto/
obj-$(CONFIG_AES) += aes.o obj-$(CONFIG_AES) += aes/ @@ -63,6 +62,8 @@ obj-$(CONFIG_TPM_V1) += tpm-v1.o obj-$(CONFIG_TPM_V2) += tpm-v2.o endif
+obj-y += crypto/ + obj-$(CONFIG_$(SPL_TPL_)GENERATE_ACPI_TABLE) += acpi/ obj-$(CONFIG_$(SPL_)MD5) += md5.o obj-$(CONFIG_ECDSA) += ecdsa/ diff --git a/lib/crypto/Kconfig b/lib/crypto/Kconfig index 6369bafac0..509bc28311 100644 --- a/lib/crypto/Kconfig +++ b/lib/crypto/Kconfig @@ -8,6 +8,15 @@ menuconfig ASYMMETRIC_KEY_TYPE
if ASYMMETRIC_KEY_TYPE
+config SPL_ASYMMETRIC_KEY_TYPE + bool "Asymmetric (public-key cryptographic) key Support within SPL" + depends on SPL + help + This option provides support for a key type that holds the data for + the asymmetric keys used for public key cryptographic operations such + as encryption, decryption, signature generation and signature + verification in the SPL. + config ASYMMETRIC_PUBLIC_KEY_SUBTYPE bool "Asymmetric public-key crypto algorithm subtype" help @@ -16,6 +25,15 @@ config ASYMMETRIC_PUBLIC_KEY_SUBTYPE appropriate hash algorithms (such as SHA-1) must be available. ENOPKG will be reported if the requisite algorithm is unavailable.
+config SPL_ASYMMETRIC_PUBLIC_KEY_SUBTYPE + bool "Asymmetric public-key crypto algorithm subtype within SPL" + depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE + help + This option provides support for asymmetric public key type handling in the SPL. + If signature generation and/or verification are to be used, + appropriate hash algorithms (such as SHA-1) must be available. + ENOPKG will be reported if the requisite algorithm is unavailable. + config RSA_PUBLIC_KEY_PARSER bool "RSA public key parser" depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE @@ -27,6 +45,17 @@ config RSA_PUBLIC_KEY_PARSER public key data and provides the ability to instantiate a public key.
+config SPL_RSA_PUBLIC_KEY_PARSER + bool "RSA public key parser within SPL" + depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE + select SPL_ASN1_DECODER + select ASN1_COMPILER + select SPL_OID_REGISTRY + help + This option provides support for parsing a blob containing RSA + public key data and provides the ability to instantiate a public + key in the SPL. + config X509_CERTIFICATE_PARSER bool "X.509 certificate parser" depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE diff --git a/lib/crypto/Makefile b/lib/crypto/Makefile index f3a414525d..6792b1d4f0 100644 --- a/lib/crypto/Makefile +++ b/lib/crypto/Makefile @@ -3,27 +3,34 @@ # Makefile for asymmetric cryptographic keys #
-obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += asymmetric_keys.o +obj-$(CONFIG_$(SPL_)ASYMMETRIC_KEY_TYPE) += asymmetric_keys.o
asymmetric_keys-y := asymmetric_type.o
-obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o +obj-$(CONFIG_$(SPL_)ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o
# # RSA public key parser # -obj-$(CONFIG_RSA_PUBLIC_KEY_PARSER) += rsa_public_key.o +obj-$(CONFIG_$(SPL_)RSA_PUBLIC_KEY_PARSER) += rsa_public_key.o rsa_public_key-y := \ rsapubkey.asn1.o \ rsa_helper.o
$(obj)/rsapubkey.asn1.o: $(obj)/rsapubkey.asn1.c $(obj)/rsapubkey.asn1.h +ifdef CONFIG_SPL_BUILD +CFLAGS_rsapubkey.asn1.o += -I$(obj) +endif + $(obj)/rsa_helper.o: $(obj)/rsapubkey.asn1.h +ifdef CONFIG_SPL_BUILD +CFLAGS_rsa_helper.o += -I$(obj) +endif
# # X.509 Certificate handling # -obj-$(CONFIG_X509_CERTIFICATE_PARSER) += x509_key_parser.o +obj-$(CONFIG_$(SPL_)X509_CERTIFICATE_PARSER) += x509_key_parser.o x509_key_parser-y := \ x509.asn1.o \ x509_akid.asn1.o \ @@ -40,11 +47,11 @@ $(obj)/x509_akid.asn1.o: $(obj)/x509_akid.asn1.c $(obj)/x509_akid.asn1.h # # PKCS#7 message handling # -obj-$(CONFIG_PKCS7_MESSAGE_PARSER) += pkcs7_message.o +obj-$(CONFIG_$(SPL_)PKCS7_MESSAGE_PARSER) += pkcs7_message.o pkcs7_message-y := \ pkcs7.asn1.o \ pkcs7_parser.o -obj-$(CONFIG_PKCS7_VERIFY) += pkcs7_verify.o +obj-$(CONFIG_$(SPL_)PKCS7_VERIFY) += pkcs7_verify.o
$(obj)/pkcs7_parser.o: $(obj)/pkcs7.asn1.h $(obj)/pkcs7.asn1.o: $(obj)/pkcs7.asn1.c $(obj)/pkcs7.asn1.h

This commit adds the option SPL_RSA_VERIFY_WITH_PKEY.
Signed-off-by: Philippe Reynes philippe.reynes@softathome.com --- lib/rsa/Kconfig | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+)
diff --git a/lib/rsa/Kconfig b/lib/rsa/Kconfig index be9775bcce..b773f17c26 100644 --- a/lib/rsa/Kconfig +++ b/lib/rsa/Kconfig @@ -47,6 +47,25 @@ config RSA_VERIFY_WITH_PKEY directly specified in image_sign_info, where all the necessary key properties will be calculated on the fly in verification code.
+config SPL_RSA_VERIFY_WITH_PKEY + bool "Execute RSA verification without key parameters from FDT within SPL" + depends on SPL + select SPL_RSA_VERIFY + select SPL_ASYMMETRIC_KEY_TYPE + select SPL_ASYMMETRIC_PUBLIC_KEY_SUBTYPE + select SPL_RSA_PUBLIC_KEY_PARSER + help + The standard RSA-signature verification code (FIT_SIGNATURE) uses + pre-calculated key properties, that are stored in fdt blob, in + decrypting a signature. + This does not suit the use case where there is no way defined to + provide such additional key properties in standardized form, + particularly UEFI secure boot. + This options enables RSA signature verification with a public key + directly specified in image_sign_info, where all the necessary + key properties will be calculated on the fly in verification code + in the SPL. + config RSA_SOFTWARE_EXP bool "Enable driver for RSA Modular Exponentiation in software" depends on DM

Add a stage pre-load that could check or modify an image.
For the moment, only a header with a signature is supported. This header has the following format: - magic : 4 bytes - version : 4 bytes - header size : 4 bytes - image size : 4 bytes - offset image signature : 4 bytes - flags : 4 bytes - reserved0 : 4 bytes - reserved1 : 4 bytes - sha256 of the image signature : 32 bytes - signature of the first 64 bytes : n bytes - image signature : n bytes - padding : up to header size
The stage uses a node /image/pre-load/sig to get some informations: - algo-name (mandatory) : name of the algo used to sign - padding-name : name of padding used to sign - signature-size : size of the signature (in the header) - mandatory : set to yes if this sig is mandatory - public-key (madatory) : value of the public key
Before running the image, the stage pre-load checks the signature provided in the header.
This is an initial support, later we could add the support of: - ciphering - uncompressing - ...
Signed-off-by: Philippe Reynes philippe.reynes@softathome.com --- boot/Kconfig | 55 ++++++ boot/Makefile | 1 + boot/image-pre-load.c | 408 ++++++++++++++++++++++++++++++++++++++++++ include/image.h | 14 ++ 4 files changed, 478 insertions(+) create mode 100644 boot/image-pre-load.c
diff --git a/boot/Kconfig b/boot/Kconfig index c8d5906cd3..47555302cd 100644 --- a/boot/Kconfig +++ b/boot/Kconfig @@ -992,6 +992,61 @@ config AUTOBOOT_MENU_SHOW
endmenu
+menu "Image support" + +config IMAGE_PRE_LOAD + bool "Image pre-load support" + help + Enable an image pre-load stage in the SPL. + This pre-load stage allows to do some manipulation + or check (for example signature check) on an image + before launching it. + +config SPL_IMAGE_PRE_LOAD + bool "Image pre-load support within SPL" + depends on SPL && IMAGE_PRE_LOAD + help + Enable an image pre-load stage in the SPL. + This pre-load stage allows to do some manipulation + or check (for example signature check) on an image + before launching it. + +config IMAGE_PRE_LOAD_SIG + bool "Image pre-load signature support" + depends on IMAGE_PRE_LOAD + select FIT_SIGNATURE + select RSA + select RSA_VERIFY_WITH_PKEY + help + Enable signature check support in the pre-load stage. + For this feature a very simple header is added before + the image with few fields: + - a magic + - the image size + - the signature + All other information (header size, type of signature, + ...) are provided in the node /image/pre-load/sig of + u-boot. + +config SPL_IMAGE_PRE_LOAD_SIG + bool "Image pre-load signature support witin SPL" + depends on SPL_IMAGE_PRE_LOAD && IMAGE_PRE_LOAD_SIG + select SPL_FIT_SIGNATURE + select SPL_RSA + select SPL_RSA_VERIFY_WITH_PKEY + help + Enable signature check support in the pre-load stage in the SPL. + For this feature a very simple header is added before + the image with few fields: + - a magic + - the image size + - the signature + All other information (header size, type of signature, + ...) are provided in the node /image/pre-load/sig of + u-boot. + +endmenu + config USE_BOOTARGS bool "Enable boot arguments" help diff --git a/boot/Makefile b/boot/Makefile index 2938c3f145..59752c65ca 100644 --- a/boot/Makefile +++ b/boot/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_$(SPL_TPL_)OF_LIBFDT) += image-fdt.o obj-$(CONFIG_$(SPL_TPL_)FIT_SIGNATURE) += fdt_region.o obj-$(CONFIG_$(SPL_TPL_)FIT) += image-fit.o obj-$(CONFIG_$(SPL_)MULTI_DTB_FIT) += boot_fit.o common_fit.o +obj-$(CONFIG_$(SPL_TPL_)IMAGE_PRE_LOAD) += image-pre-load.o obj-$(CONFIG_$(SPL_TPL_)IMAGE_SIGN_INFO) += image-sig.o obj-$(CONFIG_$(SPL_TPL_)FIT_SIGNATURE) += image-fit-sig.o obj-$(CONFIG_$(SPL_TPL_)FIT_CIPHER) += image-cipher.o diff --git a/boot/image-pre-load.c b/boot/image-pre-load.c new file mode 100644 index 0000000000..3b18c5086a --- /dev/null +++ b/boot/image-pre-load.c @@ -0,0 +1,408 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2021 Philippe Reynes philippe.reynes@softathome.com + */ + +#include <common.h> +#include <asm/global_data.h> +DECLARE_GLOBAL_DATA_PTR; +#include <image.h> +#include <mapmem.h> + +#include <u-boot/sha256.h> + +#define IMAGE_PRE_LOAD_SIG_MAGIC 0x55425348 +#define IMAGE_PRE_LOAD_SIG_OFFSET_MAGIC 0 +#define IMAGE_PRE_LOAD_SIG_OFFSET_IMG_LEN 4 +#define IMAGE_PRE_LOAD_SIG_OFFSET_SIG 8 + +#define IMAGE_PRE_LOAD_PATH "/image/pre-load/sig" +#define IMAGE_PRE_LOAD_PROP_ALGO_NAME "algo-name" +#define IMAGE_PRE_LOAD_PROP_PADDING_NAME "padding-name" +#define IMAGE_PRE_LOAD_PROP_SIG_SIZE "signature-size" +#define IMAGE_PRE_LOAD_PROP_PUBLIC_KEY "public-key" +#define IMAGE_PRE_LOAD_PROP_MANDATORY "mandatory" + +#ifndef CONFIG_SYS_BOOTM_LEN +/* use 8MByte as default max gunzip size */ +#define CONFIG_SYS_BOOTM_LEN 0x800000 +#endif + +/* + * Information in the device-tree about the signature in the header + */ +struct image_sig_info { + char *algo_name; /* Name of the algo (eg: sha256,rsa2048) */ + char *padding_name; /* Name of the padding */ + u8 *key; /* Public signature key */ + int key_len; /* Length of the public key */ + u32 sig_size; /* size of the signature (in the header) */ + int mandatory; /* Set if the signature is mandatory */ + + struct image_sign_info sig_info; /* Signature info */ +}; + +/* + * Header of the signature header + */ +struct sig_header_s { + u32 magic; + u32 version; + u32 header_size; + u32 image_size; + u32 offset_img_sig; + u32 flags; + u32 reserved0; + u32 reserved1; + u8 sha256_img_sig[SHA256_SUM_LEN]; +}; + +#define SIG_HEADER_LEN (sizeof(struct sig_header_s)) + +/* + * Offset of the image + * + * This value is used to skip the header before really launching the image + */ +ulong image_load_offset; + +/* + * This function gathers information about the signature check + * that could be done before launching the image. + * + * return: + * < 0 => an error has occurred + * 0 => OK + * 1 => no setup + */ +static int image_pre_load_sig_setup(struct image_sig_info *info) +{ + const void *algo_name, *padding_name, *key, *mandatory; + const u32 *sig_size; + int key_len; + int node, ret = 0; + + if (!info) { + log_err("ERROR: info is NULL for image pre-load sig check\n"); + ret = -EINVAL; + goto out; + } + + memset(info, 0, sizeof(*info)); + + node = fdt_path_offset(gd_fdt_blob(), IMAGE_PRE_LOAD_PATH); + if (node < 0) { + log_info("INFO: no info for image pre-load sig check\n"); + ret = 1; + goto out; + } + + algo_name = fdt_getprop(gd_fdt_blob(), node, + IMAGE_PRE_LOAD_PROP_ALGO_NAME, NULL); + if (!algo_name) { + printf("ERROR: no algo_name for image pre-load sig check\n"); + ret = -EINVAL; + goto out; + } + + padding_name = fdt_getprop(gd_fdt_blob(), node, + IMAGE_PRE_LOAD_PROP_PADDING_NAME, NULL); + if (!padding_name) { + log_info("INFO: no padding_name provided, so using pkcs-1.5\n"); + padding_name = "pkcs-1.5"; + } + + sig_size = fdt_getprop(gd_fdt_blob(), node, + IMAGE_PRE_LOAD_PROP_SIG_SIZE, NULL); + if (!sig_size) { + log_err("ERROR: no signature-size for image pre-load sig check\n"); + ret = -EINVAL; + goto out; + } + + key = fdt_getprop(gd_fdt_blob(), node, + IMAGE_PRE_LOAD_PROP_PUBLIC_KEY, &key_len); + if (!key) { + log_err("ERROR: no key for image pre-load sig check\n"); + ret = -EINVAL; + goto out; + } + + info->algo_name = (char *)algo_name; + info->padding_name = (char *)padding_name; + info->key = (uint8_t *)key; + info->key_len = key_len; + info->sig_size = fdt32_to_cpu(*sig_size); + + mandatory = fdt_getprop(gd_fdt_blob(), node, + IMAGE_PRE_LOAD_PROP_MANDATORY, NULL); + if (mandatory && !strcmp((char *)mandatory, "yes")) + info->mandatory = 1; + + /* Compute signature information */ + info->sig_info.name = info->algo_name; + info->sig_info.padding = image_get_padding_algo(info->padding_name); + info->sig_info.checksum = image_get_checksum_algo(info->sig_info.name); + info->sig_info.crypto = image_get_crypto_algo(info->sig_info.name); + info->sig_info.key = info->key; + info->sig_info.keylen = info->key_len; + + out: + return ret; +} + +static int image_pre_load_sig_get_magic(ulong addr, u32 *magic) +{ + struct sig_header_s *sig_header; + int ret = 0; + + sig_header = (struct sig_header_s *)map_sysmem(addr, SIG_HEADER_LEN); + if (!sig_header) { + log_err("ERROR: can't map first header\n"); + ret = -EFAULT; + goto out; + } + + *magic = fdt32_to_cpu(sig_header->magic); + + unmap_sysmem(sig_header); + + out: + return ret; +} + +static int image_pre_load_sig_get_header_size(ulong addr, u32 *header_size) +{ + struct sig_header_s *sig_header; + int ret = 0; + + sig_header = (struct sig_header_s *)map_sysmem(addr, SIG_HEADER_LEN); + if (!sig_header) { + log_err("ERROR: can't map first header\n"); + ret = -EFAULT; + goto out; + } + + *header_size = sig_header->header_size; + + unmap_sysmem(sig_header); + + out: + return ret; +} + +/* + * return: + * < 0 => no magic and magic mandatory (or error when reading magic) + * 0 => magic found + * 1 => magic NOT found + */ +static int image_pre_load_sig_check_magic(struct image_sig_info *info, ulong addr) +{ + u32 magic; + int ret = 1; + + ret = image_pre_load_sig_get_magic(addr, &magic); + if (ret < 0) + goto out; + + if (magic != IMAGE_PRE_LOAD_SIG_MAGIC) { + if (info->mandatory) { + log_err("ERROR: signature is mandatory\n"); + ret = -EINVAL; + goto out; + } + ret = 1; + goto out; + } + + ret = 0; /* magic found */ + + out: + return ret; +} + +static int image_pre_load_sig_check_header_sig(struct image_sig_info *info, ulong addr) +{ + void *header; + struct image_region reg; + u32 sig_len; + u8 *sig; + int ret = 0; + + /* Only map header of the header and its signature */ + header = (void *)map_sysmem(addr, SIG_HEADER_LEN + info->sig_size); + if (!header) { + log_err("ERROR: can't map header\n"); + ret = -EFAULT; + goto out; + } + + reg.data = header; + reg.size = SIG_HEADER_LEN; + + sig = (uint8_t *)header + SIG_HEADER_LEN; + sig_len = info->sig_size; + + ret = info->sig_info.crypto->verify(&info->sig_info, ®, 1, sig, sig_len); + if (ret) { + log_err("ERROR: header signature check has failed (err=%d)\n", ret); + ret = -EINVAL; + goto out_unmap; + } + + out_unmap: + unmap_sysmem(header); + + out: + return ret; +} + +static int image_pre_load_sig_check_img_sig_sha256(struct image_sig_info *info, ulong addr) +{ + struct sig_header_s *sig_header; + void *header; + u8 sha256_img_sig[SHA256_SUM_LEN]; + int ret = 0; + + sig_header = (struct sig_header_s *)map_sysmem(addr, SIG_HEADER_LEN); + if (!sig_header) { + log_err("ERROR: can't map first header\n"); + ret = -EFAULT; + goto out; + } + + header = (void *)map_sysmem(addr, sig_header->header_size); + if (!header) { + log_err("ERROR: can't map header\n"); + ret = -EFAULT; + goto out_sig_header; + } + + sha256_csum_wd(header + sig_header->offset_img_sig, info->sig_size, + sha256_img_sig, CHUNKSZ_SHA256); + + ret = memcmp(sig_header->sha256_img_sig, sha256_img_sig, SHA256_SUM_LEN); + if (ret) { + log_err("ERROR: sha256 of image signature is invalid\n"); + ret = -EFAULT; + goto out_header; + } + + out_header: + unmap_sysmem(header); + out_sig_header: + unmap_sysmem(sig_header); + out: + return ret; +} + +static int image_pre_load_sig_check_img_sig(struct image_sig_info *info, ulong addr) +{ + struct sig_header_s *sig_header; + void *image; + struct image_region reg; + u32 sig_len; + u8 *sig; + int ret = 0; + + sig_header = (struct sig_header_s *)map_sysmem(addr, SIG_HEADER_LEN); + if (!sig_header) { + log_err("ERROR: can't map first header\n"); + ret = -EFAULT; + goto out; + } + + image = (void *)map_sysmem(addr, sig_header->header_size + sig_header->image_size); + if (!image) { + log_err("ERROR: can't map full image\n"); + ret = -EFAULT; + goto out_unmap_sig_header; + } + + reg.data = image + sig_header->header_size; + reg.size = sig_header->image_size; + + sig = (uint8_t *)image + sig_header->offset_img_sig; + sig_len = info->sig_size; + + ret = info->sig_info.crypto->verify(&info->sig_info, ®, 1, sig, sig_len); + if (ret) { + log_err("ERROR: signature check has failed (err=%d)\n", ret); + ret = -EINVAL; + goto out_unmap_image; + } + + log_info("INFO: signature check has succeed\n"); + + out_unmap_image: + unmap_sysmem(image); + + out_unmap_sig_header: + unmap_sysmem(sig_header); + + out: + return ret; +} + +int image_pre_load_sig(ulong addr) +{ + struct image_sig_info info; + int ret; + + ret = image_pre_load_sig_setup(&info); + if (ret < 0) + goto out; + if (ret > 0) { + ret = 0; + goto out; + } + + ret = image_pre_load_sig_check_magic(&info, addr); + if (ret < 0) + goto out; + if (ret > 0) { + ret = 0; + goto out; + } + + /* Check the signature of the signature header */ + ret = image_pre_load_sig_check_header_sig(&info, addr); + if (ret < 0) + goto out; + + /* Check sha256 of the image signature */ + ret = image_pre_load_sig_check_img_sig_sha256(&info, addr); + if (ret < 0) + goto out; + + /* Check the image signature */ + ret = image_pre_load_sig_check_img_sig(&info, addr); + if (!ret) { + u32 header_size; + + ret = image_pre_load_sig_get_header_size(addr, &header_size); + if (ret) { + log_err("%s: can't get header size\n", __func__); + ret = -EINVAL; + goto out; + } + + image_load_offset += header_size; + } + + out: + return ret; +} + +int image_pre_load(ulong addr) +{ + int ret = 0; + + image_load_offset = 0; + + if (CONFIG_IS_ENABLED(IMAGE_PRE_LOAD_SIG)) + ret = image_pre_load_sig(addr); + + return ret; +} diff --git a/include/image.h b/include/image.h index 97e5f2eb24..fbcf70f5e4 100644 --- a/include/image.h +++ b/include/image.h @@ -48,6 +48,7 @@ struct fdt_region; extern ulong image_load_addr; /* Default Load Address */ extern ulong image_save_addr; /* Default Save Address */ extern ulong image_save_size; /* Default Save Size */ +extern ulong image_load_offset; /* Default Load Address Offset */
/* An invalid size, meaning that the image size is not known */ #define IMAGE_SIZE_INVAL (-1UL) @@ -1323,6 +1324,19 @@ struct crypto_algo *image_get_crypto_algo(const char *full_name); */ struct padding_algo *image_get_padding_algo(const char *name);
+/** + * image_pre_load() - Manage pre load header + * + * Manage the pre-load header before launching the image. + * It checks the signature of the image. It also set the + * variable image_load_offset to skip this header before + * launching the image. + * + * @param addr Address of the image + * @return: 0 on success, -ve on error + */ +int image_pre_load(ulong addr); + /** * fit_image_verify_required_sigs() - Verify signatures marked as 'required' *

Add a stage pre-load to the command bootm. Right now, this stage may be used to read a header and check the signature of the full image.
Reviewed-by: Simon Glass sjg@chromium.org Signed-off-by: Philippe Reynes philippe.reynes@softathome.com --- boot/bootm.c | 33 +++++++++++++++++++++++++++++++++ cmd/Kconfig | 10 ++++++++++ cmd/bootm.c | 2 +- include/image.h | 1 + 4 files changed, 45 insertions(+), 1 deletion(-)
diff --git a/boot/bootm.c b/boot/bootm.c index 00c00aef84..714406ab66 100644 --- a/boot/bootm.c +++ b/boot/bootm.c @@ -87,6 +87,33 @@ static int bootm_start(struct cmd_tbl *cmdtp, int flag, int argc, return 0; }
+static ulong bootm_data_addr(int argc, char *const argv[]) +{ + ulong addr; + + if (argc > 0) + addr = simple_strtoul(argv[0], NULL, 16); + else + addr = image_load_addr; + + return addr; +} + +static int bootm_pre_load(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + ulong data_addr = bootm_data_addr(argc, argv); + int ret = 0; + + if (CONFIG_IS_ENABLED(CMD_BOOTM_PRE_LOAD)) + ret = image_pre_load(data_addr); + + if (ret) + ret = CMD_RET_FAILURE; + + return ret; +} + static int bootm_find_os(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { @@ -677,6 +704,9 @@ int do_bootm_states(struct cmd_tbl *cmdtp, int flag, int argc, if (states & BOOTM_STATE_START) ret = bootm_start(cmdtp, flag, argc, argv);
+ if (!ret && (states & BOOTM_STATE_PRE_LOAD)) + ret = bootm_pre_load(cmdtp, flag, argc, argv); + if (!ret && (states & BOOTM_STATE_FINDOS)) ret = bootm_find_os(cmdtp, flag, argc, argv);
@@ -866,6 +896,9 @@ static const void *boot_get_kernel(struct cmd_tbl *cmdtp, int flag, int argc, &fit_uname_config, &fit_uname_kernel);
+ if (CONFIG_IS_ENABLED(CMD_BOOTM_PRE_LOAD)) + img_addr += image_load_offset; + bootstage_mark(BOOTSTAGE_ID_CHECK_MAGIC);
/* check image type, for FIT images get FIT kernel node */ diff --git a/cmd/Kconfig b/cmd/Kconfig index 5e25e45fd2..87aa3fb11a 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -194,6 +194,16 @@ config CMD_BOOTM help Boot an application image from the memory.
+config CMD_BOOTM_PRE_LOAD + bool "enable pre-load on bootm" + depends on CMD_BOOTM + depends on IMAGE_PRE_LOAD + default n + help + Enable support of stage pre-load for the bootm command. + This stage allow to check or modify the image provided + to the bootm command. + config BOOTM_EFI bool "Support booting UEFI FIT images" depends on CMD_BOOTEFI && CMD_BOOTM && FIT diff --git a/cmd/bootm.c b/cmd/bootm.c index e8b7066888..c5de339fba 100644 --- a/cmd/bootm.c +++ b/cmd/bootm.c @@ -126,7 +126,7 @@ int do_bootm(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) }
return do_bootm_states(cmdtp, flag, argc, argv, BOOTM_STATE_START | - BOOTM_STATE_FINDOS | BOOTM_STATE_FINDOTHER | + BOOTM_STATE_FINDOS | BOOTM_STATE_PRE_LOAD | BOOTM_STATE_FINDOTHER | BOOTM_STATE_LOADOS | #ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH BOOTM_STATE_RAMDISK | diff --git a/include/image.h b/include/image.h index fbcf70f5e4..496b7af3f3 100644 --- a/include/image.h +++ b/include/image.h @@ -351,6 +351,7 @@ typedef struct bootm_headers { #define BOOTM_STATE_OS_PREP (0x00000100) #define BOOTM_STATE_OS_FAKE_GO (0x00000200) /* 'Almost' run the OS */ #define BOOTM_STATE_OS_GO (0x00000400) +#define BOOTM_STATE_PRE_LOAD 0x00000800 int state;
#if defined(CONFIG_LMB) && !defined(USE_HOSTCC)

Add the support of image pre load in spl or tpl when loading an image from ram.
Reviewed-by: Simon Glass sjg@chromium.org Signed-off-by: Philippe Reynes philippe.reynes@softathome.com --- common/spl/spl_ram.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-)
diff --git a/common/spl/spl_ram.c b/common/spl/spl_ram.c index 3f7f7accc1..8296459257 100644 --- a/common/spl/spl_ram.c +++ b/common/spl/spl_ram.c @@ -24,9 +24,17 @@ static ulong spl_ram_load_read(struct spl_load_info *load, ulong sector, ulong count, void *buf) { + ulong addr; + debug("%s: sector %lx, count %lx, buf %lx\n", __func__, sector, count, (ulong)buf); - memcpy(buf, (void *)(CONFIG_SPL_LOAD_FIT_ADDRESS + sector), count); + + addr = (ulong)CONFIG_SPL_LOAD_FIT_ADDRESS + sector; + if (CONFIG_IS_ENABLED(IMAGE_PRE_LOAD)) + addr += image_load_offset; + + memcpy(buf, (void *)addr, count); + return count; }
@@ -37,6 +45,17 @@ static int spl_ram_load_image(struct spl_image_info *spl_image,
header = (struct image_header *)CONFIG_SPL_LOAD_FIT_ADDRESS;
+ if (CONFIG_IS_ENABLED(IMAGE_PRE_LOAD)) { + unsigned long addr = (unsigned long)header; + int ret = image_pre_load(addr); + + if (ret) + return ret; + + addr += image_load_offset; + header = (struct image_header *)addr; + } + #if CONFIG_IS_ENABLED(DFU) if (bootdev->boot_device == BOOT_DEVICE_DFU) spl_dfu_cmd(0, "dfu_alt_info_ram", "ram", "0");

This commit enhances mkimage to update the node /image/pre-load/sig with the public key.
Signed-off-by: Philippe Reynes philippe.reynes@softathome.com --- include/image.h | 15 ++++++ tools/fit_image.c | 3 ++ tools/image-host.c | 114 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+)
diff --git a/include/image.h b/include/image.h index 496b7af3f3..498eb7f2e3 100644 --- a/include/image.h +++ b/include/image.h @@ -1019,6 +1019,21 @@ int fit_image_hash_get_value(const void *fit, int noffset, uint8_t **value,
int fit_set_timestamp(void *fit, int noffset, time_t timestamp);
+/** + * fit_pre_load_data() - add public key to fdt blob + * + * Adds public key to the node pre load. + * + * @keydir: Directory containing keys + * @keydest: FDT blob to write public key + * @fit: Pointer to the FIT format image header + * + * returns: + * 0, on success + * < 0, on failure + */ +int fit_pre_load_data(const char *keydir, void *keydest, void *fit); + int fit_cipher_data(const char *keydir, void *keydest, void *fit, const char *comment, int require_keys, const char *engine_id, const char *cmdname); diff --git a/tools/fit_image.c b/tools/fit_image.c index 15f7c82d61..1884a2eb0b 100644 --- a/tools/fit_image.c +++ b/tools/fit_image.c @@ -59,6 +59,9 @@ static int fit_add_file_data(struct image_tool_params *params, size_t size_inc, ret = fit_set_timestamp(ptr, 0, time); }
+ if (!ret) + ret = fit_pre_load_data(params->keydir, dest_blob, ptr); + if (!ret) { ret = fit_cipher_data(params->keydir, dest_blob, ptr, params->comment, diff --git a/tools/image-host.c b/tools/image-host.c index eaeb76545c..ab6f756cf1 100644 --- a/tools/image-host.c +++ b/tools/image-host.c @@ -14,6 +14,11 @@ #include <image.h> #include <version.h>
+#include <openssl/pem.h> +#include <openssl/evp.h> + +#define IMAGE_PRE_LOAD_PATH "/image/pre-load/sig" + /** * fit_set_hash_value - set hash value in requested has node * @fit: pointer to the FIT format image header @@ -1111,6 +1116,115 @@ static int fit_config_add_verification_data(const char *keydir, return 0; }
+/* + * 0) open file (open) + * 1) read certificate (PEM_read_X509) + * 2) get public key (X509_get_pubkey) + * 3) provide der format (d2i_RSAPublicKey) + */ +static int read_pub_key(const char *keydir, const void *name, + unsigned char **pubkey, int *pubkey_len) +{ + char path[1024]; + EVP_PKEY *key = NULL; + X509 *cert; + FILE *f; + int ret; + + memset(path, 0, 1024); + snprintf(path, sizeof(path), "%s/%s.crt", keydir, (char *)name); + + /* Open certificate file */ + f = fopen(path, "r"); + if (!f) { + fprintf(stderr, "Couldn't open RSA certificate: '%s': %s\n", + path, strerror(errno)); + return -EACCES; + } + + /* Read the certificate */ + cert = NULL; + if (!PEM_read_X509(f, &cert, NULL, NULL)) { + printf("Couldn't read certificate"); + ret = -EINVAL; + goto err_cert; + } + + /* Get the public key from the certificate. */ + key = X509_get_pubkey(cert); + if (!key) { + printf("Couldn't read public key\n"); + ret = -EINVAL; + goto err_pubkey; + } + + /* Get DER form */ + ret = i2d_PublicKey(key, pubkey); + if (ret < 0) { + printf("Couldn't get DER form\n"); + ret = -EINVAL; + goto err_pubkey; + } + + *pubkey_len = ret; + ret = 0; + +err_pubkey: + X509_free(cert); +err_cert: + fclose(f); + return ret; +} + +int fit_pre_load_data(const char *keydir, void *keydest, void *fit) +{ + int pre_load_noffset; + const void *algo_name; + const void *key_name; + unsigned char *pubkey = NULL; + int ret, pubkey_len; + + if (!keydir || !keydest || !fit) + return 0; + + /* Search node pre-load sig */ + pre_load_noffset = fdt_path_offset(keydest, IMAGE_PRE_LOAD_PATH); + if (pre_load_noffset < 0) { + ret = 0; + goto out; + } + + algo_name = fdt_getprop(keydest, pre_load_noffset, "algo-name", NULL); + key_name = fdt_getprop(keydest, pre_load_noffset, "key-name", NULL); + + /* Check that all mandatory properties are present */ + if (!algo_name || !key_name) { + if (!algo_name) + printf("The property algo-name is missing in the node %s\n", + IMAGE_PRE_LOAD_PATH); + if (!key_name) + printf("The property key-name is missing in the node %s\n", + IMAGE_PRE_LOAD_PATH); + ret = -ENODATA; + goto out; + } + + /* Read public key */ + ret = read_pub_key(keydir, key_name, &pubkey, &pubkey_len); + if (ret < 0) + goto out; + + /* Add the public key to the device tree */ + ret = fdt_setprop(keydest, pre_load_noffset, "public-key", + pubkey, pubkey_len); + if (ret) + printf("Can't set public-key in node %s (ret = %d)\n", + IMAGE_PRE_LOAD_PATH, ret); + + out: + return ret; +} + int fit_cipher_data(const char *keydir, void *keydest, void *fit, const char *comment, int require_keys, const char *engine_id, const char *cmdname)

Set the variable key-path with the shell variable KEY_PATH that contain the keys path (used for signature). This variable key-path is provided to binman.
Signed-off-by: Philippe Reynes philippe.reynes@softathome.com --- Makefile | 1 + 1 file changed, 1 insertion(+)
diff --git a/Makefile b/Makefile index 10879f1f9c..85220419de 100644 --- a/Makefile +++ b/Makefile @@ -1332,6 +1332,7 @@ cmd_binman = $(srctree)/tools/binman/binman $(if $(BINMAN_DEBUG),-D) \ -a tpl-bss-pad=$(if $(CONFIG_TPL_SEPARATE_BSS),,1) \ -a spl-dtb=$(CONFIG_SPL_OF_REAL) \ -a tpl-dtb=$(CONFIG_TPL_OF_REAL) \ + -a key-path=${KEY_PATH} \ $(BINMAN_$(@F))
OBJCOPYFLAGS_u-boot.ldr.hex := -I binary -O ihex

Adds the support of the pre-load header with the image signature to binman.
Signed-off-by: Philippe Reynes philippe.reynes@softathome.com --- tools/binman/etype/pre_load.py | 156 +++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 tools/binman/etype/pre_load.py
diff --git a/tools/binman/etype/pre_load.py b/tools/binman/etype/pre_load.py new file mode 100644 index 0000000000..adc25fe844 --- /dev/null +++ b/tools/binman/etype/pre_load.py @@ -0,0 +1,156 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2022 Softathome +# Written by Philippe Reynes philippe.reynes@softathome.com +# +# Entry-type for the global header +# + +import struct +from dtoc import fdt_util +from patman import tools + +from binman.entry import Entry +from binman.etype.blob import Entry_blob +from binman.entry import EntryArg + +from Cryptodome.Hash import SHA256, SHA384, SHA512 +from Cryptodome.PublicKey import RSA +from Cryptodome.Signature import pkcs1_15 +from Cryptodome.Signature import pss + +PRE_LOAD_MAGIC = b'UBSH' + +RSAS = { + 'rsa1024': 1024 / 8, + 'rsa2048': 2048 / 8, + 'rsa4096': 4096 / 8 +} + +SHAS = { + 'sha256': SHA256, + 'sha384': SHA384, + 'sha512': SHA512 +} + +class Entry_pre_load(Entry_blob): + """Pre load image header + + Properties / Entry arguments: + - key-path: Path of the directory that store key (provided by the environment variable KEY_PATH) + - image: Filename of the image + - algo-name: Hash and signature algo to use for the signature + - padding-name: Name of the padding (pkcs-1.5 or pss) + - key-name: Filename of the private key to sign + - header-size: Total size of the header + - version: Version of the header + + This entry create a pre-load header that contain a global + image signature. + + For example, this creates an image with a pre-load header and a binary:: + + binman { + image2 { + filename = "sandbox.bin"; + + pre-load { + image = "sandbox.itb"; + algo-name = "sha256,rsa2048"; + padding-name = "pss"; + key-name = "private.pem"; + header-size = <4096>; + version = <1>; + }; + + blob-ext { + filename = "sandbox.itb"; + }; + }; + }; + """ + + def __init__(self, section, etype, node): + super().__init__(section, etype, node) + self.image = fdt_util.GetString(self._node, 'image') + self.pathimage = tools.GetInputFilename(self.image, + self.external and self.section.GetAllowMissing()) + self.algo_name = fdt_util.GetString(self._node, 'algo-name') + self.padding_name = fdt_util.GetString(self._node, 'padding-name') + self.key_name = fdt_util.GetString(self._node, 'key-name') + self.header_size = fdt_util.GetInt(self._node, 'header-size') + self.version = fdt_util.GetInt(self._node, 'version') + + def _CreateHeader(self): + """Create a pre load header""" + with open(self.pathimage, 'rb') as f: + image = f.read() + hash_name, sign_name = self.algo_name.split(',') + padding_name=self.padding_name + key_path, = self.GetEntryArgsOrProps([EntryArg('key-path', str)]) + if key_path == "": + key_name = self.key_name + else: + key_name = key_path + "/" + self.key_name + + # Check hash and signature name/type + if hash_name not in SHAS: + raise ValueError(hash_name + " is not supported") + if sign_name not in RSAS: + raise ValueError(sign_name + "is not supported") + + # Read the key + with open(key_name, 'rb') as pem: + key = RSA.import_key(pem.read()) + + # Check if the key has the expected size + if key.size_in_bytes() != RSAS[sign_name]: + raise ValueError("The key " + self.key_name + " don't have the expected size") + + + # Compute the hash + hash_image = SHAS[hash_name].new() + with open(self.pathimage, 'rb') as f: + image = f.read() + hash_image.update(image) + + # Compute the signature + if padding_name is None: + padding_name = "pkcs-1.5" + if padding_name == "pss": + salt_len = key.size_in_bytes() - hash_image.digest_size - 2 + padding = pss + padding_args = {'salt_bytes': salt_len} + elif padding_name == "pkcs-1.5": + padding = pkcs1_15 + padding_args = {} + else: + raise ValueError(padding_name + " is not supported") + + sig = padding.new(key, **padding_args).sign(hash_image) + + hash_sig = SHA256.new() + hash_sig.update(sig) + + version = self.version + header_size = self.header_size + image_size = len(image) + ofs_img_sig = 64 + len(sig) + flags = 0 + reserved0 = 0 + reserved1 = 0 + + first_header = PRE_LOAD_MAGIC + struct.pack('<IIIIIII', version, header_size, image_size, ofs_img_sig, flags, reserved0, reserved1) + hash_sig.digest() + + hash_first_header = SHAS[hash_name].new() + hash_first_header.update(first_header) + sig_first_header = padding.new(key, **padding_args).sign(hash_first_header) + + data = first_header + sig_first_header + sig + padd = bytearray(self.header_size - len(data)) + + return data + padd + + def ObtainContents(self): + """Obtain a placeholder for the header contents""" + self.SetContents(self._CreateHeader()) + return True

Enable the support of stage pre-load in bootm. For the moment, this stage allow to verify the signature of the full image with a header.
Signed-off-by: Philippe Reynes philippe.reynes@softathome.com --- configs/sandbox_defconfig | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 749b0f0155..95fa37869f 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -28,6 +28,8 @@ CONFIG_AUTOBOOT_SHA256_FALLBACK=y CONFIG_AUTOBOOT_NEVER_TIMEOUT=y CONFIG_AUTOBOOT_STOP_STR_ENABLE=y CONFIG_AUTOBOOT_STOP_STR_CRYPT="$5$rounds=640000$HrpE65IkB8CM5nCL$BKT3QdF98Bo8fJpTr9tjZLZQyzqPASBY20xuK5Rent9" +CONFIG_IMAGE_PRE_LOAD=y +CONFIG_IMAGE_PRE_LOAD_SIG=y CONFIG_CONSOLE_RECORD=y CONFIG_CONSOLE_RECORD_OUT_SIZE=0x1000 CONFIG_PRE_CONSOLE_BUFFER=y @@ -38,6 +40,7 @@ CONFIG_STACKPROTECTOR=y CONFIG_ANDROID_AB=y CONFIG_CMD_CPU=y CONFIG_CMD_LICENSE=y +CONFIG_CMD_BOOTM_PRE_LOAD=y CONFIG_CMD_BOOTZ=y CONFIG_CMD_BOOTEFI_HELLO=y CONFIG_CMD_ABOOTIMG=y

Adds test units for the pre-load header signature.
Signed-off-by: Philippe Reynes philippe.reynes@softathome.com --- test/py/tests/test_vboot.py | 119 ++++++++++++++++-- test/py/tests/vboot/sandbox-binman-pss.dts | 25 ++++ test/py/tests/vboot/sandbox-binman.dts | 24 ++++ .../tests/vboot/sandbox-u-boot-global-pss.dts | 25 ++++ test/py/tests/vboot/sandbox-u-boot-global.dts | 24 ++++ test/py/tests/vboot/simple-images.its | 36 ++++++ 6 files changed, 240 insertions(+), 13 deletions(-) create mode 100644 test/py/tests/vboot/sandbox-binman-pss.dts create mode 100644 test/py/tests/vboot/sandbox-binman.dts create mode 100644 test/py/tests/vboot/sandbox-u-boot-global-pss.dts create mode 100644 test/py/tests/vboot/sandbox-u-boot-global.dts create mode 100644 test/py/tests/vboot/simple-images.its
diff --git a/test/py/tests/test_vboot.py b/test/py/tests/test_vboot.py index 095e00cce3..501caa3a50 100644 --- a/test/py/tests/test_vboot.py +++ b/test/py/tests/test_vboot.py @@ -35,16 +35,18 @@ import vboot_evil # Only run the full suite on a few combinations, since it doesn't add any more # test coverage. TESTDATA = [ - ['sha1-basic', 'sha1', '', None, False, True], - ['sha1-pad', 'sha1', '', '-E -p 0x10000', False, False], - ['sha1-pss', 'sha1', '-pss', None, False, False], - ['sha1-pss-pad', 'sha1', '-pss', '-E -p 0x10000', False, False], - ['sha256-basic', 'sha256', '', None, False, False], - ['sha256-pad', 'sha256', '', '-E -p 0x10000', False, False], - ['sha256-pss', 'sha256', '-pss', None, False, False], - ['sha256-pss-pad', 'sha256', '-pss', '-E -p 0x10000', False, False], - ['sha256-pss-required', 'sha256', '-pss', None, True, False], - ['sha256-pss-pad-required', 'sha256', '-pss', '-E -p 0x10000', True, True], + ['sha1-basic', 'sha1', '', None, False, True, False], + ['sha1-pad', 'sha1', '', '-E -p 0x10000', False, False, False], + ['sha1-pss', 'sha1', '-pss', None, False, False, False], + ['sha1-pss-pad', 'sha1', '-pss', '-E -p 0x10000', False, False, False], + ['sha256-basic', 'sha256', '', None, False, False, False], + ['sha256-pad', 'sha256', '', '-E -p 0x10000', False, False, False], + ['sha256-pss', 'sha256', '-pss', None, False, False, False], + ['sha256-pss-pad', 'sha256', '-pss', '-E -p 0x10000', False, False, False], + ['sha256-pss-required', 'sha256', '-pss', None, True, False, False], + ['sha256-pss-pad-required', 'sha256', '-pss', '-E -p 0x10000', True, True, False], + ['sha256-global-sign', 'sha256', '', '', False, False, True], + ['sha256-global-sign-pss', 'sha256', '-pss', '', False, False, True], ]
@pytest.mark.boardspec('sandbox') @@ -53,10 +55,10 @@ TESTDATA = [ @pytest.mark.requiredtool('fdtget') @pytest.mark.requiredtool('fdtput') @pytest.mark.requiredtool('openssl') -@pytest.mark.parametrize("name,sha_algo,padding,sign_options,required,full_test", +@pytest.mark.parametrize("name,sha_algo,padding,sign_options,required,full_test,global_sign", TESTDATA) def test_vboot(u_boot_console, name, sha_algo, padding, sign_options, required, - full_test): + full_test,global_sign): """Test verified boot signing with mkimage and verification with 'bootm'.
This works using sandbox only as it needs to update the device tree used @@ -78,6 +80,29 @@ def test_vboot(u_boot_console, name, sha_algo, padding, sign_options, required, util.run_and_log(cons, 'dtc %s %s%s -O dtb ' '-o %s%s' % (dtc_args, datadir, dts, tmpdir, dtb))
+ def dtc_options(dts, options): + """Run the device tree compiler to compile a .dts file + + The output file will be the same as the input file but with a .dtb + extension. + + Args: + dts: Device tree file to compile. + options: Options provided to the compiler. + """ + dtb = dts.replace('.dts', '.dtb') + util.run_and_log(cons, 'dtc %s %s%s -O dtb ' + '-o %s%s %s' % (dtc_args, datadir, dts, tmpdir, dtb, options)) + + def run_binman(dtb): + """Run binman to build an image + + Args: + dtb: Device tree file used as input file. + """ + util.run_and_log(cons, [binman, 'build', '-d', "%s/%s" % (tmpdir,dtb), + '-a', "key-path=%s" % tmpdir, '-O', tmpdir, '-I', tmpdir]) + def run_bootm(sha_algo, test_type, expect_string, boots, fit=None): """Run a 'bootm' command U-Boot.
@@ -136,6 +161,23 @@ def test_vboot(u_boot_console, name, sha_algo, padding, sign_options, required, cons.log.action('%s: Sign images' % sha_algo) util.run_and_log(cons, args)
+ def sign_fit_dtb(sha_algo, options, dtb): + """Sign the FIT + + Signs the FIT and writes the signature into it. It also writes the + public key into the dtb. + + Args: + sha_algo: Either 'sha1' or 'sha256', to select the algorithm to + use. + options: Options to provide to mkimage. + """ + args = [mkimage, '-F', '-k', tmpdir, '-K', dtb, '-r', fit] + if options: + args += options.split(' ') + cons.log.action('%s: Sign images' % sha_algo) + util.run_and_log(cons, args) + def sign_fit_norequire(sha_algo, options): """Sign the FIT
@@ -173,6 +215,11 @@ def test_vboot(u_boot_console, name, sha_algo, padding, sign_options, required, handle.write(struct.pack(">I", size)) return struct.unpack(">I", total_size)[0]
+ def corrupt_file(fit,offset,value): + with open(fit, 'r+b') as handle: + handle.seek(offset) + handle.write(struct.pack(">I", value)) + def create_rsa_pair(name): """Generate a new RSA key paid and certificate
@@ -365,6 +412,49 @@ def test_vboot(u_boot_console, name, sha_algo, padding, sign_options, required, (dtb)) run_bootm(sha_algo, 'multi required key', '', False)
+ def test_global_sign(sha_algo, padding, sign_options): + """Test global image signature with the given hash algorithm and padding. + + Args: + sha_algo: Either 'sha1' or 'sha256', to select the algorithm to use + padding: Either '' or '-pss', to select the padding to use for the + rsa signature algorithm. + """ + + dtb = '%ssandbox-u-boot-global%s.dtb' % (tmpdir, padding) + cons.config.dtb = dtb + + # Compile our device tree files for kernel and U-Boot. These are + # regenerated here since mkimage will modify them (by adding a + # public key) below. + dtc('sandbox-kernel.dts') + dtc_options('sandbox-u-boot-global%s.dts' % padding, '-p 1024') + + # Build the FIT with dev key (keys NOT required). This adds the + # signature into sandbox-u-boot.dtb, NOT marked 'required'. + make_fit('simple-images.its') + sign_fit_dtb(sha_algo, '', dtb) + + # Build the dtb for binman that define the pre-load header + # with the global sigature. + dtc('sandbox-binman%s.dts' % padding) + + # Run binman to create the final image with the not signed fit + # and the pre-load header that contains the global signature. + run_binman('sandbox-binman%s.dtb' % padding) + + # Check that the signature is correctly verified by u-boot + run_bootm(sha_algo, 'global image signature', 'signature check has succeed', True, "%ssandbox.img" % tmpdir) + + # Corrupt the image (just one byte after the pre-load header) + corrupt_file("%ssandbox.img" % tmpdir, 4096, 255); + + # Check that the signature verification fails + run_bootm(sha_algo, 'global image signature', 'signature check has failed', False, "%ssandbox.img" % tmpdir) + + # Check that the boot fails if the global signature is not provided + run_bootm(sha_algo, 'global image signature', 'signature is mandatory', False) + cons = u_boot_console tmpdir = os.path.join(cons.config.result_dir, name) + '/' if not os.path.exists(tmpdir): @@ -372,6 +462,7 @@ def test_vboot(u_boot_console, name, sha_algo, padding, sign_options, required, datadir = cons.config.source_dir + '/test/py/tests/vboot/' fit = '%stest.fit' % tmpdir mkimage = cons.config.build_dir + '/tools/mkimage' + binman = cons.config.source_dir + '/tools/binman/binman' fit_check_sign = cons.config.build_dir + '/tools/fit_check_sign' dtc_args = '-I dts -O dtb -i %s' % tmpdir dtb = '%ssandbox-u-boot.dtb' % tmpdir @@ -394,7 +485,9 @@ def test_vboot(u_boot_console, name, sha_algo, padding, sign_options, required, # afterwards. old_dtb = cons.config.dtb cons.config.dtb = dtb - if required: + if global_sign: + test_global_sign(sha_algo, padding, sign_options) + elif required: test_required_key(sha_algo, padding, sign_options) else: test_with_algo(sha_algo, padding, sign_options) diff --git a/test/py/tests/vboot/sandbox-binman-pss.dts b/test/py/tests/vboot/sandbox-binman-pss.dts new file mode 100644 index 0000000000..54f82f1df5 --- /dev/null +++ b/test/py/tests/vboot/sandbox-binman-pss.dts @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + filename = "sandbox.img"; + + pre-load { + image = "test.fit"; + algo-name = "sha256,rsa2048"; + padding-name = "pss"; + key-name = "dev.key"; + header-size = <4096>; + version = <1>; + }; + + blob-ext { + filename = "test.fit"; + }; + }; +}; diff --git a/test/py/tests/vboot/sandbox-binman.dts b/test/py/tests/vboot/sandbox-binman.dts new file mode 100644 index 0000000000..56d835a938 --- /dev/null +++ b/test/py/tests/vboot/sandbox-binman.dts @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + filename = "sandbox.img"; + + pre-load { + image = "test.fit"; + algo-name = "sha256,rsa2048"; + key-name = "dev.key"; + header-size = <4096>; + version = <1>; + }; + + blob-ext { + filename = "test.fit"; + }; + }; +}; diff --git a/test/py/tests/vboot/sandbox-u-boot-global-pss.dts b/test/py/tests/vboot/sandbox-u-boot-global-pss.dts new file mode 100644 index 0000000000..4c5404842f --- /dev/null +++ b/test/py/tests/vboot/sandbox-u-boot-global-pss.dts @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + model = "Sandbox Verified Boot Test"; + compatible = "sandbox"; + + reset@0 { + compatible = "sandbox,reset"; + }; + + image { + pre-load { + sig { + algo-name = "sha256,rsa2048"; + padding-name = "pss"; + signature-size = <256>; + mandatory = "yes"; + + key-name = "dev"; + }; + }; + }; +}; diff --git a/test/py/tests/vboot/sandbox-u-boot-global.dts b/test/py/tests/vboot/sandbox-u-boot-global.dts new file mode 100644 index 0000000000..fe9a99f963 --- /dev/null +++ b/test/py/tests/vboot/sandbox-u-boot-global.dts @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + model = "Sandbox Verified Boot Test"; + compatible = "sandbox"; + + reset@0 { + compatible = "sandbox,reset"; + }; + + image { + pre-load { + sig { + algo-name = "sha256,rsa2048"; + signature-size = <256>; + mandatory = "yes"; + + key-name = "dev"; + }; + }; + }; +}; diff --git a/test/py/tests/vboot/simple-images.its b/test/py/tests/vboot/simple-images.its new file mode 100644 index 0000000000..f62786456b --- /dev/null +++ b/test/py/tests/vboot/simple-images.its @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + description = "Chrome OS kernel image with one or more FDT blobs"; + #address-cells = <1>; + + images { + kernel { + data = /incbin/("test-kernel.bin"); + type = "kernel_noload"; + arch = "sandbox"; + os = "linux"; + compression = "none"; + load = <0x4>; + entry = <0x8>; + kernel-version = <1>; + }; + fdt-1 { + description = "snow"; + data = /incbin/("sandbox-kernel.dtb"); + type = "flat_dt"; + arch = "sandbox"; + compression = "none"; + fdt-version = <1>; + }; + }; + configurations { + default = "conf-1"; + conf-1 { + kernel = "kernel"; + fdt = "fdt-1"; + }; + }; +};

Add the command verify that check the signature of an image with the pre-load header. If the check succeed, the u-boot env variable 'loadaddr_verified' is set to the address of the image (without the header).
It allows to run such commands: tftp script.img && verify $loadaddr && source $loadaddr_verified
Signed-off-by: Philippe Reynes philippe.reynes@softathome.com --- cmd/Kconfig | 7 +++++++ cmd/Makefile | 1 + cmd/verify.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+) create mode 100644 cmd/verify.c
diff --git a/cmd/Kconfig b/cmd/Kconfig index 87aa3fb11a..0460d5c3a0 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -428,6 +428,13 @@ config CMD_THOR_DOWNLOAD There is no documentation about this within the U-Boot source code but you should be able to find something on the interwebs.
+config CMD_VERIFY + bool "verify the global signature" + depends on CMD_BOOTM_PRE_LOAD + help + Verify the signature provided in a pre-load header of + a full image. + config CMD_ZBOOT bool "zboot - x86 boot command" help diff --git a/cmd/Makefile b/cmd/Makefile index 166c652d98..80e054e806 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -177,6 +177,7 @@ obj-$(CONFIG_CMD_THOR_DOWNLOAD) += thordown.o obj-$(CONFIG_CMD_XIMG) += ximg.o obj-$(CONFIG_CMD_YAFFS2) += yaffs2.o obj-$(CONFIG_CMD_SPL) += spl.o +obj-$(CONFIG_CMD_VERIFY) += verify.o obj-$(CONFIG_CMD_W1) += w1.o obj-$(CONFIG_CMD_ZIP) += zip.o obj-$(CONFIG_CMD_ZFS) += zfs.o diff --git a/cmd/verify.c b/cmd/verify.c new file mode 100644 index 0000000000..4d055e0790 --- /dev/null +++ b/cmd/verify.c @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2022 Philippe Reynes philippe.reynes@softathome.com + */ + +#include <common.h> +#include <env.h> +#include <image.h> +#include <mapmem.h> + +static ulong verify_get_addr(int argc, char *const argv[]) +{ + ulong addr; + + if (argc > 0) + addr = simple_strtoul(argv[0], NULL, 16); + else + addr = image_load_addr; + + return addr; +} + +static int do_verify(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + ulong addr = verify_get_addr(argc, argv); + int ret = 0; + + argc--; argv++; + + addr = verify_get_addr(argc, argv); + + if (CONFIG_IS_ENABLED(CMD_BOOTM_PRE_LOAD)) { + ret = image_pre_load(addr); + + if (ret) { + ret = CMD_RET_FAILURE; + goto out; + } + + env_set_hex("loadaddr_verified", addr + image_load_offset); + } + + out: + return ret; +} + +U_BOOT_CMD(verify, 2, 1, do_verify, + "verify the global signature provided in the pre-load header,\n" + "\tif the check succeed, the u-boot env variable loadaddr_verified\n" + "\tis set to the address of the image (without the header)", + "<image addr>" +);

Enable the command verify on sandbox.
Signed-off-by: Philippe Reynes philippe.reynes@softathome.com --- configs/sandbox_defconfig | 1 + 1 file changed, 1 insertion(+)
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 95fa37869f..fa7c552b66 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -45,6 +45,7 @@ CONFIG_CMD_BOOTZ=y CONFIG_CMD_BOOTEFI_HELLO=y CONFIG_CMD_ABOOTIMG=y # CONFIG_CMD_ELF is not set +CONFIG_CMD_VERIFY=y CONFIG_CMD_ASKENV=y CONFIG_CMD_GREPENV=y CONFIG_CMD_ERASEENV=y
participants (2)
-
Philippe Reynes
-
Simon Glass