Secure boot: Option for app & partition table signing to happen outside build system

This commit is contained in:
Angus Gratton 2016-12-19 13:06:21 +11:00
parent d246836cfc
commit d6fafd00db
8 changed files with 94 additions and 10 deletions

View file

@ -69,9 +69,20 @@ config SECURE_BOOTLOADER_REFLASHABLE
endchoice
config SECURE_BOOT_SIGNING_KEY
string "Secure boot signing key"
config SECURE_BOOT_BUILD_SIGNED_BINARIES
bool "Sign binaries during build"
depends on SECURE_BOOT_ENABLED
default y
help
Once secure boot is enabled, bootloader will only boot if partition table and app image are signed.
If enabled, these binary files are signed as part of the build process. The file named in "Secure boot private signing key" will be used to sign the image.
If disabled, unsigned app/partition data will be built. They must be signed manually using espsecure.py (for example, on a remote signing server.)
config SECURE_BOOT_SIGNING_KEY
string "Secure boot private signing key"
depends on SECURE_BOOT_BUILD_SIGNED_BINARIES
default secure_boot_signing_key.pem
help
Path to the key file used to sign partition tables and app images for secure boot. Once secure boot is enabled, bootloader will only boot if partition table and app image are signed.
@ -85,6 +96,20 @@ config SECURE_BOOT_SIGNING_KEY
See docs/security/secure-boot.rst for details.
config SECURE_BOOT_VERIFICATION_KEY
string "Secure boot public signature verification key"
depends on SECURE_BOOT_ENABLED && !SECURE_BOOT_BUILD_SIGNED_BINARIES
default signature_verification_key.bin
help
Path to a public key file used to verify signed images. This key is compiled into the bootloader,
and may also be used to verify signatures on OTA images after download.
Key file is in raw binary format, and can be extracted from a
PEM formatted private key using the espsecure.py
extract_public_key command.
See docs/security/secure-boot.rst for details.
config SECURE_BOOT_INSECURE
bool "Allow potentially insecure options"
depends on SECURE_BOOT_ENABLED

View file

@ -65,8 +65,17 @@ else ifdef CONFIG_SECURE_BOOTLOADER_REFLASHABLE
BOOTLOADER_DIGEST_BIN := $(BOOTLOADER_BUILD_DIR)/bootloader-reflash-digest.bin
SECURE_BOOTLOADER_KEY := $(BOOTLOADER_BUILD_DIR)/secure-bootloader-key.bin
ifdef CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES
$(SECURE_BOOTLOADER_KEY): $(SECURE_BOOT_SIGNING_KEY)
$(Q) $(ESPSECUREPY) digest_private_key -k $< $@
$(ESPSECUREPY) digest_private_key -k $< $@
else
$(SECURE_BOOTLOADER_KEY):
@echo "No pre-generated key for a reflashable secure bootloader is available, due to signing configuration."
@echo "To generate one, you can use this command:"
@echo "espsecure.py generate_flash_encryption_key $@"
@echo "then re-run make."
exit 1
endif
bootloader: $(BOOTLOADER_DIGEST_BIN)
@echo $(SEPARATOR)

View file

@ -17,8 +17,26 @@ ifdef CONFIG_SECURE_BOOT_ENABLED
# this path is created relative to the component build directory
SECURE_BOOT_VERIFICATION_KEY := $(abspath signature_verification_key.bin)
$(SECURE_BOOT_VERIFICATION_KEY): $(SECURE_BOOT_SIGNING_KEY)
ifdef CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES
# verification key derived from signing key.
$(SECURE_BOOT_VERIFICATION_KEY): $(SECURE_BOOT_SIGNING_KEY) $(SDKCONFIG_MAKEFILE)
$(ESPSECUREPY) extract_public_key --keyfile $< $@
else
# find the configured public key file
ORIG_SECURE_BOOT_VERIFICATION_KEY := $(call resolvepath,$(call dequote,$(CONFIG_SECURE_BOOT_VERIFICATION_KEY)),$(PROJECT_PATH))
$(ORIG_SECURE_BOOT_VERIFICATION_KEY):
@echo "Secure boot verification public key '$@' missing."
@echo "This can be extracted from the private signing key, see"
@echo "docs/security/secure-boot.rst for details."
exit 1
# copy it into the build dir, so the secure boot verification key has
# a predictable file name
$(SECURE_BOOT_VERIFICATION_KEY): $(ORIG_SECURE_BOOT_VERIFICATION_KEY) $(SDKCONFIG_MAKEFILE)
$(summary) CP $< $@
cp $< $@
endif
COMPONENT_EXTRA_CLEAN += $(SECURE_BOOT_VERIFICATION_KEY)

View file

@ -28,12 +28,12 @@ ESPTOOLPY_WRITE_FLASH=$(ESPTOOLPY_SERIAL) write_flash $(if $(CONFIG_ESPTOOLPY_CO
ESPTOOL_ALL_FLASH_ARGS += $(CONFIG_APP_OFFSET) $(APP_BIN)
ifdef CONFIG_SECURE_BOOT_ENABLED
ifdef CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES
ifndef IS_BOOTLOADER_BUILD
# for secure boot, add a signing step to get from unsiged app to signed app
# for locally signed secure boot image, add a signing step to get from unsigned app to signed app
APP_BIN_UNSIGNED := $(APP_BIN:.bin=-unsigned.bin)
$(APP_BIN): $(APP_BIN_UNSIGNED) $(SECURE_BOOT_SIGNING_KEY)
$(APP_BIN): $(APP_BIN_UNSIGNED) $(SECURE_BOOT_SIGNING_KEY) $(SDKCONFIG_MAKEFILE)
$(ESPSECUREPY) sign_data --keyfile $(SECURE_BOOT_SIGNING_KEY) -o $@ $<
endif
endif

View file

@ -21,7 +21,7 @@ PARTITION_TABLE_CSV_PATH := $(call dequote,$(abspath $(PARTITION_TABLE_ROOT)/$(s
PARTITION_TABLE_BIN := $(BUILD_DIR_BASE)/$(notdir $(PARTITION_TABLE_CSV_PATH:.csv=.bin))
ifdef CONFIG_SECURE_BOOT_ENABLED
ifdef CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES
PARTITION_TABLE_BIN_UNSIGNED := $(PARTITION_TABLE_BIN:.bin=-unsigned.bin)
# add an extra signing step for secure partition table
$(PARTITION_TABLE_BIN): $(PARTITION_TABLE_BIN_UNSIGNED) $(SDKCONFIG_MAKEFILE) $(SECURE_BOOT_SIGNING_KEY)

View file

@ -25,7 +25,7 @@ This is a high level overview of the secure boot process. Step by step instructi
1. The options to enable secure boot are provided in the ``make menuconfig`` hierarchy, under "Secure Boot Configuration".
2. Secure Boot Configuration includes "Secure boot signing key", which is a file path. This file is a ECDSA public/private key pair in a PEM format file.
2. Secure Boot defaults to signing images and partition table data during the build process. The "Secure boot private signing key" config item is a file path to a ECDSA public/private key pair in a PEM format file.
3. The software bootloader image is built by esp-idf with secure boot support enabled and the public key (signature verification) portion of the secure boot signing key compiled in. This software bootloader image is flashed at offset 0x1000.
@ -119,6 +119,27 @@ openssl ecparam -name prime256v1 -genkey -noout -out my_secure_boot_signing_key.
Remember that the strength of the secure boot system depends on keeping the signing key private.
Remote Signing of Images
------------------------
For production builds, it can be good practice to use a remote signing server rather than have the signing key on the build machine (which is the default esp-idf secure boot configuration). The espsecure.py command line program can be used to sign app images & partition table data for secure boot, on a remote system.
To use remote signing, disable the option "Sign binaries during build". The private signing key does not need to be present on the build system. However, the public (signature verification) key is required because it is compiled into the bootloader (and can be used to verify image signatures during OTA updates.
To extract the public key from the private key::
espsecure.py extract_public_key --keyfile PRIVATE_SIGNING_KEY PUBLIC_VERIFICATION_KEY
The path to the public signature verification key needs to be specified in the menuconfig under "Secure boot public signature verification key" in order to build the secure bootloader.
After the app image and partition table are built, the build system will print signing steps using espsecure.py::
espsecure.py sign_data --keyfile PRIVATE_SIGNING_KEY BINARY_FILE
The above command appends the image signature to the existing binary. You can use the --output argument to place the binary with signature appended into a separate file::
espsecure.py sign_data --keyfile PRIVATE_SIGNING_KEY --output SIGNED_BINARY_FILE BINARY_FILE
Secure Boot Best Practices
--------------------------

View file

@ -74,7 +74,6 @@ endif
# Correspond to the files named in COMPONENT_EMBED_FILES & COMPONENT_EMBED_TXTFILES
COMPONENT_EMBED_OBJS ?= $(addsuffix .bin.o,$(COMPONENT_EMBED_FILES)) $(addsuffix .txt.o,$(COMPONENT_EMBED_TXTFILES))
# If we're called to compile something, we'll get passed the COMPONENT_INCLUDES
# variable with all the include dirs from all the components in random order. This
# means we can accidentally grab a header from another component before grabbing our own.

View file

@ -142,6 +142,11 @@ include $(IDF_PATH)/make/common.mk
all:
ifdef CONFIG_SECURE_BOOT_ENABLED
@echo "(Secure boot enabled, so bootloader not flashed automatically. See 'make bootloader' output)"
ifndef CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES
@echo "App built but not signed. Sign app & partition data before flashing, via espsecure.py:"
@echo "espsecure.py sign_data --keyfile KEYFILE $(APP_BIN)"
@echo "espsecure.py sign_data --keyfile KEYFILE $(PARTITION_TABLE_BIN)"
endif
@echo "To flash app & partition table, run 'make flash' or:"
else
@echo "To flash all build output, run 'make flash' or:"
@ -283,8 +288,15 @@ $(APP_ELF): $(foreach libcomp,$(COMPONENT_LIBRARIES),$(BUILD_DIR_BASE)/$(libcomp
# Generation of $(APP_BIN) from $(APP_ELF) is added by the esptool
# component's Makefile.projbuild
app: $(APP_BIN)
ifeq ("$(CONFIG_SECURE_BOOT_ENABLED)$(CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES)","y") # secure boot enabled, but remote sign app image
@echo "App built but not signed. Signing step via espsecure.py:"
@echo "espsecure.py sign_data --keyfile KEYFILE $(APP_BIN)"
@echo "Then flash app command is:"
@echo $(ESPTOOLPY_WRITE_FLASH) $(CONFIG_APP_OFFSET) $(APP_BIN)
else
@echo "App built. Default flash app command is:"
@echo $(ESPTOOLPY_WRITE_FLASH) $(CONFIG_APP_OFFSET) $(APP_BIN)
endif
all_binaries: $(APP_BIN)