feat: Add API to verify the bootloader and app image

Added an API to verify the bootloader and app image before revoking the key in Secure Boot V2.
This will help in preventing the device to be bricked if the bootloader/application cannot be
verified by any other keys in efuse
pull/12486/head
Harshit Malpani 2023-07-04 10:18:47 +05:30
rodzic 2472c62fff
commit 1df186d4e1
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 441A8ACC7853D493
5 zmienionych plików z 176 dodań i 81 usunięć

Wyświetl plik

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -924,9 +924,27 @@ esp_err_t esp_ota_erase_last_boot_app_partition(void)
return ESP_OK;
}
#if SOC_EFUSE_SECURE_BOOT_KEY_DIGESTS > 1 && CONFIG_SECURE_BOOT_V2_ENABLED
esp_err_t esp_ota_revoke_secure_boot_public_key(esp_ota_secure_boot_public_key_index_t index) {
#if SOC_SUPPORT_SECURE_BOOT_REVOKE_KEY && CONFIG_SECURE_BOOT_V2_ENABLED
// Validates the image at "app_pos" with the secure boot digests other than "revoked_key_index"
static bool validate_img(esp_ota_secure_boot_public_key_index_t revoked_key_index, esp_partition_pos_t *app_pos)
{
bool verified = false;
for (int i = 0; i < SOC_EFUSE_SECURE_BOOT_KEY_DIGESTS; i++) {
if (i == revoked_key_index) {
continue;
}
if (esp_secure_boot_verify_with_efuse_digest_index(i, app_pos) == ESP_OK) {
verified = true;
ESP_LOGI(TAG, "Application successfully verified with public key digest %d", i);
break;
}
}
return verified;
}
esp_err_t esp_ota_revoke_secure_boot_public_key(esp_ota_secure_boot_public_key_index_t index)
{
if (!esp_secure_boot_enabled()) {
ESP_LOGE(TAG, "Secure boot v2 has not been enabled.");
return ESP_FAIL;
@ -939,71 +957,60 @@ esp_err_t esp_ota_revoke_secure_boot_public_key(esp_ota_secure_boot_public_key_i
return ESP_ERR_INVALID_ARG;
}
esp_image_sig_public_key_digests_t app_digests = { 0 };
esp_err_t err = esp_secure_boot_get_signature_blocks_for_running_app(true, &app_digests);
if (err != ESP_OK || app_digests.num_digests == 0) {
ESP_LOGE(TAG, "This app is not signed, but check signature on update is enabled in config. It won't be possible to verify any update.");
const esp_partition_t *running_app_part = esp_ota_get_running_partition();
#ifdef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
esp_ota_img_states_t running_app_state;
ret = esp_ota_get_state_partition(running_app_part, &running_app_state);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_get_state_partition returned: %s", esp_err_to_name(ret));
return ESP_FAIL;
}
if (running_app_state != ESP_OTA_IMG_VALID) {
ESP_LOGE(TAG, "The current running application is not marked as a valid image. Aborting..");
return ESP_FAIL;
}
#endif
esp_err_t ret;
esp_secure_boot_key_digests_t trusted_keys;
ret = esp_secure_boot_read_key_digests(&trusted_keys);
esp_err_t ret = esp_secure_boot_read_key_digests(&trusted_keys);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Could not read the secure boot key digests from efuse. Aborting..");
return ESP_FAIL;
}
if (trusted_keys.key_digests[index] == NULL) {
ESP_LOGI(TAG, "Trusted Key block(%d) already revoked.", index);
ESP_LOGI(TAG, "Given public key digest(%d) is already revoked.", index);
return ESP_OK;
}
esp_image_sig_public_key_digests_t trusted_digests = { 0 };
for (unsigned i = 0; i < SECURE_BOOT_NUM_BLOCKS; i++) {
if (i == index) {
continue; // omitting - to find if there is a valid key after revoking this digest
}
/* Check if the application can be verified with a key other than the one being revoked */
esp_partition_pos_t running_app_pos = {
.offset = running_app_part->address,
.size = running_app_part->size,
};
if (trusted_keys.key_digests[i] != NULL) {
bool all_zeroes = true;
for (unsigned j = 0; j < ESP_SECURE_BOOT_DIGEST_LEN; j++) {
all_zeroes = all_zeroes && (*(uint8_t *)(trusted_keys.key_digests[i] + j) == 0);
}
if (!all_zeroes) {
memcpy(trusted_digests.key_digests[trusted_digests.num_digests++], (uint8_t *)trusted_keys.key_digests[i], ESP_SECURE_BOOT_DIGEST_LEN);
} else {
ESP_LOGD(TAG, "Empty trusted key block (%d).", i);
}
}
}
bool match = false;
for (unsigned i = 0; i < trusted_digests.num_digests; i++) {
if (match == true) {
break;
}
for (unsigned j = 0; j < app_digests.num_digests; j++) {
if (memcmp(trusted_digests.key_digests[i], app_digests.key_digests[j], ESP_SECURE_BOOT_DIGEST_LEN) == 0) {
ESP_LOGI(TAG, "App key block(%d) matches Trusted key block(%d)[%d -> Next active trusted key block].", j, i, i);
esp_err_t err = esp_efuse_set_digest_revoke(index);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to revoke digest (0x%x).", err);
return ESP_FAIL;
}
ESP_LOGI(TAG, "Revoked signature block %d.", index);
match = true;
break;
}
}
}
if (match == false) {
ESP_LOGE(TAG, "Running app doesn't have another valid secure boot key. Cannot revoke current key(%d).", index);
if (!validate_img(index, &running_app_pos)) {
ESP_LOGE(TAG, "Application cannot be verified with any key other than the one being revoked");
return ESP_FAIL;
}
/* Check if bootloder can be verified with any key other than the one being revoked */
esp_partition_pos_t bootloader_pos = {
.offset = ESP_BOOTLOADER_OFFSET,
.size = (ESP_PARTITION_TABLE_OFFSET - ESP_BOOTLOADER_OFFSET),
};
if (!validate_img(index, &bootloader_pos)) {
ESP_LOGE(TAG, "Bootloader cannot be verified with any key other than the one being revoked");
return ESP_FAIL;
}
esp_err_t err = esp_efuse_set_digest_revoke(index);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to revoke digest (0x%x).", err);
return ESP_FAIL;
}
ESP_LOGI(TAG, "Revoked signature block %d.", index);
return ESP_OK;
}
#endif

Wyświetl plik

@ -350,10 +350,10 @@ typedef enum {
} esp_ota_secure_boot_public_key_index_t;
/**
* @brief Revokes the old signature digest. To be called in the application after the rollback logic.
* @brief Revokes the signature digest denoted by the given index. This should be called in the application only after the rollback logic otherwise the device may end up in unrecoverable state.
*
* Relevant for Secure boot v2 on ESP32-S2, ESP32-S3, ESP32-C3, ESP32-C6, ESP32-H2 where up to 3 key digests can be stored (Key \#N-1, Key \#N, Key \#N+1).
* When key \#N-1 used to sign an app is invalidated, an OTA update is to be sent with an app signed with key \#N-1 & Key \#N.
* When a key used to sign an app is invalidated, an OTA update is to be sent with an app signed with at least one of the other two keys which has not been revoked already.
* After successfully booting the OTA app should call this function to revoke Key \#N-1.
*
* @param index - The index of the signature block to be revoked

Wyświetl plik

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -282,6 +282,23 @@ esp_err_t esp_secure_boot_enable_secure_features(void);
*/
bool esp_secure_boot_cfg_verify_release_mode(void);
#if !defined(BOOTLOADER_BUILD) && SOC_SUPPORT_SECURE_BOOT_REVOKE_KEY && CONFIG_SECURE_BOOT_V2_ENABLED
/** @brief Returns the verification status of the image pointed by the part_pos argument against the public key digest present at index `efuse_digest_index`
*
* @param index[in] Index of public key digest present in efuse against which the image is to be verified
* @param part_pos[in] It is a pointer to the bootloader/app partition.
*
* @return
* - ESP_OK - if the image can be verified by the key at efuse_index.
* - ESP_FAIL - if the image cannot be verified by the key at efuse_index.
* - ESP_ERR_INVALID_ARG: Error in the passed arguments.
*/
esp_err_t esp_secure_boot_verify_with_efuse_digest_index(int efuse_digest_index, esp_partition_pos_t *part_pos);
#endif // !defined(BOOTLOADER_BUILD) && SOC_SUPPORT_SECURE_BOOT_REVOKE_KEY && CONFIG_SECURE_BOOT_V2_ENABLED
#ifdef __cplusplus
}
#endif

Wyświetl plik

@ -22,6 +22,7 @@
#include "esp_secure_boot.h"
#include "esp_ota_ops.h"
#include "esp_efuse.h"
#include "esp_efuse_chip.h"
#include "secure_boot_signature_priv.h"
@ -46,31 +47,32 @@ static esp_err_t validate_signature_block(const ets_secure_boot_sig_block_t *blo
return ESP_OK;
}
esp_err_t esp_secure_boot_get_signature_blocks_for_running_app(bool digest_public_keys, esp_image_sig_public_key_digests_t *public_key_digests)
static esp_err_t calculate_image_public_key_digests(bool verify_image_digest, bool digest_public_keys, esp_image_sig_public_key_digests_t *public_key_digests, esp_partition_pos_t *part_pos)
{
esp_image_metadata_t metadata;
const esp_partition_t* running_app_part = esp_ota_get_running_partition();
if (running_app_part == NULL) {
ESP_LOGE(TAG, "Cannot get running partition");
return ESP_FAIL;
}
const esp_partition_pos_t part_pos = {
.offset = running_app_part->address,
.size = running_app_part->size,
};
esp_err_t err = esp_image_get_metadata(&part_pos, &metadata);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Error reading metadata from running app (err=0x%x)", err);
esp_image_metadata_t img_metadata = {0};
esp_err_t ret = esp_image_get_metadata(part_pos, &img_metadata);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Error reading metadata from running app (err=0x%x)", ret);
return ESP_FAIL;
}
memset(public_key_digests, 0, sizeof(esp_image_sig_public_key_digests_t));
uint8_t image_digest[ESP_SECURE_BOOT_DIGEST_LEN] = {0};
uint8_t __attribute__((aligned(4))) key_digest[ESP_SECURE_BOOT_DIGEST_LEN] = {0};
size_t sig_block_addr = img_metadata.start_addr + ALIGN_UP(img_metadata.image_len, FLASH_SECTOR_SIZE);
// Generating the SHA of the public key components in the signature block
ESP_LOGD(TAG, "calculating public key digests for sig blocks of image offset 0x%"PRIu32" (sig block offset 0x%u)", img_metadata.start_addr, sig_block_addr);
// metadata.image_len doesn't include any padding to start of the signature sector, so pad it here
size_t sig_block_addr = metadata.start_addr + ALIGN_UP(metadata.image_len, FLASH_SECTOR_SIZE);
ESP_LOGD(TAG, "reading signatures for app address 0x%"PRIx32" sig block address 0x%x", part_pos.offset, sig_block_addr);
bzero(public_key_digests, sizeof(esp_image_sig_public_key_digests_t));
if (verify_image_digest) {
ret = bootloader_sha256_flash_contents(img_metadata.start_addr, sig_block_addr - img_metadata.start_addr, image_digest);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "error generating image digest, %d", ret);
return ret;
}
}
ESP_LOGD(TAG, "reading signature(s)");
for (unsigned i = 0; i < SECURE_BOOT_NUM_BLOCKS; i++) {
ets_secure_boot_sig_block_t block;
size_t addr = sig_block_addr + sizeof(ets_secure_boot_sig_block_t) * i;
@ -84,20 +86,59 @@ esp_err_t esp_secure_boot_get_signature_blocks_for_running_app(bool digest_publi
#elif CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME
bootloader_sha256_data(sig_block_sha, &block.ecdsa.key, sizeof(block.ecdsa.key));
#endif
bootloader_sha256_finish(sig_block_sha, public_key_digests->key_digests[i]);
bootloader_sha256_finish(sig_block_sha, key_digest);
if (verify_image_digest) {
// Check we can verify the image using this signature and this key
uint8_t temp_verified_digest[ESP_SECURE_BOOT_DIGEST_LEN];
#if CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME
bool verified = ets_rsa_pss_verify(&block.key, block.signature, image_digest, temp_verified_digest);
#elif CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME
bool verified = ets_ecdsa_verify(&block.ecdsa.key.point[0], block.ecdsa.signature, block.ecdsa.key.curve_id, image_digest, temp_verified_digest);
#endif
if (!verified) {
ESP_LOGE(TAG, "Secure boot key (%d) verification failed.", i);
continue;
}
ESP_LOGD(TAG, "Signature block (%d) is verified", i);
}
/* Copy the key digest to the buffer provided by the caller */
memcpy((void *)public_key_digests->key_digests[public_key_digests->num_digests], key_digest, ESP_SECURE_BOOT_DIGEST_LEN);
}
public_key_digests->num_digests++;
}
} else {
ESP_LOGE(TAG, "Secure boot sign blocks cannot be read from a running app (err=0x%x)", err);
ESP_LOGE(TAG, "Secure boot sign blocks cannot be read from image at %lx (err=0x%x)", part_pos->offset, err);
return ESP_FAIL;
}
}
if (public_key_digests->num_digests > 0) {
return ESP_OK;
if (ret == ESP_OK && public_key_digests->num_digests > 0) {
ESP_LOGD(TAG, "Digests successfully calculated, %d valid signatures (image offset 0x%"PRIu32")",
public_key_digests->num_digests, img_metadata.start_addr);
}
ESP_LOGE(TAG, "No signatures were found for the running app");
return ESP_ERR_NOT_FOUND;
if (public_key_digests->num_digests == 0) {
return ESP_ERR_NOT_FOUND;
}
return ret;
}
esp_err_t esp_secure_boot_get_signature_blocks_for_running_app(bool digest_public_keys, esp_image_sig_public_key_digests_t *public_key_digests)
{
esp_partition_pos_t part_pos;
const esp_partition_t* running_app_part = esp_ota_get_running_partition();
if (running_app_part == NULL) {
ESP_LOGE(TAG, "Cannot get running partition");
return ESP_FAIL;
}
part_pos.offset = running_app_part->address;
part_pos.size = running_app_part->size;
esp_err_t err = calculate_image_public_key_digests(false, digest_public_keys, public_key_digests, &part_pos);
if (public_key_digests->num_digests == 0) {
ESP_LOGE(TAG, "No signatures were found for the running app");
}
return err;
}
static esp_err_t get_secure_boot_key_digests(esp_image_sig_public_key_digests_t *public_key_digests)
@ -236,3 +277,33 @@ esp_err_t esp_secure_boot_verify_rsa_signature_block(const ets_secure_boot_signa
#endif
#endif // CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME || CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME || CONFIG_SECURE_SIGNED_ON_UPDATE_NO_SECURE_BOOT
#if SOC_SUPPORT_SECURE_BOOT_REVOKE_KEY && CONFIG_SECURE_BOOT_V2_ENABLED
esp_err_t esp_secure_boot_verify_with_efuse_digest_index(int efuse_digest_index, esp_partition_pos_t *part_pos)
{
if (!part_pos || (efuse_digest_index < 0 || efuse_digest_index >= SOC_EFUSE_SECURE_BOOT_KEY_DIGESTS)) {
return ESP_ERR_INVALID_ARG;
}
esp_image_sig_public_key_digests_t img_key_digests = {0};
esp_err_t ret = calculate_image_public_key_digests(true, true, &img_key_digests, part_pos);
if (ret != ESP_OK) {
return ESP_FAIL;
}
if (esp_efuse_get_digest_revoke(efuse_digest_index)) {
return ESP_FAIL;
}
// Read key digests from efuse
esp_secure_boot_key_digests_t efuse_key_digests;
memset(&efuse_key_digests, 0, sizeof(esp_secure_boot_key_digests_t));
esp_secure_boot_read_key_digests(&efuse_key_digests);
for (int i = 0; i < img_key_digests.num_digests; i++) {
if (!memcmp(img_key_digests.key_digests[i], efuse_key_digests.key_digests[efuse_digest_index], ESP_SECURE_BOOT_KEY_DIGEST_LEN)) {
return ESP_OK;
}
}
return ESP_FAIL;
}
#endif // SOC_SUPPORT_SECURE_BOOT_REVOKE_KEY && CONFIG_SECURE_BOOT_V2_ENABLED

Wyświetl plik

@ -556,8 +556,8 @@ Secure Boot Best Practices
2. The new OTA update is written to an unused OTA app partition.
3. The new application's signature block is validated. The public keys are checked against the digests programmed in the eFuse & the application is verified using the verified public key.
4. The active partition is set to the new OTA application's partition.
5. Device resets, loads the bootloader (verified with key #N-1) which then boots new app (verified with key #N).
6. The new app verifies bootloader with key #N (as a final check) and then runs code to revoke key #N-1 (sets KEY_REVOKE eFuse bit).
5. Device resets, loads the bootloader (verified with key #N-1 and #N) which then boots new app (verified with key #N).
6. The new app verifies bootloader and application with key #N (as a final check) and then runs code to revoke key #N-1 (sets KEY_REVOKE eFuse bit).
7. The API `esp_ota_revoke_secure_boot_public_key()` can be used to revoke the key #N-1.
* A similar approach can also be used to physically re-flash with a new key. For physical re-flashing, the bootloader content can also be changed at the same time.