diff --git a/components/app_update/esp_ota_ops.c b/components/app_update/esp_ota_ops.c index 2dd1ed2052..8ef9e0ba70 100644 --- a/components/app_update/esp_ota_ops.c +++ b/components/app_update/esp_ota_ops.c @@ -40,6 +40,12 @@ #include "esp_system.h" #include "esp_efuse.h" +#ifdef CONFIG_IDF_TARGET_ESP32 +#include "esp32/rom/crc.h" +#elif CONFIG_IDF_TARGET_ESP32S2 +#include "esp32s2/rom/crc.h" +#include "esp32s2/rom/secure_boot.h" +#endif #define SUB_TYPE_ID(i) (i & 0x0F) @@ -857,3 +863,24 @@ esp_err_t esp_ota_erase_last_boot_app_partition(void) return ESP_OK; } + +#if CONFIG_IDF_TARGET_ESP32S2 && 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 (!esp_secure_boot_enabled()) { + ESP_LOGE(TAG, "Secure boot v2 has not been enabled."); + return ESP_FAIL; + } + + if (index != SECURE_BOOT_PUBLIC_KEY_INDEX_0 && + index != SECURE_BOOT_PUBLIC_KEY_INDEX_1 && + index != SECURE_BOOT_PUBLIC_KEY_INDEX_2) { + ESP_LOGE(TAG, "Invalid Index found for public key revocation %d.", index); + return ESP_ERR_INVALID_ARG; + } + + ets_secure_boot_revoke_public_key_digest(index); + ESP_LOGI(TAG, "Revoked signature block %d.", index); + return ESP_OK; +} +#endif diff --git a/components/app_update/include/esp_ota_ops.h b/components/app_update/include/esp_ota_ops.h index 3bb063d3c0..ac0ae656e0 100644 --- a/components/app_update/include/esp_ota_ops.h +++ b/components/app_update/include/esp_ota_ops.h @@ -299,6 +299,34 @@ esp_err_t esp_ota_erase_last_boot_app_partition(void); */ bool esp_ota_check_rollback_is_possible(void); +#if CONFIG_IDF_TARGET_ESP32S2 && (CONFIG_SECURE_BOOT_V2_ENABLED || __DOXYGEN__) + +/** + * Secure Boot V2 public key indexes. + */ +typedef enum { + SECURE_BOOT_PUBLIC_KEY_INDEX_0, /*!< Points to the 0th index of the Secure Boot v2 public key */ + SECURE_BOOT_PUBLIC_KEY_INDEX_1, /*!< Points to the 1st index of the Secure Boot v2 public key */ + SECURE_BOOT_PUBLIC_KEY_INDEX_2 /*!< Points to the 2nd index of the Secure Boot v2 public key */ +} esp_ota_secure_boot_public_key_index_t; + +/** + * @brief Revokes the old signature digest. To be called in the application after the rollback logic. + * + * Relevant for Secure boot v2 on ESP32-S2 where upto 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. + * 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 + * + * @return + * - ESP_OK: If revocation is successful. + * - ESP_ERR_INVALID_ARG: If the index of the public key to be revoked is incorrect. + * - ESP_FAIL: If secure boot v2 has not been enabled. + */ +esp_err_t esp_ota_revoke_secure_boot_public_key(esp_ota_secure_boot_public_key_index_t index); +#endif /* CONFIG_IDF_TARGET_ESP32S2 */ + #ifdef __cplusplus } #endif diff --git a/components/bootloader/Kconfig.projbuild b/components/bootloader/Kconfig.projbuild index 6fd6da3f49..f402dd78b1 100644 --- a/components/bootloader/Kconfig.projbuild +++ b/components/bootloader/Kconfig.projbuild @@ -383,7 +383,7 @@ menu "Security features" config SECURE_SIGNED_APPS_RSA_SCHEME bool "RSA" - depends on ESP32_REV_MIN_3 && SECURE_BOOT_V2_ENABLED + depends on (ESP32_REV_MIN_3 || IDF_TARGET_ESP32S2) && SECURE_BOOT_V2_ENABLED help Appends the RSA-3072 based Signature block to the application. Refer to before enabling. @@ -447,7 +447,9 @@ menu "Security features" config SECURE_BOOT_V2_ENABLED bool "Enable Secure Boot version 2" - depends on ESP32_REV_MIN_3 + depends on ESP32_REV_MIN_3 || IDF_TARGET_ESP32S2 + select SECURE_ENABLE_SECURE_ROM_DL_MODE if IDF_TARGET_ESP32S2 && !SECURE_INSECURE_ALLOW_DL_MODE + select SECURE_DISABLE_ROM_DL_MODE if ESP32_REV_MIN_3 && !SECURE_INSECURE_ALLOW_DL_MODE help Build a bootloader which enables Secure Boot version 2 on first boot. Refer to Secure Boot V2 section of the ESP-IDF Programmer's Guide for this version before enabling. @@ -681,6 +683,19 @@ menu "Security features" key digest, causing an immediate denial of service and possibly allowing an additional fault injection attack to bypass the signature protection. + config SECURE_INSECURE_ALLOW_DL_MODE + bool "Don't automatically restrict UART download mode" + depends on SECURE_BOOT_INSECURE && SECURE_BOOT_V2_ENABLED + default N + help + By default, enabling either flash encryption in release mode or secure boot will automatically + disable UART download mode on ESP32 ECO3, or enable secure download mode on newer chips. + This is recommended to reduce the attack surface of the chip. + + To allow the full UART download mode to stay enabled, enable this option and ensure + the options SECURE_DISABLE_ROM_DL_MODE and SECURE_ENABLE_SECURE_ROM_DL_MODE are disabled as applicable. + This is not recommended. + config SECURE_FLASH_UART_BOOTLOADER_ALLOW_ENC bool "Leave UART bootloader encryption enabled" depends on SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT diff --git a/components/bootloader/subproject/CMakeLists.txt b/components/bootloader/subproject/CMakeLists.txt index d3f9d2e53e..5d967bd9a7 100644 --- a/components/bootloader/subproject/CMakeLists.txt +++ b/components/bootloader/subproject/CMakeLists.txt @@ -92,15 +92,15 @@ endif() if(CONFIG_SECURE_BOOT_V2_ENABLED) if(CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES) - get_filename_component(secure_boot_signing_key + get_filename_component(secure_boot_signing_key "${SECURE_BOOT_SIGNING_KEY}" ABSOLUTE BASE_DIR "${project_dir}") if(NOT EXISTS "${secure_boot_signing_key}") - message(FATAL_ERROR - "Secure Boot Signing Key Not found." - "\nGenerate the Secure Boot V2 RSA-PSS 3072 Key." - "\nTo generate one, you can use this command:" - "\n\t${espsecurepy} generate_signing_key --version 2 ${SECURE_BOOT_SIGNING_KEY}") + message(FATAL_ERROR + "Secure Boot Signing Key Not found." + "\nGenerate the Secure Boot V2 RSA-PSS 3072 Key." + "\nTo generate one, you can use this command:" + "\n\t${espsecurepy} generate_signing_key --version 2 ${SECURE_BOOT_SIGNING_KEY}") endif() set(bootloader_unsigned_bin "bootloader-unsigned.bin") @@ -117,7 +117,7 @@ if(CONFIG_SECURE_BOOT_V2_ENABLED) else() add_custom_command(OUTPUT ".signed_bin_timestamp" VERBATIM - COMMENT "Bootloader generated but not signed") + COMMENT "Bootloader generated but not signed") endif() add_custom_target (gen_signed_bootloader ALL DEPENDS "${build_dir}/.signed_bin_timestamp") @@ -166,6 +166,24 @@ elseif(CONFIG_SECURE_BOOTLOADER_REFLASHABLE) "* Not recommended to re-use the same secure boot keyfile on multiple production devices." DEPENDS gen_secure_bootloader_key gen_bootloader_digest_bin VERBATIM) +elseif(CONFIG_SECURE_BOOT_V2_ENABLED AND CONFIG_IDF_TARGET_ESP32S2) + add_custom_command(TARGET bootloader.elf POST_BUILD + COMMAND ${CMAKE_COMMAND} -E echo + "==============================================================================" + COMMAND ${CMAKE_COMMAND} -E echo + "Bootloader built. Secure boot enabled, so bootloader not flashed automatically." + COMMAND ${CMAKE_COMMAND} -E echo + "To sign the bootloader with additional private keys." + COMMAND ${CMAKE_COMMAND} -E echo + "\t${espsecurepy} sign_data -k secure_boot_signing_key2.pem -v 2 --append_signatures -o signed_bootloader.bin build/bootloader/bootloader.bin" + COMMAND ${CMAKE_COMMAND} -E echo + "Secure boot enabled, so bootloader not flashed automatically." + COMMAND ${CMAKE_COMMAND} -E echo + "\t${esptoolpy_write_flash} ${BOOTLOADER_OFFSET} ${CMAKE_BINARY_DIR}/bootloader.bin" + COMMAND ${CMAKE_COMMAND} -E echo + "==============================================================================" + DEPENDS gen_signed_bootloader + VERBATIM) elseif(CONFIG_SECURE_BOOT_V2_ENABLED) add_custom_command(TARGET bootloader.elf POST_BUILD COMMAND ${CMAKE_COMMAND} -E echo diff --git a/components/bootloader_support/src/bootloader_utility.c b/components/bootloader_support/src/bootloader_utility.c index 9c2be3ab43..ce72bf0689 100644 --- a/components/bootloader_support/src/bootloader_utility.c +++ b/components/bootloader_support/src/bootloader_utility.c @@ -820,6 +820,7 @@ void bootloader_debug_buffer(const void *buffer, size_t length, const char *labe esp_err_t bootloader_sha256_flash_contents(uint32_t flash_offset, uint32_t len, uint8_t *digest) { + if (digest == NULL) { return ESP_ERR_INVALID_ARG; } @@ -836,7 +837,7 @@ esp_err_t bootloader_sha256_flash_contents(uint32_t flash_offset, uint32_t len, while (len > 0) { uint32_t mmu_page_offset = ((flash_offset & MMAP_ALIGNED_MASK) != 0) ? 1 : 0; /* Skip 1st MMU Page if it is already populated */ uint32_t partial_image_len = MIN(len, ((mmu_free_pages_count - mmu_page_offset) * SPI_FLASH_MMU_PAGE_SIZE)); /* Read the image that fits in the free MMU pages */ - + const void * image = bootloader_mmap(flash_offset, partial_image_len); if (image == NULL) { bootloader_sha256_finish(sha_handle, NULL); diff --git a/components/bootloader_support/src/esp32/secure_boot.c b/components/bootloader_support/src/esp32/secure_boot.c index 551f522cb5..3fea158cc3 100644 --- a/components/bootloader_support/src/esp32/secure_boot.c +++ b/components/bootloader_support/src/esp32/secure_boot.c @@ -392,6 +392,17 @@ esp_err_t esp_secure_boot_v2_permanently_enable(const esp_image_metadata_t *imag ESP_LOGW(TAG, "Not disabling ROM BASIC fallback - SECURITY COMPROMISED"); #endif +#ifdef CONFIG_SECURE_DISABLE_ROM_DL_MODE + ESP_LOGI(TAG, "Disable ROM Download mode..."); + esp_err_t err = esp_efuse_disable_rom_download_mode(); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Could not disable ROM Download mode..."); + return ESP_FAIL; + } +#else + ESP_LOGW(TAG, "Not disabling ROM Download mode - SECURITY COMPROMISED"); +#endif + #ifndef CONFIG_SECURE_BOOT_V2_ALLOW_EFUSE_RD_DIS bool rd_dis_now = true; #ifdef CONFIG_SECURE_FLASH_ENC_ENABLED diff --git a/components/bootloader_support/src/esp32s2/secure_boot.c b/components/bootloader_support/src/esp32s2/secure_boot.c index 11ba0e2e94..abc49801e4 100644 --- a/components/bootloader_support/src/esp32s2/secure_boot.c +++ b/components/bootloader_support/src/esp32s2/secure_boot.c @@ -11,43 +11,311 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -#include "esp_secure_boot.h" +#include + #include "esp_log.h" +#include "esp_secure_boot.h" +#include "soc/efuse_reg.h" + +#include "bootloader_flash.h" +#include "bootloader_sha.h" +#include "bootloader_utility.h" + +#include "esp_rom_crc.h" +#include "esp_efuse.h" +#include "esp_efuse_table.h" + #include "esp32s2/rom/secure_boot.h" -static const char *TAG = "secure_boot"; +static const char *TAG = "secure_boot_v2"; +#define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) -esp_err_t esp_secure_boot_permanently_enable(void) +#define SIG_BLOCK_MAGIC_BYTE 0xe7 +#define CRC_SIGN_BLOCK_LEN 1196 +#define SIG_BLOCK_PADDING 4096 + +#define DIGEST_LEN 32 + +/* A signature block is valid when it has correct magic byte, crc and image digest. */ +static esp_err_t validate_signature_block(int block_num, const ets_secure_boot_signature_t *sig_block, uint8_t *digest) { - uint8_t hash[32]; - - if (esp_rom_efuse_is_secure_boot_enabled()) - { - ESP_LOGI(TAG, "secure boot is already enabled, continuing.."); + uint32_t crc = esp_rom_crc32_le(0, (uint8_t *)&sig_block->block[block_num], CRC_SIGN_BLOCK_LEN); + if (sig_block->block[block_num].magic_byte != SIG_BLOCK_MAGIC_BYTE) { + // All signature blocks have been parsed, no new signature block present. + ESP_LOGD(TAG, "Signature block(%d) invalid/absent.", block_num); + return ESP_FAIL; + } + if (sig_block->block[block_num].block_crc != crc) { + ESP_LOGE(TAG, "Magic byte correct but incorrect crc."); + return ESP_FAIL; + } + if (memcmp(digest, sig_block->block[block_num].image_digest, DIGEST_LEN)) { + ESP_LOGE(TAG, "Magic byte & CRC correct but incorrect image digest."); + return ESP_FAIL; + } else { + ESP_LOGD(TAG, "valid signature block(%d) found", block_num); return ESP_OK; } - ESP_LOGI(TAG, "Verifying bootloader signature...\n"); - int r = ets_secure_boot_verify_bootloader(hash, false); - if (r != ESP_OK) { - ESP_LOGE(TAG, "Failed to verify bootloader signature"); - return r; + return ESP_FAIL; +} + +// Inputs the flash_offset and length of an image(app or bootloader), validates & verifies its secure boot v2 signature. +// Generates the public key digests of the valid public keys in a signature block and writes it into trusted_keys. +// The key_digests in trusted keys whose signature blocks are invalid will be set to NULL. +static esp_err_t secure_boot_v2_digest_generate(uint32_t flash_offset, uint32_t flash_size, ets_secure_boot_key_digests_t * const trusted_keys) +{ + int i = 0; + esp_err_t ret = ESP_FAIL; + + uint8_t image_digest[DIGEST_LEN] = {0}; + uint8_t public_key_digests[SECURE_BOOT_NUM_BLOCKS][DIGEST_LEN]; + size_t sig_block_addr = flash_offset + ALIGN_UP(flash_size, FLASH_SECTOR_SIZE); + ret = bootloader_sha256_flash_contents(flash_offset, sig_block_addr - flash_offset, image_digest); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "error generating image digest, %d", ret); + return ret; } - esp_efuse_batch_write_begin(); /* Batch all efuse writes at the end of this function */ + ESP_LOGD(TAG, "reading signature block"); + const ets_secure_boot_signature_t *sig_block = bootloader_mmap(sig_block_addr, sizeof(ets_secure_boot_signature_t)); + if (sig_block == NULL) { + ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", sig_block_addr, sizeof(ets_secure_boot_signature_t)); + return ESP_FAIL; + } + + for (i = 0; i < SECURE_BOOT_NUM_BLOCKS; i++) { + ret = validate_signature_block(i, sig_block, image_digest); + if (ret != ESP_OK) { + break; + } + + /* Generating the SHA of the public key components in the signature block */ + bootloader_sha256_handle_t sig_block_sha; + sig_block_sha = bootloader_sha256_start(); + bootloader_sha256_data(sig_block_sha, &sig_block->block[i].key, sizeof(sig_block->block[i].key)); + bootloader_sha256_finish(sig_block_sha, public_key_digests[i]); + + memcpy((uint8_t *)trusted_keys->key_digests[0], public_key_digests[i], DIGEST_LEN); // Overwriting 0th index to verify each valid signature block + /* A signature block is verified when it is valid and the signature in its signature block can be verified with a valid public key */ + uint8_t verified_digest[DIGEST_LEN] = {0}; + ets_secure_boot_status_t r = ets_secure_boot_verify_signature(sig_block, image_digest, trusted_keys, verified_digest); + if (r != SB_SUCCESS) { + ESP_LOGE(TAG, "Secure boot key (%d) verification failed.", i); + ret = ESP_FAIL; + goto exit; + } + } + + // At least 1 verified signature block found. + if (i > 0) { + // validate_signature_block returns ESP_FAIL when a sig block is absent, which isn't an error. + while (--i) { + trusted_keys->key_digests[i] = public_key_digests[i]; + } + ret = ESP_OK; + } + +exit: + /* Set the pointer to an invalid/absent block to NULL */ + while (i < SECURE_BOOT_NUM_BLOCKS) { + trusted_keys->key_digests[i] = NULL; + i++; + } + ESP_LOGI(TAG, "Secure boot verification success."); + bootloader_munmap(sig_block); + return ret; +} + +/* Traverses ets_secure_boot_key_digests_t to find the number of non-null key_digests */ +static uint8_t get_signature_block_count(ets_secure_boot_key_digests_t * const trusted_keys) { + uint8_t bootloader_sig_block_count = 0; + for (uint8_t i = 0; i < SECURE_BOOT_NUM_BLOCKS; i++) { + if (trusted_keys->key_digests[i] != NULL) { + bootloader_sig_block_count++; + + for (uint8_t j = 0; j < DIGEST_LEN ; j++) { + ESP_LOGD(TAG, "Secure Boot Digest %d: 0x%x", i, *(((uint8_t *)trusted_keys->key_digests[i]) + j)); + } + } + } + return bootloader_sig_block_count; +} + +esp_err_t esp_secure_boot_v2_permanently_enable(const esp_image_metadata_t *image_data) +{ + ESP_LOGI(TAG, "enabling secure boot v2 - ESP32-S2..."); + + if (esp_secure_boot_enabled()) { + ESP_LOGI(TAG, "secure boot v2 is already enabled, continuing.."); + return ESP_OK; + } + + esp_err_t ret; + /* Verify the bootloader */ + esp_image_metadata_t bootloader_data = { 0 }; + ret = esp_image_verify_bootloader_data(&bootloader_data); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "bootloader image appears invalid! error %d", ret); + return ret; + } + + /* Check if secure boot digests are present */ + bool has_secure_boot_digest = ets_efuse_find_purpose(ETS_EFUSE_KEY_PURPOSE_SECURE_BOOT_DIGEST0, NULL); + has_secure_boot_digest |= ets_efuse_find_purpose(ETS_EFUSE_KEY_PURPOSE_SECURE_BOOT_DIGEST1, NULL); + has_secure_boot_digest |= ets_efuse_find_purpose(ETS_EFUSE_KEY_PURPOSE_SECURE_BOOT_DIGEST2, NULL); + ESP_LOGI(TAG, "Secure boot digests %s", has_secure_boot_digest ? "already present":"absent, generating.."); + + + ets_efuse_clear_program_registers(); + uint8_t i, j, bootloader_sig_block_count = 0; + if (!has_secure_boot_digest) { + ets_secure_boot_key_digests_t boot_trusted_keys, app_trusted_keys; + uint8_t boot_trusted_key_data[SECURE_BOOT_NUM_BLOCKS][DIGEST_LEN] = {0}, app_trusted_key_data[SECURE_BOOT_NUM_BLOCKS][DIGEST_LEN] = {0}; + + for(i = 0; i < SECURE_BOOT_NUM_BLOCKS; i++) { + boot_trusted_keys.key_digests[i] = boot_trusted_key_data[i]; + app_trusted_keys.key_digests[i] = app_trusted_key_data[i]; + } + + /* Generate the bootloader public key digests */ + ret = secure_boot_v2_digest_generate(bootloader_data.start_addr, bootloader_data.image_len - SIG_BLOCK_PADDING, &boot_trusted_keys); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Public key digest generation failed."); + return ret; + } + + bootloader_sig_block_count = get_signature_block_count(&boot_trusted_keys); + if (bootloader_sig_block_count <= 0) { + ESP_LOGI(TAG, "No valid signature blocks found. %d signature block(s) found.", bootloader_sig_block_count); + return ESP_FAIL; + } + ESP_LOGI(TAG, "%d signature block(s) found appended to the bootloader.", bootloader_sig_block_count); + + int unused_key_slots = ets_efuse_count_unused_key_blocks(); + if (bootloader_sig_block_count > unused_key_slots) { + ESP_LOGE(TAG, "Bootloader signatures(%d) more than available key slots(%d).", bootloader_sig_block_count, unused_key_slots); + return ESP_FAIL; + } + + for (i = 0; i < SECURE_BOOT_NUM_BLOCKS; i++) { + if (boot_trusted_keys.key_digests[i] == NULL) { + break; + } + + const uint32_t secure_boot_key_purpose[SECURE_BOOT_NUM_BLOCKS] = { ETS_EFUSE_KEY_PURPOSE_SECURE_BOOT_DIGEST0, + ETS_EFUSE_KEY_PURPOSE_SECURE_BOOT_DIGEST1, ETS_EFUSE_KEY_PURPOSE_SECURE_BOOT_DIGEST2 }; + + ets_efuse_block_t block = ets_efuse_find_unused_key_block(); + if (block == ETS_EFUSE_BLOCK_MAX) { + ESP_LOGE(TAG, "Key blocks not available."); + return ESP_FAIL; + } + + int r = ets_efuse_write_key(block, secure_boot_key_purpose[i], boot_trusted_keys.key_digests[i], DIGEST_LEN); + if (r != 0) { + ESP_LOGE(TAG, "Failed to write efuse block %d with purpose %d. Can't continue.", block, secure_boot_key_purpose[i]); + return ESP_FAIL; + } + + r = esp_efuse_set_write_protect(block); + if (r != 0) { + ESP_LOGE(TAG, "Failed to write protect efuse block %d. Can't continue.", block); + return ESP_FAIL; + } + } + + /* Generate the application public key digests */ + ret = secure_boot_v2_digest_generate(image_data->start_addr, image_data->image_len - SIG_BLOCK_PADDING, &app_trusted_keys); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Application signature block is invalid."); + return ret; + } + + int app_sig_block_count = get_signature_block_count(&app_trusted_keys); + ESP_LOGI(TAG, "%d signature block(s) found appended to the application.", app_sig_block_count); + + /* Confirm if atleast one the public key from the application matches a public key in the bootloader + (Also, ensure if that public revoke bit is not set for the matched key) */ + bool match = false; + const uint32_t revoke_bits[SECURE_BOOT_NUM_BLOCKS] = { EFUSE_SECURE_BOOT_KEY_REVOKE0, + EFUSE_SECURE_BOOT_KEY_REVOKE1, EFUSE_SECURE_BOOT_KEY_REVOKE2 }; + + for (i = 0; i < SECURE_BOOT_NUM_BLOCKS && match == false; i++) { + + if (REG_GET_BIT(EFUSE_RD_REPEAT_DATA1_REG, revoke_bits[i])) { + ESP_LOGI(TAG, "Key block(%d) has been revoked.", i); + continue; // skip if the key block is revoked + } + + for (j = 0; j < SECURE_BOOT_NUM_BLOCKS; j++) { + if (!memcmp(boot_trusted_key_data[i], app_trusted_key_data[j], DIGEST_LEN)) { + ESP_LOGI(TAG, "Application key(%d) matches with bootloader key(%d).", j, i); + match = true; + break; + } + } + } + + if (match == false) { + ESP_LOGE(TAG, "No application key digest matches the bootloader key digest."); + return ESP_FAIL; + } + + /* Revoke the empty signature blocks */ + if (bootloader_sig_block_count < SECURE_BOOT_NUM_BLOCKS) { + /* The revocation index can be 0, 1, 2. Bootloader count can be 1,2,3. */ + for (uint8_t i = bootloader_sig_block_count; i < SECURE_BOOT_NUM_BLOCKS; i++) { + ESP_LOGI(TAG, "Revoking empty key digest slot (%d)...", i); + ets_secure_boot_revoke_public_key_digest(i); + } + } + } + + esp_err_t err = esp_efuse_batch_write_begin(); + if (err != ESP_OK) { + ESP_LOGI(TAG, "Error batch programming security eFuses."); + return err; + } + + __attribute__((unused)) static const uint8_t enable = 1; - esp_efuse_write_field_bit(ESP_EFUSE_SECURE_BOOT_EN); esp_efuse_write_field_bit(ESP_EFUSE_DIS_BOOT_REMAP); esp_efuse_write_field_bit(ESP_EFUSE_DIS_LEGACY_SPI_BOOT); - // TODO: also disable JTAG here, etc +#ifdef CONFIG_SECURE_ENABLE_SECURE_ROM_DL_MODE + ESP_LOGI(TAG, "Enabling Security download mode..."); + esp_efuse_write_field_bit(ESP_EFUSE_ENABLE_SECURITY_DOWNLOAD); +#else + ESP_LOGW(TAG, "Not enabling Security download mode - SECURITY COMPROMISED"); +#endif - esp_err_t err = esp_efuse_batch_write_commit(); +#ifndef CONFIG_SECURE_BOOT_ALLOW_JTAG + ESP_LOGI(TAG, "Disable hardware & software JTAG..."); + esp_efuse_write_field_bit(ESP_EFUSE_HARD_DIS_JTAG); + esp_efuse_write_field_bit(ESP_EFUSE_SOFT_DIS_JTAG); +#else + ESP_LOGW(TAG, "Not disabling JTAG - SECURITY COMPROMISED"); +#endif - if (err == ESP_OK) { - assert(esp_rom_efuse_is_secure_boot_enabled()); - ESP_LOGI(TAG, "Secure boot permanently enabled"); +#ifdef CONFIG_SECURE_BOOT_ENABLE_AGGRESSIVE_KEY_REVOKE + esp_efuse_write_field_bit(ESP_EFUSE_SECURE_BOOT_AGGRESSIVE_REVOKE); +#endif + + esp_efuse_write_field_bit(ESP_EFUSE_SECURE_BOOT_EN); + + err = esp_efuse_batch_write_commit(); + if (err != ESP_OK) { + ESP_LOGI(TAG, "Error programming security eFuses."); + return err; } +#ifdef CONFIG_SECURE_BOOT_ENABLE_AGGRESSIVE_KEY_REVOKE + assert(ets_efuse_secure_boot_aggressive_revoke_enabled()); +#endif + + assert(esp_rom_efuse_is_secure_boot_enabled()); + ESP_LOGI(TAG, "Secure boot permanently enabled"); + return ESP_OK; } diff --git a/components/bootloader_support/src/esp32s2/secure_boot_signatures.c b/components/bootloader_support/src/esp32s2/secure_boot_signatures.c index 8f7b99486f..0d4ddc7c78 100644 --- a/components/bootloader_support/src/esp32s2/secure_boot_signatures.c +++ b/components/bootloader_support/src/esp32s2/secure_boot_signatures.c @@ -13,29 +13,32 @@ // limitations under the License. #include "sdkconfig.h" +#include +#include "esp_fault.h" #include "bootloader_flash.h" #include "bootloader_sha.h" +#include "bootloader_utility.h" #include "esp_log.h" #include "esp_image_format.h" +#include "esp_secure_boot.h" #include "esp32s2/rom/secure_boot.h" static const char* TAG = "secure_boot"; #define DIGEST_LEN 32 +#define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length) { - ets_secure_boot_key_digests_t trusted_keys = { 0 }; uint8_t digest[DIGEST_LEN]; uint8_t verified_digest[DIGEST_LEN] = { 0 }; /* Note: this function doesn't do any anti-FI checks on this buffer */ const uint8_t *data; ESP_LOGD(TAG, "verifying signature src_addr 0x%x length 0x%x", src_addr, length); - if ((src_addr + length) % 4096 != 0) { - ESP_LOGE(TAG, "addr 0x%x length 0x%x doesn't end on a sector boundary", src_addr, length); - return ESP_ERR_INVALID_ARG; - } + /* Padding to round off the input to the nearest 4k boundary */ + int padded_length = ALIGN_UP(length, FLASH_SECTOR_SIZE); + ESP_LOGD(TAG, "verifying src_addr 0x%x length", src_addr, padded_length); data = bootloader_mmap(src_addr, length + sizeof(struct ets_secure_boot_sig_block)); if (data == NULL) { @@ -43,23 +46,16 @@ esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length) return ESP_FAIL; } - // Calculate digest of main image -#ifdef BOOTLOADER_BUILD - bootloader_sha256_handle_t handle = bootloader_sha256_start(); - bootloader_sha256_data(handle, data, length); - bootloader_sha256_finish(handle, digest); -#else - /* Use thread-safe esp-idf SHA function */ - esp_sha(SHA2_256, data, length, digest); -#endif - - int r = ets_secure_boot_read_key_digests(&trusted_keys); - - if (r == ETS_OK) { - const ets_secure_boot_signature_t *sig = (const ets_secure_boot_signature_t *)(data + length); - // TODO: calling this function in IDF app context is unsafe - r = ets_secure_boot_verify_signature(sig, digest, &trusted_keys, verified_digest); + /* Calculate digest of main image */ + esp_err_t err = bootloader_sha256_flash_contents(src_addr, padded_length, digest); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Digest calculation failed 0x%x, 0x%x", src_addr, padded_length); + bootloader_munmap(data); + return err; } + + const ets_secure_boot_signature_t *sig = (const ets_secure_boot_signature_t *)(data + length); + int r = esp_secure_boot_verify_rsa_signature_block(sig, digest, verified_digest); bootloader_munmap(data); return (r == ETS_OK) ? ESP_OK : ESP_FAIL; @@ -68,15 +64,29 @@ esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length) esp_err_t esp_secure_boot_verify_rsa_signature_block(const ets_secure_boot_signature_t *sig_block, const uint8_t *image_digest, uint8_t *verified_digest) { ets_secure_boot_key_digests_t trusted_keys; + ets_secure_boot_key_digests_t trusted_key_copies[2]; + ETS_STATUS r; - int r = ets_secure_boot_read_key_digests(&trusted_keys); - if (r != 0) { - ESP_LOGE(TAG, "No trusted key digests were found in efuse!"); - } else { - ESP_LOGD(TAG, "Verifying with RSA-PSS..."); - // TODO: calling this function in IDF app context is unsafe - r = ets_secure_boot_verify_signature(sig_block, image_digest, &trusted_keys, verified_digest); + memset(&trusted_keys, 0, sizeof(ets_secure_boot_key_digests_t)); + memset(trusted_key_copies, 0, 2 * sizeof(ets_secure_boot_key_digests_t)); + + if (!esp_secure_boot_enabled()) { + return ESP_OK; } - return (r == 0) ? ESP_OK : ESP_ERR_IMAGE_INVALID; + r = ets_secure_boot_read_key_digests(&trusted_keys); + if (r != ETS_OK) { + ESP_LOGI(TAG, "Could not read secure boot digests!"); + return ESP_FAIL; + } + + // Create the copies for FI checks (assuming result is ETS_OK, if it's not then it'll fail the fault check anyhow) + ets_secure_boot_read_key_digests(&trusted_key_copies[0]); + ets_secure_boot_read_key_digests(&trusted_key_copies[1]); + ESP_FAULT_ASSERT(memcmp(&trusted_keys, &trusted_key_copies[0], sizeof(ets_secure_boot_key_digests_t)) == 0); + ESP_FAULT_ASSERT(memcmp(&trusted_keys, &trusted_key_copies[1], sizeof(ets_secure_boot_key_digests_t)) == 0); + + ESP_LOGI(TAG, "Verifying with RSA-PSS boot..."); + r = ets_secure_boot_verify_signature(sig_block, image_digest, &trusted_keys, verified_digest); + return (r == ETS_OK) ? ESP_OK : ESP_FAIL; } diff --git a/components/bootloader_support/src/esp_image_format.c b/components/bootloader_support/src/esp_image_format.c index 07e0599392..92df9fd2f8 100644 --- a/components/bootloader_support/src/esp_image_format.c +++ b/components/bootloader_support/src/esp_image_format.c @@ -275,7 +275,7 @@ static esp_err_t image_load(esp_image_load_mode_t mode, const esp_partition_pos_ "only verify signature in bootloader" into the macro so it's tested multiple times. */ #if CONFIG_SECURE_BOOT_V2_ENABLED - ESP_FAULT_ASSERT(memcmp(image_digest, verified_digest, HASH_LEN) == 0); + ESP_FAULT_ASSERT(!esp_secure_boot_enabled() || memcmp(image_digest, verified_digest, HASH_LEN) == 0); #else // Secure Boot V1 on ESP32, only verify signatures for apps not bootloaders ESP_FAULT_ASSERT(data->start_addr == ESP_BOOTLOADER_OFFSET || memcmp(image_digest, verified_digest, HASH_LEN) == 0); #endif @@ -310,7 +310,7 @@ err: // Prevent invalid/incomplete data leaking out bzero(data, sizeof(esp_image_metadata_t)); return err; - } +} esp_err_t bootloader_load_image(const esp_partition_pos_t *part, esp_image_metadata_t *data) { diff --git a/components/bootloader_support/src/idf/bootloader_sha.c b/components/bootloader_support/src/idf/bootloader_sha.c index 06fdc358d2..8d70406c72 100644 --- a/components/bootloader_support/src/idf/bootloader_sha.c +++ b/components/bootloader_support/src/idf/bootloader_sha.c @@ -51,4 +51,5 @@ void bootloader_sha256_finish(bootloader_sha256_handle_t handle, uint8_t *digest } mbedtls_sha256_free(ctx); free(handle); + handle = NULL; } diff --git a/components/bootloader_support/src/idf/secure_boot_signatures.c b/components/bootloader_support/src/idf/secure_boot_signatures.c index ee541445bd..8f7acb9ea7 100644 --- a/components/bootloader_support/src/idf/secure_boot_signatures.c +++ b/components/bootloader_support/src/idf/secure_boot_signatures.c @@ -27,6 +27,11 @@ #include "mbedtls/ctr_drbg.h" #include #include +#include "esp_secure_boot.h" + +#ifdef CONFIG_IDF_TARGET_ESP32S2 +#include +#endif #define DIGEST_LEN 32 @@ -142,6 +147,26 @@ static const char *TAG = "secure_boot_v2"; #define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) #define RSA_KEY_SIZE 384 /* RSA 3072 Bits */ +#if CONFIG_IDF_TARGET_ESP32S2 +inline static bool digest_matches(const void *trusted, const void *computed) +{ + if (trusted == NULL) { + return false; + } + + // 'trusted' is probably a pointer to read-only efuse registers, + // which only support word reads. memcmp() cannot be guaranteed + // to do word reads, so we make a local copy here (we know that + // memcpy() will do word operations if it can). + uint8_t __attribute__((aligned(4))) trusted_local[ETS_DIGEST_LEN]; + uint8_t __attribute__((aligned(4))) computed_local[ETS_DIGEST_LEN]; + + memcpy(trusted_local, trusted, ETS_DIGEST_LEN); + memcpy(computed_local, computed, ETS_DIGEST_LEN); + return memcmp(trusted_local, computed_local, ETS_DIGEST_LEN) == 0; +} +#endif /* CONFIG_IDF_TARGET_ESP32S2 */ + esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length) { uint8_t digest[DIGEST_LEN] = {0}; @@ -173,23 +198,19 @@ esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length) esp_err_t esp_secure_boot_verify_rsa_signature_block(const ets_secure_boot_signature_t *sig_block, const uint8_t *image_digest, uint8_t *verified_digest) { - int i = 0; - + uint8_t i = 0; #if CONFIG_SECURE_BOOT_V2_ENABLED /* Verify key against efuse block */ - uint8_t efuse_trusted_digest[DIGEST_LEN] = {0}, sig_block_trusted_digest[DIGEST_LEN] = {0}; - memcpy(efuse_trusted_digest, (uint8_t *) EFUSE_BLK2_RDATA0_REG, sizeof(efuse_trusted_digest)); + uint8_t sig_block_key_digest[SECURE_BOOT_NUM_BLOCKS][DIGEST_LEN] = {0}; /* Note: in IDF verification we don't add any fault injection resistance, as we don't expect this to be called during boot-time verification. */ memset(verified_digest, 0, DIGEST_LEN); - /* Generating the SHA of the public key components in the signature block */ - bootloader_sha256_handle_t sig_block_sha; - sig_block_sha = bootloader_sha256_start(); - bootloader_sha256_data(sig_block_sha, &sig_block->block[0].key, sizeof(sig_block->block[0].key)); - bootloader_sha256_finish(sig_block_sha, (unsigned char *)sig_block_trusted_digest); +#if CONFIG_IDF_TARGET_ESP32 + uint8_t efuse_trusted_digest[DIGEST_LEN] = {0}; + memcpy(efuse_trusted_digest, (uint8_t *) EFUSE_BLK2_RDATA0_REG, sizeof(efuse_trusted_digest)); - if (memcmp(efuse_trusted_digest, sig_block_trusted_digest, DIGEST_LEN) != 0) { + if (memcmp(efuse_trusted_digest, sig_block_key_digest, DIGEST_LEN) != 0) { const uint8_t zeroes[DIGEST_LEN] = {0}; /* Can't continue if secure boot is enabled, OR if a different digest is already written in efuse BLK2 @@ -200,7 +221,25 @@ esp_err_t esp_secure_boot_verify_rsa_signature_block(const ets_secure_boot_signa return ESP_FAIL; } } -#endif +#elif CONFIG_IDF_TARGET_ESP32S2 + bool match = false; + ets_secure_boot_key_digests_t efuse_trusted_digest; + ETS_STATUS r; + r = ets_secure_boot_read_key_digests(&efuse_trusted_digest); + if (r != 0) { + ESP_LOGI(TAG, "Could not read secure boot digests!"); + return ESP_FAIL; + } +#endif /* CONFIG_IDF_TARGET_ESP32 */ + + /* Generating the SHA of the public key components in the signature block */ + for (i = 0; i < SECURE_BOOT_NUM_BLOCKS; i++) { + bootloader_sha256_handle_t sig_block_sha; + sig_block_sha = bootloader_sha256_start(); + bootloader_sha256_data(sig_block_sha, &sig_block->block[i].key, sizeof(sig_block->block[i].key)); + bootloader_sha256_finish(sig_block_sha, (unsigned char *)sig_block_key_digest[i]); + } +#endif /* CONFIG_SECURE_BOOT_V2_ENABLED */ ESP_LOGI(TAG, "Verifying with RSA-PSS..."); int ret = 0; @@ -222,6 +261,19 @@ esp_err_t esp_secure_boot_verify_rsa_signature_block(const ets_secure_boot_signa } for (i = 0; i < SECURE_BOOT_NUM_BLOCKS; i++) { +#if CONFIG_IDF_TARGET_ESP32S2 + for (uint8_t j = 0; j < SECURE_BOOT_NUM_BLOCKS; j++) { + if (digest_matches(efuse_trusted_digest.key_digests[j], sig_block_key_digest[i])) { + ESP_LOGI(TAG, "eFuse key matches(%d) matches the application key(%d).", j, i); + match = true; + break; + } + } + if (match == false) { + continue; // Skip the public keys whose digests don't match. + } +# endif + const mbedtls_mpi N = { .s = 1, .n = sizeof(sig_block->block[i].key.n)/sizeof(mbedtls_mpi_uint), .p = (void *)sig_block->block[i].key.n, @@ -260,7 +312,7 @@ esp_err_t esp_secure_boot_verify_rsa_signature_block(const ets_secure_boot_signa goto exit; } - ret = mbedtls_rsa_rsassa_pss_verify( &pk, mbedtls_ctr_drbg_random, &ctr_drbg, MBEDTLS_RSA_PUBLIC, MBEDTLS_MD_SHA256, 32, + ret = mbedtls_rsa_rsassa_pss_verify( &pk, mbedtls_ctr_drbg_random, &ctr_drbg, MBEDTLS_RSA_PUBLIC, MBEDTLS_MD_SHA256, DIGEST_LEN, sig_block->block[i].image_digest, sig_be); if (ret != 0) { ESP_LOGE(TAG, "Failed mbedtls_rsa_rsassa_pss_verify, err: %d", ret); @@ -276,6 +328,10 @@ esp_err_t esp_secure_boot_verify_rsa_signature_block(const ets_secure_boot_signa free(sig_be); free(buf); - return (!ret) ? ESP_OK : ESP_ERR_IMAGE_INVALID; +#if CONFIG_IDF_TARGET_ESP32 + return (ret != 0) ? ESP_ERR_IMAGE_INVALID: ESP_OK; +#elif CONFIG_IDF_TARGET_ESP32S2 + return (ret != 0 || match == false) ? ESP_ERR_IMAGE_INVALID: ESP_OK; +#endif /* CONFIG_IDF_TARGET_ESP32 */ } #endif