Merge branch 'feature/signature_verify_updates_v3.1' into 'release/v3.1'

secure boot: Support signed app verification without hardware secure boot  (backport v3.1)

See merge request idf/esp-idf!3184
This commit is contained in:
Angus Gratton 2018-09-18 07:56:12 +08:00
commit 52413e9925
8 changed files with 138 additions and 14 deletions

View file

@ -377,7 +377,7 @@ esp_err_t esp_ota_set_boot_partition(const esp_partition_t *partition)
return ESP_ERR_OTA_VALIDATE_FAILED; return ESP_ERR_OTA_VALIDATE_FAILED;
} }
#ifdef CONFIG_SECURE_BOOT_ENABLED #ifdef CONFIG_SECURE_SIGNED_ON_UPDATE
esp_err_t ret = esp_secure_boot_verify_signature(partition->address, data.image_len); esp_err_t ret = esp_secure_boot_verify_signature(partition->address, data.image_len);
if (ret != ESP_OK) { if (ret != ESP_OK) {
return ESP_ERR_OTA_VALIDATE_FAILED; return ESP_ERR_OTA_VALIDATE_FAILED;

View file

@ -132,9 +132,59 @@ endmenu # Bootloader
menu "Security features" menu "Security features"
# These three are the actual options to check in code,
# selected by the displayed options
config SECURE_SIGNED_ON_BOOT
bool
default y
depends on SECURE_BOOT_ENABLED || SECURE_SIGNED_ON_BOOT_NO_SECURE_BOOT
config SECURE_SIGNED_ON_UPDATE
bool
default y
depends on SECURE_BOOT_ENABLED || SECURE_SIGNED_ON_UPDATE_NO_SECURE_BOOT
config SECURE_SIGNED_APPS
bool
default y
depends on SECURE_SIGNED_ON_BOOT || SECURE_SIGNED_ON_UPDATE
config SECURE_SIGNED_APPS_NO_SECURE_BOOT
bool "Require signed app images"
default n
depends on !SECURE_BOOT_ENABLED
help
Require apps to be signed to verify their integrity.
This option uses the same app signature scheme as hardware secure boot, but unlike hardware secure boot it does not prevent the bootloader from being physically updated. This means that the device can be secured against remote network access, but not physical access. Compared to using hardware Secure Boot this option is much simpler to implement.
config SECURE_SIGNED_ON_BOOT_NO_SECURE_BOOT
bool "Bootloader verifies app signatures"
default n
depends on SECURE_SIGNED_APPS_NO_SECURE_BOOT
help
If this option is set, the bootloader will be compiled with code to verify that an app is signed before booting it.
If hardware secure boot is enabled, this option is always enabled and cannot be disabled.
If hardware secure boot is not enabled, this option doesn't add significant security by itself so most users will want to leave it disabled.
config SECURE_SIGNED_ON_UPDATE_NO_SECURE_BOOT
bool "Verify app signature on update"
default y
depends on SECURE_SIGNED_APPS_NO_SECURE_BOOT
help
If this option is set, any OTA updated apps will have the signature verified before being considered valid.
When enabled, the signature is automatically checked whenever the esp_ota_ops.h APIs are used for OTA updates,
or esp_image_format.h APIs are used to verify apps.
If hardware secure boot is enabled, this option is always enabled and cannot be disabled.
If hardware secure boot is not enabled, this option still adds significant security against network-based attackers by preventing spoofing of OTA updates.
config SECURE_BOOT_ENABLED config SECURE_BOOT_ENABLED
bool "Enable secure boot in bootloader (READ DOCS FIRST)" bool "Enable hardware secure boot in bootloader (READ DOCS FIRST)"
default N default n
help help
Build a bootloader which enables secure boot on first boot. Build a bootloader which enables secure boot on first boot.
@ -169,12 +219,12 @@ endchoice
config SECURE_BOOT_BUILD_SIGNED_BINARIES config SECURE_BOOT_BUILD_SIGNED_BINARIES
bool "Sign binaries during build" bool "Sign binaries during build"
depends on SECURE_BOOT_ENABLED depends on SECURE_SIGNED_APPS
default y default y
help help
Once secure boot is enabled, bootloader will only boot if partition table and app image are signed. Once secure boot or signed app requirement is enabled, app images are required to be 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 enabled (default), 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.) If disabled, unsigned app/partition data will be built. They must be signed manually using espsecure.py (for example, on a remote signing server.)
@ -183,7 +233,7 @@ config SECURE_BOOT_SIGNING_KEY
depends on SECURE_BOOT_BUILD_SIGNED_BINARIES depends on SECURE_BOOT_BUILD_SIGNED_BINARIES
default secure_boot_signing_key.pem default secure_boot_signing_key.pem
help 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. Path to the key file used to sign app images.
Key file is an ECDSA private key (NIST256p curve) in PEM format. Key file is an ECDSA private key (NIST256p curve) in PEM format.
@ -196,11 +246,11 @@ config SECURE_BOOT_SIGNING_KEY
config SECURE_BOOT_VERIFICATION_KEY config SECURE_BOOT_VERIFICATION_KEY
string "Secure boot public signature verification key" string "Secure boot public signature verification key"
depends on SECURE_BOOT_ENABLED && !SECURE_BOOT_BUILD_SIGNED_BINARIES depends on SECURE_SIGNED_APPS && !SECURE_BOOT_BUILD_SIGNED_BINARIES
default signature_verification_key.bin default signature_verification_key.bin
help help
Path to a public key file used to verify signed images. This key is compiled into the bootloader, Path to a public key file used to verify signed images. This key is compiled into the bootloader and/or app,
and may also be used to verify signatures on OTA images after download. to verify app images.
Key file is in raw binary format, and can be extracted from a Key file is in raw binary format, and can be extracted from a
PEM formatted private key using the espsecure.py PEM formatted private key using the espsecure.py

View file

@ -12,7 +12,7 @@ COMPONENT_SRCDIRS := src
# #
# Secure boot signing key support # Secure boot signing key support
# #
ifdef CONFIG_SECURE_BOOT_ENABLED ifdef CONFIG_SECURE_SIGNED_APPS
# this path is created relative to the component build directory # this path is created relative to the component build directory
SECURE_BOOT_VERIFICATION_KEY := $(abspath signature_verification_key.bin) SECURE_BOOT_VERIFICATION_KEY := $(abspath signature_verification_key.bin)

View file

@ -17,6 +17,14 @@
#include <esp_err.h> #include <esp_err.h>
#include "soc/efuse_reg.h" #include "soc/efuse_reg.h"
#include "sdkconfig.h"
#ifdef CONFIG_SECURE_BOOT_ENABLED
#if !defined(CONFIG_SECURE_SIGNED_ON_BOOT) || !defined(CONFIG_SECURE_SIGNED_ON_UPDATE) || !defined(CONFIG_SECURE_SIGNED_APPS)
#error "internal sdkconfig error, secure boot should always enable all signature options"
#endif
#endif
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif

View file

@ -24,6 +24,20 @@
#include <bootloader_random.h> #include <bootloader_random.h>
#include <bootloader_sha.h> #include <bootloader_sha.h>
/* Checking signatures as part of verifying images is necessary:
- Always if secure boot is enabled
- Differently in bootloader and/or app, depending on kconfig
*/
#ifdef BOOTLOADER_BUILD
#ifdef CONFIG_SECURE_SIGNED_ON_BOOT
#define SECURE_BOOT_CHECK_SIGNATURE
#endif
#else /* !BOOTLOADER_BUILD */
#ifdef CONFIG_SECURE_SIGNED_ON_UPDATE
#define SECURE_BOOT_CHECK_SIGNATURE
#endif
#endif
static const char *TAG = "esp_image"; static const char *TAG = "esp_image";
#define HASH_LEN 32 /* SHA-256 digest length */ #define HASH_LEN 32 /* SHA-256 digest length */
@ -107,7 +121,7 @@ esp_err_t esp_image_load(esp_image_load_mode_t mode, const esp_partition_pos_t *
} }
// Calculate SHA-256 of image if secure boot is on, or if image has a hash appended // Calculate SHA-256 of image if secure boot is on, or if image has a hash appended
#ifdef CONFIG_SECURE_BOOT_ENABLED #ifdef SECURE_BOOT_CHECK_SIGNATURE
if (1) { if (1) {
#else #else
if (data->image.hash_appended) { if (data->image.hash_appended) {
@ -174,7 +188,7 @@ goto err;
rewritten the header - rely on esptool.py having verified the bootloader at flashing time, instead. rewritten the header - rely on esptool.py having verified the bootloader at flashing time, instead.
*/ */
if (!is_bootloader) { if (!is_bootloader) {
#ifdef CONFIG_SECURE_BOOT_ENABLED #ifdef SECURE_BOOT_CHECK_SIGNATURE
// secure boot images have a signature appended // secure boot images have a signature appended
err = verify_secure_boot_signature(sha_handle, data); err = verify_secure_boot_signature(sha_handle, data);
#else #else
@ -182,7 +196,7 @@ goto err;
if (sha_handle != NULL && !esp_cpu_in_ocd_debug_mode()) { if (sha_handle != NULL && !esp_cpu_in_ocd_debug_mode()) {
err = verify_simple_hash(sha_handle, data); err = verify_simple_hash(sha_handle, data);
} }
#endif // CONFIG_SECURE_BOOT_ENABLED #endif // SECURE_BOOT_CHECK_SIGNATURE
} else { // is_bootloader } else { // is_bootloader
// bootloader may still have a sha256 digest handle open // bootloader may still have a sha256 digest handle open
if (sha_handle != NULL) { if (sha_handle != NULL) {
@ -492,6 +506,8 @@ static esp_err_t verify_secure_boot_signature(bootloader_sha256_handle_t sha_han
{ {
uint8_t image_hash[HASH_LEN] = { 0 }; uint8_t image_hash[HASH_LEN] = { 0 };
ESP_LOGI(TAG, "Verifying image signature...");
// For secure boot, we calculate the signature hash over the whole file, which includes any "simple" hash // For secure boot, we calculate the signature hash over the whole file, which includes any "simple" hash
// appended to the image for corruption detection // appended to the image for corruption detection
if (data->image.hash_appended) { if (data->image.hash_appended) {

View file

@ -29,6 +29,11 @@ Application Example
return ESP_OK; return ESP_OK;
} }
Signature Verification
----------------------
For additional security, signature of OTA firmware images can be verified. For that, refer :ref:`secure-ota-updates`
API Reference API Reference
------------- -------------

View file

@ -32,6 +32,13 @@ The OTA data partition is two flash sectors (0x2000 bytes) in size, to prevent p
while it is being written. Sectors are independently erased and written with matching data, and if they disagree a while it is being written. Sectors are independently erased and written with matching data, and if they disagree a
counter field is used to determine which sector was written more recently. counter field is used to determine which sector was written more recently.
.. _secure-ota-updates:
Secure OTA Updates Without Secure boot
--------------------------------------
The verification of signed OTA updates can be performed even without enabling hardware secure boot. For doing so, refer :ref:`signed-app-verify`
See also See also
-------- --------

View file

@ -128,6 +128,8 @@ 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. Remember that the strength of the secure boot system depends on keeping the signing key private.
.. _remote-sign-image:
Remote Signing of Images Remote Signing of Images
------------------------ ------------------------
@ -235,3 +237,39 @@ Keyfile is the 32 byte raw secure boot key for the device. To flash this digest
esptool.py write_flash 0x0 bootloader-digest.bin esptool.py write_flash 0x0 bootloader-digest.bin
.. _secure-boot-and-flash-encr:
Secure Boot & Flash Encryption
------------------------------
If secure boot is used without :doc:`Flash Encryption <flash-encryption>`, it is possible to launch "time-of-check to time-of-use" attack, where flash contents are swapped after the image is verified and running. Therefore, it is recommended to use both the features together.
.. _signed-app-verify:
Signed App Verification Without Hardware Secure Boot
----------------------------------------------------
The integrity of apps can be checked even without enabling the hardware secure boot option. This option uses the same app signature scheme as hardware secure
boot, but unlike hardware secure boot it does not prevent the bootloader from being physically updated. This means that the device can be secured
against remote network access, but not physical access. Compared to using hardware Secure Boot this option is much simpler to implement. See :ref:`signed-app-verify-howto` for step by step instructions.
An app can be verified on update and, optionally, be verified on boot.
- Verification on update: When enabled, the signature is automatically checked whenever the esp_ota_ops.h APIs are used for OTA updates. If hardware secure boot is enabled, this option is always enabled and cannot be disabled. If hardware secure boot is not enabled, this option still adds significant security against network-based attackers by preventing spoofing of OTA updates.
- Verification on boot: When enabled, the bootloader will be compiled with code to verify that an app is signed before booting it. If hardware secure boot is enabled, this option is always enabled and cannot be disabled. If hardware secure boot is not enabled, this option doesn't add significant security by itself so most users will want to leave it disabled.
.. _signed-app-verify-howto:
How To Enable Signed App Verification
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1. Run ``make menuconfig`` -> Security features -> Enable "Require signed app images"
2. "Bootloader verifies app signatures" can be enabled, which verifies app on boot.
3. By default, "Sign binaries during build" will be enabled on selecting "Require signed app images" option, which will sign binary files as a part of build process. The file named in "Secure boot private signing key" will be used to sign the image.
4. If you disable "Sign binaries during build" option then you'll have to enter path of a public key file used to verify signed images in "Secure boot public signature verification key".
In this case, private signing key should be generated by following instructions in :ref:`secure-boot-generate-key`; public verification key and signed image should be generated by following instructions in :ref:`remote-sign-image`.