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:
commit
52413e9925
8 changed files with 138 additions and 14 deletions
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
--------
|
--------
|
||||||
|
|
||||||
|
|
|
@ -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`.
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue